How to hide parent control while showing child content? - wpf

How can I hide a parent TabControl until the child in one of its tabs is clicked on? Obviously, I need the child to be visible for the user to be able to click on it. The only thing that I have managed to come up with so far is a bit of a hack... I'm displaying an extra child item over the top of the TabControl and then hiding it and showing the TabControl upon clicking. Here is my hack:
XAML:
<Window x:Class="WpfApp1.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"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="600"
PreviewMouseLeftButtonUp="Grid_PreviewMouseLeftButtonUp">
<Window.Resources>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Width" Value="300" />
<Setter Property="Height" Value="250" />
</Style>
</Window.Resources>
<Grid>
<TabControl Name="TabControl" Width="350" Height="300">
<TabItem Header="Original">
<Rectangle Fill="Red" />
</TabItem>
<TabItem Header="Modified">
<Rectangle Fill="Blue" />
</TabItem>
<TabControl.Style>
<Style TargetType="{x:Type TabControl}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsTabControlVisible}"
Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
</TabControl>
<Rectangle Fill="Red" Margin="0,22,0,0"
PreviewMouseLeftButtonUp="Rectangle_PreviewMouseLeftButtonUp">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}"
BasedOn="{StaticResource {x:Type Rectangle}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsTabControlVisible}"
Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
</Window>
Code behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty IsTabControlVisibleProperty =
DependencyProperty.Register(nameof(IsTabControlVisible), typeof(bool),
typeof(MainWindow), null);
public bool IsTabControlVisible
{
get { return (bool)GetValue(IsTabControlVisibleProperty); }
set { SetValue(IsTabControlVisibleProperty, value); }
}
private void Rectangle_PreviewMouseLeftButtonUp(object sender,
MouseButtonEventArgs e)
{
IsTabControlVisible = true;
}
private void Grid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!TabControl.IsMouseOver) IsTabControlVisible = false;
}
}
}
I've changed the content to plain Rectangles for simplicity.
How can I improve this situation? I don't like the idea of duplicating the child content to make this work. Does anyone have any better solutions?

You could, as #XAMIMAX mentioned, hide the TabControl but not by its Opacity (because setting it to 0 would hide the child too). The following XAMLis just a quick & dirty proof of concept by hiding all the Elements except the Child:
<TabControl Name="TabControl" Width="350" Height="300" BorderBrush="Transparent">
<!-- Content of the Tabcontrol -->
<TabItem Header="Original">
<Rectangle Fill="Red" />
</TabItem>
<TabItem Header="Modified" >
<Rectangle Fill="Blue" />
</TabItem>
<!-- Triggers for the TabControl -->
<TabControl.Triggers>
<!-- Set Borderbrush to Black when tab is clicked -->
<EventTrigger RoutedEvent="MouseDown">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(BorderBrush).(SolidColorBrush.Color)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Colors.Black}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<!-- Reset BorderBrush when Mouse leaves the TabControl (choose wathever condition you like to hide the tabs) -->
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(BorderBrush).(SolidColorBrush.Color)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Colors.Transparent}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</TabControl.Triggers>
<!-- Set "Style" of Container -->
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<!-- Hide Header -->
<Setter Property="Visibility" Value="Hidden"/>
<!-- Show header when Border of parent is Black (you can choose a different Property if you like) -->
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabControl}}, Path=(BorderBrush).(SolidColorBrush.Color)}" Value="Black">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
The Tabcontrol has 2 Triggers: Setting the BorderBrush to Black when someone clicks on the TabControl and setting it to Transparent when the MousePointer leaves the TabControl. Theese two handle the visibility for the TabControl itself. Note: Add additional Triggers if your Background is different from the Color behind the TabControl.
The TabItem has a Trigger bound to the Color of the TabControl Border. If the Color is black, show the Header of the TabItem otherwise, hide them.

Related

DoubleAnimation exception when animating grid width with DataTrigger

