When clearing textbox on UI the converter doesn't trigger - wpf

When I'm adding value or changing it in a textbox, converter triggers every time. When I remove/clear value the converter never trigger. Also after saving empty filed it actually save the value it was there previously.
<TextBox Text="{Binding FemaleCT, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" PreviewTextInput="NumberValidationTextBox" VerticalContentAlignment="Center" TextAlignment="Center" Width="100">
<TextBox.Background>
<MultiBinding Converter="{StaticResource inToleranceHighlightsConverter}">
<Binding Path="FemaleCT"></Binding>
<Binding ElementName="MF" Path="Text"></Binding>
<Binding ElementName="MF_LowTolerance" Path="Text"></Binding>
<Binding ElementName="MF_HighTolerance" Path="Text"></Binding>
<Binding ElementName="SampleEnabled" Path="Text"></Binding>
</MultiBinding>
</TextBox.Background>
</TextBox>
This could be important, the data value FemaleCT is nullable decimal and is part of ObservableCollection.
public ObservableCollection<MouldCTModel> ctMeasurements
{
get { return _ctMeasurements; }
set { _ctMeasurements = value; this.RaisePropertyChanged(nameof(ctMeasurements)); CanSignScreen(); }
}
Is this normal behaviour or I'm doing something wrong?

Your problem is binding a decimal to a string property. string.empty cannot be converted to a decimal, so the binding fails and the property is not updated. Because the property doesn't change, the converter is not fired, and the object still has it's old value.
I find it best to wrap numbers, dates, etc in string properies:
private string femaleCT;
public string FemaleCT
{
get
{
return this.femaleCT;
}
set
{
this.femaleCT = value;
decimal temp;
if (decimal.TryParse(value, out temp))
{
this.model.FemaleCT = temp;
}
this.NotifyPropertyChanged();
}
}
and then write the converter to take a string, and it can decide what to do when the string is blank, or otherwise not a valid value.
This method also allows you to validate the string value. Validating the model isn't sufficient, because it is only updated when the value is a valid number.

Related

How do I detect if a property of type int is valid if the user deletes the value

