|
Hi,
Thanks for the great insight into the DateTimePicker control, it worked fabulously. However, the drop-down arrow button (same as combo-box drop-down button) doesn't appear to be flat styled. Is there a recent update that overrides this as well?
Cheers,
Kaine
P.S. - The button appears flat at design time but 3D during runtime. K
|
|
|
|
|
First, I wanted to say thanks for a great control. I have a problem and I am not sure exactly how to fix it. The flat DateTimePicker displays a calendar icon next to the dropdown arrow and on my Vista machine the calendar looking icon is slightly cut off by the dropdown arrow. Is there a way to make this icon move further left or disappear completely. Any help you can give me on this would be greatly appreciated.
|
|
|
|
|
I must say this is fine control.
I want to support showupdown property, so up-down box should be flat control.
The code that I change is:
private void OverrideDropDown(Graphics g)
{
if (!this.ShowUpDown)
{
Rectangle rect = new Rectangle(this.Width - DropDownButtonWidth, 0, DropDownButtonWidth, this.Height);
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat);
}
else
{
Rectangle rect = new Rectangle(this.Width - DropDownButtonWidth,
0, DropDownButtonWidth, this.Height/2);
ControlPaint.DrawScrollButton(g, rect, ScrollButton.Up, ButtonState.Flat);
rect.Y += this.Height / 2;
ControlPaint.DrawScrollButton(g, rect, ScrollButton.Down, ButtonState.Flat);
}
}
But it does not display, flat up-down button continuously. I want to understand the reason for same.
Whether this is solvable or not, that is secondary thing.
|
|
|
|
|
I had a quick look and it seems like when ShowUpDown is true, this control has a child control with the name "msctls_updown32" (I found it through Spy++). It looks like the child control repaint itself and hence caused the inconsitency with the float display you did above. I'm not sure how you can intercept the child window paint message, but if you can that will probably be the way to get this working nicely.
|
|
|
|
|
Hi Fadrian, good job!
I would just add a small change on the SendMessage API declaration, to use the correct one and avoid the PInvoke exception (unbalanced stack):
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hwnd, int Msg, IntPtr wParam, IntPtr lParam);
Then, when calling, just pass IntPtr.Zero, instead of 0:
...
SendMessage(this.Handle, WM_ERASEBKGND, hDC, IntPtr.Zero);
...
SendMessage(this.Handle, WM_PRINTCLIENT, ptrClientDC, IntPtr.Zero);
Cheers,
modified 25-May-20 19:20pm.
|
|
|
|
|
I would like to change the color of the red rectangle which highlight the selected date and which appears at the left of Today.
There is also another limitation I would like to work around. When you change the CalendarTitleBackColor the font of DaysOfWeek also change its color, I want to hack this so that I can assign the color for DaysOfWeek on another property. Do you guys have any clue about how to do this?
Thanks in advance,
|
|
|
|
|
Here is the equivalent VB version.
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Runtime.InteropServices
Public class FlatDateTimePicker
Inherits DateTimePicker
Private Class ComboInfoHelper
Private Declare Function GetComboBoxInfo Lib "user32.dll" (hwndCombo As Int32, ByRef info As ComboBoxInfo) As Boolean
Private Structure RECT
public Left As Int32
public Top As Int32
public Right As Int32
public Bottom As Int32
End Structure
Private Structure ComboBoxInfo
Public cbSize As Int32
Public rcItem As RECT
Public rcButton As RECT
Public stateButton As IntPtr
Public hwndCombo As IntPtr
Public hwndEdit As IntPtr
Public hwndList As IntPtr
End Structure
Public Shared Function GetComboDropDownWidth() As Int32
Dim cb As ComboBox = new ComboBox()
Dim width As Int32 = GetComboDropDownWidth(cb.Handle)
cb.Dispose()
Return Width
End Function
Public Shared Function GetComboDropDownWidth(ByVal handle As IntPtr) As Int32
Dim cbi As ComboBoxInfo = New ComboBoxInfo()
cbi.cbSize = Marshal.SizeOf(cbi)
GetComboBoxInfo(handle.ToInt32, cbi)
Dim Width As Int32 = cbi.rcButton.Right - cbi.rcButton.Left
Return Width
End Function
End Class
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA"(hwnd As Int32, wMsg As Int32, wParam As Int32, lParam As Int32) As Int32
Private Declare Function GetWindowDC Lib "user32.dll"(hwnd As Int32) As IntPtr
Private Declare Function ReleaseDC Lib "user32.dll"(hwnd As Int32, hDC As Int32) As Int32
Const WM_ERASEBKGND As Int32 = &H14
Const WM_PAINT As Int32 = &HF
Const WM_NC_HITTEST As Int32 = &H84
Const WM_NC_PAINT As Int32 = &H85
Const WM_PRINTCLIENT As Int32 = &H318
Const WM_SETCURSOR As Int32 = &H20
Private BorderPen As Pen = New Pen(Color.Black, 2)
Private BorderPenControl As Pen = New Pen(SystemColors.ControlDark, 2)
Private DroppedDown As Boolean = False
Private InvalidateSince As Int32 = 0
Private Shared DropDownButtonWidth As Int32 = 17
Shared Sub New()
'2 pixel extra is for the 3D border around the pulldown button on the left and right
DropDownButtonWidth = ComboInfoHelper.GetComboDropDownWidth() + 2
End Sub
Public Sub New()
MyBase.New()
Me.SetStyle(ControlStyles.DoubleBuffer, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
End Sub
Protected Overrides Function IsInputKey(ByVal keyData As System.Windows.Forms.Keys) As Boolean
If keyData = (Keys.Tab Or Keys.Shift) Then
Return True
ElseIf keyData = Keys.Tab Then
Return True
Else
Return MyBase.IsInputKey(keyData)
End If
End Function
Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)
MyBase.OnValueChanged(eventargs)
Me.Invalidate()
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Dim hDC As IntPtr = IntPtr.Zero
Dim gdc As Graphics = Nothing
Select Case m.Msg
Case WM_NC_PAINT
hDC = GetWindowDC(m.HWnd.ToInt32)
gdc = Graphics.FromHdc(hDC)
SendMessage(Me.Handle.ToInt32, WM_ERASEBKGND, hDC.ToInt32, 0)
SendPrintClientMsg()
SendMessage(Me.Handle.ToInt32, WM_PAINT, IntPtr.Zero.ToInt32, 0)
OverrideControlBorder(gdc)
m.Result = New IntPtr(1) 'Indicate msg has been processed
ReleaseDC(m.HWnd.ToInt32, hDC.ToInt32)
gdc.Dispose()
Case WM_PAINT
MyBase.WndProc(m)
hDC = GetWindowDC(m.HWnd.ToInt32)
gdc = Graphics.FromHdc(hDC)
OverrideDropDown(gdc)
OverrideControlBorder(gdc)
ReleaseDC(m.HWnd.ToInt32, hDC.ToInt32)
gdc.Dispose()
Case WM_SETCURSOR
MyBase.WndProc(m)
' The value 3 is discovered by trial on error, and cover all kinds of scenarios
' InvalidateSince < 2 wil have problem if the control is not in focus and dropdown is clicked
If DroppedDown And (InvalidateSince < 3) Then
Invalidate()
InvalidateSince = InvalidateSince + 1
End If
Case Else
MyBase.WndProc(m)
End Select
End Sub
Private Sub SendPrintClientMsg()
'We send this message for the control to redraw the client area
Dim gClient As Graphics = Me.CreateGraphics()
Dim ptrClientDC As IntPtr = gClient.GetHdc()
SendMessage(Me.Handle.ToInt32, WM_PRINTCLIENT, ptrClientDC.ToInt32, 0)
gClient.ReleaseHdc(ptrClientDC)
gClient.Dispose()
End Sub
Private Sub OverrideDropDown(ByVal g As Graphics)
If Not Me.ShowUpDown Then
Dim rect As Rectangle = New Rectangle(Me.Width - DropDownButtonWidth, 0, DropDownButtonWidth, Me.Height)
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat)
End If
End Sub
Private Sub OverrideControlBorder(ByVal g As Graphics)
If Not Me.Focused Or Not Me.Enabled Then
g.DrawRectangle(BorderPenControl, New Rectangle(0, 0, Me.Width, Me.Height))
Else
g.DrawRectangle(BorderPen, New Rectangle(0, 0, Me.Width, Me.Height))
End If
End Sub
Protected Overrides Sub OnDropDown(ByVal eventargs As EventArgs)
InvalidateSince = 0
DroppedDown = True
MyBase.OnDropDown(eventargs)
End Sub
Protected Overrides Sub OnCloseUp(ByVal eventargs As EventArgs)
DroppedDown = False
MyBase.OnCloseUp(eventargs)
End Sub
Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
MyBase.OnLostFocus(e)
Me.Invalidate()
End Sub
Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
MyBase.OnGotFocus(e)
Me.Invalidate()
End Sub
Protected Overrides Sub OnResize(ByVal e As EventArgs)
MyBase.OnResize(e)
Me.Invalidate()
End Sub
End Class
Malcolm Powell
|
|
|
|
|
I forgot to remove the following function before posting the VB version.
Protected Overrides Function IsInputKey(ByVal keyData As System.Windows.Forms.Keys) As Boolean
If keyData = (Keys.Tab Or Keys.Shift) Then
Return True
ElseIf keyData = Keys.Tab Then
Return True
Else
Return MyBase.IsInputKey(keyData)
End If
End Function
This was added because I needed special Tab key handling. You should remove this function if you want the default Tab key behaviour.
Malcolm Powell
|
|
|
|
|
Good work. That's exactly what I needed. Of course my thanks go also to Fadrian!
|
|
|
|
|
This is a work in progress, but can be used as it stands right now. This is an ExtendedDateTimePicker class which inherits from Windows.Systems.Forms.DateTimePicker and so can be used as a direct replacement.
**This control does not behave correctly as far as drawing in design mode, so if you can assist in that area it would be greatly appreciated....
#region ExtendedDateTimePicker<br />
public class ExtendedDateTimePicker : System.Windows.Forms.DateTimePicker<br />
{<br />
#region Enums<br />
public enum FLATSTYLE_STYLES<br />
{<br />
None = 0,<br />
Popup = 1,<br />
Standard = 2,<br />
System = 3<br />
}<br />
#endregion<br />
<br />
#region Property Variables<br />
public FLATSTYLE_STYLES _flatStyle = FLATSTYLE_STYLES.Standard;<br />
#endregion<br />
<br />
#region Properties<br />
[Browsable(true)]<br />
[EditorBrowsable(EditorBrowsableState.Always)]<br />
[Category("Appearance")]<br />
[Description("Determines the display of the control.")]<br />
public FLATSTYLE_STYLES FlatStyle<br />
{<br />
get { return _flatStyle; }<br />
set <br />
{ <br />
_flatStyle = value;<br />
if (_flatStyle == FLATSTYLE_STYLES.Popup)<br />
{<br />
_dropButton = new DropDownButton(this);<br />
_dropButton.Width = _buttonWidth + 2;<br />
_dropButton.Height = this.Height - 2;<br />
_dropButton.Top = 0;<br />
this.Controls.Add(_dropButton);<br />
if (this.RightToLeft == RightToLeft.No || !this.RightToLeftLayout)<br />
_dropButton.Left = this.Width - (_buttonWidth + 2);<br />
}<br />
else<br />
{<br />
_dropButton = null;<br />
}<br />
this.Invalidate(); }<br />
}<br />
#endregion<br />
<br />
private System.ComponentModel.Container components = null;<br />
<br />
#region Member variables<br />
protected int _buttonWidth = 17;<br />
protected DropDownButton _dropButton = null;<br />
protected bool _dropped = false;<br />
#endregion<br />
<br />
#region Contructors<br />
public ExtendedDateTimePicker()<br />
{<br />
InitializeComponent();<br />
<br />
_buttonWidth = GetComboDropDownWidth();<br />
}<br />
<br />
protected override void Dispose(bool disposing)<br />
{<br />
if (disposing)<br />
{<br />
if (components != null)<br />
components.Dispose();<br />
}<br />
base.Dispose(disposing);<br />
}<br />
#endregion<br />
<br />
#region Designer Generated code<br />
private void InitializeComponent()<br />
{<br />
components = new System.ComponentModel.Container();<br />
}<br />
#endregion<br />
<br />
#region Overridden Control Functions<br />
protected override void OnMouseEnter(EventArgs e)<br />
{<br />
base.OnMouseEnter(e);<br />
<br />
if (_flatStyle == FLATSTYLE_STYLES.Popup)<br />
{<br />
Win32.SendMessage(this.Handle, Win32.WM_NCPAINT, (IntPtr)1, 0);<br />
_dropButton.Invalidate();<br />
}<br />
}<br />
<br />
protected override void OnMouseLeave(EventArgs e)<br />
{<br />
base.OnMouseLeave(e);<br />
<br />
if (_flatStyle == FLATSTYLE_STYLES.Popup)<br />
{<br />
Win32.SendMessage(this.Handle, Win32.WM_NCPAINT, (IntPtr)1, 0);<br />
_dropButton.Invalidate();<br />
}<br />
}<br />
<br />
protected override void OnGotFocus(EventArgs e)<br />
{<br />
base.OnGotFocus(e);<br />
<br />
if (_flatStyle == FLATSTYLE_STYLES.Popup)<br />
{<br />
Win32.SendMessage(this.Handle, Win32.WM_NCPAINT, (IntPtr)1, 0);<br />
_dropButton.Invalidate();<br />
}<br />
}<br />
<br />
protected override void OnLostFocus(EventArgs e)<br />
{<br />
base.OnLostFocus(e);<br />
<br />
if (_flatStyle == FLATSTYLE_STYLES.Popup)<br />
{<br />
Win32.SendMessage(this.Handle, Win32.WM_NCPAINT, (IntPtr)1, 0);<br />
_dropButton.Invalidate();<br />
}<br />
}<br />
<br />
protected override void OnResize(EventArgs e)<br />
{<br />
base.OnResize(e);<br />
<br />
if (_flatStyle == FLATSTYLE_STYLES.Popup)<br />
{<br />
if (this.RightToLeft == RightToLeft.No || !this.RightToLeftLayout)<br />
_dropButton.Left = this.Width - (_buttonWidth + 2);<br />
<br />
Win32.SendMessage(this.Handle, Win32.WM_NCPAINT, (IntPtr)1, 0);<br />
}<br />
}<br />
<br />
protected override void WndProc(ref Message m)<br />
{<br />
IntPtr hDC = IntPtr.Zero;<br />
Graphics gDC = null;<br />
<br />
<br />
if (_flatStyle == FLATSTYLE_STYLES.Popup)<br />
{<br />
switch (m.Msg)<br />
{<br />
case Win32.WM_NCCALCSIZE:<br />
if (m.WParam == IntPtr.Zero)<br />
{<br />
Win32.NCCALCSIZE_PARAMS csp;<br />
<br />
csp = (Win32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam,<br />
typeof(Win32.NCCALCSIZE_PARAMS));<br />
<br />
csp.rgrc0.Top += 1;<br />
csp.rgrc0.Bottom -= 1;<br />
csp.rgrc0.Left += 1;<br />
csp.rgrc0.Right -= 1;<br />
<br />
Marshal.StructureToPtr(csp, m.LParam, false);<br />
}<br />
else if (m.WParam == new IntPtr(1))<br />
{<br />
Win32.NCCALCSIZE_PARAMS csp;<br />
<br />
csp = (Win32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam,<br />
typeof(Win32.NCCALCSIZE_PARAMS));<br />
<br />
csp.rgrc0.Top += 1;<br />
csp.rgrc0.Bottom -= 1;<br />
csp.rgrc0.Left += 1;<br />
csp.rgrc0.Right -= 1;<br />
Marshal.StructureToPtr(csp, m.LParam, false);<br />
}<br />
break;<br />
case Win32.WM_NCPAINT:<br />
hDC = Win32.GetWindowDC(m.HWnd);<br />
gDC = Graphics.FromHdc(hDC);<br />
Win32.SendMessage(m.HWnd, Win32.WM_ERASEBKGND, hDC, 0);<br />
OverrideControlBorder(gDC);<br />
m.Result = (IntPtr)0;
Win32.ReleaseDC(m.HWnd, hDC);<br />
gDC.Dispose();<br />
break;<br />
default:<br />
base.WndProc(ref m);<br />
break;<br />
}<br />
}<br />
else<br />
{<br />
base.WndProc(ref m);<br />
}<br />
}<br />
#endregion<br />
<br />
#region Utility Functions<br />
[Browsable(false)]<br />
private bool IsMouseInControl<br />
{<br />
get<br />
{<br />
if (this.DesignMode)<br />
{<br />
return false;<br />
}<br />
<br />
Point mPos = Control.MousePosition;<br />
bool retVal = this.ClientRectangle.Contains(this.PointToClient(mPos));<br />
return retVal;<br />
}<br />
}<br />
<br />
private void OverrideControlBorder(Graphics g)<br />
{<br />
bool allowHot = (this.Enabled && !this.DesignMode) && !(this.IsMouseInControl && Control.MouseButtons == MouseButtons.Left && !this.ContainsFocus);<br />
bool hot = (this.ContainsFocus || this.IsMouseInControl) && allowHot;<br />
Pen border = null;<br />
<br />
Rectangle rect = new Rectangle(0, 0,<br />
this.ClientRectangle.Width + 1, this.ClientRectangle.Height + 1);<br />
<br />
if (allowHot && hot)<br />
{<br />
border = new Pen(SystemColors.GrayText, 1);<br />
}<br />
else<br />
{<br />
border = new Pen(Color.White, 1);<br />
}<br />
<br />
g.DrawRectangle(border, rect);<br />
border.Dispose();<br />
}<br />
<br />
private void OverrideDropDown(Graphics g)<br />
{<br />
bool allowHot = (this.Enabled && !this.DesignMode) && !(this.IsMouseInControl && Control.MouseButtons == MouseButtons.Left && !this.ContainsFocus);<br />
bool hot = (this.ContainsFocus || this.IsMouseInControl) && allowHot;<br />
Pen border = null;<br />
Brush button = new SolidBrush(SystemColors.Control);<br />
<br />
border = (allowHot && hot) ? new Pen(SystemColors.GrayText, 1) : new Pen(Color.White, 1);<br />
<br />
if (!this.ShowUpDown)<br />
{<br />
Rectangle rect = new Rectangle(this.Width - _buttonWidth + 1, 0, _buttonWidth - 1, this.Height - 1);<br />
g.DrawRectangle(border, rect);<br />
rect.Inflate(-1, -1);<br />
rect.Height += 1;<br />
g.FillRectangle(button, rect);<br />
}<br />
else<br />
{<br />
Rectangle rect = new Rectangle(this.Width - _buttonWidth + 1, 0, _buttonWidth - 1, this.Height - 1);<br />
g.DrawRectangle(border, rect);<br />
rect.Inflate(-1, -1);<br />
rect.Height += 1;<br />
g.FillRectangle(button, rect); <br />
}<br />
border.Dispose();<br />
button.Dispose();<br />
}<br />
<br />
protected int GetComboDropDownWidth()<br />
{<br />
ComboBox cbo = new ComboBox();<br />
Win32.COMBOBOXINFO cbi = new Win32.COMBOBOXINFO();<br />
cbi.cbSize = Marshal.SizeOf(cbi);<br />
Win32.GetComboBoxInfo(cbo.Handle, ref cbi);<br />
cbo.Dispose();<br />
return cbi.rcButton.Right - cbi.rcButton.Left;<br />
}<br />
#endregion<br />
<br />
#region DropDownButton Private Class<br />
protected class DropDownButton : System.Windows.Forms.Button<br />
{<br />
#region Windows API<br />
[DllImport("user32")]<br />
protected static extern IntPtr GetWindowDC(IntPtr hWnd);<br />
<br />
[DllImport("user32")]<br />
protected static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);<br />
#endregion<br />
<br />
private System.ComponentModel.Container components = null;<br />
<br />
#region Member Variables<br />
private ExtendedDateTimePicker _owner = null;<br />
#endregion<br />
<br />
#region Constructor<br />
public DropDownButton(<br />
ExtendedDateTimePicker owner)<br />
{<br />
InitializeComponent();<br />
<br />
this._owner = owner;<br />
this.SetStyle(ControlStyles.UserPaint, true);<br />
this.SetStyle(ControlStyles.Selectable, false);<br />
this.FlatStyle = FlatStyle.Popup;<br />
}<br />
<br />
protected override void Dispose(bool disposing)<br />
{<br />
if (disposing)<br />
{<br />
if (components != null)<br />
components.Dispose();<br />
}<br />
base.Dispose(disposing);<br />
}<br />
#endregion<br />
<br />
#region Designer Generated code<br />
private void InitializeComponent()<br />
{<br />
components = new System.ComponentModel.Container();<br />
}<br />
#endregion<br />
<br />
#region Overrides<br />
protected override void OnPaint(PaintEventArgs e)<br />
{<br />
DrawControlBorder(e.Graphics);<br />
DrawButton(e.Graphics);<br />
DrawGlyph(e.Graphics);<br />
}<br />
<br />
protected override void OnMouseEnter(EventArgs e)<br />
{<br />
base.OnMouseEnter(e);<br />
<br />
Win32.SendMessage(_owner.Handle, Win32.WM_NCPAINT, (IntPtr)1, 0);<br />
}<br />
<br />
protected override void OnMouseLeave(EventArgs e)<br />
{<br />
base.OnMouseLeave(e);<br />
<br />
Win32.SendMessage(_owner.Handle, Win32.WM_MOUSELEAVE, IntPtr.Zero, 0);<br />
}<br />
<br />
protected override void OnMouseDown(MouseEventArgs mevent)<br />
{<br />
base.OnMouseDown(mevent);<br />
<br />
if (mevent.Button == MouseButtons.Left)<br />
{<br />
int x = _owner.Width - 10;<br />
int y = _owner.Height / 2;<br />
int lParam = x + y * 0x00010000;<br />
Win32.SendMessage(_owner.Handle, Win32.WM_LBUTTONDOWN, (IntPtr)1, lParam);<br />
}<br />
}<br />
#endregion<br />
<br />
#region Drawing Functions<br />
protected virtual void DrawControlBorder(Graphics g)<br />
{<br />
<br />
bool allowHot = (this.Enabled && !this.DesignMode) &&<br />
!(this.IsMouseInControl && this.ContainsFocus && _owner.ContainsFocus);<br />
bool hot = (this.ContainsFocus || this.IsMouseInControl || _owner.ContainsFocus) && allowHot;<br />
<br />
Rectangle rect = new Rectangle(0, 0,<br />
this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1);<br />
<br />
Pen border = (hot) ? new Pen(SystemColors.GrayText, 1) : new Pen(Color.White, 1);<br />
<br />
RightToLeft rtl = _owner.RightToLeft;<br />
bool rtll = _owner.RightToLeftLayout;<br />
<br />
Point leftTop = new Point(rect.Left, rect.Top);<br />
Point leftBottom = new Point(rect.Left, rect.Bottom);<br />
Point topLeft = new Point(rect.Left + 1, rect.Top);<br />
Point topRight = new Point(rect.Right - 1, rect.Top);<br />
Point rightTop = new Point(rect.Right, rect.Top);<br />
Point rightBottom = new Point(rect.Right, rect.Bottom);<br />
Point bottomLeft = new Point(rect.Left + 1, rect.Bottom);<br />
Point bottomRight = new Point(rect.Right - 1, rect.Bottom);<br />
<br />
Pen sides = new Pen(SystemColors.Control, 1);<br />
<br />
g.DrawLine((rtl == RightToLeft.Yes || rtll) ? sides : border, leftTop, leftBottom);<br />
g.DrawLine(sides, topLeft, topRight);<br />
g.DrawLine((rtl == RightToLeft.Yes || rtll) ? border : sides, rightTop, rightBottom);<br />
g.DrawLine(sides, bottomRight, bottomLeft);<br />
<br />
border.Dispose();<br />
sides.Dispose();<br />
}<br />
<br />
protected virtual void DrawButton(Graphics g)<br />
{<br />
bool allowHot = (this.Enabled && !this.DesignMode) &&<br />
!(this.IsMouseInControl && Control.MouseButtons == MouseButtons.Left<br />
&& !this.ContainsFocus);<br />
bool hot = (this.ContainsFocus || this.IsMouseInControl) && allowHot;<br />
<br />
Rectangle rect = new Rectangle(1, 1,<br />
this.ClientRectangle.Width - 2, this.ClientRectangle.Height - 2);<br />
<br />
Brush filler = (hot) ? new SolidBrush(SystemColors.Control) : <br />
new SolidBrush(SystemColors.Control);<br />
<br />
g.FillRectangle(filler, rect);<br />
filler.Dispose();<br />
}<br />
<br />
protected virtual void DrawGlyph(Graphics g)<br />
{<br />
RightToLeft rtl = _owner.RightToLeft;<br />
bool rtll = _owner.RightToLeftLayout;<br />
<br />
int xC = this.ClientRectangle.Left + ((this.ClientRectangle.Width / 2) - 1);<br />
int yC = this.ClientRectangle.Top + ((this.ClientRectangle.Height / 2) - 1);<br />
<br />
Pen pen = new Pen(SystemColors.ControlText, 1);<br />
<br />
g.DrawLine(pen, xC - 2, yC - 1, xC + 2, yC - 1);<br />
g.DrawLine(pen, xC - 1, yC, xC + 1, yC);<br />
g.DrawLine(pen, xC, yC - 1, xC, yC + 1);<br />
<br />
pen.Dispose();<br />
}<br />
#endregion<br />
<br />
#region Utilities<br />
[Browsable(false)]<br />
private bool IsMouseInControl<br />
{<br />
get<br />
{<br />
if (this.DesignMode)<br />
{<br />
return false;<br />
}<br />
<br />
Point mPos = Control.MousePosition;<br />
bool retVal = this.ClientRectangle.Contains(this.PointToClient(mPos)) ||<br />
_owner.ClientRectangle.Contains(_owner.PointToClient(mPos));<br />
return retVal;<br />
}<br />
}<br />
#endregion<br />
}<br />
#endregion<br />
}<br />
#endregion
ToDo:
1. Make it draw properly in designer
2. Support the blue highlighting with the blue button when mousing over like VS2005 comboboxes
3. Figure out the difference between System and Standard drawing styles in other controls to implement here
4. Implement None flatstyle
5. Make RightToLeft adjustments work correctly (something is screwy there with the button as it doesn't draw as expected);
How it works:
Since pre-Windows Vista, there was no way to retrieve the internal structure of the datetimepicker control, I simply created an internal class to create and draw a button as a child of the date time picker, and simple moved my button over the datetimepicker's button. The button is only created when the control's flatstyle is set to Popup at this time. All other styles simple are the default datetimepicker.
|
|
|
|
|
Hi,
Not sure if I can help, but if you send the complete source code to me, I will give it a try. At the moment it is missing the Win32 class for the above code to compile
Fadrian
|
|
|
|
|
Here is the Win32.cs file contents. It only consists of API information:
using System;<br />
using System.Collections.Generic;<br />
using System.Text;<br />
using System.Runtime.InteropServices;<br />
<br />
namespace ExtendedControls<br />
{<br />
internal class Win32<br />
{<br />
#region Windows Constants<br />
public const int WM_NCCALCSIZE = 131;<br />
public const int WM_NCHITTEST = 132;<br />
public const int WM_NCPAINT = 133;<br />
public const int WM_NCMOUSEMOVE = 160;<br />
<br />
public const int WM_SETFOCUS = 7;<br />
public const int WM_KILLFOCUS = 8;<br />
public const int WM_PAINT = 15;<br />
public const int WM_ERASEBKGND = 14;<br />
public const int WM_SETCURSOR = 32;<br />
public const int WM_MOUSEMOVE = 512;<br />
public const int WM_LBUTTONDOWN = 513;<br />
public const int WM_LBUTTONUP = 514;<br />
public const int WM_MOUSELEAVE = 675;<br />
public const int WM_PRINTCLIENT = 792;<br />
public const int CB_GETCOMBOBOXINFO = 356;<br />
<br />
public const int GWL_STYLE = -16;<br />
public const int GWL_EXSTYLE = -20;<br />
public const int ECM_FIRST = 0x1500;<br />
public const int EM_SETCUEBANNER = (ECM_FIRST + 1);<br />
#endregion<br />
<br />
#region Windows Structures<br />
#region RECT Structure<br />
[StructLayout(LayoutKind.Sequential)]<br />
public struct RECT<br />
{<br />
public int Left;<br />
public int Top;<br />
public int Right;<br />
public int Bottom;<br />
}<br />
#endregion<br />
<br />
#region NCCALCSIZE_PARAMS Structure<br />
[StructLayout(LayoutKind.Sequential)]<br />
public struct NCCALCSIZE_PARAMS<br />
{<br />
public RECT rgrc0, rgrc1, rgrc2;<br />
public IntPtr lppos;<br />
}<br />
#endregion<br />
<br />
#region COMBOBOXINFO Structure<br />
[StructLayout(LayoutKind.Sequential)]<br />
public struct COMBOBOXINFO<br />
{<br />
public int cbSize;<br />
public RECT rcItem;<br />
public RECT rcButton;<br />
public IntPtr stateButton;<br />
public IntPtr hwndCombo;<br />
public IntPtr hwndEdit;<br />
public IntPtr hwndList;<br />
}<br />
#endregion<br />
<br />
#region DATETIMEPICKERINFO Structure<br />
[StructLayout(LayoutKind.Sequential)]<br />
public struct DATETIMEPICKERINFO<br />
{<br />
public int cbSize;
public RECT rcCheck;
public int stateCheck;
public RECT rcButton;
public int stateButton;
public IntPtr hwndEdit;
public IntPtr hwndUD;
public IntPtr hwndDropDown;
}<br />
#endregion<br />
<br />
#endregion<br />
<br />
#region Constructor<br />
public Win32()<br />
{<br />
}<br />
#endregion<br />
<br />
#region Windows API<br />
[DllImport("user32", CharSet = CharSet.Auto)]<br />
public static extern int GetWindowLong(IntPtr hWnd, int index);<br />
<br />
[DllImport("user32", CharSet = CharSet.Auto)]<br />
public static extern int SetWindowLong(IntPtr hWnd, int index, int value);<br />
<br />
[DllImport("user32", CharSet = CharSet.Unicode)]<br />
public static extern bool SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, string lParam);<br />
<br />
[DllImport("user32", CharSet = CharSet.Auto)]<br />
public static extern bool SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, ref COMBOBOXINFO lParam);<br />
<br />
[DllImport("user32")]<br />
public static extern IntPtr GetWindowDC(IntPtr hWnd);<br />
<br />
[DllImport("user32")]<br />
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);<br />
<br />
[DllImport("user32", CharSet = CharSet.Auto)]<br />
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, int lParam);<br />
<br />
[DllImport("user32")]<br />
public static extern bool GetComboBoxInfo(IntPtr hwndCombo, ref COMBOBOXINFO info);<br />
#endregion<br />
<br />
#region Utility Functions<br />
public static int HiWord(int param)<br />
{<br />
if ((param & 0x80000000) == 0x80000000)<br />
return (param >> 16);<br />
else<br />
return (param >> 16) & 0xffff;<br />
}<br />
<br />
public static int LoWord(int param)<br />
{<br />
return param & 0xffff;<br />
}<br />
#endregion<br />
}<br />
}<br />
-- modified at 11:35 Monday 7th May, 2007
There is also a strange bug that I cannot seem to pinpoint. In Popup style, when you mouse out of the right hand side of the control slowly, the border on the button does not redraw. Or, if it does redraw, the code is coming up with a result that the mouse is still in the control. If you drag out at a normal or fast speed, then the button border draws correctly. It's only one the right hand side though.
|
|
|
|
|
Ok, to fix the popup in design mode issue, you just have to remove the drop down control in the else block of the FlatStyle property.
else
{
if (this.Controls.Contains(_dropButton))
this.Controls.Remove(_dropButton);
_dropButton = null;
}
To fix the mouse move issue, modify the DropDownButton class:
Add field: bool mouseEnter
Set the value to true in the MouseEnter event and false in the MouseLeave event.
Then, modify the statement in IsMouseInControl property to use that value
bool retVal = (mouseEnter && this.ClientRectangle.Contains(this.PointToClient(mPos))) ||
_owner.ClientRectangle.Contains(_owner.PointToClient(mPos));
Other comments:
- Instead of creating new Pen object everywhere and dispose it, try using the predefined pen
Pen border = (hot) ? SystemPens.HotTrack : Pens.White;
- You can use ControlPaint to draw the
ControlPaint.DrawComboButton(g, this.ClientRectangle, ButtonState.Flat);
- Instead of comparing your LParam or WParam with new IntPtr(value), you can cast it
if ((int) m.WParam == 1)
Hope the above comments help
Fadrian
|
|
|
|
|
Update on the flat DateTimePicker:
First off, thanks Fadrian for suggesting not creating my own pens. That's just me being stupid, so definitely cudos on that one.
Ok, the drawing in design mode is nearly working, just the button draws the border when you click on the control. Not sure why, but !this.designmode isn't making the bool statement false, and it should.
As far as the mouseleave issue, that was also my fault. I did have a function that checks if the mouse is in the control based on the pointtoclient, but my button was a few pixels too wide, and so even when the mouse was out of the control, it was technically still over the button. Fixed that problem by making the button the correct size. Not even sure why I was enlarging the button by 2 pixels...anyway
I tried using the ControlPaint.DrawComboButton function. Great little built in function, but does not work correctly in this situation. The function draws the flat button with the dark gray borders all the time (in this case, not desireable). Also, the glyph on the button does not match the combobox glyph when in Popup style. So I'm going to keep my drawing functions in there.
Also, at this time, I've added the 'None' style flat style. So basically, at this point, all the styles work the same as the combobox as I can't see a difference between Standard and System when it comes to these controls in particular.
If anyone is interested in the code and the dll file at this point, let me know and I'll put it up on my ftp server. Included in the source code is a textbox control with flatstyle and cuebanner support, and also combobox with cuebanner support.
|
|
|
|
|
Great to hear that it is almost working now.
It will be great if you can send me a copy of the complete source for future reference. Only if you don't mind I'm thinking that if there is further post on this, with the source code in hand I maybe be able to help answering.
My email fsudaman@gmail.com
Thanks,
FS
|
|
|
|
|
|
Is there a VB version?
Thanks
|
|
|
|
|
Sorry for the late response. I missed this post completely.
I dont have a VB.NET version but you can just take the code and compile it as a library and include that to your project. Check this post for more info.
http://www.codeproject.com/cs/combobox/ComboBox_appears_flat.asp?df=100&forumid=43335&fr=26&select=994303#xx994303xx
|
|
|
|
|
I just posted a VB version but made it a new thread with the same title by mistake. This message is just to trigger automatic email for this thread.
Malcolm Powell
|
|
|
|
|
Hello,
When I'm using FlatDateTimePicker class.
The problem is, that when a control containing it is going to be displayed, an PInvokeStackInbalanceException is raised
The debugger points to the line containing:
SendMessage(this.Handle, WM_ERASEBKGND, hDC, 0);
I don't know how to fix it or where to search for the source of the error...
Jacek Osiecki
Poland
|
|
|
|
|
Hi,
I think I know the problem you are having.
Similar issue was raised for my combobox control a while ago and it has been fixed.
Check out this article, and look at the "Support for .net 2005" message posting.
http://www.codeproject.com/cs/combobox/ComboBox_appears_flat.asp
The problem is caused by pinvoke signature.
Fadrian
|
|
|
|
|
Thanks, that's exactly what I was looking for!
I owe you one
Jacek
Poland
|
|
|
|
|
The flat DateTimePicker doesn't work properly with TransparencyKey set on a Form-owner of the control. 3-D background is showing on bottom and right edges. Do you have any idea how to fix that?
Alex
|
|
|
|
|
Hi Alex,
Been a while since I last work on this. I tried a few things before discovering the fix by trapping a calcsize message and handle it.
Declare
---------
const int WM_NCCALCSIZE = 0x83;
Add to the WndProc:
-------------------
case WM_NCCALCSIZE:
m.Result = (IntPtr) 1; // indicate msg has been processed
break;
I did some testing and seems to work well. I am not sure if there is any other implications, but maybe you can tell me. I'm no expert on this and ain't sure as in why this fixed the problem either. If anyone reading this knows the answer, please comments on this.
Fadrian
|
|
|
|
|
I tried, and find the control still does not have support for the time element
what's wrong?
|
|
|
|
|