tutorial que programacion principiantes para implementar como beginner wpf mvvm

que - Disparar un evento de doble clic desde un elemento WPF ListView usando MVVM



wpf c# mvvm (8)

En una aplicación de WPF que usa MVVM, tengo un control de usuario con un elemento de vista de lista. En tiempo de ejecución, usará enlace de datos para llenar la vista de lista con una colección de objetos.

¿Cuál es la forma correcta de adjuntar un evento de doble clic a los elementos en la vista de lista para que cuando se haga doble clic en un elemento en la vista de lista, se active un evento correspondiente en el modelo de vista y se haga clic en una referencia al elemento?

¿Cómo se puede hacer de una manera MVVM limpia, es decir, sin código en la Vista?


He encontrado una manera muy fácil y limpia de hacer esto con los desencadenantes del evento Blend SDK. Limpie MVVM, reutilizable y sin código subyacente.

Probablemente ya tengas algo como esto:

<Style x:Key="MyListStyle" TargetType="{x:Type ListViewItem}">

Ahora incluye un ControlTemplate para ListViewItem como este si no usas uno:

<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListViewItem}"> <GridViewRowPresenter Content="{TemplateBinding Content}" Columns="{TemplateBinding GridView.ColumnCollection}" /> </ControlTemplate> </Setter.Value> </Setter>

El GridViewRowPresenter será la raíz visual de todos los elementos "dentro" que componen un elemento de fila de lista. Ahora podríamos insertar un desencadenador allí para buscar eventos enrutados MouseDoubleClick y llamar un comando a través de InvokeCommandAction de esta manera:

<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListViewItem}"> <GridViewRowPresenter Content="{TemplateBinding Content}" Columns="{TemplateBinding GridView.ColumnCollection}"> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseDoubleClick"> <i:InvokeCommandAction Command="{Binding DoubleClickCommand}" /> </i:EventTrigger> </i:Interaction.Triggers> </GridViewRowPresenter> </ControlTemplate> </Setter.Value> </Setter>

Si tiene elementos visuales "arriba" de GridRowPresenter (probalby comenzando con una cuadrícula) también puede poner el disparador allí.

Lamentablemente, los eventos MouseDoubleClick no se generan a partir de cada elemento visual (son de Controles, pero no de FrameworkElements, por ejemplo). Una solución es derivar una clase de EventTrigger y buscar MouseButtonEventArgs con un ClickCount de 2. Esto efectivamente filtra todos los eventos no MouseButtonEvents y todos los eventos MoseButtonEvents con un ClickCount! = 2.

class DoubleClickEventTrigger : EventTrigger { protected override void OnEvent(EventArgs eventArgs) { var e = eventArgs as MouseButtonEventArgs; if (e == null) { return; } if (e.ClickCount == 2) { base.OnEvent(eventArgs); } } }

Ahora podemos escribir esto (''h'' es el espacio de nombres de la clase auxiliar anterior):

<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListViewItem}"> <GridViewRowPresenter Content="{TemplateBinding Content}" Columns="{TemplateBinding GridView.ColumnCollection}"> <i:Interaction.Triggers> <h:DoubleClickEventTrigger EventName="MouseDown"> <i:InvokeCommandAction Command="{Binding DoubleClickCommand}" /> </h:DoubleClickEventTrigger> </i:Interaction.Triggers> </GridViewRowPresenter> </ControlTemplate> </Setter.Value> </Setter>


Me doy cuenta de que esta discusión tiene un año, pero con .NET 4, ¿hay alguna idea sobre esta solución? Estoy absolutamente de acuerdo en que el objetivo de MVVM NO es eliminar un código detrás del archivo. También creo firmemente que el hecho de que algo sea complicado no significa que sea mejor. Esto es lo que puse en el código:

private void ButtonClick(object sender, RoutedEventArgs e) { dynamic viewModel = DataContext; viewModel.ButtonClick(sender, e); }


Me gusta usar Comandos y Comandos de Comando Adjuntos. Marlon Grech tiene una muy buena implementación de Comportamientos de comando adjuntos. Utilizándolos, podríamos asignar un estilo a la propiedad ItemContainerStyle de ListView que establecerá el comando para cada ListViewItem.

Aquí establecemos el comando para que se active en el evento MouseDoubleClick y CommandParameter será el objeto de datos en el que hacemos clic. Aquí estoy recorriendo el árbol visual para obtener el comando que estoy usando, pero también podría crear fácilmente comandos de toda la aplicación.

<Style x:Key="Local_OpenEntityStyle" TargetType="{x:Type ListViewItem}"> <Setter Property="acb:CommandBehavior.Event" Value="MouseDoubleClick" /> <Setter Property="acb:CommandBehavior.Command" Value="{Binding ElementName=uiEntityListDisplay, Path=DataContext.OpenEntityCommand}" /> <Setter Property="acb:CommandBehavior.CommandParameter" Value="{Binding}" /> </Style>

