Связывание данных между вложенными пользовательскими элементами управления

У меня есть главное окно, которое я создаю для запуска своего приложения, которое управляет серией пользовательских элементов управления. По умолчанию в главном окне находится один элемент управления внутри. У меня возникла проблема с распространением объектов зависимостей из дочерних пользовательских элементов управления обратно в родительский пользовательский элемент управления. Предупреждаю, я администратор базы данных, и это мой первый удар по MVVM, поэтому такой подход может быть полностью неверным. Если вам нужны другие фрагменты кода, я могу их предоставить :). Внутри этого окна находится один пользовательский элемент управления:

<Grid>
    <views:UserControl1 Name="ViewOne" DeviceName="DEVICE1" EventNumber="EVENT1" />
</Grid>

Этот пользовательский элемент управления содержит два объекта зависимости: одно принимающее устройство и другое принимающее событие. В зависимости от того, какой элемент я отправляю (или заказываю, если оба установлены), я меняю источники данных для любых последующих пользовательских элементов управления в UserControl1. Вот два объекта зависимости внутри UserControl1:

public partial class UserControl1: UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    public string DeviceName
    {
        get { return (string)GetValue(DeviceNameProperty);  }
        set { SetValue(DeviceNameProperty, value); }
    }

    public static readonly DependencyProperty DeviceNameProperty = 
        DependencyProperty.Register("DeviceName", typeof(string), typeof(UserControl1), new PropertyMetadata(String.Empty));

    public string EventNumber
    {
        get { return (string)GetValue(EventNumberProperty); }
        set { SetValue(EventNumberProperty, value); }
    }

    public static readonly DependencyProperty EventNumberProperty =
        DependencyProperty.Register("EventNumber", typeof(string), typeof(UserControl1), new PropertyMetadata(String.Empty));
}

Внутри Usercontrol1 у меня есть два дополнительных usercontrols, которые принимают как устройство, так и номер события от usercontrol1, который передается или устанавливается главным окном.

<UserControl x:Class="Omitted"             
         Background="White" Name="FLP" >    
<Grid Name="rootDataGrid">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <GroupBox Header="Summary" Grid.Row="0" Grid.Column="0">
        <fv:VerticalSummary x:Name="VFS" DeviceName="{Binding Path=DeviceName, ElementName=FLP, Mode=TwoWay}" EventNumber="{Binding Path=EventNumber, ElementName=FLP}" />
    </GroupBox>
    <GroupBox Header="Tags" Grid.Row="0" Grid.Column="1">
        <tags:Tags x:Name="TBF" DeviceName="{Binding Path=DeviceName, ElementName=FLP, Mode=TwoWay}" EventNumber="{Binding Path=EventNumber, ElementName=FLP}"  />
    </GroupBox>

Все работает нормально, отправляя данные из окна в родительский элемент управления, а затем из родительского элемента управления в оба пользовательских элемента управления. К сожалению, если я изменю значение в одном из моих пользовательских элементов управления (Вертикальная сводка), оно не поднимется до верхнего элемента управления, а затем перейдет к другим элементам управления. Вот вертикальное сводное представление (выделенный код) для справки:

 VerticalSummaryViewModel _vm;

    public VerticalSummary()
    {
        InitializeComponent();
        _vm = (VerticalSummaryViewModel)rootGrid.DataContext;
    }

    public string DeviceName
    {
        get { return (string)GetValue(DeviceNameProperty); }
        set { SetValue(DeviceNameProperty, value); }
    }

    public static readonly DependencyProperty DeviceNameProperty =
        DependencyProperty.Register("DeviceName", typeof(string), typeof(VerticalSummary), new PropertyMetadata(String.Empty, OnDeviceNameSet));

    public static void OnDeviceNameSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((VerticalSummary)d)._vm.DeviceName = e.NewValue.ToString();
    }

    public string EventNumber
    {
        get { return (string)GetValue(EventNumberProperty); }
        set { SetValue(EventNumberProperty, value); }
    }

    public static readonly DependencyProperty EventNumberProperty =
        DependencyProperty.Register("EventNumber", typeof(string), typeof(VerticalSummary), new PropertyMetadata(String.Empty, OnEventNumberSet));

    public static void OnEventNumberSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((VerticalSummary)d)._vm.EventNumber = e.NewValue.ToString();
    }

