how to change label content using mvvm pattern

允我心安 提交于 2019-12-13 06:49:01

问题


I try to do something easy but seems like i miss something. I try to change the content of a label when I click on a button. I do that using MVVM pattern. here is my code :

View :

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

        }

    }

at first the label is "etape1" as in the constructor, but then when i click on the next button the value doesn't change. ps : the fonction to change works because i put a breakpoint to see. Thanks for your help


回答1:


You have to use the property, not the backing field.

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;

    }

That will raise the notification for the change

Edit

It's also better using a ViewModelBase for your ViewModel

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

where you can define

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

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

Alternatives

As per this comment, we can introduce the CallerMemberName

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

Another useful variation is based on a selector, when you need to raise it for a dependent property from a different 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);
    }



回答2:


You need to raise the property changed event on the ui thread.

Replace

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

with

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

Also set the value for path not _path

Update

As pointed out in the comments, Dispatcher.Invoke is not necessary in this case since it's already on the UI thread.

Just call

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



回答3:


You are changing the value in:

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;

    }

But as You are setting _path, the setter in path is not ever called:

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

Change to:

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

}


来源:https://stackoverflow.com/questions/42349365/how-to-change-label-content-using-mvvm-pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!