C# 绑定可观察集合<;int>;到IEnumerable<;对象>;自定义控件的

C# 绑定可观察集合<;int>;到IEnumerable<;对象>;自定义控件的,c#,wpf,data-binding,collections,C#,Wpf,Data Binding,Collections,在一些帮助下,我最近在自定义控件工作中创建了绑定集合。然而,令我惊讶的是,有人告诉我,为了使自定义控件属性更灵活(可以与其他参数化集合绑定),我需要将自定义控件的属性设置为IEnumerable类型,因为协方差和逆变。然而,这似乎对我不起作用 这是控件的视图 <UserControl x:Class="BadaniaOperacyjne.Controls.Matrix" mc:Ignorable="d" Name="CustomMatrix"

在一些帮助下,我最近在自定义控件工作中创建了绑定集合。然而,令我惊讶的是,有人告诉我,为了使自定义控件属性更灵活(可以与其他参数化集合绑定),我需要将自定义控件的属性设置为
IEnumerable
类型,因为协方差和逆变。然而,这似乎对我不起作用

这是控件的视图

<UserControl x:Class="BadaniaOperacyjne.Controls.Matrix"
            mc:Ignorable="d" Name="CustomMatrix"
            d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
            <!-- ... -->
        <Grid Grid.Row="2" Grid.Column="1" Name="contentGrid">
            <ListBox ItemsSource="{Binding ElementName=CustomMatrix, Path=ItemsList}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Grid>
</UserControl>

它的代码就在这里

#region ItemsList Property
public static readonly DependencyProperty ItemsListProperty =
    DependencyProperty.Register("ItemsList", typeof(IEnumerable<object>), typeof(Matrix), new PropertyMetadata(new PropertyChangedCallback(ItemsListChanged)));
public IEnumerable<object> ItemsList
{
    get { return GetValue(ItemsListProperty) as IEnumerable<object>; }
    set { SetValue(ItemsListProperty, value); }
}
private void ItemsListChanged(object value)
{
    System.Diagnostics.Debug.WriteLine("matrix: items list changed " + value);
    if (ItemsList != null)
    {
        //ItemsList.CollectionChanged += ItemsList_CollectionChanged;
        System.Diagnostics.Debug.WriteLine("got " + string.Join(",", ItemsList.ToList()));
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("got null");
    }
}

