Expander header text alignment - wpf

So I changed the orientation of the expander to align vertically rather than horizontally but in the closed state the expander header text is aligning horizontally. Please tell me there is a way to easily fix this without expression blend etc.
<Expander Header="My Header">
I was hoping for something like AlignHeaderText vertically but I see no options for it? Does anyone have a different way they could "show" me?
So taking from the mentoring H.B provided I came up with this:
<StackPanel Orientation="Horizontal" Margin="0,0,342,0" Width="318">
<StackPanel.Triggers>
<EventTrigger RoutedEvent="Expander.Expanded" SourceName="expander1">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="1.2" Duration="0:0:0.45" Storyboard.TargetName="content" Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleX)"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</StackPanel.Triggers>
<Expander ExpandDirection="Right" Name="expander1" OpacityMask="#6C806969" Background="#FF807171">
<Expander.LayoutTransform>
<RotateTransform Angle="90"></RotateTransform>
</Expander.LayoutTransform>
<Expander.Header>
<TextBlock
Text="HEADER"
Width="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type Expander}},
Path=ActualWidth}"
/>
</Expander.Header>
<Grid x:Name="content" Background="#FF807171" Width="178">
<Grid.LayoutTransform>
<ScaleTransform ScaleX="0" ScaleY="1"/>
</Grid.LayoutTransform>
</Grid>
</Expander>
Unfortunately it does this to the expander:
It places the text at the top and the button in the middle, I was hoping for the button at the top and the text after the button?
If I change Path=ActualWidth to Height the button moves up but the header text is still left of the button and not below it?

My solution (without another properties) - based on H.B. and Sheikh Neyamat answers:
<Expander ExpandDirection="Right">
<Expander.Header>
<TextBlock Text="Header">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Expander.Header>
// Content - listbox in my case
</Expander>

Assign a TextBlock as Header via element syntax and apply a RotateTransform as the TextBlock's LayoutTransform to get the rotation. If you want vertical text that can be done differently.

An example here supporting above suggestions.

You need to also set the ExpandDirection property and set the rotation in a header template.
Below a style that flip direction when folded
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Expander">
<Style.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ExpandDirection" Value="Right"/>
</Trigger>
</Style.Triggers>
<Setter Property="BorderBrush" Value="DarkGray"/>
<Setter Property="Margin" Value="1,0,1,1"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
</Style>
</StackPanel.Resources>
<Expander Header="Command Data">
<!-- some content -->
</Expander>
<Expander Header="Line Status">
<!-- some content -->
</Expander>
</StackPanel>

Related

How to change Icon of button with data trigger ? wpf

