Custom date format for textbox - c#

Related: Binding 3 textboxes together; same DateTime different format
I have three textboxes, all are supposed to be bound together with the same date. Two of them have normal string formats. The third one has a specific format of yyyy,jjj/HHmmss. I can't figure out how to bind this textbox to the custom format I have, and make it so if I change any of the date values in it, the other textboxes will update and vice versa.
private DateTime _dateInViewModel;
public DateTime DateInViewModel
{
get { return _dateInViewModel; }
set
{
_dateInViewModel = value;
NotifyPropertyChanged("DateInViewModel");
}
}
<TextBox Name="SDate1" Text="{Binding DateInViewModel, StringFormat='MM/dd/yyyy'}" />
<TextBox Name="SDate2" Text="{Binding DateInViewModel}" />
<TextBox Name="STime1" Text="{Binding DateInViewModel, StringFormat='hh:mm:ss'}" />
The custom format can be made like:
format = String.Format("{0},{1}/{2}",
DateInViewModel.Year,
DateInViewModel.DayOfYear.ToString("d3"),
DateInViewModel.ToString("HHmmss"));
Right now, only SDate1 and STime1 bind to each other properly and update when the other is changed.
I made a converter. It properly updates SDate2 when SDate1 and STime1 are changed, but doesn't work when editing SDate2 to update the others.
public class DateTimeConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
DateTime test = (DateTime)value;
string date = String.Format("{0},{1}/{2}",
test.Year,
test.DayOfYear.ToString("d3"),
test.ToString("HHmmss"));
return (date);
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}

You need to set the convert back in the converter. This is just an example but you need to parse the value back into original source so other binds can be updated.
since your format is {0},{1}/{2} then you need to split it back up and reconstruct the intended date.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return null;
string strValue = value.ToString();
if (string.IsNullOrEmpty(strValue) && targetType == typeof(DateTime?))
{
return null;
}
else if (string.IsNullOrEmpty(strValue))
{
return DateTime.MinValue;
}
//year,dayOfYear/Time(HHmmss)
var parts = strValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 2) {
var year = parts[0];
parts = parts[1].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 2) {
var days = parts[0];
var time = parts[1];
var date = new DateTime(int.Parse(year), 1, 1)
.AddDays(int.Parse(days))
.Add(TimeSpan.Parse(time));
return date;
}
}
DateTime resultDateTime;
return DateTime.TryParse(strValue, out resultDateTime) ? resultDateTime : value;
}

Related

Datepicker ValidationRules from code behind: validation rule not called on user input

I'm creating a wpf UserControl that contains a Datepicker. This datepicker is generated from code behind in c#.
public partial class EditorDatePicker : UserControl
{
public EditorDatePicker(TagEntry element, bool isTagPresent)
{
InitializeComponent();
// datepicker binding and validation
Binding binding = new Binding();
binding.Path = new PropertyPath("DateDict[" + element.ParentTag + element.ChildTag + "]");
binding.NotifyOnValidationError = true;
binding.ValidatesOnDataErrors = true;
binding.Converter = new DateTimeConverter();
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
binding.ValidationRules.Add(new DateValidationRule());
this.datePicker.SetBinding(DatePicker.SelectedDateProperty, binding);
}
class DateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
try
{
DateTime test = (DateTime)value;
string date = test.ToString("d/M/yyyy");
return (date);
}
catch
{
return null;
}
}
return null;
}
The fact is that the validation rule is never called when I manualy enter a date in the DatePicker text field (But it's called when using the datepicker). The only thing I got is a FormatException on lost focus.
Any idea? Thanx.
One possibility is to use converter:
public class DateTimeNullConverter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is DateTime)
return value.ToString();
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var text = value as string;
DateTime result;
if (text != null && DateTime.TryParse(text, out result))
return result;
return null;
}
}
You can use it like this to bind to public DateTime? DateTime property:
<TextBox Text="{Binding DateTime, Converter={local:DateTimeNullConverter}}" />
ConvertBack will be called on lost focus.

Check if Hex string is correct in Converter

