Error while adding validation rules class to binding - c#

I wanted to add validation rules to textBox ,the problem is that when I do the following code I got the following error message,
A value of type 'myValidations' cannot be added to a collection or dictionary of type 'Collection`1'.
The class myValidations roles is active.
What can be the problem?
<TextBox x:Name="Name" Grid.Column="4" Margin="0,50,0,0" Grid.Row="2" Style="{StaticResource tooltipError}">
<Binding ElementName="textBlock" Path="Text">
<Binding.ValidationRules>
<viewModel:MyValidationsRules/>
</Binding.ValidationRules>
</Binding>
</TextBox>
The text boxes are inherit from the following style:
<Style TargetType="TextBox">
<Setter Property="AcceptsReturn" Value="True"/>
<Setter Property="AllowDrop" Value="True"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Height" Value="44"/>
<Setter Property="Width" Value="199"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<EventSetter Event="PreviewDragEnter"
Handler="DropText_PreviewDragEnter"/>
<EventSetter Event="PreviewDrop"
Handler="DropText_PreviewDrop"/>
<EventSetter Event="PreviewDragOver"
</Style>

Not sure about your problem, but I think you have missed the <TextBox.Text> tag in your code.
<TextBox x:Name="Name" Grid.Column="4" Margin="0,50,0,0" Grid.Row="2" Style="{StaticResource tooltipError}">
<TextBox.Text>
<Binding ElementName="textBlock" Path="Text">
<Binding.ValidationRules>
<viewModel:MyValidationsRules ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>

I Think the problem is that your MyValidationsRules doesn't inherit of ValidationRule or your class MyValidationsRules not override the Validate method. This class and this method must be plubics.
public class MyValidationsRules : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
}
}

Related

WPF custom window round corners

I have a problem with my xaml style for my cutom window. The style sets the edges of the window to round and looks if it is docked to an edge. But it no longer works after changing the size of the window. On Start-up all edges are round, when I snap it to the right or the left of the screen then it shows the corners not rounded. But if I resize the window, it disables the round corners completely for the whole runtime.
How can I change my xaml code to get a behavoir that does not show rounded corners in snapped window style but if I resize my window?
The folowing code is my style code. You can set it to a window and resize the window how you like and you will see my problem with it?
<Style x:Key="ModernWindowStyle" TargetType="{x:Type Window}">
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ResizeMode" Value="CanResize"/>
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="MinHeight" Value="300"/>
<Setter Property="MinWidth" Value="530"/>
<Setter Property="MaxHeight" Value="{x:Static SystemParameters.MaximizedPrimaryScreenHeight}"/>
<Setter Property="MaxWidth" Value="{x:Static SystemParameters.MaximizedPrimaryScreenWidth}"/>
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome
CaptionHeight="50"
CornerRadius="8"
ResizeBorderThickness="5"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<Border BorderThickness="1"
BorderBrush="#FFD9D9D9">
<Border.Resources>
<core:MultiValueEqualityConverter x:Key="multiValueEqualityConverter" />
</Border.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="1" />
</Grid>
<Border.Style>
<Style TargetType="Border">
<Setter Property="CornerRadius"
Value="8" />
<Setter Property="Background" >
<Setter.Value>
<LinearGradientBrush EndPoint="0,0" StartPoint="0,1">
<GradientStop Color="#FF99BDCE" />
<GradientStop Color="White" Offset="0.7"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="False">
<Condition.Binding>
<MultiBinding Delay="1000" Converter="{StaticResource multiValueEqualityConverter}" ConverterParameter="HI">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Width"
Mode="OneWay" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="RestoreBounds.Width"
Mode="OneWay" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Value="False">
<Condition.Binding>
<MultiBinding Delay="1000"
Converter="{StaticResource multiValueEqualityConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Height"
Mode="OneWay" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="RestoreBounds.Height"
Mode="OneWay" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowState}"
Value="Normal" />
</MultiDataTrigger.Conditions>
<Setter Property="CornerRadius"
Value="0" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the MultiValueEqualityConverter
public class MultiValueEqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values?.All(o => o?.Equals(values[0]) == true) == true || values?.All(o => o == null) == true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I think the issue is RestoreBounds.Height or RestoreBounds.Width don't notify the value is changed, but Height or Width do.
So the MultiBinding "thinks" RestoreBounds was not changed and does use old values, what results to false.
To fix it just pass the window to the converter and check RestoreBounds there.
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="False">
<Condition.Binding>
<MultiBinding Delay="1000" Converter="{StaticResource multiValueEqualityConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Height"
Mode="OneWay" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Mode="OneWay" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Value="False">
<Condition.Binding>
<MultiBinding Delay="1000" Converter="{StaticResource multiValueEqualityConverter}" ConverterParameter="HI">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Width"
Mode="OneWay" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Mode="OneWay" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowState}"
Value="Normal" />
</MultiDataTrigger.Conditions>
<Setter Property="CornerRadius"
Value="0" />
</MultiDataTrigger>
</Style.Triggers>
public class MultiValueEqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[1] is System.Windows.Window wnd)
{
return wnd.Height == wnd.RestoreBounds.Height && wnd.Width == wnd.RestoreBounds.Width;
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

How to disable validation of a combox element when visibility is collapsed?

I want to disable the validation of a ComboBox or a TextBox when their visibility is collapsed. Can I realize this within the XAML code?
<ComboBox Name="XYZCb" ItemsSource="{Binding XYZ}" Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}">
<ComboBox.Text>
<Binding Path="xyz" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<valid:ValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</ComboBox.Text>
</ComboBox>
Button property binding:
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource InverseAndBooleansToBooleanConverter}"
Mode="TwoWay">
<Binding ElementName="XYZCb" Path="(Validation.HasError)"/>
</MultiBinding>
<Button.IsEnabled>
If I understand correctly what you need:
<Button>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=XYZCb}" Value="True"/>
<Condition Binding="{Binding Visibility, ElementName=XYZCb}" Value="Visible"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Don't forget to remove your <Button.IsEnabled> binding.

