How to change default mouse hover behavior on dynamically generated buttons - c#

I am a relatively new user of WPF and have run into a problem regarding dynamic Button generation and Buttons' default hover properties.
I am currently working on an application where a significant number of buttons are being generated on a Canvas in the code behind. The contents of each button are unique images referenced by an array of objects containing Uri strings. This array is populated by reading in a file containing these Uri strings, so the number and placement of buttons on this canvas vary based on which file is being read.
For the most part, the appearance of the Canvas when the application runs is what was intended, however hovering over any of the Buttons replaces the image with the default blue background for the duration that the mouse overlaps.
Here is an example of the code that I am using to generate the buttons:
exampleButton = new Button { Content = "Name", Width = 50, Height = 65, Background = new ImageBrush(new BitmapImage(new Uri(#object.UriString, UriKind.Relative))) };
exampleButton.Style = exampleStyle;
exampleCanvas.Children.Add(exampleButton);
Please understand that I have omitted pieces of code irrelevant to my question.
Here is an example of the style that was used, also in the code behind:
exampleStyle = new Style(typeof(Button));
exampleStyle.Setters.Add(new Setter(Button.ForegroundProperty, Brushes.Transparent));
exampleStyle.Setters.Add(new Setter(Button.BorderBrushProperty, Brushes.Transparent));
Together these achieve the effect I am trying to create, barring hover behavior.
So far I have tried appending ControlTemplate overrides into the style declaration but am unsure of how that translates from XAML to the C# code behind. I have also tried creating and binding button templates created in the XAML but I haven't had success in finding explanations or tutorials that apply to my situation.
Any help to accomplish this via the code behind would be greatly appreciated. Of course, if I'm doing this really unconventionally and there is a more standard way of doing things I am all ears.
EDIT:
This is the XAML I am using to declare the style that my dynamically generated buttons are using.
<Style x:Key="MySuperButtonStyle" TargetType="{x:Type Button}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="65" />
<Setter Property="Foreground" Value="Transparent" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
Using this call to assign the style in the code behind:
exampleButton.Style = (Style)FindResource("MySuperButtonStyle");

This is happening because the default Button control style has a trigger that changes the Background property of the button when the mouse hovers over it. You need to use a custom style for the button:
<Style x:Key="MySuperButtonStyle" TargetType="{x:Type Button}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="65" />
<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>
Here the width and the height are set using the style, so you no longer need to set those properties in code. The control template has been changed so it consists only of a Border element with the content inside of it. There are no triggers at all, so the button won't change its appearance when clicked or hovered over.
In your code all you need to do is obtain a reference to this style then assign that to the Style property when you are creating the button.
Having said all this, in WPF you rarely need to create controls in code. Instead you should really be using the MVVM (Model-View-ViewModel) pattern and data binding. You probably shouldn't be creating styles and setters in code either.

Related

Style not being applied to inner element via Style property binding

I have got a custom control composed of many parts.
One of those parts is a Border.
I need to style that border from outside the control so i created a dependency property of type Style and bound it to the Border like this:
<ControlTemplate TargetType="{x:Type cc:DrawingLayer}" >
...
<Grid x:Name="grid" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" >
<Border x:Name="PART_AreaSelector" Style="{Binding AreaSelectorStyle}" BorderBrush="#FF3399FF" BorderThickness="1" Background="#55ADD8E6" />
</Grid>
</ControlTemplate>
In the window where i use the control i try to define its style this way:
<cc:DrawingLayer.AreaSelectorStyle>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="10" />
<Setter Property="Background" Value="Red"/>
</Style>
</cc:DrawingLayer.AreaSelectorStyle>
But it seems that the style is not applied. No property reflects the values in the style.
Can someone point out what i'm missing?
The "local" property values assigned in
<Border ... BorderBrush="#FF3399FF" BorderThickness="1" Background="#55ADD8E6" />
have higher precendence than the values set by Style Setters.
You need to set a default Style for those values.
See Dependency Property Value Precedence on MSDN.

User Control Inherited From Button in WPF

I was asked to create a Radio and CheckBox variation were visually similar to a button.
With this behavior:
When the button is clicked, it change to state checked, the background is changed, when clicked again the state is changed to unchecked and the background turn into the original brush.
At first my strategy was to create a user control. But since my control will be almost equal to a button, make sense to me use inheritance.
So my question is
Is possible to create a user control that inherit from button? If so, is that a good approach? How can I do it?
One possible approach is to use ToggleButton, but completely change its appearance when IsChecked become true:
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- IsChecked == false template -->
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Trigger.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- IsChecked == true template -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
Use 2 different templates (e.g. <TextBlock Text="On" /> and <TextBlock Text="Off" />) to see how it works.
You can use the WPF toggle button.

C# WPF Style TreeViewItem

I made a custom resource dictionary style for a TreeViewItem, but I am having difficulties with it.
<Style x:Key="StageTreeViewItem" TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<Setter Property="Foreground" Value="Gold"/>
<Setter Property="FontFamily" Value="ArialN"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid>
<Image Name="PrimaryButtonImage" Source="pack://application:,,,/Images/TreeViewItem/TreeViewItem_Normal.png"/>
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The content/header of the TreeViewItem does not exist. I put "Stage One" as Header of the TreeViewItem, but it doesn't show up. Also, if I add multiple tree view items on another, it does not expand at all.
Another thing:
How can I remove the highlights when I select the tree view item? I want it to be transparent even when I hover over it and even when I click it. I don't want anything to happen, but I just don't know how, I tried everything.
Your provided code is not making it clear how you're setting header of TreeViewItem.
For other part of the question, you can use Triggers for events happening in WPF forms. Also have a look at this link, as you'll have to define a template for changing background color on mouse hover.
IsMouseOver Trigger not working in WPF

How to access a styles resources and retrieve an inner style that doesn't have a key

I have a style for a stack panel which has a key, within the stack panel I have buttons which have a default style set via the stack panel's Style > Resources > Styles, for this reason the buttons style is not set via a key but instead is set as it is a child of the stack panel! I'm sure this is a little tricky to understand as it sure feels tricky to explain... here is the style code...
<Style x:Key="VerticalMenuPanel" TargetType="{x:Type StackPanel}">
<Setter Property="Background" Value="#115e9a" />
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="13" />
<Setter Property="FontFamily" Value="Calibri" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Height" Value="27" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="White" BorderThickness="0,0,0,1">
<Border BorderBrush="#0160a2" BorderThickness="0,0,0,1.5">
<ContentPresenter VerticalAlignment="Center" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
</Style>
So, basically, without getting into what I'm doing too much I require the need to change the style of the buttons within the stack panel based on certain events. In order to change the style I simply find the resource based on the style key! HOWEVER here is my problem, I can't reset the style back to the "default" style provided by the parent the "VerticalMenuPanel"s Style.Resources as I don't know how to retrieve the style without having a definitive key for it. The obvious thing to do would be to give the button style a key but then I would have to explicitly define the style for all of the buttons instead of the style being applied by default as it is a child of the stack panel!
The bottom line is how do I retrieve a style without a key from within a parent styles resources (programatically)? Obviously I can retrieve the parent style via its key.
I hope you understand the issue I am having, and please feel free to let me know if I can explain anything better, add more clarity or if you wish edit the post yourself :)
When the Key is not provided for a style, the TargetType becomes the key of that style.
Here is an example:
<Grid x:Name="layoutRoot">
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="100" />
</Style>
</Grid.Resources>
<Button />
</Grid>
then in the code-behind you can retrieve the default style using the Button type as the resource Key:
Style buttonStyle = layoutRoot.FindResource(typeof(Button)) as Style;

Convert XAML style to code behind style?

I have a list box whose selection colour is default plain Solid Blue colour. I read this article "How To Change WPF ListBox SelectedItem Color?" here. I want to create style given in it to code behind. so that i can assign this style to my Listbox ItemContainerStyle property.
like
Style s = ......
MyListBox.ItemContainerStyle = s;
I want to make this in code behind because if user changes theme of my software then this style (Selection Colours) should recreate itself to match the changed theme colours.
<Style x:Key="SimpleListBoxItem" TargetType="ListBoxItem">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="Border" Padding="2" SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource AuthorGradient}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I think you don't have a code behind version of this code, you just have to apply you're existing template to your listbox like below.
if your target is a template.
(NameOfListBox.SelectedItem as ListBoxItem).ContentTemplate = this.Resources["NameOfTemplate"] as DataTempate;
(NameOfListBox.SelectedItem as ListBoxItem).UpdateLayout();
if your target is a style.
(NameOfListBox.SelectedItem as ListBoxItem).Style= this.Resources["NameOfStyle"] as DataTempate;
(NameOfListBox.SelectedItem as ListBoxItem).UpdateLayout();
example
(lstMetaDataCards.SelectedItem as ListBoxItem).ContentTemplate = this.Resources["MetaDataCardAtEditState"] as DataTemplate;
(lstMetaDataCards.SelectedItem as ListBoxItem).UpdateLayout();

Categories

Resources