I am new to wpf. Actully i want to change button icon(play.ico/stop.ico).
when i click the button it changes status(status is database column) value 'Online' to Offline, or Offline to Online.
It updating in database also but
I want to show when status is online buttton should show stop.ico and when I status is offline button should show play.ico.
How to achieve this? I tried with below code but its not working. please help and suggest what i am missing something.
//Xaml code
<StackPanel Margin="0 0 0 0" Grid.Column="5" Grid.Row="0" Grid.RowSpan="2">
<Button Name="Ignition_Button1" Click="Ignition_Button1_Click_1" Width="35" Height="35" Margin="16,5,16,0"
Style="{DynamicResource CircleButton}"
Command="{Binding StartStopCommand}">
<Button.ToolTip>
<ToolTip>
<StackPanel>
<TextBlock FontWeight="Bold"
Text="Start or Stop Control Center" />
</StackPanel>
</ToolTip>
</Button.ToolTip>
<StackPanel Orientation="Vertical">
<Rectangle Width="15" Height="15" StrokeThickness="0">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="{StaticResource play.ico}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Value="Online">
<Setter Property="Fill" Value="{StaticResource stop.ico }" />
</DataTrigger>
<DataTrigger Binding="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Value="Offline">
<Setter Property="Fill" Value="{StaticResource play.ico}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</StackPanel>
</Button>
</StackPanel>
Several things I would suggest here.
Rather than ico which are bitmap and will have jaggies, I would recommend geometries and paths.
With a button which has two states you toggle between then a togglebutton is a good candidate.
Below I put my two geometries in resources of my togglebutton.
These are more usually put into a resource dictionary (along with the many other geometries used for iconography) and merged in app.xaml.
<ToggleButton IsChecked="True"
IsThreeState="False"
>
<ToggleButton.Resources>
<Geometry x:Key="StopGeom">
M0,0L32,0 32,32 0,32z
</Geometry>
<Geometry x:Key="PlayGeom">
M0,0L16,8 32,16 16,24 0,32 0,16z
</Geometry>
</ToggleButton.Resources>
<Path Fill="Black"
Stretch="Uniform"
Margin="4">
<Path.Style>
<Style TargetType="Path">
<Setter Property="Data" Value="{StaticResource PlayGeom}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type ToggleButton}}}" Value="True">
<Setter Property="Data" Value="{StaticResource StopGeom}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</ToggleButton>
This needs more work but should give you the idea of one way to do this.
For more about layout and geometries:
https://social.technet.microsoft.com/wiki/contents/articles/32610.wpf-layout-lab.aspx
I don't know how you're styling the shape of your button, but I use this one to emphasise to wpf newbies what lookless controls means:
https://social.technet.microsoft.com/wiki/contents/articles/29866.aspx

XAML expandable button/menu item on mouse over

I'm pretty much new to XAML and I'm trying to mimic following => menu
example menu (menu items I'm talking about google, facebook etc.)
Expected Behaviour:
Hoover on below Button
Should look like:
My code gives me only:
How do I achieve the expected output - make my TextBlock to appear and be part of the button?
My first approach was to use a grid and create two columns and have the second one always hidden and show only on hoover. Then I came up with belows button approach finally I found out about Expander class... Not sure whats the correct approach, below it's what I have so far, obviously far away from expected output.
<Button Name="button1" Width="170" Height="170" Cursor="Hand">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<Image Width="48" Height="48" Source="https://pbs.twimg.com/profile_images/638750728354430976/HnTYCHzN_400x400.png" />
<TextBlock Visibility="Hidden" Width="100" Height="70" VerticalAlignment="Center" HorizontalAlignment="Center" Background="pink">
MenuItem1
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=button1}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="Margin" Value="20,0,0,0" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
You can use animation to achieve this. Below is the Template for Button which uses animation to give Slide-In and Slide-Out effect on MouseEnter and MouseLeave events
<Button Name="button1" Cursor="Hand">
<Button.Template>
<ControlTemplate>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Width="48" Height="48" Source="https://pbs.twimg.com/profile_images/638750728354430976/HnTYCHzN_400x400.png" />
<TextBlock Grid.Column="1" x:Name="myTextBlock" Width="0" Height="48" VerticalAlignment="Center" HorizontalAlignment="Left" Background="pink">
MenuItem1
</TextBlock>
<TextBlock Grid.Column="1" Visibility="Hidden" Width="100" Height="48" x:Name="dummyBlock" />
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation
Storyboard.TargetName="myTextBlock"
Storyboard.TargetProperty="Width"
From="0.0"
To="100"
Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard >
<DoubleAnimation
Storyboard.TargetName="myTextBlock"
Storyboard.TargetProperty="Width"
From="100.0"
To="0"
Duration="0:0:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Here is a fair start for implementing your requirements:
1.The Style uses render transform to stretch your button.
2.The button content changes cording to a bound bool
<UserControl ...>
<UserControl.Resources>
<!--*********** Templates ***********-->
<ControlTemplate x:Key="VIEWALLTemplate">
</ControlTemplate>
<ControlTemplate x:Key="DefultTemplate">
<StackPanel Background="White" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<Image Width="48" Height="48" Source="https://pbs.twimg.com/profile_images/638750728354430976/HnTYCHzN_400x400.png" />
<TextBlock Text="MenuItem1" Visibility="Hidden" Width="100" Height="70" VerticalAlignment="Center" HorizontalAlignment="Center" Background="pink"/>
</StackPanel>
</ControlTemplate>
<!--*********** Styles ***********-->
<Style TargetType="Button">
<Setter Property="Background" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="2" ScaleY="1"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Button Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button.Content>
<ContentControl DataContext="{Binding}" Grid.Row="1">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Template" Value="{StaticResource DefultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SomeBool}" Value="true">
<Setter Property="Template" Value="{StaticResource VIEWALLTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Button.Content>
</Button>
</Grid>
</UserControl>

