Editing a MahApps Metro Circle Toggle button - wpf

I know there are a few questions surrounding the MahApps Metro styles, but i haven't found one that address the issue I am having.
I have an app that I am maintaining, a lot of which I helped build, using a central style XAML repository.
A part that I didn't build uses the style for the Metro Circle Toggle Button from MahApps. According to my Stakeholders, I need to change the selected state to be more contrasting from the normal state of the button. However I haven't been able to find where to go to access that style in my application.
My gut instinct is to create a complete style in my repository that replaces the Metro Style, but I figured I would ask around to see if anyone here could help me.
Any Hints wold be greatly appreciated.
Update 1:
I tried to use the BasedOn property to keep the amount of code down. I then set the background color to switch from black to white when "IsChecked" is True.
here is the code i added:
<Style x:Key="CustomCircleToggleButtonStyle"
TargetType="{x:Type ToggleButton}"
BasedOn="{StaticResource MetroCircleToggleButtonStyle}">
<Setter Property="Background" Value="{StaticResource DarkBorder}"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True" >
<Setter Property="Background" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
Unfortunately, there is some animation form the MahApps sinking through that makes the button go from the black, immediately to white then fade to the dark blue color that i am trying to get rid of.
Here is the wpf toggle button:
<ToggleButton Width="50" IsEnabled="{Binding IsMultipleSelected,Converter= {StaticResource BooleanNegate}}"
Height="50"
Style="{DynamicResource CustomCircleToggleButtonStyle}"
Command="{Binding Path=GroupSelectCommand}"
IsChecked="{Binding IsLasoSelected}">
<Rectangle Width="20"
Height="20"
Fill="{DynamicResource IconButtonActiveBorder}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill"
Visual="{DynamicResource appbar_lasso}" />
</Rectangle.OpacityMask>
</Rectangle>
</ToggleButton>
I am hoping to move a lot of this into the style, but I have to keep functional.

Right, so the reason it's not working with your example is you can't overwrite triggers that interact with ControlTemplate via Triggers from the Style....
So, if we go take a peek at line 615 here (which btw, I assume there's a file in your metro stuff named the same) we see inside the template the ControlTemplate.Triggers which on IsChecked is changing the opacity of ellipsebg ellipse. It in turn is hard set to fill of {DynamicResource AccentColorBrush}...
So...you could overwrite that brush at the instance level....or, just go edit the full style template to use a different brush all together.
In other words you could go change line 630 of Fill="{DynamicResource AccentColorBrush}" to another brush or brush resource and it would inherit to each instance (that would be my choice to keep centrally maintainable properties).
Or, at the instance something like:
<StackPanel>
<StackPanel.Resources>
<SolidColorBrush x:Key="AccentColorBrush">Red</SolidColorBrush>
<Style TargetType="ToggleButton"
BasedOn="{DynamicResource CustomCircleToggleButtonStyle}"/>
</StackPanel.Resources>
<ToggleButton/>
<ToggleButton/>
<ToggleButton/>
</StackPanel>
Make sense? :)

To close the loop, and post my answer for everyone else with the issue, I ended up creating a style that i put in my style repository. I think my Solution might be a little unique though as i don't actually have access the the MahApps.Metro source code in my solution.
Here is what I did:
<Style x:Key="CircleButton" TargetType="ToggleButton">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource MyFocusVisual}"/>
<Setter Property="Margin" Value="2 2 2 2"/>
<Setter Property="Width" Value="45"/>
<Setter Property="Height" Value="45"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Ellipse x:Name="BorderCircle">
<Ellipse.Fill>
<SolidColorBrush Color="SlateGray"/>
</Ellipse.Fill>
</Ellipse>
<Ellipse x:Name="BodyCircle" Margin="3" >
<Ellipse.Fill >
<SolidColorBrush Color="Black"/>
</Ellipse.Fill>
</Ellipse>
<Rectangle x:Name="Mask"
Width="20"
Height="20"
Fill="{DynamicResource IconButtonActiveBorder}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill"
Visual="{DynamicResource appbar_lasso}" />
</Rectangle.OpacityMask>
</Rectangle>
<ContentPresenter x:Name="content" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="BodyCircle" Property="Fill" Value="White"/>
<Setter TargetName="BorderCircle" Property="Fill" Value="Black"/>
<Setter TargetName="Mask" Property="Fill" Value="Black"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="BodyCircle" Property="Fill" Value="DarkOrange"/>
<Setter TargetName="BorderCircle" Property="Fill" Value="Black"/>
<Setter TargetName="Mask" Property="Fill" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This gives me total control of all aspects of the toggle button and the identical look as it had before. All i have to do to implement it is call it from the WPF view:
<ToggleButton IsEnabled="{Binding IsMultipleSelected,Converter={StaticResource BooleanNegate}}"
Style="{DynamicResource CircleButton}"
Command="{Binding Path=GroupSelectCommand}"
IsChecked="{Binding IsLasoSelected}">
</ToggleButton>
Hope this helps others out.

