How to invoke DataTrigger on a Toolbar Button during Validate.HasError - wpf

I have implemented the following xaml code to disable a toolbar button when Validation.HasError based on the text entered into the ComboBox based on the UserAccountValidationRule
The core xaml code is shown here:
<telerik:RadComboBox
x:Name="DataProviderComboBox" Width="120" Height="23" IsEditable="True" DataContext="{StaticResource MainWindowViewModel}">
<telerik:RadComboBox.Text>
<Binding Mode="TwoWay" Path="InputString">
<Binding.ValidationRules>
<validationRules:UserAccountValidationRule/>
</Binding.ValidationRules>
</Binding>
</telerik:RadComboBox.Text>
</telerik:RadComboBox>
<telerik:RadButton
Name="ToolbarButton" Width="74" HorizontalAlignment="Left" VerticalAlignment="Top" Content="Button">
<telerik:RadButton.Style>
<Style TargetType="{x:Type telerik:RadButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding (Validation.HasError), ElementName=DataProviderComboBox}" Value="true" >
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadButton.Style>
</telerik:RadButton>
</telerik:RadToolBar>
<Grid>
<telerik:RadButton
Name="OrdinaryButton" Width="74" HorizontalAlignment="Left" VerticalAlignment="Top" Content="Button" Margin="252,26,0,0">
<telerik:RadButton.Style>
<Style TargetType="{x:Type telerik:RadButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding (Validation.HasError), ElementName=DataProviderComboBox}" Value="true" >
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadButton.Style>
</telerik:RadButton>
</Grid>
What I would like is for the DataTrigger in the ToolBarButton to be invoked, but it is not.
I did a check on the validation by creating a second OrdinarryButton, and hooked this up to the validation, and this worked fine. It seems as if the DataTrigger does not work if the button is a toolbar item.
Can anyone explain how to get this to work?

So, as it turns out, if the button is an OrdinaryButton control, then the DataTrigger will be invoked and the button will be disabled on Validation.HasError = true via the xaml code below:
<Grid>
<telerik:RadButton
Name="OrdinaryButton" Width="74" HorizontalAlignment="Left" VerticalAlignment="Top" Content="Button" Margin="252,26,0,0">
<telerik:RadButton.Style>
<Style TargetType="{x:Type telerik:RadButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding (Validation.HasError), ElementName=DataProviderComboBox}" Value="true" >
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadButton.Style>
</telerik:RadButton>
</Grid>
However, if the button control is placed inside a ToolbarButton, the same xaml code will not disable the Toolbar button:
<telerik:RadComboBox
x:Name="DataProviderComboBox" Width="120" Height="23" IsEditable="True" DataContext="{StaticResource MainWindowViewModel}">
<telerik:RadComboBox.Text>
<Binding Mode="TwoWay" Path="InputString">
<Binding.ValidationRules>
<validationRulesParameterPass:UserAccountValidationRule/>
</Binding.ValidationRules>
</Binding>
</telerik:RadComboBox.Text>
</telerik:RadComboBox>
<telerik:RadButton
Name="ToolbarButton" Width="74" HorizontalAlignment="Left" VerticalAlignment="Top" Content="Button">
<telerik:RadButton.Style>
<Style TargetType="{x:Type telerik:RadButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding (Validation.HasError), ElementName=DataProviderComboBox}" Value="true" >
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadButton.Style>
</telerik:RadButton>
</telerik:RadToolBar>
I find this very peculiar, and it might be a previously undiscovered bug? If anyone knows whether this is expected behavior or not, and has a reason for the observed behavior, please feel free to post.
In the meantime, I spent some time developing an acceptable workaround.
In this case I modified the <validationRules:UserAccountValidationRule/> to take a parameter that passes a single boolean IsEnabled property:
<Binding.ValidationRules>
<validationRulesParameterPass:UserAccountValidationRule>
<validationRulesParameterPass:UserAccountValidationRule.AccountValidationParameter>
<validationRulesParameterPass:AccountValidationParameter IsEnabled="{Binding IsButtonEnabled, Source={StaticResource MainWindowViewModel}}" />
</validationRulesParameterPass:UserAccountValidationRule.AccountValidationParameter>
</validationRulesParameterPass:UserAccountValidationRule>
</Binding.ValidationRules>
I also binded the IsButtonEnabled dependency property property to the ToolBar Button item's IsEnabled property.
Then, inside the UserAccountValidationRule, based on whether the value is valid or not, the IsButtonEnabled property will be set to true or false. As this DependencyProperty is connected to the Toolbar Button's, if the validation sets this true, the button's IsEnabled property will also be set to true, and vice versa.
public class UserAccountValidationRule : ValidationRule
{
private AccountValidationParameter _accountValidationParameter;
public UserAccountValidationRule()
{
}
public AccountValidationParameter AccountValidationParameter
{
get { return _accountValidationParameter; }
set { _accountValidationParameter = value; }
}
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if ( !string.IsNullOrEmpty(value as string) && value as string == "good")
{
AccountValidationParameter.IsEnabled = true;
return new ValidationResult(true, null);
}
else
{
AccountValidationParameter.IsEnabled = false;
return new ValidationResult(false, "bad string.");
}
}
}
Problem solved.