How to make Rotate in WPF

I beginner in wpf and I want rotate a TextBlock but I have error:"Cannot resolve all property references in the property path 'RotateTransform.Angle'. Verify that applicable objects support the properties."
<TextBlock Grid.Row="1" Grid.Column="1" RenderTransformOrigin="0.5,0.5" Style="{StaticResource Rotate}">
<TextBlock.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-16.308"/>
</TransformGroup>
</TextBlock.RenderTransform>
<TextBlock.Background>
<ImageBrush ImageSource="image/1.png"></ImageBrush>
</TextBlock.Background>
</TextBlock>
and this is my style
<Style x:Key="Rotate" TargetType="{x:Type TextBlock}">
<!--<Setter Property="Width" Value="10"></Setter>
<Setter Property="Height" Value="10"></Setter>-->
<Setter Property="Background" Value="Black"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions >
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RotateTransform.Angle" To="-360" Duration="0:0:1" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
You should be able to use the orientation of a stack panel in your xaml to rotate easily, but this does not give immediate access to the angle.
<StackPanel Orientation="Horizontal">
<Textbox ...../>
</StackPanel>
Or in your style you can add a setter property.
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="-90"></RotateTransform>
</Setter.Value>
</Setter>
If you need TransformGroup in your RenderTransorm, then StoryboardTargetProperty will be look like
RenderTransform.Children[0].Angle
If you leave only RotateTransform there, then it will be
RenderTransform.Angle
In both cases, as you see, we start searching property from RenderTransform.

How can I change the distance between text and underline in a WPF TextBlock?

I have a style guide from a designer for a button that looks like a hyperlink and I am trying to get as close to it as I can with WPF styles.
But I haven't been able to change the distance between the text and the underline.
I wanted to add images for comparision but unfortunately I haven't earned enough points to do so far.
Is there a way to change the distance between text and underline?
Here is the XAML code I have so far:
<Style x:Key="LinkButton" TargetType="ButtonBase">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ButtonBase">
<StackPanel Orientation="Horizontal">
<TextBlock Text="> "/>
<TextBlock TextDecorations="Underline">
<ContentPresenter/>
</TextBlock>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="{StaticResource LxGrayBrush}"/>
<Setter Property="FontSize" Value="12"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="{StaticResource LxGreenBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
Use element syntax to add an instance of TextDecoration to the TextBlock.TextDecorations, then you can adjust the Location or PenOffset.
<TextBlock>
<TextBlock.TextDecorations>
<TextDecoration Pen="..." Location="..."/>
</TextBlock.TextDecorations>
</TextBlock>
(You may need to set the Pen via element syntax as well)
<TextBlock >
Here is my text to be displayed
<TextBlock.TextDecorations>
<TextDecoration PenOffset="3" PenOffsetUnit="Pixel"/>
</TextBlock.TextDecorations>
</TextBlock>
Adjusting PenOffset would increase/decrease the gap between text and the line.
You can do this by adding a Separator between them or by setting the Margin.
Separator:
<StackPanel Orientation="Horizontal">
<TextBlock Text="> "/>
<Separator Width="5" Visibility="Hidden" />
<TextBlock TextDecorations="Underline">
<ContentPresenter/>
</TextBlock>
</StackPanel>
Margin:
<StackPanel Orientation="Horizontal">
<TextBlock Text="> " Margin="0,0,5,0" />
<TextBlock TextDecorations="Underline">
<ContentPresenter/>
</TextBlock>
</StackPanel>
To have a line closer to the text than "Underline" there is "Baseline". It is much less flexible than the H.B. solution but also simpler.
<TextBlock TextDecorations="Baseline" />

