Wpf 绑定到装饰元素的祖先

Wpf 绑定到装饰元素的祖先,wpf,data-binding,xaml,adorner,Wpf,Data Binding,Xaml,Adorner,情况如下: <DataTemplate x:Key="ItemTemplate" DataType="local:RoutedCustomCommand"> <Button Command="{Binding}" Content="{Binding Text}" ToolTip="{Binding Description}"> <Button.Visibility&g

情况如下:

<DataTemplate x:Key="ItemTemplate"
              DataType="local:RoutedCustomCommand">
    <Button Command="{Binding}"
            Content="{Binding Text}"
            ToolTip="{Binding Description}">
        <Button.Visibility>
            <MultiBinding Converter="{StaticResource SomeConverter}">
            <!-- Converter simply checks flags matching 
                 and returns corresponding Visibility -->
                <Binding Path="VisibilityModes" /> 
                <!-- VisibilityModes is a property of local:RoutedCustomCommand -->


                <Binding Path="CurrentMode"
               RelativeSource="{RelativeSource AncestorType=local:CustomControl}" />
                <!-- CurrentMode is a property of local:CustomControl -->
            </MultiBinding>
        <Button.Visibility>
    </Button>
</DataTemplate>
<local:CustomControl>
    <!-- ... -->
    <ToolBar ...
             Width="15"
             ItemTemplate={StaticResource ItemTemplate}
             ... />
    <!-- Take a look at Width - it's especially is set to such a value 
         which forces items placement inside adorner overflow panel -->
    <!-- If you change ToolBar to ItemsControl, items won't be wrapped by adorner
         panel and everything will be OK -->
    <!-- ... -->
</local:CustomControl>

提前谢谢

好的,现在很容易看到这里发生了什么。在你最初的问题中有一些线索,但在你发布逻辑树之前,我并不清楚你在做什么

正如我所怀疑的,您的问题是由于缺乏逻辑继承造成的:在大多数示例中,您将在联机看到ContentPresenter将呈现一个框架元素,该框架元素将是工具栏的逻辑后代,因此即使可视化树被弹出窗口中断,它的事件路由和FindAncestor也会工作

在您的情况下,没有逻辑树连接,因为ContentPresenter呈现的内容不是FrameworkElement

换句话说,这将允许绑定和事件路由即使在装饰器中也能工作:

<Toolbar Width="15">
  <MenuItem .../>
  <MenuItem .../>
</Toolbar>
为此:

MyItems.ItemsSource = ComputeItems()
  .Select(item => new ContentPresenter { Content = item });
如果要在XAML中设置ItemsSource,我通常使用的技术是在我自己的类中创建一个附加属性(例如,“DataItemsSource”),并设置PropertyChangedCallback,以便在设置DataItemsSource时,它执行上面显示的.Select()来创建ContentPresenter并设置ItemsSource。这是肉:

