Change foreground color of ComboBoxItem when ComboBox is hovered - c#

At the moment, I'm styling WPF controls. This is how my custom <Button> looks hovered.
And this is how <ComboBox> looks when mouse is over it.
Simply, I want to be able to change foreground color of currently selected ComboBoxItem when whole ComboBox is hovered. In this example, I'd like to have similiar yellow color like on <Button> control.
The problem is, that <ComboBox> has different ControlTemplates for ComboBox item and toggle button. I don't know how to interact between those two.
The template for <ComboBox> is pretty big, so I'm not going to post it here. Also, you don't have to post full answer if it's not required to explain the problem.

OK, I found a solution. You need to edit default ComboBox templates.
In Visual Studio 2013 you can get a default template in Designer by clicking right mouse button on ComboBox -> Edit template -> Edit a copy...
In <ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}"> you have to add a following trigger:
<ControlTemplate.Triggers>
...
<Trigger Property="IsMouseOver" TargetName="toggleButton" Value="true">
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="Yellow" />
</Trigger>
...
</ControlTemplate.Triggers>
Also, apply a style to ComboBoxItem:
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Foreground" Value="Black"/>
</Style>
Now, it works like a charm.

Related

TextBox border radius style in WPF

I checked similar questions but I couldn't figure out the underlying logic.
I am trying to add CornerRadius to a TextBox in a WPF project.
Here's what I tried so far:
In App.xaml I created a Style that I intend to reuse:
<Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Height" Value="27"/>
<Setter Property="Padding" Value="5.5"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
Adding: <Setter Property="Border.CornerRadius" Value="5"/> didn't work. However, the following worked, but with side effects (all borders where rounded):
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="5"/>
</Style>
I want to keep the styles separate and basically use them like this:
<TextBox x:Name="ExampleTb" Style="{StaticResource TextBoxStyle}"/>
Can you please help me/ point me in the right direction?
It is actually very simple to achieve this, just follow these steps:
Step 1. Add a textbox to your window, right click on your textbox and select "Edit Template \ Edit a Copy..."
This will take you to the control template designer.
Step 2. Check this picture:
https://postimg.org/image/9h5ng8p9t/
P.S. I find blend better suited to design controls.

How can I get a WPF TextBox to auto resize after a style change?

I have a standard TextBox with a fixed width of 150 and TextWrapping set to NoWrap, this makes the control behave just like the old WinForms version - which is what I want.
However, when I click a button, I want to effectively 'convert' the TextBox into a label. To save me messing around with multiple controls, I have decide this is best done by changing the style to look like a label. Most of this is working fine, except I want the new style to auto-resize the width of the Control to ensure all text is displayed without the need to drag a selection with the mouse.
In an attempt to do this I have set the Width to auto and then MinWidth to 150, something like this:
<Style x:Key="TypeConfusedTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="3"/>
<Setter Property="Height" Value="Auto" />
<Setter Property="Width" Value="Auto" />
<Setter Property="MinWidth" Value="150" />
<Setter Property="IsTabStop" Value="False"/>
</Style>
Which I then assign on button click like so:
textBox1.Style = (Style)FindResource("TypeConfusedTextBox");
The problem is that after the Style is changed, the control just remains as a fixed width of 150. If I apply the same Width and MinWidth values directly to the TextBox with the designer (or in the xaml) then is grows as expected when restyled, but so does the original style which I do not want to happen.
What am I missing here?
I'm only guessing that you're setting TextBox.Width somthing like <TextBox Width="150" .../> if that is the case then, according to Dependency Property Setting Precedence List Style won't override your fixed value. Try setting initial Width like this:
<TextBox>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Width" Value="150" />
</Style>
</TextBox.Style>
</TextBox>
The answer by dkozl is exactly the reason why it wasn't working, and is the solution I intend to use.
However, after I posted this question I continued to try a few different things, one of which was to override the width values in code. So just to demonstrate an alternative, here is what I changed my button click code to look like:
textBox1.Style = (Style)FindResource("TypeConfusedTextBox");
textBox1.ClearProperty(WidthProperty);//this sets it to "Auto"
textBox1.MinWidth = 150;
Again, this is just an alternative, I am not recommending it as the best approach.

TreeViewItem to notify ViewModel when it is hovered

