ContextMenu प्रदर्शित करने से पहले राइट क्लिक पर ट्री व्यू नोड चुनें


100

मैं ContextMenu प्रदर्शित होने से ठीक पहले राइट क्लिक पर WPF TreeView Node का चयन करना चाहूंगा।

WinForms के लिए मैं इस तरह कोड का उपयोग कर सकता था संदर्भ मेनू के तहत क्लिक किया गया नोड खोजें , WPF विकल्प क्या हैं?

जवाबों:


130

जिस तरह से पेड़ आबादी था, उसके आधार पर प्रेषक और ई। स्रोत मान भिन्न हो सकते हैं

संभावित समाधानों में से एक है 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;
}

ट्रीव्यू या ट्री व्यू इटेम के लिए यह घटना है?
लुई राइस

1
किसी भी विचार कैसे सब कुछ अचयनित करने के लिए अगर सही क्लिक एक खाली स्थान पर है?
लुई राइस

केवल एक ही उत्तर जिसने 5 अन्य लोगों की मदद की ... मैं वास्तव में ट्रीव्यू आबादी के साथ कुछ गलत कर रहा हूं, धन्यवाद।

3
लुइस राइस के सवाल के जवाब में: if (treeViewItem == null) treeView.SelectedIndex = -1या treeView.SelectedItem = null। मेरा मानना ​​है कि या तो काम करना चाहिए।
जेम्स एम

24

यदि आप 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>

दो दिलचस्प भाग हैं:

  1. TreeViewItem.IsSelectedसंपत्ति के लिए बाध्य है IsSelectedदृश्य-मॉडल पर संपत्ति। IsSelectedदृश्य-मॉडल पर गुण को सही पर सेट करना ट्री में संबंधित नोड का चयन करेगा।

  2. जब PreviewMouseRightButtonDownनोड के दृश्य भाग (इस नमूने में TextBlock) पर आग लग जाती है, तो दृश्य IsSelected-मॉडल पर संपत्ति सही पर सेट हो जाती है। 1. वापस जा रहे हैं आप देख सकते हैं कि पेड़ पर क्लिक किया गया था कि संबंधित नोड चयनित नोड बन जाता है।

अपनी परियोजना में ब्लेंड अन्तरक्रियाशीलता प्राप्त करने का एक तरीका NuGet पैकेज Unofficial.Blend.Interactivity का उपयोग करना है ।


2
शानदार जवाब, धन्यवाद! यह दिखाने में मदद मिलेगी कि क्या 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 विधानसभाओं में क्रमशः पाई जाती हैं।
prlc

इससे मदद नहीं मिली क्योंकि यह बाध्य डेटा ऑब्जेक्ट की संपत्ति ChangePropertyActionसेट करने का प्रयास कर रहा है IsSelected, जो UI का हिस्सा नहीं है, इसलिए इसमें IsSelectedसंपत्ति नहीं है । क्या मुझसे कुछ गलत हो रही है?
एंटोनिन प्रोकाज़का

@ AntonínProcházka: मेरे उत्तर के लिए आवश्यक है कि आपके "डेटा ऑब्जेक्ट" (या मॉडल को देखें) में एक IsSelectedसंपत्ति है जो मेरे उत्तर के दूसरे पैराग्राफ में बताई गई है: मान लें कि TreeViewडेटा एक बायरियन संपत्तिIsSelected वाले दृश्य-मॉडल के एक श्रेणीबद्ध संग्रह के लिए बाध्य है ... (मेरा जोर)।
मार्टिन लीवरेज

16

"Item.Focus ();" का उपयोग करना "item.IsSlectlected = true;" का उपयोग करके 100% काम नहीं करता है। कर देता है।


इस टिप के लिए धन्यवाद। मेरी मदद की।
i8abug

अच्छा सुझाव। मैं फोकस () को पहले कॉल करता हूं, और फिर IsSelected = true सेट करता हूं।
जिम गोम्स

12

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

2
यह अपेक्षा के अनुरूप काम नहीं करता है, मुझे हमेशा मूल तत्व प्रेषक के रूप में मिलते हैं। मैंने एक समान समाधान पाया है एक social.msdn.microsoft.com/Forums/en-US/wpf/thread/… ईवेंट हैंडलर्स ने इस तरह से काम किया जैसी कि उम्मीद थी। इसे स्वीकार करने के लिए आपके कोड में कोई बदलाव? :-)
एलेक्स २23