Вот модель VerticalSummaryViewModel для справки:

public class VerticalSummaryViewModel : ObservableObject
{
    private OMSRepository _repo;

    private ObservableCollection<OMS> _feeders;
    public ObservableCollection<OMS> Feeders
    {
        get { return _feeders; }
        set {
                if (value != _feeders)
                {
                    _feeders = value;
                    OnPropertyChanged("Feeders");
                }
            }
    }

    private OMS _selectedFeeder;
    public OMS SelectedFeeder
    {
        get { return _selectedFeeder; }
        set
        {
            if(_selectedFeeder != value)
            {
                _selectedFeeder = value;
                DeviceName = _selectedFeeder.OMSName;
                OnPropertyChanged("SelectedFeeder");
            }
        }
    }

    private string _eventNumber;
    public string EventNumber
    {
        get { return _eventNumber; }
        set
        {
            if (value != _eventNumber)
            {
                _eventNumber = value;
                OnPropertyChanged("EventNumber");
            }
        }
    }

    private string _deviceName;
    public string DeviceName
    {
        get { return _deviceName; }
        set
        {
            if (value != _deviceName)
            {
                _deviceName = value;
                OnPropertyChanged("DeviceName");
            }
        }
    }

    public VerticalSummaryViewModel()
    {
        _repo = new OMSRepository();

        LoadFeederDropDown();
        LoadFeederSummary();
    }

    private void LoadFeederSummary()
    {

    }

    private void LoadFeederDropDown()
    {
        Feeders = _repo.GetFeeders();
    }       
}

person GregN    schedule 17.10.2017    source источник
comment
Строка, которую он не всплывает в верхний элемент управления, - это то, где некоторые из ваших проблем, поскольку я не думаю, что это можно сделать из чистой привязки без какого-либо более сложного кода. Похоже, вы ищете изменения данных для распространения между независимыми ViewModels (поправьте меня, если я ошибаюсь), я не смог добиться этого только с помощью привязок. Кто-то, вероятно, мог бы пролить больше света, но похоже, что вам может потребоваться реализовать некоторую форму Messenger, например здесь   -  person Ginger Ninja    schedule 18.10.2017
comment
Это именно то, чего я пытаюсь достичь. Я хотел деконструировать пользовательский элемент управления на меньшие элементы управления, чтобы иметь возможность повторного использования (некоторые функции будут использоваться повторно). Возможно, в этом случае было бы лучше просто укусить встроенный и использовать один пользовательский элемент управления целиком.   -  person GregN    schedule 18.10.2017
comment
Почему два UserControl не наследуют один и тот же DataContext от родительского окна и просто не связываются со свойствами его модели представления?   -  person mm8    schedule 18.10.2017
comment
Спасибо за ответ. Я решил использовать Messenger, чтобы достичь статуса плюс небольшой редизайн.   -  person GregN    schedule 15.11.2017


Ответы (1)


Я рекомендую вам изучить использование MVVM для вашей ситуации (что является обычным в WPF).

Что вы в конечном итоге сделаете, так это создадите одну ViewModel (VM), которая будет содержать ваши бизнес-данные (думайте об этом как о временной таблице). Затем вы привяжете ваши элементы управления, необходимые для конкретных данных, непосредственно к свойствам на виртуальной машине.

Если указать mode=TwoWay в обеих привязках, когда один элемент управления изменяет общую привязку, другая привязка получит уведомление об изменении данных и покажет изменение в своем элементе управления.


Я написал статью о MVVM для хорошей стартовой точки Xaml: создание экземпляра главной страницы ViewModel и стратегия загрузки для упрощения привязки < / а>

person ΩmegaMan    schedule 18.10.2017