I'm trying to animate a grid's width upon a toggle button being checked. I've used a data trigger in the Grid's style element to do this, however, whenever it's triggered I get an error, stating:
Cannot animate the 'Width' property on a
'System.Windows.Controls.Grid' using a
'System.Windows.Media.Animation.DoubleAnimation'
I'm pretty sure it is possible to animate the grid in the way I'm attempting, as I've gotten it to work using a click event. Does anyone see where I'm going wrong here?
<Grid Margin="0" HorizontalAlignment="Left"
TextOptions.TextFormattingMode="Display" x:Name="MainGrid" >
<Grid.Resources>
<Style TargetType="Grid">
<Style.Setters>
<Setter Property="Width" Value="300"/>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=CollapseIcon}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard >
<Storyboard >
<DoubleAnimation Duration="0:0:0.200" Storyboard.TargetProperty="Width" To="16" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
I apologize man, I completely forgot to get back to this Friday and then saw your question again today. Anyway you're so close! Move your dependency property for Width to the object and you're all done! This tested just fine on my end. Cheers!
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid Margin="0" HorizontalAlignment="Left" Width="300" Background="Red"
TextOptions.TextFormattingMode="Display" x:Name="MainGrid" >
<Grid.Resources>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=CollapseIcon}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard >
<Storyboard >
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetProperty="Width"
From="300" To="16" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
</Grid>
<ToggleButton x:Name="CollapseIcon" Content="Magic!" Height="50" Width="50"/>
</Grid>
</Window>
ADDENDUM: Forgot in a DoubleAnim you need to be explicit with both To AND FROM or it just returns NaN in the From by default. Cheers!

WPF ListItem Template - Button to Fade In

I have a ListBox with an ItemTemplate (shown below) that when the mouse is over an item, a button is displayed that will fire a Delete Command.
This works, but what I'd like is for the button to "fade in" after the mouse has been over the listitem for a couple of seconds. How can I achieve this?
<ListBox.ItemTemplate>
<DataTemplate d:DesignSource="{d:DesignInstance quizCompanion:Question}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"></ColumnDefinition>
<ColumnDefinition Width="16"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Number}"></TextBlock>
<Button
Content="x" Grid.Column="1"
Command=MyDeleteCommand>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Hidden"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsMouseOver}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
Try using a DataTrigger with a RelativeSource for the Binding.
Here's a sample ... mouse over anywhere on the StackPanel for 2 seconds or more and the hidden button will fade in. It'll disappear when the mouse is moved off. Hopefully it'll work within your ListBox ItemTemplate:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<StackPanel Height="100" Background="Yellow">
<TextBlock Text="Mouse over the yellow area to see the button"/>
<Button Width="250" Height="50" HorizontalAlignment="Left" Opacity="0">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="Fade">
<Storyboard>
<DoubleAnimationUsingKeyFrames Duration="0:0:3" Storyboard.TargetProperty="Opacity">
<LinearDoubleKeyFrame KeyTime="0:0:2" Value="0"/>
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="Fade"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Grid>
</Page>

How to stop the treeview collaps event after mouse click

How can I stop the treeview collaps event after mouse click on TreeViewItem arrow (on view)?
I need to show my treeview expanded all time.
You could set the Collapsed event in XAML:
<TreeView
Name="myTreeView"
ItemsSource="{Binding dataSource}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
<Style.Triggers>
<EventTrigger RoutedEvent="Collapsed">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Duration="0"
Storyboard.TargetProperty="(TreeViewItem.IsExpanded)">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
You can set the Collapsed event on the TreeViewItem to this:
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
{
(sender as TreeViewItem).IsExpanded = true;
}
It doesn't prevent it from collapsing however, it just automatically expands it whenever it's collapsed.
Just retemplate the TreeViewItems to not even have an arrow (and collapsible area).
e.g.
<Style TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<StackPanel>
<ContentPresenter ContentSource="Header"/>
<ItemsPresenter Margin="20,0,0,0"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
(This is the bare minimum, you will want to have triggers to show the current selection if you need that)

