Add trigger for each UserControl

My application has about 15 different UserControls that load evenly into the content area at runtime.

My project is 100% compatible with MVVM, so the following XAML is inserted into each UserControl XAML element:

<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> 

Obviously, this is not ideal, and is also a violation of DRY.

What is the best way to apply this XAML to this set of UserControls? (but not every UserControl, so a simple <Style Type="UserControl" /> would be inappropriate).

+5
source share
2 answers

I use behavior implemented as an attached property. It has two main advantages compared to System.Windows.Interactivity:

  • it can be defined in style.
  • much less xaml code in views

in your case, the view may look like this:

 <UserControl ... my:AttachedCommands.LoadedCommand="{Binding ViewLoadedCommand}"> 

In my solution, I do not use commands, but I call methods on the viewmodel if viewmodel implements the IViewModelLifeCycle interface:

 public interface IViewModelLifeCycle { void Activate(object extraData); void Deactivate(); } 

All my views use this style:

 <Style x:Key="ViewBaseStyle"> <Setter Property="my:ViewModelLifeCycleBehavior.ActivateOnLoad" Value="True" /> 

and behavior:

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

Tip: Create your custom item template in Visual Studio for View and ViewModel. Its very easy and saves a lot of time. An element template can contain xaml code with trigger / behavior, pointing to your base style, your d:DataContext definition, and the viewmodel class.

+4
source

you can create a Usercontrol with a base load using Interaction.Triggers and just put a ContentPresenter in it where you bind the real content.

 <UserControl x:class="OneOfMyOtherControls"> <MyBaseUserControl> <!-- your UserControl Content--> </MyBaseUserControl> </UserControl> 
0
source

Source: https://habr.com/ru/post/1231991/


All Articles