从字符串到TextBox的XAML绑定无法正常工作

时间:2011-11-18 18:01:02

标签: c# .net wpf data-binding

我是一个C#新手,他在理解为什么他第一次尝试理解XAML绑定无效时遇到了问题。我正在关注微软的Data Binding Overview

我有一个TextBox,最终将作为状态窗口。现在我只想能够将任意文本字符串写入其中。我还有一个我正在测试的命令模式的实例。我的命令包括将一个随机数添加到累加器并将结果打印到状态视图。

这是我的主应用程序窗口的XAML:

<Window x:Class="Experiment.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:Experiment"
        Title="Test" Height="500" Width="700" Name="Test" Closing="Test_Closing" DataContext="{Binding ElementName=statusText}" xmlns:my="clr-namespace:Experiment">
    <Window.Resources>
        <c:StatusViewText x:Key="statusViewText" />
    </Window.Resources>
    <DockPanel HorizontalAlignment="Stretch" Margin="0,0,0,0" Width="Auto">
        <!-- Elements deleted for brevity. -->
        <TextBox Margin="5,5,5,5" Name="statusText" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" AcceptsReturn="True" AcceptsTab="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" FontFamily="Courier New" FontSize="12"
                 Text="{Binding Source={StaticResource statusViewText}, Path=statusTextString, Mode=OneWay}"/>
    </DockPanel>
</Window>

代表我的数据的类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Experiment {
    public class StatusViewText {
        public StringBuilder statusText { get; set; }
        public String statusTextString { get; set; }

        public StatusViewText() {
            statusText = new StringBuilder();
        }

        public void Append(string s) {
            if (s != null) {
                statusText.Append(s);
            }

            statusTextString = statusText.ToString();
        }

        public void AppendLine(string s) {
            if (s != null) {
                statusText.AppendLine(s);
            }

            statusTextString = statusText.ToString();
        }
    }
}

最终我会在StringBuilder中使用一个合适的转换器,但我想在探索这种复杂性之前先把原理弄清楚。

如果我的理解是正确的(显然不是),这一切都应该有效。绑定目标是TextBox,target属性是Text属性。绑定源是StatusViewText类的statusTextString属性。然而,当我运行命令(并调试并查看正在更新的StatusViewText.statusTextString)时,TextBox不会更新。

我认为我可能需要自己明确添加绑定,所以我尝试在主窗口构造函数中添加InitializeComponent()之后:

        statusViewText = new StatusViewText();
        Binding statusViewTextBinding = new Binding("statusTextString");
        statusViewTextBinding.Source = statusViewText;
        statusText.SetBinding(TextBlock.TextProperty, statusViewTextBinding);

但这也不起作用。

我是否需要触发PropertyChanged事件?我认为绑定框架的重点是事件在幕后自动触发和消耗;但也许我也错了。

输出窗口中没有明显的错误,这让我相信我的绑定语法是正确的;我只是遗漏了别的东西。

HALP!

编辑13:14 EDT 谢谢 mben

好的,我做到了。添加了以下内容:

public class StatusViewText : INotifyPropertyChanged {
    public void Append(string s) {
        if (s != null) {
            statusText.Append(s);
        }

        statusTextString = statusText.ToString();
        NotifyPropertyChanged("statusTextString");
    }

    public void AppendLine(string s) {
        if (s != null) {
            statusText.AppendLine(s);
        }

        statusTextString = statusText.ToString();
        NotifyPropertyChanged("statusTextString");
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info) {
        if (PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

我进行了调试以验证它是否正在通过此代码路径,而且确实如此。但仍然没有运气。还有其他想法吗?

3 个答案:

答案 0 :(得分:3)

以下是您必须应用的更改才能让它正常工作。

以下是ViewModel的代码:

public class StatusViewText : INotifyPropertyChanged
{
    public StatusViewText()
    {
        // At least, I have a default value
        this.StatusTextString = "Hello world";
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    private string statusTextString;
    public string StatusTextString
    {
        get { return this.statusTextString; }
        set
        {
            this.statusTextString = value;
            this.OnPropertyChanged("StatusTextString");
        }
    }
}

您必须指定窗口的DataContext。代码隐藏是一个解决方案:在MainWindow的ctor中,填写数据上下文:

this.DataContext = new StatusViewText();

在XAML中,您应该说明对属性StatusTextString的绑定。它的工作原理是因为数据上下文是StatusViewText的一个实例。

Text="{Binding Path=StatusTextString}"

答案 1 :(得分:2)

您仍需要在StatusViewText上实现INotifyPropertyChanged。 绑定系统不会持续检查值,您需要在事情发生变化时通知它。

答案 2 :(得分:1)

请注意,您在代码中创建的实例与Xaml中表达的实例不同。您可以通过在StatusViewText的构造函数中设置断点来证明这一点。

将此片段放入MainWindow的构造函数中......

        StatusViewText o = (StatusViewText)FindResource("statusViewText") as StatusViewText;
        if(o!=null)
        {
            o.Append("hello");   
        }

这会影响您班级的正确实例。

你也应该改变你的课程,看起来像这样...

public  class StatusViewText:INotifyPropertyChanged
{
    public StringBuilder statusText { get; set; }
    private string _statusTextString;
    public String statusTextString
    {
        get { return _statusTextString; }
        set 
        { 
            _statusTextString = value; 
            OnPropertyChanged("statusTextString");
        }
    }
    public StatusViewText()
    {
        statusText = new StringBuilder();
    }
    public void Append(string s)
    {
        if (s != null)
        {
            statusText.Append(s);
        }
        statusTextString = statusText.ToString();
    }
    public void AppendLine(string s)
    {
        if (s != null)
        {
            statusText.AppendLine(s);
        }
        statusTextString = statusText.ToString();
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string prop)
    {
        if(PropertyChanged!=null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }
}