Select ListBoxItem if TextBox in ItemTemplate gets focus

I have added a DataTemplate to a ListBox class to bind my collection to:
<ListBox x:Name="lstEmails" Height="259" Margin="12,0,12,41" Width="276"
SelectionChanged="lstEmails_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Visibility="Hidden" Content="{Binding ID}"></Label>
<TextBox Width="200" Text="{Binding EmailAddress}"></TextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This does exactly what I want it to do. Although when I click on the TextBox, the ListBox does not automatically set the associated ListItem as Selected. I could do this in code, but I would prefer to use this as a component (no surprises there then).
Any ideas on how to achieve this?
That doesn't seem to work, it won't let me click on anything. Have I missed something. Here is my new XAML.
<UserControl.Resources>
<!--<TextBox x:Key="TB" x:Name="TextBoxInsideListBoxItemTemplate">
<TextBox.Style>-->
<Style TargetType="{x:Type TextBox}">
<Setter Property="IsHitTestVisible" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ListBoxItem}, AncestorLevel=1}}"
Value="True">
<Setter Property="IsHitTestVisible" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
<!--</TextBox.Style>
</TextBox>-->
</UserControl.Resources>
<Grid>
<ListBox x:Name="lstEmails" Height="259" Margin="12,0,12,41" Width="276" SelectionChanged="lstEmails_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--<Label Visibility="Hidden" Content="{Binding ID}"></Label>-->
<TextBox Width="220" Text="{Binding EmailAddress}" >
</TextBox>
<!--<TextBox Width="220" Text="{Binding EmailAddress}" GotFocus="TextBox_GotFocus"></TextBox>-->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Width="20" Margin="12,0,0,12" Name="btnAdd" VerticalAlignment="Bottom" Click="btnAdd_Click" Height="23" HorizontalAlignment="Left">+</Button>
<Button Width="20" HorizontalAlignment="Left" Margin="30,0,0,12" Name="btnRemove" VerticalAlignment="Bottom" Click="btnRemove_Click" Height="23">-</Button>
<Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,12" Name="btnApply" VerticalAlignment="Bottom" Width="49" Click="btnApply_Click">Apply</Button>
</Grid>
I think the click twice bit is good functionality.
You can trigger on the property IsKeyboardFocusWithin in the ItemContainerStyle and set IsSelected to true.
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="(ListBoxItem.IsSelected)">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
You could also use a Setter instead of a single frame animation but then the selection will be lost again once the focus leaves the ListBox:
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="IsSelected" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
If you have multiple instance of ListBox then you may consider using your custom listbox (by deriving it from ListBox). See the explanation here.
Or, use this hack if you have only 1 (or only small number of) such ListBox and don't want to create a separate class for that:
<TextBox x:Name="TextBoxInsideListBoxItemTemplate" ... >
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="IsHitTestVisible" Value="False" />
<Style.Triggers>
<DataTrigger
Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}, AncestorLevel=1}}"
Value="True">
<Setter Property="IsHitTestVisible" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Note that you'll have to click once again to edit text in the TextBox (which is actually cool according to me).
I had a situation where the selection of a listbox item would change its layout, so the control might have moved away from the cursor before the mouse button is released. I have found no better solution than to use a slight delay in the Storyboard if I want to keep everything in xaml.
More importantly, GotKeyboardFocus seems to work better than IsKeyboardFocusWithin for repeated selections.
<EventTrigger RoutedEvent="GotKeyboardFocus">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected">
<DiscreteBooleanKeyFrame KeyTime="00:00:00.3" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>

Resources