我在 WPF TreeView 控件之上有一个 ViewModel。我希望 ViewModel 能够从 TreeView 设置和读取 SelectedItem。但是,TreeView 的 SelectedItem 属性不可绑定。
我可以在后面的代码中设置和获取选定的项目(使用 ItemContainerGenerator 和 TreeViewItem.IsSelected = true),但这会导致后面的代码和 ViewModel 之间的通信很丑陋。
有没有人有一个干净的解决方案?
我在 WPF TreeView 控件之上有一个 ViewModel。我希望 ViewModel 能够从 TreeView 设置和读取 SelectedItem。但是,TreeView 的 SelectedItem 属性不可绑定。
我可以在后面的代码中设置和获取选定的项目(使用 ItemContainerGenerator 和 TreeViewItem.IsSelected = true),但这会导致后面的代码和 ViewModel 之间的通信很丑陋。
有没有人有一个干净的解决方案?
我可以举个例子。我所做的是在视图模型中设置IsSelected
a TreeViewItem
(而不是TreeView
本身)的属性,因为您可以绑定到它。
在我的视图模型中,我有一个属性ElementInViewModel
,它是一个形成树本身的数据结构。HierarchicalDataTemplate
我在我的 Xaml 中使用 a来显示它。数据对象本身是类型YourDomainType
,其子元素(相同类型)在其ChildElements
属性中。
在视图模型中,我设置了数据类的IsExpanded
and属性。由于下面定义的样式,他们会将此设置传递给.IsSelected
YourDomainType
TreeViewItem
这对你有用吗?
<UserControl>
<UserControl.Resources>
<CollectionViewSource Source="{Binding Path=ElementInViewModel}" x:Key="Cvs">
</CollectionViewSource>
<HierarchicalDataTemplate DataType="{x:Type DomainModel:YourDomainType}"
ItemsSource="{Binding Path=ChildElements}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</Setter>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</Setter>
</Style>
</UserControl.Resources>
<DockPanel>
<TreeView ItemsSource="{Binding Source={StaticResource Cvs}}"/>
</DockPanel>
</UserControl>
您可以使用某种代理类将 SelectedItem 属性绑定到 In 属性,并将 Out 属性绑定到您的 ViewModel:
public class Proxy : FrameworkElement
{
public static readonly DependencyProperty InProperty;
public static readonly DependencyProperty OutProperty;
public Proxy()
{
Visibility = Visibility.Collapsed;
}
static Proxy()
{
var inMetadata = new FrameworkPropertyMetadata(
delegate(DependencyObject p, DependencyPropertyChangedEventArgs args)
{
if (null != BindingOperations.GetBinding(p, OutProperty))
{
var proxy = p as Proxy;
if (proxy != null)
proxy.Out = args.NewValue;
}
});
inMetadata.BindsTwoWayByDefault = false;
inMetadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
InProperty = DependencyProperty.Register("In",
typeof (object),
typeof (Proxy),
inMetadata);
var outMetadata = new FrameworkPropertyMetadata(
delegate(DependencyObject p, DependencyPropertyChangedEventArgs args)
{
ValueSource source = DependencyPropertyHelper.GetValueSource(p, args.Property);
if (source.BaseValueSource != BaseValueSource.Local)
{
var proxy = p as Proxy;
if (proxy != null)
{
var expected = proxy.In;
if (!ReferenceEquals(args.NewValue, expected))
{
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.DataBind, new Action(delegate
{
proxy.Out = proxy.In;
}));
}
}
}
});
outMetadata.BindsTwoWayByDefault = true;
outMetadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
OutProperty = DependencyProperty.Register("Out",
typeof (object),
typeof (Proxy),
outMetadata);
}
public object In
{
get { return GetValue(InProperty); }
set { SetValue(InProperty, value); }
}
public object Out
{
get { return GetValue(OutProperty); }
set { SetValue(OutProperty, value); }
}
}
<Proxy In="{Binding ElementName=Tree, Path=SelectedItem}" Out="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}"/>
<TreeView x:Name="Tree" ItemsSource="{Binding Path=Items}"/>