WPF DatePicker - write custom datetime format - wpf

i have in my application DatePicker. I would like write standart DateTime format (e.g. 2016-10-18), select DateTime from calendar or write custom format (e.g. ddmmrrrr, ddmm, ddmmrr...)
I made DateTimeConverter:
class DateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime emptyDateTime;
if (DateTime.TryParse(value.ToString(), out emptyDateTime) && emptyDateTime == DateTime.MinValue)
return DateTime.Now;
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int valueNumber;
string valueStr = value.ToString().Replace(".", "");
if (!int.TryParse(valueStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out valueNumber))
return DateTime.Now;
if (valueStr.Length == 8) //format ddmmrrrr
{
DateTime emptyDateTime;
//for removing empty datetime (01.01.0001)
return DateTime.TryParse(value.ToString(), out emptyDateTime) && emptyDateTime == DateTime.MinValue
? (object) null
: new DateTime(valueNumber%10000, (valueNumber/10000)%100, valueNumber/1000000);
}
if (valueStr.Length == 6) //format ddmmrr
return new DateTime(2000 + valueNumber % 100, (valueNumber / 100) % 100, valueNumber / 10000);
if (valueStr.Length == 4) //format ddmm
return new DateTime(DateTime.Now.Year, valueNumber % 100, valueNumber / 100);
return DateTime.Now;
}
}
And this is my DatePicker:
<DatePicker Focusable="True" Grid.Column="4"
Text="{Binding Order.DateTime, Mode=TwoWay, Converter={StaticResource DateTimeConverter}}"
PreviewKeyDown="DatePicker_OnPreviewKeyDown"/>
This is property for binding:
class Order{
public DateTime DateTime { get; set; }
}
My problems are:
When DatePicker is null, value in Order.DateTime is 01.01.0001
When I write my format (ddmmrrrr), value in Order.DateTime is DateTime.Now
I dont see whats wrong. Thanks

your Converter checks the TryParse wrong. Change it to:
if (!DateTime.TryParse(value.ToString(), out emptyDateTime) || emptyDateTime == DateTime.MinValue)
return DateTime.Now;
return value;

Related

Invalid Cast Exception in MultiValueConverter Xamarin/WPF

While developing a MultiValueConverter I ran into an odd error.
I am getting an 'Invalid Cast Exception' on the line:
int frameSize = (int)values[0] ; // <-- thows InvalidCast Exception
I honestly cannot determine why.
public class SizeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length <3 || values[2] == null || values[1] == null || values[0] == null)
return 0;
int numItems = (int)values[2];
int separatorSize = (int)values[1];
int frameSize = (int)values[0] ; // <-- thows InvalidCast Exception
int totalSeparatorSize = (numItems - 1) * separatorSize;
int remainingArea = frameSize - totalSeparatorSize;
return remainingArea / numItems;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I verified the received values[3] array in the Converter in immediate Window:
`{object[3]}
[0]: 100
[1]: 2
[2]: 4`
Here is the calling code MSTest [TestClass]:
[TestClass]
public class TestConverters
{
[DataRow(100, 2, 4)]
[DataTestMethod]
public void Test_Piece_Sizing(double frameSize, int separatorSize, int numItems)
{
var sizeConverter = new SizeConverter();
object[] values = new object[] { frameSize, separatorSize, numItems };
Assert.AreNotEqual(0,sizeConverter.Convert(values, typeof(int), null, System.Globalization.CultureInfo.CurrentCulture));
}
}
==============================================================================
===================A N D T H E Repaired C O D E IS:========================
public class SizeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length <3 || values[2] == null || values[1] == null || values[0] == null)
return 0;
int numItems = (int)values[2];
int separatorSize = (int)values[1];
double frameSize = System.Convert.ToDouble(values[0]) ;
int totalSeparatorSize = (numItems - 1) * separatorSize;
int remainingArea = System.Convert.ToInt32(frameSize) - totalSeparatorSize;
return remainingArea / numItems;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
In the test case frameSize is a double, not an int. Either change it to int or cast to double in your Convert method.
frameSize is a double.just cast to double

is there a set of interfaces you can implement to avoid having a Converter in WPF binding

