I need to make TabControl with tabs divided by pairs. I am already make template form that looks like I need and works as I need But I need certainly understand that tab button will be on right column after it dynamically created (from source). This is my code
MainWindow.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" } }
};
}
}
}
Items in ProcessVariablesGroup now created "manually" for testing. In general it will be create based on any data.
And question: how can I place items with property IsActive = true in the first column and with IsActive = false in second. I am understand that now is the same but because I add data in constructor on right order. But if it will be created automatically I cant guarantee the right order. I check stackoverflow.com and see that changing UniformGrid to WrapPanel can help me, but I don't understand how.
A
WrapPanelpositions the child elements in sequential position from left to right which means that the order in which you add the items to the source collection is as important as when using aUniformGrid.If you want to be able to put an item in a specific column based on a property value, you could use a
Gridwith twoColumnDefinitionsas theItemsPanelTemplatefor theTabControl. The problem with this approach is to dynamically being able to add aRowDefinitionfor each pair of active and inactive items in the source collection.You could use the
GridHelpersclass from this blog post to create aGridwith a dynamic number of rows or columns:Then use an
ItemContainerStyleto put theTabItemin the right cell based on the values of the source properties:The view model will be responsible for tracking the order of the items. You could handle the
CollectionChangedevent for theObservableCollection<T>. Here is an example that should get you started:The
ProcessVariablesGroupclass should implementINotifyPropertyChangedto provide change notifications to the view: