【问题标题】:Cannot bind ItemsSource to ElementName无法将 ItemsSource 绑定到 ElementName
【发布时间】:2017-02-02 16:00:52
【问题描述】:

这可能是一个愚蠢的问题,但我不能用 ItemsSource 做一个简单的例子。我的 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>

代码:

/// <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; }
}

如果我在代码中设置 ItemsSource,或者如果我设置 DataContext=this 然后从绑定中删除 ElementName,它会起作用。但为什么它不适用于 ElementName? 我可以像这样绑定背景颜色,但不能绑定集合。

我知道如何使其与 DataContext 或代码隐藏一起工作,但我对为什么这个特定示例不起作用感兴趣,我是否遗漏了什么?

【问题讨论】:

    标签: c# wpf binding


    【解决方案1】:

    我知道如何使其与 DataContext 或代码隐藏一起工作,但我对为什么这个特定示例不起作用感兴趣,我是否遗漏了什么?

    正如@Clemens 所指出的,如果您在调用 InitializeComponent() 之前填充源集合,那么您的绑定实际上会起作用。

    在后面的代码中设置 DataContext 和在 XAML 中创建 ElementName Binding 的区别在于后者已经在 InitializeComponent 调用期间建立。稍后添加到未实现 INotifyCollectionChanged 的集合中的项目将被忽略。

    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();
    }
    

    您也可以使用 RelativeSource 绑定到父窗口,这可行:

    <ListBox ItemsSource="{Binding MyItems, RelativeSource={RelativeSource AncestorType=Window}}"/>
    

    【讨论】:

    • “因为“MyItems”不是在 XAML 标记中声明根元素的基本类型 Window 的属性。”这个解释是错误的。真正的问题只是时间问题。如果在调用InitializeComponent 之前填充items 集合,则ElementName Binding 效果很好。
    • 我不认为他绑定MyItems,而不是items,并且他没有在MyItems 中进行设置以将其链接到items
    • 即使使用 RelativeSource 绑定,它也只是偶然起作用。最好始终使用 ObservableCollection。
    • @Safe 你没有注意到问题中的MyItems getter 吗?
    • @Clemens 啊,是的,我的错我是这个答案的第一个版本 xD
    【解决方案2】:

    您在初始化窗口后设置了MyItems,并且由于您的示例中的MyItems 没有实现setter 和INotifyPropertyChanged 方式,因此您对集合所做的更改永远不会警告您的UI 线程。

    您可以设置以下两种解决方案:

    解决方案 1

    直接在 get 中实例化您的 Observable 集合:

    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;
            }
        }
    

    解决方案 2

    使用INotifyPropertyChanged接口:

    这样,当您设置项目时,您的 UI 线程就知道 xaml 部分需要进行更改

    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" });
        }
    
    ....
    

    【讨论】:

    • 您的第一个“解决方案”给出了一个非常糟糕的建议。你不应该那样做。
    • 是的,在某种程度上我也不喜欢第二种解决方案,我只是举个简单的例子,第一种解决方案应该更像@mm8 答案。
    • 不,它做了一些非常不同的事情。每次调用属性 getter 时,它都会创建一个新集合。你应该避免这种情况。
    猜你喜欢
    • 2011-08-23
    • 2011-02-06
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-02
    • 2017-01-05
    相关资源
    最近更新 更多