Windows Develop Bookmark and Share   
 index > Windows Forms General > How to create a nullable and editable DateTimePicker
 

How to create a nullable and editable DateTimePicker

Hi everyone,

Does anyone know a good way to create a nullable and editable DateTimePicker in VS C#?

Thanks!

tdcntt

tdcntt  Sunday, January 07, 2007 1:34 AM

If your using this DTP for Databinding and the like, you can use the UltraDateTimePicker that I have written(based/inspired by various other nullable/readonly capable DateTimePickers all over the Internet.

I supports everything that I needed in a DTP(nullable, bindable, readonly option, and works in Table and Flow Layout Panels(many others did not))

Here is the Code, although kinda long it is well worth it in my opinion(still needs a little optimizing though):

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Drawing;

using System.Data;

using System.Text;

using System.Windows.Forms;

using System.Globalization;

using System.Threading;

using System.Runtime.InteropServices;

namespace ProManApp

{

/// <summary>

/// The following code is written from scratch, but was inspired by

/// the NullableDateTimePicker by Claudio Grazioli, http://www.grazioli.ch

///

/// This Extended DateTimePicker adds useful functionality to the DateTimePicker class espicially for Database Apllications

/// 1. I added the abilty to set the DTP to null with the following features

/// a. NullText Property that can be used to display Text of your choise when DTP is null

/// b. Ability to type dates or times into the DTP when set to null

/// c. BackSpace and Delete keystrokes set the DTP to null

/// 2. I added a ReadOnly Mode in which the UDTP hides itself and decorates a ReadOnly TextBox in its place

/// a. By Decorating a TextBox we get all black easily readable text.

/// b. The ReadOnly TextBox has automatic Clipboard access built in which is useful.

/// c. I added a TabStopWhenReadOnly Property that allows the ReadOnly Mode to mimic the UDTP Tabstop property or not;

/// If TabStopWhenReadOnly is true then the ReadOnly Mode's TabStop will be whatever the UDTP Tabstop Property is.

/// If TabStopWhenReadOnly is false then the the ReadOnly Mode's TabStop is false.

/// </summary>

public partial class UltraDateTimePicker : DateTimePicker, ISupportInitialize

{

#region Member Variables Added To Allow Null Values

//Format and CustomForamt are shadowed since base.Format is always Custom

//and base.CustomFormat is used in setFormat to show the intended _Format

//You have to keep base.Format set to Custom to avoid superfluous ValueChanged

//events from occuring.

private DateTimePickerFormat _Format; // Variable to store 'Format'

private string _CustomFormat; //Variable to store 'CustomFormat'

private string _nullText = ""; //Variable to store null Display Text

#endregion

#region Member Variables Added To Enable ReadOnly Mode

private bool _readOnly = false;//Flag to denote the UDTP is in ReadOnly Mode

private bool _visible = true; //Overridden to show the proper Display for Readonly Mode

private bool _tabStopWhenReadOnly = false; //Variable to store whether or not the UDTP is a TabStop when in ReadOnly Mode

private TextBox _textBox;//TextBox Decorated when in ReadOnly Mode

#endregion

#region Constructor

/// <summary>

/// Basic Constructer + ReadOnly _textBox initialization

/// </summary>

public UltraDateTimePicker()

{

InitializeComponent();

initTextBox();

base.Format = DateTimePickerFormat.Custom;

_Format = DateTimePickerFormat.Long;

if (DesignMode)

setFormat();

}

#endregion

#region ISupportInitialize Members

private bool initializing = true;

public void BeginInit()

{

// TODO: Add UserControl1.BeginInit implementation

initializing = true;

}

public void EndInit()

{

base.Value = DateTime.Today;//Default the value to Today(makes me happy, but not necessary)

initializing = false;

if (DesignMode)

return;

if (this.Parent.GetType() == typeof(TableLayoutPanel))

{

TableLayoutPanelCellPosition cP = ((TableLayoutPanel)this.Parent).GetPositionFromControl(this);

((TableLayoutPanel)this.Parent).Controls.Add(_textBox, cP.Column, cP.Row);

((TableLayoutPanel)this.Parent).SetColumnSpan(_textBox, ((TableLayoutPanel)this.Parent).GetColumnSpan(this));

_textBox.Anchor = this.Anchor;

}

//I added special logic here to handle positioning the _TextBox when the UDTP is in a FlowLayoutPanel

else if (this.Parent.GetType() == typeof(FlowLayoutPanel))

{

((FlowLayoutPanel)this.Parent).Controls.Add(_textBox);

((FlowLayoutPanel)this.Parent).Controls.SetChildIndex(_textBox, ((FlowLayoutPanel)this.Parent).Controls.IndexOf(this));

_textBox.Anchor = this.Anchor;

}

else //not a TableLayoutPanel or FlowLayoutPanel so just assign the parent

{

_textBox.Parent = this.Parent;

_textBox.Anchor = this.Anchor;

}

//I use the following block of code to walk up the parent-child

//chain and find the first member that has a Load event that I can attach to

//I set the visiblilty during this event so that Databinding will work correctly

//otherwise the UDTP will fail to bind properly if its visibility is false during the

//Load event.(Strange but true, has to do with hidden controls not binding for performance reasons)

Control parent = this;

bool foundLoadingParent = false;

do

{

parent = parent.Parent;

if (parent.GetType().IsSubclassOf(typeof(UserControl)))

{

((UserControl)parent).Load += new EventHandler(UltraDateTimePicker_Load);

foundLoadingParent = true;

}

else if (parent.GetType().IsSubclassOf(typeof(Form)))

{

((Form)parent).Load += new EventHandler(UltraDateTimePicker_Load);

foundLoadingParent = true;

}

}

while (!foundLoadingParent);

}

void UltraDateTimePicker_Load(object sender, EventArgs e)

{

setVisibility();

}

#endregion

#region Public Properties Modified/Added To Allow Null Values

private bool _isNull = false;

/// <summary>

/// Modified Value Propety now of type Object and uses MinDate to mark null values

/// </summary>

new public Object Value

{

get

{

//if (this.MinDate == base.Value) //Check to see if set to MinDate(null), return null or base.Value accordingly

if (_isNull)

return null;

else

return base.Value;

}

set

{

if (value == null || value == DBNull.Value) //Check for null assignment

{

if (!_isNull) //If not already null set to null and fire event

{

_isNull = true;

this.OnValueChanged(EventArgs.Empty);

}

}

else //Value is nto null

{

if (_isNull && base.Value == (DateTime)value)//if null and value matches base.value take out of null and fire event

//(null->value needs a value changed even though base.Value did not change)

{

_isNull = false;

this.OnValueChanged(EventArgs.Empty);

}

else//change to the new value(changed event fires from base class

{

_isNull = false;

base.Value = (DateTime)value;

}

}

setFormat();//refresh format

_textBox.Text = this.Text;

}

}

/// <summary>

/// NullText property is used to access/change the Text shown when the UDTP is null

/// </summary>

#region DesignerModifiers

[Browsable(true)]

[Category("Behavior")]

[Description("Text shown when DateTime is 'null'")]

[DefaultValue("")]

#endregion

public string NullText

{

get { return _nullText; }

set { _nullText = value; }

}

/// <summary>

/// Modified Format Property stores the assigned Format and the propagates the change to base.CustomFormat

/// </summary>

#region DesignerModifiers

[Browsable(true)]

[DefaultValue(DateTimePickerFormat.Long), TypeConverter(typeof(Enum))]

#endregion

new public DateTimePickerFormat Format

{

get { return this._Format; }

set

{

this._Format = value;

this.setFormat();

}

}

private void setFormat()

{

base.CustomFormat = null;//Resets the CustomFormat(bookkeeping)

if (_isNull)//If null apply NullText to the UDTP

{

base.CustomFormat = String.Concat("'", this.NullText, "'");

}

else

{

//The Following is used to get a string representation ot the current UDTP Format

//And then set the CustomFormat to match the intended format

CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;

DateTimeFormatInfo dTFormatInfo = cultureInfo.DateTimeFormat;

switch (_Format)

{

case DateTimePickerFormat.Long:

base.CustomFormat = dTFormatInfo.LongDatePattern;

break;

case DateTimePickerFormat.Short:

base.CustomFormat = dTFormatInfo.ShortDatePattern;

break;

case DateTimePickerFormat.Time:

base.CustomFormat = dTFormatInfo.ShortTimePattern;

break;

case DateTimePickerFormat.Custom:

base.CustomFormat = this._CustomFormat;

break;

}

}

}

/// <summary>

/// Modified CustomFormat Property stores the assigned CustomFormat when null, otherwise functions as normal

/// </summary>

new public string CustomFormat

{

get { return _CustomFormat; }

set

{

this._CustomFormat = value;

this.setFormat();

}

}

#endregion

#region Public Properties Modified/Added To Enable ReadOnly Mode

/// <summary>

/// Sets the ReadOnly Property and then call the appropriate Display Function

/// If in Design Mode then return(If we dont do this then the Control could Disappear )

/// </summary>

#region DesignerModifiers

[Browsable(true)]

[Category("Behavior")]

[Description("Displays Control as ReadOnly(Black on Gray) if 'true'")]

[DefaultValue(false)]

#endregion

public bool ReadOnly

{

get { return _readOnly; }

set

{

this._readOnly = value;

setVisibility();

}

}

/// <summary>

/// This useful property allows you to say wheher or not you want

/// the TexBox to Mimic the DTP's TabStop value when in ReadOnly Mode.

/// I personally found this useful on Data entry forms. The default is false,

/// which means when in ReadOnly Mode you cannot tab into it so Tab will skip

/// ReadOnly Pickers.

/// </summary>

#region DesignerModifiers

[Category("Behavior")]

[DefaultValue(false)]

[Browsable(true)]

[EditorBrowsable(EditorBrowsableState.Always)]

#endregion

public bool TabstopWhenReadOnly

{

get { return _tabStopWhenReadOnly; }

set

{

_tabStopWhenReadOnly = value;

_textBox.TabStop = (_tabStopWhenReadOnly && this.TabStop); //TextBox is a Tabstop only if mimicing and DTP is a TabStop

}

}

/// <summary>

/// Modified the TabStop Property to support the added TabStopWhenReadOnly property

/// </summary>

new public bool TabStop

{

get { return base.TabStop; }

set

{

base.TabStop = value;

_textBox.TabStop = (_tabStopWhenReadOnly && base.TabStop);

}

}

/// <summary>

/// Sets the Visible Property and then call the appropriate Display Function

/// If in Design Mode then return(If we dont do this then the Control could Disappear )

/// </summary>

new public bool Visible

{

get { return _visible; }

set

{

_visible = value;

setVisibility();

}

}

#endregion

#region OnXXXX() Modified To Allow Null Values

/// <summary>

/// Used to change the UDTP.Value on Closeup(Without this code Closeup only changes the base.Value)

/// </summary>

/// <param name="e"></param>

protected override void WndProc(ref Message m)

{

if (m.Msg == 0x4e)

{

NMHDR nm = (NMHDR)m.GetLParam(typeof(NMHDR));

if (nm.Code == -746 || nm.Code == -722)

this.Value = base.Value;//propagate change form base to UTDP

}

base.WndProc(ref m);

}

[StructLayout(LayoutKind.Sequential)]

private struct NMHDR

{

public IntPtr HwndFrom;

public int IdFrom;

public int Code;

}

///<summary>

///Sets UDTP Value to null when Delete or Backspace is pressed

///</summary>

///<param name="e"></param>

protected override void OnKeyUp(KeyEventArgs e)

{

if (e.KeyCode == Keys.Delete || e.KeyCode == Keys.Back)

this.Value = null;

base.OnKeyUp(e);

}

/// <summary>

/// When Null and a Number is pressed this method takes the UDTP out of Null Mode

/// and resends the pressed key for timign reasons

/// </summary>

/// <param name="e"></param>

protected override void OnKeyPress(KeyPressEventArgs e)

{

base.OnKeyPress(e);

if (_isNull && Char.IsDigit(e.KeyChar))

{

this.Value = base.Value;

e.Handled = true;

SendKeys.Send("{RIGHT}");

SendKeys.Send(e.KeyChar.ToString());

}

else

base.OnKeyPress(e);

}

#endregion

#region OnXXXX() Modified To Enable ReadOnly Mode

/// <summary>

/// Refreshes the Visibility of the Control if the Parent Changes(So the _TextBox get moved and Redrawn)

/// If in Design Mode then return(If we dont do this then the Control could Disappear )

/// </summary>

/// <param name="e"></param>

protected override void OnParentChanged(EventArgs e)

{

base.OnParentChanged(e);

if (DesignMode || initializing)

return;

updateReadOnlyTextBoxParent();//update the _TextBox parent

setVisibility(); //Reset Visibilty for new parent

}

/// <summary>

/// Propagates Location to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnLocationChanged(EventArgs e)

{

base.OnLocationChanged(e);

_textBox.Location = this.Location;

}

/// <summary>

/// Propagates Size to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnSizeChanged(EventArgs e)

{

base.OnSizeChanged(e);

_textBox.Size = this.Size;

}

/// <summary>

/// Propagates Size to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnResize(EventArgs e)

{

base.OnResize(e);

_textBox.Size = this.Size;

}

/// <summary>

/// Propagates Dock to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnDockChanged(EventArgs e)

{

base.OnDockChanged(e);

_textBox.Dock = this.Dock;

}

/// <summary>

/// Propagates RightToLeft to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnRightToLeftChanged(EventArgs e)

{

base.OnRightToLeftChanged(e);

_textBox.RightToLeft = this.RightToLeft;

}

/// <summary>

/// Propagates TabStop to the _TextBox if TabStopWhenReadOnly == true

/// </summary>

/// <param name="e"></param>

protected override void OnTabStopChanged(EventArgs e)

{

base.OnTabStopChanged(e);

_textBox.TabStop = _tabStopWhenReadOnly && this.TabStop;

}

/// <summary>

/// Propagates TabIndex to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnTabIndexChanged(EventArgs e)

{

base.OnTabIndexChanged(e);

_textBox.TabIndex = this.TabIndex;

}

////// <summary>

/// Propagates Font to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnFontChanged(EventArgs e)

{

base.OnFontChanged(e);

_textBox.Font = this.Font;

}

#endregion

#region Private Methods Added To Enable ReadOnly Mode

/// <summary>

/// Added to initialize the _textBox to the default values to match the DTP

/// </summary>

private void initTextBox()

{

if (DesignMode)

return;

_textBox = new TextBox();

_textBox.ReadOnly = true;

_textBox.Location = this.Location;

_textBox.Size = this.Size;

_textBox.Dock = this.Dock;

_textBox.Anchor = this.Anchor;

_textBox.RightToLeft = this.RightToLeft;

_textBox.Font = this.Font;

_textBox.TabStop = this.TabStop;

_textBox.TabIndex = this.TabIndex;

_textBox.Visible = false;

_textBox.Parent = this.Parent;

}

private void setVisibility()

{

if (DesignMode || initializing)//Dont actually change the visibility if in Design Mode

return;

if (this._visible)

{

if (this._readOnly)

showTextBox();//If Visible and Readonly Show TextBox

else

showDTP();//If Visible and NOT ReadOnly Show DateTimePicker

}

else

{

showNone(); //If Not Visible Show Neither

}

}

private void showTextBox()

{

base.Visible = false;

_textBox.Visible = true;

_textBox.TabStop = _tabStopWhenReadOnly && this.TabStop;

}

private void showDTP()

{

_textBox.Visible = false;

base.Visible = true;

}

private void showNone()

{

_textBox.Visible = false;

base.Visible = false;

}

private void updateReadOnlyTextBoxParent()

{

if (this.Parent == null) //If UTDP.Parent == null, set _textBox.Parent == null and return

{

_textBox.Parent = null;

return;

}

if (_textBox.Parent != this.Parent) //If the Parents DO NOT already match

{

//I Added Special logic here to handle positioning the _TextBox when the UDTP is in a TableLayoutPanel

if (this.Parent.GetType() == typeof(TableLayoutPanel))

{

TableLayoutPanelCellPosition cP = ((TableLayoutPanel)this.Parent).GetPositionFromControl(this);

((TableLayoutPanel)this.Parent).Controls.Add(_textBox, cP.Column, cP.Row);

((TableLayoutPanel)this.Parent).SetColumnSpan(_textBox, ((TableLayoutPanel)this.Parent).GetColumnSpan(this));

_textBox.Anchor = this.Anchor;

}

//I added special logic here to handle positioning the _TextBox when the UDTP is in a FlowLayoutPanel

else if (this.Parent.GetType() == typeof(FlowLayoutPanel))

{

((FlowLayoutPanel)this.Parent).Controls.Add(_textBox);

((FlowLayoutPanel)this.Parent).Controls.SetChildIndex(_textBox, ((FlowLayoutPanel)this.Parent).Controls.IndexOf(this));

_textBox.Anchor = this.Anchor;

}

else //not a TableLayoutPanel or FlowLayoutPanel so just assign the parent

{

_textBox.Parent = this.Parent;

_textBox.Anchor = this.Anchor;

}

}

}

#endregion

#region Public Methods Overriden To Enable ReadOnly Mode

new public void Show()

{

this.Visible = true;

}

new public void Hide()

{

this.Visible = false;

}

#endregion

}

}

I hope this helps many many people.

I also have a UltraComboBox that addsNullable, ReadOnly, TabStopWhenReadOnly and support for Table and FlowLayoutPanels with full DataBinding support. Let me knwo if anyone would like me to post it.

m_shane_tx  Monday, January 08, 2007 9:38 PM

Thanks m_shane_tx !

I got it. You are the man!

Thanks, tdcntt

tdcntt  Tuesday, January 09, 2007 3:45 AM
From previous discussions, I dont think its possible to create a nullable datetime picker, since the DateTime class does not accept null as a value. This is just from what I know
ahmedilyas  Sunday, January 07, 2007 1:47 AM

Thanks Ahmedilyas for the quick response. Is there anyway to extend the DateTimePicker class or work around?

Thanks again.

tdcntt

tdcntt  Sunday, January 07, 2007 2:14 AM
You could experiment with the ShowCheckBox property...

Or make your own UserControl: use a flowlayoutpanel and add a datetimepicker and a checkbox (isnull). Handle the CheckedChanged event to make the datetimepicker visible/invisible... And add a property Value of type Nullable<DateTime> that returns the datetimepicker.Value when the checkbox is not checked.. and null otherwise...
timvw  Sunday, January 07, 2007 11:44 AM

If your using this DTP for Databinding and the like, you can use the UltraDateTimePicker that I have written(based/inspired by various other nullable/readonly capable DateTimePickers all over the Internet.

I supports everything that I needed in a DTP(nullable, bindable, readonly option, and works in Table and Flow Layout Panels(many others did not))

Here is the Code, although kinda long it is well worth it in my opinion(still needs a little optimizing though):

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Drawing;

using System.Data;

using System.Text;

using System.Windows.Forms;

using System.Globalization;

using System.Threading;

using System.Runtime.InteropServices;

namespace ProManApp

{

/// <summary>

/// The following code is written from scratch, but was inspired by

/// the NullableDateTimePicker by Claudio Grazioli, http://www.grazioli.ch

///

/// This Extended DateTimePicker adds useful functionality to the DateTimePicker class espicially for Database Apllications

/// 1. I added the abilty to set the DTP to null with the following features

/// a. NullText Property that can be used to display Text of your choise when DTP is null

/// b. Ability to type dates or times into the DTP when set to null

/// c. BackSpace and Delete keystrokes set the DTP to null

/// 2. I added a ReadOnly Mode in which the UDTP hides itself and decorates a ReadOnly TextBox in its place

/// a. By Decorating a TextBox we get all black easily readable text.

/// b. The ReadOnly TextBox has automatic Clipboard access built in which is useful.

/// c. I added a TabStopWhenReadOnly Property that allows the ReadOnly Mode to mimic the UDTP Tabstop property or not;

/// If TabStopWhenReadOnly is true then the ReadOnly Mode's TabStop will be whatever the UDTP Tabstop Property is.

/// If TabStopWhenReadOnly is false then the the ReadOnly Mode's TabStop is false.

/// </summary>

public partial class UltraDateTimePicker : DateTimePicker, ISupportInitialize

{

#region Member Variables Added To Allow Null Values

//Format and CustomForamt are shadowed since base.Format is always Custom

//and base.CustomFormat is used in setFormat to show the intended _Format

//You have to keep base.Format set to Custom to avoid superfluous ValueChanged

//events from occuring.

private DateTimePickerFormat _Format; // Variable to store 'Format'

private string _CustomFormat; //Variable to store 'CustomFormat'

private string _nullText = ""; //Variable to store null Display Text

#endregion

#region Member Variables Added To Enable ReadOnly Mode

private bool _readOnly = false;//Flag to denote the UDTP is in ReadOnly Mode

private bool _visible = true; //Overridden to show the proper Display for Readonly Mode

private bool _tabStopWhenReadOnly = false; //Variable to store whether or not the UDTP is a TabStop when in ReadOnly Mode

private TextBox _textBox;//TextBox Decorated when in ReadOnly Mode

#endregion

#region Constructor

/// <summary>

/// Basic Constructer + ReadOnly _textBox initialization

/// </summary>

public UltraDateTimePicker()

{

InitializeComponent();

initTextBox();

base.Format = DateTimePickerFormat.Custom;

_Format = DateTimePickerFormat.Long;

if (DesignMode)

setFormat();

}

#endregion

#region ISupportInitialize Members

private bool initializing = true;

public void BeginInit()

{

// TODO: Add UserControl1.BeginInit implementation

initializing = true;

}

public void EndInit()

{

base.Value = DateTime.Today;//Default the value to Today(makes me happy, but not necessary)

initializing = false;

if (DesignMode)

return;

if (this.Parent.GetType() == typeof(TableLayoutPanel))

{

TableLayoutPanelCellPosition cP = ((TableLayoutPanel)this.Parent).GetPositionFromControl(this);

((TableLayoutPanel)this.Parent).Controls.Add(_textBox, cP.Column, cP.Row);

((TableLayoutPanel)this.Parent).SetColumnSpan(_textBox, ((TableLayoutPanel)this.Parent).GetColumnSpan(this));

_textBox.Anchor = this.Anchor;

}

//I added special logic here to handle positioning the _TextBox when the UDTP is in a FlowLayoutPanel

else if (this.Parent.GetType() == typeof(FlowLayoutPanel))

{

((FlowLayoutPanel)this.Parent).Controls.Add(_textBox);

((FlowLayoutPanel)this.Parent).Controls.SetChildIndex(_textBox, ((FlowLayoutPanel)this.Parent).Controls.IndexOf(this));

_textBox.Anchor = this.Anchor;

}

else //not a TableLayoutPanel or FlowLayoutPanel so just assign the parent

{

_textBox.Parent = this.Parent;

_textBox.Anchor = this.Anchor;

}

//I use the following block of code to walk up the parent-child

//chain and find the first member that has a Load event that I can attach to

//I set the visiblilty during this event so that Databinding will work correctly

//otherwise the UDTP will fail to bind properly if its visibility is false during the

//Load event.(Strange but true, has to do with hidden controls not binding for performance reasons)

Control parent = this;

bool foundLoadingParent = false;

do

{

parent = parent.Parent;

if (parent.GetType().IsSubclassOf(typeof(UserControl)))

{

((UserControl)parent).Load += new EventHandler(UltraDateTimePicker_Load);

foundLoadingParent = true;

}

else if (parent.GetType().IsSubclassOf(typeof(Form)))

{

((Form)parent).Load += new EventHandler(UltraDateTimePicker_Load);

foundLoadingParent = true;

}

}

while (!foundLoadingParent);

}

void UltraDateTimePicker_Load(object sender, EventArgs e)

{

setVisibility();

}

#endregion

#region Public Properties Modified/Added To Allow Null Values

private bool _isNull = false;

/// <summary>

/// Modified Value Propety now of type Object and uses MinDate to mark null values

/// </summary>

new public Object Value

{

get

{

//if (this.MinDate == base.Value) //Check to see if set to MinDate(null), return null or base.Value accordingly

if (_isNull)

return null;

else

return base.Value;

}

set

{

if (value == null || value == DBNull.Value) //Check for null assignment

{

if (!_isNull) //If not already null set to null and fire event

{

_isNull = true;

this.OnValueChanged(EventArgs.Empty);

}

}

else //Value is nto null

{

if (_isNull && base.Value == (DateTime)value)//if null and value matches base.value take out of null and fire event

//(null->value needs a value changed even though base.Value did not change)

{

_isNull = false;

this.OnValueChanged(EventArgs.Empty);

}

else//change to the new value(changed event fires from base class

{

_isNull = false;

base.Value = (DateTime)value;

}

}

setFormat();//refresh format

_textBox.Text = this.Text;

}

}

/// <summary>

/// NullText property is used to access/change the Text shown when the UDTP is null

/// </summary>

#region DesignerModifiers

[Browsable(true)]

[Category("Behavior")]

[Description("Text shown when DateTime is 'null'")]

[DefaultValue("")]

#endregion

public string NullText

{

get { return _nullText; }

set { _nullText = value; }

}

/// <summary>

/// Modified Format Property stores the assigned Format and the propagates the change to base.CustomFormat

/// </summary>

#region DesignerModifiers

[Browsable(true)]

[DefaultValue(DateTimePickerFormat.Long), TypeConverter(typeof(Enum))]

#endregion

new public DateTimePickerFormat Format

{

get { return this._Format; }

set

{

this._Format = value;

this.setFormat();

}

}

private void setFormat()

{

base.CustomFormat = null;//Resets the CustomFormat(bookkeeping)

if (_isNull)//If null apply NullText to the UDTP

{

base.CustomFormat = String.Concat("'", this.NullText, "'");

}

else

{

//The Following is used to get a string representation ot the current UDTP Format

//And then set the CustomFormat to match the intended format

CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;

DateTimeFormatInfo dTFormatInfo = cultureInfo.DateTimeFormat;

switch (_Format)

{

case DateTimePickerFormat.Long:

base.CustomFormat = dTFormatInfo.LongDatePattern;

break;

case DateTimePickerFormat.Short:

base.CustomFormat = dTFormatInfo.ShortDatePattern;

break;

case DateTimePickerFormat.Time:

base.CustomFormat = dTFormatInfo.ShortTimePattern;

break;

case DateTimePickerFormat.Custom:

base.CustomFormat = this._CustomFormat;

break;

}

}

}

/// <summary>

/// Modified CustomFormat Property stores the assigned CustomFormat when null, otherwise functions as normal

/// </summary>

new public string CustomFormat

{

get { return _CustomFormat; }

set

{

this._CustomFormat = value;

this.setFormat();

}

}

#endregion

#region Public Properties Modified/Added To Enable ReadOnly Mode

/// <summary>

/// Sets the ReadOnly Property and then call the appropriate Display Function

/// If in Design Mode then return(If we dont do this then the Control could Disappear )

/// </summary>

#region DesignerModifiers

[Browsable(true)]

[Category("Behavior")]

[Description("Displays Control as ReadOnly(Black on Gray) if 'true'")]

[DefaultValue(false)]

#endregion

public bool ReadOnly

{

get { return _readOnly; }

set

{

this._readOnly = value;

setVisibility();

}

}

/// <summary>

/// This useful property allows you to say wheher or not you want

/// the TexBox to Mimic the DTP's TabStop value when in ReadOnly Mode.

/// I personally found this useful on Data entry forms. The default is false,

/// which means when in ReadOnly Mode you cannot tab into it so Tab will skip

/// ReadOnly Pickers.

/// </summary>

#region DesignerModifiers

[Category("Behavior")]

[DefaultValue(false)]

[Browsable(true)]

[EditorBrowsable(EditorBrowsableState.Always)]

#endregion

public bool TabstopWhenReadOnly

{

get { return _tabStopWhenReadOnly; }

set

{

_tabStopWhenReadOnly = value;

_textBox.TabStop = (_tabStopWhenReadOnly && this.TabStop); //TextBox is a Tabstop only if mimicing and DTP is a TabStop

}

}

/// <summary>

/// Modified the TabStop Property to support the added TabStopWhenReadOnly property

/// </summary>

new public bool TabStop

{

get { return base.TabStop; }

set

{

base.TabStop = value;

_textBox.TabStop = (_tabStopWhenReadOnly && base.TabStop);

}

}

/// <summary>

/// Sets the Visible Property and then call the appropriate Display Function

/// If in Design Mode then return(If we dont do this then the Control could Disappear )

/// </summary>

new public bool Visible

{

get { return _visible; }

set

{

_visible = value;

setVisibility();

}

}

#endregion

#region OnXXXX() Modified To Allow Null Values

/// <summary>

/// Used to change the UDTP.Value on Closeup(Without this code Closeup only changes the base.Value)

/// </summary>

/// <param name="e"></param>

protected override void WndProc(ref Message m)

{

if (m.Msg == 0x4e)

{

NMHDR nm = (NMHDR)m.GetLParam(typeof(NMHDR));

if (nm.Code == -746 || nm.Code == -722)

this.Value = base.Value;//propagate change form base to UTDP

}

base.WndProc(ref m);

}

[StructLayout(LayoutKind.Sequential)]

private struct NMHDR

{

public IntPtr HwndFrom;

public int IdFrom;

public int Code;

}

///<summary>

///Sets UDTP Value to null when Delete or Backspace is pressed

///</summary>

///<param name="e"></param>

protected override void OnKeyUp(KeyEventArgs e)

{

if (e.KeyCode == Keys.Delete || e.KeyCode == Keys.Back)

this.Value = null;

base.OnKeyUp(e);

}

/// <summary>

/// When Null and a Number is pressed this method takes the UDTP out of Null Mode

/// and resends the pressed key for timign reasons

/// </summary>

/// <param name="e"></param>

protected override void OnKeyPress(KeyPressEventArgs e)

{

base.OnKeyPress(e);

if (_isNull && Char.IsDigit(e.KeyChar))

{

this.Value = base.Value;

e.Handled = true;

SendKeys.Send("{RIGHT}");

SendKeys.Send(e.KeyChar.ToString());

}

else

base.OnKeyPress(e);

}

#endregion

#region OnXXXX() Modified To Enable ReadOnly Mode

/// <summary>

/// Refreshes the Visibility of the Control if the Parent Changes(So the _TextBox get moved and Redrawn)

/// If in Design Mode then return(If we dont do this then the Control could Disappear )

/// </summary>

/// <param name="e"></param>

protected override void OnParentChanged(EventArgs e)

{

base.OnParentChanged(e);

if (DesignMode || initializing)

return;

updateReadOnlyTextBoxParent();//update the _TextBox parent

setVisibility(); //Reset Visibilty for new parent

}

/// <summary>

/// Propagates Location to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnLocationChanged(EventArgs e)

{

base.OnLocationChanged(e);

_textBox.Location = this.Location;

}

/// <summary>

/// Propagates Size to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnSizeChanged(EventArgs e)

{

base.OnSizeChanged(e);

_textBox.Size = this.Size;

}

/// <summary>

/// Propagates Size to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnResize(EventArgs e)

{

base.OnResize(e);

_textBox.Size = this.Size;

}

/// <summary>

/// Propagates Dock to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnDockChanged(EventArgs e)

{

base.OnDockChanged(e);

_textBox.Dock = this.Dock;

}

/// <summary>

/// Propagates RightToLeft to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnRightToLeftChanged(EventArgs e)

{

base.OnRightToLeftChanged(e);

_textBox.RightToLeft = this.RightToLeft;

}

/// <summary>

/// Propagates TabStop to the _TextBox if TabStopWhenReadOnly == true

/// </summary>

/// <param name="e"></param>

protected override void OnTabStopChanged(EventArgs e)

{

base.OnTabStopChanged(e);

_textBox.TabStop = _tabStopWhenReadOnly && this.TabStop;

}

/// <summary>

/// Propagates TabIndex to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnTabIndexChanged(EventArgs e)

{

base.OnTabIndexChanged(e);

_textBox.TabIndex = this.TabIndex;

}

////// <summary>

/// Propagates Font to the _TextBox

/// </summary>

/// <param name="e"></param>

protected override void OnFontChanged(EventArgs e)

{

base.OnFontChanged(e);

_textBox.Font = this.Font;

}

#endregion

#region Private Methods Added To Enable ReadOnly Mode

/// <summary>

/// Added to initialize the _textBox to the default values to match the DTP

/// </summary>

private void initTextBox()

{

if (DesignMode)

return;

_textBox = new TextBox();

_textBox.ReadOnly = true;

_textBox.Location = this.Location;

_textBox.Size = this.Size;

_textBox.Dock = this.Dock;

_textBox.Anchor = this.Anchor;

_textBox.RightToLeft = this.RightToLeft;

_textBox.Font = this.Font;

_textBox.TabStop = this.TabStop;

_textBox.TabIndex = this.TabIndex;

_textBox.Visible = false;

_textBox.Parent = this.Parent;

}

private void setVisibility()

{

if (DesignMode || initializing)//Dont actually change the visibility if in Design Mode

return;

if (this._visible)

{

if (this._readOnly)

showTextBox();//If Visible and Readonly Show TextBox

else

showDTP();//If Visible and NOT ReadOnly Show DateTimePicker

}

else

{

showNone(); //If Not Visible Show Neither

}

}

private void showTextBox()

{

base.Visible = false;

_textBox.Visible = true;

_textBox.TabStop = _tabStopWhenReadOnly && this.TabStop;

}

private void showDTP()

{

_textBox.Visible = false;

base.Visible = true;

}

private void showNone()

{

_textBox.Visible = false;

base.Visible = false;

}

private void updateReadOnlyTextBoxParent()

{

if (this.Parent == null) //If UTDP.Parent == null, set _textBox.Parent == null and return

{

_textBox.Parent = null;

return;

}

if (_textBox.Parent != this.Parent) //If the Parents DO NOT already match

{

//I Added Special logic here to handle positioning the _TextBox when the UDTP is in a TableLayoutPanel

if (this.Parent.GetType() == typeof(TableLayoutPanel))

{

TableLayoutPanelCellPosition cP = ((TableLayoutPanel)this.Parent).GetPositionFromControl(this);

((TableLayoutPanel)this.Parent).Controls.Add(_textBox, cP.Column, cP.Row);

((TableLayoutPanel)this.Parent).SetColumnSpan(_textBox, ((TableLayoutPanel)this.Parent).GetColumnSpan(this));

_textBox.Anchor = this.Anchor;

}

//I added special logic here to handle positioning the _TextBox when the UDTP is in a FlowLayoutPanel

else if (this.Parent.GetType() == typeof(FlowLayoutPanel))

{

((FlowLayoutPanel)this.Parent).Controls.Add(_textBox);

((FlowLayoutPanel)this.Parent).Controls.SetChildIndex(_textBox, ((FlowLayoutPanel)this.Parent).Controls.IndexOf(this));

_textBox.Anchor = this.Anchor;

}

else //not a TableLayoutPanel or FlowLayoutPanel so just assign the parent

{

_textBox.Parent = this.Parent;

_textBox.Anchor = this.Anchor;

}

}

}

#endregion

#region Public Methods Overriden To Enable ReadOnly Mode

new public void Show()

{

this.Visible = true;

}

new public void Hide()

{

this.Visible = false;

}

#endregion

}

}

I hope this helps many many people.

I also have a UltraComboBox that addsNullable, ReadOnly, TabStopWhenReadOnly and support for Table and FlowLayoutPanels with full DataBinding support. Let me knwo if anyone would like me to post it.

m_shane_tx  Monday, January 08, 2007 9:38 PM

Thanks m_shane_tx !

I got it. You are the man!

Thanks, tdcntt

tdcntt  Tuesday, January 09, 2007 3:45 AM

This is excellent functionality. It will be nice when the native control is updated to include functionality like this.

I have two issues to resolve:

1) I just pasted the code directly into a code file and compiled for testing purposes and it gave the error that "InitializeComponent()" could not be found. Am I missing something here?

2) Commenting out th eoffending line enabled me to compile for testing but when I press "Del" or "Backspace" to null the date, the form freezes up and I have to use Task Manager to end it. Am I doing something wrong or is this related to issue 1)?

