programing

선택한 데이터 바인딩WPF 트리 보기의 항목

iphone6s 2023. 4. 27. 21:56
반응형

선택한 데이터 바인딩WPF 트리 보기의 항목

WPF 트리 보기에서 선택한 항목을 검색하려면 어떻게 해야 합니까?저는 이것을 XAML에서 하고 싶습니다. 왜냐하면 제가 그것을 묶고 싶기 때문입니다.

이 생다고지있도습다니실각하그렇▁that다▁it라고 생각할지도 모릅니다.SelectedItem하지만 분명히 존재하지 않는 은 읽기 전용이므로 사용할 수 없습니다.

이것이 바로 제가 하고 싶은 일입니다.

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

나는 그것을 묶고 싶습니다.SelectedItem내 모델에 있는 부동산으로.

하지만 이것은 나에게 오류를 줍니다.

'선택됨Item' 속성이 읽기 전용이므로 마크업에서 설정할 수 없습니다.

편집: 자, 이것이 제가 해결한 방법입니다.

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

그리고 내 xaml의 비밀번호 파일에서:

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

저는 이것이 이미 받아들여진 답이라는 것을 알고 있지만, 저는 문제를 해결하기 위해 이것을 정리했습니다.Delta의 솔루션과 유사한 아이디어를 사용하지만 TreeView를 하위 분류할 필요는 없습니다.

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>

바라건대 그것이 누군가를 도울 것입니다!

TreeView 속성이 있습니다.선택됨항목

그러나 읽기 전용이므로 바인딩을 통해 할당할 수 없으며 검색만 수행합니다.

해결책을 찾았어요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>

OP가 예상한 것보다 조금 더 많은 답변을 해줍니다.하지만 적어도 누군가에게 도움이 되길 바랍니다.

이 경우는려를 .ICommand언제든지SelectedItem 및 수 . 속성은 " " " " " " 입니다.SelectedItem에 시대에ViewModel더 이상 필요하지 않습니다.

수행 방법:

1 - 다음에 참조 추가합니다.System.Windows.Interactivity

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

. 2 - 명령이바인합니다딩에벤트을▁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>

바인딩과 GalaSoft MVVM Light 라이브러리의 EventToCommand만을 사용하여 '더 나은' 방식으로 이 작업을 수행할 수 있습니다.선택한 항목이 변경될 때 호출될 명령을 VM에 추가하고 필요한 작업을 수행하도록 명령을 초기화합니다.이 예에서는 릴레이 명령을 사용했으며 SelectedCluster 속성만 설정합니다.

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>

모두 복잡한...Caliburn Micro(http://caliburnmicro.codeplex.com/) 로 이동합니다.

보기:

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

모델 보기:

public void SetSelectedItem(YourNodeViewModel item) {}; 

저는 이 페이지에서 원작자와 동일한 답을 찾았고, 그것을 하는 방법이 항상 하나 이상이라는 것을 증명했습니다. 저를 위한 해결책은 지금까지 여기에 제공된 답보다 훨씬 쉬웠기 때문에, 저는 제가 더 많은 것을 추가하는 것이 낫다고 생각했습니다.

바인딩의 동기는 잘 유지하고 MVVM을 유지하는 것입니다.ViewModel은 "현재"와 같은 이름을 가진 속성을 사용할 수 있습니다.Thingy", 그리고 다른 곳에서, 다른 것의 DataContext는 "현재"로 묶여 있습니다.'싱키'.

TreeView에서 My Model로의 멋진 바인딩을 지원하는 데 필요한 추가 단계(예: 사용자 지정 동작, 타사 제어)를 거치는 대신 다른 것에서 My Model로의 바인딩을 지원하는 간단한 Element를 사용하여 다른 것을 TreeView로 바인딩하는 것이 해결책이었습니다.선택된다른 항목을 내 View Model에 바인딩하는 대신 추가 작업을 건너뜁니다.

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}" />

물론 이것은 현재 선택된 항목을 읽기에는 좋지만 설정하지 않으면 됩니다.

TreeViewItem을 사용할 수도 있습니다.IsSelected 속성

제 요구 사항은 TreeView가 필요하고 바인딩된 개체가 Collection<> 유형이므로 계층적 데이터 템플릿이 필요한 PRISM-MVVM 기반 솔루션이었습니다.기본 BindableSelectedItemBehavior는 하위 TreeViewItem을 식별할 수 없습니다.이 시나리오에서 작동하도록 만드는 것.

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

이를 통해 레벨에 관계없이 모든 요소를 반복할 수 있습니다.

저는 스티브 그레이트렉스가 제공한 행동에 추가할 것을 제안합니다.TreeView 컬렉션이 아닐 수 있으므로 소스의 변경 내용을 반영하지 않습니다.아이템. 그래서 트리뷰를 찾는 것이 문제입니다.데이터 컨텍스트가 원본에서 선택한 값인 트리의 항목입니다.TreeView에는 TreeView를 보유한 "ItemsHost"라는 보호된 속성이 있습니다.항목 컬렉션입니다.우리는 그것을 반사를 통해 얻을 수 있고 선택한 아이템을 찾기 위해 트리를 걸을 수 있습니다.

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

이러한 방식으로 양방향 바인딩에 대해 동작이 작동합니다.또는 항목을 이동할 수 있습니다.호스트 획득을 Behavior's OnAttached 메서드로 수행하여 바인딩이 업데이트될 때마다 반사를 사용하는 오버헤드를 줄입니다.

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

저는 이 질문에 대한 모든 해결책을 시도했습니다.아무도 내 문제를 완벽하게 해결하지 못했습니다.따라서 이러한 상속된 클래스는 재정의된 속성 SelectedItem과 함께 사용하는 것이 좋다고 생각합니다.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;
    }
} 

