如何检索在WPF-treeview中选择的项?我想在XAML中这样做,因为我想绑定它。

您可能认为它是SelectedItem,但显然它不存在是只读的,因此不可用。

这就是我想做的:

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

我想将SelectedItem绑定到我的Model上的一个属性。

但这给了我一个错误:

“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项的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中,只需订阅TreeViewItem类中的事件。

TreeViewItem.OnItemSelected += TreeViewItemSelected;

最后,在同一个ViewModel中实现这个处理器,

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

当然,绑定,

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

其他回答

(让我们都同意,TreeView在这个问题上显然是行不通的。绑定到SelectedItem是显而易见的。叹息)

我需要解决方案来正确地与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>

我意识到这已经有了一个被接受的答案,但我把这些放在一起来解决问题。它使用了与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>

希望它能帮助到一些人!

我给你带来了我的解决方案,它具有以下特点:

支持2种绑定方式 自动更新TreeViewItemIsSelected属性(根据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}" />  

也可以使用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中,只需订阅TreeViewItem类中的事件。

TreeViewItem.OnItemSelected += TreeViewItemSelected;

最后,在同一个ViewModel中实现这个处理器,

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

当然,绑定,

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

当点击某些项目列表时,你会在“选定”属性中获得数据。 视图模型:

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>