Related

How to build a button style based on another in WPF

I'm new to WPF and by following a quick tutorial I succeeded on getting a personalized button style as the following one:
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="2" StrokeThickness="1" Stroke="#60000000" StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="NormalBrush" Color="#FF0C0C13"/>
<SolidColorBrush x:Key="LightBrush" Color="#FF2E2E3E"/>
<SolidColorBrush x:Key="PressedBrush" Color="#FF209FD4"/>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#FF494968" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#FF2E2E3E" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<SolidColorBrush x:Key="HorizontalNormalBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="HorizontalLightBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="DarkBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="NormalBorderBrush" Color="Aqua"/>
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border" BorderThickness="1" Background="{StaticResource NormalBrush}" BorderBrush="#FF2E2E3E">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource LightBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It seems indeed that we need to override the ControlTemplate and that to keep some functionalities (like the behaviour while hovering the mouse) we need to define them. What is already strange to me here, is that under ControlTemplate it is defined a Border tag and then the trigger refer to that Border. So as a first question, why Border and not something else?
But now the main question is: suppose I want exactly the same button, with the same colors and functionalities but without the borders. I tried to use the BasedOn similar to the following:
<Style x:Key="MyButtonStyleNoBorder" TargetType="{x:Type Button}" BasedOn="{StaticResource MyButtonStyle}">
<Setter Property="BorderThickness" Value="0"/>
</Style>
but no way. The only solution I found is to copy the entire code of MyButtonStyle and then to change only one character (!) to have BorderThickness="0". But this to me looks stupid. Can you please help?
So as a first question, why Border and not something else?
The element named Border is the outermost element and its child will inherit most of its state, in this case the background.
But now the main question is: suppose I want exactly the same button, with the same colors and functionalities but without the borders.
If you like a button without borders you can just set the BorderThickness property of the button to 0. directly or as a setter in the style.
The only solution I found is to copy the entire code of MyButtonStyle and then to change only one character (!) to have BorderThickness="0". But this to me looks stupid. Can you please help?
Styles are kind of a list for what property to change on a target. The basedOn functionality will use the basedOn-style and "add" the new setter from the new style.
A Template is more like a drawing (and some graphical behavior) of the control and when you specify a new one you just throw away the old one. I would be possible to do a basedOn there. How would we determine what to use and what to replace?
Not the answer you wished for but hopefully you got it anyway.
<Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" Background="{StaticResource NormalBrush}" BorderBrush="#FF2E2E3E">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
Will pick up the value the Button object gets on BorderThickness.
I would have done this using MVVM (if you have not considering MVVM yet).
In my ViewModel bound to the Button, I would add a property "IsBorderLess".
Then, on the Triggers:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsBorderLess}" Value="True">
<Setter TargetName="Border" Property="BorderThickness" Value="0"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsBorderLess}" Value="False">
<Setter TargetName="Border" Property="BorderThickness" Value="10"/>
</DataTrigger>
..........

How do I create an button that changes the source image AND the color of the text in a StackPanel in WPF?

