wpf comboBox coloring issue - wpf

Due to requirements, I need to have a comboBox that works as follows:
It uses:
- one set of colors for fore/background when in view mode
- A second set of colors for fore/background when in edit mode
- Another set for selected mode (when the cursor is in the comboBox)
- Another set for disabled mode
The user will never be able to edit the contents, just click on the down arrow and select from the list.
I have the comboBox working except for the colors. Unlike other controls, simply trying to do the following (the triggers for edit mode) just doesn't work:
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused"
Value="false" />
<Condition Property="wpfMisc:myCtrl.viewMode"
Value="false" />
<Condition Property="IsEnabled"
Value="true" />
</MultiTrigger.Conditions>
<Setter Property="BorderBrush"
Value="{DynamicResource controls-editableBorderBrush}" />
<Setter Property="Background"
Value="{DynamicResource controls-editableBackgroundBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource controls-editableForegroundBrush}" />
</MultiTrigger>
What do I need to set in my style so that I can change the fore/back color of the displayed SelectedItem - i.e. make the above work?
And I am curious if anyone can tell me why a control like this doesn't use a similar interface as other data entry controls (isn't that the whole idea of polymorphism?) This isn't a big deal, just curious, that's all.
Thanks!

You don't achieve what you're after in the way that you are currently trying to achieve it. There is no need to use a MultiTrigger, just a number of sequential Trigger objects:
<ComboBox Width="150" Height="24">
<ComboBox.Style>
<Style>
<Setter Property="ComboBox.Background" Value="Green" />
<Style.Triggers>
<Trigger Property="ComboBox.IsFocused" Value="True">
<Setter Property="ComboBox.Background" Value="Red" />
</Trigger>
<Trigger Property="ComboBox.IsEnabled" Value="False">
<Setter Property="ComboBox.Background" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
Now, I've shown you the IsEnabled Trigger here to demonstrate that you could add multiple Trigger objects like this. However, you can't actually use this Trigger for IsEnabled, because there is a Trigger defined inside the default ComboBox that already has a Trigger set on that property (to make it look disabled). If you absolutely have to add a Trigger for IsEnabled, then you will have to implement your own ControlTemplate for the ComboBox to override that default behaviour. If this is the case, please take a look at the ControlTemplate Class page on MSDN or ask a new question for help with this.
To address your other requirement of your 'view mode' is a bit more tricky. The code that you provided lookslike you are trying to retrieve the value directly from a class, rather than an instance of that class. In WPF, we normally add public properties into a view model or code behind file that we can bind to.
So I would imagine that you could have a bool property named IsViewMode and then you would add another Trigger like this:
<Trigger Property="IsViewMode" Value="True">
<Setter Property="ComboBox.Background" Value="Orange" />
</Trigger>
However, if your original syntax was correct, then your Trigger would look like this:
<Trigger Property="wpfMisc:myCtrl.viewMode" Value="True">
<Setter Property="ComboBox.Background" Value="Orange" />
</Trigger>

Related

TextBox Inside ListView WPF

I have a TextBox inside my ListView. When I click on the textview, the SelectionChanged event of ListView is not fired.
Is there a way to fire that, so that the ListView item is selected?
Despite the fact that you have not asked a good question, I think I understand your problem. I'm assuming that when you said textview, you actually meant Textbox and that your problem is that your ListViewItem is not changed to the item that contains the TextBox that you clicked on. If this is so, then adding this Style should do the trick:
<Style x:Key="ListViewItemSelectionStyle" TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="False">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
While it looks strange, the second part is to ensure that the item remains selected after it loses KeyboardFocus.
You haven't provided any code, but at a guess, you haven't handled the event:
<ListView SelectionChanged="MyEventHandler" ...
</ListView>

How to modify legacy named style for having different setters based on targetTypes?

I have this named style
<Style x:Key="validationSupport" TargetType="{x:Type Control}">
<Setter Property="Margin" Value="5,2,14,2" />
...OMISSIS...
<Style.Triggers>
...OMISSIS...
<DataTrigger Binding="{Binding DataContext.ActiveWorkspace.Editable, RelativeSource={RelativeSource AncestorType=Window}}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
I use it extensively for TextBoxes, ComboBoxes, DatePickers etc, so I used as TargetType a super class for all these elements, Control.
Now I would like to differentiate the setter inside the dataTrigger using specific properties that 'Control' doesn't have. It seems I have to create different styles with different names,each for every targetType I want to differentiate, but that way I have to change the style name inside all elements which use it. Is there a smarter way to achieve that goal ? I don't want want to go and modify every xaml file I have.
Update after first answer
I have tried to put the following setters inside the datatrigger:
<Setter Property="Background" Value="#FFECECF8" />
<Setter Property="CheckBox.IsEnabled" Value="False" />
<Setter Property="DatePicker.IsEnabled" Value="False" />
<Setter Property="ComboBox.IsEnabled" Value="False" />
<Setter Property="TextBox.IsReadOnly" Value="True" />
Unfortunately the tests gave odd results. The IsEnabled property is set for TextBoxes too despite the prefix should limit its application to CheckBoxes, DatePickers and ComboBoxes.
My final need was to make some control contents unchangeable avoiding the difficult to read colors associated with disabled controls. From previous researches I understood that changing the colors for a 'disabled' control is not an easy task and involves the redefinition of the control template. So I thought to apply a combination of IsReadOnly and Background, but it is not applicable for the above problem. In fact CheckBoxes, DatePickers and ComboBoxes can only be made unchangeable using the IsEnabled property.
Am I missing something ?
There is a way, but I have to warn you - this is far from best-practice and should be avoided
WPF allows you to use desired type as a prefix for the property. That way, if you apply the style to a control that doesn't inherit from the prefixed type - the setter is ignored.
<Style x:Key="validationSupport" TargetType="{x:Type Control}">
<Setter Property="Margin" Value="5,2,14,2" />
...OMISSIS...
<Style.Triggers>
...OMISSIS...
<DataTrigger Binding="{Binding DataContext.ActiveWorkspace.Editable, RelativeSource={RelativeSource AncestorType=Window}}" Value="False">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Button.Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
[Test this extensively, since I suspect that it might create memory leaks.]

