あなたの質問は非常に興味深いものですが、その範囲は実際には非常に大きいです。
このような状況で非常に便利なツールは、フレームワークの実装を確認できるILSpyです。
私が問題にするのは、次のステートメントです。
私が得た答えは、C# のデータ バインディング エンジンは WPF/Windows フォーム UI と密接に結合されているということでした。
同意しません; データ バインディング エンジンは .Net イベントの実装と密接に結合されていますが、ターゲットとソースは何でもかまいません。.Net 言語の最も一般的なフロント エンドであるため、ほとんどの例は Windows フォーム、WPF、または ASP.Net になります。 UI がなくても、他のシナリオでマルチ バインディングを使用することは完全に可能です。
双方向バインディングを追加するとどうなりますか? さて、MultiBindingのソースを見ると、興味深いことがいくつかわかります。
- バインディング シナリオを記述するBindingModeプロパティを公開します。通常は、
OneWay
またはTwoWay
- 2 つの興味深いイベントが公開されています
NotifyOnSourceUpdated
。NotifyOnTargetUpdated
基本的な形式は次のとおりです。
// System.Windows.Data.MultiBinding
/// <summary>Gets or sets a value that indicates whether to raise the <see cref="E:System.Windows.FrameworkElement.SourceUpdated" /> event when a value is transferred from the binding target to the binding source.</summary>
/// <returns>true if the <see cref="E:System.Windows.FrameworkElement.SourceUpdated" /> event will be raised when the binding source value is updated; otherwise, false. The default value is false.</returns>
[DefaultValue(false)]
public bool NotifyOnSourceUpdated
{
get
{
return base.TestFlag(BindingBase.BindingFlags.NotifyOnSourceUpdated);
}
set
{
bool flag = base.TestFlag(BindingBase.BindingFlags.NotifyOnSourceUpdated);
if (flag != value)
{
base.CheckSealed();
base.ChangeFlag(BindingBase.BindingFlags.NotifyOnSourceUpdated, value);
}
}
}
つまり、イベントを使用して、ソースが更新されたとき ( OneWay
) と、ターゲットも更新されたとき (TwoWay
バインド用)を通知します。
複数のデータ ソースをサブスクライブできることを除いて、同様の方法で動作するクラスもあり、PriorityBinding
最も早くデータを返すものを優先します。
したがって、これがどのように機能するかは明確です。バインディングを作成するとき、一方の側 (読み取り専用の更新の場合) または両側 (たとえば、GUI でデータを変更して送り返すことができる場合) の変更をサブスクライブします。すべての通知はイベントによって管理されます。
次の質問は、本当に、誰がイベントを管理しているのかということです。簡単な答えは、ターゲットとソースの両方がそうするということです。そのため、実装INotifyPropertyChanged
が重要です。たとえば、バインディングが実際に行うことは、両側が互いの変更をサブスクライブする方法に関するコントラクトを作成することだけです。これは、ターゲットとソースが実際に密接に結合されているコントラクトです。
ObservableCollectionは、データ ソース内の更新を UI にプロモートし、UI 内のデータへの変更を基になるデータ ソースに戻すために、GUI アプリケーションで広く使用されているため、興味深いテスト ケースです。
BlockReentrancy
(コードを見て)物事が変化したことを伝えるための実際のイベントが非常に単純であることに注意しCheckReentrancy
てください。操作はアトミックであり、サブスクライバーは変更が発生した順序で変更を通知され、基になるコレクションが更新されたものと一致することが保証されます。
これは、操作全体の中で本当に難しい部分です。
つまり、.Net での DataBinding の実装は、GUI テクノロジと密接に結合されていません。ほとんどの例は、Windows フォーム、WPF、または ASP.Net アプリケーションのコンテキストで DataBinding を提示するだけです。実際のデータ バインディングはイベント ドリブンであり、それを活用するには、データの変更を同期して管理することがより重要です。インターフェイス) が定義します。
楽しむ ;-)
編集:
私は座って 2 つのクラスを作成し、 とMyCharacter
属性のMyCharacterAttribute
間に TwoWay データバインディングを設定するという明確な目的を持っていました。Health
HealthValue
public class MyCharacter : DependencyObject
{
public static DependencyProperty HealthDependency =
DependencyProperty.Register("Health",
typeof(Double),
typeof(MyCharacter),
new PropertyMetadata(100.0, HealthDependencyChanged));
private static void HealthDependencyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
}
public double Health
{
get
{
return (double)GetValue(HealthDependency);
}
set
{
SetValue(HealthDependency, value);
}
}
public void DrinkHealthPotion(double healthRestored)
{
Health += healthRestored;
}
}
public class MyCharacterAttributes : DependencyObject
{
public static DependencyProperty HealthDependency =
DependencyProperty.Register("HealthValue",
typeof(Double),
typeof(MyCharacterAttributes),
new PropertyMetadata(100.0, HealthAttributeDependencyChanged));
public double HealthValue
{
get
{
return (Double)GetValue(HealthDependency);
}
set
{
SetValue(HealthDependency, value);
}
}
public List<BindingExpressionBase> Bindings { get; set; }
public MyCharacterAttributes()
{
Bindings = new List<BindingExpressionBase>();
}
private static void HealthAttributeDependencyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
}
}
ここで注意すべき最も重要なことは、DependencyObject からの継承とDependencyPropertyの実装です。
実際には、次のようになります。簡単な WPF フォームを作成し、次のコードを設定しました。
MyCharacter Character { get; set; }
MyCharacterAttributes CharacterAttributes = new MyCharacterAttributes();
public MainWindow()
{
InitializeComponent();
Character = new MyCharacter();
CharacterAttributes = new MyCharacterAttributes();
// Set up the data binding to point at Character (Source) and
// Property Health (via the constructor argument for Binding)
var characterHealthBinding = new Binding("Health");
characterHealthBinding.Source = Character;
characterHealthBinding.NotifyOnSourceUpdated = true;
characterHealthBinding.NotifyOnTargetUpdated = true;
characterHealthBinding.Mode = BindingMode.TwoWay;
characterHealthBinding.IsAsync = true;
// Now we bind any changes to CharacterAttributes, HealthDependency
// to Character.Health via the characterHealthBinding Binding
var bindingExpression =
BindingOperations.SetBinding(CharacterAttributes,
MyCharacterAttributes.HealthDependency,
characterHealthBinding);
// Store the binding so we can look it up if necessary in a
// List<BindingExpressionBase> in our CharacterAttributes class,
// and so it "lives" as long as CharacterAttributes does, too
CharacterAttributes.Bindings.Add(bindingExpression);
}
private void HitChracter_Button(object sender, RoutedEventArgs e)
{
CharacterAttributes.HealthValue -= 10.0;
}
private void DrinkHealth_Button(object sender, RoutedEventArgs e)
{
Character.DrinkHealthPotion(20.0);
}
HitCharacter ボタンをクリックすると、CharacterAttributes.HealthValue
プロパティが 10 減少します。これによりイベントが発生し、前に設定したバインディングを介して、Character.Health
値から 10.0 が減算されます。DrinkHealth ボタンを押すと、20.0 回復Character.Health
し、20.0 増加しCharacterAttributes.HealthValue
ます。
また、このようなものは実際にUIフレームワークに組み込まれていることに注意してください- FrameworkElement
(から継承UIElement
)が実装されていますSetBinding
。GetBinding
これは理にかなっています-DataBinding GUI要素は、ユーザーインターフェイスにとって完全に有効なシナリオです! ただし、さらに詳しく見るとSetValue
、たとえば、 はBindingOperations.SetBinding
内部インターフェイスを呼び出しているだけなので、実際に a を使用しなくてもUIElement
(上記の例のように) 実装できます。ただし、引き継がなければならない 1 つの依存関係はDependencyObject
andですDependencyProperty
。これらは DataBinding が機能するために必須ですが、オブジェクトが から継承している限りDependencyObject
、テキスト ボックスの近くに移動する必要はありません :-)
ただし、欠点は、バインディングの一部がinternal
メソッドを介して実装されていることです。そのため、ネイティブのようなフレームワークの実装にアクセスできないため、実装したいバインディング アクションで追加のコードを記述する必要があるシナリオに遭遇する可能性があります。クラスはできます。ただし、上記の例のような TwoWay データバインディングは完全に可能です。