我遇到与this question相同的问题,我希望TreeViewItem在显示其上下文菜单时仍然主动选择。但是,在我的树中,每个级别都有不同类型的对象,因此我希望每个级别都有不同的ContextMenu。我正在使用HierachicalDataTemplate完成此任务。所以我有以下XAML:
<Window x:Class="Project.MainWindow">
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Project" ContentRendered="Window_ContentRendered">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="VolumeTemplate">
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource VolumeIcon}" Margin="3,3,3,3" />
<TextBlock Text="{Binding Path=Name}" Margin="3,3,3,3">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding VolumeTestCommand}"
Header="VolumeTest" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</DataTemplate>
<HierachicalDataTemplate x:Key="ServerTemplate"
ItemsSource="{Binding Volumes}"
ItemTemplate="{StaticResource VolumeTemplate}">
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ServerIcon}" Margin="3,3,3,3" />
<TextBlock Text="{Binding Name}" Margin="3,3,3,3" >
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding ServerTestCommand}"
Header="ServerTest" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</Grid.Resources>
<TreeView HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding Servers}" Name="tvMain"
ItemTemplate="{StaticResource ServerTemplate}"
PreviewMouseRightButtonDown="tvMain_PreviewMouseRightButtonDown" />
</Grid>
</Window>
代码背后:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
//set DataContext here, based on a login dialog
}
static T VisualUpwardSearch<T>(DependencyObject source) where T : DependencyObject
{
DependencyObject returnVal = source;
while (returnVal != null && !(returnVal is T))
{
if (returnVal is Visual || returnVal is Visual3D)
{
returnVal = VisualTreeHelper.GetParent(returnVal);
}
else
{
returnVal = LogicalTreeHelper.GetParent(returnVal);
}
}
return returnVal as T;
}
private void tvMain_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
TreeViewItem treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject);
if(treeViewItem != null)
{
treeViewItem.IsSelected = true;
e.Handled = true;
}
}
}
我尝试了引用问题的答案,但我认为它不起作用,因为上下文菜单在TextBlock而不是TreeViewItem上。有没有办法将ContextMenu附加到DataTemplate中的TreeViewItem,或者解决此问题的另一种方法?
答案 0 :(得分:0)
我最终必须创建一个附加属性,它将上下文菜单从TextBlock移动到TreeViewItem:
public static readonly DependencyProperty StealContextMenuProperty =
DependencyProperty.RegisterAttached(
"StealContextMenu",
typeof(bool),
typeof(ParentClass),
new UIPropertyMetadata(false, new PropertyChangedCallback(SCMChanged))
);
public static bool GetStealContextMenu(FrameworkElement obj)
{
return (bool)obj.GetValue(StealContextMenuProperty);
}
public static void SetStealContextMenu(FrameworkElement obj, bool value)
{
obj.SetValue(StealContextMenuProperty, value);
}
public static void SCMChanged(object sender, DependencyPropertyChangedEventArgs e)
{
FrameworkElement fe = sender as FrameworkElement;
if (fe == null) return;
bool value = (bool)e.NewValue;
if (!value) return;
fe.Loaded += new RoutedEventHandler(fe_Loaded);
}
public static void fe_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement fe = (FrameworkElement)sender;
FrameworkElement child;
child = VisualDownwardSearch<FrameworkElement>(fe, x => x.ContextMenu != null);
if (child != null)
{
fe.ContextMenu = child.ContextMenu;
child.ContextMenu = null;
}
}
public static T VisualDownwardSearch<T>(T source, Predicate<T> match)
where T : DependencyObject
{
Queue<DependencyObject> queue = new Queue<DependencyObject>();
queue.Enqueue(source);
while(queue.Count > 0)
{
DependencyObject dp = queue.Dequeue();
if (dp is Visual || dp is Visual3D)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(dp);
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(dp, i);
if (child is T)
{
T tChild = (T)child;
if (match(tChild)) return tChild;
}
queue.Enqueue(child);
}
}
else
{
foreach (DependencyObject child in LogicalTreeHelper.GetChildren(dp))
{
if (child is T)
{
T tChild = (T)child;
if (match(tChild)) return tChild;
}
queue.Enqueue(child);
}
}
}
return null;
}