I have 2 classes, DateTime (the framework class) and FriendlyDateTime (somethign that implements I notify property changed.
the class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ToolSuite.Contract.BaseClasses;
namespace foo.WizardElements
{
public class FriendlyDateTime : NotifyPropertyChangedBase
{
public FriendlyDateTime()
{
}
public FriendlyDateTime(DateTime? value)
{
Date = value;
}
public DateTime? Date
{
get
{
if (base._values.ContainsKey("Date"))
return Get<DateTime>("Date");
else
return null;
}
set
{
if (value == null)
{
if (base._values.ContainsKey("Date"))
base._values.Remove("Date");
}
else
Set<DateTime>("Date", value.Value);
base.NotifyPropertyChanged("Date");
base.NotifyPropertyChanged("Hour");
base.NotifyPropertyChanged("Minute");
}
}
public int Hour
{
get { return Date.HasValue ? Date.Value.Hour : 0; }
set
{
if (Hour > 23)
Hour = 23;
var d = Date.HasValue ? Date.Value : DateTime.Now;
Date = new DateTime(d.Year, d.Month, d.Day, value, Minute, 0);
}
}
public int Minute
{
get { return Date.HasValue ? Date.Value.Minute : 0; }
set
{
if (Minute > 59)
Minute = 59;
var d = Date.HasValue ? Date.Value : DateTime.Now;
Date = new DateTime(d.Year, d.Month, d.Day, Hour, value, 0);
}
}
static public implicit operator DateTime?(FriendlyDateTime value)
{
return value.Date;
}
static public implicit operator FriendlyDateTime(DateTime? value)
{
// Note that because RomanNumeral is declared as a struct,
// calling new on the struct merely calls the constructor
// rather than allocating an object on the heap:
return new FriendlyDateTime(value);
}
}
}
the converter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Globalization;
using ToolSuite.Contract.BaseClasses;
namespace foo.WizardElements
{
[ValueConversion(typeof(FriendlyDateTime), typeof(DateTime?))]
public class FriendlyDateTimeValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(DateTime?))
{
return ((FriendlyDateTime)value).Date;
}
else if (targetType == typeof(FriendlyDateTime))
{
return new FriendlyDateTime(value as DateTime?);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(DateTime?))
{
return ((FriendlyDateTime)value).Date;
}
else if (targetType == typeof(FriendlyDateTime))
{
return new FriendlyDateTime(value as DateTime?);
}
return null;
}
}
}
Is there a set of interfaces I could implement that would amke WPF automatically use my converter w/o making the calling class aware.
so going from this
<we:DateTimeRangeElement Date="{Binding Path=Filter.EndTime, Mode=TwoWay, Converter={StaticResource DateTimeConverter}}" />
to this
<we:DateTimeRangeElement Date="{Binding Path=Filter.EndTime, Mode=TwoWay}" />
You can use the TypeConverter attribute on your class, passing in your implementation of IValueConverter as the argument.
For example:
[TypeConverter(typeof(FriendlyDateTimeValueConverter ))]
public class FriendlyDateTime
{
...
}
There is a good explanation at http://blogs.windowsclient.net/rob_relyea/archive/2008/04/10/strings-to-things-or-how-xaml-interprets-attribute-values.aspx.

Creating custom GroupDescription based on DateTime