public class MyItemsSourceHelper ...
{
  ... RegisterAttached("DataItemsSource", ..., new FrameworkPropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
    {
      var dataSource = GetDataItemsSource(obj);
      obj.SetValue(ItemsControl.ItemsSource,
        dataSource==null ? null :
        dataSource.Select(item => new ContentPresenter { Content = item });
    }
  }
这将使其工作:

<Toolbar Width="15" DataTemplate="..."
  my:MyItemsSourceHelper.DataItemsSource="{Binding myItems}" />


其中myItems是
数据模板应用于的非
框架元素的集合。(使用
也可以内联列出项目。好的,
工具栏的溢出面板似乎有非常奇怪的行为-它有度量问题和随机绑定问题,所以我设计了简单的
命令列表
控件,它使用
弹出窗口
,所有功能都很好

此控件符合我的要求,请根据需要随意修改

以下是样式:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:vm="clr-namespace:Company.Product">

  <SolidColorBrush x:Key="PressedCommandButtonBackgroundBrush" Color="#FFDFB700" />
  <SolidColorBrush x:Key="DisabledCommandButtonBackgroundBrush" Color="#FFDDDDDD" />
  <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#FF444444" />
  <SolidColorBrush x:Key="FocusedBorderBrush" Color="#FFFFD700" />

  <ControlTemplate x:Key="PopupButtonTemplate"
                  TargetType="vm:Button">
    <Canvas Margin="{TemplateBinding Padding}" 
             Width="16" 
             Height="16">
      <Ellipse x:Name="Circle"
                  Fill="{TemplateBinding Background}"
                  Canvas.Left="0"
                  Canvas.Top="0"
                  Width="16"
                  Height="16"
                  Stroke="{TemplateBinding BorderBrush}"
                  StrokeThickness="2" />
      <Path x:Name="Arrow" 
               Fill="Transparent"
               Canvas.Left="1"
               Canvas.Top="1"
               Width="14"
               Height="14"
               Stroke="Blue"
               StrokeThickness="1.7"
               StrokeStartLineCap="Round"
               StrokeLineJoin="Miter"
               StrokeEndLineCap="Triangle"
               Data="M 1.904,1.904 L 11.096,11.096 M 4.335,9.284 L 11.096,11.096 M 9.284,4.335 L 11.096,11.096" />
    </Canvas>
    <ControlTemplate.Triggers>
      <Trigger Property="IsMouseOver" Value="True">
        <Setter TargetName="Circle"
                     Property="Fill" Value="{DynamicResource FocusedBorderBrush}" />
      </Trigger>
      <Trigger Property="IsFocused" Value="True">
        <Setter TargetName="Circle"
                     Property="Fill" Value="{DynamicResource FocusedBorderBrush}" />
      </Trigger>
      <Trigger Property="IsPressed" Value="True">
        <Setter TargetName="Circle"
                     Property="Fill" Value="{StaticResource PressedCommandButtonBackgroundBrush}" />
      </Trigger>
      <Trigger Property="IsEnabled" Value="False">
        <Setter TargetName="Circle" 
                     Property="Fill" Value="{StaticResource DisabledCommandButtonBackgroundBrush}" />
        <Setter TargetName="Arrow" 
                     Property="Stroke" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
      </Trigger>
    </ControlTemplate.Triggers>
  </ControlTemplate>

  <Style x:Key="PopupButtonStyle"
        TargetType="vm:Button"
        BasedOn="{StaticResource {x:Type vm:Button}}">
    <Setter Property="Template" Value="{StaticResource PopupButtonTemplate}" />
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="BorderBrush" Value="Black" />
    <Setter Property="Padding" Value="0" />
  </Style>

  <ItemsPanelTemplate x:Key="ItemsPanelTemplate">
    <StackPanel Orientation="Vertical" />
  </ItemsPanelTemplate>

  <DataTemplate x:Key="CommandTemplate"
               DataType="vmc:DescriptedCommand">
    <vm:LinkButton Content="{Binding Text}"
                    Command="{Binding}"
                    ToolTip="{Binding Description}" />
  </DataTemplate>

  <ControlTemplate x:Key="ControlTemplate" 
                  TargetType="vm:CommandsHost">
    <Grid>
      <vm:Button x:Name="Button" 
                    Style="{StaticResource PopupButtonStyle}"
                    Margin="0"
                    Command="{x:Static vm:CommandsHost.OpenPopupCommand}"
                    ToolTip="{TemplateBinding ToolTip}"
                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />

      <Popup x:Name="PART_Popup" 
                Placement="Right"
                PlacementTarget="{Binding ElementName=Button}"
                StaysOpen="False"
                IsOpen="{Binding IsOpen, Mode=TwoWay, 
                                 RelativeSource={x:Static RelativeSource.TemplatedParent}}">
        <Border BorderThickness="{TemplateBinding BorderThickness}" 
                     Padding="{TemplateBinding Padding}" 
                     BorderBrush="{TemplateBinding BorderBrush}" 
                     Background="{TemplateBinding Background}" 
                     SnapsToDevicePixels="True">
          <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
        </Border>
      </Popup>
    </Grid>
    <ControlTemplate.Triggers>
      <Trigger Property="ToolTip" Value="{x:Null}">
        <Setter TargetName="Button"
                     Property="ToolTip" 
                     Value="{Binding Command.Description, RelativeSource={x:Static RelativeSource.Self}}" />
      </Trigger>
      <Trigger SourceName="PART_Popup"
                  Property="IsOpen" Value="True">
        <Setter TargetName="Button"
                     Property="Background" 
                     Value="{StaticResource PressedCommandButtonBackgroundBrush}" />
      </Trigger>
      <Trigger Property="HasItems" Value="False">
        <Setter Property="IsEnabled" Value="False" />
      </Trigger>
      <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
          <Condition Binding="{Binding HasItems, 
                                              RelativeSource={x:Static RelativeSource.Self}}" 
                            Value="False" />
          <Condition Binding="{Binding EmptyVisibility,
                                              RelativeSource={x:Static RelativeSource.Self},
                                              Converter={StaticResource NotEqualsConverter},
                                              ConverterParameter={x:Null}}" 
                            Value="True" />
        </MultiDataTrigger.Conditions>
        <Setter Property="Visibility"
                     Value="{Binding EmptyVisibility,
                                     RelativeSource={x:Static RelativeSource.Self}}" />
      </MultiDataTrigger>
    </ControlTemplate.Triggers>
  </ControlTemplate>

  <Style TargetType="vm:CommandsHost"
        BasedOn="{StaticResource {x:Type ItemsControl}}">
    <Setter Property="Template" Value="{StaticResource ControlTemplate}" />
    <Setter Property="ItemsPanel" Value="{StaticResource ItemsPanelTemplate}" />
    <Setter Property="ItemTemplate" Value="{StaticResource CommandTemplate}" />
    <Setter Property="Background" Value="White" />
    <Setter Property="BorderBrush" Value="Black" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="Padding" Value="2" />
    <Setter Property="FontSize" Value="{DynamicResource ReducedFontSize}" />
  </Style>

