我需要制作 TabControl,其选项卡成对划分。
但我当然需要了解,选项卡按钮在动态创建(从源代码)后将位于右列。
这是我的代码
主窗口.xaml
<Window x:Class="HMI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:localVM="clr-namespace:HMI.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<localVM:HMIViewModel/>
</Window.DataContext>
<TabControl TabStripPlacement="Right" ItemsSource="{Binding ProcessVariablesGroups}">
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Height" Value="70"/>
<Setter Property="Width" Value="70"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" BorderThickness="1" BorderBrush="Black" CornerRadius="5" Margin="2">
<ContentPresenter x:Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Center" ContentSource="Header" Margin="10,2"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="LightGray" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Border" Property="Background" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabControl.Style>
<Style TargetType="TabControl" x:Name="myName">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" ContentSource="SelectedContent"/>
<ItemsPresenter Grid.Column="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Style>
<TabControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" VerticalAlignment="Top" HorizontalAlignment="Right"/>
</ItemsPanelTemplate>
</TabControl.ItemsPanel>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Name}" Margin="5" Width="120"/>
<ListView ItemsSource="{Binding ProcessVariables}">
<ListView.View>
<GridView>
<GridViewColumn Header="Some column"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Window>
ViewModelDTO.cs
using System.Collections.ObjectModel;
namespace HMI.ViewModel
{
public class ProcessVariablesGroup
{
public string Name { get; set; }
public string Value { get; set; }
public bool IsActive { get; set; }
public ObservableCollection<string> ProcessVariables { get; set; } = new ObservableCollection<string>();
}
}
HMIViewModel.cs
using System.Collections.ObjectModel;
namespace HMI.ViewModel
{
public class HMIViewModel
{
private readonly ObservableCollection<ProcessVariablesGroup> _processVariablesGroups;
public ObservableCollection<ProcessVariablesGroup> ProcessVariablesGroups => _processVariablesGroups;
public HMIViewModel()
{
_processVariablesGroups = new ObservableCollection<ProcessVariablesGroup>
{
new ProcessVariablesGroup { Name = "P1A", IsActive = true, ProcessVariables = new ObservableCollection<string>() { "P1A1", "P1A2", "P1A3" } },
new ProcessVariablesGroup { Name = "P1P", IsActive = false, ProcessVariables = new ObservableCollection<string>() { "P1P1", "P1P2", "P1P3" } },
new ProcessVariablesGroup { Name = "P2A", IsActive = true, ProcessVariables = new ObservableCollection<string>() { "P2A1", "P2A2", "P2A3" } },
new ProcessVariablesGroup { Name = "P2P", IsActive = false, ProcessVariables = new ObservableCollection<string>() { "P2P1", "P2P2", "P2P3" } },
new ProcessVariablesGroup { Name = "P3A", IsActive = true, ProcessVariables = new ObservableCollection<string>() { "P3A1", "P3A2", "P3A3" } },
new ProcessVariablesGroup { Name = "P3P", IsActive = false, ProcessVariables = new ObservableCollection<string>() { "P3P1", "P3P2", "P3P3" } }
};
}
}
}
ProcessVariablesGroup 中的项目现在“手动”创建以进行测试。一般来说,它将根据任何数据创建。
还有问题:如何将属性 IsActive = true 的项目放置在第一列中,将 IsActive = false 的项目放置在第二列中。我知道现在是一样的,但是因为我按正确的顺序在构造函数中添加数据。但如果它是自动创建的,我不能保证正确的顺序。 我检查了 stackoverflow.com 并发现将 UniformGrid 更改为 WrapPanel 可以帮助我,但我不明白如何实现。
A
WrapPanel
将子元素从左到右按顺序放置,这意味着将项目添加到源集合的顺序与使用 UniformGrid
时一样重要。
如果您希望能够根据属性值将项目放入特定列中,您可以使用
Grid
和两个 ColumnDefinitions
作为 ItemsPanelTemplate
的 TabControl
。这种方法的问题是能够动态地为源集合中的每对活动和非活动项目添加 RowDefinition
。
您可以使用
这篇博文中的
GridHelpers
类来创建具有动态行数或列数的Grid
:
<TabControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid VerticalAlignment="Top" HorizontalAlignment="Right"
local:GridHelpers.RowCount="{Binding DataContext.ProcessVariablesGroups.Count,
RelativeSource={RelativeSource AncestorType=TabControl}}"
local:GridHelpers.ColumnCount="2" />
</ItemsPanelTemplate>
</TabControl.ItemsPanel>
然后使用
ItemContainerStyle
根据源属性的值将 TabItem
放入右侧单元格中:
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}" Value="False">
<Setter Property="Grid.Column" Value="1" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Grid.Row" Value="{Binding N}" />
</Style>
</TabControl.ItemContainerStyle>
视图模型将负责跟踪项目的顺序。您可以为
CollectionChanged
处理 ObservableCollection<T>
事件。这是一个可以帮助您入门的示例:
public class HMIViewModel
{
private readonly ObservableCollection<ProcessVariablesGroup> _processVariablesGroups;
public ObservableCollection<ProcessVariablesGroup> ProcessVariablesGroups => _processVariablesGroups;
public HMIViewModel()
{
_processVariablesGroups = new ObservableCollection<ProcessVariablesGroup>
{
new ProcessVariablesGroup { Name = "P1A", IsActive = true, N = 0, ProcessVariables = new ObservableCollection<string>() { "P1A1", "P1A2", "P1A3" } },
new ProcessVariablesGroup { Name = "P1P", IsActive = false, N = 0, ProcessVariables = new ObservableCollection<string>() { "P1P1", "P1P2", "P1P3" } },
new ProcessVariablesGroup { Name = "P2A", IsActive = true, N = 1, ProcessVariables = new ObservableCollection<string>() { "P2A1", "P2A2", "P2A3" } },
new ProcessVariablesGroup { Name = "P2P", IsActive = false, N = 1, ProcessVariables = new ObservableCollection<string>() { "P2P1", "P2P2", "P2P3" } },
new ProcessVariablesGroup { Name = "P3A", IsActive = true, N = 2, ProcessVariables = new ObservableCollection<string>() { "P3A1", "P3A2", "P3A3" } },
new ProcessVariablesGroup { Name = "P3P", IsActive = false, N = 2, ProcessVariables = new ObservableCollection<string>() { "P3P1", "P3P2", "P3P3" } }
};
_processVariablesGroups.CollectionChanged += OnCollectionChanged;
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
int numberOfActiveProcessVariablesGroups = 0;
int numberOfInactiveProcessVariablesGroups = 0;
foreach (var item in _processVariablesGroups)
item.N = item.IsActive ? numberOfActiveProcessVariablesGroups++ : numberOfInactiveProcessVariablesGroups++;
}
}
ProcessVariablesGroup
类应该实现INotifyPropertyChanged
以向视图提供更改通知:
public class ProcessVariablesGroup : INotifyPropertyChanged
{
public string Name { get; set; }
public string Value { get; set; }
public bool IsActive { get; set; }
public ObservableCollection<string> ProcessVariables { get; set; } = new ObservableCollection<string>();
private int _n;
public int N
{
get { return _n; }
set { _n = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}