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 Gestion_DVSG
{
///
/// 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.
///
public 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
///
/// Basic Constructer + ReadOnly _textBox initialization
///
public UltraDateTimePicker(): base()
{
//InitializeComponent();
initTextBox();
base.Format = DateTimePickerFormat.Custom;
_Format = DateTimePickerFormat.Long;
if (DesignMode)setFormat();
}
#endregion
#region ISupportInitialize Members
private bool initializing = true;
public void BeginInit()
{
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;
///
/// Modified Value Propety now of type Object and uses MinDate to mark null values
///
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 not 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;
}
}
///
/// NullText property is used to access/change the Text shown when the UDTP is null
///
#region DesignerModifiers
[Browsable(true)]
[Category("Behavior")]
[Description("Text shown when DateTime is 'null'")]
[DefaultValue("")]
#endregion
public string NullText
{
get { return _nullText; }
set { _nullText = value; }
}
///
/// Modified Format Property stores the assigned Format and the propagates the change to base.CustomFormat
///
#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;
}
}
}
///
/// Modified CustomFormat Property stores the assigned CustomFormat when null, otherwise functions as normal
///
new public string CustomFormat
{
get { return _CustomFormat; }
set
{
this._CustomFormat = value;
this.setFormat();
}
}
#endregion
#region Public Properties Modified/Added To Enable ReadOnly Mode
///
/// 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 )
///
#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();
}
}
///
/// 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.
///
#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
}
}
///
/// Modified the TabStop Property to support the added TabStopWhenReadOnly property
///
new public bool TabStop
{
get { return base.TabStop; }
set
{
base.TabStop = value;
_textBox.TabStop = (_tabStopWhenReadOnly && base.TabStop);
}
}
///
/// 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 )
///
new public bool Visible
{
get { return _visible; }
set
{
_visible = value;
setVisibility();
}
}
#endregion
#region OnXXXX() Modified To Allow Null Values
///
/// Used to change the UDTP.Value on Closeup(Without this code Closeup only changes the base.Value)
///
///
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;
}
///
///Sets UDTP Value to null when Delete or Backspace is pressed
///
///
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete || e.KeyCode == Keys.Back)
this.Value = null;
base.OnKeyUp(e);
}
///
/// When Null and a Number is pressed this method takes the UDTP out of Null Mode
/// and resends the pressed key for timign reasons
///
///
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
///
/// 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 )
///
///
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
if (DesignMode || initializing)
return;
updateReadOnlyTextBoxParent();//update the _TextBox parent
setVisibility(); //Reset Visibilty for new parent
}
///
/// Propagates Location to the _TextBox
///
///
protected override void OnLocationChanged(EventArgs e)
{
base.OnLocationChanged(e);
_textBox.Location = this.Location;
}
///
/// Propagates Size to the _TextBox
///
///
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
_textBox.Size = this.Size;
}
///
/// Propagates Size to the _TextBox
///
///
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
_textBox.Size = this.Size;
}
///
/// Propagates Dock to the _TextBox
///
///
protected override void OnDockChanged(EventArgs e)
{
base.OnDockChanged(e);
_textBox.Dock = this.Dock;
}
///
/// Propagates RightToLeft to the _TextBox
///
///
protected override void OnRightToLeftChanged(EventArgs e)
{
base.OnRightToLeftChanged(e);
_textBox.RightToLeft = this.RightToLeft;
}
///
/// Propagates TabStop to the _TextBox if TabStopWhenReadOnly == true
///
///
protected override void OnTabStopChanged(EventArgs e)
{
base.OnTabStopChanged(e);
_textBox.TabStop = _tabStopWhenReadOnly && this.TabStop;
}
///
/// Propagates TabIndex to the _TextBox
///
///
protected override void OnTabIndexChanged(EventArgs e)
{
base.OnTabIndexChanged(e);
_textBox.TabIndex = this.TabIndex;
}
//////
/// Propagates Font to the _TextBox
///
///
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
_textBox.Font = this.Font;
}
#endregion
#region Private Methods Added To Enable ReadOnly Mode
///
/// Added to initialize the _textBox to the default values to match the DTP
///
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
}
}