web-dev-qa-db-ja.com

.NET WinForms INotifyPropertyChangedは、バインディングが変更されると、すべてのバインディングを更新します。もっと良い方法?

Windowsフォームアプリケーションでは、INotifyPropertyChangedをトリガーするプロパティの変更により、フォームは、変更されたプロパティだけでなく、バ​​インドされたオブジェクトからすべてのプロパティを読み取ります。 (以下のサンプルコードを参照してください)

インターフェイスには変更するプロパティの名前が必要なので、これはばかげて無駄に思えます。一部のプロパティゲッターでは計算を実行する必要があるため、アプリで多くのクロッキングが発生しています。

これを行うためのより良い方法がない場合は、不要な読み取りを破棄するために、ゲッターに何らかのロジックを実装する必要があります。

私は何かが足りないのですか?もっと良い方法はありますか?別のプレゼンテーションテクノロジを使用するように言わないでください。私はWindowsMo​​bileでこれを行っています(ただし、動作は完全なフレームワークでも発生します)。

問題を示すためのおもちゃのコードを次に示します。ボタンをクリックすると、1つのプロパティが変更された場合でも、両方のテキストボックスにデータが入力されます。

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace Example
{
public class ExView : Form
{
    private Presenter _presenter = new Presenter();
    public ExView()
    {
        this.MinimizeBox = false;

        TextBox txt1 = new TextBox();
        txt1.Parent = this;
        txt1.Location = new Point(1, 1);
        txt1.Width = this.ClientSize.Width - 10;
        txt1.DataBindings.Add("Text", _presenter, "SomeText1");

        TextBox txt2 = new TextBox();
        txt2.Parent = this;
        txt2.Location = new Point(1, 40);
        txt2.Width = this.ClientSize.Width - 10;
        txt2.DataBindings.Add("Text", _presenter, "SomeText2");

        Button but = new Button();
        but.Parent = this;
        but.Location = new Point(1, 80);
        but.Click +=new EventHandler(but_Click);
    }

    void but_Click(object sender, EventArgs e)
    {
        _presenter.SomeText1 = "some text 1";
    }
}

public class Presenter : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    private string _SomeText1 = string.Empty;
    public string SomeText1
    {
        get
        {
            return _SomeText1;
        }
        set
        {
            _SomeText1 = value;
            _SomeText2 = value; // <-- To demonstrate that both properties are read
            OnPropertyChanged("SomeText1");
        }
    }

    private string _SomeText2 = string.Empty;
    public string SomeText2
    {
        get
        {
            return _SomeText2;
        }
        set
        {
            _SomeText2 = value;
            OnPropertyChanged("SomeText2");
        }
    }

    private void OnPropertyChanged(string PropertyName)
    {
        PropertyChangedEventHandler temp = PropertyChanged;
        if (temp != null)
        {
            temp(this, new PropertyChangedEventArgs(PropertyName));
        }
    }
}

}

24
Dave Welling

イベントが発生したときにすべてのプロパティが読み取られる理由は、ProperyChangedイベントが発生したときにバインディングオブジェクトで呼び出されるPushDataメソッドにあります。スタックトレースを見ると、内部オブジェクトBindToObjectのPropValueChangedメソッドが呼び出され、BindingManagerでOncurrentchangedイベントが呼び出されていることがわかります。バインディングメカニズムは、現在のアイテムの変更を追跡しますが、より詳細な区別は行いません。 「犯人」のPushDataメソッドは、プロパティのゲッターを呼び出します(リフレクターを使用してコードを見てください)。したがって、それを回避する方法はありません。とはいえ、経験則として、getおよびsetアクセサーでは、重い処理を行うことはお勧めしません。そのために別々のgetメソッドとsetメソッドを使用します(可能な場合)

この記事、特にこのコメントもご覧ください( http://www.codeproject.com/Messages/2514032/How-Binding-watches-control-properties-ie-how-doe.aspx )、これはpropertychangedイベントがどのように発生するかを正確に説明していますが、ゲティア問題には対処しません: http://www.codeproject.com/KB/database/databinding_tutorial.aspx?msg=2514032 ==

探索するアイデアは、ゲッターが呼び出されるのを遅らせることです。これは、バインディングのControlUpdateModeプロパティをいじることで実現できます。この値がNeverに設定されている場合、変更があったときに対応するコントロールは更新されません。ただし、値をOnPropertyChangedに戻すと、PushDataメソッドが呼び出されるため、ゲッターにアクセスします。したがって、例を考えると、このコードはテキストボックス2の更新を一時的に防ぎます。

void but_Click(object sender, EventArgs e)
        {                   
            txt2.DataBindings[0].ControlUpdateMode = ControlUpdateMode.Never;
            _presenter.SomeText1 = "some text 1";
        }
15
anchandra

私はこのようなサブクラス化バインディングをテストし、OnPropertyChangedを管理しています。

public class apBinding : Binding
{

        public apBinding(string propertyName, INotifyPropertyChanged dataSource, string dataMember)
            : base(propertyName, dataSource, dataMember)
        {
            this.ControlUpdateMode = ControlUpdateMode.Never;
            dataSource.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {

            if (e.PropertyName == this.BindingMemberInfo.BindingField)
            {
                 this.ReadValue();
            }
        }
    }

今私が見つけた問題は、コントロールがリンクされたオブジェクトの値を上書きすることです、それで私はに変更しました

public class apBinding : Binding
{

        public apBinding(string propertyName, INotifyPropertyChanged dataSource, string dataMember)
            : base(propertyName, dataSource, dataMember)
        {

            dataSource.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.ControlUpdateMode = ControlUpdateMode.Never;
            if (e.PropertyName == this.BindingMemberInfo.BindingField)
            {
                 this.ReadValue();
            }
        }
    }

次に、propertychangesが最初に呼び出されたときに、controlupdateを無効にします。コントロールは最初の実行時に正しく更新されます。

1
pedro