另一个视图模型中的RaisePropertyChanged

时间:2015-07-23 07:36:19

标签: c# wpf mvvm

我有一个主要的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被更改时,提升属性已更改以更新Property1。

因此,当我更改Property2(我使用断点测试)时,问题是Property1.Get未被调用。

问题是:

为什么这不起作用?  如何让这个工作?

谢谢:)

4 个答案:

答案 0 :(得分:3)

记住!您无法从实例调用RaisePropertyChanged(),因为该方法在MVVM Light中受到保护!所以在MenuViewModel

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

MainViewModel订阅RaisePropertyChangedProperty2的活动StatusBarViewModel。{/ p>

StatusBarVM.PropertyChanged += OnProperty2Changed

在这个委托方法中调用:

private void OnProperty2Changed(object sender, PopertyChangedEventArgs e)
{
    if (e.PropertyName == "Property2")
    {
            MenuVM.RaiseProperty1Changed();
    } 
}

答案 1 :(得分:1)

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

答案 2 :(得分:0)

由于其他答案显示了很好的解决方法,他们创建了对ViewModel的硬引用,或检查要更改的属性的名称,并且我发现太多耦合,我建议您使用另一种方法来实现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);
            }
        }

使用:

如果StatusBar包含名为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 !!"); 
        }

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

答案 3 :(得分:0)

我想通过要通知的属性来增强@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,我想这就是您要搜索的地方。

致谢