我想在顯示ContextMenu之前選擇一個WPF TreeView節點。在顯示ContextMenu之前選擇TreeView節點右鍵點擊ContextMenu
For WinForms我可以使用這樣的代碼Find node clicked under context menu,什麼是WPF的替代品?
我想在顯示ContextMenu之前選擇一個WPF TreeView節點。在顯示ContextMenu之前選擇TreeView節點右鍵點擊ContextMenu
For WinForms我可以使用這樣的代碼Find node clicked under context menu,什麼是WPF的替代品?
根據樹稀少,the sender and the e.Source values may vary的方式。
一個可能的解決方案是使用VisualTreeHelper使用e.OriginalSource找到樹型視圖:
private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
TreeViewItem treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);
if (treeViewItem != null)
{
treeViewItem.Focus();
e.Handled = true;
}
}
static TreeViewItem VisualUpwardSearch(DependencyObject source)
{
while (source != null && !(source is TreeViewItem))
source = VisualTreeHelper.GetParent(source);
return source as TreeViewItem;
}
在XAML,XAML中添加的PreviewMouseRightButtonDown處理程序:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- We have to select the item which is right-clicked on -->
<EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
</Style>
</TreeView.ItemContainerStyle>
然後處理這樣的事件:
private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseEventArgs e)
{
TreeViewItem item = sender as TreeViewItem;
if (item != null)
{
item.Focus();
e.Handled = true;
}
}
預期它不工作,我總是得到根元素作爲發件人。我找到了一個類似的解決方案http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/25e113a5-6f52-4c25-974f-d58b9b689f62/添加此方法的事件處理程序按預期工作。你的代碼接受它的任何改變? :-) – alex2k8 2009-02-26 22:23:49
它顯然取決於你如何填充樹視圖。我發佈的代碼的作品,因爲這是我在我的一個工具中使用的確切代碼。 – Stefan 2009-02-27 07:27:47
注意,如果您在此處設置調試點,則可以查看發件人的類型,根據設置樹的方式不同,這當然會有所不同。 – Mark 2013-04-11 17:18:43
使用 「item.Focus();」似乎不工作100%,使用「item.IsSelected = true;」確實。
Almost Right,但您需要注意樹中的非視覺效果(例如Run
)。
static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
{
while (source != null && source.GetType() != typeof(T))
{
if (source is Visual || source is Visual3D)
{
source = VisualTreeHelper.GetParent(source);
}
else
{
source = LogicalTreeHelper.GetParent(source);
}
}
return source;
}
我認爲註冊一個類處理程序應該做的伎倆。 只需註冊就TreeViewItem的PreviewMouseRightButtonDownEvent路由事件處理程序在你的app.xaml.cs代碼文件是這樣的:
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.PreviewMouseRightButtonDownEvent, new RoutedEventHandler(TreeViewItem_PreviewMouseRightButtonDownEvent));
base.OnStartup(e);
}
private void TreeViewItem_PreviewMouseRightButtonDownEvent(object sender, RoutedEventArgs e)
{
(sender as TreeViewItem).IsSelected = true;
}
}
您可以在鼠標按下事件與選擇它。這會在上下文菜單啓動之前觸發選擇。
使用來自alex2k8的原創想法,正確處理來自Wieser Software Ltd的非視覺效果,來自Stefan的XAML,來自Erlend的IsSelected,以及我真正做出的貢獻靜態方法一般:
XAML:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- We have to select the item which is right-clicked on -->
<EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown"
Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
</Style>
</TreeView.ItemContainerStyle>
C#代碼隱藏:
void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
TreeViewItem treeViewItem =
VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject);
if(treeViewItem != null)
{
treeViewItem.IsSelected = true;
e.Handled = true;
}
}
static T VisualUpwardSearch<T>(DependencyObject source) where T : DependencyObject
{
DependencyObject returnVal = source;
while(returnVal != null && !(returnVal is T))
{
DependencyObject tempReturnVal = null;
if(returnVal is Visual || returnVal is Visual3D)
{
tempReturnVal = VisualTreeHelper.GetParent(returnVal);
}
if(tempReturnVal == null)
{
returnVal = LogicalTreeHelper.GetParent(returnVal);
}
else returnVal = tempReturnVal;
}
return returnVal as T;
}
編輯:上面的代碼總是工作對於這種情況罰款,但在另一種情況下,當LogicalTreeHelper返回一個值時,VisualTreeHelper.GetParent返回null,所以修復了這個問題。
如果您只想使用XAML解決方案,則可以使用Blend Interactivity。
假設TreeView
是必然具有Boolean
財產IsSelected
和String
財產Name
以及命名Children
子項的集合視圖模型的分層集合數據。
<TreeView ItemsSource="{Binding Items}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseRightButtonDown">
<ei:ChangePropertyAction PropertyName="IsSelected" Value="true" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
有兩個有趣的部分:
的TreeViewItem.IsSelected
屬性綁定到該視圖模型的IsSelected
財產。將view-model上的IsSelected
屬性設置爲true將選擇樹中的相應節點。
當PreviewMouseRightButtonDown
在節點的可視部分(在本示例中爲TextBlock
)觸發時,視圖模型上的IsSelected
屬性設置爲true。回到1.你可以看到樹中點擊的相應節點成爲選定的節點。
在項目中獲得Blend Interactivity的一種方法是使用NuGet包Unofficial.Blend.Interactivity。
我在使用HierarchicalDataTemplate方法選擇孩子時遇到問題。如果我選擇了節點的子節點,它會以某種方式選擇該子節點的父節點。我發現MouseRightButtonDown事件會在孩子每個級別被調用。例如,如果你有一棵樹是這樣的:
項目1
- 兒童1
- 兒童2
- 的SubItem1
- Subitem2
如果我選擇了Subitem2,事件將會啓動三次,並選擇項目1。我用布爾和異步調用解決了這個問題。
private bool isFirstTime = false;
protected void TaskTreeView_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var item = sender as TreeViewItem;
if (item != null && isFirstTime == false)
{
item.Focus();
isFirstTime = true;
ResetRightClickAsync();
}
}
private async void ResetRightClickAsync()
{
isFirstTime = await SetFirstTimeToFalse();
}
private async Task<bool> SetFirstTimeToFalse()
{
return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return false; });
}
感覺有點cludgy但基本上我通過布爾設置爲true,在第一輪和具有它(在這種情況下,3)在幾秒鐘內另一個線程重置。這意味着下一個通過它將嘗試向上移動樹的地方會跳過,從而讓您選擇正確的節點。它似乎工作到目前爲止:-)
使用MVVM解決它的另一種方法是綁定命令右鍵單擊您的視圖模型。您可以在其中指定其他邏輯以及source.IsSelected = true
。 這僅使用xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
從System.Windows.Interactivity
。
XAML的觀點:
<TreeView ItemsSource="{Binding Items}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseRightButtonDown">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.TreeViewItemRigthClickCommand}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
視圖模型:
public ICommand TreeViewItemRigthClickCommand
{
get
{
if (_treeViewItemRigthClickCommand == null)
{
_treeViewItemRigthClickCommand = new RelayCommand<object>(TreeViewItemRigthClick);
}
return _treeViewItemRigthClickCommand;
}
}
private RelayCommand<object> _treeViewItemRigthClickCommand;
private void TreeViewItemRigthClick(object sourceItem)
{
if (sourceItem is Item)
{
(sourceItem as Item).IsSelected = true;
}
}
感謝堆解決方案Alex2k8 – 2011-03-09 05:19:40
是TreeView或TreeViewItem的這個事件嗎? – 2012-11-20 10:24:57
一個任何想法如何取消選擇一切,如果右鍵點擊是在一個空的位置? – 2012-11-21 02:42:11