2010-10-06 32 views
6

Estoy tratando de agregar un botón a un ListView personalizado (MyListView) que desencadena un comando (MyCustomCommand) definido en MyListView. He agregado el botón (y un texto de título) aplicando una plantilla de control. El problema es que no he encontrado una manera de activar MyCustomCommand al hacer clic en el botón. Lo que finalmente quiero lograr es abrir un Popup o ContextMenu donde pueda seleccionar qué columnas deberían estar visibles en ListView.WPF: Enlace al comando de ControlTemplate

Aquí es mi fuente de plantilla:

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

Aquí es la definición de 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; } 
} 

Y aquí es el XAML que se crea una instancia sencilla de MyListView:

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

Observe HeaderTitle que está vinculado a DependencyProperty en MyListView. Esto funciona como se esperaba ¿Por qué no funciona de la misma manera con los comandos? ¿Alguna pista de cómo hacer que esto funcione?

Respuesta

2

No estoy seguro si esta es la forma correcta de hacerlo. Es un poco difícil de leer el código fuente en los comentarios, por lo que escribir esta respuesta como una respuesta ...

Aquí es el constructor de MyListView + los métodos de encuadernación de comando:

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

Los enlaces no están establecidos arriba en un contexto estático. Las únicas cosas que son estáticas son la propiedad de dependencia para el comando, y el propio comando:

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

private static RoutedCommand showColumnPickerCommand; 

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

El comando tiene que ser estática a capaz de unirse a ella desde el XAML como esto:

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

Usted debe empezar por hacer la propiedad envoltorio para el comando estático y utilizar

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

Generalmente solo deseas una propiedad ICommand si el comando se establece en un valor diferente en cada caso (como botón) o si es algo así como un DelegateCommand/RelayCommand en un ViewModel. También debe eliminar todo el código adicional en el getter y en su lugar inicializar el comando en línea o en el constructor estático y conectar el CommandBinding en el constructor de instancias del control.

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

* * ACTUALIZACIÓN

El RoutedCommand misma debe ser declarada como estática. Las propiedades de instancia de ICommand son útiles cuando un consumidor externo de su control está pasando un comando para ejecutar, que no es lo que quiere aquí. Tampoco es necesario que haya un DP aquí y el que está utilizando se declara incorrectamente; para ser utilizables, necesitan tener propiedades de envoltura de instancia con GetValue/SetValue.

public static RoutedCommand ShowColumnPickerCommand 
{ 
    get; private set; 
} 

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

Gracias mucho. Eso resolvió mi caso :) Ahora puedo abrir una ventana emergente cuando se ejecuta el comando. –

+0

Me he encontrado con un nuevo problema ... El botón para activar el comando solo está disponible (habilitado) en la primera instancia de MyListView en una ventana. Tiene algo que ver con la palabra clave estática en: Command = {x: Static local: MyListView.MyCustomCommand} –

+0

Los botones con comandos se deshabilitan cuando el comando CanExecute es falso o el comando no tiene asociado el controlador Execute. Asegúrese de que no ocurra nada extraño con CanExecute y que CommandBinding se esté configurando en cada instancia de ListView y no en un contexto estático que solo afectará al primero. –

Cuestiones relacionadas