选择 TreeView 项时显示不同的 UI 元素 [英] Show Different UI elements when selecting TreeView Items

查看:23
本文介绍了选择 TreeView 项时显示不同的 UI 元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 WPF 新手 - 我想为我的服务器创建一个测试器我想在应用程序的右侧有一个 TreeView,每当用户选择一个节点时 - 右侧会显示适当的项目.例如我有一个 Connection 节点,在它下面有许多 Sessions 节点,Connection 和 Session 有不同的参数.我已经使用 mvvm 构建了树视图并且一切正常,但是我如何才能实现第二个目标?

I am new with WPF - I want to create a tester for my server I want to have on the right side of the application a TreeView and whenever a user selects a node - appropriate items is shown on the right side. For example I have a Connection node and under it many Sessions nodes, The Connection and Session have different parameters. I have built the tree view using mvvm and all works fine, but how can I achieve the second goal?

xaml

<Window x:Class="Tree1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:self ="clr-namespace:Tree1"
    xmlns:models ="clr-namespace:Tree1.Models"
    Title="MainWindow" Height="350" Width="525">

<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type self:TestPlanViewModel}" ItemsSource="{Binding Connections}">
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type self:ConnectionViewModel}" ItemsSource="{Binding Sessions}">
        <StackPanel>
            <TextBlock Text="Connection" Margin="10, 0, 0,0"></TextBlock>
        </StackPanel>
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type self:SessionViewModel}">
        <StackPanel Orientation="Horizontal">
            <TextBlock  Margin="10,0,0,0" Text="Session"></TextBlock>
        </StackPanel>
    </DataTemplate>
</Window.Resources>
<Grid Margin="10">
    <Button Height="23" VerticalAlignment="Top" Margin="277,10,144,0" Name="addSessionBtn" Width="76"></Button>

    <TreeView Name="testPlanTview" Margin="0,10,283,0" SelectedItemChanged="testPlanTview_SelectedItemChanged">
        <TreeViewItem ItemsSource="{Binding Connections}" Header="Test Plan">
        </TreeViewItem>

    </TreeView>
</Grid>

推荐答案

您可以使用 数据模板.您的问题有两种可能的解决方案.

You can use DataTemplates. There are two possible solution for your problem.

首先让我们创建一个基类:

First of all let's create a base class:

public abstract class SelectableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private bool isSelected;

    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public bool IsSelected
    {
        get
        {
            return isSelected;
        }
        set
        {
            if (isSelected != value)
            {
                isSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }
    }
}

对于我们的示例,我们有两个类:

For our example we have two classes:

  • 汽车,其属性为Hp"(int)和Matriculation"(DateTime)
  • 人,其属性为姓名"(字符串)和姓氏"(字符串)

它们都扩展了SelectableObject.现在是 ViewModel(当然只是一个示例):

Both of them extend SelectableObject. Now the ViewModel (of course it is just a sample):

public class ViewModel : SelectableObject
{
    private ArrayList tree = new ArrayList();
    private ObjectWrapper current;
    private Car car = new Car();
    private Person person = new Person();

    public ViewModel()
    {
        car.Hp = 120;
        car.Matriculation = DateTime.Today;
        car.PropertyChanged += new PropertyChangedEventHandler(OnItemPropertyChanged);

        person.Name = "John";
        person.Surname = "Doe";
        person.PropertyChanged += new PropertyChangedEventHandler(OnItemPropertyChanged);

        tree.Add(car);
        tree.Add(person);
    }

    private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        SelectableObject impl = (SelectableObject)sender;
        if (e.PropertyName == "IsSelected" && impl.IsSelected)
        {
            Current = new ObjectWrapper(impl);
        }
    }

    public ObjectWrapper Current
    {
        get
        {
            return current;
        }
        private set
        {
            current = value;
            OnPropertyChanged("Current");
        }
    }

    public IEnumerable Tree
    {
        get
        {
            return tree;
        }
    }
}

ViewModel 使用了一个 ObjectWrapper 类:

The ViewModel uses a the ObjectWrapper class:

public class ObjectWrapper
{
    private readonly object wrappedInstance;
    private readonly ReadOnlyCollection<PropertyWrapper> propertyWrappers;

