将标签绑定到一个“变量”

28

假设我有一个名为X的全局变量INT。由于X是全局的,我们可以假定任何东西都可以修改它的值,因此它会被不断地改变。

假设我有一个名为"label"的Label控件。这是我想要实现的目标:

我想要将label.Text的值与变量X "绑定"起来。以这样的方式,当变量X被更改时,它将反映回label.Text。

现在,我不想编写事件监听器并使用委托来完成这个任务(我希望代码量尽可能少)。是否有一种方法可以使用DataBinding组件来完成这个任务?或者其他新技术?


2
当你说全局变量时,你是指类中的静态变量/属性之类的东西吗? - Gishu
@Gishu:是的,就像 public int X 一样。 - Ian
请注意,这被称为“类实例变量”。 - Noldorin
4个回答

37

如果您想使用数据绑定基础结构,并反映对值所做的更改,您需要一种方法来通知UI有关绑定值所做的更改。

因此,最好的方法是使用属性并实现INotifyPropertyChanged接口,像这样:

class frmFoo : Form, INotifyPropertyChanged
{        
    private string _foo;

    public string Foo
    {
        get { return _foo; }
        set
        {
            _foo = value;
            OnPropertyChanged("Foo");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

同时要记得首先在标签上设置绑定:

public frmFoo()
{
    InitializeComponent();
    lblTest.DataBindings.Add(new Binding("Text", this, "Foo"));
}

2
我认为这个答案正是Ian所需要的。 也许在检查是否为空之前创建一个PropertyChanged的副本会更好,否则它可能会在被调用之前变成null,就像这样: <code> protected virtual void OnPropertyChanged(string property) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(property)); } } </code> (有关详细信息,请参见https://dev59.com/pHRB5IYBdhLWcg3wSVYI#672666) - Simon D.
我同意,这是更好的PropertyChanged模式参考实现,支持多线程场景。 - Iñaki Elcoro

10

对于一个多线程程序(因此几乎所有的Windows Forms程序都是),iCe的答案并不是一个好的答案,因为它无论如何都不能让您改变标签(您会得到一些跨线程错误)。最简单的解决方法是在Setter中创建属性:

private string _labelText;
public string labelText
{
    get { return _labelText; }
    set
    {
        _labelText = value;
        updateLabelText(_labelText); //setting label to value
   }
}

updateLabelText(string)是线程安全的:

delegate void updateLabelTextDelegate(string newText);
private void updateLabelText(string newText)
{
     if (label1.InvokeRequired)
     {
          // this is worker thread
          updateLabelTextDelegate del = new updateLabelTextDelegate(updateLabelText);
          label1.Invoke(del, new object[] { newText });
     }
     else
     {
          // this is UI thread
          label1.Text = newText;
     }
}

谢谢!在我看来,这是最简单和最好的解决方案。 - user2979010

4
我认为你不能绑定一个公共变量。一个变量本身没有通知监听器其值更改的能力。
这就是为什么你需要将变量包装在属性中。在setter中,你会引发一个事件来通知绑定到它的UI控件,以便它们可以刷新并显示新值。框架有一个机制 - INotifyPropertyChanged - 请尝试此链接了解如何操作。

1
创建一个X属性。在setter中更新label.Text属性。
private int _x;
public int X {
    get 
    { 
        return _x; 
    } 
    set 
    { 
        _x = value;
        label.Text = _x.ToString();
    }
}

2
如果您在不是UI线程的线程中执行此操作,将会触发“跨线程操作无效”异常。 - Pic Mickael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接