</ResourceDictionary>

逻辑如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace Company.Product
{
  public class CommandsHost : ItemsControl
  {
    #region Override Metadata for DefaultStyleKey dependency property
             private static readonly object DefaultStyleKeyMetadataOverrider =
                 new Func<object>(
                   delegate
    {
      FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
                           typeof(CommandsHost),
                           new FrameworkPropertyMetadata(typeof(CommandsHost)));
      return null;
    })();
    #endregion

             #region Add owner to the Popup.IsOpen dependency property
             public bool IsOpen
    {
      get { return (bool)GetValue(IsOpenProperty); }
      set { SetValue(IsOpenProperty, value); }
    }

    public static readonly DependencyProperty IsOpenProperty =
                       Popup.IsOpenProperty.AddOwner(
                               typeof(CommandsHost),
                               new FrameworkPropertyMetadata(false));
    #endregion

             public static readonly DescriptedCommand OpenPopupCommand =
                 new DescriptedCommand("Options", "Show available options",
                                       "OpenPopup", typeof(CommandsHost));

    #region CommandsHost.OpenPopup class-wide command binding
             private static readonly object CommandsHost_OpenPopupCommandClassBindingRegistrator =
                 new Func<object>(
                   delegate
    {
      CommandManager.RegisterClassCommandBinding(
                           typeof(CommandsHost),
                           new CommandBinding(CommandsHost.OpenPopupCommand, OpenPopup, CanOpenPopup));

      return null;
    })();

    private static void CanOpenPopup(object sender, CanExecuteRoutedEventArgs e)
    {
      if (!(sender is CommandsHost))
        throw new Exception("Internal inconsistency - sender contradicts with corresponding binding");

      var instance = (CommandsHost)sender;

      instance.CanOpenPopup(e);
    }

    private static void OpenPopup(object sender, ExecutedRoutedEventArgs e)
    {
      if (!(sender is CommandsHost))
        throw new Exception("Internal inconsistency - sender contradicts with corresponding binding");

      var instance = (CommandsHost)sender;

      if (!((RoutedCommand)e.Command).CanExecute(e.Parameter, instance))
        throw new Exception("Internal inconsistency - Execute called while CanExecute is false");

      instance.OpenPopup(e);
    }

    #endregion

             #region EmptyVisibility dependency property
             public Visibility? EmptyVisibility
    {
      get { return (Visibility?)GetValue(EmptyVisibilityProperty); }
      set { SetValue(EmptyVisibilityProperty, value); }
    }

    public static readonly DependencyProperty EmptyVisibilityProperty =
                 DependencyProperty.Register(
                               "EmptyVisibility", typeof(Visibility?),
                               typeof(CommandsHost),
                               new FrameworkPropertyMetadata(null));
    #endregion

             public Popup popup;

    protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)
    {
      if (popup != null)
      {
        popup.Opened -= popup_Opened;
      }

      popup = null;

      base.OnTemplateChanged(oldTemplate, newTemplate);
    }

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      popup = Template.FindName("PART_Popup", this) as Popup;
      if (popup != null)
      {
        popup.Opened += popup_Opened;
      }
    }

    private UIElement FindFirstFocusableVisualChild(DependencyObject root)
    {
      if (root is UIElement)
      {
        var ui = (UIElement)root;
        if (ui.Focusable)
          return ui;
      }

      UIElement result = null;
      for (var i = 0; result == null && i < VisualTreeHelper.GetChildrenCount(root); ++i)
      {
        var child = VisualTreeHelper.GetChild(root, i);
        result = FindFirstFocusableVisualChild(child);
      }

      return result;
    }

    void popup_Opened(object sender, EventArgs e)
    {
      var firstItem = ItemsSource.Cast<object>().FirstOrDefault();

      var container = ItemContainerGenerator.ContainerFromItem(firstItem) as ContentPresenter;

      if (container == null)
        return;

      if (container.IsLoaded)
      {
        var focusable = FindFirstFocusableVisualChild(container);
        if (focusable != null)
        {
          focusable.CaptureMouse();
          focusable.Focus();
        }
      }
      else
        container.Loaded +=
                         delegate
      {
        var focusable = FindFirstFocusableVisualChild(container);
        if (focusable != null)
        {
          focusable.CaptureMouse();
          focusable.Focus();
        }
      };
    }

    private void CanOpenPopup(CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = HasItems;
    }

    protected void OpenPopup(ExecutedRoutedEventArgs e)
    {
      if (popup != null)
      {
        popup.IsOpen = true;
      }
    }
  }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Windows.Controls;
