乐水悠悠

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

从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了。

posted on 2026-07-15 10:54  金币闪耀  阅读(19)  评论(0)    收藏  举报