wpf mvvm binding icommand

relay command wpf



enlazar un comando dentro de un elemento del cuadro de lista a una propiedad en el elemento principal de viewmodel (6)

He estado trabajando en esto durante aproximadamente una hora y he analizado todas las preguntas de SO relacionadas.

Mi problema es muy simple:

Tengo HomePageVieModel:

HomePageVieModel +IList<NewsItem> AllNewsItems +ICommand OpenNews

Mi marca:

<Window DataContext="{Binding HomePageViewModel../> <ListBox ItemsSource="{Binding Path=AllNewsItems}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock> <Hyperlink Command="{Binding Path=OpenNews}"> <TextBlock Text="{Binding Path=NewsContent}" /> </Hyperlink> </TextBlock> </StackPanel> </DataTemplate> </ListBox.ItemTemplate>

La lista se muestra bien con todos los elementos, pero para mi vida, todo lo que intente con el Comando no funcionará:

<Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel, AncestorLevel=1}}"> <Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel,**Mode=FindAncestor}**}"> <Hyperlink Command="{Binding Path=OpenNewsItem, RelativeSource={RelativeSource AncestorType=vm:HomePageViewModel,**Mode=TemplatedParent}**}">

Yo siempre consigo:

System.Windows.Data Error: 4: No se puede encontrar el origen para enlazar con la referencia .....

Actualizar Estoy configurando mi ViewModel de esta manera? No pensé que esto importaría

<Window.DataContext> <Binding Path="HomePage" Source="{StaticResource Locator}"/> </Window.DataContext>

Uso la clase ViewModelLocator del juego de herramientas MVVMLight que hace la magia.


Así que parece que está intentando dar el DataContext apropiado al HyperLink para activar ICommand. Creo que un simple enlace de nombre de elemento puede resolver esto.

<Window x:Name="window" DataContext="{Binding HomePageViewModel../> <ListBox ItemsSource="{Binding Path=AllNewsItems}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock> <Hyperlink DataContext="{Binding DataContext ,ElementName=window}" Command="{Binding Path=OpenNews}"> <TextBlock Text="{Binding Path=NewsContent}" /> </Hyperlink> </TextBlock> </StackPanel> </DataTemplate>

AncestorType solo verifica los tipos visuales, no los tipos de modelos de vista.


Bueno, es un poco tarde, lo sé. Pero solo recientemente he enfrentado el mismo problema. Debido a razones arquitectónicas, decidí usar un localizador de modelos de vista estático en lugar de dataContextSpy.

<UserControl x:Class="MyView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:locator="clr-namespace: MyNamespace" DataContext="{Binding Source={x:Static locator:ViewModelLocator.MyViewModel}}" > <ListBox ItemsSource="{Binding Path=AllNewsItems}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock> <Hyperlink Command="{Binding Source={x:Static locator:ViewModelLocator.MyViewModel}, Path=OpenNews}" CommandParameter="{Binding}"> <TextBlock Text="{Binding Path=NewsContent}" /> </Hyperlink> </TextBlock> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </UserControl>

El localizador estático de modelos de vista crea una instancia del modelo de vista:

namespace MyNamespace { public static class ViewModelLocator { private static MyViewModelType myViewModel = new MyViewModelType(); public static MyViewModelType MyViewModel { get { return myViewModel ; } } } }

El uso de esta solución alternativa es otra forma de enlazar desde una plantilla de datos a un comando que se encuentra en el modelo de vista.


Hay dos problemas trabajando contra ti aquí.

El DataContext para el DataTemplate se establece en el elemento que muestra la plantilla. Esto significa que no puedes usar el enlace sin establecer una fuente.

El otro problema es que la plantilla significa que el elemento no es técnicamente parte del árbol lógico, por lo que no puede buscar ancestros más allá del nodo DataTemplate .

Para resolver esto necesita tener el alcance de enlace fuera del árbol lógico. Puedes usar un DataContextSpy definido here .

<ListBox ItemsSource="{Binding Path=AllNewsItems}"> <ListBox.Resources> <l:DataContextSpy x:Key="dataContextSpy" /> </ListBox.Resources> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock> <Hyperlink Command="{Binding Source={StaticResource dataContextSpy}, Path=DataContext.OpenNews}" CommandParameter="{Binding}"> <TextBlock Text="{Binding Path=NewsContent}" /> </Hyperlink> </TextBlock> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>


La respuesta de @Darren funciona bien en la mayoría de los casos, y debería ser el método preferido si es posible. Sin embargo, no es una solución de trabajo donde se dan las siguientes condiciones (de nicho):

  • DataGrid con DataGridTemplateColumn
  • .NET 4
  • Windows XP

... y posiblemente también en otras circunstancias. En teoría debería fallar en todas las versiones de Windows; pero en mi experiencia, el enfoque ElementName funciona en un DataGrid en Windows 7 pero no en XP.

En el siguiente ejemplo ficticio, estamos tratando de enlazar a un ICommand llamado ShowThingCommand en el UserControl.DataContext (que es el ViewModel):

<UserControl x:Name="ThisUserControl" DataContext="whatever..."> <DataGrid ItemsSource="{Binding Path=ListOfThings}"> <DataGrid.Columns> <DataGridTemplateColumn Header="Thing"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Command="{Binding ElementName=ThisUserControl, Path=ShowThingCommand}" CommandParameter="{Binding Path=ThingId}" Content="{Binding Path=ThingId}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </UserControl>

Debido a que DataTemplate no está en el mismo VisualTree que el control principal, no es posible hacer una referencia al control por parte de ElementName .

Para resolver esto, se puede usar el poco conocido .NET 4 y superior {x: Referencia} . Modificando el ejemplo anterior:

<UserControl x:Name="ThisUserControl" DataContext="whatever..."> <DataGrid ItemsSource="{Binding Path=ListOfThings}"> <DataGrid.Columns> <DataGridTemplateColumn Header="Thing"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Command="{Binding Source={x:Reference ThisUserControl}, Path=ShowThingCommand}" CommandParameter="{Binding Path=ThingId}" Content="{Binding Path=ThingId}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </UserControl>

Para referencia, vea las siguientes publicaciones de :

Pregunta 19244111

Pregunta 5834336

... y para obtener una explicación de por qué ElementName no funciona en esta circunstancia, consulte esta publicación del blog que contiene una solución preliminar de .NET 4.


Un ejemplo ligeramente diferente, pero descubrí que al hacer referencia al contenedor principal (utilizando ElementName) en el enlace, puede obtener su DataContext y sus propiedades posteriores mediante la sintaxis de ruta. Como se muestra abajo:

<ItemsControl x:Name="lstDevices" ItemsSource="{Binding DeviceMappings}"> <ItemsControl.ItemTemplate> <DataTemplate> <Grid> <ComboBox Text="{Binding Device}" ItemsSource="{Binding ElementName=lstDevices, Path=DataContext.AvailableDevices}" /> ... </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>


prueba algo como esto

<Button Command="{Binding DataContext.YourCommand,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"

no puede encontrar su enlace de comando dentro del cuadro de lista porque establece un diferente contexto de datos que el modelo de vista para ese cuadro de lista