使用System.Windows;
使用System.Windows.Input;
使用System.Windows.Controls.Primitives;
使用System.Windows.Media;
名称空间公司
{
公共类CommandsHost:ItemsControl
{
#DefaultStyleKey依赖项属性的区域覆盖元数据
私有静态只读对象DefaultStyleKeyMetadataOverrider=
新职能(
代表
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
类型(命令列表),
新的框架属性ymetadata(typeof(CommandsHost));
返回null;
})();
#端区
#区域将所有者添加到Popup.IsOpen依赖项属性
公共图书馆
{
获取{return(bool)GetValue(IsOpenProperty);}
set{SetValue(IsOpenProperty,value);}
}
公共静态只读从属属性IsOpenProperty=
Popup.IsOpenProperty.AddOwner(
类型(命令列表),
新的FrameworkPropertyMetadata(假));
#端区
公共静态只读DescriptedCommand和OpenPopupCommand=
新的描述命令(“选项”,“显示可用选项”,
“OpenPopup”,typeof(CommandsHost));
#region CommandsHost.OpenPopup类范围的命令绑定
私有静态只读对象CommandsHost_OpenPopupCommandClassBindingRegistrator=
新职能(
代表
{
CommandManager.RegisterClassCommandBinding(
类型(命令列表),
新的CommandBinding(CommandsHost.OpenPopupCommand、OpenPopup、CanOpenPopup));
返回null;
})();
私有静态void CanOpenPopup(对象发送方,CanExecuteRoutedEventArgs e)
{
如果(!(发送方是CommandsHost))
抛出新异常(“内部不一致-发送方与相应绑定冲突”);
var实例=(CommandsHost)发送方;
例如:卡诺彭(e);
}
私有静态void OpenPopup(对象发送器,ExecutedRoutedEventArgs e)
{
如果(!(发送方是CommandsHost))
抛出新异常(“内部不一致-发送方与相应绑定冲突”);
var实例=(CommandsHost)发送方;
if(!((RoutedCommand)e.Command).CanExecute(e.Parameter,instance))
抛出新异常(“内部不一致-在CanExecute为false时调用Execute”);
OpenPopup(e);
}
#端区
#区域空可视性依赖属性
公共可视性?空可视性
{
获取{return(可见性?)GetValue(EmptyVisibilityProperty);}
set{SetValue(EmptyVisibilityProperty,value);}
}
公共静态只读从属属性EmptyVisibilityProperty=
从属属性。寄存器(
“空可视性”,类型(可视性?),
类型(命令列表),
新的FrameworkPropertyMetadata(null));
#端区
公共弹出窗口;
受保护的覆盖无效OnTemplateChanged(ControlTemplate oldTemplate、ControlTemplate newTemplate)
{
如果(弹出!=null)
{
popup.Opened-=打开的弹出窗口;
}
弹出=空;
base.OnTemplateChanged(旧模板、新模板);
}
应用程序模板()上的公共重写无效
{
base.OnApplyTemplate();
popup=Template.FindName(“部分
<Toolbar Width="15" DataTemplate="..."
  my:MyItemsSourceHelper.DataItemsSource="{Binding myItems}" />
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:vm="clr-namespace:Company.Product">

  <SolidColorBrush x:Key="PressedCommandButtonBackgroundBrush" Color="#FFDFB700" />
  <SolidColorBrush x:Key="DisabledCommandButtonBackgroundBrush" Color="#FFDDDDDD" />
  <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#FF444444" />
  <SolidColorBrush x:Key="FocusedBorderBrush" Color="#FFFFD700" />

  <ControlTemplate x:Key="PopupButtonTemplate"
                  TargetType="vm:Button">
    <Canvas Margin="{TemplateBinding Padding}" 
             Width="16" 
             Height="16">
      <Ellipse x:Name="Circle"
                  Fill="{TemplateBinding Background}"
                  Canvas.Left="0"
                  Canvas.Top="0"
                  Width="16"
                  Height="16"
                  Stroke="{TemplateBinding BorderBrush}"
                  StrokeThickness="2" />
      <Path x:Name="Arrow" 
               Fill="Transparent"
               Canvas.Left="1"
               Canvas.Top="1"
               Width="14"
               Height="14"
               Stroke="Blue"
               StrokeThickness="1.7"
               StrokeStartLineCap="Round"
               StrokeLineJoin="Miter"
               StrokeEndLineCap="Triangle"
               Data="M 1.904,1.904 L 11.096,11.096 M 4.335,9.284 L 11.096,11.096 M 9.284,4.335 L 11.096,11.096" />
    </Canvas>
    <ControlTemplate.Triggers>
      <Trigger Property="IsMouseOver" Value="True">
        <Setter TargetName="Circle"
                     Property="Fill" Value="{DynamicResource FocusedBorderBrush}" />
      </Trigger>
      <Trigger Property="IsFocused" Value="True">
        <Setter TargetName="Circle"
                     Property="Fill" Value="{DynamicResource FocusedBorderBrush}" />
      </Trigger>
      <Trigger Property="IsPressed" Value="True">
        <Setter TargetName="Circle"
                     Property="Fill" Value="{StaticResource PressedCommandButtonBackgroundBrush}" />
      </Trigger>
      <Trigger Property="IsEnabled" Value="False">
        <Setter TargetName="Circle" 
                     Property="Fill" Value="{StaticResource DisabledCommandButtonBackgroundBrush}" />
        <Setter TargetName="Arrow" 
                     Property="Stroke" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
      </Trigger>
    </ControlTemplate.Triggers>
  </ControlTemplate>

  <Style x:Key="PopupButtonStyle"
        TargetType="vm:Button"
        BasedOn="{StaticResource {x:Type vm:Button}}">
    <Setter Property="Template" Value="{StaticResource PopupButtonTemplate}" />
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="BorderBrush" Value="Black" />
    <Setter Property="Padding" Value="0" />
  </Style>

  <ItemsPanelTemplate x:Key="ItemsPanelTemplate">
    <StackPanel Orientation="Vertical" />
  </ItemsPanelTemplate>

  <DataTemplate x:Key="CommandTemplate"
               DataType="vmc:DescriptedCommand">
    <vm:LinkButton Content="{Binding Text}"
                    Command="{Binding}"
                    ToolTip="{Binding Description}" />
  </DataTemplate>

  <ControlTemplate x:Key="ControlTemplate" 
                  TargetType="vm:CommandsHost">
    <Grid>
      <vm:Button x:Name="Button" 
                    Style="{StaticResource PopupButtonStyle}"
                    Margin="0"
                    Command="{x:Static vm:CommandsHost.OpenPopupCommand}"
                    ToolTip="{TemplateBinding ToolTip}"
                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />

      <Popup x:Name="PART_Popup" 
                Placement="Right"
                PlacementTarget="{Binding ElementName=Button}"
                StaysOpen="False"
                IsOpen="{Binding IsOpen, Mode=TwoWay, 
                                 RelativeSource={x:Static RelativeSource.TemplatedParent}}">
        <Border BorderThickness="{TemplateBinding BorderThickness}" 
                     Padding="{TemplateBinding Padding}" 
                     BorderBrush="{TemplateBinding BorderBrush}" 
                     Background="{TemplateBinding Background}" 
                     SnapsToDevicePixels="True">
          <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
        </Border>
      </Popup>
    </Grid>
    <ControlTemplate.Triggers>
      <Trigger Property="ToolTip" Value="{x:Null}">
        <Setter TargetName="Button"
                     Property="ToolTip" 
                     Value="{Binding Command.Description, RelativeSource={x:Static RelativeSource.Self}}" />
      </Trigger>
      <Trigger SourceName="PART_Popup"
                  Property="IsOpen" Value="True">
        <Setter TargetName="Button"
                     Property="Background" 
                     Value="{StaticResource PressedCommandButtonBackgroundBrush}" />
      </Trigger>
      <Trigger Property="HasItems" Value="False">
        <Setter Property="IsEnabled" Value="False" />
      </Trigger>
      <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
          <Condition Binding="{Binding HasItems, 
                                              RelativeSource={x:Static RelativeSource.Self}}" 
                            Value="False" />
          <Condition Binding="{Binding EmptyVisibility,
                                              RelativeSource={x:Static RelativeSource.Self},
                                              Converter={StaticResource NotEqualsConverter},
                                              ConverterParameter={x:Null}}" 
                            Value="True" />
        </MultiDataTrigger.Conditions>
        <Setter Property="Visibility"
                     Value="{Binding EmptyVisibility,
                                     RelativeSource={x:Static RelativeSource.Self}}" />
      </MultiDataTrigger>
    </ControlTemplate.Triggers>
  </ControlTemplate>

  <Style TargetType="vm:CommandsHost"
        BasedOn="{StaticResource {x:Type ItemsControl}}">
    <Setter Property="Template" Value="{StaticResource ControlTemplate}" />
    <Setter Property="ItemsPanel" Value="{StaticResource ItemsPanelTemplate}" />
    <Setter Property="ItemTemplate" Value="{StaticResource CommandTemplate}" />
    <Setter Property="Background" Value="White" />
    <Setter Property="BorderBrush" Value="Black" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="Padding" Value="2" />
    <Setter Property="FontSize" Value="{DynamicResource ReducedFontSize}" />
  </Style>

