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();
});
};
};
If you are updating the UI from another thread you need to use
Control.Invoke, there is no way around it
--
--
Ignacio Machin,
ignacio.machin AT dot.state.fl.us
Florida Department Of Transportation
"Marc Gravell" <marc.g...@gmail.com> wrote in message
news:%23eHdg0w...@TK2MSFTNGP05.phx.gbl...
Marc
"Ignacio Machin ( .NET/ C# MVP )" <ignacio.machin AT dot.state.fl.us> wrote
in message news:OWEuH$xrGH...@TK2MSFTNGP02.phx.gbl...
It does make sense, however, you have a coupling between the change that
occurs in your object, and the UI. Since that change happens on another
thread, you have to marshal that call to the proper thread to update the UI.
--
- Nicholas Paldino [.NET/C# MVP]
- m...@spam.guard.caspershouse.com
"Marc Gravell" <marc.g...@gmail.com> wrote in message
news:O9dFTIy...@TK2MSFTNGP05.phx.gbl...
Marc
Hope this helps somebody out there; feel free to tear it apart if I'm "off
on one"... Actually, most of this code is just ctor-forwarding! Shame MS
didn't build it in by default...
Marc
public class ThreadedBinding : Binding {
// ctors to match Binding
public ThreadedBinding(string propertyName, object dataSource,
string dataMember)
: base(propertyName, dataSource, dataMember) {
}
public ThreadedBinding(string propertyName, object dataSource,
string dataMember, bool formattingEnabled)
: base(propertyName, dataSource, dataMember, formattingEnabled)
{
}
public ThreadedBinding(string propertyName, object dataSource,
string dataMember, bool formattingEnabled, DataSourceUpdateMode
dataSourceUpdateMode)
: base(propertyName, dataSource, dataMember, formattingEnabled,
dataSourceUpdateMode) {
}
public ThreadedBinding(string propertyName, object dataSource,
string dataMember, bool formattingEnabled, DataSourceUpdateMode
dataSourceUpdateMode, object nullValue)
: base(propertyName, dataSource, dataMember, formattingEnabled,
dataSourceUpdateMode, nullValue) {
}
public ThreadedBinding(string propertyName, object dataSource,
string dataMember, bool formattingEnabled, DataSourceUpdateMode
dataSourceUpdateMode, object nullValue, string formatString)
: base(propertyName, dataSource, dataMember, formattingEnabled,
dataSourceUpdateMode, nullValue, formatString) {
}
public ThreadedBinding(string propertyName, object dataSource,
string dataMember, bool formattingEnabled, DataSourceUpdateMode
dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider
formatInfo)
: base(propertyName, dataSource, dataMember, formattingEnabled,
dataSourceUpdateMode, nullValue, formatString, formatInfo) {
}
// main purpose; detect x-thread operations and compensate
protected override void OnBindingComplete(BindingCompleteEventArgs
e) {
base.OnBindingComplete(e);
if (e.BindingCompleteContext ==
BindingCompleteContext.ControlUpdate
&& e.BindingCompleteState == BindingCompleteState.Exception
&& e.Exception is InvalidOperationException) {
if (Control != null && Control.InvokeRequired) {
Control.BeginInvoke(new MethodInvoker(ReadValue));
}
}
}
}