Как отобразить TreeViewItem по-разному, свернуто или развернуто - PullRequest
0 голосов
/ 15 апреля 2020

У меня есть набор условий, которые я хочу отобразить в TreeView. У меня есть 2 типа условий: сложные и простые условия.

Сложные условия могут иметь другие сложные / простые условия как дочерние.

В моем пользовательском интерфейсе простые условия отображаются в виде строки в TextBlock. Сложные условия отображаются в виде TreeViewItems. Когда TreeviewItems свернуты, я хочу, чтобы пользовательский интерфейс отображал их строковое представление только в TextBlock, как простые условия.

Когда они развернуты, я хочу, чтобы они отображались по-другому. Это лучше объяснить на рисунке:

A picture can say more than words

В сложном состоянии дочерние / подусловия связаны с логическими c операторами (И / ИЛИ / XOR), которые отображаются в виде ListView перед TreeView. Сложное условие всегда имеет еще одного потомка, чем операторы logi c.

Вот мой XAML:

<Page x:Class="ProContrA.UI.Views.Pages.DisplayConditionsPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  xmlns:local="clr-namespace:ProContrA.UI.Views.Pages"
  xmlns:view ="clr-namespace:ProContrA.UI.ViewModels"
  mc:Ignorable="d" 
  d:DesignHeight="450" d:DesignWidth="800"
  Title="DisplayConditionsPage">
<StackPanel Orientation="Vertical">
    <StackPanel.DataContext>
        <view:ConditionsViewModel/>
    </StackPanel.DataContext>
    <TreeView ItemsSource="{Binding Conditions}" Name="TV">
        <TreeView.Resources>                
            <!-- Hierarchical for complex condidtion (coupled with logic operator) -->
            <HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type view:ComplexConditionViewModel}">
                <StackPanel Orientation="Vertical" Background="{Binding BackgroundBrush}">
                    <!--Display string value when collapsed and expanded-->
                    <TextBlock Text="{Binding Value}"/>
                    <!-- Display sub items only whenn expanded-->
                    <Grid  Background="{Binding BackgroundBrush}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/> <!--1st column for list of logic operators-->
                            <ColumnDefinition Width="*"/> <!--2nd column for treeview of subitems-->
                        </Grid.ColumnDefinitions>
                        <Grid Grid.Column="0" Margin="3,15"> <!-- List of logic operators within cmplex condition-->
                            <ListView ItemsSource="{Binding LogicOperators}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top">
                                <ListView.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding}" Margin="1" />
                                    </DataTemplate>
                                </ListView.ItemTemplate>
                            </ListView>
                        </Grid>                            
                        <!-- Tree with subitems of complexcondition-->
                        <TreeView ItemsSource="{Binding Children}" Grid.Column="1" Name="InnerTV" Margin="3"  Background="{Binding BackgroundBrush}" />
                    </Grid>
                </StackPanel>
            </HierarchicalDataTemplate>

            <!-- Datatemplate for simple conditions-->
            <DataTemplate DataType="{x:Type view:SimpleConditionViewModel}">
                <TextBlock Text="{Binding Value}" Background="{Binding BackgroundBrush}" Padding="3"/>
            </DataTemplate>

        </TreeView.Resources>
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>
</StackPanel>
</Page>

И для лучшего понимания ViewModels:

using Model.Conditions;
using System.Collections.ObjectModel;

namespace ProContrA.UI.ViewModels
{
/// <summary>
/// An interface representing a visual representation of a condition
/// </summary>
public interface IConditionViewModel
{       
    public ICondition Condition { get; set; }

    /// <summary>
    /// The logic operators within a <see cref="ComplexCondition"/>
    /// </summary>
    public ObservableCollection<string> LogicOperators { get; set; }

    /// <summary>
    /// The child conditions of a <see cref="ComplexCondition"/>
    /// </summary>
    public ObservableCollection<IConditionViewModel> Children { get; set; }

    /// <summary>
    /// The value of the condition as string
    /// </summary>
    public string Value { get; set; }
}
}

Простое условие:

using Model.Conditions;
using Model.Conditions.Evaluation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace ProContrA.UI.ViewModels
{
/// <summary>
/// An class representing a visual representation of a <see cref="SimpleCondition"/>
/// </summary>
public class SimpleConditionViewModel : Base.BaseViewModel, IConditionViewModel
{
    /// <summary>
    /// Default constructor
    /// </summary>
    /// <param name="condition">The <see cref="ICondition"/> that should be represented visually</param>
    public SimpleConditionViewModel(ICondition condition)
    {
        Condition = condition;
    }

    #region Properties
    /// <summary>
    /// The <see cref="ICondition"/> that is represented by this ViewModel
    /// </summary>
    public ICondition Condition { get; set; }

    /// <summary>
    /// The <see cref="Condition"/>s value
    /// </summary>
    public string Value
    {
        get { return Condition.Value; }
        set { OnPropertyChanged(new PropertyChangedEventArgs(nameof(Value))); }
    }

    /// <summary>
    /// The Background 
    /// </summary>
    public System.Windows.Media.SolidColorBrush BackgroundBrush
    {
        get
        {
            var red = new System.Windows.Media.Color() { R = 255, G = 0, B = 0, A = 255 };
            var green = new System.Windows.Media.Color() { R = 0, G = 255, B = 0, A = 255 };

            return Evaluator.Evaluate(Condition) == true ? new System.Windows.Media.SolidColorBrush(green) : new System.Windows.Media.SolidColorBrush(red);
        }
    }

    /// <summary>
    /// The <see cref="Condition"/>s children (<see cref="SimpleCondition"/>s and <see cref="ComplexCondition"/>s) as ViewModels.
    /// For a <see cref="SimpleConditionViewModel"/> the collection should be always empty.
    /// </summary>
    public ObservableCollection<IConditionViewModel> Children { get; set; } = new ObservableCollection<IConditionViewModel>();

    /// <summary>
    /// The <see cref="Condition"/>s <see cref="ICondition.LogicOperators"/>.
    /// For a <see cref="SimpleConditionViewModel"/> the collection should be always empty.
    /// </summary>
    public ObservableCollection<string> LogicOperators { get; set; } = new ObservableCollection<string>(); 
    #endregion
}
}

