|
■No66506 (ゆうた さん) に返信
> しかし下記のソースコードだとtextBoxCl1F2をフォーム上で変更しても
> this.class.Foo2が変更されず
this.class.Foo2 ではなく、実際には
this.class1.Foo2 のことかと思いますが、それはさておき。
> (実際に作成しているプログラムでは下記のソースコードの場合のnumericUpDownCl1F1への変更も
> データソースに反映されないのですが、うまく再現できませんでした。)
Class1 の各プロパティを、get; set; による自動実装プロパティとはせず、明示的に
getter / setter を書くようにしておき、どのタイミングで setter が呼ばれているかを
追跡してみてください。numericUpDownCl1F1 の値を WriteValue するよりも前の段階で、
そのコントロールのバインド元変数に、修正前の値が再代入されていませんか?
> デバッグしてみると最初のデータソースへの書き込み後に
> textBoxCl1F2の値がclass1.Foo2の値に置き換わってしまいます。
WriteValue メソッドが呼ばれた場合、その影響範囲は全プロパティに及びます。
つまり numericUpDownCl1F1.DataBindings["Text"].WriteValue() が呼ばれた場合、
その影響範囲は Foo1/numericUpDownCl1F1 だけには留まらず、実際には
Foo2/textBoxCl1F2 にも影響を与えています。
今回の場合、最初の WriteValue メソッドの呼び出しによって、
class1.Foo1 に対して numericUpDownCl1F1.Value の値が新しく書き込まれます。
そしてそれと同時に、class1 のメンバーである Foo2 も更新されのですが、
class1.Foo2 側はまだ WriteValue されていないため、class1.Foo2 に入るのは
以前の古い値です。そしてこの再セットし直された古い値が今回の問題を引き起こしています。
バインド時に、Foo2 に対する ControlUpdateMode を Never にしていないため、
古い値が class1 に再セットされたときに、その変化がコントロール側にも伝わってしまい、
結果として textBoxCl1F2.Text が古い値に上書きされて戻ってしまうわけです。
> textBoxCl1F2の値が変更されてしまう理由がよくわからず、
上記のような理由から、もしも
this.numericUpDownCl1F1.DataBindings["Value"].WriteValue();
this.textBoxCl1F2.DataBindings["Text"].WriteValue();
という元のコードを、その実行順序を入れ替えて
this.textBoxCl1F2.DataBindings["Text"].WriteValue();
this.numericUpDownCl1F1.DataBindings["Value"].WriteValue();
に変更したとすれば、今度は class1.Foo2 を保存できるようになるでしょう。
(その代わり、class1.Foo1 が書き換わるタイミングを失いますが)
> どのように対処すればよいのかわかりません。
今回の場合、Never + WriteValue で更新する際には、データの逆流を防ぐため、
WriteValue 前に、「自身以外」の ControlUpdateMode をすべて Never にしてみてください。
そうすれば、自身以外のコントロールが古い値に書き戻される現象を防ぐことができます。
this.textBoxCl1F2.DataBindings["Text"].ControlUpdateMode = ControlUpdateMode.Never;
// numericUpDownCl1F1 の値を反映させる
this.numericUpDownCl1F1.DataBindings["Value"].WriteValue();
this.textBoxCl1F2.DataBindings["Text"].ControlUpdateMode = ControlUpdateMode.OnPropertyChanged;
あるいは、ControlUpdateMode を自分で切り替えるかわりに、
その役目を BindingSource に任せてしまうという手もあります。
この場合、RaiseListChangedEvents プロパティを false にしておくことで
他のプロパティが連動して更新される現象を抑制できます。
private Class1 class1;
private BindingSource bndSrc0, bndSrc1;
public Form1()
{
InitializeComponent();
this.class1 = new Class1(10, 20);
// 直接バインドしない。
// this.numericUpDownCl1F1.DataBindings.Add("Value", this.class1, "Foo1", false, DataSourceUpdateMode.Never);
// this.textBoxCl1F2.DataBindings.Add("Text", this.class1, "Foo2", false, DataSourceUpdateMode.Never);
// this.numericUpDownCl2V1.DataBindings.Add("Value", this.class1.class2, "Val1", false, DataSourceUpdateMode.Never);
// this.textBoxCl2V2.DataBindings.Add("Text", this.class1.class2, "Val2", false, DataSourceUpdateMode.Never);
// BindingSource に中継させるようにする。
this.bndSrc0 = new BindingSource() { DataSource = this.class1 };
this.bndSrc1 = new BindingSource(this.bndSrc0, "class2");
this.numericUpDownCl1F1.DataBindings.Add("Value", this.bndSrc0, "Foo1", false, DataSourceUpdateMode.Never);
this.textBoxCl1F2.DataBindings.Add("Text", this.bndSrc0, "Foo2", false, DataSourceUpdateMode.Never);
this.numericUpDownCl2V1.DataBindings.Add("Value", this.bndSrc1, "Val1", false, DataSourceUpdateMode.Never);
this.textBoxCl2V2.DataBindings.Add("Text", this.bndSrc1, "Val2", false, DataSourceUpdateMode.Never);
}
private void button1_Click(object sender, EventArgs e)
{
this.bndSrc0.RaiseListChangedEvents = false; // WriteValue する前に false にしておく
this.numericUpDownCl1F1.DataBindings["Value"].WriteValue();
this.textBoxCl1F2.DataBindings["Text"].WriteValue();
this.numericUpDownCl2V1.DataBindings["Value"].WriteValue();
this.textBoxCl2V2.DataBindings["Text"].WriteValue();
this.bndSrc0.RaiseListChangedEvents = true; // 処理が終わったので true に戻す
this.numericUpDownA_Cl1F1.Value = this.class1.Foo1;
this.textBoxA_Cl1F2.Text = this.class1.Foo2.ToString();
this.numericUpDownA_Cl2V1.Value = this.class1.class2.Val1;
this.textBoxA_Cl2V2.Text = this.class1.class2.Val2.ToString();
}
> private static int aa;
> public int Val1 { get { return aa; } set { aa = value; } }
これだと、インスタンスメンバーにしたいのか、クラスメンバーにしたいのか、
中途半端な実装に見えます。もしもインスタンスメンバーとして実装した上で
データバインドしたいのであれば、クラスに変更通知が必要になるかと。
|