void ItemsList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    System.Diagnostics.Debug.WriteLine("matrix: current items list collection changed");
}
private static void ItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ((Matrix)d).ItemsListChanged(e.NewValue);
}
#endregion
#区域项目列表属性
公共静态只读从属属性ItemsListProperty=
DependencyProperty.Register(“ItemsList”、typeof(IEnumerable)、typeof(Matrix)、new PropertyMetadata(new PropertyChangedCallback(ItemsListChanged));
公共IEnumerable项目列表
{
获取{返回GetValue(ItemsListProperty)作为IEnumerable;}
set{SetValue(ItemsListProperty,value);}
}
私有void ItemsListChanged(对象值)
{
System.Diagnostics.Debug.WriteLine(“矩阵:项目列表已更改”+值);
if(ItemsList!=null)
{
//ItemsList.CollectionChanged+=ItemsList\u CollectionChanged;
System.Diagnostics.Debug.WriteLine(“got”+string.Join(“,”,ItemsList.ToList());
}
其他的
{
System.Diagnostics.Debug.WriteLine(“获取空值”);
}
}
void ItemsList\u CollectionChanged(对象发送方,System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(“矩阵:当前项列表集合已更改”);
}
私有静态无效项列表已更改(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
((矩阵)d).项目已更改(如新值);
}
#端区
使用该控件的窗口如下所示

<custom:Matrix x:Name="customMatrix" DockPanel.Dock="Top" Title="{Binding Title}" ItemsList="{Binding Items}"/>

代码隐藏在后面

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public ViewModel()
    {
        Items = new ObservableCollection<int> { 1, 2, 3, 4, 5, 6};

        Items.CollectionChanged += Items_CollectionChanged;
    }

    void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("problem manager: items list changed " + e.NewItems.Count);
    }

    public ObservableCollection<int> Items { get; private set; }

    protected string title;
    public string Title
    {
        get { return title; }
        set
        {
            if (title != value)
            {
                title = value;
                NotifyPropertyChanged("Title");
            }
        }
    }

    protected void NotifyPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

public ViewModel VM { get; private set; }

// the window's constructor
private ProblemManager()
{
    VM = new ViewModel();

    DataContext = VM;
    InitializeComponent();

    VM.Title = "title";
}

private int i = 0;
private void btnAddRow_Click(object sender, RoutedEventArgs e)
{
    //VM.Items.Add(++i);
    VM.Items[2] = 112;
    //customMatrix.ItemsList = new ObservableCollection<object> { 1, 2, 3 };
    //customMatrix.ItemsList.Add(66);
    //////
    VM.Title = (++i).ToString();
}
公共类视图模型:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
公共视图模型()
{
项目=新的可观察集合{1,2,3,4,5,6};
Items.CollectionChanged+=Items\u CollectionChanged;
}
无效项\u CollectionChanged(对象发送方,System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(“问题管理器:项目列表已更改”+e.NewItems.Count);
}
公共可观测集合项{get;private set;}
受保护的字符串标题;
公共字符串标题
{
获取{返回标题;}
设置
{
如果(标题!=值)
{
标题=价值;
通知财产变更(“所有权”);
}
}
}
受保护的void NotifyPropertyChanged(字符串名称)
{
PropertyChangedEventHandler处理程序=PropertyChanged;
if(处理程序!=null)
{
处理程序(此,新PropertyChangedEventArgs(名称));
}
}
}
公共视图模型VM{get;private set;}
//窗口的构造函数
私人问题经理()
{
VM=新的ViewModel();
DataContext=VM;
初始化组件();
VM.Title=“Title”;
}
私有整数i=0;
私有无效btnAddRow_单击(对象发送者,路由目标e)
{
//VM.Items.Add(++i);
VM.Items[2]=112;
//customMatrix.ItemsList=新的ObservableCollection{1,2,3};
//customMatrix.ItemsList.Add(66);
//////
VM.Title=(++i.ToString();
}
当我将
ItemsList
控件的
dependencProperty
更改为
observedcollection
或至少
observedcollection
时,它工作正常。
真的有可能吗?如果是这样,那么是我犯的错误吗?

协方差
允许用于
IEnumerable
,但我只检查了它的
允许用于引用类型,而不允许用于值类型(例如int)

如果您使用
ObservableCollection
绑定,您的版本将正常工作,因为
字符串是引用类型

因此,您可以使用
IEnumerable(非泛型版本)
作为DP的返回类型,如下所示,以便它也适用于值类型:

public static readonly DependencyProperty ItemsListProperty =
    DependencyProperty.Register("ItemsList", typeof(IEnumerable), typeof(Matrix),
        new PropertyMetadata(new PropertyChangedCallback(ItemsListChanged)));

public IEnumerable ItemsList
{
    get { return (IEnumerable)GetValue(ItemsListProperty); }
    set { SetValue(ItemsListProperty, value); }
}

协方差
允许用于
IEnumerable
,但我只检查了它的
允许用于引用类型,而不允许用于值类型(例如int)

如果您使用
ObservableCollection
绑定,您的版本将正常工作,因为
字符串是引用类型

因此,您可以使用
IEnumerable(非泛型版本)
作为DP的返回类型,如下所示,以便它也适用于值类型:

public static readonly DependencyProperty ItemsListProperty =
    DependencyProperty.Register("ItemsList", typeof(IEnumerable), typeof(Matrix),
        new PropertyMetadata(new PropertyChangedCallback(ItemsListChanged)));

public IEnumerable ItemsList
{
    get { return (IEnumerable)GetValue(ItemsListProperty); }
    set { SetValue(ItemsListProperty, value); }
}

没有调用
ItemsListChanged
吗?删除所有代码并使用
ItemsControl
。不要试图重新发明轮子。它已经被发明了,但没有。这对我来说很奇怪,当然需要一个解释或者指出我的错误是什么@HighCore我并不是在尝试实现一个列表,它是更复杂的事情的开始。然而,在解决将集合绑定到自定义控件的问题时,有人告诉我,这是使集合绑定更灵活的方法,我无法做到这一点。@patryk很抱歉,你说的是
自定义控件
,但你在这里创建的是
用户控件
。从概念上和从代码/XAML的角度来看,这是两件截然不同的事情。再次使用ItemsControl。不要试图重新发明轮子。微软已经做到了这一点,而且他们比我们任何人都清楚。你说得有道理——到目前为止,我还没有发现这两者之间的区别,谢谢。再说一次,如果我的目的是列出一个简单的收藏,我不会在这里寻求帮助。我不再重新发明任何类型的轮子:)不会调用
ItemsListChanged
吗?删除所有代码并使用
ItemsControl