另一个视图模型中的RaisePropertyChanged

本文关键字:RaisePropertyChanged 模型 视图 另一个 | 更新日期: 2023-09-27 18:04:06

我有一个主ViewModel

public class MainViewModel : ViewModelBase
{
    public MenuViewModel MenuVM { get; set; }
    public StatusBarViewModel StatusBarVM {get; set; }
}

每个子视图模型都有一个绑定在视图上的属性:

public class MenuViewModel
{
    private string _property1;
    public  string  Property1
    {
        get { return _property1; }
    }
}

public class StatusBarViewModel
{
    private string _property2;
    public  string  Property2
    {
        get { return _property2; }
        set
        {
            _property2 = value;
            RaisePropertyChanged("Property2");
            RaisePropertyChanged("Property1");
        } 
    }
}

我想做的是,当Property2被改变时,引发property changed以便更新Property1。

所以问题是Property1.Get不被调用时,我改变Property2(我测试了一个断点)。

问题是:

为什么不工作?如何做到这一点?

谢谢

另一个视图模型中的RaisePropertyChanged

记住!你不能从实例中调用RaisePropertyChanged(),因为该方法在MVVM Light中受到保护!因此,在MenuViewModel:

中创建一个包装器
public void RaiseProperty1Changed()
{
     RaisePropertyChanged("Property1");
}

在你的MainViewModel中订阅StatusBarViewModelProperty2的事件RaisePropertyChanged

StatusBarVM.PropertyChanged += OnProperty2Changed

在这个委托方法调用中:

private void OnProperty2Changed(object sender, PopertyChangedEventArgs e)
{
    if (e.PropertyName == "Property2")
    {
            MenuVM.RaiseProperty1Changed();
    } 
}
public class MainViewModel : ViewModelBase
{
    public MenuViewModel MenuVM { get; set; }
    public StatusBarViewModel StatusBarVM { get; set; }
    public MainViewModel()
    {
        MenuVM = new MenuViewModel();
        StatusBarVM = new StatusBarViewModel();
        StatusBarVM.PropertyChanged += (s, e) =>
        {
            if (e.PropertyName == "Property2" && MenuVM != null)
                MenuVM.RaisePropertyChanged("Property1");
        };
    }
}
public class MenuViewModel : INotifyPropertyChanged
{
    private string _property1;
    public string Property1
    {
        get { return _property1; }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    internal void RaisePropertyChanged(string p)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(p));
    }
}
public class StatusBarViewModel : INotifyPropertyChanged
{
    private string _property2;
    public string Property2
    {
        get { return _property2; }
        set
        {
            _property2 = value;
            RaisePropertyChanged("Property2");
        }
    }
    private void RaisePropertyChanged(string p)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(p));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

由于其他答案显示了很好的解决方案,它们创建了对ViewModels的引用,或者检查要更改的属性的名称,我发现它们太耦合了,我建议您使用另一种方法来实现StatusBar。

一个好的方法是使用Messenger模式,我在这里构建了一个示例库,它提供了一个StatusBar UserControl,和一个Helper类StatusManager,它帮助StatusBar内部的控件到Subsribe进行更新,并允许外部对象将updates发送到StatusBar

订阅:

public static void Subscribe<TSubscriber, TMessage>(string token, Action<TMessage> subscriberAction)
        {
            _subscribers.Add(new Subscribtion<TSubscriber, TMessage>(subscriberAction, token));
        }
更新:

public static void UpdateStatus<TSubscriber, TMessage>(object status, string token)
        {
            if (!(status.GetType() == typeof(TMessage)))
            {
                throw new ArgumentException("Message type is different than the second type argument");
            }
            var subscribersWithCorrespondingType = (from subscriber in _subscribers
                               where (subscriber.GetType().GenericTypeArguments[0] == typeof(TSubscriber)) &&
                                      (subscriber.GetType().GenericTypeArguments[1] == typeof(TMessage))
                               select subscriber).ToList();
            var subscribers = (from subscriber in subscribersWithCorrespondingType
                               where ((Subscribtion<TSubscriber, TMessage>) subscriber).Token == token
                               select subscriber).ToList();
            foreach (var subscriber in subscribers)
            {
                ((Subscribtion<TSubscriber, TMessage>)subscriber).SubscriberAction((TMessage)status);
            }
        }

使用:

如果状态栏包含一个名为lblCursorPosition的标签,它可以订阅类型为string的更新:

StatusManager.Subscribe<Label, string>((s) =>
            {
                lblCursorPosition.Content = s;
            });

和更新它看起来像这样:

private void Button_Click(object sender, RoutedEventArgs e)
        {
            StatusManager.UpdateStatus<Label, string>("StatusBar's label updated !!"); 
        }

如您所见,这提供了更多的独立性和灵活性,我很高兴收到建议和更正。

我想增强@Sebastian Richter解决方案更通用的属性通知:

/// <summary>
/// Wrapper to Raise a Property name from outside this View Model
/// It's not possible to call RaisePropertyChanged(..) from outside in MVVM Light.
/// </summary>
/// <param name="propertyName"></param>
internal void OuterRaisePropertyChanged(string propertyName)
{
    RaisePropertyChanged(propertyName);
}

然后从另一个Class实例调用它:

public class StatusBarViewModel
{
    private string _property2;
    public  string  Property2
    {
        get { return _property2; }
        set
        {
            _property2 = value;
            RaisePropertyChanged(nameof(Property2));
            OuterRaisePropertyChanged(nameof(MenuVM.Property1));
        } 
    }
}

请注意,使用nameof关键字的好处是,当您使用VS IDE重命名属性名称时,它将重命名包含RaisePropertyChanged参数的解决方案中的所有变量名称。

@Epitouille,我想这就是你一直在寻找的东西。

相关文章:
  • 没有找到相关文章