2015-09-22 14 views
5

Meine Anwendung verfügt über 15 verschiedene UserControls, die zur Laufzeit einheitlich in einen Inhaltsbereich geladen werden.Trigger zu jedem UserControl hinzufügen

Mein Projekt ist zu 100% MVVM-konform, so habe ich das folgende XAML in jede XAML Usercontrol eingefügt:

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

Offensichtlich ist dies nicht ideal ist und auch eine Verletzung des DRY.

Wie kann dieser XAML am besten auf diese Benutzersteuerelemente angewendet werden? (aber nicht alle UserControl, so eine einfache <Style Type="UserControl" /> wäre unpassend).

+0

Warum nicht mit einem Schlüssel stylen? –

+0

@Nikita Eigentlich weiß ich nicht, wie man einen Stil erstellt, der zusammengesetzte und komplexe Anhang-Eigenschaften wie 'Interaction.Triggers' setzt. – Dai

+0

Wo befindet sich ViewLoadedCommand? Ist das in Ihrer Sicht Modell? Sind Sie sicher, dass Sie diesen Befehl aufrufen möchten ** jedes Mal, wenn ein Benutzersteuerelement geladen wird? –

Antwort

0

können Sie mit den Interaction.Triggern ein "basis-load" Usercontrol erstellen und einfach einen ContentPresenter einfügen, an den Sie den echten Inhalt binden.

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

Ich verwende Verhalten als angefügte Eigenschaft implementiert. Es hat zwei wesentliche Vorteile gegenüber System.Windows.Interactivity:

  • es kann in Stil definiert werden.
  • viel weniger XAML-Code in den

in Ihrem Fall Ansichten könnte die Ansicht wie folgt aussehen:

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

In meiner Lösung, ich bin Befehle nicht verwenden, aber ich nenne Methoden auf Viewmodel wenn das Viewmodel IViewModelLifeCycle Schnittstelle implementiert:

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

All meine Ansichten verwendet diesen Stil:

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

und das Verhalten:

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


} 

TIPP: Erstellen Sie Ihre custom Item Template in Visual Studio für View und Ansichtsmodell. Es ist sehr einfach und spart viel Zeit. Die Elementvorlage kann XAML-Code mit dem Trigger/Behavior enthalten, der auf Ihren Basisstil, Ihre d:DataContext Definition und Ihre Viewmodel-Klasse verweist.

Verwandte Themen