Related

Bind enum value to a property using style setter

I have the following code. Please look at Radio Button Style, I want to bind the value of the enum to a property in the ViewModel when the radio button is checked. I have property called SelectedWellType and I want to bind to it. But it says a binding cannot be set on Property of Type Property in Setter. Please help.
<RadioButton
Name="IndividualWellsRadioButton"
Grid.Row="0"
KeyboardNavigation.IsTabStop="True"
KeyboardNavigation.TabIndex="1"
KeyboardNavigation.AcceptsReturn="True"
Content="{StaticResource IndividualWells}"
IsEnabled="{Binding IsIndividualWellAutoAnalysisPossible}">
<RadioButton.IsChecked>
<Binding
Converter="{StaticResource enumToBoolConverter}"
ConverterParameter="{x:Static dataDef:AutoAnalysisModes.IndividualWells}"
Path="AutoAnalysisMode" />
</RadioButton.IsChecked>
<RadioButton.Style>
<Style
TargetType="{x:Type RadioButton}">
<Style.Triggers>
<Trigger
Property="IsChecked"
Value="True">
<Setter
Property="{Binding SelectedWellType}"
Value="{x:Static dataDef:AutoAnalysisModes.IndividualWells}"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
</RadioButton>

WPF Binding to a RadioButton GroupName

I have a dynamic ListView using a DataTemplate as follows:
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="#ccc">
<RadioButton GroupName="MyRadioButtonGroup">
<StackPanel Orientation="Horizontal" Margin="0,0,0,5" Width="{Binding ActualWidth, ElementName=CheckoutGrid}">
<TextBlock Text="{Binding Path=Test1, StringFormat='{}{0} - '}" />
<TextBlock Text="{Binding Path=Test2, StringFormat='{} test {0} - '}" />
<TextBlock Text="{Binding Path=Test, StringFormat='{} test {0}'}" />
</StackPanel>
</RadioButton>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
I would like to have a button disabled until one of the RadioButton IsChecked:
<RadioButton Padding="15,10" VerticalAlignment="Center">
<RadioButton.Style>
<Style TargetType="RadioButton" BasedOn="{StaticResource styleToggleButton4}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyRadioButtonGroup, Path=IsChecked}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
<TextBlock FontWeight="SemiBold" FontSize="14" Margin="0">NEXT</TextBlock>
</RadioButton>
So the problem is I do not know how to properly bind to the RadioButton GroupName="MyRadioButtonGroup". You will see in the DataTrigger above I am trying Binding="{Binding ElementName=MyRadioButtonGroup, Path=IsChecked}" Value="False", but that is not working for me since obviously it is a GroupName and not an x:Name.
Any suggestions on how to approach this properly? I would rather want to handle this on the front-end if at all possible.
You could bind your radio button to a command and then use the isChecked as a parameter. Then bind to a boolean value based off that. I think this may be more of a workaround though.
<RadioButton
Command="{Binding MyCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}"
GroupName="radio" />
<RadioButton
Command="{Binding MyCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}"
GroupName="radio" />
Then in code behind or VM, it would require implementing the ICommand Interface and INotifyPropertyChanged
public bool IsChecked
{
get { return isChecked;}
set
{
isChecked = value;
OnPropertyChanged();
}
}
private void OnMyCommand(object obj)
{
if(obj is bool)
{
IsChecked = (bool)obj;
}
}
A group has no IsChecked property that you can bind to.
If you don't have a source property that keeps track of whether there is at least one checked RadioButton and "want to handle this on the front-end" - which I guess means in the view - you could write some code that simply sets the Tag property of the ListView to true/false depending on whether there is a RadioButton selected. It is a one-liner:
private void ListView_Checked(object sender, RoutedEventArgs e) => lv.Tag = true;
<ListView x:Name="lv" ToggleButton.Checked="ListView_Checked">
<ListView.ItemTemplate>
...
<ListView.ItemTemplate>
</ListView>
<RadioButton Padding="15,10" VerticalAlignment="Center">
<RadioButton.Style>
<Style TargetType="RadioButton" BasedOn="{StaticResource styleToggleButton4}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=lv, Path=Tag}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
<TextBlock FontWeight="SemiBold" FontSize="14" Margin="0">NEXT</TextBlock>
</RadioButton>

WPF, Validation.ErrorTemplate and Windows Phone

