简体   繁体   中英

How to assign ItemsSource property

Okay, sorry for my previous mess.

The situation is this: I have two custom objects defined as follows: 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 which for simplicity defines some data using those two objects as follows: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 the ItemsSource of the MainObjectIC in the code behind like so:

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

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

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

From what I understood it may be a very very bad and useless to assign the ItemsSource property from the code behind.

Thank you for improving your code example. It still wasn't quite complete, but it's close enough to be able to provide an answer.

In your example, the main thing missing is to simply add the necessary {Binding} expression. 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 item already is an object of type MainObject (which is why your TextBlock binding works). So all that remains to be done is bind the ItemsSource property to the MainObject.SubObjectData property.

(I had to add the Grid.Column assignment, which appeared to be missing from your example above.)

The above change is completely sufficient to get your example to work as you desire. However, you can also improve the code by using the same basic approach to the top-level control as well. To do so, your VM.VMList field needs to be changed to be 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 just remove the explicit assignment that's in your constructor:

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

With those changes, your XAML no longer needs to give any of the controls names, and you can bind directly to the relevant 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>

A key point that might not be obvious in the above is that every control has a DataContext . When you using the {Binding} syntax, by default the property path is relative to that context. In the top-level control, the context is what you set it to be in the constructor. But in the individual list item template, the context is the individual data object for that list item, which in your case is the MainObject object. So in that context, you just bind to the SubObjectData property, just like you bind to the MainObjectName . It works exactly the same, and for the same reason.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM