【问题标题】:Prism RequestNavigate and ViewModel OnPropertyChangedPrism RequestNavigate 和 ViewModel OnPropertyChanged
【发布时间】:2013-11-26 10:51:20
【问题描述】:

我在我的 WPF 4.5 项目中使用Telerik RadRibbonView。设置如下所示。

在我的 Shell 中,我有一个 RibbonView 和一个 TabControl,它们被定义为称为“RibbonRegion”和“TabRegion”的区域。 RibbonRegion 基本上是应用程序的菜单,而 TabRegion 保存主要内容。

我还创建了一个带有视图的模块,其中包含一个 RibbonTab 和一个 RibbonButton。此按钮连接到一个命令,该命令设置 RibbonContextTabView 和 TabItemView 的 DataContext 并将它们注册到各自的区域中。 ContextTab 和 TabItem 共享同一个 ViewModel。此 ViewModel 有一个属性“IsSelected”,ContextTab 和 TabItem 都绑定到该属性。

    if (_regionManager.Regions["RibbonRegion"].Views.Any(v => v.GetType() == typeof(ContextTabView)) && _regionManager.Regions["TabRegion"].Views.Any(v => v.GetType == typeof(TabItemView)))
    {
        _regionManager.RequestNavigate("RibbonRegion", new Uri("ContextTabView", UriKind.Relative));
        _regionManager.RequestNavigate("TabRegion", new Uri("TabItemView", UriKind.Relative));
    }
    else
    {
        ContextTabView contextTabView = _container.Resolve<ContextTabView>();
        TabItemView tabItemView = _container.Resolve<TabItemView>();
        contextTabView.DataContext = tabItemView.DataContext = new ContextTabTabItemViewModel();
        _regionManager.RegisterViewWithRegion("RibbonRegion", () => contextTabView);
        _regionManager.RegisterViewWithRegion("TabRegion", () => tabItemView);
    }

第一次执行上述命令时,会设置视图的 DataContext,然后将它们注册到区域中。这也将“IsSelected”属性设置为 true。如果我将焦点更改为 RibbonTab,我的 ContextTab 和 TabItem 将失去焦点,并且“IsSelected”属性设置为 false。如果我再次按下按钮,RequestNavigate 将被执行,并且该属性再次设置为 true。这是我的问题。如果我第三次这样做,什么都不会发生! RequestNavigate 已执行,但该属性未设置为 true,并且 Views 不会重新获得焦点。我对 PRISM 还很陌生,恐怕我离这里很远。任何帮助将不胜感激。

【问题讨论】:

  • 根据我的理解,每次按下按钮时,都会为已经处于活动状态的视图调用 RequestNavigate,这是正确的吗?通过活动,我的意思是视图正在区域中显示。您是否在 ViewModel 构造函数中将 IsSelected 属性设置为“true”?您似乎对 Navigation 行为感到困惑。您想将焦点设置在指定控件上,还是想在每个 Region 内的 Views 之间切换?问候。
  • 是的,我想你是对的。 Views 已经处于活动状态,只是没有聚焦。我确实想再次将属性 IsSelected 设置为 true。但是如何从另一个 ViewModel 访问 ViewModel 中的 IsSelected?我应该使用Prism EventAggregator吗?
  • 我想我明白了。我在 Module _container.RegisterType&lt;IViewModelB, ViewModelB&gt;(); 中注册了 ViewModel。然后我通过 ViewModelA public ViewModelA(IViewModelB viewModelB) { _viewModelB = viewModelB; } 的构造函数注入它,现在我可以在执行命令时访问 IsSelected 属性。
  • 最后一种方法不是最好的松散耦合通信。 EventAggregator 仅使用弱引用帮助保持 ViewModels 解耦。我现在回复你。

标签: c# wpf mvvm telerik prism-4


【解决方案1】:

为了以松散耦合的方式保持 ViewModels 之间的通信,您可以简单地使用 EventAggregator 并从 Command Button 引发事件> 实现,然后由 TabItemViewModel 处理。

您提到的将一个 ViewModel 添加到另一个中的解决方案并不理想,因为这些组件最终会以紧密耦合方式工作并将不正确的情况定义为 Views/ViewModels不会依赖于另一个视图

因此,要完成 EventAgregation 方法,您需要从 View/ViewModel 上的容器抛出构造函数接收 EventAggregator,其中单击该按钮,然后在每个 ViewModels 上您希望订阅 到该事件设置 IsSelected EventHandler 方法中的 属性。

您可以订阅“GiveFocusEvent”事件并在 ViewModels 上处理它,这将设置他们的 IsSelected 属性如下图:

public TabItemViewModel(IEventAggregator eventAggregator, ..){
    ...
    GiveFocusEvent setFocusEvent = eventAggregator.Get<GiveFocusEvent>();
    setFocusEvent.Subscribe(SetFocusEventHandler, ThreadOption.UIThread);
}

public void SetFocusEventHandler(){
     // change IsSelected property value..
}

Event 将从 Button 的 CommandHandler 方法内部发布,如下所示:

this.eventAggregator.GetEvent<GiveFocusEvent>().Publish();

请注意,您需要创建并让您的“GiveFocusEvent”事件类继承自CompositePresentationEvent

public class GiveFocusEvent : CompositePresentationEvent<string>{}

希望对你有帮助,

问候。

【讨论】:

  • 非常感谢您的回答。它工作得很好,而且是松耦合的!我对此有一个愚蠢的后续问题。如果我创建另一个 Module,它的设置与我描述的一样,我可以 订阅 相同的事件吗?我猜所有订阅此特定事件的 ViewModels 都将其 IsSelected 属性设置为 true,因此我需要为每个 Module 创建一个事件类?
  • @Dux 是的,你可以在多个视图模型中订阅同一个事件。
  • 事件聚合器也可以在不同的模块之间工作。因此,订阅特定事件的每个 ViewModel 都会执行其 EventHandler 方法。如果您想使用不同的按钮在不同的视图上专门设置属性,您将引发不同的事件。您还可以在您之前提到的 MSDN Prism 章节中查看 订阅过滤。如果您可以选择已接受的答案,我将不胜感激。问候。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多