Changing between named styles - wpf

A few months ago in a C# application I began using a ListBox as a container for some UserPanels. When a panel was selected it was highlighted, just like any listbox item. I found the following XAML I was able to add to give all items a transparent background (Not sure where I originally found this or I'd link it)
<Application.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="border" Background="Transparent">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="border" Property="Background">
<Setter.Value>Transparent</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
This worked fine but now I need to sometimes have a background color instead of a transparent background. There is only 1 listbox and the contents change so I want to programatically change the style in the codebehind.
I found examples on changing the style in code but I wasn't able to create two of this same style. I gave it a x:name="transparentListbox", copied it, and gave the other one x:name="normalListbox" with a background of Blue, but I get an XML parse exception having two style elements, possibly because they're both generically trying to modify every listbox.
How can I have two named styles that accomplish the same thing (modifying the background when an item is selected) that I can switch between in code as needed?
Edit:
In every case my listbox is used to store UserPanels. I add them to the listbox using lstPanels.Items.Add(p) where p is an instance of a class derived from UserPanel.
When I first made the app there were multiple windows so the windows that required transparency had this style, and those that required selecting items did not. Managing multiple windows became cumbersome so it was re-factored into a single window and the listbox would be cleared and loaded with different types of panels when the mode changed. Some still required transparent backgrounds, but now some did not.
Programatically assigning a named style to the Listbox as a whole, when the mode changes, would be okay. Assigning a style to every ListBoxItem would involve updates to a lot of code as that functionality is spread out.
Perhaps the solution would be to maintain a single style but have the background property be bound to a varaible, if that is at all possible?

I have always created the style in my UserControl.Resources section.
In your case:
<Style x:Key="ListBoxStyle1" TargetType="MyDerivedListBoxItem">
...
<Style x:Key="ListBoxStyle2" TargetType="MyDerivedListBoxItem">
...
and in the code behind I have set the styles this way.
If Not MyListBox.ItemContainerStyle.Equals(CType(Resources("ListBoxStyle1"), Style)) Then
MyListBox.ItemContainerStyle= CType(Resources("ListBoxStyle1"), Style)

I wouldn't mess with trying to modify styles in your code. It may be feasible and even work, but that seems like a world of pain to me. One idea that might help is that you could inherit from ListBoxItem, defining your own control. You could then put a dependency property on that inheritor like UseAlternateBackgroundColor of type bool or something.
Then, you'd modify your style:
<Style TargetType="MyDerivedListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyDerivedListBoxItem}">
<Border x:Name="border" Background="Transparent">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="border" Property="Background">
<Setter.Value>Transparent</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="UseAlternateBackgroundColor" Value="true">
<Setter TargetName="border" Property="Background">
<Setter.Value>Black</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Generally speaking, I personally try to avoid dealing with layout and visual style considerations in code as much as possible.

You need to set different x:Key directives, if the styles are resources the Name is pretty useless anyway. If you do not set a key the TargetType is used as the key instead which causes a collision. To apply one of the styles in code you can call FindResource with the respective key.

Related

How can I create a button where caption changes based on boolean in viewmodel but button is based on a style resource?

Perhaps I'm missing something obvious, but I can't figure this one out... I have a button style defined in App.xml that presents a "flat" looking button:
<Style x:Key="FlatButton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="ButtonBorder" BorderThickness="2" BorderBrush="{DynamicResource color_Logo}" Padding="5,3,5,3" HorizontalAlignment="Stretch" Background="White">
<TextBlock x:Name="ButtonText" FontSize="12" Foreground="{DynamicResource color_Logo}" HorizontalAlignment="Center" VerticalAlignment="Center"><ContentPresenter /></TextBlock>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextBlock.FontWeight" Value="Bold" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="BorderBrush" Value="Gray" TargetName="ButtonBorder" />
<Setter Property="Foreground" Value="Gray" TargetName="ButtonText" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource color_LogoLight}" TargetName="ButtonBorder"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Works perfectly fine throughout my app. I now need to create a button with this style but where the content, or caption, of the button changes based on the state of a boolean value in the viewmodel. I've tried several iterations and I either end up with a complaint from the compiler that the style property is already set or I just see the object type name as the caption.
I suppose I could create a text property in the viewmodel that exposes the proper caption, but this seems to violate the separation of concerns in MVVM. While I know it's not an arrest-able offense if I do that, my viewmodel shouldn't care about the way the UI presents something, right? It just exposes the state of the object and the UI makes the decision on how to present it.
Another option is to create two buttons and hide the one that is not appropriate, based on the viewmodel boolean. This seems to conform to the MVVM pattern a bit better but I feel like I should be able to do this with a trigger on a single button.
Is it possible to override part of the style resource?
I'm going with the two-button solution, at the moment, but I'd just like to know what I'm missing.
Thanks.
J
Is it possible to override part of the style resource?
No. You can indeed base a Style on another Style and override specfic setters but you cannot "override" only a part of a ControlTemplate. Unfortunately you must then (re)define the entire template as a whole:
WPF: Is there a way to override part of a ControlTemplate without redefining the whole style?
I suppose I could create a text property in the viewmodel that exposes the proper caption, but this seems to violate the separation of concerns in MVVM.
Well, not at all. The very name View Model implies being a model to the View. Of course it depends on a handful of design choices, but this is not a violation of MVVM, in my opinion.
Besides that, I would go with a DataTrigger (defined in Style.Triggers) where you can set specific, view-only strings given the value of your boolean property.
And, please, don't go with the two-button solution, that is closer to an arrestable offense ;o)

How to override the enabled /disabled look on a button in wpf

