WPF ट्रीव्यू में SelectItem के लिए डेटा बाइंडिंग


241

मैं उस आइटम को कैसे पुनः प्राप्त कर सकता हूं जिसे WPF-treeview में चुना गया है? मैं एक्सएएमएल में ऐसा करना चाहता हूं, क्योंकि मैं इसे बांधना चाहता हूं।

आप सोच सकते हैं कि यह है, SelectedItemलेकिन जाहिर है कि मौजूद नहीं है आसानी से और इसलिए अनुपयोगी है।

यही है जो मैं करना चाहता हूं:

<TreeView ItemsSource="{Binding Path=Model.Clusters}" 
            ItemTemplate="{StaticResource ClusterTemplate}"
            SelectedItem="{Binding Path=Model.SelectedCluster}" />

मैं SelectedItemअपने मॉडल पर एक संपत्ति के लिए बाध्य करना चाहता हूं।

लेकिन यह मुझे त्रुटि देता है:

'चुनी हुई' संपत्ति केवल पढ़ने के लिए है और मार्कअप से निर्धारित नहीं की जा सकती।

संपादित करें: ठीक है, यह वह तरीका है जिससे मैंने इसे हल किया है:

<TreeView
          ItemsSource="{Binding Path=Model.Clusters}" 
          ItemTemplate="{StaticResource HoofdCLusterTemplate}"
          SelectedItemChanged="TreeView_OnSelectedItemChanged" />

और मेरे xaml के codebehindfile में:

private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    Model.SelectedCluster = (Cluster)e.NewValue;
}

51
यार ये तो चूसा। इसने मुझे भी मारा। मैं यहां आने की उम्मीद कर रहा हूं कि एक अच्छा तरीका है और मैं सिर्फ एक बेवकूफ हूं। यह पहली बार है कि मैं दुखी हूं कि मैं एक बेवकूफ नहीं हूं ..
आंद्रेई रोनीया

6
यह वास्तव में बाध्यकारी अवधारणा को बेकार और गड़बड़ करता है
डेल्टा

उम्मीद है कि यह ICommand पर एक ट्री दृश्य आइटम का चयन किया बदल कॉल वापस करने के लिए बाँध के लिए कुछ एक मदद कर सकता है jacobaloysious.wordpress.com/2012/02/19/...
याकूब aloysious

9
बाध्यकारी और MVVM के संदर्भ में, पीछे कोड "प्रतिबंधित" नहीं है, बल्कि पीछे के कोड को दृश्य का समर्थन करना चाहिए। मेरे द्वारा देखे गए अन्य सभी समाधानों से मेरी राय में, पीछे का कोड एक बहुत ही बेहतर विकल्प है क्योंकि यह अभी भी दृश्यमॉडल के लिए "बाइंडिंग" के साथ काम कर रहा है। केवल नकारात्मक यह है कि यदि आपके पास एक्सएएमएल में काम करने वाले एक डिजाइनर के साथ एक टीम है, तो पीछे का कोड टूट / उपेक्षित हो सकता है। यह एक समाधान के लिए भुगतान करने के लिए एक छोटी सी कीमत है जिसे लागू करने में 10 सेकंड लगते हैं।
nrjohnstone

सबसे आसान समाधानों में से एक: stackoverflow.com/questions/1238304/…
JoanComasFdz

जवाबों:


240

मुझे लगता है कि यह पहले से ही एक जवाब स्वीकार कर लिया है, लेकिन मैं इस समस्या को हल करने के लिए एक साथ रखा। यह डेल्टा के समाधान के लिए एक समान विचार का उपयोग करता है, लेकिन ट्री व्यू को उप-करने की आवश्यकता के बिना:

public class BindableSelectedItemBehavior : Behavior<TreeView>
{
    #region SelectedItem Property

    public object SelectedItem
    {
        get { return (object)GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object), typeof(BindableSelectedItemBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged));

    private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var item = e.NewValue as TreeViewItem;
        if (item != null)
        {
            item.SetValue(TreeViewItem.IsSelectedProperty, true);
        }
    }

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();

        this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        if (this.AssociatedObject != null)
        {
            this.AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
        }
    }

    private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        this.SelectedItem = e.NewValue;
    }
}

इसके बाद आप अपने XAML में इसका उपयोग कर सकते हैं:

<TreeView>
    <e:Interaction.Behaviors>
        <behaviours:BindableSelectedItemBehavior SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
    </e:Interaction.Behaviors>
</TreeView>

उम्मीद है कि यह किसी की मदद करेगा!


5
जैसा कि ब्रेंट ने बताया कि मुझे बाइंडिंग में मोड = टूवे को जोड़ने की भी आवश्यकता थी। मैं एक "ब्लेंडर" नहीं हूं इसलिए परिचित नहीं था व्यवहार <> System.Windows.Interactivity से वर्ग। असेंबली एक्सप्रेशन ब्लेंड का हिस्सा है। जो लोग इस विधानसभा को पाने के लिए ट्रायल खरीदना / इंस्टॉल नहीं करना चाहते हैं, उनके लिए आप BlendSDK डाउनलोड कर सकते हैं जिसमें System.Windows.Interactivity शामिल है। BlendSDK 3 3.5 के लिए ... मुझे लगता है कि यह 4.0 के लिए BlendSDK 4 है। नोट: यह केवल आपको वह चीज़ प्राप्त करने की अनुमति देता है जो चयनित है, आपको चयनित आइटम सेट करने की अनुमति नहीं देता है
माइक रोली

4
आप FrameworkPropertyMetadata (null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemhanhanged) द्वारा UIPropertyMetadata को भी बदल सकते हैं;
फिलिमिंडजी

3
यह समस्या को हल करने के लिए एक दृष्टिकोण होगा: stackoverflow.com/a/18700099/4227
14

