I am trying to use Validation in WPF. I created a NotNullOrEmptyValidationRule as shown below:
public class NotNullOrEmptyValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (String.IsNullOrEmpty(value as String))
return new ValidationResult(false, "Value cannot be null or empty");
return new ValidationResult(true, null);
}
}
Now, I need to use it in my application. In my App.xaml file I declared the Style for the TextBox. Here is the declaration.
<Style x:Key="textBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Green"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Now, I want to use it on my TextBox so I am using the following code:
<TextBox Style="{StaticResource textBoxStyle}">
<TextBox.Text>
<Binding>
<Binding.ValidationRules>
<NotNullOrEmptyValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
The error comes on the Tag NotNullOrEmptyValidationRule. The XAML syntax checker is not able to resolve the NotNullOrEmptyValidationRule. I have even tried putting the namespace but it does not seem to work.
You just need to add the xmlns to your Window, and use that to reference your ValidationRule.
In WPF, the object is perfectly fine to be used from the same assembly.
Since your rule isn't defined in the standard XAML namespace, you have to create a mapping to your clr namespace like so:
<Window ...
xmlns:local="clr-namespace:MyNamespaceName">
And then you would use it like so:
<Binding Path=".">
<Binding.ValidationRules>
<local:NotNullOrEmptyValidationRule />
</Binding.ValidationRules>
</Binding>
Edit
I added a Path statement to the Binding. You have to tell the Binding what to bind to :)
i see your binding on the TextBox is set to a path of 'Text' - is that a field on whatever the datacontext of this textbox is? is the textbox actually getting a value put into it? also, if you put a breakpoint in your validation method, is that ever getting fired?
you may want to lookup how to log failures in binding and review those as well..
You do not have this line in ur code behind
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
Me.**NameOfTextBox**.DataContext = Me
End Sub
There is a bug in Visual Studio and Expression Blend that causes this problem. What you need to do is make sure that the Validation rule is in a separately project/assembly that you can reference. This should resolve the problem.
However, you will have to add back the namespace in order for it to work.
Related
What I have is a custom window. Added a bool dependencyproperty. I want to use this dependency property as a condition for my triggers. A way to get around my triggers so to speak. Unfortunately I have propery non-null value exception being thrown. Banging my head with this one. I also tested the dependency property before the binding on the triggers. It never hits the dependency property wrapper. No errors thrown/shown when I do that.
DependencyProperty setup:
/// <summary>
/// The override visibility property
/// </summary>
public static readonly DependencyProperty OverrideVisibilityProperty = DependencyProperty.Register(
"OverrideVisibility", typeof(bool), typeof(MyWindow), new PropertyMetadata(false));
/// <summary>
/// Gets or sets the override visibility.
/// </summary>
/// <value>The override visibility.</value>
public bool OverrideVisibility
{
get
{
return (bool)this.GetValue(OverrideVisibilityProperty);
}
set
{
this.SetValue(OverrideVisibilityProperty, value);
}
}
Trigger setup in style
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="WindowStyle" Value="None" />
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=OverrideVisibility}" Value="false" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="WindowCloseButton" Property="Visibility" Value="Visible" />
</MultiTrigger.Setters>
</MultiTrigger>
</ControlTemplate.Triggers>
Form xaml Setup:
<local:MyWindow x:Class="MyForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="500"
Height="500"
OverrideVisibility="True">
Your error is on this line:
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=OverrideVisibility}" Value="false" />
Specifically, this is your error:
AncestorType={x:Type Window}
You probably have an error in your Output Window in Visual Studio that says something like:
Error: No OverrideVisibility property found on object Window
Instead of that Binding, use the name/type of your custom Window... something like this:
AncestorType={x:Type YourPrefix:YourCustomWindow}
Additionally, you said this:
It never hits the dependency property wrapper
It wouldn't... they are just for your use... they're not used by the Framework. If you want to monitor what values are going through a DependencyProperty, then you need to register a PropertyChangedCallback event handler. You can find out more from the Custom Dependency Properties page on MSDN.
UPDATE >>>
Ah, I just noticed the comments. You might still be able to do it if you can declare an Attached Property in an assembly that both your Style and your view have access to. If that's a possibility, then take a look at the Attached Properties Overview page on MSDN to find out how to do that.
Finally, you can bind to an Attached Property like this:
<animation Storyboard.TargetProperty="(ownerType.propertyName)" .../>
This example was from the Property Path Syntax page on MSDN.
Good Day,
I have a TextBlock element with a black background and text with a black foreground color. I do not want my users to see the text until a task is completed. Then the text will turn into a greenish color.
My style trigger in xaml looks like:
<Style x:Key="DataImportCompletedStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#FF000000" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsImportCompleted}" Value="True">
<Setter Property="Foreground" Value="#FF99F999" />
</DataTrigger>
</Style.Triggers>
</Style>
My TextBlock looks like:
<TextBlock x:Name="ImportStatusMesage"
Grid.Row="3"
Margin="5,0,5,10"
Background="Black"
FontSize="18"
Foreground="#FF000000"
Style="{StaticResource DataImportCompletedStyle}"
Text="Data Import Completed" />
And my code behind for the IsImportCompleted boolean property is:
private bool isImportCompleted;
public bool IsImportCompleted
{
get { return isImportCompleted; }
set
{
isImportCompleted = value;
System.Diagnostics.Debug.WriteLine("Import Process Completed...OnPropertyChanged");
OnPropertyChanged("IsImportCompleted");
}
}
which does implement INotifyPropertyChanged. The task works fine and updates the IsImportCompleted property as I am seeing my message in the Output window, but the text doesn't change color.
I thought by using INotifyProperty that the UI would update itself.
I'm using Snoop and verified that the IsImportCompleted is set to true. But still no text color change.
Any advice,
TIA,
coson
I am quoting from the comment of the Asker. which according to him, this solved his problem
Never mind, I figured it out. I'm setting the Foreground property in my XAML which will always override what I set in the trigger based on the precedence rules. Once I took out the Foreground property definition in my TextBlock tag, everything worked!
I want to change the state of a dependency property (Mode) in a UserControl triggered off of a change change of a second dependency property (ViewType).
I tried defining a DataTrigger as follows, but for some reason it doesn't work:
<UserControl.Resources>
<Style TargetType="{x:Type UserControls:MyUserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType}" Value="ViewType2">
<Setter Property="Mode" Value="Logon"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
To verify that my binding was working correctly I wrote the following test XAML in the same user control:
<Style x:Key="butstyle">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType}" Value="ViewType2">
<Setter Property="Control.Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
When I assigned this style to a button, I see the foreground color change to white at the right time, which proves my Binding to ViewType is working.
I seems I need to use a style under usercontrol.resources (like in the first block of code above), but the "Mode" property is never set.
Is this the right way to do this? I'd like to try to keep this in XAML if possible, but I'm not comfortable how triggers should work when they are not related directly to visual elements.
In case its relevant, the "ViewType" bound property is defined in a parent UserControl, and the "Mode" property is defined on MyUserControl.
Update:
I used a converter to find out that my trigger is being called, however my setter is not getting engaged for some reason. Here is the code for my DP which I am trying to set. Breakpoints on neitehr the set() nor the OnModeChanged() are called. Is there anything wrong with my Dependency Property?
public enum ModeStates { Logon, NextState }
public ModeStates Mode
{
get { return (ModeStates)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
protected static readonly DependencyProperty ModeProperty = DependencyProperty.Register(
"Mode", typeof(ModeStates), typeof(MyUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnModeChanged))
);
private static void OnModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// .......
}
Update:
I was able to use DependencyPropertyDescriptor.AddValueChanged() to solve my problem in a pretty clean fashion.
If the dependency properties in question are defined on the UserControl you need to set it's style property, not add a default style inside the resources.
to set the style inline in XAML
<UserControl.Style>
<Style TargetType="{x:Type UserControls:MyUserControl}">
<!-- Triggers -- >
</Style>
</UserControl.Style>
In addition to Adam's answer, are you setting the Mode property in the <UserControl> tag anywhere? If you do, it is overwriting the triggered value.
Also, on occasion I have had issues with a triggered value not getting set unless a default value is also defined in the style.
For example, sometimes this will not work
<UserControl.Style>
<Style TargetType="{x:Type UserControls:MyUserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType}" Value="ViewType2">
<Setter Property="Mode" Value="Logon"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
while this does
<UserControl.Style>
<Style TargetType="{x:Type UserControls:MyUserControl}">
<Setter Property="Mode" Value="{x:Null}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType}" Value="ViewType2">
<Setter Property="Mode" Value="Logon"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
I have the following singleton class which contains the following property:
public class Manager : INotifyPropertyChanged
{
public bool IsOnline
...
public static Manager Instance
...
}
In the mark-up I am trying to change the color of a button based on this online property:
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding Source="{x:Static storage:Manager.Instance}" Path="IsOnline"/>
</DataTrigger.Binding>
<Setter Property="Background" Value="#8000FF00"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
This binding <Binding Source="{x:Static storage:Manager.Instance}" Path="IsOnline"/> fails with the exception:
Cannot convert the value in attribute 'Source' to object of type 'System.Windows.Markup.StaticExtension'.
I have quadruple-checked the "storage" namespace; I know it is both referenced and correct. The Instance property is static, so I do not understand why this binding would fail. I have similar bindings to static properties all over that work just fine.
Any ideas?
I've built a sample app that does exactly what you're describing, it works with no issues. You can download it here.
Have you tried using a ValueConverter to inspect the value that the StaticExtension is getting?
(See method 2 on this page)
I just had exactly the same weird problem!
Solution:
You MUST instantiate the class before doing any XAML operations with that class!
I am building a WPF 4.0 Application using MVVM. The Model is generated using Entity Framework 4.0.
I am using Data binding on the WPF to bind the user input to model properties.
What is the easiest way to validate user input ?
I prefer an approach where I can set the validation rules on the Model rather than on the WPF itself. How can this be done? Any samples are appreciated.
The easiest way i found is taken from this book, pages 624-625.
The ViewModel should implement IDataErrorInfo
private string _newItem;
public string NewItem
{
get { return _newItem; }
set
{
if (Equals(_newItem, value)) return;
_newItem = value;
SendPropertyChanged("NewItem");
}
}
public string this[string propertyName]
{
get
{
if (propertyName == "NewItem")
{
var valid = NewItem.All(Char.IsLetterOrDigit);
if (!valid)
return "NewItem can only contain letters and numbers.";
}
return null;
}
}
And the view the long version:
<TextBox>
<TextBox.Text>
<Binding UpdateSourceTrigger="PropertyChanged" Path="NewItem">
<Binding.ValidationRules>
<DataErrorValidationRule></DataErrorValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Or the short version:
<TextBox Text="{Binding NewItem,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
It should create a nice red border around your textbox when rule fails, and you can play around with the error message the way you want, for example bind the error message to a textbox tool tip (MSDN):
<Window.Resources>
<Style x:Key="TextBoxInError" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
And then just add this to the textbox:
Style="{StaticResource TextBoxInError}"
Cheers!
The BookLibrary sample application of the WPF Application Framework (WAF) project shows a MVVM application. It uses the Entity Framework and defines the validation rules on the Model (Entity) classes.