WPF MVVM change static resource style dynamically

I am new to WPF and here is what I am trying to do. I want to change a button's style, which is a static resource, based on whether a radio button is checked on not. Below is sample code:
<RadioButton Name="rbInfoCorrect"
GroupName="InfoQuestion"
cal:Message.Attach="ShouldProceed">
<RadioButton.Content>
<TextBlock Text="Yes" Foreground="White"/>
</RadioButton.Content>
</RadioButton>
<RadioButton Name="rbInfoNotCorrect"
GroupName="InfoQuestion"
cal:Message.Attach="ShouldStop">
<RadioButton.Content>
<TextBlock Text="No" Foreground="White"/>
</RadioButton.Content>
</RadioButton>
<Button x:Name="btnProceed" cal:Message.Attach="Proceed">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource WhiteButton}"/>
</Button.Style>
</Button>
<Button x:Name="btnStop" cal:Message.Attach="Stop">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource WhiteButton}"/>
</Button.Style>
</Button>
So when 'Yes' is checked, I want to change the 'Proceed' button style to BlueButton (another static resource) and when 'No' is checked, I want to change the 'Stop' button style to BlueButton. Of course this is part of a bigger problem so I just trying to be specific with the sample example.
The part I am stuck at is how to update the StaticResource from WhiteButton to BlueButton based on the radio button checked status. Any direction would be greatly appreciated.
Thanks.
You don't need to change the whole Style to do that. You can just add a simple DataTrigger to react directly to the Checkbox.IsChecked property. It's far simpler to declare the Styles like this instead:
<Style x:Key="ProceedButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rbInfoCorrect}"
Value="True">
<Setter Property="Background" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="StopButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rbInfoNotCorrect}"
Value="True">
<Setter Property="Background" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
Even if you want more properties to change, you can just declare the default ones as plain Setters and the ones triggered by the Checkbox.IsChecked property inside the DataTrigger. If you have lots of common properties in the two Styles, then you can still declare them all in one and base the other one on that one and just add the different DataTrigger.
We can replace the entire style using IMultiValue Converter.
For your requirement I came up with this example
public class StyleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
if (values.Length < 1)
return Binding.DoNothing;
bool isCorrect = (bool)values[0];
bool isNotCorrect = (bool)values[1];
Style firstStyle = (Style)values[2];
Style secondStyle = (Style)values[3];
if (isCorrect)
return firstStyle;
if (isNotCorrect)
return secondStyle;
return Binding.DoNothing;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
XAML
<Window x:Class="StackWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackWpf"
Title="MainWindow" Name="window" Height="350" Width="525" >
<Window.Resources>
<ResourceDictionary>
<Style TargetType="Button" x:Key="WhiteStyle">
<Setter Property="Background" Value="White"/>
</Style>
<Style TargetType="Button" x:Key="BlueStyle">
<Setter Property="Background" Value="Blue"/>
</Style>
<local:StyleConverter x:Key="styleConverter"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<RadioButton Name="rbInfoCorrect" IsChecked="False"
GroupName="InfoQuestion" Margin="80,19,382,257">
<RadioButton.Content>
<TextBlock Text="Yes" Foreground="Black"/>
</RadioButton.Content>
</RadioButton>
<RadioButton Name="rbInfoNotCorrect" IsChecked="False"
GroupName="InfoQuestion" Margin="80,38,391,257">
<RadioButton.Content>
<TextBlock Text="No" Foreground="Black"/>
</RadioButton.Content>
</RadioButton>
<Button Content="Button" Margin="80,114,294,161">
<Button.Style>
<MultiBinding Converter="{StaticResource styleConverter}">
<Binding ElementName="rbInfoCorrect"
Path="IsChecked" />
<Binding ElementName="rbInfoNotCorrect"
Path="IsChecked" />
<Binding Source="{StaticResource WhiteStyle}" />
<Binding Source="{StaticResource BlueStyle}" />
</MultiBinding>
</Button.Style>
</Button>
</Grid>
</Window>
I have used this good article http://social.msdn.microsoft.com/Forums/vstudio/en-US/b25973bb-9e9c-4a99-8234-39a042e0a478/apply-styles-dynamically-to-buttons-in-xaml?forum=wpf earlier to tackle my issue.
I'm not sure if you can replace the complete Style but you can user a DataTrigger to change some properties of the button:
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource WhiteButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=rbInfoCorrect, Path=IsChecked}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=rbInfoCorrect, Path=IsChecked}" Value="False">
<Setter Property="Background" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
Check out http://Globalizer.codeplex.com.
It switches out the entire style when it switches a language.
You basically look up the style in the resource dictionary, kill it and load a new one.

