WPF数据绑定不更新视图

31

我有一个文本块:

<TextBlock HorizontalAlignment="Left" Name="StatusText" Margin="0,20" TextWrapping="Wrap" Text="{Binding StatusText}">
            ... Status ...
</TextBlock>

代码后置文件:

public StatusPage()
{
    InitializeComponent();
    this.DataContext = new StatusPageViewModel(this);
}

并且在 viewModel 中:

private string _statusText;
/// <summary>
/// Status text
/// </summary>
public string StatusText
{
    get { return _statusText; }
    set { _statusText = value; }
}

并在viewModel中的函数中:

string statusText = Status.GetStatusText();
this.StatusText = statusText;

GetStatusText() 返回类似于 "工作完成" 的字符串等。该函数的返回值被赋给 this.StatusText,但是 TextBlock 的文本属性没有改变,仍然显示占位符 "... 状态 ..."。

我知道有类似这样的问题 --> 点击 <-- 但是在阅读了这篇文章之后,我仍然无法找到解决方案。

@更新

按照您的建议,我更新了我的代码,现在它是这样的:

public string StatusText
{
    get 
    {
        return _statusText;
    }
    set 
    {
        _statusText = value; 
        RaisePropertyChanged("StatusText");
    }
}

以及viewModel的声明:

 public class StatusPageViewModel : ObservableObject, INavigable

其中:

ObservableObject类是:

public abstract class ObservableObject : INotifyPropertyChanged
{
    #region INotifyPropertyChanged Members

    /// <summary>
    /// Raises the PropertyChange event for the property specified
    /// </summary>
    /// <param name="propertyName">Property name to update. Is case-sensitive.</param>
    public virtual void RaisePropertyChanged(string propertyName)
    {
        OnPropertyChanged(propertyName);
    }

    /// <summary>
    /// Raised when a property on this object has a new value.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raises this object's PropertyChanged event.
    /// </summary>
    /// <param name="propertyName">The property that has a new value.</param>
    protected virtual void OnPropertyChanged(string propertyName)
    {

        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }

    #endregion // INotifyPropertyChanged Members
}

但它仍然不起作用


你的问题解决了吗? - Mayur Dhingra
13
那么解决方案是什么呢?我正在面临同样的问题。已经实现了INotifyPropertyChanged,1way/2ways模式没有区别。 - mischka
我有同样的问题。在应用程序运行时更改绑定属性(例如OneWay/TwoWay)后,属性的getter被调用,一切看起来都很好,但是在重新启动后仍然无法正常工作。 - IngoB
这可能会帮助未来的某个人,但尽管我实现了接口,却忘记在我的MainWindow类声明中实际引用它。例如:MainWindow : Window, INotifyPropertyChanged - Reahreic
8个回答

42
您需要在ViewModel中实现INotifyPropertyChanged接口,以便通知View属性已更改。
这是相关的MSDN页面链接:System.ComponentModel.INotifyPropertyChanged 最重要的是,您应该在属性的setter方法中触发PropertyChanged事件。

2
尝试将Binding.Mode属性设置为OneWay - 可能是UI没有监听更新。 - toadflakz

10

添加双向绑定模式,因为默认情况下Textblock的绑定模式是单向的。

<TextBlock HorizontalAlignment="Left" Name="StatusText" Margin="0,20" TextWrapping="Wrap" Text="{Binding StatusText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            ... Status ...
</TextBlock>

当然,你还需要实现INotifyPropertyChanged接口,以便达到目的。请参考此链接了解如何进行实现。


18
将绑定设置为双向没有意义,因为控件无法更改文本,因此不需要能够更新源。 - sean.net

6

当使用DataModels时,必须确保模型在初始加载时完整。因此,如果您这样做:this.DataContext = mainViewModel,并且您的mainViewModel的某些部分未加载(= null),则无法将它们绑定。例如,我有一个模型,在该模型中有一个名为Program的对象。我将TextBlock的Text绑定到Model.Program.Name上。程序对象在初始加载时未连接,因此您必须在加载后重新绑定到一个已加载的对象,否则无法发送通知。


重新绑定到已加载的对象。这非常有帮助。谢谢。 - psl
你怎么重新绑定和重新加载它? - undefined

3

您的视图模型需要实现 INotifyPropertyChanged 接口,每当您的属性更改时(例如在 setter 中)都需要引发它。

如果没有这个接口,WPF 就无法知道属性已经更改。


2
我曾遇到这个问题,以下是我做错的地方...
注意:我的 INotifyPropertyChanged 编码是正确的。
在我的视图中,我有
<SomeView.DataContext>
    <SomeViewModel/>
<SomeView.DataContext/>

...这将调用VM的构造函数(创建一个实例指向/绑定到)。

在另一个类中(我的程序主窗口的ViewModel代码中),我也在实例化ViewModel,即:

private SomeAstractBaseViewModel someViewModel = new SomeViewModel();
private SomeAstractBaseViewModel someOtherViewModel = new SomeOtherViewModel(); 

他们都继承自一个超类,我在不同的实例之间来回切换,以显示主窗口部分中的不同视图 - 这是我的意图。
如何将所有这些连接起来是另一个问题,但当我删除问题所在的 xaml(在本答案顶部),该 xaml 之前实例化了另一个“未使用”的 ViewModel 实例时,视图将根据 INotifyPropertyChanged 机制进行更新。

2
感谢上帝有这篇文章。我曾经苦苦挣扎,试图找出如何阻止WPF创建每个视图模型对象的额外实例,并在每次交换视图时调用每个vm对象的ctor。我以为我已经做到了,但我还是决定再试一次,结果完美解决了问题。不知道为什么你会被踩,但还是谢谢你。 - dmscs

2
在我的情况下,一个 UserControl 嵌套在一个 View 中,当我尝试对 UserControl 内的属性进行 Binding 时,框架会在 ViewModel 中查找该属性,但实际上它并不存在。为了解决这个问题,我必须指定 ElementName

MyControl.xaml:

<UserControl
    ...
    x:Name="MyUserControl">
    ...
    <Label Content="{Binding MyProperty, ElementName=MyUserControl}"
    ...

MyControl.xaml.cs:

public partial class MyControl: UserControl, INotifyPropertyChanged
{
    ...
    private string _myProperty = "";

    public string MyProperty
    {
        get => _myProperty ;
        set
        {
            if (value == _myProperty ) return;
            _myProperty = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    ...

1
当您调用OnPropertyChanged时,请确保包括属性名称。今天我浪费了几个小时,这该死的隐藏绑定魔法真是令人沮丧。给我一个错误消息或其他东西,而不是默默地失败!
因此,不要只调用没有提供参数的OnPropertyChanged(),而是在属性setter内调用它(这样CallerMemberName会为您填充),或者自己提供参数。
例如,在xaml中的某个地方,您有绑定:
... Value="{Binding Progress}" ...

然后在代码后台,您有一个从INotifyPropertyChanged继承的DataContext对象,在更新值后的某个方法中调用:

OnPropertyChanged("Progress");

0
在我的情况下,问题是当我调用 OnPropertyChanged 时,我传递了错误的成员名称。例如:

不正确的

        private Student std = new Student();
        public Student Std { 
            get { return std; } 
            set { std = value; OnPropertyChanged("Student"); } 
}

正确

        private Student std = new Student();
        public Student Std { 
            get { return std; } 
            set { std = value; OnPropertyChanged("Std"); } 
}

这里我需要传递属性名称而不是类名


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