In my WPF web app I have an image and a textblock inside of a stack panel, making a button. I have it set up so that if I mouseOver the image, it changes the source of the image so that another image appears(same image, but grayed-out). Also, if you mouseOver the textblock the text color changes from black to gray. How do I get both of these things to happen at once if the mouse rolls over either, or over the StackPanel that houses the items? My code:
<StackPanel x:Name="IntegrationRequestLinkStackPanel">
<Button x:Name="IntegrationRequestLinkButton" Content="Integration Request">
<Button.Template>
<ControlTemplate>
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Propery="Source" Value+"Images/requestNew.png" />
<Setter Property="Cursor" Value="Hand" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="Images"requestNewBW.png" />
</Trigger>
</Style.Trigger>
</Style>
</Image.Style>
</Image>
</ControlTemplate>
</Button>
</Button>
<Textblock Text="Integration Request"............
The code for the Textblock is pretty much the same as for the button. Really I want the whole stack panel to be the button and when mouseOver the image inside changes and the text changes color.
The way you describe it, you want a button with an image and label as its content. You can place both in the template.
<Button x:Name="IntegrationRequestLinkButton"
Content="Integration Request">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Foreground" Value="Black" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<DockPanel LastChildFill="True">
<Image x:Name="Icon"
DockPanel.Dock="Left"
Stretch="None"
Margin="0,0,5,0"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Source="Images/requestNew.png" />
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Icon"
Property="Source"
Value="Images/requestNewBW.png" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Gray" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Typically, one adjusts inherited properties which apply to the entire control (like Foreground) using triggers on the Style as opposed to the Template. Use template triggers when you need to change a specific element defined by your template.
As a matter of style, it seems odd that you would want to show black text and a color image by default, and gray text and a grayscale image on mouse-over. I would expect you to want the opposite.
First off, I'm not sure what you mean by "WPF web app". If you mean Silverlight, I have only tested this code in a desktop application, so your results may differ.
If you want the whole thing (image + text) to be a button, I would say that the best thing is to make the template of the button the StackPanel, rather than the other way around. Here is some code:
<Button>
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="Transparent">
<StackPanel>
<Image x:Name="buttonImage"
Width="30" Height="30"
Source="Images/requestNew.png"></Image>
<TextBlock x:Name="buttonText"
Text="Integration Request"></TextBlock>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="buttonImage"
Property="Source" Value="Images/requestNewBW.png"></Setter>
<Setter TargetName="buttonText"
Property="Foreground" Value="Red"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
So, this is a button with a template containing a StackPanel with a image and a text block which change when you mouse over any part of the button
You'll notice that I added a grid with a transparent background. You can remove it, what it does is it makes the whole rectangular area of the button trigger the change of the IsMouseOver property (if it wasn't there, it would only trigger when the mouse is over the image or the text itself).

wpf xaml checkbox template in style