How to make a WPF listbox item not clickable

I am using a listbox to display a list of items that I am selecting programmatically in a voice activated program. Is there any way to keep the selected item from being clicked on? I do want mouse over functionality, just not clicking on the actual item.
I have tried to set Focusable (does not do anything for what I want) and IsEnabled (disables mouse over)
Here is my current style:
<Style x:Key="GlyphList" TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" IsEnabled="False"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate DataType="models:SpellingGlyph">
<Label VerticalAlignment="Bottom">
<Label.Template>
<ControlTemplate>
<Grid>
<TextBlock Name="MainText" Text="{Binding Text}" VerticalAlignment="Bottom" Margin="0,0,5,0"/>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ObjectReferenceEqualityConverter}">
<Binding />
<Binding Path="SelectedItem" RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBox}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="MainText" Property="Foreground" Value="#75BAFF"/>
<Setter TargetName="MainText" Property="FontWeight" Value="SemiBold"/>
<Setter TargetName="MainText" Property="FontSize" Value="22"/>
</DataTrigger>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MultiBooleanConverter}"> <!--SelectedItem trumps mouse over-->
<Binding ElementName="MainText" Path="IsMouseOver"/>
<Binding Path="SelectedItem" RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBox}" Converter="{StaticResource InvertedNullCheckToBooleanConverter}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="MainText" Property="Foreground" Value="#75BAFF"/>
<Setter TargetName="MainText" Property="FontWeight" Value="SemiBold"/>
<Setter TargetName="MainText" Property="FontSize" Value="22"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Label.Template>
</Label>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ListBox PreviewMouseDown="ListBox_OnPreviewMouseDown"..
private void ListBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
An alternative solution to what I accepted was to add the following in my view model:
if (_viewModelSetSelectedGlyph != value) return;
_selectedGlyph = value;
OnPropertyChanged("SelectedGlyph");
Then, set the _viewModelSetSelectedGlyph each time before actually setting the SelectedGlyph. Then, when the UI tries to set the SelectedGlyph, it would not succeed, only when the view model sets it by also setting the private variable does it succeed.
Simply set IsHitTestVisible to false
https://msdn.microsoft.com/en-us/library/system.windows.uielement.ishittestvisible(v=vs.110).aspx

Validating data templates for items in an itemscontrol

I have an ItemsControl (let's say a ListBox) and I have a DataTemplate for the contents. When I press a button, I want all ListBoxItems to be validated. This works.
Yet, although all items are properly validated and I can retrieve error messages for them, WPF does only show the ValidationError.Template for the SelectedItem of the ListBox in question. It does not display the ValidationError.Template for the other items which failed validation. I do update the source of the binding for every item and the Validation.HasError Property is set to true for them! Just the visuals are missing, the style is not being applied!
Has anyone got a solution for the problem?
Sample
A TextBox Style:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<AdornedElementPlaceholder Name="MyAdorner" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="{DynamicResource TextBoxBackgroundBrush}" />
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Background" Value="{DynamicResource TextBoxFocusBackgroundBrush}" />
</Trigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Background" Value="{DynamicResource ErrorBrush}" />
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
A DataTemplate for a Person Entity:
<DataTemplate DataType="{x:Type entities:Person}" x:Key="PersonItemStyle">
<Grid>
<TextBox x:Name="SomeTextBox">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validators:RequiredFieldValidationRule ErrorMessage="Please enter a name!" />
</Binding.ValidationRules/>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</DataTemplate>
Somewhere in some control:
<ListBox Grid.Row="1" x:Name="ListBoxPersons" Style="{DynamicResource ListBoxStyle}" ItemsSource="{Binding Path=Persons}"
ItemContainerStyle="{StaticResource PersonItemStyle}">
</ListBox>
Then try editing a few persons, for example set their names to null or use any erroneous binding. When you validate, the triggers for Validation.HasError will only be set for the selected item.
How can one work around that problem?
You are setting the ItemContainerStyle to a DataTemplate, why? The style is applied to all textboxes so you shouldn't have to set the ItemContainerStyle separately.

Categories