Thanks,

Bill

WBC2  Thursday, June 07, 2007 2:12 PM

It should work fine.

Don't forget though that it is a partial class autogenerated by Visual Studio.

So to make it work you need to:

1) Create a new user control in Visual Studio called "UltraDateTimePicker"

2) Delete all the code in UltraDateTimePicker.cs and replace the it with the above code.

The reason your wont recognize InitializeComponent() is because you don't have the other part of the partial class that is autogenerated by VS. (It in UltraDateTimerPicker.Designer.cs

Also, this UltraDateTimePicker uses the ISupportInitializeInterface which means it needs a BeginInit() call before seeting any control properties and a EndInit() call after setting theControls properties when the control is being loaded. Normally the Visual Designer sees the ISupportInitializeand automatically creates these calls in your Form1.Designer.cs file, but if you are using this control wihtout using the VS2005 visual editor you will have to do 2 things.

1)Call BeginInit() after decalring the control, butbefore setting any initialproperties(any at all).

2)Call EndInit() after setting all the controls initial properties(size, items, parent, anchor, etc).

I suggest something like this.

private Form1_Load(object sender, EventArgs e)

{

//#####Keep this stuff first

UltraDateTimePicker picker1 = new UltraDateTimePicker();

UltraDateTimePicker picker2 = new UltraDateTimePicker();

UltraDateTimePicker picker3 = new UltraDateTimePicker();

UltraDateTimePicker picker4 = new UltraDateTimePicker();

UltraDateTimePicker picker5 = new UltraDateTimePicker();

picker1.BeginInit();

picker2.BeginInit();

picker3.BeginInit();

picker4.BeginInit();

picker5.BeginInit();

//#####End of first stuff

//#####Middle Stuff

//Put UltraDateTimePicker Initial Property Changes here as well as any other thing you want set at from load.

//#####End of Middle Stuff

//#####Keep this stuff last

picker1.EndInit();

picker2.EndInit();

picker3.EndInit();

picker4.EndInit();

picker5.EndInit();

//#####End of last stuff

}

