.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 Command。这似乎是由于此视图的内容更改引起的。因此,每次我添加一个新视图时,父视图都将其称为已加载事件。是否只有在显示视图后才能运行命令?
答案 0 :(得分:1)
Loaded
事件对于仅在加载控件时一次意图触发动作是非常不可靠的。从Loaded
的{{1}}事件的reference开始。
由于用户启动的系统主题更改,已加载和已卸载都可能在控件上引发。主题更改会导致控件模板和所包含的可视化树无效,从而使整个控件卸载和重新加载。 因此,不能假定仅在通过导航到该页面的页面首次加载页面时发生加载。
在Prism中,您可以通过创建自定义区域行为来对导航进行操作。在您的示例中,一旦视图添加到区域中,您想在视图模型上执行命令。创建一个所有目标视图模型都实现的接口,并使用一个命令,该命令应在第一次显示视图时执行。
FrameworkElement
创建一个区域行为,当将视图添加到区域时,该行为将监视区域的public interface IInitializableViewModel
{
ICommand Initialize { get; }
}
集合并执行一次命令。它将检查每个视图的数据上下文,如果实现了该接口,则该命令不为null,并且该命令可以执行。
Views
将Prism应用程序中的自定义区域行为添加到区域行为集合中。
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);
}
}
}
}
}
将相应视图添加到任何区域时,每个视图模型上的命令将仅执行一次。出于演示目的,在这里使用界面更加容易,但是您还可以为命令创建一个附加属性,该属性附加到视图并绑定到视图模型。