यह स्पष्ट रूप से इस बात पर निर्भर करता है कि आप पेड़ के दृश्य को कैसे आबाद करते हैं। मैंने जो कोड पोस्ट किया है वह काम करता है, क्योंकि यह सटीक कोड है जो मैं अपने एक टूल में उपयोग करता हूं।
स्टीफन

ध्यान दें कि यदि आप एक डिबग बिंदु सेट करते हैं, तो आप देख सकते हैं कि आपका प्रेषक किस प्रकार का है, जो निश्चित रूप से आपके द्वारा पेड़ को सेट करने के आधार पर भिन्न होगा

जब यह काम करता है तो यह सबसे सरल समाधान की तरह लगता है। इसने मेरे लिए काम किया। वास्तव में, आपको केवल प्रेषक को ट्री व्यू इट के रूप में डालना चाहिए क्योंकि यदि यह नहीं है, तो यह एक बग है।
craftworkgames

12

एलेक्स 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 जब लॉरिकलट्री हेल्पर ने एक मान लौटाया, तो यह ठीक हो गया।


1
इसे आगे बढ़ाने के लिए, यह उत्तर एक डिपेंडेंसीप्रोपरेट्री एक्सटेंशन में इस पर लागू होता है: stackoverflow.com/a/18032332/84522
टेरेंस

7

लगभग सही है , लेकिन आपको पेड़ में गैर दृश्यों के लिए बाहर देखने की जरूरत है, (जैसे 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 treeViewItem = VisualUpwardSearch <TreeViewItem> (eOriginalSource as DependencyObject) लिखता हूं, तो मैं इसका उपयोग कैसे कर सकता हूं; यह मुझे रूपांतरण त्रुटि देता है
Rati_Ge

TreeViewItem TreeViewItem = VisualUpwardSearch <TreeViewItem> (e.OriginalSource, डिपेंडेंसीओबजेक्ट के रूप में) TreeViewItem;
एंथनी वाइजर

6

मुझे लगता है कि एक क्लास हैंडलर को रजिस्टर करना चाहिए। बस अपने ऐप्लिकेशन में 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;
    }
}

मेरे लिए काम किया! और सरल भी।
दिनवल्लो

2
नमस्ते नाथन। ऐसा लगता है कि कोड वैश्विक है और प्रत्येक ट्री व्यू को प्रभावित करेगा। एक समाधान है कि केवल स्थानीय है बेहतर नहीं होगा? यह दुष्प्रभाव पैदा कर सकता है?
एरिक ओउलेट

यह कोड पूरे WPF एप्लीकेशन के लिए वास्तव में वैश्विक है। मेरे मामले में, यह आवश्यक व्यवहार था इसलिए यह एप्लिकेशन के भीतर उपयोग किए जाने वाले सभी ट्रीव्यू के अनुरूप था। हालाँकि आप इस ईवेंट को ट्रीव्यू उदाहरण पर स्वयं पंजीकृत कर सकते हैं, इसलिए यह केवल उस ट्रीव्यू के लिए लागू है।
नाथन स्वनेट

2

MVVM का उपयोग करके इसे हल करने का दूसरा तरीका अपने दृश्य मॉडल पर राइट क्लिक के लिए बाइंड कमांड है। वहां आप अन्य तर्क भी निर्दिष्ट कर सकते हैं source.IsSelected = true। इससे ही उपयोग होता xmlns:i="http://schemas.microsoft.com/expression/2010/intera‌​ctivity"है 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;
        }
    }

1

मुझे बच्चों के साथ 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। चूँकि बच्चा सबसे पहले कहा जाता है। इस गुण को सत्य पर सेट करने से माता-पिता को अन्य कॉल अक्षम हो जाएंगी।
बासित एवर

0

आप इसे माउस डाउन ईवेंट के साथ चुन सकते हैं। संदर्भ मेनू के अंदर जाने से पहले वह चयन को ट्रिगर कर देगा।


0

यदि आप 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>
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.