5

にバインドされたDependencyProperty型を持つカスタム コントロールがあります。ObservableCollectionobservableCollection

<MyControl MyCollectionProperty = {Binding MyObservableCollection} ...

に追加しMyObservableCollectionても更新されないという問題がありMyCollectionPropertyます。

MyObservableCollectionそれを機能させるには、完全に置き換える必要があります。

MyObservableCollection = null;
MyObservableCollection = new ObservableCollection(){...}

これに対処するより良い方法はありますか?

編集:

    public ObservableCollection<string> Columns
    {
        get { return (ObservableCollection<string>)GetValue(ColumnsProperty); }
        set { SetValue(ColumnsProperty, value); }
    }

    public static readonly DependencyProperty ColumnsProperty =
        DependencyProperty.Register("Columns", typeof(ObservableCollection<string>), typeof(MyControl),
                                    new PropertyMetadata(new ObservableCollection<string>(), OnChanged));
4

2 に答える 2

18

In addition to what grantz has answered, I would suggest to declare the property with type IEnumerable<string> and check at runtime if the collection object implements the INotifyCollectionChanged interface. This provides greater flexibility as to which concrete collection implementation may be used as property value. A user may then decide to have their own specialized implementation of an observable collection.

Note also that in the ColumnsPropertyChanged callback the CollectionChanged event handler is attached to the new collection, but also removed from the old one.

public static readonly DependencyProperty ColumnsProperty =
    DependencyProperty.Register(
        "Columns", typeof(IEnumerable<string>), typeof(MyControl),
        new PropertyMetadata(null, ColumnsPropertyChanged));

public IEnumerable<string> Columns
{
    get { return (IEnumerable<string>)GetValue(ColumnsProperty); }
    set { SetValue(ColumnsProperty, value); }
}

private static void ColumnsPropertyChanged(
    DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    var control= (MyControl)obj;
    var oldCollection = e.OldValue as INotifyCollectionChanged;
    var newCollection = e.NewValue as INotifyCollectionChanged;

    if (oldCollection != null)
    {
        oldCollection.CollectionChanged -= control.ColumnsCollectionChanged;
    }

    if (newCollection != null)
    {
        newCollection.CollectionChanged += control.ColumnsCollectionChanged;
    }

    control.UpdateColumns();
}

private void ColumnsCollectionChanged(
    object sender, NotifyCollectionChangedEventArgs e)
{
    // optionally take e.Action into account
    UpdateColumns();
}

private void UpdateColumns()
{
    ...
}
于 2013-02-22T11:56:43.143 に答える
6

以下は、役立つ可能性のある実際の例です。

この例では、メソッド OnChanged がすぐに呼び出され、[追加] ボタンがクリックされると、"Changed" がコンソールに書き込まれます。

制御

public class MyControl : Control
{

    public ObservableCollection<string> ExtraColumns
    {
        get { return (ObservableCollection<string>)GetValue(ExtraColumnsProperty); }
        set { SetValue(ExtraColumnsProperty, value); }
    }

    public static readonly DependencyProperty ExtraColumnsProperty =
        DependencyProperty.Register("ExtraColumns", typeof(ObservableCollection<string>), typeof(MyControl),
                                    new PropertyMetadata(new ObservableCollection<string>(), OnChanged));

    static void OnChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        (sender as MyControl).OnChanged();

    }

    void OnChanged()
    {
        if ( ExtraColumns != null )
            ExtraColumns.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ExtraColumns_CollectionChanged);
    }

    void ExtraColumns_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        Console.WriteLine("Changed");    
    }
}

<Window x:Class="WpfApplication18.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication18"
    Title="MainWindow" Height="350" Width="525">
  <StackPanel>
    <local:MyControl ExtraColumns="{Binding Extras}"/>
    <Button Click="Button_Click">Add</Button>
  </StackPanel>
</Window>

ウィンドウ コード ビハインド

public partial class MainWindow : Window
{
    private ObservableCollection<string> _extras = new ObservableCollection<string>( );
    public ObservableCollection<string> Extras
    {
        get { return _extras; }
        set
        {
            if (value != _extras)
            {
                _extras = value;
            }
        }
    }


    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Extras.Add("Additional");
    }
}
于 2013-02-22T03:55:35.037 に答える