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