I have a DateTimePicker as follows:
<UserControl
...
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
...
>
<xctk:DateTimePicker Name="MyDatePicker"
Value="{Binding MyDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Format="Custom" FormatString="dd/MM/yyyy HH:mm:ss"
AutoCloseCalendar="True"/>
I'm using IDateErrorInfo on my data model to handle business logic errors; for example:
public class MyViewModel : IDataErrorInfo
{
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
string error = DataValid();
CanExecute = (error == string.Empty);
return error;
}
}
CanExecute is a property which manages whether the user can select to submit the data. This all works well, however, if I simply select the date and mash the keyboard (type random letters), the date is reset to 01/01/01. What I would like to happen is for the date to effectively remain unchanged (that is, as it was before I mashed the keyboard). However, I can't seem to find a place to handle the casting error which obviously is occurring when this happens.
How can I trap this?
(The DateTimePicker control is part of the WPF Extension Kit)
If you don't like the way the control handles errors you can handle errors yourself in a subclass, an example of this in my old question Wpf Datepicker Input modification
Related
I encountered a problem with the IDataErrorInfo Interface and a wizard I'm currently programming.
The intention of my programm is to ask some Inputs ( usually done with a barcode scanner) and depending on the inputs start a specific sequence.
This is working as intendet. To make sure to catch wrong scans all inputs are check with an event ( OnValueParseFailed) If this event is triggered my current textbox is focused and all text selected:
this.MyWizardViewModel.ValueParseFailed += (s, e) =>
{
switch (e.Parameter)
{
case "ProductionOrder":
this.TextBoxProduction.Focus();
this.TextBoxProduction.SelectAll();
break;
The Interface itself is included this way:
public string this[string name]
{
get
{
string result = null;
if ((name == "ProductionOrder") && (!string.IsNullOrEmpty(this.ProductionOrder)))
{
if (this.System.FirmwareVersion == 0)
result = Lang.Strings.WrongEntry;
}
Its working for the first run. But if the wizard is finished or aborted and run a second time without closing the app, no error message is shown.
The Reset simply returns the app to default values.
public void ResetApplikation()
{
this.System.Clear(); // reset System values
this.ProductionOrder = string.Empty;
this.BmsTypeCode = string.Empty;
this.CellStack1TypeCode = string.Empty;
this.CellClass1 = string.Empty;
this.CellStack2TypeCode = string.Empty;
this.CellClass2 = string.Empty;
this.IsSystemProgrammed = false;
this.IsSystemParameterized = false;
this.MyMachine.Abort(); // reset wizard state
}
While debugging I can see the Interface to be handeled correctly. But no error is displayed.
In XAML the binding is set TwoWay
<TextBox Name="TextBoxProduction" Grid.Row="2" Width="200" Margin="10"
Style="{StaticResource TextBoxNormal}" Loaded="TextBoxProduction_Loaded"
Text="{Binding Path=ProductionOrder, ValidatesOnDataErrors=True,
NotifyOnValidationError=True, Delay=100,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
I'm using MahApps but as the textbox class is based on the wpf textbox I doubt a bug in this element is the problem. Any suggestions would be great.
Thank you.
The Answer of Domysee helped me.
Implementing INotifyDataErrorInfo instead of IDataErrorInfo was a major change but it fixed the problem!
Is there a way to disable certain characters from being written into a wpf textbox without using code in the code behind file?
I have a few int fields that are bound to text boxes that I would like to limit to keys 0-9 only. If I enter anything else I do get the red validation error but that is not enough.
I recommend using IDataErrorInfo for WPF validation since WPF already understands how to use it, and its easy to implement.
You have to add the interface on your class, and the required methods will look like this:
#region IDataErrorInfo Members
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
if (columnName == "YourProperty")
{
int property = Convert.ToInt32(YourProperty);
if (property < 0 || property > 9)
return "The value must be between 0 and 9";
}
return string.Empty;
}
}
#endregion
Next, you need to set ValidatesOnDataErrors=True in your TextBox binding so it runs the validation whenever the property changes.
When something has gone wrong, it adds a red border on your control and the message you put on your validation:
You can read more about how to use the interface:
WPF: Validation made easy with IDataErrorInfo
Really simple WPF form data validation - how to?
In fact, this task is not possible enterily in xaml.
At some point, you need to write some code.
These are some links could be useful
http://blog.magnusmontin.net/2013/08/26/data-validation-in-wpf/
http://soumya.wordpress.com/2010/05/09/wpf-simplified-part-15-data-validation/
I've been working on an application in MVVM Light lately. I have a TextBox in my XAML bound to my UI. I'd like to validate any input and ensure that only numbers are entered. I've tried the following code:
My TextBox:
<TextBox TabIndex="1" Height="23" MinWidth="410" DockPanel.Dock="Left"
HorizontalAlignment="Left"
Text="{Binding Input, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsEnabled}"
AcceptsReturn="False"
local:FocusExtension.IsFocused="{Binding IsFocused}">
And in my ViewModel:
private string input;
public string Input
{
get { return this.input; }
set
{
decimal test;
if(decimal.TryParse(value, out test))
{
this.input = value;
}
else
{
this.input = "";
}
RaisePropertyChanged("Input");
}
}
This fails to update the UI. If I enter "B" and check the debugger, it runs through the setter, but fails to actually update the UI.
Curiously, if I set this.input = "TEST"; in the else block, the UI updates, but, if I attempt to set it to "", string.Empty, or the value of input before the validation, the UI fails to update.
Is this by design? Possibly a bug? Is there something I'm doing wrong?
Edit I mistakenly forgot to include RaisePropertyChanged in my example code. I've updated it. Raising it isn't the problem as I've watched the debugger run all the way through raising it and returning input via the getter.
Way you use strign type property and then convert to decimal, easier to change lik this:
public decimal Input
{
get { return this.input; }
set
{
this.input = value;
RaisePropertyChanged("Input");
}
}
And for validate use IDataErrorInfo (read more: http://blogs.msdn.com/b/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx)
What we have done is created a Custom Control, since we use it for a Currency Text Box. I warn you I have no validation that this is a good idea, or falls in line with MVVM model because all manipulation of the control are done in code behind.
In the control on the textbox we have an event on PreviewTextInput that does this
e.Handled = Functions.DoubleConverter(Convert.ToChar(e.Text), ((TextBox)sender).Text.Replace("$", ""));
Then for the function (which isnt perfect, I have a few issues with it still) is:
static public bool DoubleConverter(char c, string str)
{
if (!char.IsDigit(c))
{
if (c == '.' && (str.Contains('.')))
{
return true;
}
else if (c != '.')
{
return true;
}
}
return false;
}
Please use this as a reference, not exactly as is because it is a very crude implementation.
I have following XAML -
<TextBox Text="{Binding Path=NumberOfItems, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
<Button Command="{Binding Path=StartCommand}">Start</Button>
In ViewModel -
public string this[string columnName]
{
get
{
string result = null;
switch (columnName)
{
case "NumberOfItems":
if (this.NumberOfItems <= 0)
{
result = "Items required";
}
break;
}
return result;
}
}
Whenever, TextBox changes values the trigger works accordingly. But in following cases, It is not working -
When user presses delete button in keyboard after selecting whole text of textbox.
When user deletes the last character present in TextBox.
however ValidatesOnDataError is working.
How can I make it work when TextBox empties?
When user enters any invalid data, TextBox Style changes. I want this to be known in click of Start Button.
How can I make aware the Start button that TextBox has invalid data?
I am guessing you are trying to Bind a property of type integer to the text property of your textbox. Since integers cannot be null, empty string will not work in this case. You can use either nullable integer or you can create your own converter to handle empty string.
When user enters any invalid data, TextBox Style changes. I want this to be known in click of Start Button.
How can I make aware the Start button that TextBox has invalid data?
change your validation code to:
case "NumberOfItems":
if (this.NumberOfItems==null || this.NumberOfItems <= 0)
{
result = "Items required";
}
break;
You can check string.IsNullOrEmpty(this["NumberOfItems"]) within the CanExceuteStartCommand of your StartCommand
I have a WPF DataGrid with a single DataGridTextColumn.
It lets the user enter a variable number of dates (the dates themselves are wrapped in a class, for binding purposes).
But (using UpdateSourceTrigger=Default) when the user uses the Tab key to navigate away from the DataGrid, the cell he was editing doesn't seem to leave editing mode / get validated.
The entered value is shown in red, and the row header shows an exclamation mark that is framed by a dotted line.
Setting UpdateSourceTrigger to LostFocus fixes that - but when my code resets the underlying collection (to facilitate consecutive user input) to a single-item collection, the DataGrid only shows a single row. Despite CanUserAddRows="True" the new, empty row goes missing (if my code resets the collection by simply clearing it, only the column header is shown).
I tried to work around that by setting Mode to OneWayToSource - but then the DataGrid no longer shows the entered values.
If I set UpdateSourceTrigger to PropertyChanged, the validation happens to soon, resulting in a bad user experience (Dates should be formatted DD/MM/YYYY. But, as soon as the user enters "1/1", "01/01/2016" is shown. Using backspace to edit the year, causes the value to change to "01/01/0201", and so on)
There seems to be a solution (https://social.msdn.microsoft.com/Forums/vstudio/en-US/983b3b5b-7115-4821-b621-85b43578636e/how-can-i-end-a-datagrid-cell-edit-when-focus-switches-to-a-winforms-hosted-control?forum=wpf) that requires code-behind, but I'd much prefer a cleaner solution.
My code:
<DataGrid AutoGenerateColumns="False"CanUserAddRows="True" ItemsSource="{Binding CourseDatesParameterForRequest}">
<DataGrid.Columns>
<DataGridTextColumn Header="Datum" Binding="{Binding Date, ConverterCulture=nl-BE, Mode=TwoWay,
StringFormat=\{0:dd/MM/yyyy\}, UpdateSourceTrigger=Default, ValidatesOnDataErrors=True}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
private ObservableCollection<DateWrapper> _courseDatesParameterForRequest;
public ObservableCollection<DateWrapper> CourseDatesParameterForRequest
{
get { return _courseDatesParameterForRequest; }
set
{
if (value != _courseDatesParameterForRequest)
{
_courseDatesParameterForRequest = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(nameof(CourseDatesParameterForRequest)));
}
}
}
}
public class DateWrapper
{
public int ID { get; set; }
public DateTime Date { get; set; }
public DateWrapper(DateTime date)
{
Date = date;
}
public DateWrapper(){}
}
Thanks in advance!