2
@ लुकास जैसा कि ऊपर XAML कोड स्निपेट में दिखाया गया है। बस के {Binding SelectedItem, Mode=TwoWay}साथ बदलें{Binding MyViewModelField, Mode=TwoWay}
स्टीव ग्रेट्रेक्स

4
@ पास्कल इटxmlns:e="http://schemas.microsoft.com/expression/2010/interactivity"
स्टीव ग्रेट्रेक्स

46

यह गुण मौजूद है: TreeView.SelectedItem

लेकिन यह आसानी से है, इसलिए आप इसे एक बंधन के माध्यम से असाइन नहीं कर सकते, केवल इसे पुनः प्राप्त कर सकते हैं


मैं इस उत्तर को स्वीकार करता हूं, क्योंकि वहां मुझे यह लिंक मिला है, जो मेरे स्वयं के उत्तर दें: msdn.microsoft.com/en-us/library/ms788714.aspx
Natrium

1
तो क्या TreeView.SelectedItemउपयोगकर्ता द्वारा किसी आइटम (उर्फ OneWayToSource) का चयन करने पर यह मॉडल पर एक संपत्ति को प्रभावित कर सकता है ?
शिम्मी वेइटहैंडलर

43

संलग्न गुणों और कोई बाहरी निर्भरता के साथ उत्तर दें, क्या कभी आवश्यकता नहीं होनी चाहिए!

आप एक जुड़ी हुई संपत्ति बना सकते हैं जो कि बाँधने योग्य हो और जिसमें एक गेटटर और सेटर हो:

public class TreeViewHelper
{
    private static Dictionary<DependencyObject, TreeViewSelectedItemBehavior> behaviors = new Dictionary<DependencyObject, TreeViewSelectedItemBehavior>();

    public static object GetSelectedItem(DependencyObject obj)
    {
        return (object)obj.GetValue(SelectedItemProperty);
    }

    public static void SetSelectedItem(DependencyObject obj, object value)
    {
        obj.SetValue(SelectedItemProperty, value);
    }

    // Using a DependencyProperty as the backing store for SelectedItem.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(TreeViewHelper), new UIPropertyMetadata(null, SelectedItemChanged));

    private static void SelectedItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is TreeView))
            return;

        if (!behaviors.ContainsKey(obj))
            behaviors.Add(obj, new TreeViewSelectedItemBehavior(obj as TreeView));

        TreeViewSelectedItemBehavior view = behaviors[obj];
        view.ChangeSelectedItem(e.NewValue);
    }

    private class TreeViewSelectedItemBehavior
    {
        TreeView view;
        public TreeViewSelectedItemBehavior(TreeView view)
        {
            this.view = view;
            view.SelectedItemChanged += (sender, e) => SetSelectedItem(view, e.NewValue);
        }

        internal void ChangeSelectedItem(object p)
        {
            TreeViewItem item = (TreeViewItem)view.ItemContainerGenerator.ContainerFromItem(p);
            item.IsSelected = true;
        }
    }
}

उस वर्ग के नाम स्थान घोषणा को अपने XAML में जोड़ें और निम्नानुसार बाँधें (स्थानीय है कि मैंने नाम स्थान घोषणा कैसे घोषित की है):

        <TreeView ItemsSource="{Binding Path=Root.Children}" local:TreeViewHelper.SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}">

    </TreeView>

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


4
+1, इस थ्रेड imho में सर्वश्रेष्ठ उत्तर। System.Windows.Interactivity पर कोई निर्भरता नहीं है, और दो-तरफ़ा बाइंडिंग (MVVM वातावरण में प्रोग्रामेटिक रूप से सेटिंग) की अनुमति देता है। उत्तम।
क्रिस रे

5
इस दृष्टिकोण के साथ एक समस्या यह है कि व्यवहार केवल तब ही काम करना शुरू कर देगा जब चयनित वस्तु को एक बार बाइंडिंग के माध्यम से सेट किया गया हो (यानी ViewModel से)। यदि VM में प्रारंभिक मान शून्य है, तो बाइंडिंग DP मान को अद्यतन नहीं करेगा, और व्यवहार सक्रिय नहीं किया जाएगा। आप एक अलग डिफ़ॉल्ट चयनित आइटम (जैसे एक अमान्य आइटम) का उपयोग करके इसे ठीक कर सकते हैं।
मार्क

6
@Mark: संलग्न संपत्ति के UIPropertyMadadata को इंस्टेंट करते समय ऊपर दिए गए शून्य के बजाय बस नई ऑब्जेक्ट () का उपयोग करें। समस्या तब दूर हो जानी चाहिए ...
barnacleboy

2
TreeViewItem के लिए कास्ट मेरे लिए विफल रहता है क्योंकि मैं मानता हूं कि मैं डेटाट्राइप द्वारा संसाधनों से लागू एक HierarchicalDataTemplate का उपयोग कर रहा हूं। लेकिन अगर आप ChangeSelectedItem को हटाते हैं, तो एक व्यूमॉडल के लिए बाइंड करना और आइटम को पुनर्प्राप्त करना ठीक काम करता है।
केसी सेबेन

1
मैं भी कलाकारों के साथ TreeViewItem के लिए समस्या हो रही है। उस बिंदु पर, ItemContainerGenerator में केवल रूट आइटम के संदर्भ होते हैं, लेकिन मुझे इसकी आवश्यकता है कि गैर-रूट आइटम भी प्राप्त करने में सक्षम हों। यदि आप किसी एक का संदर्भ देते हैं, तो कलाकार विफल हो जाता है और अशक्त हो जाता है। निश्चित नहीं है कि यह कैसे तय किया जा सकता है?
Bob Tway

39

खैर, मुझे एक समाधान मिला। यह गड़बड़ करता है, ताकि MVVM काम करे।

