【问题标题】:How can I have controls bound to a property change when the property changes by data received over network?当通过网络接收的数据更改属性时,如何将控件绑定到属性更改?
【发布时间】:2020-07-09 10:30:23
【问题描述】:

我有一类用户,它们具有一些属性,例如姓名、年龄、性别和消息 所以我有一个表单,其中为每个用户的每个属性动态创建了一些文本框,并将每个文本框绑定到适当的属性。

当用户连接到我的程序并更改其属性时,文本框不会更改。

这是我的用户类:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace binding_network
{
    class user : INotifyPropertyChanged
    {
        private string _name;

        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    NotifyPropertyChanged();
                }
            }
        }
        private int _age;

        public int Age
        {
            get { return _age; }
            set
            {
                if (_age != value)
                {
                    _age = value;
                    NotifyPropertyChanged();
                }
            }
        }
        private string _message;

        public string Message
        {
            get { return _message; }
            set
            {
                if (_message != value)
                {
                    _message = value;
                    NotifyPropertyChanged();
                }
            }
        }
        private string _gender;

        public string Gender
        {
            get { return _gender; }
            set
            {
                if (true)
                {
                    _gender = value;
                    NotifyPropertyChanged();
                }
            }
        }




        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

这是我的表单代码:

public partial class Form1 : Form
{
    private BindingSource userBindingSource = new BindingSource();
    BindingList<user> userList = new BindingList<user>();
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        ///some code to create textboxes dynamically....

        txtName.DataBindings.Clear();
        txtName.DataBindings.Add("text", userBindingSource[userIndex], "name");
        txtAge.DataBindings.Clear();
        txtAge.DataBindings.Add("text", userBindingSource[userIndex], "age");
        txtGender.DataBindings.Clear();
        txtGender.DataBindings.Add("text", userBindingSource[userIndex], "gender");
        txtMessage.DataBindings.Clear();
        txtMessage.DataBindings.Add("text", userBindingSource[userIndex], "message");
    }
}

通过这种方法,我通过网络接收数据

private void GetMessage(object obj)
{
    user user1 = (user)obj;
    try
    {
        while (true)
        {
            byte[] buffer = new byte[1024];
            int rec = user1.SocketClient.Receive(buffer, 0, buffer.Length, 0);
            Array.Resize(ref buffer, rec);
            if (rec > 0)
            {
                user1.Name = BitConverter.ToString(buffer, 0);
                user1.Gender = BitConverter.ToString(buffer, 80);
                user1.Age = BitConverter.ToInt32(buffer, 96);
                user1.Message = BitConverter.ToString(buffer, 160);
            }
        }

    }
    catch (Exception ex)
    {

        MessageBox.Show(ex.ToString());
    }

}

但收到数据后文本框不刷新

【问题讨论】:

    标签: c# winforms sockets data-binding


    【解决方案1】:

    似乎缺少很多代码,但这里有一些东西......

    userBindingSource 尚未连接到 userList

    绑定propertyNamedataMember 参数的大小写不正确。

    userIndex 未定义。

    即使是这样,绑定到userBindingSource[userIndex] 也不允许导航源(也许你可以接受)。

    所以让我们解决这些问题:

    public partial class Form1 : Form
    {
        private BindingSource userBindingSource = new BindingSource();
        BindingList<user> userList = new BindingList<user>();
        int userIndex = 0;
    
        public Form1()
        {
            InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
            userBindingSource.DataSource = userList;
            userIndex = userBindingSource.Position;
    
            ///some code to create textboxes dynamically....
    
            txtName.DataBindings.Clear();
            txtName.DataBindings.Add("Text", userBindingSource, "Name");
            txtAge.DataBindings.Clear();
            txtAge.DataBindings.Add("Text", userBindingSource, "Age");
            txtGender.DataBindings.Clear();
            txtGender.DataBindings.Add("Text", userBindingSource, "Gender");
            txtMessage.DataBindings.Clear();
            txtMessage.DataBindings.Add("Text", userBindingSource, "Message");
        }
    }
    

    假设userList 已被填充,您现在可以像这样导航userBindingSource

    // However you're tracking userIndex, or maybe...
    // userIndex = userList.IndexOf(user1);
    userBindingSource.Position = userIndex;
    

    或以下任何一种:

    userBindingSource.MoveFirst();
    userBindingSource.MovePrevious();
    userBindingSource.MoveNext();
    userBindingSource.MoveLast();
    

    最后,移除GetMessage 中的无限while (true) 循环。

    此时,如果您的数据被正确接收和解析,您的TextBox 控件应该会更新。

    编辑...

    所以你是多线程的,这很棒。

    现在我们必须确保导致 UI 更改的所有操作都在正确的线程上完成。

    让我们这样做(假设GetMessageForm 类中定义):

            if (rec > 0)
            {
                var name = BitConverter.ToString(buffer, 0);
                var gender = BitConverter.ToString(buffer, 80);
                var age = BitConverter.ToInt32(buffer, 96);
                var message = BitConverter.ToString(buffer, 160);
    
                this.Invoke(new Action(() =>
                {
                    user1.Name = name;
                    user1.Gender = gender;
                    user1.Age = age;
                    user1.Message = message;
                }));
            }
    

    还有这个:

    catch (Exception ex)
    {
        this.Invoke(new Action(() => MessageBox.Show(ex.ToString())));
    }
    

    并绑定源导航(如果在不同的线程上):

    this.Invoke(new Action(() => userBindingSource.Position = userIndex));
    

    您也可以考虑使用BeginInvoke 方法。

    【讨论】:

    • 感谢您的回答是的,我使用userIndex = userList.IndexOf(user1);,但它不起作用。因为我在不同的线程中使用GetMessage 方法,所以当属性更改时,编译器会抛出异常并显示此消息:跨线程操作无效。控件 "" 从创建它的线程以外的线程访问。
    • 感谢亲爱的@rfmodulator 的回答,我们使用多线程来拥有更轻的主线程,但在线程中,我们再次将主函数Action(() =&gt; 传递给主线程。这种多线程编程有什么好处??有什么方法可以处理自己线程中的方法吗??
    • @robert 这就是我们如何将操作编组回 UI 线程以避免“跨线程异常”导致 Control 的状态被更改。在这里,我在 other 线程中做了所有可能的事情,以尽量减少 UI 线程上的负载。您可能会看到几种不同的方式来完成它,但基本原理是相同的。这是一个简单的解释,我建议更多地研究这个主题。
    • 亲爱的@rfmodulator 你对使用CheckForIllegalCrossThreadCalls = false有什么想法?它对我的程序有一些负面影响吗?如果可能的话,请给我一些参考以进行更多调查……最后,我很抱歉我的英语不好,再次感谢亲爱的@rfmodulator。
    • @robert 不要设置CheckForIllegalCrossThreadCalls = false,最终你的程序会不可靠,你也不知道为什么。有传言说它是在 .NET 2.0 中引入的,以提供与 .NET 1.1 的向后兼容性……古老的历史。不要从后台线程直接与 UI 组件交互。您可以搜索“WinForms 多线程”了解更多信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 2017-07-07
    相关资源
    最近更新 更多