TextBlock Text属性在更新其源绑定属性时没有更新?

时间:2016-09-27 04:37:58

标签: c# wpf xaml binding properties

我在XAML中有以下代码片段。

Communication link failure due to underlying exception:
**BEGIN NESTED EXCEPTION**

java.net.ConnectionException
MESSAGE: connection timed out......

这将生成以下输出,如图1所示。

Figure1.

这背后的代码如下所示。

<Grid>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="01*"/>
        <ColumnDefinition Width="03*"/>
        <ColumnDefinition Width="01*"/>
    </Grid.ColumnDefinitions>

    <Button Name="btnPrevious" Grid.Column="0" Content="Previous" Click="btnPrevious_Click"/>
    <TextBlock Name="txtBlockName" Grid.Column="1" Text="{Binding SelectedName, UpdateSourceTrigger=PropertyChanged}" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center"/>
    <Button Name="btnNext" Grid.Column="2" Content="Next"  Click="btnNext_Click"/>

</Grid>

public partial class MainWindow : Window, INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public List<string> namesList = new List<string>(); public string SelectedName { get { return namesList[1]; } set { if (value != namesList[1]) { namesList[1] = value; NotifyPropertyChanged("SelectedName"); } } } public MainWindow() { InitializeComponent(); namesList.Add("ABC"); namesList.Add("DEF"); namesList.Add("GHI"); this.DataContext = this; } private void btnPrevious_Click(object sender, RoutedEventArgs e) { string str = namesList[0]; namesList[0] = namesList[1]; namesList[1] = str; this.IsEnabled = false; } private void btnNext_Click(object sender, RoutedEventArgs e) { string str = namesList[2]; namesList[2] = namesList[1]; namesList[1] = str; this.IsEnabled = false; } protected void NotifyPropertyChanged(String propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } 的{​​{1}}属性绑定到Text属性。并且没有更新TextBlock属性的更新。虽然我的类实现了SelectedName接口并定义了它的SelectedName行为,但它仍然无法正常工作。

在调试时,我设置了一些断点来监视INotifyPropertyChanged属性值,我发现它根据需要进行了更新,但NotifyPropertyChanged的{​​{1}}属性没有更新。如图2所示。

Figure2.

我在互联网上看到了很多问题和解决方案,但没有解决我的问题。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:1)

在目前为止给出的两个答案中,唯一一个与我认为合理的替代方案相近的是this answer中提出的第二个选项(即在&#34之后;如果你想要的话要更新&#39; SelectedName你可以&#34; )。

那就是说,在我看来,你最好改变你的数据结构,以便更接近地模拟用户界面中实际发生的事情。如果您花时间这样做,您的代码将更容易编写和理解,并且会更简单。

在这个特定的例子中,这意味着,因为(看起来)你希望能够浏览三个不同的文本值,你应该只使用该属性来呈现当前文本值,并使用索引变量来保持跟踪选择的值。例如:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    // Don't make fields public. If you do want to expose the list
    // use a public read-only property. And even there, think carefully
    // about whether you want callers to be able to modify the list; you
    // can return a `ReadOnlyCollection<string>` that wraps your list if
    // you only want callers to be able to examine the contents, rather than
    // modify it.
    //
    // Also, make any field that you never change "readonly".
    private readonly List<string> namesList = new List<string>();

    // Here's the index that keeps track of which string is selected
    private int _index;

    public string SelectedName
    {
        get { return namesList[_index]; }
    }

    public MainWindow()
    {
        InitializeComponent();

        namesList.Add("ABC");
        namesList.Add("DEF");
        namesList.Add("GHI");

        this.DataContext = this;
    }

    private void btnPrevious_Click(object sender, RoutedEventArgs e)
    {
        if (_index > 0)
        {
            _index--;
            NotifyPropertyChanged(nameof(SelectedName));
        }

        // I don't know what you expect this to do. You are setting the window's
        // "IsEnabled" property to false, which doesn't seem useful. More likely,
        // you intend to set the "Previous" button's enabled state, but even there
        // you really only want to set it to false if the _index value is 0. If the
        // _index value had been 2 and was just set to 1, you still want the
        // "Previous" button enabled. This would actually be an excellent place for
        // you to learn how to implement an ICommand, to have its CanExecute() method
        // return a value based on the _index value (where the "Previous" ICommand
        // object would return true unless _index is 0, and the "Next" ICommand
        // object would return true unless _index is namesList.Count - 1). Then you
        // can bind the button's "Command" property to the appropriate ICommand
        // object for the button and WPF will automatically deal with enabling
        // or disabling the button according to the command's state
        this.IsEnabled = false;
    }

    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
        if (_index < namesList.Count - 1)
        {
            _index++;
            NotifyPropertyChanged(nameof(SelectedName));
        }

        // See comment above.
        this.IsEnabled = false;
    }

    protected void NotifyPropertyChanged(String propertyName)
    {
        PropertyChanged?.DynamicInvoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

请注意,对于其他两个答案中的任何一个中提出的任何更改,您仍然会遇到与每个文本值之间的导航相关的错误。初始点击按钮可能会起作用,但之后您的数据结构会不连贯,您将无法获得所需的结果。

本回答中的示例解决了所有这些问题以及您最初的担忧。

答案 1 :(得分:0)

您永远不会设置“SelectedName”,您可以将其设为只读。

永远不会引发PropertyChanged。

如果您希望绑定通知它,您应该

NotifyPropertyChanged("SelectedName");

更改_namesLIst的索引#1后,

public partial class MainWindow : Window , INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private readonly List<string> _namesList = new List<string>();

    public string SelectedName
    {
        get { return _namesList[1]; }            
    }

    public MainWindow()
    {
        InitializeComponent();

        _namesList.Add("ABC");
        _namesList.Add("DEF");
        _namesList.Add("GHI");

        DataContext = this;
    }

    private void btnPrevious_Click(object sender, RoutedEventArgs e)
    {
        var str = _namesList[0];
        _namesList[0] = _namesList[1];
        _namesList[1] = str;
        NotifyPropertyChanged(nameof(SelectedName));
        IsEnabled = false;
    }

    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
        var str = _namesList[2];
        _namesList[2] = _namesList[1];
        _namesList[1] = str;
        NotifyPropertyChanged(nameof(SelectedName));

        IsEnabled = false;
    }

    private void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

如果你想'更新'SelectedName,你可以

    public string SelectedName
    {
        get { return _namesList[1]; }
        set
        {
            if (value == _namesList[1]) return;
            _namesList[1] = value;
            NotifyPropertyChanged("SelectedName");
        }
    }

    private void btnPrevious_Click(object sender, RoutedEventArgs e)
    {
        var str = _namesList[0];
        _namesList[0] = _namesList[1];
        SelectedName = str;            
        IsEnabled = false;
    }

    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
        var str = _namesList[2];
        _namesList[2] = _namesList[1];
        SelectedName = str;            
        IsEnabled = false;
    }

答案 2 :(得分:0)

从未调用过SelectedName Setter属性。所以你可以使用BindingExpression.UpdateSource()方法更新TextBlock.Text,如下所示,

 private void btnPrevious_Click(object sender, RoutedEventArgs e)
    {
        string str = namesList[0];
        namesList[0] = namesList[1];
        namesList[1] = str;
        BindingExpression be = txtBlockName.GetBindingExpression(TextBlock.TextProperty);
        be.UpdateSource();

        this.IsEnabled = false;
    }