I hope this helps solve your problem.

Oh and you have to be wirking in .NET 2.0

Here is the UltraDateTimePicker.Designer.cs code although you shouldn't need it as VS generates it all for you.

namespace YourNameSpace

{

partial class UltraDateTimePicker

{

/// <summary>

/// Required designer variable.

/// </summary>

private System.ComponentModel.IContainer components = null;

/// <summary>

/// Clean up any resources being used.

/// </summary>

/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>

protected override void Dispose(bool disposing)

{

if (disposing && (components != null))

{

components.Dispose();

}

base.Dispose(disposing);

}

#region Component Designer generated code

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

components = new System.ComponentModel.Container();

}

#endregion

}

}

m_shane_tx  Friday, June 15, 2007 1:36 AM
If you are interested by a commercial component, I have published a beta of a new kind of control. It is called a FieldPackEditor, capable of editing any set of rule-based fields. A date/time is such a data and thus I have derived a class from the editor specially designed to be a DateTimePicker replacement. It is very important to understand that this is not a derived class of a textbox, masked textbox or DateTimePicker. It has been designed from scratch and therefore will be very flexible to fix every shortcomings of the native control.

I have added some screencasts to see it in action. This is the best way to realize how powerful it is.

Best regards,

Nicolas Cadilhac
Smart PropertyGrid.Net @ VisualHint
Microsoft PropertyGrid Resource List
Free PropertyGrid for MFC
Smart FieldPackEditor.Net / DateTimePicker