पहले इस वर्ग को जोड़ें:

public class ExtendedTreeView : TreeView
{
    public ExtendedTreeView()
        : base()
    {
        this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(___ICH);
    }

    void ___ICH(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        if (SelectedItem != null)
        {
            SetValue(SelectedItem_Property, SelectedItem);
        }
    }

    public object SelectedItem_
    {
        get { return (object)GetValue(SelectedItem_Property); }
        set { SetValue(SelectedItem_Property, value); }
    }
    public static readonly DependencyProperty SelectedItem_Property = DependencyProperty.Register("SelectedItem_", typeof(object), typeof(ExtendedTreeView), new UIPropertyMetadata(null));
}

और इसे अपने xaml में जोड़ें:

 <local:ExtendedTreeView ItemsSource="{Binding Items}" SelectedItem_="{Binding Item, Mode=TwoWay}">
 .....
 </local:ExtendedTreeView>

3
यह केवल एक चीज है जो अब तक मेरे लिए काम करने के करीब है। मुझे वास्तव में यह समाधान पसंद है।
राचेल

1
पता नहीं क्यों, लेकिन यह मेरे लिए काम नहीं किया :( मैं पेड़ से चयनित आइटम प्राप्त करने में सफल रहा लेकिन इसके विपरीत नहीं - पेड़ के बाहर से चयनित आइटम को बदलने के लिए।
इरेज

यह निर्भरता संपत्ति को BindsTwoWayByDefault के रूप में सेट करने के लिए थोड़ा नटवर होगा, फिर आपको XAML में टूवे निर्दिष्ट करने की आवश्यकता नहीं होगी
स्टीफन होल्ट

यह सबसे अच्छा तरीका है। यह अन्तरक्रियाशीलता संदर्भ का उपयोग नहीं करता है, यह पीछे के कोड का उपयोग नहीं करता है, इसमें स्मृति रिसाव नहीं है जैसे कुछ व्यवहार हैं। धन्यवाद।
अलेक्जेंड्रू डिकू

जैसा कि उल्लेख किया गया है, यह समाधान 2 तरह से बाध्यकारी के साथ काम नहीं करता है। यदि आप व्यूमॉडल में मान सेट करते हैं, तो परिवर्तन ट्री व्यू में नहीं फैलता है।
रिचर्ड मूर

25

यह जवाब देता है कि ओपी अपेक्षा से थोड़ा अधिक है ... लेकिन मुझे उम्मीद है कि यह कम से कम कुछ मदद कर सकता है।

यदि आप एक निष्पादित करने के लिए चाहते हैं ICommandजब भी SelectedItemबदल गया है, आप एक घटना पर एक कमांड और एक संपत्ति के उपयोग बाध्य कर सकते हैं SelectedItemमेंViewModel अब कोई आवश्यकता नहीं है।

ऐसा करने के लिए:

1- संदर्भ जोड़ें System.Windows.Interactivity

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

2- घटना के लिए कमांड को बांधें SelectedItemChanged

<TreeView x:Name="myTreeView" Margin="1"
            ItemsSource="{Binding Directories}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectedItemChanged">
            <i:InvokeCommandAction Command="{Binding SomeCommand}"
                                   CommandParameter="
                                            {Binding ElementName=myTreeView
                                             ,Path=SelectedItem}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <TreeView.ItemTemplate>
           <!-- ... -->
    </TreeView.ItemTemplate>
</TreeView>

3
संदर्भ System.Windows.InteractivityNuGet से स्थापित किया जा सकता है: nuget.org/packages/System.Windows.Interactivity.WPF
Junle

मैं इस समस्या को घंटों तक हल करने की कोशिश कर रहा हूं, मैंने इसे लागू कर दिया है, लेकिन मेरी कमांड काम नहीं कर रही है, कृपया आप मेरी मदद कर सकते हैं?
अल्फी

1
2018 के अंत में Microsoft द्वारा WPF के लिए XAML व्यवहार पेश किया गया था। इसका उपयोग इसके स्थान पर किया जा सकता है System.Windows.Interactivity। यह मेरे लिए काम किया गया था (.NET कोर प्रोजेक्ट के साथ प्रयास किया गया)। चीजों को सेट करने के लिए, बस Microsoft.Xaml.Behaviors.Wpf नगेट पैकेज जोड़ें , नाम स्थान को बदलें xmlns:i="http://schemas.microsoft.com/xaml/behaviors"। अधिक जानकारी प्राप्त करने के लिए - कृपया ब्लॉग
rychlmoj

19

यह केवल बाध्यकारी और गालासॉफ्ट MVVM लाइट लाइब्रेरी के EventToommommand का उपयोग करके एक 'अच्छे' तरीके से पूरा किया जा सकता है। अपने वीएम में एक कमांड जोड़ें, जिसे तब चुना जाएगा जब चयनित आइटम को बदल दिया जाए, और जो भी आवश्यक हो कार्रवाई करने के लिए कमांड को इनिशियलाइज़ करें। इस उदाहरण में मैंने एक RelayCommand का उपयोग किया है और केवल चयनित क्लस्टर संपत्ति सेट करूँगा।

public class ViewModel
{
    public ViewModel()
    {
        SelectedClusterChanged = new RelayCommand<Cluster>( c => SelectedCluster = c );
    }

    public RelayCommand<Cluster> SelectedClusterChanged { get; private set; } 

    public Cluster SelectedCluster { get; private set; }
}

फिर अपने xaml में EventToCommand व्यवहार जोड़ें। यह मिश्रण का उपयोग करके वास्तव में आसान है।

<TreeView
      x:Name="lstClusters"
      ItemsSource="{Binding Path=Model.Clusters}" 
      ItemTemplate="{StaticResource HoofdCLusterTemplate}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectedItemChanged">
            <GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding SelectedClusterChanged}" CommandParameter="{Binding ElementName=lstClusters,Path=SelectedValue}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TreeView>

