1

基本のDependencyObjectクラスがあります。ここでは、オブジェクトを受け取り、プロパティを取得し、INotifyPropertyChangedを実装する型である各プロパティに対して、新しいPropertyChangedEventHandlerを追加します。これでハンドラメソッドでは、オブジェクト "sender"とPropertyChangedEventArgs "e"のパラメータを取得します。私の質問は、差出人がINotifyPropertyChangedを実装する型のプロパティである場合、だれでも動的にプロパティ名を取得する方法を知っているかということです。

これが私が取り組んでいるものです:

public class BaseDependencyObject : DependencyObject, INotifyPropertyChanged
{
    public BaseDependencyObject()
    {

    }

    protected void SetValues(Object thisObject, Object entity)
    {
        try
        {
            PropertyInfo[] properties = entity.GetType().GetProperties();
            foreach (PropertyInfo property in properties)
            {
                var value = property.GetValue(entity, null);
                var valueIsEntity = value is System.ServiceModel.DomainServices.Client.Entity;
                var thisObjectsProperty = thisObject.GetType().GetProperty(property.Name);

                if (thisObjectsProperty != null && value != null)
                {
                    if (valueIsEntity)
                    {
                        if (thisObjectsProperty.PropertyType.GetInterface("INotifyPropertyChanged", true) != null)
                        {
                            var propertyInstance = Activator.CreateInstance(thisObjectsProperty.PropertyType);

                            ((INotifyPropertyChanged)propertyInstance).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
                        }

                        SetValues(thisObjectsProperty, value);
                    }

                    else if (thisObjectsProperty.PropertyType.GetInterface("ICollection", true) != null
                        && thisObjectsProperty.PropertyType.GetGenericArguments().Count() > 0)
                    {
                        Type genericType = thisObjectsProperty.PropertyType.GetGenericArguments()[0];

                        var observableCollection = Activator.CreateInstance(thisObjectsProperty.PropertyType) as IList;

                        if (observableCollection is INotifyCollectionChanged)
                            ((INotifyCollectionChanged)observableCollection).CollectionChanged += this.Object_CollectionChanged;

                        if (observableCollection is INotifyPropertyChanged)
                            ((INotifyPropertyChanged)observableCollection).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);

                        foreach (var item in (IEnumerable)value)
                        {
                            var newItem = Activator.CreateInstance(genericType);

                            if (newItem != null)
                            {
                                SetValues(newItem, item);
                                observableCollection.Add(newItem);
                            }
                        }
                    }

                    else
                    {
                        thisObjectsProperty.SetValue(thisObject, value, null);

                    }
                }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    protected void Object_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (var item in e.NewItems)
                {
                    if (item is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
                    }
                }
                break;

            case NotifyCollectionChangedAction.Remove:
                foreach (var item in e.OldItems)
                {
                    if (item is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged -= this.Object_PropertyChanged;
                    }
                }
                break;
        }

    }

    protected void Object_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.NotifyPropertyChanged(e.PropertyName);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;

        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));

    }
}

SetValuesメソッドの最初のパラメータは、ビューモデルで使用されるDependencyObject型です。 2番目のパラメータは、DomainServiceのContext.LoadOperationから返されるエンティティです。

私の問題は、INotifyCollectionChanged.CollectionChangedが発生したときに、コレクションのプロパティ名でPropertyChangedイベントを発生させることができるようにする必要があることです。だから誰かが何かアドバイスがあれば私はそれを大いに感謝するでしょう。前もって感謝します。

編集する

イベントを発生させているプロパティ名を取得する方法を考え出しました。これが私のPropertyChangedEventHandlerの編集版です。

protected void Object_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var properties = this.GetType().GetProperties().Where(x => x.PropertyType == sender.GetType()).ToArray();

        foreach (var property in properties)
        {
            this.NotifyPropertyChanged(property.Name);
        }

        //this.NotifyPropertyChanged(e.PropertyName);
    }

基本的にはこれで私が探していたことができますが、親切にも私はまだ正しいことをしていません。別の型のプロパティであるObservableCollectionが追加されているとき、UIElementはまだ更新されていません。

