如何将TextBox控件绑定到一个StringBuilder实例?

8
我希望有几个文本框能够对底层字符串的更改做出反应。因此,如果我更改了字符串的内容,所有这些文本框也会更改其内容。
现在,我不能使用String类型,因为它是不可变的。所以我选择了StringBuilder。但是TextBox对象的Text属性只接受String。
有没有一种简单的方法将StringBuilder对象“绑定”到TextBox的Text属性上?
非常感谢!
PS:TextBox目前是WPF。但是由于Mono的缘故,我可能会切换到Windows Forms。

PS:TextBox 目前是 WPF。但由于 Mono 的原因,我可能会转换到 Windows Forms。 - user51710
7个回答

11

你可以暴露一个属性,其getter返回Stringbuilder的ToString()方法。 然后,表单可以绑定到此属性。

private StringBuilder _myStringBuilder;

public string MyText
{
  get { return _myStringBuilder.ToString(); }
}

谢谢你的回答,Ray。但是我仍然需要手动将MyText分配给textbox.Text,不是吗?我正在寻找的是一种构造,所有参与者共享同一个内存位置。 - user51710
1
如果您正在使用WPF,TextBox将如下所示: <TextBox Text="{Binding Path=MyText}" />并且绑定会为您处理此操作。 - Ray Booysen
如果文本框通过上述语法绑定到属性,WPF将为您处理所有绑定。只要您通过调用NotifyPropertyChanged(“MyText”)来适应StringBuilder的任何更改(在INotifyPropertyChanged的情况下),一切都会很好。 - Ray Booysen
有了这些提示,我应该能够创建绑定。谢谢。我一直在寻找/希望找到一种方法来避免事件处理,但现在意识到这是不可能的,因为在某个时刻文本框必须被通知需要重新绘制。 - user51710
顺便问一下,看了你的描述:如果我想在运行时绑定和解除数据源到控件甚至添加新的带有相应绑定的控件怎么办? 我正在寻找textBox1.DataBinding.Add(...),但IntelliSense不知道。有什么想法吗? - user51710
显示剩余2条评论

10

这是我在WPF中将StringBuilder绑定到TextBox所使用的方法:

public class BindableStringBuilder : INotifyPropertyChanged
{
    private readonly StringBuilder _builder = new StringBuilder();

    private EventHandler<EventArgs> TextChanged;

    public string Text
    {
        get { return _builder.ToString(); }
    }

    public int Count
    {
        get { return _builder.Length; }
    }

    public void Append(string text)
    {
        _builder.Append(text);
        if (TextChanged != null)
            TextChanged(this, null);
        RaisePropertyChanged(() => Text);
    }

    public void AppendLine(string text)
    {
        _builder.AppendLine(text);
        if (TextChanged != null)
            TextChanged(this, null);
        RaisePropertyChanged(() => Text);
    }

    public void Clear()
    {
        _builder.Clear();
        if (TextChanged != null)
            TextChanged(this, null);
        RaisePropertyChanged(() => Text);
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
    {
        if (propertyExpression == null)
        {
            return;
        }

        var handler = PropertyChanged;

        if (handler != null)
        {
            var body = propertyExpression.Body as MemberExpression;
            if (body != null)
                handler(this, new PropertyChangedEventArgs(body.Member.Name));
        }
    }

    #endregion


}

在ViewModel中:

public BindableStringBuilder ErrorMessages { get; set; }
ErrorMessages.AppendLine("Missing Image: " + imagePath);

在Xaml中:

<TextBox Text="{Binding ErrorMessages.Text, Mode=OneWay}"/>

当然,如果需要的话,您可以公开其他 StringBuilder 方法。


3
这很有用。请注意,必须初始化BindableStringBuilder:public BindableStringBuilder ErrorMessages { get ; } = new BindableStringBuilder(); - blearyeye

9
看起来我的上一个答案表述得不太好,许多人误解了我的观点,所以我会根据人们的评论再试一次。
仅因为String对象是不可变的,并不意味着类型为String的变量不能被更改。如果一个对象有一个String类型的属性,那么将新的String对象赋给该属性会导致该属性发生变化(在我的原始答案中,我称之为变量突变,显然有些人不同意在这种情况下使用“突变”这个术语)。
WPF数据绑定系统可以绑定到此属性。如果通过INotifyPropertyChanged通知它属性发生更改,则它将更新绑定的目标,从而允许许多文本框绑定到相同的属性并在属性更新时全部更改,而无需任何其他代码。
因此,没有必要使用StringBuilder作为属性的后备存储。相反,您可以使用标准的String属性并实现INotifyPropertyChanged。
public class MyClass : INotifyPropertyChanged
{
    private string myString;

    public string MyString
    {
        get
        { return myString; }
        set
        {
            myString = value;
            OnPropertyChanged("MyString");
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        { handler(this, new PropertyChangedEventArgs(propertyName)); }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

WPF可以绑定到此,将自动捕捉并处理属性值的任何更改。不,String对象没有变异,但是String属性已经发生变异(或更改,如果您愿意这样说)。


1
你可以继承文本框并重写 Text 属性来检索和写入字符串构建器。

如果基础的 StringBuilder 发生了变化,我仍然需要发出和处理通知,对吗? - user51710

1
简单来说,不行。Text属性只接受字符串。所以无论源是什么,你都必须将其转换为字符串。
为了让你能够轻松地为许多文本框设置一次,你可以拥有一个类属性,它总是设置所有文本框的值...
public string MyString
{
  get
  {
   ///... 
  }
  set 
  {
    textbox1.Text = value;
    textbox2.Text = value;
    //...
  }
}

我认为发帖者正在寻找一种绑定数据的方法,而不是具体更新文本框列表的方法。 :) - Ray Booysen
@Ray,我知道你的意思,但他正在寻找一种同时更新多个控件的方法...而这是其中一种方法。 - Kon

0
我会用自定义类将 StringBuilder 包装起来,该类包含一个 Add 方法、Text 方法和一个 OnChanged 事件。
Add 方法与 StringBuilder 实例连接起来,这样当调用它时,它会将文本添加到 StringBuilder 实例并触发事件。然后当事件触发时,使用 Text 方法在 StringBuilder 上执行 ToString 操作。
public class StringBuilderWrapper
{
   private StringBuilder _builder = new StringBuilder();
   private EventHandler<EventArgs> TextChanged;
   public void Add(string text)
   {
     _builder.Append(text);
     if (TextChanged != null)
       TextChanged(this, null);
   }
   public string Text
   {
     get { return _builder.ToString(); }
   }
}

-5

您可以将TextBox的Text属性绑定到一个字符串属性... String对象是不可变的,但String类型的变量是完全可变的...

string mutable = "I can be changed";
mutable = "see?";

但是,您需要将其封装在实现INotifyPropertyChanged接口的对象中。


3
字符串对象是不可变的。当像您刚才所做的那样修改字符串时,实际上会创建一个新的字符串。 - Ray Booysen
@Ray:我正想说呢 :) - Christian C. Salvadó
不过,在我看来,答案的其余部分很糟糕(无意冒犯)。 - Kon
不太确定。关于与WPF绑定的正确答案的本质已经提到了。如果他说一个类型为String的属性可以被更改,并且这个属性是你需要绑定的(而不是继续讨论变量的可变性,其实是变化性),那么他就会得到一个好的答案。 - Sam Meldrum
有我看不到的编辑或者其他什么吗?Giraffe明确表示String对象是不可变的。我不确定这是否是一个真正“好”的答案,但我认为它不应该受到如此多的批评。 - Jon Skeet
显示剩余7条评论

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