提问者:小点点

如何使用mvvm模式更改标签内容


我试着做一些简单的事情,但似乎我错过了一些事情。 我尝试在单击按钮时更改标签的内容。 我使用MVVM模式来实现这一点。 下面是我的代码:

查看:

    <Button x:Name="buttonNext" 
        HorizontalAlignment="Center" 
        VerticalAlignment="Center"
        Grid.Column="1"
        Grid.Row="2"
        Width="85" 
        Height="35"
        Style="{StaticResource AccentedSquareButtonStyle}"
        Command="{Binding Path=Next}">
        <TextBlock Text="Next"
               TextWrapping="Wrap"
               TextAlignment="Center"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"/>
    </Button>
<Label Name="Path"
       Grid.ColumnSpan="2"
       HorizontalAlignment="Center"
       VerticalAlignment="Center"
       Content="{Binding path}"
       FontWeight="Bold"
       Foreground="DeepSkyBlue"
       />

ViewModel:

    public ICommand Next { get; set; } 
    private string _path;
    public string path
    {
        get
        {
            return _path;
        }
        set
        {
            _path = value;

            RaisePropertyChanged("path");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = null;

    protected virtual void RaisePropertyChanged(string propName)
    {
        if (PropertyChanged != null)
        {
            Task.Run(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
        }
    }

    public page4ViewModel(NavigationViewModel navigationViewModel)
    {
      _path = "etape1";
       Next = new BaseCommand(GoNext);
    }

    private void GoNext(object obj)
    {
        switch (_path)
        {
            case "etape1":
                _path = "etape2";
                break;
            case : "etape2"
                _path = "etape3";
                break;
            case "etape3":
                _path = "etape4";
                break;
            default:
                _path = " ";
                break;

        }

    }

一开始,标签是“etape1”,就像构造函数中一样,但是当我单击next按钮时,值没有改变。 ps:功能改变工作,因为我放了一个断点看。 谢谢你的帮助


共3个答案

匿名用户

您必须使用属性,而不是备份字段。

private void GoNext(object obj)
{
    switch (_path)
    {
        case "etape1":
            path = "etape2"; // without underscore
            break;
        case : "etape2"
            path = "etape3";
            break;
        case "etape3":
            path = "etape4";
            break;
        default:
            path = " ";
            break;

    }

这将引发更改通知

编辑

对ViewModel使用ViewModelBase也更好

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

您可以在其中定义

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        this.VerifyPropertyName(propertyName);

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

替代方案

根据这个注释,我们可以引入CallerMemberName

protected virtual void RaisePropertyChanged<T>([CallerMemberName] string propertyName = null)

另一个有用的变体是基于选择器的,当您需要从不同的setter为依赖属性引发选择器时

    protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        RaisePropertyChanged(body.Member.Name);
    }

匿名用户

您需要在ui线程上引发property changed事件。

替换

if (PropertyChanged != null)
    {
        Task.Run(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
    }

if (PropertyChanged != null)
    {
        Application.Current.Dispatcher.Invoke(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
    }

还设置path而不是_path的值

更新

正如注释中指出的,Dispatcher.invoke在这种情况下不是必需的,因为它已经在UI线程上了。

打电话就行了

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

匿名用户

您正在更改中的值:

private void GoNext(object obj)
{
    switch (_path)
    {
        case "etape1":
            _path = "etape2";
            break;
        case : "etape2"
            _path = "etape3";
            break;
        case "etape3":
            _path = "etape4";
            break;
        default:
            _path = " ";
            break;

    }

但是在设置_path时,不会调用path中的setter:

public string path
{
    set ///This is never called
    {
        _path = value;
        RaisePropertyChanged("path");
    }
}

更改为:

switch (path)
{
    case "etape1":
        path = "etape2";
        break;
    case : "etape2"
        path = "etape3";
        break;
    case "etape3":
        path = "etape4";
        break;
    default:
        path = " ";
        break;

}