WPF: How to change canvas of a button on press? - c#

I am new to XAML and WPF.
On Button press I want to change the canvas drawn on the button from ic_maximize to ic_restore and switch the canvas when the button is pressed again. I am using the mahapps library. Can you please tell me how to go about doing this?
I have tried a lot of different StackOverflow links, but none of them are pertinent to my problem.
Here is the style for my maximize button. I have the "IsPressed" triggers ready but not able to figure out what need to be set when it is triggered.-
<Canvas x:Key="ic_maximize" Width="13.3333" Height="13.3333" Canvas.Left="0" Canvas.Top="0">
<Rectangle Width="11.7333" Height="11.7333" Canvas.Left="2.136" Canvas.Top="-0.536002" Stretch="Fill" StrokeThickness="1.06667" StrokeLineJoin="Round" Stroke="#FF999999"/>
<Rectangle Width="11.7333" Height="11.7333" Canvas.Left="5.36442e-007" Canvas.Top="1.6" Stretch="Fill" StrokeThickness="1.06667" StrokeLineJoin="Round" Stroke="#FF999999" Fill="#FF161616"/>
</Canvas>
<Canvas x:Key="ic_restore" Width="12" Height="12" Canvas.Left="0" Canvas.Top="0">
<Rectangle Width="11.7333" Height="11.7333" Canvas.Left="5.36442e-007" Canvas.Top="0.266666" Stretch="Fill" StrokeThickness="1.06667" StrokeLineJoin="Round" Stroke="#FF999999"/>
</Canvas>
<Style x:Key="ExtendedMaxButtonStyle"
TargetType="{x:Type Button}"
BasedOn="{StaticResource MetroWindowButtonStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Name="grid" Background="{StaticResource MaxButton.Grid.background}">
<!-- either one of the ic_maximize/ic_restore canvases should come here -->
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="grid" Property="Background" Value="{StaticResource MaxButton.MouseOver.Background}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<!-- What should I write here?-->
</Trigger>
<Trigger Property="IsPressed" Value="False">
<!-- What should I write here?-->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Place both Canvas on top of each other, and hide one upon IsPressed.
<Grid Name="grid">
<Canvas Background="Purple">
<Canvas.Style>
<Style TargetType="Canvas">
<Style.Triggers>
<DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
<TextBlock Text="ic_max" FontSize="48"/>
</Canvas>
<Canvas Background="Green">
<Canvas.Style>
<Style TargetType="Canvas">
<Style.Triggers>
<DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
<TextBlock Text="ic_restore" FontSize="48"/>
</Canvas>
</Grid>

Related

How to trigger parent element using child?

What I am trying to do is when my mouse got over a button inside a border that border background color must change.
<Border Name="HistoryBorder"
Grid.Column="0"
Height="30"
VerticalAlignment="Top"
Margin="0,0,326.8,0"
Background="#FF323030"
CornerRadius="0,0,15,0"
MouseDown="draging_bar">
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="75"
Name="HistoryButton"
Height="30"
Margin="0"
FontFamily="/Fonts/#Minecraft"
Foreground="#FFB6B002" >
<Button.Content>
History
</Button.Content>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=HistoryButton, Path=IsMouseOver}" Value="True">
<DataTrigger.Setters>
<Setter Property="Background" Value="Green" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Border>
As you can see border is parent and button is a child in it. When I try to use targetname it is not working. Is something like that even possible ?
As far as I understand, setting Style for Border to be bind with it's child's IsMouseOver can be one of the ways.
<Border Name="HistoryBorder"
Grid.Column="0"
Height="30"
VerticalAlignment="Top"
Margin="0,0,326.8,0"
CornerRadius="0,0,15,0"
>
<!-- You have to put Background color to Style. Or priority makes it cannot be overrided from the Setter of Style -->
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="#FF323030"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=HistoryButton, Path=IsMouseOver}" Value="True">
<DataTrigger.Setters>
<Setter Property="Background" Value="Black"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
...

Custom Control Background Color in WPF

