Reputation: 1230
I am quite new to WPF and now am encountered a scenario were i couldn't find any effective solution. In my project i have many viewmodels and usercontrols. The main window is splited into two and in the left hand side i display usercontrols according to the current viewmodel everythis works fine. The problem starts when i wanted to display a second usercontrol in the right hand side according to what user selected in the left hand side. The user control has many text boxes and Combo box. how will i bind this data from the cosequtive view model?
//mainwindow.xaml
<Window.resources>
<Datatemplate Datatype={x:Type vm:Viewmodel1}>
<loc:Usercontrol1/> // in the left hand side
</DataTemplate>
<Datatemplate Datatype={x:Type vm:Viewmodel2}>
<loc:Usercontrol2/> // in the lefthand side
</DataTemplate>
</Window.Resources>
...
<Grid Grid.Column="0">
<ContentControl Content={Binding CurrentViewModel}/>
</Grid>
<Grid Grid.Column="1">
<Grid.Resources>
<Datatemplate Datatype={x:Type vm:Viewmodel1}>
<loc:Usercontrol3 NameDp={Binding Name}/> // in the right hand side
</DataTemplate>
<Datatemplate Datatype={x:Type vm:Viewmodel2}>
<loc:Usercontrol3/> // in the rightthand side
</DataTemplate>
</Grid.Resources>
</Grid>
// Usercontrol3.xaml
<Grid>
<TextBox Text="{Binding Path=NameDp, ElementName=UserControl3}" />
</Grid>
// UserControl3.cs
public static readonly DependencyProperty NameUCProperty =DependencyProperty.Register("NameDp", typeof(string), typeof(UserControl3), new FrameworkPropertyMetadata(NamePropertyChanged));
public string NameDp
{
get
{
return (string)GetValue(NameUCProperty);
}
set
{
SetValue(NameUCProperty, value);
}
}
//ViewModel
Public Name {get;set;}
In each viewmodels i get data from the Database and i would like to Bind those data to the Usercontrols in the right hand side according to the user selection. How do i Bind these data? Is this the right approch or am i completly wrong?
Upvotes: 0
Views: 138
Reputation:
That's definitely one way to do that. For me, I'd organize things this way. I've got a prototype around here that demonstrates how this stuff works...
First, our MainWindow
<Window x:Class="WpfUserControlsBindingListeningNotMuchHere.MainWindow"
xmlns:t="clr-namespace:WpfUserControlsBindingListeningNotMuchHere"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:ms="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525">
<Window.DataContext>
<t:ViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<t:UserControl1 x:Name="uc1"
Items="{Binding Items}"
Selected="{Binding Selected, Mode=TwoWay}" />
<t:UserControl2 Grid.Column="1"
DataContext="{Binding Result}" />
</Grid>
</Window>
Note uc1. It takes in a list and exposes the selected item. These are all bound to the ViewModel. Properties Items
and Selected
are DependencyProperties I have defined on the UserControl. I can show the code if you want, but it should be understandable without. (The Mode=TwoWay
could be done away with if I tweaked the DP definition.)
In the ViewModel, I listen for changes to Selected
and do my work accordingly.
class ViewModel : INotifyPropertyChanged
{
private object _selected;
private object _result;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<object> Items { get; private set; }
public object Selected
{
get { return this._selected; }
set
{
if (_selected == value)
return;
this._selected = value;
PropertyChanged(this, new PropertyChangedEventArgs("Selected"));
}
}
public object Result
{
get { return this._result; }
set
{
if (_result == value )
return;
this._result = value;
PropertyChanged(this, new PropertyChangedEventArgs("Result"));
}
}
public ViewModel()
{
Items = new ObservableCollection<object>();
Items.Add(1);
Items.Add("hello");
Items.Add(3.0d);
PropertyChanged += OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != "Selected")
return;
//DO MASSIVE WORK HER ON BACKGROUND THREAD OR SOMETHING LOL
Result = "OMG THIS TOOK A LONG TIME, " + Selected.ToString();
}
}
So, I'm simply watching for a change to Selected, at which point I do my work (business logic) and expose the result within another property. This is then bound to my second UserControl in the UI.
As I said, the UC code is trivial...
<UserControl x:Class="WpfUserControlsBindingListeningNotMuchHere.UserControl1"
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"
x:Name="root">
<ListBox ItemsSource="{Binding Items, ElementName=root}"
SelectedItem="{Binding Selected, ElementName=root}" />
</UserControl>
and in the codebehind (OMG CODEBEHIND LOOK OUT EVIL)
public partial class UserControl1 : UserControl
{
#region Items
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(IEnumerable<object>),
typeof(UserControl1),
new UIPropertyMetadata(null));
public IEnumerable<object> Items
{
get { return (IEnumerable<object>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
#endregion
#region Selected
public static readonly DependencyProperty SelectedProperty =
DependencyProperty.Register(
"Selected",
typeof(object),
typeof(UserControl1),
new UIPropertyMetadata(null));
public object Selected
{
get { return (object)GetValue(SelectedProperty); }
set { SetValue(SelectedProperty, value); }
}
#endregion
public UserControl1()
{
InitializeComponent();
}
}
and
<UserControl x:Class="WpfUserControlsBindingListeningNotMuchHere.UserControl2"
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="300" d:DesignWidth="300">
<TextBlock Text="{Binding}" />
</UserControl>
No codebehind on this one.
Upvotes: 1