VisualHint  Saturday, June 16, 2007 12:59 AM
Thanks for the code, m_shane_tx. I tried Grazioli's NullableDateTimePicker, but it doesn't support typed-in dates. Yours needs some indication when it receives focus when in the null state, such as selecting the NullText or if empty, at least showing a cursor. Once they start typing, it works like the regular DateTimePicker, which is what I needed.

Another thing I noticed is, when they are typing, the date format changed to MM/dd/yyyy even though the CustomFormat was set to MM/dd/yy.

kaborka  Wednesday, October 17, 2007 1:43 AM
Hi,

I have a problem about using UltraDateTimePicker, please see:
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/c0b62c82-5fad-482e-ad81-c6bfd524e172

Thank you very much.

Best regards,
Diviner.
Diviner Chan  Friday, October 03, 2008 3:36 AM

The last entry on the link you provided marked as the answer is indeed correct.

UltraDateTimePicker usesthe CustomFormat property of the base class to show blank when the Date is null the CustomFormat property of the UDTP Is written to manage the null value and the base CustomFormat at the same time. Therefore when you arecasting to DateTimePicker in your code your are bypassingthe new CustomFormat property of the UltraDateTimePicker which is causingunexpected behavior

i.e.

The code "if (objControl is DateTimePicker)"evaluates totrue because UltraDateTimePicker is also a DateTimePicker by inheritance.