これが私のDependencyObjectsとViewModelの例です。

public class LOB : DependencyObject
{
    public Int32 ID
    {
        get { return (Int32)GetValue(IDProperty); }
        set
        {
            SetValue(IDProperty, value);
            NotifyPropertyChanged("ID");
        }
    }

    public static readonly DependencyProperty IDProperty =
    DependencyProperty.Register("ID", typeof(Int32), typeof(LOB), null);

    public ObservableCollection<Group> Groups
    {
        get { return (ObservableCollection<Group>)GetValue(GroupsProperty); }
        set
        {
            SetValue(GroupsProperty, value);
            NotifyPropertyChanged("Groups");
        }
    }

    public static readonly DependencyProperty GroupsProperty =
    DependencyProperty.Register("Groups", typeof(ObservableCollection<Group>), typeof(LOB), new PropertyMetadata(null, OnGroupsPropertyChanged));

    static void OnGroupsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            ((INotifyCollectionChanged)e.NewValue).CollectionChanged += new NotifyCollectionChangedEventHandler(((LOB)obj).Object_CollectionChanged);
            ((INotifyPropertyChanged)e.NewValue).PropertyChanged += new PropertyChangedEventHandler(((LOB)obj).Object_PropertyChanged);
        }
        if (e.OldValue != null)
        {
            ((INotifyCollectionChanged)e.OldValue).CollectionChanged -= ((LOB)obj).Object_CollectionChanged;
            ((INotifyPropertyChanged)e.OldValue).PropertyChanged -= ((LOB)obj).Object_PropertyChanged;
        }
    }

}

public class Group : DependencyObject
{
    public Int32 ID
    {
        get { return (Int32)GetValue(IDProperty); }
        set
        {
            SetValue(IDProperty, value);
            NotifyPropertyChanged("ID");
        }
    }

    public static readonly DependencyProperty IDProperty =
    DependencyProperty.Register("ID", typeof(Int32), typeof(Group), null);

    public String GroupName
    {
        get { return (String)GetValue(GroupNameProperty); }
        set
        {
            SetValue(GroupNameProperty, value);
            NotifyPropertyChanged("GroupName");
        }
    }

    public static readonly DependencyProperty GroupNameProperty =
    DependencyProperty.Register("GroupName", typeof(String), typeof(Group), null);

}

public class MyViewModel : DependencyObject
{
    public static readonly DependencyProperty LobCollectionProperty =
        DependencyProperty.Register("LobCollection",
            typeof(ObservableCollection<LOB>),
            typeof(MyViewModel),
            new PropertyMetadata(null, LobCollectionPropertyChanged));

    public ObservableCollection<LOB> LobCollection
    {
        get { return (ObservableCollection<MainBusinessLine>)GetValue(LobCollectionPropertyChanged); }
        set
        {
            SetValue(MainBusinessLineCollectionProperty, value);
            NotifyPropertyChanged("LobCollection");
        }
    }

    static void LobCollectionPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var viewModel = obj as MyViewModel;

        if (viewModel == null)
            return;

        if (e.OldValue != null)
        {
            ((INotifyCollectionChanged)e.OldValue).CollectionChanged -= viewModel.LobCollection_Changed;
        }

