在树视图中指定数据上下文和绑定分层数据的正确方法是什么
本文关键字:数据 分层 是什么 方法 绑定 上下文 视图 | 更新日期: 2023-09-27 18:32:00
我有一个相当简单的数据模型:
public class ServiceModel
{
private static readonly IQmiServiceManager ServiceManager = Bootstrap.Instance.DomainManager.QmiServiceManager;
private string _serviceName;
private ObservableCollection<string> _serviceMessages;
public string Name
{
get { return _serviceName; }
private set { _serviceName = value.ToUpper(); }
}
public ObservableCollection<string> Messages
{
get { return _serviceMessages; }
private set { _serviceMessages = value; }
}
public ServiceModel(string ServiceName, IList<string> ServiceMessages)
{
Name = ServiceName;
Messages = new ObservableCollection<string>(ServiceMessages);
}
}
。封装在此视图模型中:
public class ServiceCollectionViewModel
{
private readonly ObservableCollection<ServiceModel> _serviceModels = new ObservableCollection<ServiceModel>();
public ObservableCollection<ServiceModel> ServiceModels
{
get { return _serviceModels; }
}
}
我有以下树视图 xaml 定义:
<TreeView Name="ServiceTree" Grid.Row="1" ItemsSource="{Binding Services}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Messages}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
树中没有输出任何内容。我尝试遵循几个关于分层数据绑定的教程,但我只是难以理解适合我的具体情况的正确技术。
另外,应该如何设置数据上下文?我在视图的代码隐藏中按如下方式设置上下文:
public partial class ServiceView : UserControl
{
private readonly ServiceCollectionViewModel _serviceCollection = new ServiceCollectionViewModel();
public ObservableCollection<ServiceModel> Services
{
get { return _serviceCollection.ServiceModels; }
}
public ServiceView()
{
InitializeComponent();
DataContext = _serviceCollection;
_serviceCollection.LoadServices();
}
}
你似乎很困惑。您正在尝试将数据绑定到 ServiceView.Services
属性,那么 ServiceCollectionViewModel
类的用途是什么?视图模型的唯一目的是提供其视图所需的所有数据(和功能)。
现在我们可以走不同的路线...这完全取决于你真正想要什么。如果要将数据从UserControl
外部绑定到 ServiceView.Services
属性,则必须将 Services
属性声明为 DependencyProperty
。在 UserControl
中,您将使用如下所示的RelativeSource Binding
将数据绑定到属性:
外面:
<YourPrefix:ServiceView Services="{Binding YourExternalViewModelProperty}" />
里面:
<TreeView Name="ServiceTree" Grid.Row="1" ItemsSource="{Binding Services,
RelativeSource={RelativeSource AncestorType={x:Type YourPrefix:ServiceView}}}">
...
</TreeView>
使用此方法,无需将DataContext
设置为任何内容,因为我们使用 RelativeSource
来设置数据源。或者,您可以将DataContext
设置为内部UserControl
代码隐藏或视图模型的实例(从内部或外部),然后通常像这样绑定数据:
<TreeView Name="ServiceTree" Grid.Row="1" ItemsSource="{Binding Services}">
...
</TreeView>
因此,回顾一下,如果将DataContext
设置为对象的实例,则Binding Path
会查看该对象中的属性以自行解析。否则,如果在Binding Path
中设置RelativeSource
(或ElementName
),则仅更改该Binding
的数据源,而Binding Path
应该是这些对象的属性。
更新>>>
HierarchicalDataTemplate
类基本上是常规旧DataTemplate
类的轻微扩展,可以被认为是具有ItemsSource
属性的DataTemplate
。因此,如果您可以定义DataTemplate
的内容,那么您可以定义HierarchicalDataTemplate
的内容...只需确保将 HierarchicalDataTemplate.ItemsSource
属性设置为有效的集合属性...在您的情况下,Messages
属性:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type YourPrefix:ServiceModel}"
ItemsSource="{Binding Messages}"> <!-- Collection Property In ServiceModel -->
<TextBlock Text="{Binding Name}" /> <!-- Property In ServiceModel -->
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
如果您不太清楚DataTemplate
,请查看 MSDN 上的"数据绑定概述"页。
您在这里有很多问题。
-
ItemsSource
绑定应该是ServiceModels
而不是Services
,因为这是您ServiceCollectionViewModel
中的属性名称。这样你就可以得到顶级项目。 -
您正在尝试将项目样式绑定到
IsExpanded
和IsSelected
属性,但视图模型上没有此类属性。 -
在
HierarchicalDataTemplate
内部,您将ItemsSource
属性直接绑定到TreeViewItem
的DataContext
,在本例中为ServiceModel
。使ServiceModel
实现IEnumerable<string>
公开消息,或直接绑定到Messages
属性: -
第二级
HierarchicalDataTemplate
将Text
属性绑定到Messages
,但该级的DataContext
是String
类型,没有Messages
属性。因此,在这里您应该绑定到数据上下文本身:
关于数据上下文,最好使用静态资源在 XAML 中对其进行初始化。此外,定义分层数据模板的更简洁的方法是使用基于项类型的隐式模板。因为您真的不想为更多的层次结构级别(例如:4、5)内联定义分层模板。 请参阅下面的完全修复的 XAML:
<Grid>
<Grid.Resources>
<!--Data context for the whole Grid-->
<myNamespace:ServiceCollectionViewModel x:Key="MyViewModel" />
</Grid.Resources>
<TreeView Name="ServiceTree"
DataContext="{StaticResource MyViewModel}"
ItemsSource="{Binding ServiceModels}">
<TreeView.Resources>
<!--Implicit style for TreeViewItem, IsExpanded and IsSelected have no binding because
they are not used by view-models-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
<!--Template for the first level items-->
<HierarchicalDataTemplate DataType="{x:Type myNamespace:ServiceModel}" ItemsSource="{Binding Messages}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<!--Template for the second level, due to the fact that String is not hierarchical
regular DataTemplate is enough-->
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
我相信这个既更容易理解,因此也更容易维护。