0

I have a ViewModel which has an ObservableCollection<MyData> MainCollection and MyData SelectedData that holds the currently selected item from the MainCollection. MyData consists of several properties and also of an ObservableCollection<MyValuePair> MyPairs. MyValuePair consists of 2 properties Description and Value just like a KeyValuePair (Cannot use a Dictionary because I want to have TwoWay binding).

I want to dynamically create controls foreach MyValuePair according to my DataTemplate (I am trying to do it like here: Adding controls dynamically in WPF MVVM):

        //do not want to create another viewModel
        <DataTemplate DataType="{x:Type vm:MyValuePairViewModel }">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            //problem can only select one MyValuePair at a time
            <TextBlock Text="{Binding MyValuePair.Description, Mode=TwoWay}"/>
            <TextBox Template="{StaticResource TextBoxBaseControlTemplate}" Grid.Row="0" Grid.Column="1" Text="{Binding MyValuePair.Value, Mode=TwoWay}"/>
        </Grid>
    </DataTemplate>

MyData:

public class MyData
{
    //Properties
    ObservableCollection<MyValuePair> MyPairs { get; set; }
}

MainViewModel:

public class MainViewModel : ViewModelBase
{
    ObservableCollection<MyData> MainCollection { get; set; }

    MyData selectedData
    public MyData SelectedData
    {
        get { return selectedData; }
        set
        {
            Set(() => SelectedData, ref selectedData, value);
        }
    }
}

MyValuePair:

public class MyValuePair
{
    private string description;
    public string Description
    {
        get { return description; }
        set { description = value; }
    }

    private string _value;
    public string Value
    {
        get { return _value; }
        set { _value = value; }
    }

but the problem is I do not want to create another ViewModel (MyValuePairViewModel) for my MyValuePair because then I have to synchronize it with my MainViewModel everytime the SelectedData changes or if the MyPair (of type MyValuePair) changes I have to sync it back to the MainViewModel and it does not feel right.

So is there an easy way to bind my DataTemplate directly to each element inside of the ObservableCollection<MyValuePair> property in SelectedData so that the DataTemplate is created for each MyPairs?


Edit At the moment I am using another ViewModel:

public class MyValuePairViewModel : ViewModelBase
{
    private MyValuePair myValuePair;
    public MyValuePair MyValuePair
    {
        get { return myPair; }
        set
        {
            Set(() => MyValuePair, ref myValuePair, value);
        }
    }

    public MyValuePairViewModel(MyValuePair myValuePair)
    {
        MyValuePair = myValuePair;
    }
}

and added it inside my MainViewModel like this:
public ObservableCollection<MyValuePairViewModel> MyPairs { get; set; }.
So for every element inside MyPairs I create the above DataTemplate

Whenever the selection of the ListView changes I do the following:

public void RefreshKeyValuePairs()
    {
        if (MyPairs != null)
        {
            MyPairs.Clear();
        }

        if (SelectedData != null)
        {
            foreach (MyValuePair item in SelectedData.MyPairs)
            {
                MyValuePairViewModel vm = new MyValuePairViewModel(item);
                MyPairs.Add(vm);
            }
        }
    }

However, in this case whenever I type something inside the created TextBox it is not updated inside the MainViewModel's SelectedData only inside MyValuePairViewModel and I have to manually sync it inside the MyValuePairViewModel Set method to the MainViewModel's SelectedData (but this also requires that I have the instance of the MainViewModel inside MyValuePairViewModel).

In other words I want to bind directly to the created elements SelectedData.MyPairs[0].Description bind to first created TextBlock, SelectedData.MyPairs[1].Description bind to second created TextBlock and so on so that it automatically updates in both directions. If SelectedData.MyPairs were not a collection but a normal property I could bind to it directly.

4
  • I don't follow the question really. Using a listbox like that templates out each item into the controls in your itemtemplate. Maybe the answer is to use the current item binding / and set IsSynchronizedWithCurrentItem true on the listbox. Bind to the default view of your observablecollection. Commented Apr 6, 2018 at 16:07
  • 1
    I think you should change your bindings in the data template. Instead of SelectedData.MyPairs.Description use Description, and do the same for the Value property Commented Apr 6, 2018 at 17:33
  • @Andy Tried to clarify it a little bit. Commented Apr 6, 2018 at 18:08
  • 1
    Dr WPF built something called an observable dictionary. I think that does bind two ways. Been a while since I used one. drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/ Commented Apr 6, 2018 at 18:21

1 Answer 1

1

You can bind your UI element directly to the MyValuePair object. You do not need a specific ViewModel class MyValuePairViewModel.

The only thing that you will miss is the implementation of INotifyPropertyChanged on MyValuePair. The UI will be initialized normally, but programmatic updates to the Description and Value properties after the initialisation will not appear in the UI. This won't be a problem, if you don't make any programmatic updates.

If you are going to make changes to the Description and Value properties, the easiest thing would be to implement the INotifyPropertyChanged directly on MyValuePair. This only requires a few lines of code and would be easier than duplicating the functionality with a separate class MyValuePairViewModel.

Sign up to request clarification or add additional context in comments.

3 Comments

I am unable to get the binding correct (it displays only the strings and not the controls with the bound text). Currently I have got <ItemsControl ItemsSource="{Binding SelectedData.MyPairs}"/>I and changed the binding of the DataTemplate to Description and Value (removed MyValuePair.). What I also do not understand is what do I use as my target type in my DataTemplate when I am not using MyValuePairViewModel ?
Now I used inside my DataTemplate just the model m:MyValuePair and it works but I am still creating instances of MyValuePairViewModel for every ValuePair inside SelectedData.MyPairs. Although it works I am not sure if it is the way you suggested to bind it directly to the UI.
But I think I like it more to bind it to a viewModel (model stays clean) but what I do not like is the binding of the DataTemplate is working with the model m:MyValuePair but not with the viewModel vm:MyValuePairViewModel (I used Description as well as ValuePair.Description). Why? Is it because MyPairs is of type MyValuePair and not MyValuePairViewModel?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.