[C#] WPF Treeview에서 SelectedItem에 대한 데이터 바인딩

WPF-treeview에서 선택된 항목을 어떻게 검색합니까? 바인딩하기 때문에 XAML 에서이 작업을 수행하고 싶습니다.

당신은 것을 생각 SelectedItem하는하지만 분명히 존재하지 않는 읽기 전용 따라서 사용할 수없는 것입니다.

이것이 내가하고 싶은 일입니다.

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

SelectedItem내 모델의 속성에 바인딩하고 싶습니다 .

그러나 이것은 나에게 오류를 준다 :

‘SelectedItem’속성은 읽기 전용이며 마크 업에서 설정할 수 없습니다.

편집 :
좋아, 이것이 내가 해결 한 방법입니다.

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



답변

나는 이것이 이미 대답을 받아 들였다는 것을 알고 있지만 문제를 해결하기 위해 이것을 합쳤다. 델타의 솔루션과 비슷한 아이디어를 사용하지만 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.SelectedItem

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


답변

필요한 경우 외부 속성없이 첨부 된 속성으로 답변하십시오!

바인딩 가능하고 게터와 세터가있는 연결된 속성을 만들 수 있습니다.

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를 구현한다고 가정 한 것입니다.


답변

글쎄, 나는 해결책을 찾았다. 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- 명령을 이벤트에 바인딩 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에서 선택한 항목이 변경 될 때 호출 될 명령을 추가하고 필요한 조치를 수행하도록 명령을 초기화하십시오. 이 예제에서는 RelayCommand를 사용했으며 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) {};