So casting with "((DateTimePicker)objControl).CustomFormat = dateFormat;" is changing the base class' CustomFormat Property and not the UltraDateTimePicker's CustomFormat property.

Your current "work around" mentioned in the linked post is actually the solution to the problem.

Martin Shane Horn

m_shane_tx  Monday, October 13, 2008 3:11 PM
Martin,

Thank you very much. Your explaination is clear. I get it now.

Diviner.
Diviner Chan  Friday, October 17, 2008 2:04 AM
Help VB!

I converted it, but I just cant seem to get it to work properly!!! Do you have working code?

thanks

Joe
bestlerjoe  Friday, March 20, 2009 8:19 AM
Thanks for the control!
I've made an improvement that I'd like to pass on.

If the value of the datetimepicker is null and the dropdown button is clicked I wanted it to select today's date on the MonthCalendar that drops down.
Took me awhile but I found a solution. You must override the OnDropDown method and use the user32.dll function SendMessage to set the selection on the calendar.

[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, SYSTEMTIME lParam);

private const int DTM_FIRST = 0x1000;
private const int DTM_GETMONTHCAL = (DTM_FIRST + 8);
private const int MCM_SETCURSEL = 0x1002;

[StructLayout(LayoutKind.Sequential)]
public class SYSTEMTIME
{
public short wYear;
public short wMonth;
public short wDayOfWeek;
public short wDay;
public short wHour;
public short wMinute;
public short wSecond;
public short wMilliseconds;
}