I have created a custom control as you see below (Generic.xaml):
<Style TargetType="{x:Type local:MyCC}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCC}">
<Grid Name="grd" Height="{Binding Height}" Width="{Binding Width}">
<Rectangle Name="FirstRec" Fill="Blue"/>
<Rectangle Name="SecondRec" Fill="Black" Margin="1"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property = "IsMouseOver" Value = "True">
<Setter Property = "Background" Value = "Red" />
</Trigger>
</Style.Triggers>
</Style>
For FirstRec I want to get the Foreground color of the CC (instead of blue) and for the SecondRec I need to get the Background (instead of black). Therefore now the trigger does not work properly! I also don't want to bind the Height and Width of the Grid because the CC has its own height and width but I don't know how to use it! Would you please help me?!
EDIT:
It is actually a switch that has a status. if status == 0 it shows an empty rectangle. if status == 1 it shows a filled rectangle. if status == 3 || status == 4 it shows a red cross on it. here is the cc:
<Style TargetType="{x:Type local:BreakerCC}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:BreakerCC}">
<Grid Name="grd" Background="{TemplateBinding Background}" >
<Rectangle Name="MainRectangle" StrokeThickness="1" Stroke="{TemplateBinding Foreground}"/>
<Path Name="Line1" Stroke="Red" StrokeThickness="1" Stretch="Fill">
<Path.Data>
<LineGeometry StartPoint="0,0" EndPoint="1,1" />
</Path.Data>
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Status}" Value="0x00">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Status}" Value="0x01">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Status}" Value="0x02">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Status}" Value="0x03">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
<Path Name="Line2" Stroke="Red" StrokeThickness="1" Stretch="Fill">
<Path.Data>
<LineGeometry StartPoint="0,1" EndPoint="1,0" />
</Path.Data>
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Status}" Value="0x00">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Status}" Value="0x01">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Status}" Value="0x02">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Status}" Value="0x03">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And this is the definition in MainWindow.xaml
<Window.Resources>
<Style x:Key="230KV" TargetType="Control">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style x:Key="132KV" TargetType="Control">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground" Value="Green"/>
</Style>
<Style x:Key="400KV" TargetType="Control">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground" Value="Purple"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<CC:BreakerCC Status="{Binding Status}" Style="{StaticResource 132KV}" Height="20" Width="20"/>
</StackPanel>
</Grid>
But when the status is 3 or 4 i need to change the Foreground to Red and that doesn't work!
Use TemplateBindings for the Brushes. Binding Width and Height is not at all necessary.
<Style TargetType="local:MyCC">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyCC">
<Grid>
<Rectangle Fill="{TemplateBinding Foreground}"/>
<Rectangle Fill="{TemplateBinding Background}" Margin="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
As a note, instead of a Grid you would usually have a Border element and assign TemplateBindings to its Background, BorderBrush and BorderThickness properties - as done by default by Visual Studio when you create a new custom control.
EDIT: It seems that you do not need a custom control at all. Just create ContentControl styles like these:
<Window.Resources>
<Style TargetType="ContentControl" x:Key="BaseStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Path x:Name="path" Stroke="{TemplateBinding Foreground}"
Data="{TemplateBinding Content}" Stretch="Fill"/>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Status}" Value="0">
<Setter TargetName="path"
Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Status}" Value="1">
<Setter TargetName="path"
Property="Visibility" Value="Hidden"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Black"/>
<Setter Property="BorderBrush" Value="{Binding Foreground,
RelativeSource={RelativeSource Self}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Width" Value="20"/>
<Setter Property="Height" Value="20"/>
</Style>
<Style x:Key="230KV" TargetType="ContentControl" BasedOn="{StaticResource BaseStyle}">
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style x:Key="132KV" TargetType="ContentControl" BasedOn="{StaticResource BaseStyle}">
<Setter Property="Foreground" Value="Green"/>
</Style>
<Style x:Key="400KV" TargetType="ContentControl" BasedOn="{StaticResource BaseStyle}">
<Setter Property="Foreground" Value="Purple"/>
</Style>
</Window.Resources>
<StackPanel>
<ContentControl Style="{StaticResource 230KV}">
<PathGeometry>M0,0 L1,1 M0,1 L1,0</PathGeometry>
</ContentControl>
<ContentControl Style="{StaticResource 132KV}">
<PathGeometry>M0,0 L1,1 M0,1 L1,0</PathGeometry>
</ContentControl>
<ContentControl Style="{StaticResource 400KV}">
<PathGeometry>M0,0 L1,1 M0,1 L1,0</PathGeometry>
</ContentControl>
</StackPanel>

Change the color of elements inside a Grid once a mouse hovers the Grid

