2010-04-08 7 views

Antwort

18

Immer, wenn ich versuche, die Ansicht in einer "reinen" MVVM-Anwendung (kein Code-Behind in View) direkt zu beeinflussen, werde ich Attached Properties verwenden, um den Effekt einzukapseln, den ich versuche zu erreichen. Ich werde eine Schnittstelle erstellen, die die Aktionen definiert, die ich mit benutzerdefinierten Ereignissen durchführen möchte. Ich implementiere dann diese Schnittstelle in jedem ViewModel, das diese Befehle in der Ansicht ausführt. Schließlich binde ich mein ViewModel an die angefügte Eigenschaft in meiner Ansichtsdefinition. Der folgende Code zeigt, wie dies für SelectAll und eine TextBox funktioniert. Dieser Code kann leicht erweitert werden, um fast jede Aktion für eine Komponente in der Ansicht auszuführen.

Mein Befestigt Eigentum und Schnittstellendefinition:

using System.Windows; 
using System.Windows.Controls; 
using System; 
using System.Collections.Generic; 

namespace SelectAllSample 
{ 
    public static class TextBoxAttach 
    { 
     public static readonly DependencyProperty TextBoxControllerProperty = DependencyProperty.RegisterAttached(
      "TextBoxController", typeof(ITextBoxController), typeof(TextBoxAttach), 
      new FrameworkPropertyMetadata(null, OnTextBoxControllerChanged)); 
     public static void SetTextBoxController(UIElement element, ITextBoxController value) 
     { 
      element.SetValue(TextBoxControllerProperty, value); 
     } 
     public static ITextBoxController GetTextBoxController(UIElement element) 
     { 
      return (ITextBoxController)element.GetValue(TextBoxControllerProperty); 
     } 

     private static readonly Dictionary<ITextBoxController, TextBox> elements = new Dictionary<ITextBoxController, TextBox>(); 
     private static void OnTextBoxControllerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      var element = d as TextBox; 
      if (element == null) 
       throw new ArgumentNullException("d"); 

      var oldController = e.OldValue as ITextBoxController; 
      if (oldController != null) 
      { 
       elements.Remove(oldController); 
       oldController.SelectAll -= SelectAll; 
      } 

      var newController = e.NewValue as ITextBoxController; 
      if (newController != null) 
      { 
       elements.Add(newController, element); 
       newController.SelectAll += SelectAll; 
      } 
     } 
     private static void SelectAll(ITextBoxController sender) 
     { 
      TextBox element; 
      if (!elements.TryGetValue(sender, out element)) 
       throw new ArgumentException("sender"); 
      element.Focus(); 
      element.SelectAll(); 
     } 
    } 

    public interface ITextBoxController 
    { 
     event SelectAllEventHandler SelectAll; 
    } 

    public delegate void SelectAllEventHandler(ITextBoxController sender); 
} 

Meine Viewmodel Definition:

public class MyViewModel : ITextBoxController 
{ 
    public MyViewModel() 
    { 
     Value = "My Text"; 
     SelectAllCommand = new RelayCommand(p => 
     { 
      if (SelectAll != null) 
       SelectAll(this); 
     }); 
    } 

    public string Value { get; set; } 
    public RelayCommand SelectAllCommand { get; private set; } 

    public event SelectAllEventHandler SelectAll; 
} 

My View Definition:

<Window x:Class="SelectAllSample.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:loc="clr-namespace:SelectAllSample" 
    Title="Window1" Height="150" Width="150"> 
    <x:Code><![CDATA[ 
     public Window1() 
     { 
      InitializeComponent(); 
      DataContext = new MyViewModel(); 
     } 
    ]]></x:Code> 
    <StackPanel> 
     <TextBox Text="{Binding Value}" loc:TextBoxAttach.TextBoxController="{Binding}" /> 
     <Button Content="Select All" Command="{Binding SelectAllCommand}" /> 
    </StackPanel> 
</Window> 

Hinweis: Dank Josh Smith für RelayCommand (siehe Code in Abbildung 3 auf this page). Es wird in MyViewModel in diesem Beispiel (und fast allen meinen MVVM-Code) verwendet.

+0

Vielen Dank! – Justin

+0

Ich erhalte eine Fehlermeldung: "Delegate 'System.Action' benötigt keine Argumente"? – Justin

+1

Wo ist der Fehler? Stellen Sie sicher, dass die Anzahl der Parameter in Ihrer Event-Handler-Definition konsistent mit der Definition Ihres Handlers ist (und diese verwendet). Ich verwende ein Lambda mit RelayCommand, um das Ereignis in meinem Beispiel zu erhöhen. –