I'm grouping some data and PropertyGroupDescription works fine most of the time. However if that property is a DateTime and i wan't to group several dates together as one group (like 30 days in each group or something) I would need a new GroupDescription. Problem is I have no idea how the class actually works and how I would design such a class.
I'm hoping to be able to inherit PropertyGroupDescription (instead of the basic abstract class) because this will also be based on a property but here I'm grouping based on a range of values instead of a single value == 1 group.
Any guide or even a ready class like this?
A bit late, but as you say yourself IValueConverter can be used for this - here's a simple converter I used once that will group by a friendly relative date string:
public class RelativeDateValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var v = value as DateTime?;
if(v == null) {
return value;
}
return Convert(v.Value);
}
public static string Convert(DateTime v)
{
var d = v.Date;
var today = DateTime.Today;
var diff = today - d;
if(diff.Days == 0) {
return "Today";
}
if(diff.Days == 1) {
return "Yesterday";
}
if(diff.Days < 7) {
return d.DayOfWeek.ToString();
}
if(diff.Days < 14) {
return "Last week";
}
if(d.Year == today.Year && d.Month == today.Month) {
return "This month";
}
var lastMonth = today.AddMonths(-1);
if(d.Year == lastMonth.Year && d.Month == lastMonth.Month) {
return "Last month";
}
if(d.Year == today.Year) {
return "This year";
}
return d.Year.ToString(culture);
}
public static int Compare(DateTime a, DateTime b)
{
return Convert(a) == Convert(b) ? 0 : a.CompareTo(b);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can then use it like this:
view.GroupDescriptions.Add(
new PropertyGroupDescription("Property",
new RelativeDateValueConverter()));

INotifyPropertyChanged does not update Value Converter

I have some properties which implement the INotifyPropertyChanged interface. It works fine. But in my code I also use some value converters (if value < 3 - make grid red, if value >3 and value < 10 - make grid blue, etc.).
The problem is how to refresh value converter after PropertyChanged was raised?
Is there simple code behind solution?
Thanks all and sorry for my bad English!
Here some code:
public class NotifyColors : INotifyPropertyChanged
{
private Color _TodayColor;
public Color TodayColor
{
get
{
return _TodayColor;
}
set
{
if (_TodayColor != value)
{
_TodayColor = value;
OnPropertyChanged("TodayColor");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
// it raised correctly when I change color with color picker control
}
}
}
// here is value converter
[ValueConversion(typeof(object), typeof(Brush))]
public class PositionToBackgroundConverter : IValueConverter
{
ModulePreferences ModulePrefs;
public PositionToBackgroundConverter(ModulePreferences ModulePrefs)
{
this.ModulePrefs = ModulePrefs;
}
#region IValueConverter Member
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (ModulePrefs.UseColoringByPosition)
{
try
{
if (value != null)
{
short value_short = (short)value;
if (value_short <= 3)
return (Brush)new SolidColorBrush(ModulePrefs.NotifyColorsObj._TodayColor); // here is changing property
else
return (Brush)new SolidColorBrush(ModulePrefs.NotifyColorsObj.T100PlusColor);
}
else
return Brushes.Transparent;
}
catch
{
return Brushes.Transparent;
}
}
else
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
And here I apply my value converter to the grid:
// assign backgroundconverter
var grid = new FrameworkElementFactory(typeof(Grid));
bin = new Binding();
bin.Path = new PropertyPath(string.Format("DataItem.{0}", LastPositionColumnName));
bin.Converter = new PositionToBackgroundConverter(ProjectViewObj.ModulePrefs);
grid.SetValue(Grid.BackgroundProperty, bin);
If you have done it "correctly" the PropertyChanged event will cause an update of the bindings which bind to that property, when this occurs any converters that are used in the same binding will reconvert the values. So normally the conversions happen on their own. If this is not the case you are probably using the converters "inappropriately", please post some code because without it it's quite impossible to tell what exactly you are doing wrong.
Edit: You use grid.SetValue(Grid.BackgroundProperty, bin), you should use grid.SetBinding(Grid.BackgroundProperty, bin) instead since it is a binding.
Edit2: This really has nothing to do with converters.
In your sample code you bind to IntValue, then you change TodayColor and expect the binding to be updated, not gonna happen. If you want the binding to react to both properties you have to either use a MultiBinding or raise the respective events since your properties are interdependent.
i.e.
private Color _TodayColor;
public short _IntValue;
public short IntValue
{
get { return _IntValue; }
set
{
if (_IntValue != value)
{
_IntValue = value;
OnPropertyChanged("IntValue");
OnPropertyChanged("TodayColor");
}
}
}
public Color TodayColor
{
get { return _TodayColor; }
set
{
if (_TodayColor != value)
{
_TodayColor = value;
OnPropertyChanged("TodayColor");
OnPropertyChanged("IntValue");
}
}
}

wpf dependency property enum collection

I have an enum listing all possible settings:
public enum Settings
{
Settings1,
Settings2,
Settings3
}
In my user control i want to implement a new depedency property that holds a list of settings and be able to use it like this:
<my:Control Settings="Settings1, Settings2" />
How should i implement this?
In your UserControl, make your Dependency property a collection of Settings (perhaps rename your enum to Setting), and then you can populate it in XAML with:
<my:Control>
<my:Control.Settings>
<x:Static Member="my:Setting.Setting1" />
<x:Static Member="my:Setting.Setting2" />
</my:Control.Settings>
</my:Control>
I haven't tested this :)
If you want to stick with a comma separated list, then make your UserControl Settings DP a string, and then on the property changed event handler, split the string and use Enum.Parse on each result to store the settings as your Setting enum type.
public class StringDelimitedToEnumListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<Settings> retval = new List<Settings>();
if(value == null || value.GetType() != typeof(string) || targetType != typeof(List<Settings>) )
{
throw new ArgumentException("value must be of type string, target type must be of type List<Settings>");
}
string workingValue = value.ToString();
if (String.IsNullOrEmpty(workingValue))
{
throw new ArgumentException("value must not be an empty string");
}
if (workingValue.Contains(','))
{
string[] tokens = workingValue.Split(',');
foreach (string s in tokens)
{
retval.Add((Settings)Enum.Parse(typeof(Settings), s));
}
return retval;
}
else
{
//there was only 1 value
retval.Add((Settings)Enum.Parse(typeof(Settings),workingValue);
return retval;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//do we care?
throw new NotImplementedException();
}
}

Resources