Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为图表用户控件创建项目DP_C#_Wpf_Binding_Observablecollection_Dependency Properties - Fatal编程技术网

C# 为图表用户控件创建项目DP

C# 为图表用户控件创建项目DP,c#,wpf,binding,observablecollection,dependency-properties,C#,Wpf,Binding,Observablecollection,Dependency Properties,我正忙于创建一个具有一些基本图表/图形功能的用户控件。本质上,我想要一个Items依赖属性,控件的用户可以绑定到该属性。然后,控件将显示对源所做的所有项目和更新 到目前为止,我所做的是在我的用户控件中创建一个Items DP,代码隐藏 public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection&l

我正忙于创建一个具有一些基本图表/图形功能的用户控件。本质上,我想要一个Items依赖属性,控件的用户可以绑定到该属性。然后,控件将显示对源所做的所有项目和更新

到目前为止,我所做的是在我的用户控件中创建一个Items DP,代码隐藏

public static readonly DependencyProperty ItemsProperty =
    DependencyProperty.Register("Items",
    typeof(ObservableCollection<Polyline>),
    typeof(RPGraph),
    new FrameworkPropertyMetadata(
    new ObservableCollection<Polyline>(),
    new PropertyChangedCallback(OnItemsChanged)));

public ObservableCollection<Polyline> Items 
{
    get { return (ObservableCollection<Polyline>)GetValue(ItemsProperty); }
    set { SetValue(ItemsProperty, value);  }
}

public static void OnItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
我的第一个绊脚石是,当我的收藏发生变化时,OnItemChanged没有接到电话。几个小时后,我发现了一个解释原因的帖子。按照这个建议解决了我问题的一部分。现在,我可以向ObservableCollection列表中添加新项目多段线。但是,如果我在多段线中添加了一个额外的点或修改了一个点,该怎么办。通过对前面问题的了解,我找到了要点。更改了事件。然后我订阅了它,并将更新代码放在那里

这最终是可行的,但必须有一个更好或更优雅的方法来实现这一点,正如上面所说的,我认为这一切归结为不使用ObservableCollection?有什么建议吗

下面是代码草案的工作方法,我只是想让它工作:-:

    public static void OnItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {     
        var thisControl = d as RPGraph;

        foreach (Polyline poly in thisControl.Items)
            thisControl.manager.Items.Add(poly.Points.ToArray());

        if (e.OldValue != null)
        {
            var coll = (INotifyCollectionChanged)e.OldValue;

            // Unsubscribe from CollectionChanged on the old collection
            coll.CollectionChanged -= Items_CollectionChanged;
        }

        if (e.NewValue != null)
        {
            var coll = (ObservableCollection<Polyline>)e.NewValue;

            // Subscribe to CollectionChanged on the new collection
            coll.CollectionChanged += (o, t) => {
                ObservableCollection<Polyline> items = o as ObservableCollection<Polyline>;

                thisControl.manager.Items.Add(items[t.NewStartingIndex].Points.ToArray());

                foreach (Polyline poly in items)
                {
                    poly.Points.Changed += (n, m) => {

                        for (int i = 0; i < thisControl.manager.Items.Count; i++)
                            thisControl.manager.Items[i] = thisControl.Items[i].Points.ToArray();

                        thisControl.manager.DrawGraph(thisControl.graphView);
                    };
                }
                thisControl.manager.DrawGraph(thisControl.graphView); 
            };
        }
        thisControl.manager.DrawGraph(thisControl.graphView);  
    }

您完全正确,ObservableCollection不会在其任何项更改其属性值时发出通知

您可以扩展ObservableCollection的功能,为这些情况添加通知

可能是这样的:

public sealed class ObservableNotifiableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
    public event ItemPropertyChangedEventHandler ItemPropertyChanged;
    public event EventHandler CollectionCleared;

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
    {
        base.OnCollectionChanged(args);

        if (args.NewItems != null)
        {
            foreach (INotifyPropertyChanged item in args.NewItems)
            {
                item.PropertyChanged += this.OnItemPropertyChanged;
            }
        }

        if (args.OldItems != null)
        {
            foreach (INotifyPropertyChanged item in args.OldItems)
            {
                item.PropertyChanged -= this.OnItemPropertyChanged;
            }
        }
    }

    protected override void ClearItems()
    {
        foreach (INotifyPropertyChanged item in this.Items)
        {
            item.PropertyChanged -= this.OnItemPropertyChanged;
        }

        base.ClearItems();
        this.OnCollectionCleared();
    }

    private void OnCollectionCleared()
    {
        EventHandler eventHandler = this.CollectionCleared;
        if (eventHandler != null)
        {
            eventHandler(this, EventArgs.Empty);
        }
    }

    private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        ItemPropertyChangedEventHandler eventHandler = this.ItemPropertyChanged;
        if (eventHandler != null)
        {
            eventHandler(this, new ItemPropertyChangedEventArgs(sender, args.PropertyName));
        }
    }
}

然后,您可以订阅ItemPropertyChanged事件并完成您的工作。

AFAIK没有其他方法可以完成此操作。您必须在要监视的层次结构的每个级别上使用事件处理程序。例如,监视列表以检测插入和删除,然后监视列表中的每个元素以检测属性何时更改,等等。如您所述,这会导致复杂的冗长代码。这看起来是一种更好的方法,并提供了一个可重用的解决方案。在根据这个解决方案搜索了更多内容之后,我注意到我的问题已经有了一些答案,比如这个,它解释了为什么需要覆盖ClearItems等。谢谢,它打开了我的新世界。关于上面的代码,这只适用于实现INotifyPropertChanged right的对象?@Avalan,是的,完全正确。这就是为什么您会看到T:INotifyPropertyChanged的位置。您只需要PropertyChanged事件。或者,您可以更新此代码并使用duck类型,而不需要明确的接口。很好!我刚刚学到了一些新东西,这里有一个很好的解释:谢谢,我现在正在实现一段整洁的代码!