I'm develepoing a Windows Phone application, and I need to validate some user inputs in text boxs. Here the XAML of one of these textbox:
<TextBox
Name="times"
Grid.Row="1"
Height="80"
Text="{Binding UpdateSourceTrigger=Explicit,
Mode=TwoWay,
Path=orari,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnValidationError=true}"
TextChanged="TextBoxChangedHandler"
/>
Using BreakPoints I'm sure the IDataError finds the error, but the TextBox appereance doesn't change. I've read I should use Validate.ErrorTemplate in the XAML, but I don't find this option, pheraps it doesn't exist in Windows Phone? How can I do to change the style of the textbox if the input is not valid?
Thanks
Hard to tell from what you have posted, but here's an example of some of my code that does something very similar perhaps it can help you locate your error.
The style to be used by textboxes I want to be validated, gets a red box when an error has occurred.
<Style x:Key="ValidationTextBox" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<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>
The text box itself
<TextBox Style="{StaticResource ValidationTextBox}">
<TextBox.Text>
<Binding Path="Description" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<rules:MandatoryInputRule ValidatesOnTargetUpdated="True" />
<rules:IllegalCharsRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
An example validation rule
class IllegalCharsRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value != null)
{
string input = value as string;
if (input.Contains(",") || input.Contains("/") || input.Contains(#"\") || input.Contains(".") || input.Contains("\"") || input.Contains("'"))
return new ValidationResult(false, "Validation error. Illegal characters.");
}
return new ValidationResult(true, null);
}
}

multiple binding to IsEnable

I need to bind a TextBox that meets two criteria:
IsEnabled if Text.Length > 0
IsEnabled if user.IsEnabled
Where user.IsEnabled is pulled from a data source. I was wondering if anyone had a easy method for doing this.
Here is the XAML:
<ContentControl IsEnabled="{Binding Path=Enabled, Source={StaticResource UserInfo}}">
<TextBox DataContext="{DynamicResource UserInfo}" Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding Path=Text, RelativeSource={RelativeSource Self}, Converter={StaticResource LengthToBool}}"/>
</ContentControl>
As GazTheDestroyer said you can use MultiBinding.
You can also acomplish this with XAML-only solution using MultiDataTrigger
But you should switch the conditions cause triggers support only equality
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text.Length}" Value="0" />
<Condition Binding="{Binding Source=... Path=IsEnabled}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False" />
</MultiDataTrigger>
</Style.Triggers>
If one of the condition is not met the value be set to its default or value from the style. But do not set local value as it overrides style's and trigger's values.
Since you only need a logical OR, you just need two Triggers to your each of the properties.
Try this XAML:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=InputText, Path=Text}" Value="" >
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=MyIsEnabled}" Value="False" >
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<Label>MyIsEnabled</Label>
<CheckBox IsChecked="{Binding Path=MyIsEnabled}" />
</StackPanel>
<TextBox Name="InputText">A block of text.</TextBox>
<Button Name="TheButton" Content="A big button.">
</Button>
</StackPanel>
I set DataContext to the Window class which has a DependencyProperty called MyIsEnabled. Obviously you would have to modify for your particular DataContext.
Here is the relevant code-behind:
public bool MyIsEnabled
{
get { return (bool)GetValue(IsEnabledProperty); }
set { SetValue(IsEnabledProperty, value); }
}
public static readonly DependencyProperty MyIsEnabledProperty =
DependencyProperty.Register("MyIsEnabled", typeof(bool), typeof(MainWindow), new UIPropertyMetadata(true));
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
Hope that helps!
Bind IsEnabled using a MultiBinding

WPF TextBox setting Text using Trigger during validation

I am having a requirement where in I have to revert the values of a TextBox to old value when the user enters a wrong input. I am using MVVM framework so I dont want to write any codebehind. The Text and Tag of TextBox is databound from ViewModel variable. So my Tag field of TextBox will always have old value. I want to use the Tag field value to revert my Text value.
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right"
Foreground="Orange"
FontSize="12pt">
</TextBlock>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true" >
<Setter Property="ToolTip"
Value="{Binding Path=Tag,RelativeSource={RelativeSource Self}}">
</Setter>
<Setter Property="Text"
Value="{Binding Path=Tag,RelativeSource={RelativeSource Self}}">
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<TextBox Width="68" Tag="{Binding SampleText}" Height="23" HorizontalAlignment="Left" Margin="39,37,0,0" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="SampleText" NotifyOnValidationError="True" ValidatesOnDataErrors="True" ValidatesOnExceptions="True">
<Binding.ValidationRules>
<val:SampleTextValidator></val:SampleTextValidator>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Now When an error happens, the TextBox is highlighted red.I have written a Trigger to revert the value back to original value (value stored in Tag field). Tt is not working. But Tooltip part is working. I am confused fully. Please help where am I doing wrong!!!. Correct me with a sample code if possible!!!!
My first guess is that when you made your text input invalid (eg. delete all values), you cause the tag to bind to the same value, hence, it will reflect an empty string.
What you need is a separate property for your original value to bind your tag to.
private string _oldValue;
public string OldValue
{
get {...}
set {... NotifyPropertyChanged()...}
}
private string _sampleText;
public string SampleText
{
get { return _sampleText; }
set {
OldValue = _sampleText;
_sampleText = value;
NotifyPropertyChanged(...);
}
}
<TextBox Width="68" Tag="{Binding OldValue}" ... >
Don't forget to implement INotifyPropertyChanged.

Resources