यह एक अच्छा समाधान है, खासकर यदि आप पहले से ही MvvmLight टूलकिट का उपयोग कर रहे हैं। हालांकि यह चयनित नोड को सेट करने की समस्या को हल नहीं करता है और चयन को ट्रीव्यू अपडेट करता है।
13

12

सभी जटिल ... Caliburn Micro (http://caliburnmicro.codeplex.com/) के साथ जाएं

राय:

<TreeView Micro:Message.Attach="[Event SelectedItemChanged] = [Action SetSelectedItem($this.SelectedItem)]" />

ViewModel:

public void SetSelectedItem(YourNodeViewModel item) {}; 

5
हाँ ... और कहाँ है कि TreeView पर SelectItem सेट करता है ?
mnn

कैलिबर्न अच्छा और सुरुचिपूर्ण है। नेस्टेड पदानुक्रमों के लिए काफी आसानी से काम करता है
पुरुषार्थ

8

मैं मूल लेखक के रूप में एक ही उत्तर की तलाश में इस पृष्ठ पर आया था, और यह साबित करने के लिए हमेशा एक से अधिक तरीके हैं, मेरे लिए समाधान यहां दिए गए उत्तरों की तुलना में बहुत आसान था, इसलिए मुझे लगा कि मैं भी इसे जोड़ सकता हूं ढेर करने के लिए।

बाइंडिंग के लिए प्रेरणा इसे अच्छा और MVVM रखना है। ViewModel का संभावित उपयोग संपत्ति w / एक नाम है जैसे कि "CurrentThingy", और कहीं और, किसी अन्य चीज़ पर DataContext "CurrentThingy" के लिए बाध्य है।

आवश्यक चरणों के माध्यम से जाने के बजाय (जैसे: कस्टम व्यवहार, 3 पार्टी नियंत्रण) ट्री मॉडल से मेरे मॉडल के लिए एक अच्छा बंधन का समर्थन करने के लिए, और फिर मेरे मॉडल से कुछ और से, मेरा समाधान सरल तत्व का उपयोग करना था दूसरी चीज़ को बाध्य करना TreeView.SelectedItem, मेरे ViewModel के लिए दूसरी चीज़ को बाध्य करने के बजाय, जिससे अतिरिक्त कार्य की आवश्यकता होती है।

XAML:

<TreeView x:Name="myTreeView" ItemsSource="{Binding MyThingyCollection}">
.... stuff
</TreeView>

<!-- then.. somewhere else where I want to see the currently selected TreeView item: -->

<local:MyThingyDetailsView 
       DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}" />

बेशक, यह वर्तमान में चयनित आइटम को पढ़ने के लिए बहुत अच्छा है, लेकिन इसे सेट नहीं करना है, जो मुझे चाहिए।


1
स्थानीय क्या है: MyThingyDetailsView? मुझे वह स्थानीय मिलता है: MyThingyDetailsView चयनित आइटम रखता है, लेकिन आपके दृश्य मॉडल को यह जानकारी कैसे मिलती है? यह ऐसा करने के लिए एक अच्छा, साफ तरीका दिखता है, लेकिन मुझे बस थोड़ी और जानकारी चाहिए ...
बॉब हॉर्न

स्थानीय: MyThingyDetailsView XAML से भरा एक UserControl है, जो एक "चीज़" उदाहरण के बारे में विवरण देता है। यह सामग्री के रूप में किसी अन्य दृश्य के बीच में एम्बेडेड है, इस दृश्य का DataContext तत्व वर्तमान में चयनित ट्री व्यू आइटम है, जो एलीमेंट बाइंडिंग का उपयोग करता है।
वेस

6

तुम भी TreeViewItem.IsSelected संपत्ति का उपयोग करने में सक्षम हो सकता है


मुझे लगता है कि यह सही उत्तर हो सकता है। लेकिन मैं एक उदाहरण या सर्वोत्तम-अभ्यास की सिफारिश देखना चाहता हूं कि कैसे आइटम की IsSelected संपत्ति TreeView को पास की जाती है।
अनूपेपी

3

इंटरैक्शन के उपयोग के बिना XAML बाइंडेबल सेलेक्टेम प्रॉपर्टी बनाने का एक तरीका भी है। व्यवहार।

public static class BindableSelectedItemHelper
{
    #region Properties

    public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(BindableSelectedItemHelper),
        new FrameworkPropertyMetadata(null, OnSelectedItemPropertyChanged));

    public static readonly DependencyProperty AttachProperty = DependencyProperty.RegisterAttached("Attach", typeof(bool), typeof(BindableSelectedItemHelper), new PropertyMetadata(false, Attach));

    private static readonly DependencyProperty IsUpdatingProperty = DependencyProperty.RegisterAttached("IsUpdating", typeof(bool), typeof(BindableSelectedItemHelper));

    #endregion

    #region Implementation

    public static void SetAttach(DependencyObject dp, bool value)
    {
        dp.SetValue(AttachProperty, value);
    }

    public static bool GetAttach(DependencyObject dp)
    {
        return (bool)dp.GetValue(AttachProperty);
    }

    public static string GetSelectedItem(DependencyObject dp)
    {
        return (string)dp.GetValue(SelectedItemProperty);
    }

    public static void SetSelectedItem(DependencyObject dp, object value)
    {
        dp.SetValue(SelectedItemProperty, value);
    }

    private static bool GetIsUpdating(DependencyObject dp)
    {
        return (bool)dp.GetValue(IsUpdatingProperty);
    }

    private static void SetIsUpdating(DependencyObject dp, bool value)
    {
        dp.SetValue(IsUpdatingProperty, value);
    }

    private static void Attach(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        TreeListView treeListView = sender as TreeListView;
        if (treeListView != null)
        {
            if ((bool)e.OldValue)
                treeListView.SelectedItemChanged -= SelectedItemChanged;

            if ((bool)e.NewValue)
                treeListView.SelectedItemChanged += SelectedItemChanged;
        }
    }

    private static void OnSelectedItemPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        TreeListView treeListView = sender as TreeListView;
        if (treeListView != null)
        {
            treeListView.SelectedItemChanged -= SelectedItemChanged;

            if (!(bool)GetIsUpdating(treeListView))
            {
                foreach (TreeViewItem item in treeListView.Items)
                {
                    if (item == e.NewValue)
                    {
                        item.IsSelected = true;
                        break;
                    }
                    else
                       item.IsSelected = false;                        
                }
            }

            treeListView.SelectedItemChanged += SelectedItemChanged;
        }
    }

    private static void SelectedItemChanged(object sender, RoutedEventArgs e)
    {
        TreeListView treeListView = sender as TreeListView;
        if (treeListView != null)
        {
            SetIsUpdating(treeListView, true);
            SetSelectedItem(treeListView, treeListView.SelectedItem);
            SetIsUpdating(treeListView, false);
        }
    }
    #endregion
}