</ResourceDictionary>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace Company.Product
{
  public class CommandsHost : ItemsControl
  {
    #region Override Metadata for DefaultStyleKey dependency property
             private static readonly object DefaultStyleKeyMetadataOverrider =
                 new Func<object>(
                   delegate
    {
      FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
                           typeof(CommandsHost),
                           new FrameworkPropertyMetadata(typeof(CommandsHost)));
      return null;
    })();
    #endregion

             #region Add owner to the Popup.IsOpen dependency property
             public bool IsOpen
    {
      get { return (bool)GetValue(IsOpenProperty); }
      set { SetValue(IsOpenProperty, value); }
    }

    public static readonly DependencyProperty IsOpenProperty =
                       Popup.IsOpenProperty.AddOwner(
                               typeof(CommandsHost),
                               new FrameworkPropertyMetadata(false));
    #endregion

             public static readonly DescriptedCommand OpenPopupCommand =
                 new DescriptedCommand("Options", "Show available options",
                                       "OpenPopup", typeof(CommandsHost));

    #region CommandsHost.OpenPopup class-wide command binding
             private static readonly object CommandsHost_OpenPopupCommandClassBindingRegistrator =
                 new Func<object>(
                   delegate
    {
      CommandManager.RegisterClassCommandBinding(
                           typeof(CommandsHost),
                           new CommandBinding(CommandsHost.OpenPopupCommand, OpenPopup, CanOpenPopup));

      return null;
    })();

    private static void CanOpenPopup(object sender, CanExecuteRoutedEventArgs e)
    {
      if (!(sender is CommandsHost))
        throw new Exception("Internal inconsistency - sender contradicts with corresponding binding");

      var instance = (CommandsHost)sender;

      instance.CanOpenPopup(e);
    }

    private static void OpenPopup(object sender, ExecutedRoutedEventArgs e)
    {
      if (!(sender is CommandsHost))
        throw new Exception("Internal inconsistency - sender contradicts with corresponding binding");

      var instance = (CommandsHost)sender;

      if (!((RoutedCommand)e.Command).CanExecute(e.Parameter, instance))
        throw new Exception("Internal inconsistency - Execute called while CanExecute is false");

      instance.OpenPopup(e);
    }

    #endregion

             #region EmptyVisibility dependency property
             public Visibility? EmptyVisibility
    {
      get { return (Visibility?)GetValue(EmptyVisibilityProperty); }
      set { SetValue(EmptyVisibilityProperty, value); }
    }

    public static readonly DependencyProperty EmptyVisibilityProperty =
                 DependencyProperty.Register(
                               "EmptyVisibility", typeof(Visibility?),
                               typeof(CommandsHost),
                               new FrameworkPropertyMetadata(null));
    #endregion

             public Popup popup;

    protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)
    {
      if (popup != null)
      {
        popup.Opened -= popup_Opened;
      }

      popup = null;

      base.OnTemplateChanged(oldTemplate, newTemplate);
    }

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      popup = Template.FindName("PART_Popup", this) as Popup;
      if (popup != null)
      {
        popup.Opened += popup_Opened;
      }
    }

    private UIElement FindFirstFocusableVisualChild(DependencyObject root)
    {
      if (root is UIElement)
      {
        var ui = (UIElement)root;
        if (ui.Focusable)
          return ui;
      }

      UIElement result = null;
      for (var i = 0; result == null && i < VisualTreeHelper.GetChildrenCount(root); ++i)
      {
        var child = VisualTreeHelper.GetChild(root, i);
        result = FindFirstFocusableVisualChild(child);
      }

      return result;
    }

    void popup_Opened(object sender, EventArgs e)
    {
      var firstItem = ItemsSource.Cast<object>().FirstOrDefault();

      var container = ItemContainerGenerator.ContainerFromItem(firstItem) as ContentPresenter;

      if (container == null)
        return;

      if (container.IsLoaded)
      {
        var focusable = FindFirstFocusableVisualChild(container);
        if (focusable != null)
        {
          focusable.CaptureMouse();
          focusable.Focus();
        }
      }
      else
        container.Loaded +=
                         delegate
      {
        var focusable = FindFirstFocusableVisualChild(container);
        if (focusable != null)
        {
          focusable.CaptureMouse();
          focusable.Focus();
        }
      };
    }

    private void CanOpenPopup(CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = HasItems;
    }

    protected void OpenPopup(ExecutedRoutedEventArgs e)
    {
      if (popup != null)
      {
        popup.IsOpen = true;
      }
    }
  }
}