first post so please go easy.
I am trying to get the value of a checkbox from another page (this is part of a navigation form in WPF project), but it always resets the value to false as soon as the page is changed. How do I get the IsChecked value to bind properly? I am learning just starting to work with WPF so the template is very basic, if there is a better way to do this then I am open to it.
Also the code behind page is vb.net not c# if that is going to play a part.
the style is called as follows:
<CheckBox Content="Photo" x:Name="OT_Photo" Grid.Row="0" Grid.Column="0" Style="{StaticResource ElementCheckbox}" />
There are several hundred checkboxes across these pages so I would very much like to avoid variables for each one.
The style is in 'Application.xaml'
<Style TargetType="CheckBox" x:Key="ElementCheckbox">
<Setter Property="IsThreeState" Value="True" />
<Setter Property="IsChecked" Value="{Binding IsChecked ,Mode=TwoWay}" />
<Setter Property="Foreground" Value="Navy" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Margin" Value="15,4,0,4" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="MaxWidth" Value="500" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<DockPanel DockPanel.Dock="Left">
<Border x:Name="Border" CornerRadius="0" VerticalAlignment="Top">
<Image Height="20" Width="20" Name="CheckMark" />
</Border>
<TextBlock x:Name="CheckText" Text="{TemplateBinding Content}" Margin="5,0,0,0" FontSize="{TemplateBinding FontSize}" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Top" />
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter TargetName="CheckMark" Property="Source" Value="CheckBoxNull.png" />
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="CheckMark" Property="Source" Value="CheckBoxCross.png" />
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="CheckMark" Property="Source" Value="CheckBoxTick.png" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Effect" TargetName="Border">
<Setter.Value>
<DropShadowEffect ShadowDepth="1" Color="Blue" Opacity="1" BlurRadius="2"/>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Blue" TargetName="CheckText" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanks in advance of any help!
I should add I have been stuck on this for over a day, I have tried template binding, binding paths, and an on click procedure based on the c# answers I have found already. always resulting in the checkbox being reset though.
Solved
My problem was unrelated to the checkbox as it happened, the page was not named statically so the properties always served the default values. (rookie mistake - much shame)
on the up side the code above works perfectly (in its limited way) if anyone wants to pinch it!
VB.NET:
Private Sub MainForm_Loaded(sender As Object, e As RoutedEventArgs) Handles MainForm.Loaded
Me.NavigationService.Navigate(ContactDetailsPage)
End Sub
instead of XAML stating the source:
<NavigationWindow x:Name="MainForm" x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="" Source="ContactDetails.xaml">
I think the problem is that you define your binding in your template instead in your control. You usually don't do bindings in your templates unless it is a DataTemplate.
<CheckBox IsChecked="{Binding Value}" Content="Photo" x:Name="OT_Photo" Grid.Row="0" Grid.Column="0" Style="{StaticResource ElementCheckbox}" />
Don't forget to set de DataContext for your object.
You might want to test the binding first without templating. If this doesn't solve your problem it might be useful to post the code-behind as well.

Display Error using Datatemplate

today you helped me with Need multiple styles dependent on Listboxitem
But now i get a gray rectangle while drawing or moving the objects.
Picture with how it should look like, and how it looks like when iam drawing a line:
https://drive.google.com/file/d/0B_F04v1afwM5c0kteExsamlMcHM/edit?usp=sharing
here is the code i used:
<ListBox.Resources>
<DataTemplate DataType="{x:Type model:PathLine}">
<Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}" Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}" >
</Path>
</DataTemplate>
<DataTemplate DataType="{x:Type model:ImageObject}">
<Image Source="H:\Dropbox\WPF Projekte\Arena WPF\ArenaProgram\ArenaProgram\Pictures\tree1.png" Name="imgPic1" Width="100" Height="75" Stretch="Fill" VerticalAlignment="Top" />
</DataTemplate>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Crimson" ShadowDepth="3" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
hope someone can help me :)
Aha! Finally, I know what you're talking about. :)
It seems to me that that (in my opinion, white) rectangle with a light crimson border is the ListBoxItem object. You added a Style that tells the ListBoxItem to display a crimson glow around its edge when it is selected, so that is what you are seeing. Try adding a Setter which sets the Background property to Transparent:
<Style TargetType="ListBoxItem">
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Crimson" ShadowDepth="3" BlurRadius="10"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
If that doesn't work, you might think about removing the Trigger altogether.

WPF image button

I am using two button in WPF i want to change their image ie image button on DIsable how to do it can any one help me?
In WPF an Image button is simply a Button with its Content set to an image. So, if you want to change the image, you simply need to assign another image to the Content property of the Button.
You cann't change Image, since in WPF Button is just a button as #Daniel Hilgarth wrote. However you can make new style for button like - and apply it to your buttons. This might work like your requirements.
<Style x:Key="BtnStyle" TargetType="{x:Type Button}">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<!--Image-->
<Image x:Name="BtnImage" Stretch="Fill" />
<ContentPresenter Margin="2" HorizontalAlignment="Center" RecognizesAccessKey="True" VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<!--Trigger for IsEnabled-->
<Trigger Property="IsEnabled" Value="false">
<!--Set the source of the image-->
<Setter TargetName="BtnImage" Property="Source" Value="pack://application:,,,/YouAppShortName;component/Image.png" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resources