卸载视图时调用 LoadedCommand

LoadedCommand is called when view is unloaded

我的 .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();
   }
}

谁能告诉我,我做错了什么?或者这种行为不是正确的做法?

编辑

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

Loaded 事件对于以 一次加载控件 为目的的触发操作非常不可靠。来自 FrameworkElementLoaded 事件的 reference

Loaded and Unloaded might both be raised on controls as a result of user-initiated system theme changes. A theme change causes an invalidation of the control template and the contained visual tree, which in turn causes the entire control to unload and reload. Therefore Loaded cannot be assumed to occur only when a page is first loaded through navigation to the page.

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

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