How do I detect if a property of type int is valid if the user deletes the value.
My application has a textbox and a button. If the value in the textbox is valid, the button is shown, if not, the button is invisible. In the constructor, the button is set to invisible.
I'm using binding and my property is
private int _myProperty;
public int MyProperty
{
get { return _myProperty; }
set
{
//various logic about if the value parses validation (if else statements), if it fails the value is set back to 0, the visibility of the button is made invisible and the code returns at this point
this._myProperty = value;
OnPropertyChanged("MyProperty");
this.MyVis = System.Windows.Visibility.Visible;// it'valid
}
}
Now, if I type in something like "12A", the code inside the setter will simply detect it's not a valid string, set this._myProperty = 0 and then return (which is fine for my needs!)
The problem I have is, deleting! I am allowed to delete the content of the textbox but when I do, the code doesn't hit setter! Therefore the logic inside the property is never hit and my button's visibility remains untouched (So it could remain visible). And when I click thebuttonit shows the value 0 where as 0 is not what the user has entered (the textbox is null). I'd rather show my invalid numbers as -99 or null (usingint?but even changing my properties to typeint?` does not affect this behaviour)
I'm now beginning to think using integer with textboxes is a bad thing in WPF but, it's not realistic to show my integers as strings and do various casting etc (although I guess it would fix it, I don't consider this a real fix)
Not only that but my range goes from 0 to a very large number and as such, a combobox or a dial is not a suitable solution to the UX.
If I change MyProperty to type string, the setter value is set when I hit delete! But as it's a number I'd like to keep the property as type int. I don't know what I've done wrong.
This is a typical case for Commands.
Your XAML should look like:
<TextBox x:Name="SomeNumberTextBlock"/>
<Button Command="{Binding CmdToDoSomething, Mode=OneWay}" CommandParameter="{Binding ElementName=SomeNumberTextBlock, Path=Text, , UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
Your ViewModel:
#region command ToDoSomething
private DelegateCommand _cmdToDoSomething;
public DelegateCommand CmdToDoSomething {
get { return _cmdToDoSomething ?? (_cmdToDoSomething = new DelegateCommand(ToDoSomething, CanToDoSomething)); }
}
private void ToDoSomething(Object parameter) {
//button logic
}
private bool CanToDoSomething(Object parameter) {
int dummyInt;
return int.TryParse((String)parameter, out dummyInt);
}
#endregion
This will disable the Button when the input is not right.
If you really want to hide it:
<Button Command="{Binding CmdToDoSomething, Mode=OneWay}" CommandParameter="{Binding ElementName=SomeNumberTextBlock, Path=Text, Mode=OneWay}">
<Button.Style>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Button.Style>
</Button>
NB: I'm using DelegateCommand (you can found it here) but any type of Command will do.
One of possible solutions is create private field with type int and public property with type string. In setter you can convert string to int - and will know if your input string is not correct.
Another just prevent input incorrect character.
private int? _myProperty = null;
public string MyProperty
{
get { return _myProperty==null ? "" : _myProperty.ToString(); }
set
{
int number = 0;
if (Int32.TryParse(value, out number))
_myProperty = number;
else
{
// you can generate exception and
// use ExceptionValidationRule in Binding.ValidationRules
// in such case WPF will show red border around your textBox
_myProperty = null;
}
OnPropertyChanged("MyProperty");
// If you use MVVM than more properly will create converter
// {Binding MyProperty, Converter=StringToIsEnabledConverter}
// instead of dirrectly set visibility
this.MyVis = System.Windows.Visibility.Visible;// it'valid
}
}

WPF/MVVM: Notifying For Wrapped Model Properties In The ViewModel

This is a question about MVVM best practice.
Let's say I've got a pair of properties F0 & F1 in my Model, such that F0 follows F1 (based on some logic):
class Model
{
private float _f0;
public float F0
{
get { return _f0; }
private set
{
_f0 = value;
RaisePropertyChanged("F0"); //I know this isn't allowed - bear with me...
}
}
private float _f1;
public float F1
{
get { return _f1; }
set
{
_f1 = value;
RaisePropertyChanged("F1");
if(someLogic)
F0 = F1;
}
}
}
If I've got an instance of Model in my ViewModel, I could make this work by binding directly to F0 (OneWay) and F1 (TwoWay), i.e.:
<TextBox Text="{Binding Path=modelInstance.F0, Mode=OneWay}" IsReadOnly="True"/>
<Slider Value="{Binding Path=modelInstance.F1, Mode=TwoWay}" />
Of course, that's not MVVM. Better to wrap them & "flatten" the properties presented by the ModelView:
class ViewModel
{
public float Model_F0
{
get{ return modelInstance.F0; }
}
public float Model_F1
{
get{ return modelInstance.F1; }
set
{
modelInstance.F1 = value;
RaisePropertyChanged("Model_F1");
}
}
}
The binding then becomes:
<TextBox Text="{Binding Path=Model_F0, Mode=OneWay}" IsReadOnly="True"/>
<Slider Value="{Binding Path=Model_F1, Mode=TwoWay}" />
The obvious issue here is that the View won't know when F0 gets changed, as it's bound to Model_F0 (and not directly to F0). So my question is: what is the proper way to solve this? In the first case I "break" the MVVM pattern by binding directly to properties within the model; in the second case the behavior is incorrect.
Note that the above is a highly simplified example; in reality "F1" may affect more properties than just "F0" (or none at all). Thus explicitly raising Model_F0 in the Model_F1 setter isn't a workable solution; I'm looking for a more general way to handle such a scenario, where some of the Model's properties may affect others, and those properties need to be wrapped by the ViewModel for consumption by the View.

Identifying the changes between User and Programmatic in DataGridTextColumn in WPF

I would like to distinguish the change in DataGridTextColumn between progrmmatic and User changes.
<DataGridTextColumn Header="Position" Binding="{Binding Position, StringFormat=N2}" Width="100" IsReadOnly="False"/>
In the above column, if user modifies the value of the Position, I need to know to know the value.
I am looking for something similar to Ignoring text/value changes due to databinding
So Position is a public property.
Never set Position in your code. In you code change position (and then NotifyPropertyChanged). Any call to Position get you know came from the UI.
private sting position;
public sting Position
{
get { return position; }
set
{
if (position == value) return;
position = value;
}
}

Binding to double field with validation

I'm trying to bind TextBox to double property of some object with UpdateSourceTrigger=PropertyChanged. The goal is to immediately during editing validate entered value to be in allowed range (and display an error if not). I want to implement validation on Model level, i.e. via IDataErrorInfo.
All works great when I bind to int property, but if property is double then a frustrating editing behavior appears: after erasing last significant digit in fractional part of number - the decimal separator is automatically erased (with all possible fractional zeroes). For example, after erasing digit '3' from number '12.03' the text is changed to '12' instead of '12.0'.
Please, help.
Here is the sample code:
MainWindow.xaml:
<Window x:Class="BindWithValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="80" Width="200" WindowStartupLocation="CenterOwner">
<StackPanel>
<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
<TextBox.Style>
<Style TargetType="TextBox">
<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>
</TextBox.Style>
</TextBox>
</StackPanel>
</Window>
MainWindow.xaml.cs:
namespace BindWithValidation
{
public partial class MainWindow : Window
{
private UISimpleData _uiData = new UISimpleData();
public MainWindow()
{
InitializeComponent();
DataContext = _uiData;
}
}
}
UISimpleData.cs:
namespace BindWithValidation
{
public class UISimpleData : INotifyPropertyChanged, IDataErrorInfo
{
private double _doubleField = 12.03;
public double DoubleField
{
get
{
return _doubleField;
}
set
{
if (_doubleField == value)
return;
_doubleField = value;
RaisePropertyChanged("DoubleField");
}
}
public string this[string propertyName]
{
get
{
string validationResult = null;
switch (propertyName)
{
case "DoubleField":
{
if (DoubleField < 2 || DoubleField > 5)
validationResult = "DoubleField is out of range";
break;
}
default:
throw new ApplicationException("Unknown Property being validated on UIData");
}
return validationResult;
}
}
public string Error { get { return "not implemented"; } }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
if ( PropertyChanged != null )
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
The behavior of binding float values to a textbox has been changed
from .NET 4 to 4.5. With .NET 4.5 it is no longer possible to enter a
separator character (comma or dot) with ‘UpdateSourceTrigger =
PropertyChanged’ by default.
Microsoft says, this (is) intended
If you still want to use ‘UpdateSourceTrigger = PropertyChanged’, you
can force the .NET 4 behavior in your .NET 4.5 application by adding
the following line of code to the constructor of your App.xaml.cs:
public App()
{
System.Windows.FrameworkCompatibilityPreferences
.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
}
(Sebastian Lux - Copied verbatim from here)
I realize I'm a little late to the party but I found a (I think) rather clean solution to this problem.
A clever converter that remembers the last string converted to double and returns that if it exists should do everything you want.
Note that when the user changes the contents of the textbox, ConvertBack will store the string the user input, parse the string for a double, and pass that value to the view model. Immediately after, Convert is called to display the newly changed value. At this point, the stored string is not null and will be returned.
If the application instead of the user causes the double to change only Convert is called. This means that the cached string will be null and a standard ToString() will be called on the double.
In this way, the user avoids strange surprises when modifying the contents of the textbox but the application can still trigger a change.
public class DoubleToPersistantStringConverter : IValueConverter
{
private string lastConvertBackString;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is double)) return null;
var stringValue = lastConvertBackString ?? value.ToString();
lastConvertBackString = null;
return stringValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is string)) return null;
double result;
if (double.TryParse((string)value, out result))
{
lastConvertBackString = (string)value;
return result;
}
return null;
}
}
Tried formatting the value with decimal places?
It may be weird though since you will then always have N decimal places.
<TextBox.Text>
<Binding Path="DoubleField" StringFormat="{}{0:0.00}" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"/>
</TextBox.Text>
If having fixed decimal places is not good enough, you may have to write a converter that treats the value as a string and converts it back to a double.
The problem is that you are updating your property every time the value changes. When you change 12.03 to 12.0 it is rounded to 12.
You can see changes by providing delay by changing the TextBox in xaml like this
<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged,Delay=500, ValidatesOnDataErrors=True}">
but delay will notify and set the property after the delay time in mili sec.
Better use StringFormat like this
<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged,StringFormat=N2, ValidatesOnDataErrors=True}">
I have run into the same problem, and have found a quite simple solution: use a custom validator, which does not return "valid" when the Text ends in "." or "0":
double val = 0;
string tmp = value.ToString();
if (tmp.EndsWith(",") || tmp.EndsWith("0") || tmp.EndsWith("."))
{
return new ValidationResult(false, "Enter another digit, or delete the last one.");
}
else
{
return ValidationResult.ValidResult;
}
Try using StringFormat on your binding:
<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat='0.0'}">
Not sure if that string format is even right since I've not done one for a while but it's just an example