I'm assuming that this may be a style issue, but being new to wpf I'm not sure what I should be changing. I have built a user control containg various buttons (whose background colour I have set to be transparent). None the less when added to a form or other user control they exhibit a coloured background when disabled, or when run over by the mouse when enabled (see illustration below)
What do I need to do to remove the grey background on the button's disabled state, and also the blue background that appears on rollover when it is enabled. My aim is to try aand retain a clean and clear rendition of the button images.
Thanks
You would need to override the ControlTemplate of the Button controls using a Style. The following Style will apply to all Button controls, but you can specify a Key for the Style and use it on specific Button controls if you wish.
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="ButtonContent">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ButtonContent"
Property="Background"
Value="Transparent"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>

wpf ListView - disable header mouse over effect

I have a simple 1 column ListView and I need to disable the mouse over effect for the GridViewColumnHeader.
I tried grabbing the style from here: http://msdn.microsoft.com/en-us/library/ms788747.aspx
However that gave my header a purple gradient so I guess it was the wrong style. I do notice it has a
<VisualState x:Name="MouseOver">
But I have no idea how to remove that without finding and including the correct GridViewColumnHeader style and then removing it.
I tried the following, but it doesn't do anything (The VisualState overrides?) and wouldn't work anyway as setting the background to null wouldn't be what I want.
<Style x:Key="hcs" TargetType="{x:Type GridViewColumnHeader}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
The problem is, like you said yourself, in MouseOver visual state and its Storyboard. I think it`s better to get rid from redundant functionality than try to override it later. So we basically just need the same style but without that storyboard. For bare minimum we need to have following things (can be found on MSDN via the link you provided):
<Color x:Key="BorderLightColor">#FFCCCCCC</Color>
<Color x:Key="BorderDarkColor">#FF444444</Color>
<Style x:Key="GridViewColumnHeaderGripper"
TargetType="Thumb">
<!-- Full GridViewColumnHeaderGripper style here -->
</Style>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewColumnHeader">
<!-- Standard template but with redundant Storyboard removed -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The last Style doesn`t have a key so it applies to all GridViewColumnHeader on page \ application (depends on where it is defined). Or you can set the key and use it only in specific grids.
That`s bare minimum so for more customizable approach it may be better to copy other parts of default style too.

In code-behind how can I override tabitem colors set by an application-wide theme?

I change the background color of my TabItem conditionally in code behind. This works fine as long as no theme is set in the App.xaml. How can I change colors of a TabItem (in code) while keeping an application-wide theme?
Background:
I'm using a free WPF theme from Nukeation Reuxables. The theme is set in the Application.Resources section of my App.xaml:
<ResourceDictionary Source="/ReuxablesLegacy;component/Edge.xaml" />.
I'm trying to conditionally set the background color of a TabItem in code behind:
MyTabItem.Background = new SolidColorBrush(Colors.Gray);
The background color changes if I remove or comment out the App.xaml line that sets the theme. Why does the theme break my code? I'm changing the tab color (as data is loaded) to show which tabs contain data.
I'm aware that XAML and binding are typically used to change colors, but the solutions I've attempted seem overly complex. Here is my related StackOverflow question seeking an all XAML and binding solution. The answers given just raised more questions which I haven't found answers to.
Thanks in advance.
Edit:
The problem does not happen when changing the background of a button:
button1.Background = new SolidColorBrush(Colors.Red);
The button changes color as expected (while using an application-wide theme).
It REALLY depends on how the theme itself is implemented. If the theme was using TemplateBinding to bind to the background color of the tab to the theme's controls, then your code behind solution would work. That is probably why your button properly changes background colors in your example.
You'll probably have to dig deep into the tab's style xaml, and modify it there first for the codebehind solution to work.
If you look at the ControlTemplate of TabItem its Background property is bound to a internal resource it doesn't do TemplateBinding, I presume the theme has just given colors to it. For your code to work you have to restyle it.
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,-4,0"
Background="{TemplateBinding Background}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1,1,1,1"
CornerRadius="2,12,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="100" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have made two changes to the normal ControlTemplate, you can do the same to your theme.xaml.
Changed Background="{StaticResource LightBrush}" to Background="{TemplateBinding Background}"
Removed Background setter in IsSelected = True trigger.
Unfortunately, this is a simple problem with no simple answer (that I have found so far). Taking a different path to satisfy users was my final solution.
Instead of attempting to change the TabItem Background color I simply change the TabItem Foreground color to Gray if NO data exists. I also add the word "Blank" like this: "Tab 1 Blank", "Tab 2 Blank", etc. This an effective solution to the original problem. I describe it in detail here.

Selected item loses style when focus moved out in WPF ListBox

What do I have?
I have a ListBox populated with items from an XML file. Given a DynamicResource for Style property and written trigger for IsSelected in ItemContainerStyle.
What do I want to do?
I want to keep the selected Item highlighted even after focus moved out of the ListBox.
What problem am I facing?
When I select an item the style specified in IsSelected trigger works. But, when I move the focus out of list box (press tab or click on some other control) the selected item loses its style. Is there any way so that I can retain the selected item style?
Thanks in advance!
The answer referenced will in some cases solve the problem, but is not ideal as it breaks when the control is disabled/readonly and it also overrides the color schemes, rather than taking advantage of them. My suggestion is to add the following in the ListBox tags:
<ListBox....>
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<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="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
What this will do is set the Highlight background color on the list box item whenever it is selected (regardless of the control state).
My answer is based on help from the answers already given to these answers, along with the following blog: http://blogs.vbcity.com/xtab/archive/2009/06/29/9344.aspx
If you're only setting the background color, try replacing ControlBrush for the ListBox, as per this answer.

Resources