इसके बाद आप अपने XAML में इसका उपयोग कर सकते हैं:

<TreeView  helper:BindableSelectedItemHelper.Attach="True" 
           helper:BindableSelectedItemHelper.SelectedItem="{Binding SelectedItem, Mode=TwoWay}">

3

मैंने इस प्रश्न के सभी समाधानों की कोशिश की। किसी ने भी मेरी समस्या का पूरी तरह से समाधान नहीं किया। इसलिए मुझे लगता है कि पुनर्निर्धारित संपत्ति चयनित इत्तेम के साथ इस तरह के विरासत वाले वर्ग का उपयोग करना बेहतर है। यह पूरी तरह से काम करेगा यदि आप GUI से ट्री एलिमेंट चुनते हैं और यदि आप अपने कोड में यह प्रॉपर्टी वैल्यू सेट करते हैं

public class TreeViewEx : TreeView
{
    public TreeViewEx()
    {
        this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(TreeViewEx_SelectedItemChanged);
    }

    void TreeViewEx_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        this.SelectedItem = e.NewValue;
    }

    #region SelectedItem

    /// <summary>
    /// Gets or Sets the SelectedItem possible Value of the TreeViewItem object.
    /// </summary>
    public new object SelectedItem
    {
        get { return this.GetValue(TreeViewEx.SelectedItemProperty); }
        set { this.SetValue(TreeViewEx.SelectedItemProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public new static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object), typeof(TreeViewEx),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedItemProperty_Changed));

    static void SelectedItemProperty_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        TreeViewEx targetObject = dependencyObject as TreeViewEx;
        if (targetObject != null)
        {
            TreeViewItem tvi = targetObject.FindItemNode(targetObject.SelectedItem) as TreeViewItem;
            if (tvi != null)
                tvi.IsSelected = true;
        }
    }                                               
    #endregion SelectedItem   

    public TreeViewItem FindItemNode(object item)
    {
        TreeViewItem node = null;
        foreach (object data in this.Items)
        {
            node = this.ItemContainerGenerator.ContainerFromItem(data) as TreeViewItem;
            if (node != null)
            {
                if (data == item)
                    break;
                node = FindItemNodeInChildren(node, item);
                if (node != null)
                    break;
            }
        }
        return node;
    }

    protected TreeViewItem FindItemNodeInChildren(TreeViewItem parent, object item)
    {
        TreeViewItem node = null;
        bool isExpanded = parent.IsExpanded;
        if (!isExpanded) //Can't find child container unless the parent node is Expanded once
        {
            parent.IsExpanded = true;
            parent.UpdateLayout();
        }
        foreach (object data in parent.Items)
        {
            node = parent.ItemContainerGenerator.ContainerFromItem(data) as TreeViewItem;
            if (data == item && node != null)
                break;
            node = FindItemNodeInChildren(node, item);
            if (node != null)
                break;
        }
        if (node == null && parent.IsExpanded != isExpanded)
            parent.IsExpanded = isExpanded;
        if (node != null)
            parent.IsExpanded = true;
        return node;
    }
} 

यह बहुत तेजी से होगा यदि UpdateLayout () और IsExpanded को कुछ नोड्स के लिए नहीं कहा जाता है। जब UpdateLayout () और IsExpanded को कॉल करने की आवश्यकता नहीं है? जब पहले ट्री आइटम का दौरा किया गया था। कैसे पता चलेगा? कंटेनररोम इटेम () रिटर्न नड्स के लिए अशक्त है। इसलिए हम पेरेंट नोड का विस्तार केवल तभी कर सकते हैं जब कंटेनरफ्रॉम इट () बच्चों के लिए शून्य हो।
कोपरनिक

3

मेरी आवश्यकता PRISM-MVVM आधारित समाधान के लिए थी जहां एक ट्री व्यू की आवश्यकता थी और बाउंड ऑब्जेक्ट संग्रह संग्रह <> का है और इसलिए इसे HierarchicalDataTemplate की आवश्यकता है। डिफ़ॉल्ट BindableSelectedItemBehavior बच्चे की पहचान नहीं कर पाएगा। इस परिदृश्य में काम करने के लिए।

public class BindableSelectedItemBehavior : Behavior<TreeView>
{
    #region SelectedItem Property

    public object SelectedItem
    {
        get { return (object)GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object), typeof(BindableSelectedItemBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged));