        if (e.NewValue != null)
        {
            ((INotifyCollectionChanged)e.NewValue).CollectionChanged += viewModel.LobCollection_Changed;
        }

    }

    void LobCollection_Changed(object sender, NotifyCollectionChangedEventArgs e)
    {
        NotifyPropertyChanged("LobCollection");
    }
}  


  • 私はきちんとあなたの質問を理解するようにしましょう:あなたはこのクラスを手に入れました:"パブリッククラスFoo:INotifyPropertyChanged {INotifyCollectionChangedコレクション; CollectionChangedイベントが発生したときに、所有クラスでPropertyChangedイベントを発生させる必要があります。あれは正しいですか? - David Yaw
  • はい。それで合っています。加えて、私は自分のオリジナルの投稿を編集して、それをいくらか更新したバージョンを表示するようにしました。これは最善の解決策ではないと認めましたが、今のところは別のオブザーバブルコレクション内のオブザーバブルコレクションに追加できるようにしたいと思います。 - jhorton
  • LOBクラスとグループクラスで何をしているかは私にとって最も理にかなっています。基本クラスでそれを処理することは確かに可能ですが、DependancyPropertyとINotifyPropertyChangedを混在させると難しくなります。また、技術的には、INotifyPropertyChangedの契約に従わないと思います。プロパティの値は変更されておらず、その内部のコレクションは変更されています。これを行うための標準的な方法は、CollectionChangedイベントを発生させ、そのままにすることです。 - David Yaw
  • 最初に、xamlに何らかの変更を加えたときに、私のUIElementが更新され、新しい項目が正しく表示されたことを付け加えます。そのため、「DependencyPropertyとINotifyPropertyChangedを使用して、今それを過剰に設計したことがありますか」という問題が発生しました。そして、上記のあなたの声明によって、私はしたと思います。それで、あなたは何がより良いプラクティス、DPのものかINotifyのものだと思いますか?そして、あなたはそれをCollectionChangedイベントでそれを残すとも言いました。そのコレクションプロパティに対してPropertyChangedイベントを発生させる必要はありませんか。そして、ご回答いただきありがとうございます。私の頭は壁に当たって痛くなっていました。 - jhorton
  • DependancyObjectに関する私の一般的な理念INotifyPropertyChangedはどちらかを使うことです。同じクラスで両方を使用する必要がある場合は、DependancyPropertyに対してPropertyChangedイベントを発生させないでください。そのプロパティにバインドしているものがすでにDPから変更通知を受け取っている場合は、PropertyChangedは不要です。 - David Yaw

1 답변


2

上記の会話の後、これはかなり議論の余地はありませんが、サブクラスで定義されたプロパティでコレクションが変更されたときにPropertyChangedイベントを発生させる基本クラスを実装する方法について考えました。私が言ったように、それは少し標準的ではありません、しかしここに私がそうする方法があります。

class FancyCollectionAndPropertyChangedBase : INotifyPropertyChanged
{
    private Dictionary<ICollectionChanged, String> collectionNameLookup = new Dictionary<ICollectionChanged, String>();

    protected FancyCollectionAndPropertyChangedBase()
    {
        this.PropertyChanged += MyPropertyChanged;
    }

    private void MyPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if(this.collectionNameLookup.ContainsValue(e.PropertyName)
        {
            KeyValuePair<INotifyCollectionChanged, String> oldValue = this.collectionNameLookup.First(kvp => kvp.Value == e.Name);
            oldValue.Key -= MyCollectionChanged;
            this.collecitonNameLookup.Remove(oldValue.Key);

            INotifyCollectionChanged collection = this.GetType().GetProperty(e.PropertyName, BindingFlags.FlattenHierarchy).GetValue(this, null);
            collection.CollectionChanged += MyCollectionChanged;
            this.collectionNameLookup.Add(collection, e.Name);
        }
        else if(typeof(INotifyCollectionChanged).IsAssignableFrom(this.GetType().GetProperty(e.PropertyName,  BindingFlags.FlattenHierarchy).PropertyType))
        {
            // Note: I may have gotten the IsAssignableFrom statement, above, backwards. 
            INotifyCollectionChanged collection = this.GetType().GetProperty(e.PropertyName, BindingFlags.FlattenHierarchy).GetValue(this, null);
            collection.CollectionChanged += MyCollectionChanged;
            this.collectionNameLookup.Add(collection, e.Name);
        }
    }

    private void MyCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        this.NotifyPropertyChanged(this.collectionNameLookup[sender];
    }
}


  • David、私はあなたが上に投稿したあなたのコードの基礎を使ってそれを私のBaseクラスに実装したことを知らせたかっただけです。すべてが円滑に機能しています。これを手助けするために時間を割いていただいてありがとうございます。すべてを混ぜ合わせることがトリックであることがわかったので、これを答えとしてマークします。また、DependencyObjectから継承を削除し、INotifyインターフェイスのみを実装しました。将来、誰かが完成したコードを見る必要があるなら、私はそれを投稿してうれしいです。再度、感謝します。 - jhorton

関連する質問

最近の質問