У меня есть набор условий, которые я хочу отобразить в TreeView. У меня есть 2 типа условий: сложные и простые условия.
Сложные условия могут иметь другие сложные / простые условия как дочерние.
В моем пользовательском интерфейсе простые условия отображаются в виде строки в TextBlock. Сложные условия отображаются в виде TreeViewItems. Когда TreeviewItems свернуты, я хочу, чтобы пользовательский интерфейс отображал их строковое представление только в TextBlock, как простые условия.
Когда они развернуты, я хочу, чтобы они отображались по-другому. Это лучше объяснить на рисунке:
В сложном состоянии дочерние / подусловия связаны с логическими 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. Наверное, это неправильное представление о моей точке зрения.
Спасибо за вашу помощь заранее.