Hello,
I am having issues with DateTime editor in the grid. We have custom date/time format for our dates - "HH:mm:ss dd-MMM-yyyy" and the datetime editor is set to free form date time with this mask. When user typed a value in the editor it is passed to FreeFormDateTimeProvider.Validate method, which calls DateInput.ParseDate. DateTimeLexer splits this kind of values just fine, but obviously time related tokens comes first and then the date related tokens.
Unfortunately DateTimeParser.Parse method is made the way that it parse date at first and then time from the tokens list. In my case this means time value is parsed and the date value is dropped to default. Why didn't you check that if after ParseDate and ParseTime calls the date portion is null but there are still remaining tokens in the list, so you may call ParseDate one more time?
Do you have any suggestion how could I resolve this issue? Everything related to date time parsing is not extendable at all, starting from DateInput, most of the classes and methods are not public and even some public methods are not virtual. Can I have the Telerik free form date time typing capabilities in the editor but still have time part before date part?
Thanks Dess, I already did a similar workaround as you could imagine from my previous comment :)
In my CustomFreeFormDateTimeProvider I am calling my own Parse method (from Validate) which looks like this
private DateTime? Parse(string input)
{
if (input == null)
return null;
DateTime? result;
var regex = new Regex(@"^(?<group1>[^\s]+)\s+(?<group2>[^\s]+)$");
var match = regex.Match(input.Trim());
if (match.Success)
{
var part1 = match.Groups["group1"].Value;
var part2 = match.Groups["group2"].Value;
var part1Val = Parser.ParseDate(part1, DateTime.MinValue);
var part2Val = Parser.ParseDate(part2, DateTime.MinValue);
if (part1Val == null || part2Val == null)
{
result = part1Val ?? part2Val;
}
else
{
if (part1.Contains(Parser.CultureInfo.DateTimeFormat.TimeSeparator))
{
result = new DateTime(part2Val.Value.Year, part2Val.Value.Month, part2Val.Value.Day,
part1Val.Value.Hour, part1Val.Value.Minute, part1Val.Value.Second);
}
else
{
result = new DateTime(part1Val.Value.Year, part1Val.Value.Month, part1Val.Value.Day,
part2Val.Value.Hour, part2Val.Value.Minute, part2Val.Value.Second);
}
}
}
else
{
result = Parser.ParseDate(input, new DateTime?());
}
return result;
}
I still have to allow user to enter date time value in any format with time before date (as it is shown by default in the cell editor) or with date before time, etc. So this free format parsing is just a saver to me and I want to keep it as much as possible.
So my idea, as you may see above, was to split typed value by whitespace into 2 parts (time and date) and to parse them independently using your default methods. But as I "cannot" predict which part is time and which is date I thought that I may temporary set the format to only time and parse both of them as time with DateTime.Min date as a base date. Like this:
Parser.DateFormat = "T";
var part1Val = Parser.ParseDate(part1, DateTime.MinValue);
var part2Val = Parser.ParseDate(part2, DateTime.MinValue);
then check which of the values have DateTime.MinValue as the date, meaning that this part was the time and I may take it from there. And then return Parser.DateFormat back and parse the other part, which will be just date.
But as I wrote in my previous (a bit emotional) comment, you just ignoring the value passed as baseDate and returning Now + 2 hours (this I cannot understand at all, how it will work if I am at 23:00, will it return next day as date?).
So I cannot check which of the returned values have DateTime.MinValue as date because both will not have it. So to detect which part is time and which is date, I ended up by checking for the time separator in the string as you may see above. Unfortunately this is not fully reliable because it will not work if user typed only e.g. hour like "12 11-May-2019". But I wouldn't expect this case to be frequent so this workaround should work for me. Especially with the quite old version of the Telerik libraries I am using, because I will not able to get any of your fixes even if you will upload them tomorrow.
public
RadForm1()
{
InitializeComponent();
GridViewDateTimeColumn dateTimeColumn =
new
GridViewDateTimeColumn(
"DateTimeColumn"
);
dateTimeColumn.FormatString =
"{0:HH:mm:ss dd-MMM-yyyy}"
;
this
.radGridView1.Columns.Add(dateTimeColumn);
this
.radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
this
.radGridView1.Rows.Add(DateTime.Now);
this
.radGridView1.CellEditorInitialized += radGridView1_CellEditorInitialized;
}
private
void
radGridView1_CellEditorInitialized(
object
sender, GridViewCellEventArgs e)
{
RadDateTimeEditor dtEditor = e.ActiveEditor
as
RadDateTimeEditor;
if
(dtEditor !=
null
)
{
RadDateTimeEditorElement el = dtEditor.EditorElement
as
RadDateTimeEditorElement;
el.TextBoxElement.MaskType = MaskType.FreeFormDateTime;
el.TextBoxElement.Provider =
new
CustomFreeFormDateTimeProvider(el.TextBoxElement.Mask, el.TextBoxElement.Culture, el.TextBoxElement);
}
}
public
class
CustomFreeFormDateTimeProvider : FreeFormDateTimeProvider
{
public
CustomFreeFormDateTimeProvider(
string
mask, CultureInfo culture, RadMaskedEditBoxElement owner) :
base
(mask, culture, owner)
{
}
public
override
bool
Validate(
string
stringValue)
{
bool
result =
base
.Validate(stringValue);
if
(result ==
false
)
{
string
[] tokens = stringValue.Split(
new
char
[] {
' '
,
':'
}, StringSplitOptions.RemoveEmptyEntries);
int
timeSeparatorIndex = stringValue.IndexOf(
":"
);
int
dateSeparatorIndex = stringValue.IndexOf(
"-"
);
if
(timeSeparatorIndex < dateSeparatorIndex)
//time is before the date
{
DateTime parsedDate;
if
(DateTime.TryParse(stringValue,
out
parsedDate))
{
this
.Value = parsedDate;
return
true
;
}
}
}
return
result;
}
}
Oh guys, you killing me. I thought I could split the value myself and parse each part separately, passing DateTime.MinValue as the base to detect which of the parts contains time value. But you just ignoring the base date value at all! In DateTimeEntry class you have
public override DateTime Evaluate(DateTime date)
{
return this.Time.Evaluate(this.Date.Evaluate(DateTime.Now.AddMilliseconds(7200000.0)));
}
Why? Why do you ignoring date value here and passing some magic value of Now + 2 hour?
As of your suggestion, I already have the custom format in the editor to display the value there by default in this format. By I need to allow users to paste date/time value from other sources to this editor, thus I cannot limit it to the strong mask and this free form date time feature was ideal for me.
public
RadForm1()
{
InitializeComponent();
GridViewDateTimeColumn dateTimeColumn =
new
GridViewDateTimeColumn(
"DateTimeColumn"
);
dateTimeColumn.FormatString =
"{0:HH:mm:ss dd-MMM-yyyy}"
;
this
.radGridView1.Columns.Add(dateTimeColumn);
this
.radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
this
.radGridView1.Rows.Add(DateTime.Now);
this
.radGridView1.CellEditorInitialized += radGridView1_CellEditorInitialized;
}
private
void
radGridView1_CellEditorInitialized(
object
sender, GridViewCellEventArgs e)
{
RadDateTimeEditor dtEditor = e.ActiveEditor
as
RadDateTimeEditor;
if
(dtEditor !=
null
)
{
RadDateTimeEditorElement el = dtEditor.EditorElement
as
RadDateTimeEditorElement;
el.TextBoxElement.MaskType = MaskType.FreeFormDateTime;
}
}
I have logged it in our feedback portal by making this thread public. You can track its progress, subscribe for status changes and add your comments on the following link - feedback item.
I have also updated your Telerik points.
Unfortunately, the issue is at a location that is too deep in the FreeFormDateTimeProvider functionality for any customization to have effect on it. I would recommend you to use the same custom format for the editor as well - HH:mm:ss dd-MMM-yyyy.
GridViewDateTimeColumn dateTimeColumn =
new
GridViewDateTimeColumn(
"DateTimeColumn"
);
dateTimeColumn.FormatString =
"{0:HH:mm:ss dd-MMM-yyyy}"
;
dateTimeColumn.Format = DateTimePickerFormat.Custom;
dateTimeColumn.CustomFormat =
"HH:mm:ss dd-MMM-yyyy"
;
this
.radGridView1.Columns.Add(dateTimeColumn);
Regards,
Dess | Tech Support Engineer, Sr.
Progress Telerik