如何检索在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;
}
我找到了一个解决办法。它可以移动混乱,这样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>
我试过了这道题的所有解。没有人能完全解决我的问题。所以我认为最好使用这样的继承类与重定义属性SelectedItem。如果你从GUI中选择tree element并在代码中设置此属性值,它将完美地工作
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;
}
}
这可以以一种“更好”的方式完成,只使用绑定和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>
当点击某些项目列表时,你会在“选定”属性中获得数据。
视图模型:
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>
还有一种方法可以在不使用Interaction.Behaviors的情况下创建XAML可绑定SelectedItem属性。
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}">