Cannot bind ItemsSource to ElementName

2019-09-14 01:28发布

This is probably a silly question, but I can't make a simple example with ItemsSource work. My XAML:

<Window x:Class="TestDataGrid.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:TestDataGrid"
        mc:Ignorable="d" Height="250" Width="300" Name="MyWindow">
    <ListBox ItemsSource="{Binding MyItems, ElementName=MyWindow}" Background="{Binding MyBrush, ElementName=MyWindow}"/>
</Window>

Code:

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private ObservableCollection<Item> items;

    public MainWindow()
    {
        InitializeComponent();
        items = new ObservableCollection<Item>();
        items.Add(new Item { Key = "Key1", Value = "Value1" });
        items.Add(new Item { Key = "Key2", Value = "Value2" });
        items.Add(new Item { Key = "Key3", Value = "Value3" });
    }

    public ObservableCollection<Item> MyItems
    {
        get { return items; }
    }

    public Brush MyBrush
    {
        get { return Brushes.LightPink; }
    }
}

public class Item
{
    public string Key { get; set; }
    public string Value { get; set; }
}

It works if I set ItemsSource in code, or if I set DataContext=this and then remove ElementName from my binding. But why doesn't it work with ElementName? I can bind the background color like this, but not collection.

I know how to make it work with DataContext, or in code-behind, but I'm interested in why this particular example doesn't work, am I missing something?

标签: c# wpf binding
2条回答
淡お忘
2楼-- · 2019-09-14 01:44

I know how to make it work with DataContext, or in code-behind, but I'm interested in why this particular example doesn't work, am I missing something?

As pointed out by @Clemens your binding will actually work if you populate the source collection before the InitializeComponent() is called.

The difference between setting a DataContext in code behind and creating an ElementName Binding in XAML is that the latter is already established during the InitializeComponent call. Items that are added later - to a collection that does not implement INotifyCollectionChanged - are ignored.

public MainWindow()
{
    items = new ObservableCollection<Item>();
    items.Add(new Item { Key = "Key1", Value = "Value1" });
    items.Add(new Item { Key = "Key2", Value = "Value2" });
    items.Add(new Item { Key = "Key3", Value = "Value3" });
    InitializeComponent();
}

You could also bind to the parent window using a RelativeSource though, This works:

<ListBox ItemsSource="{Binding MyItems, RelativeSource={RelativeSource AncestorType=Window}}"/>
查看更多
Animai°情兽
3楼-- · 2019-09-14 01:44

You set your MyItems after initializing your windows and since MyItems in your example doesn't implement a setter and the INotifyPropertyChanged manner, your UI thread is never warn by the change you made on your collection.

Here two solutions you can set up:

SOLUTION 1

Instantiate your Observable collection directly in the get:

public ObservableCollection<Item> MyItems {
        get {
            var _items = new ObservableCollection<Item>();
            _items.Add(new Item { Key = "Key1", Value = "Value1" });
            _items.Add(new Item { Key = "Key2", Value = "Value2" });
            _items.Add(new Item { Key = "Key3", Value = "Value3" });
            return _items;
        }
    }

SOLUTION 2

Use INotifyPropertyChanged interface:

Like that when you set your items, Your UI thread knows there is a change to do in the xaml part

public partial class MainWindow : Window, INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    private ObservableCollection<Item> _items;
    public ObservableCollection<Item> MyItems {
        get { return _items; }
        set {
            _items = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(MyItems)));
        }
    }

    public MainWindow () {
        InitializeComponent();
        MyItems = new ObservableCollection<Item>();
        MyItems.Add(new Item { Key = "Key1", Value = "Value1" });
        MyItems.Add(new Item { Key = "Key2", Value = "Value2" });
        MyItems.Add(new Item { Key = "Key3", Value = "Value3" });
    }

....
查看更多
登录 后发表回答