I made a text box to input and show color Hex value.The binding is twoway to a color property of a parent.
Everything is working but, I need to make sure, in case I enter manually a Hex in the text box, and if this a not correct string, then use and display the current Hex value of the color, rather than trying to change it.
Here is what I tried but obviously it's not working, I'm a beginner and I have only a little experience with converter and WPF. If I write anything but not a valid Hex string, at the moment the textbox gets a red outline, but I wish that in this case, the Hex previous string reappears.
[ValueConversion(typeof(Color), typeof(String))]
public class ColorToStringConverter : IValueConverter
{
public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture)
{
Color colorValue = (Color)value;
return ColorNames.GetColorName(colorValue);
}
public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ColorHexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var hexCode = System.Convert.ToString(value);
//if (string.IsNullOrEmpty(hexCode))
// return null;
try
{
var color = (Color)ColorConverter.ConvertFromString(hexCode);
return color;
}
catch
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var hexCode = System.Convert.ToString(value);
Regex myRegex = new Regex("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
bool isValid = false;
if (string.IsNullOrEmpty(hexCode))
{
isValid = false;
}
else
{
isValid = myRegex.IsMatch(hexCode);
}
try
{
return hexCode;
}
catch
{
return null;
}
}
}
And the C# class for the TextBox
public class ColorHex : TextBox
{
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Enter)
{
BindingExpression bindingExpression = BindingOperations.GetBindingExpression(this, TextProperty);
if (bindingExpression != null)
bindingExpression.UpdateSource();
}
}
}
And its xaml in Generic.xaml
<local:ColorHex x:Name="PART_ColorHex" Style="{StaticResource ColorPickerTextBox}" Text="{Binding SelectedColor, Converter={StaticResource ColorToHexConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ColorPicker}}}" />
Any idea ?
thank you
A valid hex color has the form '#nnn' or '#nnnnnn'.
So, the regex for this case would be: (^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)
NOw, you could add these lines of code:
var regex = #"(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)"
var match = Regex.Match(inputYouWantToCheck, regex,RegexOptions.IgnoreCase);
if (!match.Success)
{
// Color is not valid
}
Hope this helps.
What if you did something where you add a label next to the textbox to show an example of what color has been entered. You would just need to change the label color change each time.
I'd do it differently, using property to validate the color:
public Color Color { get; set; } = Colors.Red;
public string ColorText
{
get { return (new ColorConverter()).ConvertToString(Color); }
set
{
Color = (Color)ColorConverter.ConvertFromString(value);
OnPropertyChanged();
}
}
Bind to ColorText, it will throw in case it's wrong hex, and you can use ExceptionValidationRule to display it (as a red border):
<TextBox Text="{local:ExceptionBinding Path=ColorText}" />
where
public class ExceptionBinding : Binding
{
public ExceptionBinding() : base()
{
ValidationRules.Add(new ExceptionValidationRule());
}
}
As a bonus you can enter known colors as text, e.g. "Red":

How to clear the date bound to a CalendarDatePicker if certain conditions occur in UWP?

I have a CalendarDatePicker whose Date property is bound to a converter.
I don't want it to show the date if the date is default(01-01-0001)
My code
class DateTimeToCalendarDateTimeOffsetConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
try
{
DateTime date = (DateTime)value;
return new DateTimeOffset(date);
}
catch (Exception ex)
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
try
{
DateTimeOffset dto = (DateTimeOffset)value;
return dto.DateTime;
}
catch (Exception ex)
{
return DateTime.MinValue;
}
}
}
But by default it set's todays date.
What value can I set to clear the date?
From the discussion we had earlier, I think you wanted to set the the Date of the CalendarDatePicker according to the value which is got from a server, but at first this value is set to "0001-01-01" by default.
So you can do it like this:
<Page.Resources>
<local:DateTimeToCalendarDateTimeOffsetConverter x:Key="cvt" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<CalendarDatePicker x:Name="picker" Date="{x:Bind Path=dateTime,Converter={StaticResource cvt}, Mode=TwoWay}" />
</Grid>
code behind:
private DateTime dateTime;
public MainPage()
{
this.InitializeComponent();
dateTime = new DateTime(0001, 01, 01);
}
This variable dateTime is fake by me and is set in the code behind, and the converter is like this:
public class DateTimeToCalendarDateTimeOffsetConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var date = (DateTime)value;
if (date == new DateTime(0001, 01, 01))
{
return null;
}
return new DateTimeOffset?(date);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
var dto = (DateTimeOffset)value;
return dto.DateTime;
}
}
But be aware that the Date is constrained by MinDate and MaxDate, if Date is smaller than MinDate, the value is set to MinDate. If the Date is greater than MaxDate, the value is set to MaxDate. So if you set the date here for example "0010-01-02", and your MinDate is set to "2000-01-01", when return this date in your Converter, the CalendarDatePicker will show "01/01/2000".
For more information about this control, you can refer to CalendarDatePicker class.

