2010-10-06 2 views
6

Ich versuche, eine benutzerdefinierte ListView (MyListView) eine Schaltfläche hinzufügen, die einen in MyListView definierten Befehl (MyCustomCommand) auslöst. Ich habe die Schaltfläche (und einen Titeltext) hinzugefügt, indem ich eine ControlTemplate anwendete. Das Problem ist, dass ich keine Möglichkeit gefunden habe, MyCustomCommand auszulösen, wenn ich auf die Schaltfläche klicke. Was ich schließlich erreichen möchte, ist ein Popup oder ContextMenu zu öffnen, wo ich auswählen kann, welche Spalten in der ListView sichtbar sein sollen.WPF: Binden an Befehl von ControlTemplate

Hier ist meine Vorlage Quelle:

<Style TargetType="local:MyListView"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="local:MyListView"> 
       <Border Name="Border" BorderThickness="1" BorderBrush="Black"> 
        <Grid> 
         <Grid.RowDefinitions> 
          <RowDefinition Height="30" /> 
          <RowDefinition /> 
         </Grid.RowDefinitions> 

         <Grid Background="LightSteelBlue"> 
          <Grid.ColumnDefinitions> 
           <ColumnDefinition /> 
           <ColumnDefinition Width="Auto" /> 
          </Grid.ColumnDefinitions> 
          <TextBlock Margin="3,3,3,3" Text="{TemplateBinding HeaderTitle}" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontSize="16" /> 
          <Button Margin="3,3,3,3" Grid.Column="1" 
            VerticalAlignment="Center" HorizontalAlignment="Right" Height="20" 
            Command="{TemplateBinding MyCustomCommand}">A button</Button> 
         </Grid> 

         <ScrollViewer Grid.Row="1" Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}"> 
          <ItemsPresenter /> 
         </ScrollViewer> 
        </Grid> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Hier ist die Definition für MyListView:

public class MyListView : ListView 
{ 
    public static readonly DependencyProperty MyCustomCommandProperty = 
     DependencyProperty.Register("MyCustomCommand", typeof(ICommand), typeof(MyListView)); 

    private static RoutedCommand myCustomCommand; 

    public ICommand MyCustomCommand 
    { 
     get 
     { 
      if (myCustomCommand == null) 
      { 
       myCustomCommand = new RoutedCommand("MyCustomCommand", typeof(MyListView)); 

       var binding = new CommandBinding(); 
       binding.Command = myCustomCommand; 
       binding.Executed += binding_Executed; 

       CommandManager.RegisterClassCommandBinding(typeof(MyListView), binding); 
      } 
      return myCustomCommand; 
     } 
    } 

    private static void binding_Executed(object sender, ExecutedRoutedEventArgs e) 
    { 
     MessageBox.Show("Command Handled!"); 
    } 


    public static readonly DependencyProperty HeaderTitleProperty = 
     DependencyProperty.Register("HeaderTitle", typeof(string), typeof(MyListView)); 

    public string HeaderTitle { get; set; } 
} 

Und hier ist das XAML, die eine einfache Instanz MyListView erstellt:

<local:MyListView VerticalAlignment="Top" HeaderTitle="ListView title"> 
    <ListView.View> 
     <GridView> 
      <GridViewColumn Width="70" Header="Column 1" /> 
      <GridViewColumn Width="70" Header="Column 2" /> 
      <GridViewColumn Width="70" Header="Column 3" /> 
     </GridView> 
    </ListView.View> 

    <ListViewItem>1</ListViewItem> 
    <ListViewItem>2</ListViewItem> 
    <ListViewItem>1</ListViewItem> 
    <ListViewItem>2</ListViewItem> 
</local:MyListView> 

Beachten Sie HeaderTitle, das an die DependencyProperty in MyListView gebunden ist. Dies funktioniert wie erwartet. Warum funktioniert es nicht mit Befehlen? Irgendwelche Hinweise, wie man das funktioniert?

Antwort

2

Ich bin nicht sicher, ob dies der richtige Weg ist, dies zu tun. Es ist ein bisschen schwierig Quellcode in den Kommentaren zu lesen, so schreibe ich diese Antwort als Antwort ...