    public ObjectWrapper(object instance)
    {
        Collection<PropertyWrapper> collection = new Collection<PropertyWrapper>();
        Type instanceType;

        wrappedInstance = instance;
        instanceType = instance.GetType();

        foreach (PropertyInfo propertyInfo in instanceType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
        {
            collection.Add(new PropertyWrapper(instance, propertyInfo));
        }

        propertyWrappers = new ReadOnlyCollection<PropertyWrapper>(collection);
    }

    public ReadOnlyCollection<PropertyWrapper> PropertyWrappers
    {
        get
        {
            return propertyWrappers;
        }
    }

    public object Instance { get { return wrappedInstance; } }
}

public class PropertyWrapper
{
    private readonly object instance;
    private readonly PropertyInfo propertyInfo;

    public PropertyWrapper(object instance, PropertyInfo propertyInfo)
    {
        this.instance = instance;
        this.propertyInfo = propertyInfo;
    }

    public string Label
    {
        get
        {
            return propertyInfo.Name;
        }
    }

    public Type PropertyType
    {
        get
        {
            return propertyInfo.PropertyType;
        }
    }

    public object Value
    {
        get
        {
            return propertyInfo.GetValue(instance, null);
        }
        set
        {
            propertyInfo.SetValue(instance, value, null);
        }
    }
}

第一个解决方案(最简单的一个)您可以使用隐式数据模板:

First solution (the easiest one) You can use implicit datatemplating:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="600">

    <DockPanel>
        <TreeView ItemsSource="{Binding Tree}" DockPanel.Dock="Left" Margin="5">
            <TreeView.ItemContainerStyle>
                <Style BasedOn="{StaticResource {x:Type TreeViewItem}}" TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>


        <ContentControl Content="{Binding Path=Current.Instance, Mode=OneWay}" Margin="5">
            <ContentControl.Resources>
                <DataTemplate DataType="{x:Type local:Car}">
                    ... define your car template here ...
                </DataTemplate>

                <DataTemplate DataType="{x:Type local:Person}">
                    ... define your person template here ...
                </DataTemplate>
            </ContentControl.Resources>
        </ContentControl>
    </DockPanel>
</Window>

第二个解决方案(恕我直言是最好的)您可以利用 ObjectWrapper 对象,通过使用 ItemsControl(这里我使用了 用于 DateTime 和 Int 控件的扩展 WPF 工具包:

Second solution (imho the best one) You can take advantage of ObjectWrapper object, by using an ItemsControl (here I used Extended WPF Toolkit for DateTime and Int controls):

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="600">

    <Window.Resources>
        <local:ItemTemplateSelector x:Key="ItemTemplateSelector" />

        <DataTemplate x:Key="{x:Type sys:String}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Label, Mode=OneWay}" VerticalAlignment="Center" />
                <TextBox Text="{Binding Path=Value, Mode=TwoWay}" Margin="5" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="{x:Type sys:DateTime}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Label, Mode=OneWay}" VerticalAlignment="Center" />
                <toolkit:DateTimePicker Value="{Binding Path=Value, Mode=TwoWay}" Margin="5" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="{x:Type sys:Int32}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Label, Mode=OneWay}" VerticalAlignment="Center" />
                <toolkit:IntegerUpDown Value="{Binding Path=Value, Mode=TwoWay}" Margin="5" />
            </StackPanel>
        </DataTemplate>


    </Window.Resources>

    <DockPanel>
        <TreeView ItemsSource="{Binding Tree}" DockPanel.Dock="Left" Margin="5">
            <TreeView.ItemContainerStyle>
                <Style BasedOn="{StaticResource {x:Type TreeViewItem}}" TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>

        <ItemsControl ItemsSource="{Binding Path=Current.PropertyWrappers, Mode=OneWay}"
                      Margin="5" ItemTemplateSelector="{StaticResource ItemTemplateSelector}" />
    </DockPanel>
</Window>

ItemTemplateSelector 的实现并不难:

public class ItemTemplateSelector : DataTemplateSelector
{
    public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        PropertyWrapper propertyWrapper = (PropertyWrapper)item;
        FrameworkElement frameworkElement = (FrameworkElement)container;
        DataTemplate dataTemplate = (DataTemplate)frameworkElement.TryFindResource(propertyWrapper.PropertyType);

        return dataTemplate;
    }
}

我的回答很长,但我想向您展示您可以使用的两条道路.当然你可以通过使用属性来改进PropertyWrapper.

My answer is quite long, but I wanted to show you both roads you can use. Of course you can improve the PropertyWrapper by using attributes.

这篇关于选择 TreeView 项时显示不同的 UI 元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