WPF MVVM 트리 보기 선택됨항목

더 나은 답변이지만 선택 항목을 가져오거나 설정하는 방법에 대해서는 언급하지 않았습니다.View Model의 항목입니다.

  1. IsSelected 부울 속성을 ItemViewModel에 추가하고 TreeViewItem의 StyleSetter에서 바인딩합니다.
  2. 선택한 항목 추가TreeView의 데이터 컨텍스트로 사용되는 ViewModel에 대한 항목 속성입니다.위 솔루션에서 누락된 부분입니다.
항목 VM...
공용 속성이 부울로 선택됨얻다반환 _func.선택한 노드는 나입니다.종료 Get설정(값을 부울 값으로 지정)IsSelected 값인 경우_func.SelectedNode = If(값, Me, Nothing)종료할 경우속성 변경 상승()끝 집합끝 특성트리 VM...
공용 속성 선택됨Item As ItemVM얻다반송 _선택됨항목종료 Get설정(항목으로 값 설정)VM)_선택된 경우항목이 값인 경우돌아가다종료할 경우Dim prev = _선택됨항목_선택된항목 = 값만약 이전이 아무것도 아니라면 그때.앞의선택 여부 = 거짓종료할 경우_선택된 경우그러면 항목은 아무것도 아닙니다._selectedItem.선택 여부 = 참종료할 경우끝 집합끝 특성
<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>

하루 동안 인터넷을 공부한 후 일반 WPF/C# 환경에서 일반 트리 보기를 만든 후 항목을 선택할 수 있는 나만의 해결책을 찾았습니다.

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

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

그런 다음 TreeView가 바인딩된 데이터가 포함된 ViewModel에서 TreeView에서 이벤트를 구독하면 됩니다.항목 클래스.

TreeViewItem.OnItemSelected += TreeViewItemSelected;

그리고 마지막으로 이 핸들러를 동일한 View Model에 구현합니다.

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

물론 구속력은

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

이 스레드가 10년 된 것은 알지만 문제는 여전히 존재합니다.

원래 질문은 선택한 항목을 '검색'하는 것이었습니다.또한 선택한 항목을 내 뷰 모델에서 "취득"해야 했습니다(설정하지 않음).이 스레드의 모든 답변 중에서 문제에 다르게 접근하는 것은 'Wes'의 답변뿐입니다.'선택한 항목'을 데이터 바인딩의 대상으로 사용할 수 있는 경우 데이터 바인딩의 소스로 사용합니다.다른 뷰 특성에 대해 수행했습니다. 뷰 모델 특성에 대해 수행합니다.

우리는 두 가지가 필요합니다.

  • 뷰 모델에 종속성 속성 생성(내 트리 뷰가 'MyObject' 유형의 개체에 바인딩되어 있는 'MyObject' 유형의 경우)
  • 트리 보기에서 바인딩합니다.선택된View 생성자의 이 속성에 대한 항목(예, 코드 뒤에 있지만 데이터 컨텍스트도 여기서 초기화할 가능성이 높습니다.)

모델 보기:

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

View 모델의 선택한 항목을 View의 선택한 항목에서 업데이트하는 데 완벽하게 작동하는 이 솔루션(가장 쉽고 메모리 누수가 없는 솔루션)을 제안합니다.

보기 모델에서 선택한 항목을 변경하면 보기의 선택한 항목이 업데이트되지 않습니다.

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

(TreeView가 이 문제와 관련하여 명백하게 결함이 있다는 것에 모두 동의합니다.선택한 항목에 바인딩항목은 분명했을 것입니다.한숨)

TreeView의 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>

다음과 같은 기능을 제공하는 솔루션을 제공합니다.

  • 2방향 바인딩 지원

  • TreeViewItem을 자동으로 업데이트합니다.IsSelected 속성(SelectedItem에 따라)

  • TreeView 하위 분류 없음

  • ViewModel에 바인딩된 항목은 모든 유형(null인 경우도 있음)일 수 있습니다.

1/ CS에 다음 코드를 붙여넣습니다.

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}" />  

일부 항목 목록을 클릭하면 "선택됨" 속성에 데이터가 표시됩니다.

모델 보기:

public class ShellViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    private ObservableCollection<Books> _books;

    private List<Books> bookList = new List<Books>();

    public ObservableCollection<Books> Books
    {
        get { return _books; }
        set { _books = value; NotifyPropertyChanged("Books"); }
    }

    private Books books;

    public Books Selected
    {
        get { return books; }
        set { books = value; }
    }


    public ShellViewModel()
    {
        bookList = new List<Books>()
        {
            new Books{BookName = "Harry Poter",Price ="15$"},
            new Books{BookName = "Harry Poter 2 ",Price ="14.95$"},
            new Books{BookName = "Harry Poter 3",Price ="18.50$"},
            new Books{BookName = "Harry Poter 4",Price ="32.90$"},
        };

        Books = new ObservableCollection<Books>(bookList);
    }
}

public class Books
{
    public string BookName { get; set; }
    public string Price { get; set; }
}

XAML:

 <ListView x:Name="lst" Grid.Row="2" ItemsSource="{Binding Books}" SelectedItem="{Binding Selected}">
        <ListView.View>
            <GridView >
                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding BookName}"  />
                <GridViewColumn Header="Price" Width="100" DisplayMemberBinding="{Binding Price}"/>
            </GridView>
        </ListView.View>
    </ListView>

저는 이것이 게시된 지 오래되었다는 것을 알지만, FWIW 저는 텔리크의 RadTreeView를 사용하고 있고, 선택되었습니다.그 동안 문제가 해결되었거나 텔레릭이 해결해 주었습니다.

언급URL : https://stackoverflow.com/questions/1000040/data-binding-to-selecteditem-in-a-wpf-treeview

반응형