Para los comandos, puede implementar un ICommand directamente, o usar algunos de los ayudantes como los que vienen en MVVM Toolkit .


Me resulta más sencillo vincular el comando cuando se crea la vista:

var r = new MyView(); r.MouseDoubleClick += (s, ev) => ViewModel.MyCommand.Execute(null); BindAndShow(r, ViewModel);

En mi caso, BindAndShow ve así (updatecontrols + avalondock):

private void BindAndShow(DockableContent view, object viewModel) { view.DataContext = ForView.Wrap(viewModel); view.ShowAsDocument(dockManager); view.Focus(); }

Aunque el enfoque debería funcionar con cualquier método que tengas para abrir nuevos puntos de vista.


Por favor, el código detrás no es algo malo en absoluto. Desafortunadamente, mucha gente en la comunidad de WPF entendió esto mal.

MVVM no es un patrón para eliminar el código detrás. Es para separar la parte de vista (apariencia, animaciones, etc.) de la parte lógica (flujo de trabajo). Además, puedes probar la parte lógica de la unidad.

Conozco suficientes escenarios en los que tiene que escribir código porque el enlace de datos no es una solución para todo. En su escenario, manejaría el evento de DoubleClick en el código detrás del archivo y delegaría esta llamada al ViewModel.

Las aplicaciones de muestra que usan código detrás y todavía cumplen con la separación MVVM se pueden encontrar aquí:

Marco de aplicación de WPF (WAF) - http://waf.codeplex.com


Puede usar la función de Acción de Caliburn para mapear eventos a métodos en su ViewModel. Suponiendo que tiene un método ItemActivated en su ViewModel , entonces el XAML correspondiente se vería así:

<ListView x:Name="list" Message.Attach="[Event MouseDoubleClick] = [Action ItemActivated(list.SelectedItem)]" >

Para más detalles, puede examinar la documentación y las muestras de Caliburn.


Puedo hacer que esto funcione con .NET 4.5. Parece sencillo y no se necesita un tercero o código.

<ListView ItemsSource="{Binding Data}"> <ListView.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ListView.ItemsPanel> <ListView.ItemTemplate> <DataTemplate> <Grid Margin="2"> <Grid.InputBindings> <MouseBinding Gesture="LeftDoubleClick" Command="{Binding ShowDetailCommand}"/> </Grid.InputBindings> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Image Source="../images/48.png" Width="48" Height="48"/> <TextBlock Grid.Row="1" Text="{Binding Name}" /> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView>


Vi la solución de rushui con InuptBindings, pero todavía no pude llegar al área del ListViewItem donde no había texto, incluso después de configurar el fondo como transparente, así que lo resolví usando diferentes plantillas.

Esta plantilla es para cuando ListViewItem ha sido seleccionado y está activo:

<ControlTemplate x:Key="SelectedActiveTemplate" TargetType="{x:Type ListViewItem}"> <Border Background="LightBlue" HorizontalAlignment="Stretch"> <!-- Bind the double click to a command in the parent view model --> <Border.InputBindings> <MouseBinding Gesture="LeftDoubleClick" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ItemSelectedCommand}" CommandParameter="{Binding}" /> </Border.InputBindings> <TextBlock Text="{Binding TextToShow}" /> </Border> </ControlTemplate>

Esta plantilla es para cuando ListViewItem ha sido seleccionado y está inactivo:

<ControlTemplate x:Key="SelectedInactiveTemplate" TargetType="{x:Type ListViewItem}"> <Border Background="Lavender" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding TextToShow}" /> </Border> </ControlTemplate>

Este es el estilo predeterminado utilizado para ListViewItem:

<Style TargetType="{x:Type ListViewItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Border HorizontalAlignment="Stretch"> <TextBlock Text="{Binding TextToShow}" /> </Border> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsSelected" Value="True" /> <Condition Property="Selector.IsSelectionActive" Value="True" /> </MultiTrigger.Conditions> <Setter Property="Template" Value="{StaticResource SelectedActiveTemplate}" /> </MultiTrigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsSelected" Value="True" /> <Condition Property="Selector.IsSelectionActive" Value="False" /> </MultiTrigger.Conditions> <Setter Property="Template" Value="{StaticResource SelectedInactiveTemplate}" /> </MultiTrigger> </Style.Triggers> </Style>

Lo que no me gusta es la repetición del TextBlock y su encuadernación de texto, no sé si puedo pasar por alto en una sola ubicación.

¡Espero que esto ayude a alguien!