I'm working on a WPF application. I have a button, which contains a Grid with a TextBlock and a Path element, as following:
<Button x:Name="btn" Margin="1">
<Button.Content>
<Grid Style="{DynamicResource button_special_hover}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="15*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="tx" TextWrapping="Wrap" Text="{DynamicResource MenuString}" Grid.Column="2" />
<Path x:Name="path" Stretch="Fill" Fill="{DynamicResource MarkerBrush}" Data="M 65.3334,41.3334C 65.3334,45.0133 62.3467,48 58.6667" Grid.Column="0" />
</Grid>
</Button.Content>
What I want to achieve is that once I hover the grid inside the button with my mouse, I want the foreground of the text inside the textblock to turn into white and the fill color for the path element to turn into white too.
What I've tried to do is declare a style for the grid as following: (button_special_hover)
<Style x:Key="button_special_hover" TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Transparent"/>
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Path}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Fill" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
I don't get what I want. Once I hover over the grid, nothing happens. Only when I hover the Textblock inside the grid, the text turns into White. Once I hover the Path, nothing happens.
How can I achieve that once I hover the grid both the textblock foreground and the Path Fill turn into White? Thanks!
Your problem is that you specified the fill on the path element.
NOTE: I 'fixed' the Data by added ",41.3334" to it.
<Path x:Name="path" Stretch="Fill" Fill="{DynamicResource MarkerBrush}" Data="M 65.3334,41.3334C 65.3334,45.0133 62.3467,48 58.6667,41.3334" Grid.Column="0" />
When you do that, you can't override it. If you specified it in the style instead then you'd get the results you want.
Remove Fill from Path:
<Path x:Name="path" Stretch="Fill" Data="M 65.3334,41.3334C 65.3334,45.0133 62.3467,48 58.6667,41.3334" Grid.Column="0" />
and add Fill to the style:
<Style x:Key="button_special_hover" TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Transparent"/>
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Path}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid},
Path=IsMouseOver}" Value="True">
<Setter Property="Fill" Value="White"/>
</DataTrigger>
</Style.Triggers>
<!-- "All I did was add this line below" -->
<Setter Property="Fill" Value="{DynamicResource MarkerBrush}"/>
</Style>
</Style.Resources>
</Style>

Setter for elements inside ContentPresenter

I have a ControlTemplate like this :
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="20" Color="Black" ShadowDepth="0"
Opacity="0.5" />
</Setter.Value>
</Setter>
<Setter Property="FontWeight" Value="SemiBold"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="20" Color="Black" ShadowDepth="0" Opacity="1" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
When IsMouseOver property changes to True, I want all the images inside the contentPresenter to have the DropShadowEffect, not the button itself.
I tried changing the setter property to Image.Effect but it doesn't work.
How can I do it?
This is how my button looks like when Mouse is over:
And this is how it looks when it is not:
As you can see, both the Image and TextBlock is getting dropshadow effect. But I want only the Image to get it.
You can take the style recommended by Sinatr and put it in the ControlTemplate.Resources section of your button's control template. Here's an example that worked for me. It also handles the IsPressed event, during which I assume you want adjust the drop shadow effect for just the image:
<ControlTemplate x:Key="BtnTemplate" TargetType="{x:Type Button}">
<Grid Background="Transparent">
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="FontWeight" Value="SemiBold"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
<ControlTemplate.Resources>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=Button}}" Value="True">
<DataTrigger.Setters>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="20" Color="Black" ShadowDepth="0" Opacity="0.5"/>
</Setter.Value>
</Setter>
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource AncestorType=Button}}" Value="True">
<DataTrigger.Setters>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="20" Color="Black" ShadowDepth="0" Opacity="1" />
</Setter.Value>
</Setter>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ControlTemplate.Resources>
</ControlTemplate>
You don't really need to define Button template, rather create an Image style
<Style x:Key="style"
TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=Button}}"
Value="True">
<DataTrigger.Setters>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="20"
Color="Black"
ShadowDepth="0"
Opacity="0.5" />
</Setter.Value>
</Setter>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
and apply it to each image you show inside button
<Button>
...
<Image Style="{StaticResource style}" ... />
...
</Button>

Custom Button doesn't show its content properly

Okay, I made a Custom Button in my .xaml file in my WPF Project, I've done everything, but the Content isn't showing... The C# code is default, nothing changed, but here's my .xaml code:
<Button Name="TestButton" Content="TESTING" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="197,158,0,0" Height="30" Width="120">
<Button.Template>
<ControlTemplate>
<Image Height="30" Width="120" Stretch="Fill">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="/Resources/btn_primary_normal.png"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="/Resources/btn_primary_hover.png"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Source" Value="/Resources/btn_primary_disabled.png"/>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</ControlTemplate>
</Button.Template>
</Button>
When you replace the ControlTemplate of a control you replace all of its visual functionality, including the part that displays the Content property. If you want a button that shows an image and whatever is in the Content property you are pretty close, just add a ContentPresenter into your template like so:
<Button Name="TestButton" Content="TESTING" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="197,158,0,0" Height="30" Width="120">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Image Height="30" Width="120" Stretch="Fill">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="/Resources/btn_primary_normal.png"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="/Resources/btn_primary_hover.png"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Source" Value="/Resources/btn_primary_disabled.png"/>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
Also note the specification of the TargetType in the ControlTemplate, this is important.

Categories

Resources