N.J
N.J

Reputation: 1230

Managing and Binding Usercontrols

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

Answers (1)

user1228
user1228

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

Related Questions