C# 使用ObservableCollection的自定义控件绑定

C# 使用ObservableCollection的自定义控件绑定,c#,.net,C#,.net,我有自定义控件MyGrid public class MyGrid : Canvas { //... ObservableCollection<object> items = new ObservableCollection<object>(); public ObservableCollection<object> Items { get { return items; } set {

我有自定义控件MyGrid

public class MyGrid : Canvas
{
//...
    ObservableCollection<object> items = new ObservableCollection<object>();
    public ObservableCollection<object> Items
    {
        get { return items; }
        set { 
            items = value;
            UpdateValues();
            UpdateGrid();
        }
    }
    //..
}
公共类MyGrid:Canvas { //... ObservableCollection items=新的ObservableCollection(); 公共可观测收集项目 { 获取{返回项;} 集合{ 项目=价值; updateValue(); UpdateGrid(); } } //.. } 我希望项目可以从XAML代码绑定:

<local:MyGrid Items="{Binding Numbers}" />

其中数字是ObservableCollection(这很好,我可以使用它绑定到默认控件)。
我曾尝试将项定义为DependencyProperty,但它是静态的,我需要在具有不同数据源的页面上使用多个控件,因此使用静态项将不起作用。上面的代码也不起作用。InitializeComponent()引发异常:未能分配到属性“App.MyGrid.Items”。[第27行位置:114]。我怎样才能让它工作呢?

也许这会对你有所帮助

<ItemsControl ItemsSource="{Binding items}">
     <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Margin="0,5,5,0" VerticalAlignment="Top">
                        <TextBlock TextAlignment="Right" FontWeight="Bold" Text="{Binding yourVariable}" Height="16"/>
                        <TextBlock TextAlignment="Right" Text="{Binding yourVariable1}" FontSize="26"/>
                        <TextBlock TextAlignment="Right" Text="{Binding yourVariable2}" FontSize="10" Foreground="DarkGray"/>
                    </StackPanel>                        
                </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.Template>
            <ControlTemplate>
                <ScrollViewer Padding="{TemplateBinding Padding}" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
                    <ItemsPresenter />
                </ScrollViewer>
            </ControlTemplate>
        </ItemsControl.Template>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel>
                </WrapPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>


这可能不是你真正想要的。但我认为这可以帮助您。

随着MyGrid从Canvas扩展而来(Canvas也是一个DependencyObject),您可以在MyGrid中实现Dependency属性

<ItemsControl ItemsSource="{Binding items}">
     <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Margin="0,5,5,0" VerticalAlignment="Top">
                        <TextBlock TextAlignment="Right" FontWeight="Bold" Text="{Binding yourVariable}" Height="16"/>
                        <TextBlock TextAlignment="Right" Text="{Binding yourVariable1}" FontSize="26"/>
                        <TextBlock TextAlignment="Right" Text="{Binding yourVariable2}" FontSize="10" Foreground="DarkGray"/>
                    </StackPanel>                        
                </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.Template>
            <ControlTemplate>
                <ScrollViewer Padding="{TemplateBinding Padding}" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
                    <ItemsPresenter />
                </ScrollViewer>
            </ControlTemplate>
        </ItemsControl.Template>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel>
                </WrapPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
然后还可以使用
PropertyChangedCallback
实现它,这将允许您注册/取消注册事件本身,然后您可以在其中更新网格/值

因此,您可以这样更改MyGrid:

public class MyGrid : Canvas
{
    protected static PropertyChangedCallback ItemsPropertyChangedCallback = new PropertyChangedCallback(ItemsPropertyChanged);

    public static DependencyProperty ItemsProperty = DependencyProperty.RegisterAttached("Items", typeof(INotifyCollectionChanged), typeof(MyGrid), new PropertyMetadata(null, ItemsPropertyChangedCallback));

    private static void ItemsPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        MyGrid thisGrid = (MyGrid)sender;
        if (thisGrid == null)
        {
            return;
        }
        thisGrid.UnregisterItems(e.OldValue as INotifyCollectionChanged);
        thisGrid.RegisterItems(e.NewValue as INotifyCollectionChanged);
        thisGrid.Refresh();
    }

    public INotifyCollectionChanged Items 
    { 
        get
        {
            return (INotifyCollectionChanged)GetValue(ItemsProperty);
        }
        set
        {
            SetValue(ItemsProperty, value);
        }
    }

    protected void UnregisterItems(INotifyCollectionChanged items)
    {
        if (items == null)
        {
            return;
        }
        items.CollectionChanged -= ItemsChanged;
    }

    protected void RegisterItems(INotifyCollectionChanged items)
    {
        if (items == null)
        {
            return;
        }
        items.CollectionChanged += ItemsChanged;
    }

    protected virtual void UpdateValues()
    {
        System.Diagnostics.Debug.WriteLine("Updating values");
    }

    protected virtual void UpdateGrid()
    {
        System.Diagnostics.Debug.WriteLine("Updating grid");
    }

    public void Refresh()
    {
        UpdateValues();
        UpdateGrid();
    }

    protected virtual void ItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        Refresh();
    }

    public MyGrid()
    {
    }
}
在Xaml中,您可以稍后绑定到Items属性。当使用另一个集合更改Items属性时,它将从上一个对象(如果有)的已更改事件中注销,然后注册到新对象(如果有)。之后,它将调用类的Refresh方法(然后调用UpdateValues/UpdateGrid方法)

我也部分同意@user3248647的观点,如果可以的话,你应该利用绑定和ContentTemplates,但是如果你不能使用它们,你可以让你的dependencProperty至少做出这样的反应


是的,DependencyProperty在类上是静态的,但是属性本身总是在类内部实现的。使用PropertyChangedCallback时,只需将发送者强制转换回“MyGrid”,然后您就可以更改实例成员:)

Items集合声明为属性,但对于绑定,您需要将其声明为公共属性。希望这能帮到你:谢谢你的反馈,我发布了错误的代码,我使用的是公共财产。邮政编码updated@Icepickle我没弄错你的想法吧?编辑的代码如下:public readonly ObservableCollection items=new ObservableCollection();公共ObservableCollection项{get{return Items;}set{}}仍然不起作用。@EricTroy,没关系,我错了:D