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