मैं ContextMenu प्रदर्शित होने से ठीक पहले राइट क्लिक पर WPF TreeView Node का चयन करना चाहूंगा।
WinForms के लिए मैं इस तरह कोड का उपयोग कर सकता था संदर्भ मेनू के तहत क्लिक किया गया नोड खोजें , WPF विकल्प क्या हैं?
मैं ContextMenu प्रदर्शित होने से ठीक पहले राइट क्लिक पर WPF TreeView Node का चयन करना चाहूंगा।
WinForms के लिए मैं इस तरह कोड का उपयोग कर सकता था संदर्भ मेनू के तहत क्लिक किया गया नोड खोजें , WPF विकल्प क्या हैं?
जवाबों:
जिस तरह से पेड़ आबादी था, उसके आधार पर प्रेषक और ई। स्रोत मान भिन्न हो सकते हैं ।
संभावित समाधानों में से एक है e.riginalSource का उपयोग करना और VisualTreeHelper का उपयोग करके TreeViewItem को खोजना:
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;
}
if (treeViewItem == null) treeView.SelectedIndex = -1
या treeView.SelectedItem = null
। मेरा मानना है कि या तो काम करना चाहिए।
यदि आप XAML- एकमात्र समाधान चाहते हैं तो आप Blend अन्तरक्रियाशीलता का उपयोग कर सकते हैं।
मान लें कि 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
दृश्य-मॉडल पर संपत्ति। IsSelected
दृश्य-मॉडल पर गुण को सही पर सेट करना ट्री में संबंधित नोड का चयन करेगा।
जब PreviewMouseRightButtonDown
नोड के दृश्य भाग (इस नमूने में TextBlock
) पर आग लग जाती है, तो दृश्य IsSelected
-मॉडल पर संपत्ति सही पर सेट हो जाती है। 1. वापस जा रहे हैं आप देख सकते हैं कि पेड़ पर क्लिक किया गया था कि संबंधित नोड चयनित नोड बन जाता है।
अपनी परियोजना में ब्लेंड अन्तरक्रियाशीलता प्राप्त करने का एक तरीका NuGet पैकेज Unofficial.Blend.Interactivity का उपयोग करना है ।
i
और ei
नामस्थान मैपिंग हालांकि हल करती है और कौन सी असेंबली उन्हें मिल सकती है। मेरा मानना है: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
और xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
, जो कि System.Windows.Interactivity और Microsoft.Expression.Interlies विधानसभाओं में क्रमशः पाई जाती हैं।
ChangePropertyAction
सेट करने का प्रयास कर रहा है IsSelected
, जो UI का हिस्सा नहीं है, इसलिए इसमें IsSelected
संपत्ति नहीं है । क्या मुझसे कुछ गलत हो रही है?
IsSelected
संपत्ति है जो मेरे उत्तर के दूसरे पैराग्राफ में बताई गई है: मान लें कि TreeView
डेटा एक बायरियन संपत्तिIsSelected
वाले दृश्य-मॉडल के एक श्रेणीबद्ध संग्रह के लिए बाध्य है ... (मेरा जोर)।
XAML में, XAML में एक पूर्वावलोकनMouseRightButtonDown हैंडलर जोड़ें:
<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;
}
}
एलेक्स 2k8 से मूल विचार का उपयोग करते हुए, Werer 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;
}
संपादित करें: पिछला कोड हमेशा इस परिदृश्य के लिए ठीक काम करता था, लेकिन एक अन्य दृश्य में VisualTreeHelper.GetParent जब लॉरिकलट्री हेल्पर ने एक मान लौटाया, तो यह ठीक हो गया।
लगभग सही है , लेकिन आपको पेड़ में गैर दृश्यों के लिए बाहर देखने की जरूरत है, (जैसे 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's PreviewMouseRightButtonDownEvent पर एक रूटेड ईवेंट हैंडलर रजिस्टर करें। 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;
}
}
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;
}
}
मुझे बच्चों के साथ HierarchicalDataTemplate विधि का चयन करने में समस्या हो रही थी। अगर मैंने एक नोड के बच्चे का चयन किया तो यह किसी भी तरह उस बच्चे के मूल माता-पिता का चयन करेगा। मुझे पता चला कि माउसरूटबटनडाउन इवेंट में हर स्तर पर बच्चे को बुलाया जाएगा। उदाहरण के लिए यदि आपके पास कुछ इस तरह से एक पेड़ है:
आइटम 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; });
}
यह थोड़ा बुरा लगता है, लेकिन मूल रूप से मैं पहले पास से गुजरने के लिए बूलियन को सेट करता हूं और इसे कुछ सेकंड (इस मामले में 3) में दूसरे धागे पर रीसेट किया जाता है। इसका मतलब यह है कि अगला उस स्थान से गुजरता है जहां वह पेड़ को ऊपर ले जाने की कोशिश करेगा और आपको सही नोड के साथ छोड़ दिया जाएगा। यह अब तक काम करने लगता है :-)
MouseButtonEventArgs.Handled
करना है true
। चूँकि बच्चा सबसे पहले कहा जाता है। इस गुण को सत्य पर सेट करने से माता-पिता को अन्य कॉल अक्षम हो जाएंगी।
आप इसे माउस डाउन ईवेंट के साथ चुन सकते हैं। संदर्भ मेनू के अंदर जाने से पहले वह चयन को ट्रिगर कर देगा।
यदि आप MVVM पैटर्न के भीतर रहना चाहते हैं, तो आप निम्नलिखित कार्य कर सकते हैं:
राय:
<TreeView x:Name="trvName" ItemsSource="{Binding RootElementListView}" Tag="{Binding ClickedTreeElement, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:YourTreeElementClass}" ItemsSource="{Binding Path=Subreports}">
<TextBlock Text="{Binding YourTreeElementDisplayProperty}" PreviewMouseRightButtonDown="TreeView_PreviewMouseRightButtonDown"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
कोड के पीछे:
private void TreeView_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is TextBlock tb && tb.DataContext is YourTreeElementClass te)
{
trvName.Tag = te;
}
}
ViewModel:
private YourTreeElementClass _clickedTreeElement;
public YourTreeElementClass ClickedTreeElement
{
get => _clickedTreeElement;
set => SetProperty(ref _clickedTreeElement, value);
}
अब आप या तो ClickedTreeElement संपत्ति परिवर्तन पर प्रतिक्रिया कर सकते हैं या आप एक आदेश का उपयोग कर सकते हैं जो आंतरिक रूप से ClickedTreeElement के साथ काम करता है।
विस्तारित दृश्य:
<UserControl ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<TreeView x:Name="trvName" ItemsSource="{Binding RootElementListView}" Tag="{Binding ClickedTreeElement, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseRightButtonUp">
<i:InvokeCommandAction Command="{Binding HandleRightClickCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:YourTreeElementClass}" ItemsSource="{Binding Path=Subreports}">
<TextBlock Text="{Binding YourTreeElementDisplayProperty}" PreviewMouseRightButtonDown="TreeView_PreviewMouseRightButtonDown"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</UserControl>