WPF DataBinding to Flag Enum (in a PropertyGrid)

I need to have the ability to select multiple values as is the nature of a Flag enumeration from a WPF view (all be it, in a PropertyGrid).
The properties in question are dynamic and no pre-defined DataTemplates can be used as the type of the properties will be discovered at runtime. (A DataTemplate which can detect if an enumeration is a Flag may prove helpful, but from my understanding I would need to know the Flag Enum types ahead of time to achieve this and that will not be the case).
I have tried out a number of proprietary and open source property grids for WPF and none seem to support 'Flags' attributed enum types out of the box.
A solution to this issue would be anything that would allow me to databind to + select multiple values for said Flags Enum for any commercial or open source WPF PropertyGrid.
Code:
Example PropertyType:
public class PropertyTypeOne
{
public PropertyTypeOne()
{
IntProp = 1;
InProp2 = 2;
BoolProp = true;
Boolprop2 = false;
StringProp = "string1";
DoubleProp = 2.3;
EnumProp = FlagEnumDataTYpe.MarketDepth;
}
public int IntProp { get; set; }
public int InProp2 { get; set; }
public bool BoolProp { get; set; }
public bool BoolProp2 { get; set; }
public string StringProp { get; set; }
public double DoubleProp { get; set; }
//This is the property in question
public FlagEnumDataType EnumProp { get; set; }
}
Example Flag Enumeration Type:
[Flags]
public enum FlagEnumDataType : byte
{
None = 0,
Trade = 1,
Quote = 2,
MarketDepth = 4,
All = 255
}
Note:
If the solution makes use of the Open Source WPF PropertyGrid (http://www.codeplex.com/wpg) I will implement the changes /additions back into the control.
Thanks.
I haven't found a truly elegant way of doing this, but from talking with the Developers at Mindscape and here's something crude but functional thats works with the Mindscape PropertyGrid.
First, we create a template for the flag-enum editor itself. This is an ItemsControl populated using the EnumValuesConverter from the WPF Property Grid library:
<ms:EnumValuesConverter x:Key="evc" />
<local:FlaggyConverter x:Key="fc" />
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}">
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Now we need to display the check boxes as checked according to whether the flag is on or off. This requires two things: first, an IMultiValueConverter so it can consider both the flag at hand and the context value, and second, a way for individual check boxes to read the context value. (By context value I mean the actual property value. E.g. the context value might be Flag1 | Flag4 | Flag32.) Here's the converter:
public class FlaggyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int flagValue = (int)values[0];
int propertyValue = (int)values[1];
return (flagValue & propertyValue) == flagValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
For propagating the context value, I'm going to take a shortcut and use Tag. You might prefer to create an attached property with a more meaningful name.
Now the control will display checks for the flags that are set, but won't yet update the value when you click a checkbox on or off. Unfortunately, the only way I've found to make this work is by handling the Checked and Unchecked events and setting the context value by hand. In order to do this, we need place the context value in a place where it can be updated from the check box event handlers. This means two-way binding a property of the check box to the context value. Again I'll use Tag though you may want something a bit cleaner; also, I'm going to use direct event handling, though depending on your design you may want to wrap this up into an attached behaviour (this would work particularly well if you were creating attached properties to carry around the context value).
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}" Tag="{Binding Value, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Tag="{Binding Tag, ElementName=ic, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource fc}" Mode="OneWay">
<Binding />
<Binding Path="Tag" ElementName="ic" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Note the two-way binding of the Tag: this is so that when we set Tag from our event handling code, it propagates back to ic.Tag, and from there to the property's Value.
The event handlers are mostly obvious but with one wrinkle:
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}" Tag="{Binding Value, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Tag="{Binding Tag, ElementName=ic, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource fc}" Mode="OneWay">
<Binding />
<Binding Path="Tag" ElementName="ic" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Event Handlers:
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox cb = (CheckBox)sender;
int val = (int)(cb.Tag);
int flag = (int)(cb.Content);
val = val | flag;
cb.Tag = (Curses)val;
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
CheckBox cb = (CheckBox)sender;
int val = (int)(cb.Tag);
int flag = (int)(cb.Content);
val = val & ~flag;
cb.Tag = (Curses)val;
}
Note the cast when setting cb.Tag. Without this, WPF internally fails to convert the value to the enum type when trying to propagate it back to the source. Here Curses is my enum type. If you want a fully flexible, type-agnostic editor, you'll want to provide this externally, for example as an attached property on the check box. You could either infer this using a converter or propagate it from an editor EditContext.
Finally we need to hook this up to the grid. You can do this either on a property-by-property basis:
<ms:PropertyGrid>
<ms:PropertyGrid.Editors>
<ms:PropertyEditor PropertyName="Curses" EditorTemplate="{StaticResource FlagEditorTemplate}" />
</ms:PropertyGrid.Editors>
</ms:PropertyGrid>
or by using a smart editor declaration to hook up all properties whose types have a FlagsAttribute. For info about creating and using smart editors, see http://www.mindscape.co.nz/blog/index.php/2008/04/30/smart-editor-declarations-in-the-wpf-property-grid/.
If you want to save space you can change the ItemsControl to a ComboBox though you'll need to do some additional work to handle the collapsed display; I haven't explored this in detail.
Found on the Internet, slightly improved, but didn't have time to test it yet.
/// <summary>
/// Two-way conversion from flags to bool and back using parameter as mask
/// Warning: The trick is in storing value locally between calls to Convert and ConvertBack
/// You must have a single instance of this converter per flags property per object
/// Do not share this converter between different objects or properties
/// Typical usage:
/// [Flags] enum FlagType { None = 0, Trade = 1, Quote = 2, Report = 4, All = 255 }
/// <local:EditableFlagsToBooleanConverter x:Key="FlagsToBooleanConverter" />
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Trade}}" >Trade</CheckBox>
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Quote}}" >Quote</CheckBox>
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Report}}" >Report</CheckBox>
/// </summary>
public class EditableFlagsToBooleanConverter : IValueConverter
{
private ulong _target;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is Enum && value is Enum)
{
var mask = (ulong) parameter;
_target = (ulong) value;
return ((mask & _target) != 0);
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool && parameter is Enum)
{
var mask = (ulong)parameter;
if ((bool)value)
{
_target |= mask;
}
else
{
_target &= ~mask;
}
return _target;
}
return Binding.DoNothing;
}
}

Resources