1

INotifyPropertyChangedを実装するクラスがあります。あるviewModelでクラスのインスタンスを作成します。この機能をクラスから削除して、インスタンスの作成後に注入することは可能ですか?ICustomTypeDescriptorがこれを実現すると聞きましたが、使い方がわかりません。

public class C : ICustomNotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public int _id;
    public string _name;

    public int Id
    {
        get { return _id; }
        set
        {
            if (_id == value)
            {
                return;
            }

            _id = value;
            OnPropertyChanged("Id");
        }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
            {
                return;
            }

            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
4

2 に答える 2

0

これは機能しません。サブクラスを作成して注入することはできますが、バイトコードを変更して、適切なメソッドがCALLEDになっていることを確認する必要があります。これはより難しいメソッドです。

于 2010-03-18T11:17:15.517 に答える
0

オブジェクトが最初に作成され、プロパティが設定されたときに通知が発生しないようにするだけの場合は、プロパティが一度設定されるまで false であるブール フラグを追加できます。フラグが true の場合にのみ通知を実行します。

編集:

すべてのコードを削除した後にそこに機能を取得するきれいな方法はないと思いますがINotifyPropertyChanged、インスタンスの外部から機能を制御する方法はたくさんあります。

このコードはすべて、VisualStudio ではなくテキスト エディターで記述したことに注意してください。いかなる方法でもテストされていません。

通知を有効にするメソッドを追加します。

public class OptionalNotification : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string name) ...

    bool _shouldNotify;

    public void EnableNotifications()
    {
        _shouldNotify = true;
    }

    string _someProperty;
    public string SomeProperty
    {
        get { return _someProperty; }
        set 
        {
            if(_someProperty == value) return

            _someProperty = value;

            if(_shouldNotify) OnPropertyChanged("SomeProperty");
        }
    }
}

インスタンス化の時点でインスタンスが通知を生成する必要があるかどうかがわかっている場合は、メソッドを使用せずに同じことを行うことができます。その場合、コンストラクターでブール値パラメーターが必要になるだけです。

もう 1 つのバリエーションは、ファクトリ パターンを使用することです。この場合、ファクトリはブール フラグへの内部アクセスを持ち、構築時にそれを設定します。

条件をプロキシにカプセル化します。

public interface IEntity : INotifyPropertyChanged
{
    string SomeProperty { get; set; }
}

public class Entity : IEntity
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string name) ...

    string _someProperty;
    public string SomeProperty
    {
        get { return _someProperty; }
        set 
        {
            if(_someProperty == value) return

            _someProperty = value;
            OnPropertyChanged("SomeProperty");
        }
    }
}

public class EntityNotificationProxy : IEntity
{
    IEntity _inner;

    public EntityNotificationProxy(IEntity entity)
    {
        _inner = entity;
        _inner.PropertyChanged += (o,e) => { if(ShouldNotify) OnPropertyChanged(o,e); }
    }

    public bool ShouldNotify { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(object sender, PropertChangedEventArgs e)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if(handler != null) handler(sender, e);
    }

    public string SomeProperty
    {
        get { return _inner.SomeProperty; }
        set 
        {
            if(_inner.SomeProperty == value) return

            _inner.SomeProperty = value;
        }
    }
}

ここで、消費するクラスは、エンティティ自体ではなくエンティティ プロキシを取得します (ただし、IEntityインターフェイス/抽象化をプログラムする場合にのみ参照するため、それほど賢明ではありません)。プロキシのラッピングは、ファクトリまたは IoC コンテナー/DI フレームワークを介して行うことができます。

このアプローチの主な利点は、エンティティが純粋なINotifyPropertyChanged実装を維持し、条件付きの側面が外部から処理されることです。もう 1 つの利点は、プログラミングを抽象化と制御の反転に強制するのに役立つことです。

INotifyPropertyChanged主な欠点は、この条件付き動作を実現する実装ごとにプロキシを作成する必要があることです。

レジストリを作成して、どのインスタンスが通知を発生させる必要があるか、または発生させないかを追跡します。

public static class PropertyNotificationRegistry
{
    static IDictionary<INotifyPropertyChanged, bool> _registeredClasses
        = new Dictionary<INotifyPropertyChanged, bool>;

    static void Register(INotifyPropertyChanged o, bool shouldNotify)
    {
        if(!(_registeredClasses.ContainsKey(o)) _registeredClasses.Add(o, shouldNotify);
        // could also implement logic to update an existing class in the dictionary
    }

    public static void ShouldNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
    {
        Register(o, true);
    }

    public static void ShouldNotNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
    {
        Register(o, false);
    }

    public static void NotifyPropertyChanged(this INotifyPropertyChanged o, Action notificationAction)
    {
        if(_registeredClasses.ContainsKey(o))
        {
            bool shouldNotify = _registeredClasses.Where(x => x.Key == o).Single().Value;

            if(shouldNotify) notificationAction();
        }
    }
}

public class EntityUsingNotificationRegistry : INotifyPropertyChanged
{
    ... // all the standard INotifyPropertyChanged stuff

    string _someProperty;
    public string SomeProperty
    {
        get { return _someProperty; }
        set 
        {
            if(_someProperty == value) return;

            _someProperty = value;
            this.NotifyPropertyChanged(() => OnPropertyChanged("SomeProperty"));
        }
    }
}

public class SomethingInstantiatingOurEntity
{
    public void DoSomething()
    {
        var entity1 = new EntityUsingNotificationRegistry();
        entity1.ShouldNotifyWhenPropertiesChange();

        var entity2 = new EntityUsingNotificationRegistry();
        entity2.ShouldNotNotifyWhenPropertiesChange();

        entity1.SomeProperty = "arbitrary string"; // raises event
        entity2.SomeProperty = "arbitrary string"; // does not raise event

        var entity3 = new EntityUsingNotificationRegistry();
        entity3.SomeProperty = "arbitrary string"; // does not raise event
        entity3.ShouldNotifyWhenPropertiesChange();
        entity3.SomeProperty = "another arbitrary string"; // now raises event
    }
}

現在、レジストリには、すべてのインスタンスへの参照が保持されており、これらのインスタンスがガベージ コレクターによって取得されないという明確な欠点があります。s を使用してレジストリを実装することで、これを解決できる可能性がありますがWeakReference、特定の実装を推奨するために、その使用法について十分に理解しているわけではありません。

于 2010-03-18T13:28:51.570 に答える