Wpf 向每个用户控件添加触发器
我的应用程序有大约15个不同的用户控件,它们在运行时统一加载到内容区域 我的项目100%符合MVVM,因此我将以下XAML插入到每个UserControl的XAML中:Wpf 向每个用户控件添加触发器,wpf,xaml,Wpf,Xaml,我的应用程序有大约15个不同的用户控件,它们在运行时统一加载到内容区域 我的项目100%符合MVVM,因此我将以下XAML插入到每个UserControl的XAML中: <UserControl ... xmlns:intr="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" > <intr:Interaction.Triggers>
<UserControl
...
xmlns:intr="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
>
<intr:Interaction.Triggers>
<intr:EventTrigger EventName="Loaded">
<intr:InvokeCommandAction Command="{Binding ViewLoadedCommand}"/>
</intr:EventTrigger>
</intr:Interaction.Triggers>
<!-- Rest of UserControl content here -->
</UserControl>
显然,这并不理想,也违反了DRY
将此XAML应用于这组用户控件的最佳方式是什么?(但不是每个用户控件,因此简单的
是不合适的)。您可以使用Interaction.Triggers创建一个“基本负载”用户控件,只需在其中放置一个ContentPresenter,将真正的内容绑定到该控件即可
<UserControl x:class="OneOfMyOtherControls">
<MyBaseUserControl>
<!-- your UserControl Content-->
</MyBaseUserControl>
</UserControl>
我使用作为附加属性实现的行为。与System.Windows.Interactive相比,它有两个主要优势:
- 它可以在样式中定义
- 视图中的xaml代码要少得多
<UserControl ...
my:AttachedCommands.LoadedCommand="{Binding ViewLoadedCommand}">
“我的所有视图”都使用此样式:
<Style x:Key="ViewBaseStyle">
<Setter Property="my:ViewModelLifeCycleBehavior.ActivateOnLoad" Value="True" />
提示:
在VisualStudio中为View和ViewModel创建视图。这很简单,节省了很多时间。项目模板可以包含带有触发器/行为的xaml代码,指向您的基本样式、您的
d:DataContext
定义和您的viewmodel类。为什么不使用键进行样式设置?@Nikita实际上,我不知道如何编写一个样式来设置复合和复杂的附件属性,如Interaction.Triggers
。ViewLoadedCommand位于哪里?这在您的视图模型中吗?你确定要在每次加载用户控件时调用此命令吗?@MikeEason正确,这是一个RelayCommand
,是的,我希望在每次加载时都调用它,它是onload
的代理。似乎这是不可能的,我们需要检查是否有解决方法。
<Style x:Key="ViewBaseStyle">
<Setter Property="my:ViewModelLifeCycleBehavior.ActivateOnLoad" Value="True" />
public static class ViewModelLifeCycleBehavior
{
public static readonly DependencyProperty ActivateOnLoadProperty = DependencyProperty.RegisterAttached("ActivateOnLoad", typeof (bool), typeof (ViewModelLifeCycleBehavior),
new PropertyMetadata(ActivateOnLoadPropertyChanged));
public static void SetActivateOnLoad(FrameworkElement element, bool value)
{
element.SetValue(ActivateOnLoadProperty, value);
}
public static bool GetActivateOnLoad(FrameworkElement element)
{
return (bool)element.GetValue(ActivateOnLoadProperty);
}
private static void ActivateOnLoadPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (DesignerProperties.GetIsInDesignMode(obj)) return;
var element = (FrameworkElement)obj;
element.Loaded -= ElementLoaded;
element.Unloaded -= ElementUnloaded;
if ((bool) args.NewValue == true)
{
element.Loaded += ElementLoaded;
element.Unloaded += ElementUnloaded;
}
}
static void ElementLoaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement) sender;
var viewModel = (IViewModelLifeCycle) element.DataContext;
if (viewModel == null)
{
DependencyPropertyChangedEventHandler dataContextChanged = null;
dataContextChanged = (o, _e) =>
{
ElementLoaded(sender, e);
element.DataContextChanged -= dataContextChanged;
};
element.DataContextChanged += dataContextChanged;
}
else if (element.ActualHeight > 0 && element.ActualWidth > 0) //to avoid activating twice since loaded event is called twice on TabItems' subtrees
{
viewModel.Activate(null);
}
}
private static void ElementUnloaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
var viewModel = (IViewModelLifeCycle)element.DataContext;
viewModel.Deactivate();
}
}