Поиск привязки данных WPF в неправильном DataContext - PullRequest
0 голосов
/ 28 октября 2019

Я изучаю паттерн MVVM и у меня возникли проблемы с привязкой данных. Я понимаю, как это работает, но в моем примере он использует неправильный DataContext для поиска привязки.

Я не нашел другой вопрос, который подходит для моей проблемы.

Итак, у меня есть это представление:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterChoiceView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <ItemsControl ItemsSource="{Binding CashRegisters}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel></WrapPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</UserControl>

Со следующим ViewModel:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kenshinaro.CashRegister.UI.View.CashRegister;

namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
    public class CashRegisterChoiceViewModel : MVVM.ViewModel
    {
        private ObservableCollection<CashRegisterItemsControlView> _cashRegisters = new ObservableCollection<CashRegisterItemsControlView>()
        {
            new CashRegisterItemsControlView()
            {
                DataContext = new CashRegisterItemsControlViewModel()
                {
                    View = new CashRegisterCardView()
                    {
                        DataContext = new CashRegisterCardViewModel()
                        {
                            Model = new Model.CashRegister()
                            {
                                Name = "1"
                            }
                        }
                    }
                }
            },
            new CashRegisterItemsControlView()
            {
                DataContext = new CashRegisterItemsControlViewModel()
                {
                    View = new CashRegisterCardView()
                    {
                        DataContext = new CashRegisterCardViewModel()
                        {
                            Model = new Model.CashRegister()
                            {
                                Name = "2"
                            }
                        }
                    }
                }
            },
            new CashRegisterItemsControlView()
            {
                DataContext = new CashRegisterItemsControlViewModel()
                {
                    View = new CashRegisterCardView()
                    {
                        DataContext = new CashRegisterCardViewModel()
                        {
                            Model = new Model.CashRegister()
                            {
                                Name = "3"
                            }
                        }
                    }
                }
            }
        };

        public ObservableCollection<CashRegisterItemsControlView> CashRegisters
        {
            get => _cashRegisters;
            set => SetProperty(ref _cashRegisters, value);
        }
    }
}

(Мой класс ViewModel реализует интерфейс INotifyPropertyChanged)

Так что это представление показывает список этого представления:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterItemsControlView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <md:Card Margin="10">
        <Grid >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <local:CashRegisterCardView Padding="5"/>
            <Button Margin="20" Grid.Row="1" Content="Pick me"/>
        </Grid>
    </md:Card>
</UserControl>

ViewModel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
    public class CashRegisterItemsControlViewModel : MVVM.ViewModel
    {
        private View.CashRegister.CashRegisterCardView _view;

        public View.CashRegister.CashRegisterCardView View
        {
            get => _view;
            set => SetProperty(ref _view, value);
        }
    }
}

И это представление также показывает это представление:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterCardView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" Background="White" >
    <Grid>
        <TextBlock Text="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=local:CashRegisterCardView}}"/>
    </Grid>
</UserControl>

ViewModel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kenshinaro.CashRegister.Model;

namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
    public class CashRegisterCardViewModel : MVVM.ViewModel
    {
        private Model.CashRegister _model;

        public Model.CashRegister Model
        {
            get => _model;
            set => SetProperty(ref _model, value);
        }

        public String Name
        {
            get => _model.Name;
            set => _model.Name = value;
        }
    }
}

Теперь, еслиЯ запускаю приложение. Привязка к TextBlock в последнем виде не отображается. Выходные данные VisualStudio сообщают:

System.Windows.Data Ошибка: 40: Ошибка пути BindingExpression: свойство 'Name' не найдено в 'object' '' CashRegisterItemsControlViewModel '(HashCode = 35528341)'. BindingExpression: Path = DataContext.Name;DataItem = 'CashRegisterCardView' (Name = '');целевым элементом является TextBlock (Name = '');Целевым свойством является «Текст» (тип «Строка»)

. Как я могу из этого выбрать, оно ищет Свойство в DataContext CashRegisterItemsControlView, но я просто не понимаю почему, потому чтоя устанавливаю DataContext вручную при инициализации коллекции (только для тестирования):

private ObservableCollection<CashRegisterItemsControlView> _cashRegisters = new ObservableCollection<CashRegisterItemsControlView>()
        {
            new CashRegisterItemsControlView()
            {
                DataContext = new CashRegisterItemsControlViewModel()
                {
                    View = new CashRegisterCardView()
                    {
                        DataContext = new CashRegisterCardViewModel()
                        {
                            Model = new Model.CashRegister()
                            {
                                Name = "1"
                            }
                        }
                    }
                }
            },
...

Так почему же он по-прежнему использует неверный DataContext?

Теперь, когда я изменяю привязку к View.DataContext.Nameон ищет в заданном вручную DataContext:

System.Windows.Data Ошибка: 40: Ошибка пути BindingExpression: свойство 'View' не найдено в 'object' '' CashRegisterCardView '(Name =' ')». BindingExpression: Path = View.DataContext.Name;DataItem = 'CashRegisterCardView' (Name = '');целевым элементом является TextBlock (Name = '');Свойство target - «Текст» (тип «Строка»)

Я в замешательстве .. Надеюсь, вы мне поможете.

Спасибо!

ЕслиВам нужна информация о других классах, просто спросите. Я не хочу ставить больше кода в вопрос, так как он достаточно длинный.

1 Ответ

1 голос
/ 28 октября 2019

Модели имеют ссылки на виды. Это нарушение шаблона MVVM.

Корень проблемы - в реализации CashRegisterItemsControlView:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterItemsControlView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <md:Card Margin="10">
        <Grid >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <local:CashRegisterCardView Padding="5"/>
            <Button Margin="20" Grid.Row="1" Content="Pick me"/>
        </Grid>
    </md:Card>
</UserControl>

создает экземпляр CashRegisterCardView (<local:CashRegisterCardView Padding="5"/>), который неимеет свой собственный DataContext и наследует DataContext от родителя. View из CashRegisterItemsControlViewModel не используется.

Вы можете изменить его, чтобы он заработал, например:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterItemsControlView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <md:Card Margin="10">
        <Grid >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <ContentControl Content="{Binding View}" Padding="5"/>
            <Button Margin="20" Grid.Row="1" Content="Pick me"/>
        </Grid>
    </md:Card>
</UserControl>

, но было бы намного лучшепереработать архитектуру вашего приложения

...