How to leave DateTimePicker object blank (Select a date) on load when DateTime is null (01/01/0001) but populate when there is a value?

I'm trying to get a DateTimePicker object to populate with the default text (Select a date) when it doesn't get any date back from the database. If there is a date in the database, the field will populate with that date.
I wroting code that has a two way bind on SelectedDate option to a DateTime property on in the code-behind. It works properly and populates the field with 01/01/0001 since that is the null of DateTime objects. I've tried to changing it to a OneWayToSource and just bind the date if it is greater than 01/01/0001 but it puts a redbox around the object if it doesn't get a date.
Any suggestion?
Thanks for the help everyone! Here is the solution that I found.
[ValueConversion(typeof(DateTime), typeof(DateTime))]
class DateTimeNullConverter: IValueConverter
{
public object Convert (object value, Type targetType, object parameter, Culture culture)
{
if (value != null)
{
DateTime dateTime = (DateTime)value;
if (dateTime.Year.ToString == "1")
return null;
else
return dateTime;
}
else
{
return null;
}
}
public object ConvertBack (object value, Type targetType, object parameter, Culture culture)
{
DateTime convertDateTime;
if (value == null)
{
convertDateTime = new DateTime();
}
else
{
convertDateTime = (DateTime) value;
}
return convertDateTime;
}
}
Create a DateBlankConverter converter that binds to the same control:
<DatePicker x:Name="DatePickerInstance"
Visibility="{Binding ElementName=DatePickerInstance,
Converter={StaticResource DateBlankConverter}, ConverterParameter={Binding Date}}"/>
And inside the converter check if the date is null to hide or show the DatePicker, or change the property you need.
You could try with some code in the setter and getter of your property
private DateTime? _date;
public DateTime? Date
{
get
{
if (null == _date)
{
//Set some initial value
//or just return some default value without setting the property
}
return _date;
}
set
{
if (value != _date)
{
_date = value;
this.OnPropertyChanged("Date");
}
}
}
Thanks for the help everyone! Here is the solution that I found.
[ValueConversion(typeof(DateTime), typeof(DateTime))]
class DateTimeNullConverter: IValueConverter
{
public object Convert (object value, Type targetType, object parameter, Culture culture)
{
if (value != null)
{
DateTime dateTime = (DateTime)value;
if (dateTime.Year.ToString == "1")
return null;
else
return dateTime;
}
else
{
return null;
}
}
public object ConvertBack (object value, Type targetType, object parameter, Culture culture)
{
DateTime convertDateTime;
if (value == null)
{
convertDateTime = new DateTime();
}
else
{
convertDateTime = (DateTime) value;
}
return convertDateTime;
}
}

An issue when TextBox is binding to double and enter negative number that little than -1

I have an issue when i binding a textbox to Propery and enter a negative number that is less than -1 - for example -0.45:
the textbox:
<TextBox Text="{Binding Txt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
and the property:
double txt;
public double Txt
{
get { return txt; }
set { txt = value; OnPropertyChanged("Txt"); }
}
it seems when i try to enter -0.54 it changes immediatly to 0, why?
Here is the convertor that does the job ( So leave your view model as it is- You can use it for both decimal and double). We just need to hold the decimal and -ve position initially:
public class DecimalConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value !=null)
{
return value.ToString();
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string data = value as string;
if (data == null)
{
return value;
}
if (data.Equals(string.Empty))
{
return 0;
}
if (!string.IsNullOrEmpty(data))
{
decimal result;
//Hold the value if ending with .
if (data.EndsWith(".") || data.Equals("-0"))
{
return Binding.DoNothing;
}
if (decimal.TryParse(data, out result))
{
return result;
}
}
return Binding.DoNothing;
}
}
So we hold the values or do nothing on binding
when you enter the decimal value it becomes 0 again so the best way to do this is using the lostfocus trigger:
<TextBox Text="{Binding Txt, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Grid.Row="0"/>
Also you need to do this in the view model:
public double Txt
{
get { return txt; }
set
{
if (!txt.Equals(value))
{
txt = value;
OnPropertyChanged("Txt");
}
}
}

Categories

Resources