How to assign the ItemsSource property

Ok, sorry for my previous mess.

The situation is like this: I have two custom objects that are defined like this MainObject:

public class MainObject
{
    private string mainObjectName;
    public string MainObjectName { get { return mainObjectName; } }

    private List<SubObject> subObjectData;
    public List<SubObject> SubObjectData { get { return subObjectData; } }

    public MainObject(string name, List<SubObject> objectData)
    {
        mainObjectName = name;
        subObjectData = objectData;
    }
}

      

subobject:

public class SubObject
{
    private string subObjectName;
    public string SubObjectName { get { return subObjectName; } }

    private List<int> integerData;
    public List<int> IntegerData { get { return integerData; } }

    public SubObject(string name, List<int> data)
    {
        subObjectName = name;
        integerData = data;
    }
}

      

I also have a viewmodel that for simplicity defines some data using these two objects like this: VM

public List<Model.MainObject> VMList = new List<Model.MainObject>()
    {
        new Model.MainObject("MainItem1", new List<Model.SubObject>()
        {
            new Model.SubObject("SubItem1", new List<int>() { 1,6,3}),
            new Model.SubObject("SubItem2", new List<int>() { 5,2,9})
        }),
        new Model.MainObject("MainItem2", new List<Model.SubObject>()
        {
            new Model.SubObject("SubItem1", new List<int>() { 0,3,1}),
            new Model.SubObject("SubItem2", new List<int>() { 7,5,2})
        })
    };

      

now i have the following UI

<Grid>
    <ItemsControl Name="MainObjectIC">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding MainObjectName}"/>
                    <ItemsControl Name="SubObjectIC">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding SubObjectName}"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

      

I assign ItemsSource MainObjectIC in code like so:

ViewModel.VM dc = new ViewModel.VM();

    public MainWindow()
    {
        InitializeComponent();
        DataContext = dc;
        MainObjectIC.ItemsSource = dc.VMList;
    }

      

I also want to assign ItemsSource to SubObjectIC, but for that I have to get ItemsControl object. And this is what I am trying to achieve.

From what I understood, it can be very bad and useless to assign the ItemsSource property from code.

+3


source to share


1 answer


Thanks for improving the code. It was still incomplete, but close enough to provide an answer.

In your example, the main thing missing is just add the required expression {Binding}

. In particular:

<ItemsControl Name="SubObjectIC" Grid.Column="1"
              ItemsSource="{Binding SubObjectData}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding SubObjectName}"/>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

      

The context for the element is already a type object MainObject

(which is why your binding TextBlock

works). Thus, all that remains to be done is to bind a property ItemsSource

to a property MainObject.SubObjectData

.

(I had to add an assignment Grid.Column

, which turns out to be missing from your example above.)

The above change is enough to make your example work as you wish. However, you can also improve your code by using the same basic top-level management approach. To do this, the field VM.VMList

must be changed as a property (WPF only binds to properties, not fields):



class VM
{
    public List<MainObject> VMList {  get { return _vmList; } }

    private readonly List<MainObject> _vmList = new List<MainObject>()
    {
        new MainObject("MainItem1", new List<SubObject>()
        {
            new SubObject("SubItem1", new List<int>() { 1,6,3}),
            new SubObject("SubItem2", new List<int>() { 5,2,9})
        }),
        new MainObject("MainItem2", new List<SubObject>()
        {
            new SubObject("SubItem1", new List<int>() { 0,3,1}),
            new SubObject("SubItem2", new List<int>() { 7,5,2})
        })
    };
}

      

Then you can simply remove the explicit assignment that you have in your constructor:

public MainWindow()
{
    InitializeComponent();
    DataContext = dc;
}

      

With these changes, your XAML should no longer give any of the control names, and you can directly bind to the corresponding properties:

<Window x:Class="TestSO42929995WpfNestedData.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestSO42929995WpfNestedData"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <ItemsControl ItemsSource="{Binding VMList}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition/>
              <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="{Binding MainObjectName}"/>
            <ItemsControl Grid.Column="1"
                          ItemsSource="{Binding SubObjectData}">
              <ItemsControl.ItemTemplate>
                <DataTemplate>
                  <TextBlock Text="{Binding SubObjectName}"/>
                </DataTemplate>
              </ItemsControl.ItemTemplate>
            </ItemsControl>
          </Grid>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
  </Grid>
</Window>

      

The key point, which may not be obvious in the above, is what each control has DataContext

. When you use the syntax {Binding}

, by default the property path is relative to this context. In a top-level control, the context is what you set in the designer. But in an individual item template, the context is a separate data object for that list item, which in your case is an object MainObject

. So in this context, you are simply binding to a property SubObjectData

, just like you are binding to MainObjectName

. It works exactly the same and for the same reason.

+2


source







All Articles