regarding WPF validation; - wpf

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.

Related

wpf comboBox coloring issue

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>

How do I get simple editing on a Percent formatted field in WPF TextBox?

When you format a TextBox as currency and click to edit it, the $ and commas do not cause a problem, you can just edit and tab with no problem. When you format a field as a percentage things do not work so well.
<TextBox Text="{Binding CostMarkup,
StringFormat=P}"
Style="{StaticResource ctrlSpacingTight}" />
If the underlying value is 0.1 it correctly displays as 10%, if you go to edit it still shows as 10% the % will cause a problem plus it will change the underlying value from .1 to 10. I wrote a Converter to handle all this but I'm wondering if there isn't a better way. In particular is there a way to handle it the way currency handles it?
There is a built in currency converter so I suspect the currency version of StringFormat uses that. While there is a ZoomPerentageConverter it doesn't do what I would expect. Is there a way to hook in to StringFormat=P and have it invoke my Converter instead of having to go to every instance and explicitly specify it?
<TextBox Text="{Binding CostMarkup,
StringFormat=P,
Converter={StaticResource pctConverter}}"
Style="{StaticResource ctrlSpacingTight}" />
I've always found that it's easiest to display the raw data when editing, and the formatted value when not.
Here's an example that does that using a trigger
<Style x:Key="ctrlSpacingTight" TargetType="{x:Type TextBox}">
<!-- Other Style Setters -->
<Setter Property="Text" Value="{Binding CostMarkup, StringFormat={}{0:C}}" />
<Style.Triggers>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Text" Value="{Binding CostMarkup}" />
</Trigger>
</Style.Triggers>
</Style>
If ctrlSpacingTight is a global style, you can create a style for your TextBox that is BasedOn your global style.
<Style x:Key="CurrencyTextBox" TargetType="{x:Type TextBox}"
BasedOn="{StaticResource ctrlSpacingTight}">
<Setter Property="Text" Value="{Binding CostMarkup, StringFormat={}{0:C}}" />
<Style.Triggers>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Text" Value="{Binding CostMarkup}" />
</Trigger>
</Style.Triggers>
</Style>

Redundant converter calls when using Triggers to determine value

I noticed that my XAML markup is wasting resources by doing conversions which it is not supposed to do. e.g. i have the following Style which acts as a switch:
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDownloaded}" Value="True">
<Setter Property="Source"
Value="{Binding Data, Converter={StaticResource ByteArrayToBitmapImageConv}}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsDownloaded}" Value="False">
<Setter Property="Source"
Value="{Binding Url, Converter={StaticResource UrlToBitmapImageConv}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Obviously this should either download an image if it has not been cached or turn the raw data into a BitmapImage. The problem is that as soon as both cases have taken place at least once both converters are called when the DataContext changes, irrespectively of the value that IsDownloaded has. So it will either display the converted image but still download it independendly in the background or it will download the image and try to convert null (the data) to a BitmapImage.
Setting the binding mode to OneTime did not help sadly.
I am looking for a clean way to avoid this as it even occurs multiple times in my application. e.g. here:
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{StaticResource ContentNothingSelected}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Static local:App.Settings}, Path=DisplayMode_Current}"
Value="Description">
<Setter Property="Content" Value="{StaticResource DescriptionViewer}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Source={x:Static local:App.Settings}, Path=DisplayMode_Current}"
Value="WebPage">
<Setter Property="Content" Value="{StaticResource WebPageViewer}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Source={x:Static local:App.Settings}, Path=DisplayMode_Current}"
Value="Media">
<Setter Property="Content" Value="{StaticResource MediaViewer}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Even if the display mode is set to Media or Description the application will navigate to the corresponding site in the background, wasting resources and throwing occasional out of place Javascript-error notifications.
I previously did most of this in code but i remodelled it to be more declarative and i would like to keep it that way, any help would be appreciated.
You could reconsider using Triggers and instead use a custom control and the Visual State Manager. Put the logic of downloading and caching and converting images in the custom control and simply set the appropriate states. It's hard to give an example without more xaml I'm afriad.

WPF Multiple Validation.ErrorTemplate s depending on the type of error

I am just starting out with WPF and im playing around mvvm and with styling.
I have a text box bound to my view model field FirstName.
FirstName is a required field and has a max length.
[Required(ErrorMessage = "Required")]
[StringLength(20, ErrorMessage = "Too long")]
public String FirstName
{
get { return firstName; }
set
{
firstName = value;
NotifyOfPropertyChange("FirstName");
NotifyOfPropertyChange("CanSave");
}
}
I have added some styles to show up when there is an error
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right"
Foreground="Red"
FontSize="14pt"
Margin="-15,0,0,0"
FontWeight="Bold">*</TextBlock>
<AdornedElementPlaceholder Name="controlWithError"/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="Background" Value="{StaticResource ErrorBackgroundBrush}"/>
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="{StaticResource TextControlBorderBrush}" />
</Trigger>
</Style.Triggers>
</Style>
So now i have a nicely formatted box that has a red background and a red asteriks when there is an error. When you start typing this is removed, and then when the length hits 20, and there is an error again, this turns back to the validationtemplate.
My question is, has anyone implemented different templates for different types of error.
What i would like to do is for every property that is required to have a red asteriks to begin with - no red background. Then when they have typed something in, and the required field error has gone, the asteriks should go.
But the other error validaiton should maybe show an exclamation mark, and a red background.
Anyone?

How to define a default tooltip style for all Controls

I would like to define a style with a template when there are validation errors and would display the first error message as a tooltip.
It works fine when targeting specific control like DatePicker in the following xaml.
<Style TargetType="{x:Type ToolKit:DatePicker}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
I cannot get it to work for Control though, i.e. the following doesn't give any tooltip
<Style TargetType="{x:Type ToolKit:Control}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Any idea?
I would recommend you create a Behavior for this one.
Every control is unique in itself and this is not a reliable way to attach a specific behavior to all controls. And in fact, you may end up setting this property on unwanted controls that don't even require validation.
Behaviors are clean and you can assign them to selected controls only. Attached Properties or a Master Behavior can be used to assign Behaviors to child controls.
Here is a CodeProject article from Josh Smith to get you started on Behaviors.

Resources