    private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var behavior = sender as BindableSelectedItemBehavior;
        if (behavior == null) return;
        var tree = behavior.AssociatedObject;
        if (tree == null) return;
        if (e.NewValue == null)
            foreach (var item in tree.Items.OfType<TreeViewItem>())
                item.SetValue(TreeViewItem.IsSelectedProperty, false);
        var treeViewItem = e.NewValue as TreeViewItem;
        if (treeViewItem != null)
            treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
        else
        {
            var itemsHostProperty = tree.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            if (itemsHostProperty == null) return;
            var itemsHost = itemsHostProperty.GetValue(tree, null) as Panel;
            if (itemsHost == null) return;
            foreach (var item in itemsHost.Children.OfType<TreeViewItem>())
            {
                if (WalkTreeViewItem(item, e.NewValue)) 
                    break;
            }
        }
    }

    public static bool WalkTreeViewItem(TreeViewItem treeViewItem, object selectedValue)
    {
        if (treeViewItem.DataContext == selectedValue)
        {
            treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
            treeViewItem.Focus();
            return true;
        }
        var itemsHostProperty = treeViewItem.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        if (itemsHostProperty == null) return false;
        var itemsHost = itemsHostProperty.GetValue(treeViewItem, null) as Panel;
        if (itemsHost == null) return false;
        foreach (var item in itemsHost.Children.OfType<TreeViewItem>())
        {
            if (WalkTreeViewItem(item, selectedValue))
                break;
        }
        return false;
    }
    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        if (this.AssociatedObject != null)
        {
            this.AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
        }
    }

    private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        this.SelectedItem = e.NewValue;
    }
}

यह स्तर के बावजूद सभी तत्वों के माध्यम से पुनरावृति करने में सक्षम बनाता है।


धन्यवाद! यह एकमात्र ऐसा दृश्य था जो मेरे परिदृश्य के लिए काम करता है जो आपके विपरीत नहीं है।
रॉबर्ट

बहुत अच्छी तरह से काम करता है, और भ्रमित होने के लिए चयनित / विस्तारित बाइंडिंग का कारण नहीं बनता है ।
जंग

2

मैं स्टीव ग्रेट्रेक्स द्वारा प्रदान किए गए व्यवहार के अतिरिक्त सुझाव देता हूं। उसका व्यवहार स्रोत से परिवर्तनों को प्रतिबिंबित नहीं करता है क्योंकि यह ट्री व्यूइमेट्स का संग्रह नहीं हो सकता है। तो यह ट्रीवूइटेम को उस पेड़ में खोजने की बात है, जो स्रोत से डेटावोनेक्स्ट चयनित है। ट्री व्यू में एक संरक्षित संपत्ति है, जिसे "आइटमहॉस्ट" कहा जाता है, जो ट्री व्यू इट संग्रह का संग्रह है। हम इसे प्रतिबिंब के माध्यम से प्राप्त कर सकते हैं और चयनित आइटम की खोज करने वाले पेड़ पर चल सकते हैं।

private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var behavior = sender as BindableSelectedItemBehaviour;

        if (behavior == null) return;

        var tree = behavior.AssociatedObject;

        if (tree == null) return;

        if (e.NewValue == null) 
            foreach (var item in tree.Items.OfType<TreeViewItem>())
                item.SetValue(TreeViewItem.IsSelectedProperty, false);

        var treeViewItem = e.NewValue as TreeViewItem; 
        if (treeViewItem != null)
        {
            treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
        }
        else
        {
            var itemsHostProperty = tree.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            if (itemsHostProperty == null) return;

            var itemsHost = itemsHostProperty.GetValue(tree, null) as Panel;

            if (itemsHost == null) return;

            foreach (var item in itemsHost.Children.OfType<TreeViewItem>())
                if (WalkTreeViewItem(item, e.NewValue)) break;
        }
    }

    public static bool WalkTreeViewItem(TreeViewItem treeViewItem, object selectedValue) {
        if (treeViewItem.DataContext == selectedValue)
        {
            treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
            treeViewItem.Focus();
            return true;
        }

        foreach (var item in treeViewItem.Items.OfType<TreeViewItem>())
            if (WalkTreeViewItem(item, selectedValue)) return true;

        return false;
    }

इस तरह से व्यवहार दो-तरफ़ा बाइंडिंग के लिए काम करता है। वैकल्पिक रूप से, हर बार बाइंडिंग अपडेट के दौरान प्रतिबिंब का उपयोग करने के ओवरहेड को सहेजते हुए व्यवहार के अधिग्रहण को आइटमहोस्ट अधिग्रहण को स्थानांतरित करना संभव है।


2

WPF MVVM ट्री व्यू सेलेक्ट इटेम

... एक बेहतर उत्तर है, लेकिन ViewModel में सिलेक्ट इट को प्राप्त करने / सेट करने के तरीके का उल्लेख नहीं करता है।

  1. अपने ItemViewModel में IsSelected बूलियन प्रॉपर्टी जोड़ें, और TreeViewItem के लिए स्टाइल सेटर में इसे बांधें।
  2. TreeView के लिए DataContext के रूप में उपयोग किए गए अपने ViewModel में एक चयनितआइटम गुण जोड़ें। यह उपरोक्त समाधान में लापता टुकड़ा है।
    'आइटमवीएम ...
    सार्वजनिक संपत्ति को बूलियन के रूप में जाना जाता है
        प्राप्त
            वापसी _func.SelectedNode है
        अंत मिलता है
        सेट (मान बूलियन के रूप में)
            यदि IsSelected मान तब
                _func.SelectedNode = यदि (मान, मुझे, कुछ भी नहीं)
            अगर अंत
            RaisePropertyChange ()
        अंतिम सेट
    अंतिम संपत्ति
    'ट्रीवीएम ...
    ItemVM के रूप में सार्वजनिक संपत्ति चयनित
        प्राप्त
            वापसी _selectedItem
        अंत मिलता है
        सेट करें (ItemVM के रूप में मान)
            अगर _selectedItem का मूल्य है तो
                वापसी
            अगर अंत
            मंद प्रचलित = _selectedItem
            _selectedItem = मान
            यदि प्रचलित है तो कुछ भी नहीं
                prev.slectlected = गलत
            अगर अंत
            अगर _selectedItem IsNot कुछ भी नहीं है
                _selectedItem.IsSelected = True
            अगर अंत
        अंतिम सेट
    अंतिम संपत्ति