I am currently using the code from this blogpost in order to have my TreeView highlight those items, which are currently hovered by the mouse. This is working as intended, however now I want the TreeViewItems to notify their attached ViewModels when they are hovered / not hovered.
However I'm at a loss on how I can achieve this. The corresponding XAML code looks like the following:
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<Trigger Property="Controls:TreeViewHelper.IsMouseDirectlyOverItem" Value="True">
<Setter Property="Background" Value="Green" />
</Trigger>
</Stile.Triggers>
</Style>
How can I bind the property from my ViewModel, named TreeNodeModel.IsHovered to the TreeViewItem (or probably the attached dependency property IsMouseDirectlyOverItem) so that I can react on those changes from within my code?
All the examples I found via google only explained how the set the background color. Thanks in advance for your time on the probably trivial answer.
In your Style, try adding a Setter which binds IsMouseDirectlyOverItem to IsHovered, and use the OneWayToSource binding mode to push the value the right way:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Controls:TreeViewHelper.IsMouseDirectlyOverItem"
Value="{Binding Path=IsHovered, Mode=OneWayToSource}" />
<Style.Triggers>
...
</Style>
EDIT: As IsMouseDirectlyOver is read-only, and read-only DPs can't be the target of any bindings, Fredrik Hedblad's PushBinding may be a possible workaround: OneWayToSource Binding for ReadOnly Dependency Property
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="pb:PushBindingManager.StylePushBindings">
<Setter.Value>
<pb:PushBindingCollection>
<pb:PushBinding TargetDependencyProperty="Controls:TreeViewHelper.IsMouseDirectlyOverItem"
Path="IsHovered" />
</pb:PushBindingCollection>
</Setter.Value>
</Setter>
<Style.Triggers>
...
</Style>

How do I highlight a treeview selected item with some color?

I have a treeview in WPF. I want a different color when i select the treeviewitem.
Simple Trigger in TreeView.ItemContainerStyle can't help for default TreeView template.
For standard template highlighting is done via background changing for specific element inside TreeView template. This specific element is not accessible for programmer without TreeView template changing. By default resource is used to set background on this element for highlighting.
So there are few ways:
simple (but side effects possible): redefine resource with key {x:Static SystemColors.HighlightBrushKey} for TreeView or ItemsPanel template;
Redefine complete Template for TreeView.
Try following code. It should work.
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>

How do I know whether to use a style or override a control template?

This question is inspired by this recent question and other situations I've encountered in my WPF development. How do I know whether it is enough to set a style on a control to override some default behavior vs creating a new control template?
More concretely, in the question above, the author wants to change the look of a ListBoxItem when it is selected. (See code reprinted below). Everything works, except the Background property. How is one supposed to know that they should override the Control Template for this?
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Content" Value="{Binding Path=Name}"/>
<Setter Property="Margin" Value="2"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
As to whether to use a style or template Ray provided a great response.
As to how to solve your problem without creating a template, maybe I can help.
The background color is being set by the SystemColors. Using Blend and creating a template you can see the exact xaml.
So if NO TEMPLATES! is a requirement you can always change what that resource is.
Example :
<ListBox>
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Yellow" />
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Content" Value="{Binding Path=Name}"/>
<Setter Property="Margin" Value="2"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
<ListBoxItem>Test 1</ListBoxItem>
<ListBoxItem>Test 2</ListBoxItem>
<ListBoxItem>Test 3</ListBoxItem>
</ListBox>
That will give you the background color for that given ListBox and not screw up anything else in the app.
Styles can be thought of very closely to CSS styles in HTML. If all you want to do is change the basic properties of a control such as Background, Foreground or whatever properties it exposes then a Style is exactly what you need. Styles also allow you to apply triggers so for animations, a style is also sufficient.
If you're finding you want to change the intrinsice behaviours / inner workings on a control then a control template is what you want. For example, if you want to change how a button is laid out by adding some sort of grid behaviour, then using a control template is the way forward.
Unfortunately, for your specific example, you don't know unless you try it. Basically you first try it with a Style....and if that doesn't work for whatever reason, then you write a ControlTemplate. You usually only end up writing ControlTemplates for the reasons Ray mentioned.
My guess is that the trigger you're trying to set has also been hardcoded in the ControlTemplate...which is bad design imo because it prevents the Style from overriding it.
By "Background" I take it to mean the "blue" rectangle that surrounds the ListBoxItem when it is selected?
This is actually the FocusVisualStyle property, which is a style that describes what the item should look like when it is focused. The Control explicitly sets this property (described here), so in order to override it, you will have to redefine the Control Template, making sure to use a default Style setter to set it to {x:Null}.

Categories