M
Marc Gravell
In the following example, the Bindings don't update the UI if the property
change is triggered from the non-UI thread - see the async button - from the
listbox contents (and observation) the change to the object has occurred
correctly; have I got a foobar somewhere, or is this the expected behaviour?
I can "fix" it by adding an additional few lines (follows main code), but
this is ugly; any ideas? And can I get a robust MVP-style UI in a threaded
environment using this binding approach?
Marc
===
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Diagnostics;
namespace TestApp {
static class Program {
[STAThread]
static void Main() {
SomeClass obj = new SomeClass();
using (Form f = new Form())
using(TextBox tb1 = new TextBox())
using (TextBox tb2 = new TextBox())
using (TextBox tb3 = new TextBox())
using (BindingSource bs = new BindingSource())
using (Button cycleSyncButton = new Button())
using (ListBox lb = new ListBox())
using (Button cycleAsyncButton = new Button()) {
// debug ougput:
obj.PropertyChanged += delegate(object sender,
PropertyChangedEventArgs args) {
lb.BeginInvoke((ThreadStart)delegate { // list property
changes
string message =
string.Format("{0}={1}",args.PropertyName,sender.GetType().GetProperty(args.PropertyName).GetValue(sender,null));
lb.Items.Insert(0, message);
});
}; // end debug ougput:
cycleSyncButton.Click += delegate { obj.Cycle(); };
cycleAsyncButton.Click += delegate {
ThreadPool.QueueUserWorkItem(delegate { obj.Cycle(); }); };
tb1.Dock = tb2.Dock = tb3.Dock = cycleSyncButton.Dock =
cycleAsyncButton.Dock = DockStyle.Top;
lb.Dock = DockStyle.Fill;
cycleAsyncButton.Text = "Cycle Async";
cycleSyncButton.Text = "Cycle Sync";
bs.DataSource = typeof(SomeClass);
tb1.DataBindings.Add(new Binding("Text", bs, "Prop1",
true));
tb2.DataBindings.Add(new Binding("Text", bs, "Prop2",
true));
tb3.DataBindings.Add(new Binding("Text", bs, "Prop3",
true));
f.Controls.AddRange(new Control[] {lb, cycleAsyncButton,
cycleSyncButton, tb3, tb2, tb1 });
List<SomeClass> data = new List<SomeClass>();
data.Add(obj);
bs.DataSource = data;
f.ShowDialog();
}
}
}
sealed class SomeClass : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new
PropertyChangedEventArgs(propertyName));
}
private string _prop1 = "val1", _prop2 = "val2", _prop3 = "val3";
public string Prop1 {
get { return _prop1; }
set { if (_prop1 != value) { _prop1 = value;
OnPropertyChanged("Prop1"); } }
}
public string Prop2 {
get { return _prop2; }
set { if (_prop2 != value) { _prop2 = value;
OnPropertyChanged("Prop2"); } }
}
public string Prop3 {
get { return _prop3; }
set { if (_prop3 != value) { _prop3 = value;
OnPropertyChanged("Prop3"); } }
}
public void Cycle() {
string temp = Prop1;
Prop1 = Prop2;
Prop2 = Prop3;
Prop3 = temp;
}
}
}
===
additional fixup lines (just after existing PropertyChanged +=):
obj.PropertyChanged += delegate(object sender, PropertyChangedEventArgs
args) {
if (ReferenceEquals(sender, bs.Current) && f.InvokeRequired) {
f.BeginInvoke((ThreadStart)delegate {
bs.ResetCurrentItem();
});
};
};
change is triggered from the non-UI thread - see the async button - from the
listbox contents (and observation) the change to the object has occurred
correctly; have I got a foobar somewhere, or is this the expected behaviour?
I can "fix" it by adding an additional few lines (follows main code), but
this is ugly; any ideas? And can I get a robust MVP-style UI in a threaded
environment using this binding approach?
Marc
===
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Diagnostics;
namespace TestApp {
static class Program {
[STAThread]
static void Main() {
SomeClass obj = new SomeClass();
using (Form f = new Form())
using(TextBox tb1 = new TextBox())
using (TextBox tb2 = new TextBox())
using (TextBox tb3 = new TextBox())
using (BindingSource bs = new BindingSource())
using (Button cycleSyncButton = new Button())
using (ListBox lb = new ListBox())
using (Button cycleAsyncButton = new Button()) {
// debug ougput:
obj.PropertyChanged += delegate(object sender,
PropertyChangedEventArgs args) {
lb.BeginInvoke((ThreadStart)delegate { // list property
changes
string message =
string.Format("{0}={1}",args.PropertyName,sender.GetType().GetProperty(args.PropertyName).GetValue(sender,null));
lb.Items.Insert(0, message);
});
}; // end debug ougput:
cycleSyncButton.Click += delegate { obj.Cycle(); };
cycleAsyncButton.Click += delegate {
ThreadPool.QueueUserWorkItem(delegate { obj.Cycle(); }); };
tb1.Dock = tb2.Dock = tb3.Dock = cycleSyncButton.Dock =
cycleAsyncButton.Dock = DockStyle.Top;
lb.Dock = DockStyle.Fill;
cycleAsyncButton.Text = "Cycle Async";
cycleSyncButton.Text = "Cycle Sync";
bs.DataSource = typeof(SomeClass);
tb1.DataBindings.Add(new Binding("Text", bs, "Prop1",
true));
tb2.DataBindings.Add(new Binding("Text", bs, "Prop2",
true));
tb3.DataBindings.Add(new Binding("Text", bs, "Prop3",
true));
f.Controls.AddRange(new Control[] {lb, cycleAsyncButton,
cycleSyncButton, tb3, tb2, tb1 });
List<SomeClass> data = new List<SomeClass>();
data.Add(obj);
bs.DataSource = data;
f.ShowDialog();
}
}
}
sealed class SomeClass : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new
PropertyChangedEventArgs(propertyName));
}
private string _prop1 = "val1", _prop2 = "val2", _prop3 = "val3";
public string Prop1 {
get { return _prop1; }
set { if (_prop1 != value) { _prop1 = value;
OnPropertyChanged("Prop1"); } }
}
public string Prop2 {
get { return _prop2; }
set { if (_prop2 != value) { _prop2 = value;
OnPropertyChanged("Prop2"); } }
}
public string Prop3 {
get { return _prop3; }
set { if (_prop3 != value) { _prop3 = value;
OnPropertyChanged("Prop3"); } }
}
public void Cycle() {
string temp = Prop1;
Prop1 = Prop2;
Prop2 = Prop3;
Prop3 = temp;
}
}
}
===
additional fixup lines (just after existing PropertyChanged +=):
obj.PropertyChanged += delegate(object sender, PropertyChangedEventArgs
args) {
if (ReferenceEquals(sender, bs.Current) && f.InvokeRequired) {
f.BeginInvoke((ThreadStart)delegate {
bs.ResetCurrentItem();
});
};
};