1 year ago
#375702
MERUN KUMAR MAITY
How to show/hide Rectangles on TabItem selection depending on a Tab Control ViewModel in WPF?
I have a WPF application where I need to use Tab Control. In my Tab Control I have total three tab items.
Here my MainWindow.xaml code :
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TabControl
Grid.Row="0"
x:Name="TestTabs">
<TabItem Header="Section 1"/>
<TabItem Header="Section 2"/>
<TabItem Header="Section 3"/>
</TabControl>
</Grid>
Due to my design purpose I need to underline the TabItem whenever it is selected. To give more design to the UI, I add some Data trigger animation into it. My approach is, I give two rectangles under the first and second Tab item and bind With Tab Control selected index. So, every time any individual Tab item is selected the Enter animation and Exit animation will fired/execute. I do not give any rectangle under the third tab item because the previous two rectangles will cover tho whole animation of those three tab items and it's sufficient.
But the problem is, the Rectangles Enter animations and Exit animations are interfere with each other and both are execute at a same time when I click any specific tab item bounded to the tab control selected index.
To overcome this issue I implement a View Model. Using this View Model I can track the last two tab items which I selected. To verify that my View Model is perfectly working or not. I tested with a Button. If I click the button then a Message Box will appear and it will show my current and Previous tab item which I selected.
But I don't know How to hide and show Rectangles depending on the last two tab items which I selected through the View Model. My approach is only to show the rectangles that are bounded to their Tab items. I give an example suppose the first Tab item named "Section 1" has a Rectangle named Rect1 and the second Tab item named "Section 2" has a Rectangle named Rect2. this Rect1 and Rect2 are only visible if their parent tab item is selected through under the View Model.
Here is my Complete MainWindow.xaml code :
<Window.Resources>
<local:TestViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TabControl DataContext="{StaticResource MainViewModel}"
SelectedIndex="{Binding Selected}"
Grid.Row="0"
x:Name="TestTabs">
<TabItem Header="Section 1"/>
<TabItem Header="Section 2"/>
<TabItem Header="Section 3"/>
</TabControl>
<Button Content="Check
Selected Index"
Grid.Row="1"
x:Name="TestButton"
Click="TestButton_OnClick"/>
<DockPanel x:Name="rp" Grid.Row="0" LastChildFill="False" HorizontalAlignment="Stretch">
<Canvas DockPanel.Dock="Left" >
<Rectangle x:Name="Rect1" Fill="#ff0000" VerticalAlignment="Top" Width="50.2" Height="4" Margin="6,25,0,0" SnapsToDevicePixels="True" UseLayoutRounding="True" RenderOptions.EdgeMode="Aliased" RenderOptions.BitmapScalingMode="HighQuality" >
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="RenderOptions.EdgeMode" Value="Aliased"/>
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TestTabs, Path=SelectedIndex}" Value="1">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="RenderOptions.EdgeMode" Value="Aliased"/>
<Setter Property="Visibility" Value="Visible" />
<Setter Property="IsEnabled" Value="True" />
<DataTrigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="MyBeginStoryboard3"/>
<RemoveStoryboard BeginStoryboardName="MyBeginStoryboard4"/>
<BeginStoryboard Name="MyBeginStoryboard1">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="0" To="63.5" Duration="0:0:0.2"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="Width" From="50.2" To="50.2" Duration="0:0:0.2"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Name="MyBeginStoryboard2">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="63.5" To="0" Duration="0:0:0.2"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="Width" From="50.2" To="50.2" Duration="0:0:0.2"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=TestTabs, Path=SelectedIndex}" Value="2"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<RemoveStoryboard BeginStoryboardName="MyBeginStoryboard1"/>
<RemoveStoryboard BeginStoryboardName="MyBeginStoryboard2"/>
<BeginStoryboard Name="MyBeginStoryboard3">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="0" To="123.5" Duration="0:0:0.2"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="Width" From="50.2" To="50.2" Duration="0:0:0.2"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard Name="MyBeginStoryboard4">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="123.5" To="0" Duration="0:0:0.2"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="Width" From="50.2" To="50.2" Duration="0:0:0.2"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<Rectangle x:Name="Rect2" Fill="#ff0000" VerticalAlignment="Top" Width="50.2" Height="4" Margin="68,25,0,0" SnapsToDevicePixels="True" UseLayoutRounding="True" RenderOptions.EdgeMode="Aliased" RenderOptions.BitmapScalingMode="HighQuality" >
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="RenderOptions.EdgeMode" Value="Aliased"/>
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TestTabs, Path=SelectedIndex}" Value="2">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="RenderOptions.EdgeMode" Value="Aliased"/>
<Setter Property="Visibility" Value="Visible" />
<Setter Property="IsEnabled" Value="True" />
<DataTrigger.EnterActions>
<BeginStoryboard Name="MyBeginStoryboard5">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="0" To="63.5" Duration="0:0:0.2"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="Width" From="50.2" To="50.2" Duration="0:0:0.2"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Name="MyBeginStoryboard6">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="63.5" To="0" Duration="0:0:0.2"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="Width" From="50.2" To="50.2" Duration="0:0:0.2"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Canvas>
</DockPanel>
</Grid>
Here is my Main View Model code name as TestViewModel :
class TestViewModel : INotifyPropertyChanged
{
private int _selected;
public int Selected
{
get { return _selected; }
set
{
int temp = _selected;
_selected = value;
_previousSelected = temp;
NotifyPropertyChanged("Selected", temp, value);
NotifyPropertyChanged("PreviousSelected", temp, temp);
}
}
int _previousSelected = 0;
public int PreviousSelected
{
get { return _previousSelected; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged<T>(string propertyName, T oldvalue, T newvalue)
{
OnPropertyChanged(this, new PropertyChangedExtendedEventArgs<T>(propertyName, oldvalue, newvalue));
}
public virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(sender, e);
}
}
public class PropertyChangedExtendedEventArgs<T> : PropertyChangedEventArgs
{
public virtual T OldValue { get; private set; }
public virtual T NewValue { get; private set; }
public PropertyChangedExtendedEventArgs(string propertyName, T oldValue, T newValue)
: base(propertyName)
{
OldValue = oldValue;
NewValue = newValue;
}
}
Here is the MainWindow.xaml.cs logic code where I tested the View model with a Button and Message Box to see if the last two item selection is perfectly working or not.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TestButton_OnClick(object sender, RoutedEventArgs e)
{
var vm = TestTabs.DataContext as TestViewModel;
MessageBox.Show(string.Format("You selected tab {0} ,previous selected tab{1}", vm.Selected, vm.PreviousSelected));
}
}
I don't know that my approach is correct or not that means hiding or showing the rectangles depending on that View Model is a good idea or not but I need some similar solution and it's may be different that means the rectangles are only trigger their animation depending on View model. But the answer is almost same. All type of suggestion are accepted.
I attach a GIF so that people understand that this effect I want to get in my TabControl on Tab Item selection. I know it can be done easily with code behind but I want to get solution in pure XAML and MVVM base.
c#
wpf
xaml
mvvm
datatrigger
0 Answers
Your Answer