シンプルなバインダーののこぎり...

画像








こんな感じでした...一方向のバインダーが必要でした。 コントロールからソースへではなく、その逆です。 コード内でソースの値を10万回変更しました。あらゆる種類のテキストボックスについて頭を痛めたくありません。 それらを自分で更新してほしい...



実際、ブルジョアにはすでに強力でクールなバインダーが組み込まれています。 パワフルでクールなため、ほとんどドキュメントがありません。 より正確には、それはたくさんあります-そして、どこでも不鮮明です。 手短に言えば、私はブルジョアの技術に唾を吐き、これらの手でバインダーを傷つけることに決めました...今ここで私は新人を示しています-おそらく誰かがC#円を拡大するために役立つはずです。



タスク:コントロール(私の場合、すべてのtextoxes)をソースバインドして、ソースが更新されるたびに、コントロール自体が(自動的に)更新されるようにします。



概念的に、この問題を解決するには、名前空間System.Reflectionとソースオブジェクトのset-accessorのイベントという2つのことが必要です。 「PropertyChanged」のようなもの。 コードでは次のようになります。



ソースオブジェクトクラス
string _Property; public string Property { get { return this._Property; } set { this._Property = value; if (this.PropertyIsChanged != null) { this.PropertyIsChanged.Invoke(); } } } public event Action PropertyIsChanged;
      
      







次の2つに注意してください。



1)if(this.PropertyIsChanged!= Null){this.PropertyIsChanged.Invoke(); -このコードは必要です。そうしないと、nullReference例外が発生する可能性があります。



2)パブリックイベントアクションPropertyIsChanged; -イベントは、一般に受け入れられているようにpublic修飾子で宣言され、 voidを返し、パラメーターを受け入れない組み込みのActionデリゲートに基づいています 。 他の人ではなく、なぜこの特定のデリゲートなのでしょうか? はい、バインダーがイベントハンドラーを接続するのはまさにこのイベントであるため、イベントハンドラーは1つのことのみを行います:ソースの新しい値を読み取ります(セットアクセサーの実行時、設定時、少なくとも1つのリスナーがいる場合はイベントも機能します、もちろん)、特定のコントロールの指定されたプロパティ(たとえば、.Text)に割り当てます。 つまり、バインドイベントハンドラーはvoidを返し、入力パラメーターはありません。



すべて、ソースオブジェクトの準備ができました。 実際、バインダーコード自体は残ります。



そのため、私の単純なバインダーには4つの主要なフィールドしかありません。

バインダークラスフィールド
バインドされているコントロールとそのプロパティの名前へのリンク
 Control _targetControl; /// <summary> /// Get; set-once. ///  Control (),    -. /// </summary> public Control TargetControl { get { return this._targetControl; } set { if (this._targetControl != null) { /* do nothing */ } else { this._targetControl = value; } } } string _targetControlProperty; /// <summary> /// Get; set-once. ///    Control'a, ///    -. /// </summary> public string TargetControlProperty { get { return this._targetControlProperty; } set { if (this._targetControlProperty != null) { /* do nothing */ } else { this._targetControlProperty = value; } } }
      
      







ソースオブジェクトとそのプロパティの名前へのリンク
 /// <summary> /// ,      Control. /// </summary> object _dataSourceObject; /// <summary> /// Get; set-once. ///    -, ///     Control. /// </summary> public Object DataSourceObject { get { return this._dataSourceObject; } set { if (this._dataSourceObject != null) { /* do nothing */ } else { this._dataSourceObject = value; } } } string _dataSourceObjectProperty; /// <summary> /// Get; set-once. ///   , ///    Control(). /// </summary> public string DataSourceObjectProperty { get { return this._dataSourceObjectProperty; } set { if (this._dataSourceObjectProperty != null) { /* do nothing */ } else { this._dataSourceObjectProperty = value; } } }
      
      









どうぞ コンストラクター。



バインダーコンストラクター
 public SimpleBinder( Control targetControl, string targetControlProperty, object dataSourceObject, string dataSourceProperty, string dataSourcePropertyChanged = "") { // safety checks CheckIfPropertyExists(targetControl, targetControlProperty); CheckIfPropertyExists(dataSourceObject, dataSourceProperty); // end safety this._targetControl = targetControl; this._targetControlProperty = targetControlProperty; this._dataSourceObject = dataSourceObject; this._dataSourceObjectProperty = dataSourceProperty; if (dataSourcePropertyChanged == String.Empty) { this.Binding(); } else { CheckIfEventExists(dataSourceObject, dataSourcePropertyChanged); this.Binding(dataSourcePropertyChanged, null); } }
      
      







コンストラクターには、4つの必須パラメーター(クラスの上記のフィールドに対応)と1つの無料パラメーターがあります。 freeパラメーターは、ソースオブジェクトのパブリックイベントの名前であり、指定されたプロパティの値が変更されたことを通知する役割を果たします。 私はすでにこのクラスのコードを引用しました。 繰り返しますが、制御のソースであると主張するオブジェクトは、このようなイベントの存在を処理する必要があります...これはバインダー自体の問題ではありません。



また、イベントの名前はオプションのパラメーターであるため、次のコードが必要です。



 if (dataSourcePropertyChanged == String.Empty) { this.Binding(); } else { CheckIfEventExists(dataSourceObject, dataSourcePropertyChanged); this.Binding(dataSourcePropertyChanged, null); }
      
      





イベントが指定されていない場合、自動更新は必要ありません。 次に、.Binding()メソッドがパラメーターなしでトリガーされます。



this.Binding()
 private void Binding() { this.Binding_SetValueToControl(this._targetControlProperty, this.DataSourceObjectProperty); }
      
      







コードからわかるように、.Binding()はプライベートメソッド.Binding_SetValueToControl()を呼び出します...ここにあります:



.Binding_SetValueToControl()
 private void Binding_SetValueToControl( string targetControlProperty, string dataSourceProperty) { this.TargetControl.GetType() .GetProperty(targetControlProperty) //     .SetValue(this.TargetControl, this.DataSourceObject.GetType() .GetProperty(dataSourceProperty) //     .GetValue(this.DataSourceObject) ); }
      
      







これは、まさに反射のメカニズムが使用される場所です。 この記事のコンテキストには、メソッド.GetProperty()などの詳細な分析は含まれていませんが、原則として、ここではすべてが直感的に明確です。 これが、コントロールとソースオブジェクトのプロパティの名前を持つコンストラクター文字列を要求した理由であることが一目瞭然です。これはSystem.Reflectionデバイスによるものです。



最後の質問は残っていますが、コンストラクターで指定されている場合、バインダーはどのようにイベントをバインドしますか?



など:
 private void Binding_DataSourcePropertyChangedEvent( string dataSourcePropertyChanged, Delegate propertyChangedEventHandler = null ) { if (propertyChangedEventHandler != null) { this.DataSourceObject.GetType() .GetEvent(dataSourcePropertyChanged) .AddEventHandler(this.DataSourceObject, propertyChangedEventHandler); } else { SimpleBinder RefToThis = this; this.DataSourceObject.GetType() .GetEvent(dataSourcePropertyChanged) .AddEventHandler(this.DataSourceObject, new Action( () => { RefToThis.UpdateControl(RefToThis.GetDataSourcePropertyValue()); } )); } }
      
      







すべて同じリフレクションメカニズムを使用して、.AddEventHandler()はBindイベントハンドラーを、ソースオブジェクトのクラスに元々存在していた(そしてそのセットアクセサーで開始された)パブリックイベントに接続します。 ここでは、1)Actionタイプの新しいデリゲートが作成され、2)ラムダ式から生成されたメソッドが渡されます(その内容と使用方法はこの記事では説明していません)。 原則として、すべて。



今、それを使用する方法:



 SourceObj = new SomeClass("Text?"); // -. "Text?" -     . SimpleBinder txtBoxBinder = new SimpleBinder(this.label1, "Text", // Control   ,    SourceObj, "Property", //      "PropertyIsChanged"); //  -.
      
      





以上です。



All Articles