привязка команды внутри элемента списка к свойству на родительском объекте модели представления - PullRequest
20 голосов
/ 21 февраля 2011

Я работал над этим около часа и посмотрел все связанные с этим вопросы.

Моя проблема очень проста:

У меня есть HomePageVieModel:

HomePageVieModel
+IList<NewsItem> AllNewsItems
+ICommand OpenNews

Моя разметка:

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

Этот список хорошо работает со всеми предметами, но для моей жизни все, что я попробую для Команды, не сработает:

<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}**}">

Я просто всегда получаю:

System.Windows.Data Ошибка: 4: Не удается найти источник для привязки со ссылкой .....

Обновление Я устанавливаю свою ViewModelкак это?Не думал, что это будет иметь значение:

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

Я использую класс ViewModelLocator из набора инструментов MVVMLight, который творит чудеса.

Ответы [ 6 ]

31 голосов
/ 13 ноября 2012

Немного другой пример, но, Я обнаружил, что, ссылаясь на родительский контейнер (используя ElementName) в привязке, вы можете получить доступ к его DataContext и его последующим свойствам, используя синтаксис Path. Как показано ниже:

<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>
13 голосов
/ 21 февраля 2011

Здесь против вас работают две проблемы.

DataContext для DataTemplate установлено на элемент, который отображает шаблон.Это означает, что вы не можете просто использовать привязку без установки источника.

Другая проблема заключается в том, что шаблон означает, что элемент технически не является частью логического дерева, поэтому вы не можете искать предков за пределами DataTemplate узел.

Чтобы решить эту проблему, необходимо, чтобы привязка была вне логического дерева.Вы можете использовать DataContextSpy, определенный здесь .

<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>
8 голосов
/ 21 февраля 2011

Похоже, вы пытаетесь передать правильный DataContext для HyperLink, чтобы запустить ICommand. Я думаю, что простая привязка имени элемента может решить эту проблему.

<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 проверяет только визуальные типы, но не типы ViewModel.

6 голосов
/ 29 апреля 2011

попробуйте что-то вроде этого

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

он не может найти привязку вашей команды внутри списка, потому что вы установили другой текстовый текстовый указатель, чем модель представления для этого списка

2 голосов
/ 14 декабря 2012

Ну, уже немного поздно, я знаю.Но я только недавно столкнулся с той же проблемой.Из-за архитектурных соображений я решил использовать статический локатор модели представления вместо 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>

Статический локатор модели представления создает модель представления:

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

Использование этого обходного пути - другой способпривязать шаблон данных к команде, находящейся в модели представления.

1 голос
/ 01 марта 2016

Ответ от @Darren хорошо работает в большинстве случаев и должен быть предпочтительным методом, если это возможно.Однако это не рабочее решение, при котором все следующие (нишевые) условия выполняются:

  • DataGrid с DataGridTemplateColumn
  • .NET4
  • Windows XP

... и, возможно, при других обстоятельствах.Теоретически это должно произойти сбой во всех версиях Windows;но по моему опыту подход ElementName работает в DataGrid в Windows 7 и выше, но не в XP.

В следующем вымышленном примере мы пытаемся привязать к ICommand называется ShowThingCommand в UserControl.DataContext (который является моделью представления):

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

Из-за DataTemplate не находясь в том же VisualTree, что и основной элемент управления, невозможно сослаться обратно на элемент управления с помощью ElementName .

Чтобы решить эту проблему, малоизвестный .NET 4 и выше {x: Ссылка} .Изменение приведенного выше примера:

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

Для справки см. Следующие сообщения stackoverflow:

Вопрос 19244111

Вопрос 5834336

... и объяснение того, почему ElementName не работает в этих обстоятельствах, см. в этом блоге , который содержит обходной путь до .NET 4.

...