I have tried to code this validation,
<ComboBox Name="CmbPlace" DisplayMemberPath="Name"
SelectedValuePath="PlaceId"
materialDesign:ComboBoxAssist.ShowSelectedItem="true"
Width="130" HorizontalContentAlignment="Right" Margin="5"
DropDownOpened="CmbPlace_OnDropDownOpened">
<ComboBox.Text>
<Binding Path="PlaceId">
<Binding.ValidationRules>
<validation:RequireCmbValidation ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</ComboBox.Text>
for TextBox and another component like this Validation Working as well only for ComboBox I got this Problem
this is my Validation and I know this will be Return ValidationResult.ValidResult successfully
public class RequireCmbValidation: ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (!string.IsNullOrWhiteSpace(value?.ToString()))
if (value.ToString() != "0")
return ValidationResult.ValidResult;
else
return new ValidationResult(false, "Please Select one Item");
return new ValidationResult(false, "Required!");
}
}
I solved my problem
Replace <ComboBox.Text> to <ComboBox.SelectedValue>
after 5 hours... but I hope this will help others...
I am trying to validate Date Picker using IDataErrorInfo
I want to check if selected date is a week ahead also if selected date is in the past
What I've done so far...
DateTime today = DateTime.Today;
DateTime seven = DateTime.Today.AddDays(+7);
DateTime dt = Convert.ToDateTime(DepartureDate);
if (columnName == "DepartureDate")
{
//range between today and seven days ahead +7
if (dt >= today && dt <= seven)
{
result = valid;
return result;
}
//can't be in the past
if (dt.Date == today)
{
result = "Can't be in the past";
return result;
}
if (dt.Date == today.Date)
{
result = valid;
return result;
}
}
.
<DatePicker Height="25" Validation.ErrorTemplate="{StaticResource eTemplate}" SelectedDate="{Binding StartDate}" HorizontalAlignment="Left" Margin="424,170,0,0" Name="DepartureDate" VerticalAlignment="Top" Width="119" FontSize="12">
<DatePicker.DisplayDate>
<Binding Path="DepartureDate" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged"/>
</DatePicker.DisplayDate>
</DatePicker>
Maybe you could do something like:
<DatePicker Width="100" Height="30" >
<DatePicker.SelectedDate>
<Binding Path="DepartureDate" Mode="OneWayToSource" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:MyValidationRule/>
</Binding.ValidationRules>
</Binding>
</DatePicker.SelectedDate>
</DatePicker>
public class MyValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
return new ValidationResult(false, "ErrorContent");
}
}
Implementing your logic in the Validate method.
There is also an event on the control you could hook called DateValidationError. https://msdn.microsoft.com/en-us/library/system.windows.controls.datepicker.datevalidationerror(v=vs.110).aspx
If you wanted to do code behind.
I'm working on a WPF project where I have a DataGrid bounded to a ObservableCollection. The values are being bounded properly but the issue that I am running into is that I can't edit the columns with double values. It will not let me insert a period into the cell.
This is what I have for the XAML:
<DataGrid Name="dataGrid" AutoGenerateColumns="False"
CanUserResizeColumns="True" CanUserAddRows="False" CanUserSortColumns="True" ItemsSource="{Binding}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
ColumnWidth="*" Margin="0,51,186,58"
RowEditEnding="dataGrid_RowEditEnding">
<DataGrid.Columns>
<DataGridTextColumn Header="Field 1" Binding="{Binding Field1, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Field 2" Binding="{Binding Field2, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Field 3" Binding="{Binding Field3, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Field 4" Binding="{Binding Field4, UpdateSourceTrigger=PropertyChanged}" />
<DataGridCheckBoxColumn Header="Field 5" Binding="{Binding Field5, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Field 6" Binding="{Binding Field6, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
<DataGrid>
And here is the class (Sorry for the weird class properties :/ I made it that way on purpose)
class FieldClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _field1;
public int Field1
{
get { return _field1; }
set
{
_field1 = value;
OnPropertyChanged("Field1");
}
}
private int _field2;
public int Field2
{
get { return _field2; }
set
{
_field2 = value;
OnPropertyChanged("Field2");
}
}
private double _field3;
public double Field3
{
get { return _field3; }
set
{
_field3 = value;
OnPropertyChanged("Field3");
}
}
private double _field4;
public double Field4
{
get { return _field4; }
set
{
_field4 = value;
OnPropertyChanged("Field4");
}
}
private bool _field5;
public bool Field5
{
get { return _field5; }
set
{
_field5 = value;
OnPropertyChanged("Field5");
}
}
private double _field6;
public double Field6
{
get { return _field6; }
set
{
_field6 = value;
OnPropertyChanged("Field6");
}
}
public FieldClass()
{
_field1 = 0;
_field2 = 0;
_field3 = 0.0;
_field4 = 0.0;
_field5 = false;
_field6 = 0.0;
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
How can I make it so on the DataGrid, if I wanted to update the value of a column (lets say i want to update Field3 or any field that has a double), I can insert a double value like 2.1?
Is a data template needed? Not to sure how to go about that, still a beginner.
Thanks for the help!
If you really want to obtain the behavior with PropertyChanged trigger you can try to use IsAsync=true of Binding, but I'm not sure that's the correct solution.
<DataGridTextColumn Header="Field 3" Binding="{Binding Field3, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:n\}, IsAsync=True}" />
I would say that the solution would be to change DataGridTextColumn to DataGridTemplateColumn and use NumericUpDown in its template. NumericUpDown is supposed to handle such cases better than TextBox.
Here is an example with Extended WPF Toolkit control (Spin buttons can be hidden if necessary for it to look like TextBox)
<DataGridTemplateColumn Header="Field1" Width="200">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<xctk:DoubleUpDown AllowSpin="False" ShowButtonSpinner="False"
BorderThickness="0"
CultureInfo="en-US"
Value="{Binding Field1, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
License allows non-commercial use.
As a quick fix I would simply add Delay property (in milliseconds) to DataGridTextColumn bindings. It makes binding to update only after that amount of time has elapsed since the user stopped typing.
<DataGridTextColumn Header="Field1"
Width="200"
Binding="{Binding Field1, Delay=500, UpdateSourceTrigger=PropertyChanged}"/>
It is reasonable enough for me: if I type some number with decimal separator, I type the separator and next digit fast enough to fit the Delay.
Delay is available since .NET Framework 4.5.
A summary of the article "Not an easy solution for a simple common problem. Binding a TextBox to a numeric property".
1) Description of the problem.
The main one solved in the topic is the problem with entering a decimal point in a TextBox bound to a non-integer property. It manifests itself when using the UpdateSourceTrigger = PropertyChanged binding in TwoWay mode.
In addition to a dot, it is also impossible to enter insignificant zeros.
While this is rarely needed for leading zeros (000123), trailing zeros (123.000) pose a problem.
Let's say entering the number 123.0004, with a trivial binding, will not work.
The second, incidentally solved and often required, task is to restrict the user to enter "only numbers".
It is solved in a more general form: by restricting the entry of only numerical values. Signs are allowed for signed types, decimal point for non-integer numbers, separators of digit groups are allowed in a given culture, signs of scientific presentation.
2) Causes of the problem. Internal logic implemented in Binding.
Consider what happens when you enter a number with a decimal point and non-significant zeros:
The number 123.000 is entered;
The assignment of the value to the source property (UpdateSource) is triggered;
For assignment, the string is converted to 123.0;
The number is transferred to Reflection;
Reflection assigns the passed number and calls the binding to update the target (UpdateTarget);
The binding converts the number 123.0 to its string representation. And here the point and insignificant zeros are discarded! The string "123" is obtained from the number 123.0;
This string is passed to the DependencyObject to be assigned to the Text property. BUT in the Text property there is a string "123.000". And since it is not equal to the string "123", the value "123" is assigned to the Text property - the point and zeros are gone!
In order for the assignment not to occur, it is necessary to compare the new value not just with the current one, but convert both values to the numeric type of the source property and compare these numbers.
But DependencyObject doesn't "know" anything about bindings. Doesn't even know if this property has bindings.
To solve the problem, you need to make a decision analyzing the current value of the Text property even before converting the number to a string. And if the same number can be obtained from Text, then the assignment should not occur.
3) Applying a multi converter to bind Double properties.
First, let's implement the most obvious solution through a multi-converter.
The algorithm of its work is very simple - it receives two values: binding to the source property and binding to the target property.
By comparing two values, it can return either a string representation of the number, or it can undo the assignment by returning Binding.DoNothing.
Converting a number to a string and back is carried out taking into account the culture transferred to the converter.
For debugging added output to Debug, and in the Control Window showing this output.
Two more validators have been added to the XAML in the binding to demonstrate type conversion.
Full code of MultiConverter:
using System;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Data;
namespace BindingStringToNumeric
{
/// <summary>Сравнивает полученное <see cref="Double"/> число со <see cref="String"/> текстом.<br/>
/// Если из текста получается такое же число, то присвоение значение по привязке отменяется.</summary>
/// <remarks>Значения должны приходить в массиве в параметре values метода <see cref="IMultiValueConverter.Convert(object[], Type, object, CultureInfo)"/><br/>
/// В массиве должо быть два значения:<br/>
/// 0 - значение источника в <see cref="Double"/> типе,<br/>
/// 1 - <see cref="String"/> Text с которым надо сравнить число.</remarks>
public class DoubleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Debug.Write(GetType() + $".Convert.values: {values[0]}, \"{values[1]}\"");
double source = (double)values[0];
string text = (string)values[1];
object ret;
// Получение из текста числа (в переданной культуре) и сравнение его с числом источника.
// Если они равны, то отменяется присвоение значения.
if (double.TryParse(text, NumberStyles.Any, culture, out double target) && target == source)
ret = Binding.DoNothing;
// Иначе число источника переводится в строку в заданнной культуре и возвращается.
else
ret = source.ToString(culture);
Debug.WriteLine($"; return: {ret ?? "null"}");
return ret; ;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
Debug.Write(GetType() + $".ConvertBack.value: \"{value}\" to ");
object ret = null;
string text = (string)value;
// Если строка пустая, то это считается эквивалентом нуля.
if (string.IsNullOrWhiteSpace(text))
ret = 0.0;
// Иначе проверяется возвожность перевода строки в число в заданной культуре.
// Если перевод возможен, то возвращается полученное число.
else if (double.TryParse(text, NumberStyles.Any, culture, out double target))
ret = target;
Debug.WriteLine($"return: {ret ?? "null"}");
// Если ret значение не присваивалось, то значит строка некорректна
// Тогда возвращается null, что вызывает ошибку валидации.
if (ret == null)
return null;
// Иначе возвращается массив с одним элементом: полученным числом.
return new object[] { ret };
}
}
}
Full XAML Windows:
<Window x:Class="AppBindingToNumeric.DoubleConverterWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AppBindingToNumeric"
xmlns:dgn="clr-namespace:WpfCustomControls.Diagnostics;assembly=WpfCustomControls"
xmlns:bnd="clr-namespace:BindingStringToNumeric;assembly=BindingToNumeric"
mc:Ignorable="d" FontSize="20"
Title="Example #2: Binding to Double Property with MultiConverter"
Height="450" Width="1000">
<FrameworkElement.Resources>
<bnd:DoubleConverter x:Key="DoubleConverter"/>
<local:Numbers x:Key="Numbers"/>
</FrameworkElement.Resources>
<FrameworkElement.DataContext>
<Binding Mode="OneWay" Source="{StaticResource Numbers}"/>
</FrameworkElement.DataContext>
<Grid Background="LightGreen">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<UniformGrid Background="LightBlue" Columns="2">
<TextBlock Text="TextBlock"/>
<TextBox Margin="5" Text="{Binding DoubleValue}" IsEnabled="False"/>
<TextBlock Text="BindingMode=TwoWay"/>
<TextBox x:Name="tbValidate" Margin="5">
<TextBox.Text>
<MultiBinding Converter="{StaticResource DoubleConverter}" UpdateSourceTrigger="PropertyChanged">
<MultiBinding.ValidationRules>
<local:DebugValidationRule Title="MultiBinding"/>
</MultiBinding.ValidationRules>
<Binding Path="DoubleValue">
<Binding.ValidationRules>
<local:DebugValidationRule Title="Binding"/>
</Binding.ValidationRules>
</Binding>
<Binding Path="Text" ElementName="tbValidate"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
<TextBlock Text="BindingMode=OneTime"/>
<TextBox x:Name="tbDogitsOnly" Margin="5">
<TextBox.Text>
<MultiBinding Converter="{StaticResource DoubleConverter}" UpdateSourceTrigger="PropertyChanged">
<Binding Path="DoubleValue"/>
<Binding Path="Text" ElementName="tbDogitsOnly" Mode="OneTime"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</UniformGrid>
<dgn:DebugBox Grid.Row="1" Margin="10" FontSize="18"
IsOutputsText="{Binding IsActive,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"/>
</Grid>
</Window>
Video test of the converter: https://youtu.be/TauKTs7279Y
4) A universal multiconverter for binding to a numerical property of any type.
For a full-fledged generic converter, we first need to create a generic method for obtaining a validating parser for any numeric type.
I solved this by creating parsers that return an object for each numeric type, a dictionary that stores these parsers, and a method that returns a parser by type.
Everything is implemented in a static class.
The logic is very simple, so there are no detailed comments. Only XML documentation tags are specified.
The source codes are posted on GitHub (link to the repository at the end of the answer), so I don't post them here.
Video test of the converter: https://youtu.be/0LFHlgxvQso
The big drawback of this solution is the difficulty of applying it in XAML. And an error in one of the bindings or in its parameter will cause the converter to work incorrectly.
To simplify the use, need to encapsulate this converter in a markup extension and implement the transfer of the old value of the Text property to the converter in the same place.
5) Extending the markup, including creating an attached property, a private converter and a private MultiBinding.
The entire code is over 700 lines long.
It has been published on GitHub and there is no point in republishing it here.
Here is just an example of its use:
<TextBox Margin="5" Text="{bnd:BindToNumeric DoubleValue}"/>
<TextBox Margin="5" Text="{bnd:BindToNumeric DecimalValue, IsNumericOnly=False}"/>
For reference, also the complete XAML of the тest Window:
<Window x:Class="AppBindingToNumeric.MarkupExtensionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AppBindingToNumeric"
xmlns:bnd="clr-namespace:BindingStringToNumeric;assembly=BindingToNumeric"
xmlns:dgn="clr-namespace:WpfCustomControls.Diagnostics;assembly=WpfCustomControls"
mc:Ignorable="d"
Title="Example #4: Binding to a Numeric Properties with the Markup Extension"
Height="450" Width="1000" FontSize="20">
<FrameworkElement.Resources>
<local:Numbers x:Key="Numbers" DoubleValue="123" DecimalValue="456" IntegerValue="799"/>
</FrameworkElement.Resources>
<FrameworkElement.DataContext>
<Binding Mode="OneWay" Source="{StaticResource Numbers}"/>
</FrameworkElement.DataContext>
<Grid Background="LightGreen">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<UniformGrid Background="LightBlue" Columns="3">
<TextBlock Text="To Double - Numeric Only"/>
<TextBox Margin="5" Text="{bnd:BindToNumeric DoubleValue}"/>
<TextBox Margin="5" Text="{Binding DoubleValue}" IsEnabled="False"/>
<TextBlock Text="To Decimal - Any Value"/>
<TextBox Margin="5" Text="{bnd:BindToNumeric DecimalValue, IsNumericOnly=False}"/>
<TextBox Margin="5" Text="{Binding DecimalValue}" IsEnabled="False"/>
<TextBlock Text="To Integer - Numeric Only"/>
<TextBox Margin="5" Text="{bnd:BindToNumeric IntegerValue}"/>
<TextBox Margin="5" Text="{Binding IntegerValue}" IsEnabled="False"/>
</UniformGrid>
<dgn:DebugBox Grid.Row="1" Margin="10" FontSize="18"
IsOutputsText="{Binding IsActive,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"/>
</Grid>
</Window>
Source codes for GitHub: https://github.com/EldHasp/CyberForumMyCycleRepos/tree/master/BindingToNumber
I am using a MultiBinding in two different points in my XAML. Here is the code:
<StatusBarItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="X " />
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource CoordinateToStringConverter}" TargetNullValue="-">
<Binding Path="ChartMouseX" />
<Binding Path="AxisSettingsViewModel.XAxisSettings.LabelFormat" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Text=" Y " />
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource CoordinateToStringConverter}" TargetNullValue="-">
<Binding Path="ChartMouseY" />
<Binding Path="AxisSettingsViewModel.YAxisSettings.LabelFormat" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</StatusBarItem>
I am facing a weird issue. The first MultiBinding works perfectly, but the second one is never called. If I comment out the first MultiBinding, the second one starts to work as expected.
Is this some kind of limitation in WPF? Or am I missing something about multibindings?
P.S: The RaisePropertyChanged is correctly invoked. However, in the second binding the converter does not get called at all.
EDIT
Here is the code of the Converter:
using System;
using System.Globalization;
using System.Windows.Data;
namespace LogViewer.Converters
{
public class CoordinateToStringConverter : IMultiValueConverter
{
#region IMultiValueConverter members
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values != null && values.Length == 2)
{
object value = values[0];
string format = values[1].ToString();
if (value is DateTime)
return ((DateTime)value).ToString(format);
if (value is TimeSpan)
return ((TimeSpan)value).ToString();
if (value is double)
return ((double)value).ToString(format);
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
#endregion
}
}
I have analyzed your issue. Which is working fine for me. Nothing wrong with the multibinding.
Have you checked the converter with breakpoint was it called two times. Otherwise your problem is in ChartMouseY and ChartMouseX.
I want to increase Progress-bar value based on two textbox's Text. I wrote this XAML but there is an error "Two-way binding requires path or xpath" when I do MultiBinding in ProgressBar.Value
<Window.Resources>
<local:Class1 x:Key="ConverterM"/>
</Window.Resources>
<TextBox Height="23" HorizontalAlignment="Left" Margin="157,59,0,0"
Name="textBox1" VerticalAlignment="Top" Width="120" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="157,108,0,0"
Name="textBox2" VerticalAlignment="Top" Width="120" />
<ProgressBar Height="24" HorizontalAlignment="Left" Margin="120,160,0,0"
Name="progressBar1" VerticalAlignment="Top" Width="243" >
<ProgressBar.Value>
<MultiBinding Converter="{StaticResource ConverterM}">
<Binding />
<Binding ElementName="textBox1" Path="Text" />
<Binding ElementName="textBox2" Path="Text" />
</MultiBinding>
</ProgressBar.Value>
</ProgressBar>
Value Converter:
public class Class1 : IMultiValueConverter
{
public object Convert(object[] values,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
if (values[1] != null && values[2]!=null)
{
if (((string)values[1]).Length==((string)values[2]).Length)
{
return 5.0;
}
}
else
{
return 0.0;
}
}
public object[] ConvertBack(object value,
Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I think that <Binding /> is not necessary. try to delete it and change indexes in converter.
Two-way binding requires path or xpath
This happens when you haven’t set the Path= on binding. By default WPF binding will take the Path= part by default.
To avoid this you need to give Path for each Binding you specify in MultiBinding. here in your case there was an empty binding which has no Path defined thats why you have experience with the above error.
I have came across the same issue but the accepted answer does not say what the error is, So thought of sharing this.