XAML styles - Nested tabcontrols triggering eachother's animations

I've invented a metro-style tab control which is built to contain two levels of tabs just like the Zune media library does; one large and one smaller below that. When you change tab for the first level, it works fine and animates correctly. Each of the two tabs contains another TabControl using the same template, but when you change the tabs there even the tabstrip animates; like the whole ContentPresenter of the container TabControl animates rather than the ContentPresenter of the child TabControl. If that makes sense :P
Here's the style:
<Style x:Key="MetroTabControl" TargetType="{x:Type TabControl}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Background" Value="White" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<ControlTemplate.Resources>
<Storyboard x:Key="TabSelectionChangedStoryboard">
<DoubleAnimation Storyboard.TargetName="TabControlContent"
Storyboard.TargetProperty="Opacity"
To="100"
From="0"
FillBehavior="HoldEnd"
Duration="0:0:45.0" />
<ThicknessAnimation Storyboard.TargetName="TabControlContent"
Storyboard.TargetProperty="Margin"
From="0,25,0,-25"
To="0,0,0,0"
FillBehavior="HoldEnd"
Duration="0:0:0.3">
</ThicknessAnimation>
</Storyboard>
</ControlTemplate.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border>
<TabPanel
IsItemsHost="True">
</TabPanel>
</Border>
<Border x:Name="BorderPresenter" BorderThickness="0"
Grid.Row="1"
BorderBrush="White"
Background="White">
<ContentPresenter x:Name="TabControlContent" ContentSource="SelectedContent" Margin="0" >
</ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="SelectionChanged">
<BeginStoryboard Storyboard="{StaticResource TabSelectionChangedStoryboard}" />
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
That happens because SelectionChanged event bubbles up to parent TabControl and triggers its animation. As one possible easy solution you can add SelectionChanged event handler to parent TabControl and check if it is the original source for this event:
<TabControl SelectionChanged="RootTabControl_SelectionChanged">
<TabItem>
<TabControl>
<!-- TabItems here -->
</TabControl>
</TabItem>
</TabControl>
And here`s the code:
private void RootTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender != e.OriginalSource)
e.Handled = true;
}

WPF: Creating a ListView with expanding ListItems

So I want a list of items that when you select them they expand to show more info (no toggleButton).
I figure there are multiple ways to do this but what I started at was that I had a ListView bound to the collection of viewModels and then defined the ViewModel view as Expander. Problem here was binding the selected one to be expanded.
I started getting multiple ideas on how this could be done differently. Perhaps modding the ControlTemplate of the ListView to have it's items set as my own type of expander. But I'm not sure how well that works when the ItemsSource is set for the list.
Problem is I'm not too sure what the best way here is.
You can easily select the DataTemplate of the selected ListViewItem by setting ListView.ItemContainerStyle and using appropriate triggers.
Here's an example of how you can not only change the visual tree of the selected item, but also animate its properties at the same time as well.
<ListView ItemsSource="{Binding ...}">
<ListView.Resources>
<!-- this is what unselected items will look like -->
<DataTemplate x:Key="DefaultItemTemplate">
<TextBlock FontSize="12" Margin="0,0,10,0" Text="Unselected" />
</DataTemplate>
<DataTemplate x:Key="SelectedItemTemplate">
<Border BorderBrush="Red" BorderThickness="2" Padding="5">
<TextBlock FontSize="12" Margin="0,0,10,0" Text="Selected" />
</Border>
</DataTemplate>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<!-- set properties for all items -->
<Setter Property="Margin" Value="0,2,0,2" />
<Setter Property="Padding" Value="0,2,0,2" />
<Setter Property="ContentTemplate" Value="{StaticResource DefaultItemTemplate}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<!-- change what the selected item looks like -->
<Setter Property="ContentTemplate" Value="{StaticResource SelectedItemTemplate}" />
<!-- animate it as well -->
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="MinHeight" To="80" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="MinHeight" To="0" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>

Resources