【问题标题】:LoadedCommand is called when view is unloaded卸载视图时调用 LoadedCommand
【发布时间】:2020-07-27 08:11:00
【问题描述】:

我的 .net Core 3.1 WPF 应用程序中有一个行为,它在视图显示后调用 ViewModel 中的命令。

public class LoadedBehavior
{
   public static DependencyProperty LoadedCommandProperty
      = DependencyProperty.RegisterAttached(
         "LoadedCommand",
         typeof(ICommand),
         typeof(LoadedBehavior),
         new PropertyMetadata(null, OnLoadedCommandChanged));

   private static void OnLoadedCommandChanged
      (DependencyObject depObj, DependencyPropertyChangedEventArgs e)
   {
      if (depObj is FrameworkElement frameworkElement && e.NewValue is ICommand)
      {
         frameworkElement.Loaded
            += (o, args) => { (e.NewValue as ICommand)?.Execute(null); };
      }
   }

   public static ICommand GetLoadedCommand(DependencyObject depObj)
   {
      return (ICommand)depObj.GetValue(LoadedCommandProperty);
   }

   public static void SetLoadedCommand(
      DependencyObject depObj,
      ICommand value)
   {
      depObj.SetValue(LoadedCommandProperty, value);
   }
}

此行为附加在视图内:

behaviors:LoadedBehavior.LoadedCommand="{Binding LoadedCommand}"

我正在使用 Prisms RegionManager 将我的视图注入视图内的特定区域。当我现在尝试注入新视图时,会再次调用从旧视图加载的命令。这似乎来自行为。

为了更好地理解,这里也是被调用以在特定区域内显示新视图的代码

public class NavigationService
{
   private readonly IServiceLocator _serviceLocator;
   private readonly IRegionManager _regionManager;

   public NavigationService(IServiceLocator serviceLocator, IRegionManager regionManager)
   {
      _serviceLocator = serviceLocator;
      _regionManager = regionManager;
   }

   public void Navigate(string regionName, object view)
   {
      RemoveAllViews(regionName);
      _regionManager.AddToRegion(regionName, view);
   }

   public void Navigate<T>(string regionName) where T : FrameworkElement
   {
      var view = _serviceLocator.GetInstance<T>();
      Navigate(regionName, view);
   }

   public void RemoveAllViews(string regionName)
   {
      _regionManager.Regions[regionName].RemoveAll();
   }
}

谁能告诉我,我在这里做错了什么?还是这种行为不可行?

编辑

发布后,我发现了问题:加载的命令被多次调用。这似乎是由于该视图的内容发生更改而引起的。所以每次我添加一个新视图时,父视图都会调用它的加载事件。有没有办法只在视图显示后运行命令?

【问题讨论】:

  • 如果是“旧”视图模型的命令,那么这可能仍然是视图的数据上下文。我一般不是棱镜的粉丝,因为它很复杂。对于区域和区域经理尤其如此。 99% 的情况下,任何初始化逻辑都应该放在视图模型中。
  • 嗯,Prism 对我正在编写的所有应用程序都非常有效,而且我从来没有遇到过这样奇怪的问题。所以我认为我在这里做错了什么。我添加了当我想在区域内显示新视图时调用的代码。也许这有帮助

标签: c# wpf .net-core prism


【解决方案1】:

Loaded 事件对于以在加载控件时触发一次的意图是非常不可靠的。来自Loaded事件的referenceFrameworkElement

由于用户启动的系统主题更改,控件上可能会同时引发 Loaded 和 Unloaded。主题更改会导致控件模板和包含的可视化树失效,进而导致整个控件卸载和重新加载。 因此,不能假定仅在通过导航到页面首次加载页面时才发生 Loaded。

在 Prism 中,您可以通过创建自定义区域行为来对导航进行操作。在您的示例中,一旦将视图添加到区域,您希望在视图模型上执行命令。创建一个界面,您的所有目标视图模型都使用一个应在首先显示视图时执行的命令来实现。

public interface IInitializableViewModel
{
   ICommand Initialize { get; }
}

创建一个区域行为,用于监视区域的Views 集合并在将视图添加到区域时执行一次命令。它会检查每个视图的数据上下文,如果实现了接口,则命令不为空,命令可以执行。

public class InitializableDataContextRegionBehavior : RegionBehavior
{
   public const string BehaviorKey = nameof(InitializableDataContextRegionBehavior);

   protected override void OnAttach()
   {
      Region.Views.CollectionChanged += OnViewsCollectionChanged;
   }

   private void OnViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
   {
      if (e.Action == NotifyCollectionChangedAction.Add)
      {
         foreach (var frameworkElement in e.NewItems.OfType<FrameworkElement>())
         {
            if (frameworkElement.DataContext is IInitializableViewModel initializableViewModel &&
                initializableViewModel.Initialize != null &&
                initializableViewModel.Initialize.CanExecute(null))
            {
               initializableViewModel.Initialize.Execute(null);
            }
         }
      }
   }
}

将您的 Prism 应用程序中的自定义区域行为添加到区域行为集合中。

protected override void ConfigureDefaultRegionBehaviors(IRegionBehaviorFactory regionBehaviors)
{
   base.ConfigureDefaultRegionBehaviors(regionBehaviors);
   regionBehaviors.AddIfMissing(InitializableDataContextRegionBehavior.BehaviorKey, typeof(InitializableDataContextRegionBehavior));
}

当相应的视图添加到任何区域时,每个视图模型上的命令将只执行一次。此处使用接口更容易用于演示目的,但您也可以为命令创建附加属性,将其附加到视图并绑定到视图模型。

【讨论】:

  • 我什至不知道存在这样一个可覆盖的方法。我已经测试过它,它就像一个魅力。通过这种实现方式,我可以在视图加载后将视图添加到区域。非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-28
  • 2011-02-27
相关资源
最近更新 更多