【问题标题】:Howto bind TextBox control to a StringBuilder instance?如何将 TextBox 控件绑定到 StringBuilder 实例?
【发布时间】:2009-01-05 16:30:34
【问题描述】:

我想要几个文本框来响应底层字符串的变化。因此,如果我要更改字符串的内容,所有这些文本框也会更改其内容。

现在,我不能使用 String 类型,因为它是不可变的。所以我选择了 StringBuilder。但是 TextBox 对象的 Text 属性只接受 String。

有没有一种简单的方法可以将 StringBuilder 对象“绑定”到 TextBox 的 Text 属性?

非常感谢!

PS:TextBox 目前是 WPF。但我可能会因为 Mono 而改用 Windows 窗体。

【问题讨论】:

  • PS:TextBox 目前是 WPF。但我可能会因为 Mono 而改用 Windows 窗体。

标签: c# .net string textbox stringbuilder


【解决方案1】:

您始终可以公开一个 getter 属性,该属性返回 Stringbuilder 的 ToString()。然后表单可以绑定到这个属性。

private StringBuilder _myStringBuilder;

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

【讨论】:

  • 感谢雷的回答。但是我仍然需要手动将 MyText 分配给 textbox.Text,不是吗?我正在寻找的是一个所有参与者共享相同内存位置的结构。
  • 如果您使用的是 WPF,则 TextBox 看起来像这样 并且绑定会为您解决这个问题。,
  • 如果文本框通过上述语法绑定到属性,WPF 将为您处理所有绑定。只要您在 INotifyPropertyChanged 的​​情况下通过调用 NotifyPropertyChanged("MyText) 来满足对 StringBuilder 的任何更改,一切都会好起来的。
  • 有了这些提示,我应该能够创建绑定。比你。我一直在寻找/希望在没有事件处理的情况下做到这一点,但现在意识到这是不可能的,因为在某些时候必须通知文本框它需要重新绘制。
  • 顺便说一句。查看您的描述:如果我想在控件之间绑定和取消绑定数据源,甚至在运行时添加具有相应绑定的新控件,该怎么办。我在寻找 textBox1.DataBinding.Add(...) 但 IntelliSense 不知道。有什么想法吗?
【解决方案2】:

这是我用来在 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 方法。

【讨论】:

  • 这很有用。注意 BindableStringBuilder 必须初始化:public BindableStringBuilder ErrorMessages { get ; } = new BindableStringBuilder();
【解决方案3】:

看来我之前的回答措辞不太好,因为很多人误解了我的意思,所以我会考虑到人们的cmets再试一次。

仅仅因为 String 对象是不可变的,并不意味着 String 类型的变量不能更改。如果一个对象具有 String 类型的属性,那么为该属性分配一个新的 String 对象会导致该属性发生变化(在我原来的答案中,我将此称为变量变异,显然有些人不同意使用术语“ mutate”在这种情况下)。

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 属性 发生了变异(或更改,如果您愿意的话)。

【讨论】:

    【解决方案4】:

    您可以继承文本框并覆盖 Text 属性以检索并写入字符串生成器。

    【讨论】:

    • 如果底层 StringBuilder 发生变化,我仍然需要发出和处理通知,不是吗?
    【解决方案5】:

    简单地说,不。 Text 属性只接受一个字符串。因此,无论来源如何,您都必须将其转换为字符串。

    为了使您能够轻松地为多个文本框设置一次,您可以拥有一个始终设置所有文本框值的类属性...

    public string MyString
    {
      get
      {
       ///... 
      }
      set 
      {
        textbox1.Text = value;
        textbox2.Text = value;
        //...
      }
    }
    

    【讨论】:

    • 我认为发布者正在寻找一种绑定数据的方法,并且没有要更新的具体文本框列表。 :)
    • @Ray,我知道你的意思,但他正在寻找一种同时更新多个控件的方法......这是一种方法。
    【解决方案6】:

    我会将StringBuilder 包装在一个自定义类中,其中包含Add 方法、Text 方法和OnChanged 事件。

    连接Add 方法,以便在调用它时将文本添加到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(); }
       }
    }
    

    【讨论】:

      【解决方案7】:

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

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

      但是,您需要将其包装在实现 INotifyPropertyChanged 的​​对象中。

      【讨论】:

      • 字符串对象不可变。像您刚刚所做的那样修改字符串时,真正发生的是创建了一个新字符串。
      • @Ray:我正要这么说:)
      • 雷 +1。字符串绝对是不可变的。每个字符串/字符串赋值都会在内存中分配新空间。
      • 字符串是不可变的并不会阻止您绑定到字符串类型对象的属性。这将是执行此操作的常用方法。
      • @Sam,Giraffe 根本没有解决绑定问题。他错误地谈论了字符串可变性(这是一个词吗?)。
      猜你喜欢
      • 2019-09-30
      • 1970-01-01
      • 2011-11-03
      • 2011-05-20
      • 2011-01-28
      • 1970-01-01
      • 2018-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多