在树视图中指定数据上下文和绑定分层数据的正确方法是什么

本文关键字:数据 分层 是什么 方法 绑定 上下文 视图 | 更新日期: 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 上的"数据绑定概述"页。

您在这里有很多问题。

  1. ItemsSource绑定应该是ServiceModels而不是Services,因为这是您ServiceCollectionViewModel中的属性名称。这样你就可以得到顶级项目。

  2. 您正在尝试将项目样式绑定到IsExpandedIsSelected属性,但视图模型上没有此类属性。

  3. HierarchicalDataTemplate内部,您将ItemsSource属性直接绑定到TreeViewItemDataContext,在本例中为ServiceModel。使ServiceModel实现IEnumerable<string>公开消息,或直接绑定到Messages属性:

  4. 第二级HierarchicalDataTemplateText属性绑定到Messages,但该级的DataContextString类型,没有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>

我相信这个既更容易理解,因此也更容易维护。