Внешнее свойство внутри DataTemplate WPF - PullRequest
4 голосов
/ 01 апреля 2009

Сценарий: у меня есть ListBox, а у ListBoxItems есть DataTemplate. То, что я хочу сделать, это поместить ContextMenu в шаблон данных. Суть в том, что я хочу, чтобы этот ContextMenu ItemsSource отличался в зависимости от определенных свойств в окне. Сначала я думал, что я могу просто связать ItemsSource со свойством в окне, и это вернет ItemsSource; Тем не менее, я не могу связать это свойство правильно. Я полагаю, что это потому, что я нахожусь в DataTemplate и, следовательно, DataContext (я считаю, что это правильное слово) относится к этому ListBoxItem, а не из окна. Как я могу получить ContextMenu, который находится внутри DataTemplate, для привязки к свойству за пределами DataTemplate.

Ответы [ 2 ]

5 голосов
/ 01 апреля 2009

Вы можете получить DataContext из своего окна, используя синтаксис RelativeSource FindAncestor

<DataTemplate>
  <TextBlock Text="{Binding MyInfo}">
    <TextBlock.ContextMenu>
      <Menu ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.MyContextMenuItems}"/>
    </TextBlock.ContextMenu>
  </TextBlock>
</DataTemplate>

Не совсем уверен, но переплет правильный ... Если ваш DataContext относится к другому типу объекта, вам просто нужно изменить AncestorType (например, UserControl).

2 голосов
/ 01 апреля 2009

Это может быть хорошим кандидатом для AttachedProperty . По сути, вы должны обернуть ваше ContextMenu в UserControl, а затем добавить свойство зависимости в UserControl. Например:

MyContextMenu.xaml

<UserControl x:Class="MyContextMenu" ...>
  <UserControl.Template>
    <ContextMenu ItemSource="{Binding}" />
  </UserControl.Template>
</UserControl>

MyContextMenu.xaml.cs

public static readonly DependencyProperty MenuItemsSourceProperty = DependencyProperty.RegisterAttached(
  "MenuItemsSource",
  typeof(Object),
  typeof(MyContextMenu),
  new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetMenuItemsSource(UIElement element, Boolean value)
{
  element.SetValue(MenuItemsSourceProperty, value);
  // assuming you want to change the context menu when the mouse is over an element.
  // use can use other events.  ie right mouse button down if its a right click menu.
  // you may see a perf hit as your changing the datacontext on every mousenter.
  element.MouseEnter += (s, e) => {
    // find your ContextMenu and set the DataContext to value
    var window = element.GetRoot();
    var menu = window.GetVisuals().OfType<MyContextMenu>().FirstOrDefault();
    if (menu != null)
      menu.DataContext = value;
  }
}
public static Object GetMenuItemsSource(UIElement element)
{
  return element.GetValue(MenuItemsSourceProperty);
}

Window1.xaml

<Window ...>
  <Window.Resources>
    <DataTemplate TargetType="ListViewItem">
      <Border MyContextMenu.MenuItemsSource="{Binding Orders}">
        <!-- Others -->
      <Border>
    </DataTemplate>
  </Window.Resources>
  <local:MyContextMenu />
  <Button MyContextMenu.MenuItemsSource="{StaticResource buttonItems}" />
  <ListView ... />
</Window>

VisualTreeHelpers

public static IEnumerable<DependencyObject> GetVisuals(this DependencyObject root)
{
    int count = VisualTreeHelper.GetChildrenCount(root);
    for (int i = 0; i < count; i++)
    {
        var child = VisualTreeHelper.GetChild(root, i);
        yield return child;
        foreach (var descendants in child.GetVisuals())
        {
            yield return descendants;
        }
    }
}

public static DependencyObject GetRoot(this DependencyObject child)
{
    var parent = VisualTreeHelper.GetParent(child)
    if (parent == null)
      return child;
    return parent.GetRoot();
}

Этот пример не проверен. Я посмотрю сегодня вечером и уточню его точность.

...