【问题标题】:how to change label content using mvvm pattern如何使用 mvvm 模式更改标签内容
【发布时间】:2017-07-10 00:22:49
【问题描述】:

我尝试做一些简单的事情,但似乎我错过了什么。单击按钮时,我尝试更改标签的内容。我使用 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"
       />

视图模型:

    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”,但是当我单击下一个按钮时,值不会改变。 ps:更改的功能有效,因为我放了一个断点来查看。感谢您的帮助

【问题讨论】:

  • 你为什么在一个任务中提高你的propertychanged async?

标签: c# wpf button mvvm label


【解决方案1】:

您必须使用属性,而不是支持字段。

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);
        }
    }

替代品

根据this的评论,我们可以介绍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);
    }

【讨论】:

  • 您可能还想将CallerMemberName 属性添加到您的 RaisePropertyChanged 方法中,这样就可以在不显式指定属性名称的情况下调用它(因此无需验证属性名称)。
  • @Clemens 当然,谢谢。由于有时还需要从另一个 setter 调用依赖属性的更改,所以我更喜欢在我的 ViewModelBase 中添加另一个带有 Expression&lt;Func&lt;T&gt;&gt; selectorExpression 的版本
【解决方案2】:

您需要在 ui 线程上引发属性更改事件。

替换

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

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

同时设置path 的值而不是_path

更新

正如 cmets 中所指出的,在这种情况下不需要 Dispatcher.Invoke,因为它已经在 UI 线程上。

只要打电话

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

【讨论】:

  • 你真的需要Dispatcher吗?我几乎可以肯定,如果您执行“NextCommand”,您已经在 UI 线程中......
  • 不,你是对的。它已经在 UI 线程中。我只是想说要特别避免使用 Task.Run。
  • Task.RunDispatcher.Invoke 在这里都没有意义。不用检查PropertyChanged != null,而是将整个RaisePropertyChanged 方法体替换为PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
  • @Connell.O'Dennell 感谢您的回答,但不行,仍然不起作用,他是否应该进入“如果”条件?因为他没有....
  • @Json 你的 ViewModel 类是否真的实现了INotifyPropertyChanged,还是只声明了一个PropertyChanged 事件?
【解决方案3】:

您正在更改以下值:

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 中的设置器永远不会被调用:

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;

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-18
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多