How would I increment a color value on MouseOver trigger in XAML template?

Hi all,
I am working on creating a button template to use in my WPF application. The format is basically color tiles (Think Windows Phone style for the most part.).
If I was wanting to change the color of the button in the event of a MouseOver, I would just make some code like this (let's say the original button color is Gray):
<Window.Resources>
<Style x:Key="ColorTileButton" TargetType="Button>
<!-- Insert various property editing here. -->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightGray" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False>
<Setter Property="Background" Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
</Style>
</Window.Resources>
But obviously this wouldn't work if the original button color was, say, Firebrick because I would end up with a bunch of standard Gray buttons. So I was wondering if there would be a way to rewrite this button template so that it would just increment the values of R, G, and B by about 10 to give the button a lighter color, then decrement is when the mouse left. Help me out?
Thanks guys.
I believe what you are describing is ColorAnimation an example, or you can look at this Forum Post. An interesting note from the Forum post.
A trigger makes a change to one or more properties when its condition is satisfied, and then resets the property values when its condition is no longer satisfied.
So what he is suggesting is:
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightGray" />
</Trigger>
</ControlTemplate.Triggers>
it would reset to FireBrick as you gave in your example

regarding WPF validation;

In my application I have a model and viewmodel which implement IDataError this all works but for example when I open view for adding new customer if my validation rule requires First and Last name not to be null or empty those validations are immediately evaluated and user sees form with errors asking them to enter those data. how can I just show clean entry form but still show validation when property changes or input lost focus?
first if your rule say that first and lastname should not be empty - its right that the user see the validation error.
what i have done is to use a ValidationTemplate for empty values, so that the user just see a "*" for requiered field.
<ControlTemplate x:Key="ValidationTemplateEmpty" >
<DockPanel>
<TextBlock Text="*" Margin="0,0,3,0" Foreground="Red" Visibility="{Binding ElementName=MyAdornedElement,Path=AdornedElement.Visibility}"
ToolTip="{Binding ElementName=MyAdornedElement,Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
<AdornedElementPlaceholder Name="MyAdornedElement" />
</DockPanel>
</ControlTemplate>
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding Path=(Validation.Errors).CurrentItem.ErrorContent, RelativeSource={x:Static RelativeSource.Self}}"/>
<Setter Property="Background" Value="{StaticResource BrushErrorLight}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Validation.HasError" Value="true"/>
<Condition Property="Text" Value=""/>
</MultiTrigger.Conditions>
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplateEmpty}"/>
<Setter Property="ToolTip" Value="{Binding Path=(Validation.Errors).CurrentItem.ErrorContent, RelativeSource={x:Static RelativeSource.Self}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
If you implements IDataErrorInfo why do you use validation rule?
In this scenario the validation you implemented is wrong. While in other situations a LastName property cannot be empty in this scenario it is allowed.
What is not allowed, is saving a Customer with empty fields.
So you have to adjust your validation in this ViewModel accordingly.
To have input validation the way you describe it (lost focus) is impossible if you want to give the user the freedom to enter the fields in random order.
I see two acceptable ways:
Keep the input/field validation but make it less in your face; just a simple mark next to the textbox.
Validate the Cutomer object when clicking the Save button and setting errors in the UI. You could even code the CanExecute of the SaveCommand but that might make the validation (when can I save, what did I do wrong) more obscure.

Is it possible to assign ToolTip text (containing non-latin characters) in a Control Template of a WPF custom control?

In a WPF app I have a custom control. I would like the ToolTip for objects, derived from this custom control, depends on a value of one of the attributes of this custom control.
Is it possible to declare it in a Control Template of this custom control?
Something like:
<ControlTemplate>
??? // <!--XAML ToolTip declaration -->
...
<ControlTemplate.Triggers>
<Trigger Property="MyProperty" Value="FirstValue">
<Setter ...??? /> // <!--XAML ToolTip text assignment -->
</Trigger>
...
<Trigger Property="MyProperty" Value="SecondValue">
<Setter ...??? /> // <!--XAML ToolTip text assignment -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Edited (added):
I have found the solution:
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="MyProperty" Value="FirstValue" />
</MultiTrigger.Conditions>
<Setter TargetName="PART_Backgr" Property="ToolTip" Value="Available"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="MyProperty" Value="SecondValue" />
</MultiTrigger.Conditions>
<Setter TargetName="PART_Backgr" Property="ToolTip" Value="Sold"/>
</MultiTrigger>
It is working excellent.
But one problem remained: In fact Value="Available" and others such values should contain non-latin characters (the application is localized in Russian language). When I am trying to compile it with Value="Свободно", I get en error:
'Invalid character in the given encoding.' XML is not valid.
What solution could be to this problem? Maybe I could change somewhere the encoding by which Generic.xaml page compiles? Or change XAML code somehow?
(In fact, in every window I have lots of non-latin characters in XAML and everything compiles OK. But, maybe, the problem is in the way they used.)
Does this not work?
<ControlTemplate.Triggers>
<Trigger Property="MyProperty" Value="FirstValue">
<Setter Property="ToolTip" Value="..."/>
</Trigger>
</ControlTempalte.Triggers>
To try and address your other issue, the following works for me without any special changes:
<TextBlock Text="Свободно" ToolTip="Свободно"/>
I copied and pasted that text from your question into my XAML code.

Resources