private SYSTEMTIME DateTimeToSysTime(DateTime time)
{
return new SYSTEMTIME
{
wYear = (short)time.Year,
wMonth = (short)time.Month,
wDayOfWeek = (short)time.DayOfWeek,
wDay = (short)time.Day,
wHour = (short)time.Hour,
wMinute = (short)time.Minute,
wSecond = (short)time.Second,
wMilliseconds = 0
};
}

protected override void OnDropDown(EventArgs eventargs)
{
if (this.Value == null)
{
DateTime d = DateTime.Now.Date;
//Reset value for the DateTimePicker control
this.Value = d;

//retrieve the handle of the MonthCalendar in the DateTimePicker
IntPtr p = SendMessage(this.Handle, DTM_GETMONTHCAL, 0, 0);

//Re-select the date on the MonthCalendar
SYSTEMTIME t = DateTimeToSysTime(DateTime.Now.Date);
SendMessage(p, MCM_SETCURSEL, 0, t);
}
base.OnDropDown(eventargs);
}
thomasnast  Tuesday, July 07, 2009 3:11 PM
m_shane_tx where can I find the UltraComboBox you refer at this post ? thx.
Plaguebreath  Saturday, August 08, 2009 5:03 PM
Thank you for this improvement.

I found that I needed to include your code because without it the drop down calendar would become unresponsive to mouse clicks after the date was deleted and became null. The drop down calendar could be restored to functionality if I closed it and then typed a number into the control. After that the drop down would behave as expected.
pilotindemand  Monday, September 28, 2009 11:20 AM

You can use google to search for other answers

Custom Search

More Threads

• How to prevent "FileNotFoundException"?
• Convert to bitmap after Rotate transform
• Problems with remove() in System.Windows.Forms.TreeNodeCollection
• Highest value in listbox
• ListBox sorting
• TrackBar.BackColor does not use VisualStyles of Parent TabPage???
• Scrollbar fires twice on every click?
• Parent/Child Forms
• WebBrowser control and User Agent string
• Child control event on parent form resize