Hier ist der Konstruktor von MyListView ist + die Befehlsbindeverfahren:

public MyListView() 
{   
    showColumnPickerCommand = new RoutedCommand("ShowColumnPickerCommand", typeof(MyListView)); 

    var binding = new CommandBinding(); 
    binding.Command = showColumnPickerCommand; 
    binding.Executed += ShowColumnPicker; 
    binding.CanExecute += ShowColumnPickerCanExecute; 

    CommandBindings.Add(binding); 
} 

private void ShowColumnPicker(object sender, ExecutedRoutedEventArgs e) 
{ 
    MessageBox.Show("Show column picker");   
} 

private void ShowColumnPickerCanExecute(object sender, CanExecuteRoutedEventArgs e) 
{ 
    e.CanExecute = true; 
} 

Die Bindungen nicht festgelegt werden in einem statischen Kontext. Die einzigen Dinge, die statisch sind, sind die DependencyProperty für den Befehl und den Befehl selbst:

public static readonly DependencyProperty ShowColumnPickerCommandProperty = 
    DependencyProperty.Register("ShowColumnPickerCommand", typeof(RoutedCommand), typeof(MyListView)); 

private static RoutedCommand showColumnPickerCommand; 

public static RoutedCommand ShowColumnPickerCommand 
{ 
    get 
    { 
     return showColumnPickerCommand; 
    } 
} 

Der Befehl muss in der Lage statisch sein, um es wie dies aus dem XAML zu binden:

<Button Command="{x:Static local:MyListView.ShowColumnPickerCommand}" /> 
6

Sie sollten, indem die Wrapper-Eigenschaft für den Befehl starten statische und verwenden

Command={x:Static local:MyListView.MyCustomCommand} 

Allgemeinen nur Sie eine ICommand Eigenschaft wollen, wenn der Befehl für jede Instanz (Like-Button) auf einen anderen Wert gesetzt wird oder wenn es sich um einen DelegateCommand/RelayCommand in einem ViewModel handelt. Sie sollten auch den gesamten zusätzlichen Code im Getter entfernen und stattdessen den Befehl entweder inline oder im statischen Konstruktor initialisieren und die CommandBinding im Instanzkonstruktor des Steuerelements verbinden.

CommandBindings.Add(new CommandBinding(MyCustomCommand, binding_Executed)); 

* * UPDATE

Die RoutedCommand selbst sollte als statisch deklariert werden. ICommand-Instanzeigenschaften sind nützlich, wenn ein externer Benutzer Ihres Steuerelements einen auszuführenden Befehl übergibt, was hier nicht gewünscht ist. Es gibt auch keine Notwendigkeit für eine DP hier und die, die Sie verwenden, ist falsch deklariert - um verwendbar zu sein, müssen sie Instanz Wrapper Eigenschaften mit GetValue/SetValue haben.

public static RoutedCommand ShowColumnPickerCommand 
{ 
    get; private set; 
} 

static MyListView() 
{   
    ShowColumnPickerCommand = new RoutedCommand("ShowColumnPickerCommand", typeof(MyListView)); 
} 
+0

Dank viel. Das hat meinen Fall gelöst :) Jetzt kann ich ein Popup öffnen, wenn der Befehl ausgeführt wird. –

+0

Ich bin auf ein neues Problem gestoßen ... Die Schaltfläche zum Auslösen des Befehls ist nur in der ersten Instanz von MyListView in einem Fenster verfügbar (aktiviert). Hat es etwas mit dem Schlüsselwort Static in: Command = {x: Static local: MyListView zu tun.MyCustomCommand} –

+0

Schaltflächen mit Befehlen werden deaktiviert, wenn entweder CanExecute des Befehls false ist oder der Befehl keinen Execute-Handler hat. Stellen Sie sicher, dass nichts Ungewöhnliches mit CanExecute passiert und dass die CommandBinding auf jeder ListView-Instanz und nicht in einem statischen Kontext eingerichtet wird, der nur die erste betrifft. –