<TreeView ItemsSource="{Binding Path=TreeVM}" 
          BorderBrush="Transparent">
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded}"/>
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
            <TextBlock Text="{Binding Name}"/>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

1

एक दिन के लिए इंटरनेट अध्ययन करने के बाद मैं एक बनाने के बाद एक आइटम के चयन के लिए अपने खुद के समाधान सामान्य एक में treeview सामान्य WPF / सी # पर्यावरण

private void BuildSortTree(int sel)
        {
            MergeSort.Items.Clear();
            TreeViewItem itTemp = new TreeViewItem();
            itTemp.Header = SortList[0];
            MergeSort.Items.Add(itTemp);
            TreeViewItem prev;
            itTemp.IsExpanded = true;
            if (0 == sel) itTemp.IsSelected= true;
            prev = itTemp;
            for(int i = 1; i<SortList.Count; i++)
            {

                TreeViewItem itTempNEW = new TreeViewItem();
                itTempNEW.Header = SortList[i];
                prev.Items.Add(itTempNEW);
                itTempNEW.IsExpanded = true;
                if (i == sel) itTempNEW.IsSelected = true;
                prev = itTempNEW ;
            }
        }

1

यह TreeView आइटम की IsSelected संपत्ति का उपयोग करके भी किया जा सकता है। यहां बताया गया है कि मैंने इसे कैसे प्रबंधित किया,

public delegate void TreeviewItemSelectedHandler(TreeViewItem item);
public class TreeViewItem
{      
  public static event TreeviewItemSelectedHandler OnItemSelected = delegate { };
  public bool IsSelected 
  {
    get { return isSelected; }
    set 
    { 
      isSelected = value;
      if (value)
        OnItemSelected(this);
    }
  }
}

तब ViewModel में वह डेटा होता है जिसमें आपका TreeView बाध्य होता है, बस TreeViewItem वर्ग में ईवेंट की सदस्यता लें।

TreeViewItem.OnItemSelected += TreeViewItemSelected;

और अंत में, इस हैंडलर को उसी ViewModel में लागू करें,

private void TreeViewItemSelected(TreeViewItem item)
{
  //Do something
}

और निश्चित रूप से,

<Setter Property="IsSelected" Value="{Binding IsSelected}" />    

यह वास्तव में एक अंडर रेटेड समाधान है। प्रत्येक ट्रीव्यू तत्व की IsSelected संपत्ति को बांधने और सोचने के अपने तरीके को बदलने से, और IsSelected ईवेंट को बुदबुदाने से आपको अंतर्निहित कार्यक्षमता का उपयोग करने के लिए मिलता है जो दो तरह से बाध्यकारी के साथ अच्छी तरह से काम करता है। मैंने इस समस्या के कई प्रस्तावित समाधानों की कोशिश की है, और यह पहली बार काम किया है। बस तार करने के लिए थोड़ा जटिल है। धन्यवाद।
रिचर्ड मूर

1

मुझे पता है कि यह धागा 10 साल पुराना है लेकिन समस्या अभी भी मौजूद है ...।

मूल प्रश्न चयनित आइटम को 'पुनः प्राप्त करने' के लिए था। मुझे अपने दृश्यमॉडल में चयनित आइटम को "प्राप्त" करने की आवश्यकता है (इसे सेट नहीं किया गया है)। इस थ्रेड के सभी उत्तरों में से, 'वेस' द्वारा किया गया एकमात्र ऐसा ही है जो समस्या को अलग तरीके से बताता है: यदि आप 'चयनित आइटम' का उपयोग डेटाबाइंडिंग के लिए एक लक्ष्य के रूप में कर सकते हैं तो इसे डेटाबाइंडिंग के लिए स्रोत के रूप में उपयोग करें। वेस ने इसे दूसरे व्यू प्रॉपर्टी के लिए किया, मैं इसे व्यूमोडल प्रॉपर्टी के लिए करूंगा:

हमें दो चीजें चाहिए:

  • व्यूअमॉडल में एक निर्भरता संपत्ति बनाएँ (मेरे प्रकार के 'MyObject' के मामले में क्योंकि मेरा ट्रीव्यू 'MyObject' प्रकार के ऑब्जेक्ट के लिए बाध्य है)
  • Treeview.SelectedItem से इस गुण को व्यू के निर्माता में बाँधें (हाँ जो कोड के पीछे है लेकिन, यह संभावना है कि आप अपने डेटाकोन्टेक्ट को वहां भी डाल देंगे)

viewmodel:

public static readonly DependencyProperty SelectedTreeViewItemProperty = DependencyProperty.Register("SelectedTreeViewItem", typeof(MyObject), typeof(MyViewModel), new PropertyMetadata(OnSelectedTreeViewItemChanged));

    private static void OnSelectedTreeViewItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as MyViewModel).OnSelectedTreeViewItemChanged(e);
    }

    private void OnSelectedTreeViewItemChanged(DependencyPropertyChangedEventArgs e)
    {
        //do your stuff here
    }

    public MyObject SelectedWorkOrderTreeViewItem
    {
        get { return (MyObject)GetValue(SelectedTreeViewItemProperty); }
        set { SetValue(SelectedTreeViewItemProperty, value); }
    }

देखें निर्माता:

Binding binding = new Binding("SelectedItem")
        {
            Source = treeView, //name of tree view in xaml
            Mode = BindingMode.OneWay
        };

        BindingOperations.SetBinding(DataContext, MyViewModel.SelectedTreeViewItemProperty, binding);

0

(चलो बस सभी सहमत हैं कि ट्रीव्यू इस समस्या के संबंध में स्पष्ट रूप से पर्दाफाश किया गया है। चयनितइमाइंड को बाइंड करना स्पष्ट था। )

मुझे TreeViewItem की IsSelected संपत्ति के साथ ठीक से बातचीत करने के लिए समाधान की आवश्यकता थी, इसलिए यहां मैंने यह कैसे किया:

// the Type CustomThing needs to implement IsSelected with notification
// for this to work.
public class CustomTreeView : TreeView
{
    public CustomThing SelectedCustomThing
    {
        get
        {
            return (CustomThing)GetValue(SelectedNode_Property);
        }
        set
        {
            SetValue(SelectedNode_Property, value);
            if(value != null) value.IsSelected = true;
        }
    }

    public static DependencyProperty SelectedNode_Property =
        DependencyProperty.Register(
            "SelectedCustomThing",
            typeof(CustomThing),
            typeof(CustomTreeView),
            new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.None,
                SelectedNodeChanged));

    public CustomTreeView(): base()
    {
        this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(SelectedItemChanged_CustomHandler);
    }

    void SelectedItemChanged_CustomHandler(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        SetValue(SelectedNode_Property, SelectedItem);
    }

    private static void SelectedNodeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var treeView = d as CustomTreeView;
        var newNode = e.NewValue as CustomThing;

        treeView.SelectedCustomThing = (CustomThing)e.NewValue;
    }
}

इस XAML के साथ:

<local:CustonTreeView ItemsSource="{Binding TreeRoot}" 
    SelectedCustomThing="{Binding SelectedNode,Mode=TwoWay}">
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
        </Style>
    </TreeView.ItemContainerStyle>
</local:CustonTreeView>

0

मैं आपको अपना समाधान लाता हूं जो निम्नलिखित विशेषताएं प्रदान करता है:

  • बंधन के 2 तरीके का समर्थन करता है

  • ऑटो अपडेट TreeViewItem.IsSellected गुण (चयनित के अनुसार)

  • कोई TreeView उपवर्ग नहीं

  • ViewModel से बंधे आइटम किसी भी प्रकार के हो सकते हैं (यहां तक ​​कि अशक्त)

1 / अपने सीएस में निम्नलिखित कोड चिपकाएँ:

public class BindableSelectedItem
{
    public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached(
        "SelectedItem", typeof(object), typeof(BindableSelectedItem), new PropertyMetadata(default(object), OnSelectedItemPropertyChangedCallback));

    private static void OnSelectedItemPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var treeView = d as TreeView;
        if (treeView != null)
        {
            BrowseTreeViewItems(treeView, tvi =>
            {
                tvi.IsSelected = tvi.DataContext == e.NewValue;
            });
        }
        else
        {
            throw new Exception("Attached property supports only TreeView");
        }
    }

    public static void SetSelectedItem(DependencyObject element, object value)
    {
        element.SetValue(SelectedItemProperty, value);
    }

    public static object GetSelectedItem(DependencyObject element)
    {
        return element.GetValue(SelectedItemProperty);
    }

    public static void BrowseTreeViewItems(TreeView treeView, Action<TreeViewItem> onBrowsedTreeViewItem)
    {
        var collectionsToVisit = new System.Collections.Generic.List<Tuple<ItemContainerGenerator, ItemCollection>> { new Tuple<ItemContainerGenerator, ItemCollection>(treeView.ItemContainerGenerator, treeView.Items) };
        var collectionIndex = 0;
        while (collectionIndex < collectionsToVisit.Count)
        {
            var itemContainerGenerator = collectionsToVisit[collectionIndex].Item1;
            var itemCollection = collectionsToVisit[collectionIndex].Item2;
            for (var i = 0; i < itemCollection.Count; i++)
            {
                var tvi = itemContainerGenerator.ContainerFromIndex(i) as TreeViewItem;
                if (tvi == null)
                {
                    continue;
                }

                if (tvi.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
                {
                    collectionsToVisit.Add(new Tuple<ItemContainerGenerator, ItemCollection>(tvi.ItemContainerGenerator, tvi.Items));
                }

                onBrowsedTreeViewItem(tvi);
            }

            collectionIndex++;
        }
    }

}

2 / XAML फ़ाइल में उपयोग का उदाहरण

<TreeView myNS:BindableSelectedItem.SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" />  

0

मैं इस समाधान का प्रस्ताव करता हूं (जिसे मैं सबसे आसान और मेमोरी लीक मुक्त मानता हूं) जो कि व्यू के चयनित आइटम से व्यूमॉडल के चयनित आइटम को अपडेट करने के लिए पूरी तरह से काम करता है।

कृपया ध्यान दें कि ViewModel से चयनित आइटम को बदलना दृश्य के चयनित आइटम को अपडेट नहीं करेगा।

public class TreeViewEx : TreeView
{
    public static readonly DependencyProperty SelectedItemExProperty = DependencyProperty.Register("SelectedItemEx", typeof(object), typeof(TreeViewEx), new FrameworkPropertyMetadata(default(object))
    {
        BindsTwoWayByDefault = true // Required in order to avoid setting the "BindingMode" from the XAML
    });

    public object SelectedItemEx
    {
        get => GetValue(SelectedItemExProperty);
        set => SetValue(SelectedItemExProperty, value);
    }

    protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
    {
        SelectedItemEx = e.NewValue;
    }
}

XAML का उपयोग

<l:TreeViewEx ItemsSource="{Binding Path=Items}" SelectedItemEx="{Binding Path=SelectedItem}" >
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.