Сложное условие:

using Model.Conditions;
using Model.Conditions.Evaluation;
using ProContrA.UI.ViewModels.Base;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace ProContrA.UI.ViewModels
{
/// <summary>
/// An class representing a visual representation of a <see cref="ComplexCondition"/>
/// </summary>
public class ComplexConditionViewModel : ViewModels.Base.BaseViewModel,IConditionViewModel
{
    /// <summary>
    /// Default constructor
    /// </summary>
    /// <param name="condition">The <see cref="ICondition"/> that should be represented visually</param>
    public ComplexConditionViewModel(ICondition condition)
    {
        Condition = condition;

        Condition.Children?.ForEach(cond =>
        {
            if (cond is SimpleCondition)
            {
                Children.Add(new SimpleConditionViewModel(cond));
            }
            else if (cond is ComplexCondition)
            {
                Children.Add(new ComplexConditionViewModel(cond));
            }
        });

        Condition.LogicOperators?.ForEach(logOp =>
        {
            LogicOperators.Add(logOp);
        });

        ExpandCommand = new RelayCommand(Expand, CanExpand);
    }

    #region Properties
    /// <summary>
    /// The <see cref="ICondition"/> that is represented by this ViewModel
    /// </summary>
    public ICondition Condition { get; set; }

    /// <summary>
    /// The <see cref="Condition"/>s value
    /// </summary>
    public string Value
    {
        get { return Condition.Value; }
        set { OnPropertyChanged(new PropertyChangedEventArgs(nameof(Value))); }
    }

    /// <summary>
    /// The Background 
    /// </summary>
    public System.Windows.Media.SolidColorBrush BackgroundBrush
    {
        get
        {
            var red = new System.Windows.Media.Color() { R = 255, G = 0, B = 0, A = 255 };
            var green = new System.Windows.Media.Color() { R = 0, G = 255, B = 0, A = 255 };

            return Evaluator.Evaluate(Condition) == true ? new System.Windows.Media.SolidColorBrush(green) : new System.Windows.Media.SolidColorBrush(red);
        }
    }

    /// <summary>
    /// The <see cref="Condition"/>s children (<see cref="SimpleCondition"/>s and <see cref="ComplexCondition"/>s) as ViewModels
    /// </summary>
    public ObservableCollection<IConditionViewModel> Children { get; set; } = new ObservableCollection<IConditionViewModel>();

    /// <summary>
    /// The <see cref="Condition"/>s <see cref="ICondition.LogicOperators"/>
    /// </summary>
    public ObservableCollection<string> LogicOperators { get; set; } = new ObservableCollection<string>();

    private bool isExpanded;
    /// <summary>
    /// If the <see cref="ComplexConditionViewModel"/> is expanded
    /// </summary>
    public bool IsExpanded
    {
        get { return this.isExpanded; }
        set
        {
            if (value != this.isExpanded)
            {
                this.isExpanded = value;
                OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsExpanded)));
            }
        }
    } 
    #endregion

    #region Expand Command
    /// <summary>
    /// The command for expanding the <see cref="ComplexCondition"/>
    /// </summary>
    public RelayCommand ExpandCommand { get; set; }

    public bool CanExpand(object o)
    {
        if (Condition is ComplexCondition) return true;
        return false;
    }

    private void Expand(object o)
    {
        // only expand when its a complex condition. simple conditions cant be expanded since they dont have any children
        if (Condition is ComplexCondition)
        {
            var childs = Condition.Children;
            this.Children = new ObservableCollection<IConditionViewModel>();

            foreach (var cond in childs)
            {
                if (cond is SimpleCondition)
                {
                    Children.Add(new SimpleConditionViewModel(cond));
                }
                else if (cond is ComplexCondition)
                {
                    Children.Add(new ComplexConditionViewModel(cond));
                }
            }
        }
    } 
    #endregion

}
}

Вот хранилище: RePos

Кто-нибудь знает, как для достижения этой цели? Я прочитал кое-что о доступе к родительскому элементу, но я не уверен, как это сделать или даже если это правильный путь к go. Наверное, это неправильное представление о моей точке зрения.

Спасибо за вашу помощь заранее.

...