从AntdUI的NotifyProperty开始设计数据绑定
Winform下实时刷新界面控件上的内容是个高频需求,一般都是用Invoke来实现。比如在界面上刷新日志,可以这样写:
Invoke((new Action(() =>
{
String msg = "串口消息" + Environment.NewLine;
txtReceiveMessage.AppendText(string.Format("{0} - {1}",DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"),msg));
})));
但是在一堆业务逻辑里突然出现这么一段,很破坏连贯性。如果是实时展示多个传感器数据,要写多个Invoke,更烦。
其实C#早就有了数据绑定功能,绑定数据之后,更新对象的属性值,绑定控件的值也会随之刷新,但是,这个功能不支持异步,一旦异步就报“System.InvalidOperationException:线程间操作无效”。我一直以为是微软的老毛病,功能只做了一半。
最近看AntdUIDemo ,发现他是用数据绑定来实现界面刷新,而且可以跨线程更新,就去翻了他的源码,发现他的控件重写了Control.Text属性,改成这样:
[Editor(typeof(MultilineStringEditor), typeof(UITypeEditor))]
[Description("文本")]
[Category("Appearance")]
[DefaultValue(null)]
public override string? Text
{
get
{
return this.GetLangI(LocalizationText, text);
}
set
{
if (!(text == value))
{
text = value;
if (BeforeAutoSize())
{
Invalidate();
}
OnTextChanged(EventArgs.Empty);
OnPropertyChanged("Text");
}
}
}
数据类上继承INotifyPropertyChanged,控件类上也继承INotifyPropertyChanged,在主线程上又加了一次OnPropertyChanged("Text")。所以要支持异步,得每个项目都要重写相关控件?太麻烦了。
看到这里,我的折腾心上来了,真就没有更简单一点的方法了吗?还真找到了,AsyncOperation,这是一个用来跟踪异步操作的生存期的类,其中的Post函数,可以从异步操作中向主进程发送消息。也就是说,异步修改属性后,通过AsyncOperation类把“数据改动”这个消息发送到主界面上,就能正常展示。
- 先创建一个AsyncProperty类
public class AsyncProperty : INotifyPropertyChanged
{
protected AsyncOperation operation;
public event PropertyChangedEventHandler PropertyChanged;
public AsyncProperty()
{
operation = AsyncOperationManager.CreateOperation(null);
}
// 通过Post函数,将属性更改消息发送到主进程
public void PostProperty(string propertyName)
{
operation.Post(new System.Threading.SendOrPostCallback(_ =>
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}), null);
}
}
- 继承AsyncProperty类,创建一个User类
public class User : AsyncProperty
{
private string name;
private int age = 0;
private string address;
public string Name
{
get { return name; }
set
{
if (name == value) return;
name = value;
PostProperty(nameof(Name));
}
}
public int Age
{
get { return age; }
set
{
if (age == value) return;
age = value;
PostProperty(nameof(Age));
}
}
public string Address
{
get { return address; }
set
{
if (address == value) return;
address = value;
PostProperty(nameof(Address));
}
}
}
这时候把用户属性绑定到原生控件Label的Text上,创建一个异步线程,修改用户属性,Label上就能即时刷新值,不需要Invoke了。

浙公网安备 33010602011771号