I've created a simple DateTime array that contains 3 items. These items are set to use the values of three different DateTimePickers on my form. Before I go further into using the array, I need to make sure it is actually using the correct values, and it does not appear to be doing so. Here's my code:
namespace Test
{
public partial class Form1 : Form
{
DateTime[] monSchedule = new DateTime[3];
public Form1()
{
InitializeComponent();
monSchedule[0] = monStart.Value;
monSchedule[1] = monEnd.Value;
monSchedule[2] = monLunch.Value;
}
private void Form1_Load(object sender, EventArgs e)
{
setDefaults();
}
private void setDefaults()
{
monStart.Value = DateTime.Parse("00:00");
monEnd.Value = DateTime.Parse("00:00");
monLunch.Value = DateTime.Parse("00:00");
}
private void validate()
{
MessageBox.Show("You entered time " + monSchedule[0]);
}
When I load my form, setDefaults(); should change the values to the current date with a time of 00:00. When I press the button to show the value in the array, it is pulling current date and current time. I need it to pull whatever the current time in that DateTimePicker is. So if a user types 10:00 into the DateTimePicker (they are formatted HH:mm), then I need the MessageBox to say the time is 10:00 AM. If I change the value to 22:00, then I need the messagebox to say the time is 10:00 PM. etc. (Date is irrelevant in my scenario, I'm not concerned with what the date is at all. Only the time.)
I suspect it may be because of the order it's written in. Is the array storing the value of the DateTimePicker BEFORE setDefaults(); is run? If so, how do I make the values of the array items dynamic since the values of the DateTimePickers are going to change a lot and I need the array elements to be updating with the latest values?
EXTRA INFO:
-Using Visual Studio
-Added the DateTimePickers in design view, changed the format to HH:mm there, did not change the default values in design view
-Ignoring date completely, only concerned with time right now
PS: I was also struggling with where to declare the array so it was accessible in multiple other methods and found I had to declare the array initializer within public partial class Form1, but then add the items in the array within public Form1(), because it wouldn't let me add them under public partial class Form1. I don't know if this is correct though, but it seemed to work when I tested with an array of strings so I went with it.
I have to say that this is a bit of a regression. In your previous question, JoshPart gave you good advice in the form of user controls, although he may have left some gaps too large for you to fill on your own.
Using arrays in this manner might work for a single day, but it won't scale well to a full week.
In case anyone reading this is wondering why I'm talking about a full week, I refer you to the previous question. Also, I recognize that I'm going off-topic for this specific question but I believe this to be an XY problem and the previous question was actually based on the real problem and work that was more on-the-mark.
Let's start with what we know. I've gleaned this from the two questions and the various comments in both.
You have DateTimePicker controls for start, end, and lunch. You're only interested in the time portion so you have Format set to "Custom" and CustomFormat set to "HH:mm". Assumption: lunch is a fixed length so the end time isn't needed.
You have the aforementioned controls times seven, one set for each day of the week.
You've written validation code (range tests) to determine if values are entered correctly, and you're able to show a label with red exclamation marks when that test fails.
You've identified that it's getting too complicated just having a bunch of controls on a form.
So far, so good. Now for your goal.
You're looking for a way to organize the controls, and the data they collect, to make it easier to work with them.
A user control is still the way to go here. You'll benefit from encapsulating all that repeated functionality into a single place and being able to reuse it.
Start by creating a user control -- we'll call it DayPanel -- and put all the controls for a single day on that canvas. Name the controls without any regard for the day of week (e.g. start, lunch, and end). Your user control will neither know nor care which day it represents.
Add an event handler for the ValueChanged event to the DateTimePicker controls. Instead of double-clicking the control, go to the events list in the Properties tool window and type a name, such as the one below, for the ValueChanged event. Do the same for the other two controls and it will reuse the event handler that it created the first time. Whenever the user changes a time, this event handler will be called and it will effect changes to the UI.
private void picker_ValueChanged(object sender, EventArgs e)
{
// In case you need to know which DateTimePicker was changed, take a look at 'sender':
//DateTimePicker picker = (DateTimePicker)sender;
UpdateWarningState();
}
As Jimi mentioned, the sender object will be a reference to the DateTimePicker control that sent the event. You probably won't need it but it's there if you do.
UpdateWarningState just hides/shows the warning label based on the validity of the inputs.
private void UpdateWarningState()
{
warningLabel.Visible = !IsInputValid(start.Value.TimeOfDay, lunch.Value.TimeOfDay, end.Value.TimeOfDay);
}
I had suggested in comments on the previous question that it seemed to make sense to get true if the inputs are valid and then use the logical negative for the visibility of the warning label.
As Paul Hebert pointed out, you really only need to compare a TimeSpan, so IsInputValid receives the TimeOfDay property to deal with only that much.
private bool IsInputValid(TimeSpan startTime, TimeSpan lunchTime, TimeSpan endTime)
{
return startTime < lunchTime && lunchTime.Add(TimeSpan.FromMinutes(30)) < endTime;
}
In fact, even though you're only inputting a time, the control still returns a date part in its Value property. If you want to be certain that you're not comparing times in different dates, you'll definitely need to use the TimeOfDay property. That said, by not presenting the date part, you have a measure of control over that so it's not a pressing concern. If you had to worry about crossing over midnight, that would complicate things.
Note that I've dealt with that earlier assumption that lunch is a fixed length by adding 30 minutes in the comparison to the end time.
Why not just do all that in the ValueChanged event handler?
The Single Responsibility Principle. IsInputValid does one thing: business logic; it tells you if the inputs are valid based on range testing. UpdateWarningState does a different thing: UI logic; it updates the visibility of warning label based on the validity of the inputs.
UpdateWarningState is reusable. You can call it from other event handlers in the future. Event handlers really shouldn't ever do much. They're more like telephone operators: "how may I direct your call?"
IsInputValid is reusable. The business logic can be extracted from your UI code at some point in the future and be reused by something else. I'll admit that the name leaves something to be desired; it fits here but probably should be different outside this context.
But what good is this user control if you have no way of working with its data? The consumer needs to be able to interact with it. A user control is just another class so you can define public properties, methods, and events as you see fit. We'll add properties for the three values of interest:
public TimeSpan Start
{
get => start.Value.TimeOfDay;
set => start.Value = start.Value.Date + value;
}
public TimeSpan Lunch
{
get => lunch.Value.TimeOfDay;
set => lunch.Value = lunch.Value.Date + value;
}
public TimeSpan End
{
get => end.Value.TimeOfDay;
set => end.Value = end.Value.Date + value;
}
What's interesting to note about these properties is that they don't have their own backing storage. Instead, they defer to the controls and translate between their own TimeSpan data type and the controls' DateTime data type. On get, they return just the TimeOfDay property. On set, they remove the time portion (with .Date) and add the time of day.
If you were building this for someone else to consume, you'd want to ensure that the Days property is 0 and that the whole value is non-negative, and either throw ArgumentOutOfRangeException or (gasp!) clamp the value to the acceptable range.
Now that you have a functioning control for a single day, you can slap a bunch of them on the main form. Back in Form1, add seven instances of the DayPanel control and name them monday through sunday. Before we get to initialization, let's create a lookup for these user controls.
private readonly Dictionary<DayOfWeek, DayPanel> _dayPanelLookup;
public Form1()
{
InitializeComponent();
_dayPanelLookup = new Dictionary<DayOfWeek, DayPanel>()
{
[DayOfWeek.Monday] = monday,
[DayOfWeek.Tuesday] = tuesday,
[DayOfWeek.Wednesday] = wednesday,
[DayOfWeek.Thursday] = thursday,
[DayOfWeek.Friday] = friday,
[DayOfWeek.Saturday] = saturday,
[DayOfWeek.Sunday] = sunday
};
}
Now the Load handler can then initialize all the properties. This DefaultTime duplicates the TimeSpan.Zero constant for the purpose of giving it a distinct meaning and can help with refactoring later on.
private static readonly TimeSpan DefaultTime = TimeSpan.Zero;
private void Form1_Load(object sender, EventArgs e)
{
SetDefaults();
}
private void SetDefaults()
{
foreach (DayPanel dayPanel in _dayPanelLookup.Values)
{
dayPanel.Start = DefaultTime;
dayPanel.Lunch = DefaultTime;
dayPanel.End = DefaultTime;
}
}
And just for fun, we can use _dayPanelLookup to grab one of them based on a variable containing the day of the week.
public void someButton_Click(object sender,
{
DayOfWeek whichDay = SelectADay();
DayPanel dayPanel = _dayPanelLookup[whichDay];
// ...
}
That should address the main concern of organizing the controls and making it easy to work with them and their values. What you do with it once the user presses some as-yet-unidentified button on the form is a whole new adventure.
There are yet better ways of doing all of this, I'm sure. I'm not a UI developer, I just play one on TV. For your purposes, I hope this not only gives you the guidance you needed at this point in this project but also illuminates new avenues of thought about how to structure your programs in the future.
It is unclear what you want the date component of the date part of DateTime to be DateTime.Parse("00:00") should return midnight today or 12/27/18 12:00:00 AM;
This is also the same value as DateTime.Today
In addition, you can create a new DateTime with a constructor
monStart.Value = new DateTime(2018, 12, 27, 0, 0, 0);
This is midnight the today
Note:
Reading the description in your updated question, it appears that the DateTimePicker controls values are accessed on a Button Click. If this is the actual scenario, you probably don't need a DateTime array field at all: you could just read the values directly from the DTP controls and use the values in-place.
The example assumes (to comply with the question) that you need that array anyway.
A possible way to proceed:
Set the default values in the Form.Load event. Initialize the monSchedule array values right after, so the values are synchronized. Note that the Form.Load event handler code is (of course) executed after the class constructor (public Form1() { }): the Form object must be already initialized.
Assign an event handler to all the DateTimePicker controls (same event for all). The event handler is used to assign the new values to the monSchedule array. The event could be the ValueChanged event or, possibly, the more generic Validating event. The former is raised each time you change any part of the Time value (the hour value or minutes value). The latter only when the control loses the focus. Your choice.
Use the sender object in the event handler to determine which control raised the event and update the corresponding array value.
An example, using a switch statement and a case statement with a when clause:
Notes:
1. You need C# 7.0+ to use this switch syntax. Otherwise, you could switch using a Type pattern (see the Docs) or the DateTimePicker name (see the example).
2. The DTP_ValueChanged(object sender, EventArgs e) event (the ValueChanged handler) is assigned to all the DateTimePicker controls.
public partial class Form1 : Form
{
DateTime[] monSchedule = new DateTime[3];
private void Form1_Load(object sender, EventArgs e)
{
SetDefaultDTPValues();
}
private void SetDefaultDTPValues()
{
monStart.Value = DateTime.Parse("00:00");
monEnd.Value = DateTime.Parse("00:00");
monLunch.Value = DateTime.Parse("00:00");
monSchedule[0] = monStart.Value;
monSchedule[1] = monEnd.Value;
monSchedule[2] = monLunch.Value;
}
private void DTP_ValueChanged(object sender, EventArgs e)
{
switch (sender)
{
case DateTimePicker dtp when dtp.Equals(monStart):
monSchedule[0] = dtp.Value;
break;
case DateTimePicker dtp when dtp.Equals(monEnd):
monSchedule[1] = dtp.Value;
break;
case DateTimePicker dtp when dtp.Equals(monLunch):
monSchedule[2] = dtp.Value;
break;
}
}
}
On a Button.Click event:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show($"Start: {monSchedule[0].ToString("hh:mm tt")} " +
$"End: {monSchedule[1].ToString("hh:mm tt")} " +
$"Lunch: {monSchedule[2].ToString("hh:mm tt")}");
}
If the C# version in use doesn't allow this switch statement syntax, you can use the DateTimePicker name instead (there are other options, see the examples in the Docs):
private void DTP_ValueChanged(object sender, EventArgs e)
{
DateTimePicker dtp = sender as DateTimePicker;
switch (dtp.Name)
{
case "monStart":
monSchedule[0] = dtp.Value;
break;
case "monEnd":
monSchedule[1] = dtp.Value;
break;
case "monLunch":
monSchedule[2] = dtp.Value;
break;
}
}
Related
Just can't get it with datepicker validation.
I have datepicker From and datepicker To, so I want to prevent the user from doing some kung fu and seting datepicker From to be bigger than datepicker To, I've bumped across some questions but couldn't find the answer, so I've tried doing the easiest way I could think of:
Set MaxDate property for datepicker from in form_load event
private void Form1_Load(object sender, EventArgs e)
{
datepickerFrom.MaxDate = datepickerFrom.Value;
}
Then do the same for value_changed event
private void datepickerFrom_ValueChanged(object sender, EventArgs e)
{
datepickerFrom.MaxDate = datepickerFrom.Value;
}
This was easy and fine, only few lines of code, and I've only needed datepickerFrom_ValueChanged event, but recently I've tried typing date into datepicker insted of selecting it, and then all hell broke loose.
So I came to some solution for validation, instead of setting MaxDate property, I've tried this.
private void dtFrom_ValueChanged(object sender, EventArgs e)
{
DateTime from = datepickerFrom.Value;
DateTime to = datepickerTo.Value;
int year= from.Year > to.Year ? to.Year : from.Year;
int month = from.Month > to.Month ? to.Month : from.Month;
int day = from.Day > to.Day ? to.Day : from.Day;
int hour = from.Hour > to.Hour ? to.Hour : from.Hour;
int minute = from.Minute > to.Minute ? to.Minute : from.Minute;
int second = from.Second > to.Second ? to.Second : from.Second;
//setting datepicker value
datepickerFrom.Value = new DateTime(year, month, day, hour, minute, second);
}
This works fine, but feels like bit of headache, and I have to do this for datepickerTO_ValueChanged event also, sure I could make one method and call it two times, but still feels like there is a batter way for this, so any suggestions?
Thank you for your time
Solution 1:
You can handle datePickerTo close event and do something like:
private void dateTimePickerTo_CloseUp(object sender, EventArgs e)
{
DateTime fromdate = Convert.ToDateTime(dateTimePickerFrom.Value);
DateTime todate1 = Convert.ToDateTime(dateTimePickerTo.Value);
if (fromdate > todate1)
//Error
}
You can also use DateTime.Compare whcih get two date
like
int result = DateTime.Compar(dateTimePickerFrom.Value ,dateTimePickerTo.Value);
if result is 1 means From date is earlier, see this link.
Note1:
but as you said if user type in From or To textboxes then closeup event never fire so you need compare them in where you want to process
such as button click.
Note2:
As #Sinatr comment if Value is DateTime then don't need to convert it so the code would be like:
if (dateTimePickerFrom.Value >dateTimePickerTo.Value)
//Error
Your proposal would lead to a horrible interface. Suppose the following case:
From = 1 jan 2000
To = 1 feb 2000
User wants to change both values to 2010. He starts with the from value:
From = 1 jan 2010
Now he wants to change the TO value to 1 feb 2010. Alas, he can't.
Proper usage would be: add some button with which the operator can affirm he has changed all data, start checking it and update. In windows this button is usually named Apply Now or OK. Why deviate from this windows standard.
private void OnFormLoading(object sender, ...)
{
this.FromDate.MinValue = ... // use the real absolute min value you want ever to allow
this.FromDate.MaxValue = ...;
this.ToDate.MinValue = ...;
this.ToDate.MaxValue = ...;
}
Don't do any checking as long as the operator is making changes. Strat checking the input values when he indicates that he finished making changes:
private void OnButtonApplyNow_Clicked(object sender, ...)
{
bool InputOk = CheckInput();
if (!inputOk)
{
ShowIncorrectInput(); // for instance using a MessageBox
}
}
I would like to use a numericupdown control on my application. I'm well aware that I could use a plain textbox instead, but I rather like the way this particular control's UI fits with what I'm doing in my application.
It also needs to have 0's at the left, per desired text output. If I'm not mistaken, this is not supported by standard numericupdown controls. It should never exceed 4 digits in length. However, if I input more, it must show favor to new keystrokes and drop left-most digits instead. The up and down arrows should increment/decrement the value per usual behavior. Even after keying in values.
It should never be allowed to run negative. It should only accept whole integers. This is easily handled by the stock functionality though.
Partly posting an answer for others who may follow. Partly looking for assurance that I'm not being an idiot.
Note that this hack depends on firing Sync() before extracting the final value. The timer will fire pretty quickly, but does not guarantee that things will happen in the correct order. It may not hurt to manually trigger Sync() immediately before extracting values.
public class UpDownWith0 : System.Windows.Forms.NumericUpDown
{
private System.Windows.Forms.Timer addzeros = new System.Windows.Forms.Timer();
public UpDownWith0()
{
this.addzeros.Interval = 500; //Set delay to allow multiple keystrokes before we start doing things
this.addzeros.Stop();
this.addzeros.Tick += new System.EventHandler(this.Sync);
}
protected override void OnTextBoxTextChanged(object source, System.EventArgs e)
{
this.addzeros.Stop(); //reset the elapsed time every time the event fires, handles multiple quick proximity changes as if they were one
this.addzeros.Start();
}
public void Sync(object sender, System.EventArgs e)
{
int val;
this.addzeros.Stop();
if (this.Text.Length > 4)
{
//I never want to allow input over 4 digits in length. Chop off leftmost values accordingly
this.Text = this.Text.Remove(0, this.Text.Length - 4);
}
int.TryParse(this.Text, out val); //could use Value = int.Parse() here if you preferred to catch the exceptions. I don't.
if (val > this.Maximum) { val = (int)this.Maximum; }
else if (val < this.Minimum) { val = (int)this.Minimum; }
this.Value = val; //Now we can update the value so that up/down buttons work right if we go back to using those instead of keying in input
this.Text = val.ToString().PadLeft(4, '0'); //IE: display will show 0014 instead of 14
this.Select(4, 0); //put cursor at end of string, otherwise it moves to the front. Typing more values after the timer fires causes them to insert at the wrong place
}
}
On a Form I have two controls, A and B. Each one sends an event when its value changes. The Form handles A's ValueChanged event by setting B to some value, and B's ValueChanged by setting A's value.
Do I need to do anything special to prevent an infinite loop, where the user changes A, sending a ValueChanged event which causes B to update, which now sends it ValueChanged event, causing A to update...
I know some GUI toolkits, such as Qt, have logic built into the infrastructure to prevent loops like that. (See the "Enter Your Age" example in Ch. 1 of the Blanchette & Summerfield book on Qt4.) Some older toolkits required the programmer to define and manage a flag to detect recursion. For WinForms, I haven't read a definitive statement anywhere on this.
For a concrete example, suppose A and B are NumericUpDown controls to show temperature in Fahrenheit and Celsius. When the user changes one, the other updates to show the corresponding temperature in the other system.
In my experience, the loop usually ends because the values stop actually changing. Your example falls into this case. Consider the following scenario:
User changes the Fahrenheit to 32
Events update the Celsius to 0
Events set the Fahrenheit to 32
Nothing further happens because Fahrenheit did not change
This is usually implemented in the properties by putting a check at the top of the setter to not raise the changed event when the new value is the same as the current value.
The way in which I've managed to prevent this problem when implementing custom controls, is by raising events only when the value actually does get changed, like this.
public string Text
{
get { return _text; }
set
{
if (_text != value)
{
_text = value;
OnTextChanged();
}
}
}
One could argue that not checking whether the value is actually different before firing the event is bad code.
I don't recall ever experiencing these issues with Windows Forms controls, so the ones I've been using must be doing this check correctly.
The events will not loop in the NumericUpDown example that you have provided. The ValueChanged event of the NumericUpDown will only get fired when the value changes i.e. If the value of a NumericUpDown is 5 and in code you again set it to 5, no event will be fired. This behavior of the event stops it from looping when you have two NumericUpDown controls.
Now suppose you have two NumericUpDown controls A & B.
A was changed by the user, it fires an event
On the event fired by A, you calculate and set the value of B. B detects a value change and fires an event
On the event fired by B, you calculate and set the value of A. However this value would be the same as the original value and Windows will not fire an event of ValueChanged.
So in the case of Windows Form Controls, the Framework manages it for you, if you want to achieve this for your own classes you follow a similar principle. On the setter of a value, check if the new value is different from the old value. Only if it differs fire an event.
I think you should detach B event before set B in A event. So B event never fire when you set B in A event. you should same thing for B event. You can break loop.
Sorry for my English. I hope it help you.
There isn't really any standard. As a user, you should handle controls that raise useless events, and controls that do not. Even for .NET's own controls, documentation is lacking on this aspect so you're left just trying it.
Using a flag to detect recursion is possible, but nicer IMO is to change control.Value = newvalue; to if (control.Value != newvalue) control.Value = newvalue;.
That said, if you write your own controls, please do not raise useless events, and please clearly document that fact.
To answer your question I have built a simple test with the code below.
public partial class Form1 : Form
{
Random r = new Random();
public Form1()
{
InitializeComponent();
}
private void Form1_Shown(object sender, EventArgs e)
{
// This triggers the infinite loop between the two controls
numericUpDown1.Value = 10;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
int cur = (int)numericUpDown2.Value;
int r1 = r.Next(1, 100);
while (r1 == cur)
r1 = r.Next(1, 100);
Console.WriteLine("Random in NUM1=" + r1);
numericUpDown2.Value = r1;
}
private void numericUpDown2_ValueChanged(object sender, EventArgs e)
{
int cur = (int)numericUpDown1.Value;
int r1 = r.Next(1, 100);
while (r1 == cur)
r1 = r.Next(1, 100);
Console.WriteLine("Random in NUM2=" + r1);
numericUpDown1.Value = r1;
}
}
The form has only two NumericUpDown initialized with values 1 and 2.
As you could test in Visual Studio 2013 there is nothing to prevent an infinite recursion in the case in which you really manage to generate different numbers at every ValueChanged event.
To prevent the infinite loop you could use the usual technique. Declare a global form variable
bool changing = false;
Inside both events add this code
try
{
if (changing) return;
changing = true;
.... event code ....
}
finally
{
changing = false;
}
I need to add a textbox in my application that starts showing with the value 0.00 just like a ATM, as you type the numbers then it keeps the two decimal point until satisfied with the value for example the sequence to end up of a value of 1023.00 would be (as I type)
0.01
0.10
1.02
10.23
102.30
1023.00
Is this possible to do in a windows forms application?. I am just not sure how to go about it.
Thank you.
In this kind of scenario I would not use a textbox, but a label or a read-only textbox. To get the user input just use the key-press event on your form (you have to enable KeyPreview on the form too) - then just remember the keys/numbers pressed and output the format you are targeting - should be a rather easy algorithm (Char.IsNumber, and String.Format might come in handy):
private int _inputNumber = 0;
private void Form_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
if (!Char.IsNumber(e.KeyChar)) return;
_inputNumber = 10*_inputNumber + Int32.Parse(e.KeyChar.ToString());
ReformatOutput();
}
private void ReformatOutput()
{
myOutput.Text = String.Format("{0:0.00}", (double)_inputNumber / 100.0);
}
Note: Why not use a textbox - because it's internal logic with select/replace/remove/type is so overcomplicated that the cases you would have to check are just to much to handle gracefully - so instead of trying to change a cannon into a sling you should start with the sling.
I have a c# .net project and want an input text box for a date value. I want it to display a default value of mm/dd/yyyy and then allow users to enter valid dates.
I have tried using a masked text box but it doesn't like having the above format.
If i try and use //____ looks naff and so does 00/00/0000 and of course if you put in '0' like 03/11/2009 you get 3/11/29 because the 0's are deleted as part of the mask.
What is the best way to set up an input box like this, effectively with a mask, only allowing numbers, and validation (though at this point I am not so worried about that).
This seems so simple and isn't.
Try examining the the date in the OnChange event. Check the length of the text, adding / when appropriate.
Alternatively, see if the the DatePicker may be a better choice...
Why not try and use a plugin like the jQuery UI DatePicker?
http://docs.jquery.com/UI/Datepicker
They are lightweight, tested and saves work!
Assuming Windows Forms, the DateTimePicker is the tool for the job.
Here is an example with the MaskedTextBox, but since you say it doesn't work...
Another good try, if you're using DataBinding, if by instantiating a Binding (Binding for WPF) class and handling its Parse and Format events (these don't seem to exist in WPF, but there must be some way).
In short:
Binding b = new Binding("Text", BindingSourceInstance, "data.DateCreated");
b.Parse += new EventHandler(b_Parse);
b.Format += new EventHandler(b_Format);
private void b_Format(object sender, ConvertEventArgs e) {
if (e.DesiredType != typeof(DateTime)) return;
e.Value.ToString("dd/MM/yyyy");
// Or you may prefer some other variants:
// e.Value.ToShortDateString();
// e.Value.ToLongDateString();
// e.Value.ToString("dd/MM/yyyy", CultureInfo.CurrentCulture.DateTimeInfo.ShortDatePattern);
}
private void b_Parse(object sender, ConvertEventArgs e) {
// Choose whether you to handle perhaps bad input formats...
e.Value = DateTime.Parse(e.Value, CultureInfo.InvariantCulture); // The CultureInfo here may be whatever you choose.
}
Using masked text box for date is a headache, best to use DateTime.TryParseExact() with control validated event.
It will guarantee user input in correct format without too much coding, like in this example:
you need to use also a ToolTip to instruct the user to the correct input.
private void txtEntryDate_Validated(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(txtEntryDate.Text))
{
DateTime entryDate;
if (DateTime.TryParseExact(txtEntryDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out entryDate))
{
// further actions for validations
}
else
{
setTooltip(txtEntryDate, "txtEntryDate", "Invalid date format date must be formatted to dd/MM/yyyy");
txtEntryDate.Focus();
}
}
else
{
setTooltip(txtEntryDate, "txtEntryDate", "Please provide entry date in the format of dd/MM/yyyy");
txtEntryDate.Focus();
}
}
and the ToolTip class:
private void setTooltip(Control Ctrl, string CtrlCaption, string ToolTipMsg)
{
ToolTip tt1 = new ToolTip();
tt1.AutoPopDelay = 5000;
tt1.InitialDelay = 1000;
tt1.ReshowDelay = 500;
tt1.ShowAlways = false;
tt1.SetToolTip(Ctrl, CtrlCaption);
tt1.Show(ToolTipMsg, Ctrl,5000);
}