0

I have a mainwindow that I spawn to start my application that drives a series of user controls. By default, the main window has one usercontrol inside. I'm having a challenge in propogating dependencyobjects from child usercontrols back up to a parent usercontrol. Forewarning, I'm a DBA and this is my first stab at MVVM so the approach could be completely invalid. If you need any other code snippets I can provide them as well :). This window has one usercontrol inside:

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

That user control contains two dependency objects: one receiving device and another one receiving an event. Based upon which item I send (or ordered if both set), I change the datasources for any subsequent usercontrols within UserControl1. Here are the two dependency objects inside the 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));
}

Inside Usercontrol1, I have two additional usercontrols which accept both a device and event number from usercontrol1 which is passed or set by the mainwindow.

<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>

Everything works fine sending data from the window to the parent control and then the parent control down to both user controls. Unfortunately, if I change the value in one of my user controls (Vertical Summary) it does not bubble up to the top control and then trickle down to the other controls. Here is the Vertical Summary View (codebehind) for reference:

 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();
    }

Here is the VerticalSummaryViewModel for reference:

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();
    }       
}
GregN
  • 142
  • 1
  • 13
  • The line "it does not bubble up to the top control" is where some of your issue is as i dont think that can be done from pure binding without some more complicated code behind. It sounds like you are looking for data changes to propagate between independent ViewModels (correct me if im wrong), i have not been able to accomplish this by bindings alone. Someone could probably shed more light, but it sounds like you may need to implement some form of `Messenger` like [here](http://www.c-sharpcorner.com/UploadFile/20c06b/messaging-system-in-wpf/) – Ginger Ninja Oct 17 '17 at 22:39
  • That's exactly what I'm trying to accomplish. I wanted to deconstruct a usercontrol in to smaller usercontrols such that I can have some reusability (some functions will be reused). Maybe in this instance it would be better to just bite the built and use one usercontrol in it's entirety. – GregN Oct 17 '17 at 23:45
  • Why don't the two UserControl inherit the same DataContext from the parent window and simply bind to properties of its view model? – mm8 Oct 18 '17 at 13:46
  • Thanks for the feedback. I decided to go with a Messenger to achieve the status plus a slight redesign. – GregN Nov 15 '17 at 20:04

1 Answers1

0

I recommend you look into using MVVM for your situation (which is a common one in WPF).

What you will end up doing is create a single ViewModel (VM) which will contain your business data (think of it as temporary table). Then you will bind your controls specific data needs directly to properties on the VM.

By specifying mode=TwoWay on the both bindings, when one control changes a shared binding, the binding other binding will receive notice that the data has changed and show the change in its control.


I have written an article on MVVM for a good launching point Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122