|
There is a problem when i change RightToLeft property to Yes
|
|
|
|
|
Booky,
Thanks for pointing that out. It is something that I haven't looked at before
Well, I did a quick hack just then and it works for me. Below is the snipplet of the code that will do the job. Replace the original PaintFlatDropDown function with the revised code below.
private static bool DrawRightToLeft(Control ctrl)
{
if (ctrl.RightToLeft == RightToLeft.Yes ||
(ctrl.RightToLeft == RightToLeft.Inherit && ctrl.Parent.RightToLeft == RightToLeft.Yes))
{
return true;
}
return false;
}
public static void PaintFlatDropDown(Control ctrl, Graphics g)
{
Rectangle rect = new Rectangle(ctrl.Width-DropDownButtonWidth, 0, DropDownButtonWidth, ctrl.Height);
if (DrawRightToLeft(ctrl))
rect = new Rectangle(1, 0, DropDownButtonWidth, ctrl.Height);
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat);
}
Hope that help.
Fadrian
|
|
|
|
|
flickering !!
If there are so much combobox in the same form, flickering is increaseing. Because Windows drawing the normal combobox before our painting.
Serdar YILMAZ
Senior Developer
|
|
|
|
|
Adding following code will not make flickering any more!!
protected override void OnPaint(PaintEventArgs e)
{
// base.OnPaint (e);
}
switch (m.Msg)
{
...
case WM_ERASEBKGND:
//indicate msg has been processed
m.Result = (IntPtr) 1;
break;
...
Thanks,
ChangYoung
jucyblue@gmail.com
jucyblue@gmail.com
|
|
|
|
|
Has no change ...
Serdar YILMAZ
Senior Developer
|
|
|
|
|
Thanks for the post and suggestion. I really don't have much idea on how to fix that, as my control is drawing on top of existing control to leverage all the existing functionality. If someone has a solution to this I will be please to know.
Fadrian
|
|
|
|
|
I changed Draw Mode of this control.
Now, there are NO flicker.
And I added ImageList properties.
public syComboBox()
{
...
...
base.SetStyle(ControlStyles.ResizeRedraw, true);
this.DrawMode = DrawMode.OwnerDrawVariable;
}
...
...
protected virtual void Draw(bool fillContent)
{
Graphics graphics1 = Graphics.FromHwnd(base.Handle);
// Drawing Border
... drawing border procedures
... if (this.isMouseOver | this.Focused) then draw HOT else draw NORMAL
// Drawing Button
ButtonState bs = (DroppedDown) ? ButtonState.Pushed : ButtonState.Normal;
... drawing combobox button procedures
if (fillContent)
{
Rectangle rectangle1 = (1, 1, this.Width-SystemInformation.HorizontalScrollBarArrowWidth-2, this.Height-2);
Color color1 = this.BackColor;
if (!base.Enabled)
{
color1 = SystemColors.Control;
}
SolidBrush brush1 = new SolidBrush(color1);
graphics1.FillRectangle(brush1, 1, 1, rectangle1.Width, 2); // top
graphics1.FillRectangle(brush1, 1, rectangle1.Bottom - 2, rectangle1.Width, 2); // bottom
graphics1.FillRectangle(brush1, 1, 1, 2, rectangle1.Height); // left
graphics1.FillRectangle(brush1, rectangle1.Right - 2, 1, 2, rectangle1.Height); // right
}
}
...
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
this.isMouseOver = false;
this.Draw(false);
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
this.isMouseOver = true;
this.Draw(false);
}
protected override void OnLostFocus(System.EventArgs e)
{
base.OnLostFocus(e);
this.Draw(false);
}
protected override void OnGotFocus(System.EventArgs e)
{
base.OnLostFocus(e);
this.Draw(false);
}
protected override void WndProc(ref Message msg)
{
if (msg.Msg == 15)
{
base.DefWndProc(ref msg);
this.Draw(true);
}
else
{
base.WndProc(ref msg);
}
}
-----------------
Serdar YILMAZ
Senior Developer
|
|
|
|
|
please, can you post the modified source ?
|
|
|
|
|
Great control, small, simple, and yet does all it's supposed to do. Well, most of it anyway, that's where I want to ask you some questions.
1.First is that I implemented a mouseenter/mouseleave event to highlight the border with different color depending on whether the mouse is hovering above the control or not. However if I repeatedly move my mouse in and out of control at least three-four times a second I notice a lot of flickering at the drop down button. My guess is that when you do a WM_PAINT event you let the default wndproc to take over and then paint the custom border and dropdown button which causes the flickering. Is there any way to redraw this manually without base class? I tried it, but I'm very new at this whole GDI and window messages thing, so, not surprisingly I failed.
2.This is also related to mouseenter/mouseleave thing. You said that your combobox did not work with simple style. What did you mean by that, what exactly did not work? Because for some reason, and I don't know why I have problems when the style is set to dropdown instead of dropdownlist. The issue I have is that when I set the style to dropdown, sometimes, when I move the mouse quickly in or out of the control mouseenter/mouseleave events do not fire. So the border of the control does not change, it's really annoying. I tried catching wm_mouseleave message in the wndproc, but it seems it never gets sent. Any idea what's causing it?
I also posted a question about your datetimepicker control. Can you take a look at it too?
Thanks.
|
|
|
|
|
Thanks for your input.
I haven't looked at the control for a while, so it is a slow start for me as well. These are my answers to your questions:
1. Instead of doing the drawing in mouseenter and mouseleave, I suggest that you just set a bool flag in those function such as m_bMouseEntered = true in the mouseenter even tna m_bMouseEntered = false in the mouseleave event. Change the implementation of PaintFlatControlBorder so that it first test for if (m_bMouseEntered) then call ControlPaint.DrawBorder(g, rect, MyColor, ButtonBorderStyle.Solid);
2. ComboBox has 3 style, simple style looks like a normal List control. you can set that property and have a look at your winform for the difference. I don't think the dropdownlist and dropdown style would be different in dealing with those events.
|
|
|
|
|
first and foremost, good job!
second, I noticed that in WndProc you have:
IntPtr hDC = GetWindowDC(this.Handle);
Graphics gdc = Graphics.FromHdc(hDC);
however, only messages WM_NC_PAINT and WM_PAINT make use of them, while for any other message they're not used at all, therefore, my advice to you would be to limit their scope to just the observed messages. In any case I know you're cleaning up:
ReleaseDC(m.HWnd, hDC);
gdc.Dispose();
however, why create and dispose of resources, in this case, relatively expensive resources, that will not be used.
good luck
|
|
|
|
|
Thanks. I agree with your point. The code was structured that way was partially caused by my laziness and also trying to make the code as easy to read as possible. I did plan to build up a library of these controls, but I have stopped for a while due to some change in circumstances. Keep your suggestions coming and will benefit me and others
|
|
|
|
|
I have a Sony Vaio with 1900x1200 resolution.
This FlatCombobox works great, except there is an odd artifact:
Ther is a wide 3D gap showing between the Textbox portion of the control and the arrow part of the control.
Any ideas on this? I tried it as a C# dll and also with the VB.Net code snippet. They both do the same thing.
Elizabeth Gee
Information Architect - .Net Developer
http://www.nwtd.com
|
|
|
|
|
Liz,
Geez.. that is a very wide screen that you have! I haven't got the environment to try, but did you try my suggested code in other thread:
====================================================
When it come to adjusting for drawing, I tempt to use the graphic resolution, so say "g" is the Graphics object, I would do something like this:
int iWidth = (int) ((g.DpiX/96.0f) * DROPDOWNWIDTH);
I use 96 there because that is the pixel size for standard font display. I also make it a float so that we keep the precision when performing the math.
====================================================
See if that works for you.
Fadrian
|
|
|
|
|
Fadrian,
I am new to CodeProject.com and am excited to hear from you. Yes I did add this dropdownwidth code snippet from ont of the threads as well as the resize snippet.
I also added the OnResize snippet
Everything compiles well.
I think it may be the videocard on the Sony Vaio stretches things, as I am having troubles with the rest of my form where relative co-ordinates are working out better than absolute co-ordinates. This is interesting, and may become more of an issue for software developers as the panoramic screen resolutions become more common over time.
Elizabeth Gee
Information Architect - .Net Developer
http://www.nwtd.com
|
|
|
|
|
BTW,
I went to the Form level properties and set the AutoScale to False. This did not change the Bug on the control where there is a 3D wide artifact between the textbox and the arrow.
Elizabeth Gee
Information Architect - .Net Developer
http://www.nwtd.com
|
|
|
|
|
Liz,
AutoScale can be nasty and I constantly run into problem with it.
Can you capture the screen of the 3D wide artifact problem and send to my email a/c? Once I see the exact prob, I may be able to make other suggestion.
Fadrian
|
|
|
|
|
Here is a link to a screenshot of your Flat Combo on my Sony Vaio at 16oox1200 resolution:
http://www32.brinkster.com/icontoo/Temp/FlatComboOnSonyVaio.html
Also, here is your code with the two modifications made by others, which I compiled down to a C# DLL as depicted in the screenshot:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
namespace DrawFlat
{
[ToolboxBitmap(typeof(System.Windows.Forms.ComboBox))]
public class FlatComboBox: ComboBox
{
public const int WM_ERASEBKGND = 0x14;
public const int WM_PAINT = 0xF;
public const int WM_NC_PAINT = 0x85;
public const int WM_PRINTCLIENT = 0x318;
[DllImport("user32.dll", EntryPoint="SendMessageA")]
public static extern int SendMessage (IntPtr hwnd, int wMsg, IntPtr wParam, object lParam);
[DllImport("user32")]
public static extern IntPtr GetWindowDC (IntPtr hWnd );
[DllImport("user32")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC );
public FlatComboBox()
: base()
{
this.SetStyle(ControlStyles.DoubleBuffer, true);
}
protected override void OnSelectedValueChanged(EventArgs e)
{
base.OnSelectedValueChanged (e);
this.Invalidate();
}
protected override void WndProc(ref Message m)
{
if (this.DropDownStyle == ComboBoxStyle.Simple)
{
base.WndProc(ref m);
return;
}
IntPtr hDC = GetWindowDC(this.Handle);
Graphics gdc = Graphics.FromHdc(hDC);
switch (m.Msg)
{
case WM_NC_PAINT:
SendMessage(this.Handle, WM_ERASEBKGND, hDC, 0);
SendPrintClientMsg(); // send to draw client area
PaintFlatControlBorder(this, gdc);
m.Result = (IntPtr) 1; // indicate msg has been processed
break;
case WM_PAINT:
base.WndProc(ref m);
// flatten the border area again
Pen p = new Pen((this.Enabled? BackColor:SystemColors.Control), 2);
gdc.DrawRectangle(p, new Rectangle(2, 2, this.Width-3, this.Height-3));
PaintFlatDropDown(this, gdc);
PaintFlatControlBorder(this, gdc);
break;
default:
base.WndProc(ref m);
break;
}
ReleaseDC(m.HWnd, hDC);
gdc.Dispose();
}
private void SendPrintClientMsg()
{
// We send this message for the control to redraw the client area
Graphics gClient = this.CreateGraphics();
IntPtr ptrClientDC = gClient.GetHdc();
SendMessage(this.Handle, WM_PRINTCLIENT, ptrClientDC, 0);
gClient.ReleaseHdc(ptrClientDC);
gClient.Dispose();
}
private void PaintFlatControlBorder(Control ctrl, Graphics g)
{
Rectangle rect = new Rectangle(0, 0, ctrl.Width, ctrl.Height);
if (ctrl.Focused == false || ctrl.Enabled == false )
ControlPaint.DrawBorder(g, rect, SystemColors.ControlDark, ButtonBorderStyle.Solid);
else
ControlPaint.DrawBorder(g, rect, Color.Black, ButtonBorderStyle.Solid);
}
public static void PaintFlatDropDown(Control ctrl, Graphics g)
{
const int DROPDOWNWIDTH = 18;
int iWidth = (int) ((g.DpiX/96.0f) * DROPDOWNWIDTH);
//Rectangle rect = new Rectangle(ctrl.Width-DROPDOWNWIDTH, 0, DROPDOWNWIDTH, ctrl.Height);
Rectangle rect = new Rectangle(ctrl.Width-iWidth, 0, iWidth, ctrl.Height);
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat);
}
protected override void OnLostFocus(System.EventArgs e)
{
base.OnLostFocus(e);
this.Invalidate();
}
protected override void OnGotFocus(System.EventArgs e)
{
base.OnGotFocus(e);
this.Invalidate();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Invalidate();
}
}
}
Elizabeth Gee
Information Architect - .Net Developer
http://www.nwtd.com
|
|
|
|
|
Liz,
Looks like the width of the dropdown combo is 1 pixel out. I would just change the DROPDOWNWIDTH to 19. It doesn't really matter in normal screen display for the dropdown combo to be 1 pixel wider. Perhaps 19 would be a better size to be used as it will cover the standard 17 pixel width combo and total of 2 3D pixels outer border for the left and right.
Fadrian
|
|
|
|
|
Beautiful!
I changed the DROPDOWNWIDTH from 18 to 20 then tho 21, and 21 closed it up perfectly at 1900x1200 resolution
Thanks!
Elizabeth Gee
Information Architect - .Net Developer
http://www.nwtd.com
|
|
|
|
|
Glad to hear. Hmm... the solution may only work with high res display, and may not view nicely in low res display. I still suspect the problem lies with the AutoScale setting and the font used, but don't take my word for that. I yet to find a programatic way to solve this problem. If anyone knows the answer, I will appreciate your feedback.
|
|
|
|
|
Solved: (Fairly much so)
I tested this on my Sony Vaio at:1920x1200, 1600x1200, and 1280x1024. At the higher two resolutions the below fix works great.
Also on my standard Dell desktop machine at 1280x1024, it works great.
I added a check to your code to check for the Screen Resolution width, and it adjusts the DROPDOWNWIDTH for the resolution above or below 1500.
I compiled this C# into a DLL and then tested it on both machines.
What I ran into was the Sony Vaio does a software screen resolution change, not a hardware one, thus when I set the Sony Vaio to 1280x1024, get that small vertical artifact between the textbox and the arrow parts of the combo box.
All the rest of the code I left the same as the last time I posted it on this thread. I included the VB.Net and C#.Net variations of this code snippet below...
[Visual Basic.Net]
Public Sub PaintFlatDropDown(ByVal ctrl As Control, ByVal g As Graphics)
Dim DROPDOWNWIDTH As Integer
Dim scrRes As Screen
Dim intScrWidth As Integer
intScrWidth = scrRes.PrimaryScreen.Bounds.Width
If (intScrWidth < 1500) Then
DROPDOWNWIDTH = 18
Else
DROPDOWNWIDTH = 21
End If
Dim rect As Rectangle = New Rectangle(ctrl.Width - DROPDOWNWIDTH, 0, DROPDOWNWIDTH, ctrl.Height)
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat)
End Sub
[C#.Net]
public static void PaintFlatDropDown(Control ctrl, Graphics g)
{
int DROPDOWNWIDTH = 18;
Screen screen = Screen.PrimaryScreen;
if (screen.Bounds.Width > 1500)
DROPDOWNWIDTH = 21;
int iWidth = (int) ((g.DpiX/96.0f) * DROPDOWNWIDTH);
//Rectangle rect = new Rectangle(ctrl.Width-DROPDOWNWIDTH, 0, DROPDOWNWIDTH, ctrl.Height);
Rectangle rect = new Rectangle(ctrl.Width-iWidth, 0, iWidth, ctrl.Height);
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat);
}
Elizabeth Gee
Information Architect - .Net Developer
http://www.nwtd.com
|
|
|
|
|
Hi Elizabeth,
You could also try the snippet:
[C# .NET]
public static void PaintFlatDropDown(Control ctrl, Graphics g)
{
Rectangle rect = new Rectangle(ctrl.Width-SystemInformation.VerticalScrollBarWidth, 0, SystemInformation.VerticalScrollBarWidth, ctrl.Height);
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat);
}
It works fine at lower resulutions, and I guess it should also work fine at higher resolutions since it gets the width of the button from the system... you'll also reduce a bit your code.
Best wishes
|
|
|
|
|
Small correction over my own code.
I've just found that my code snippet shows part of the 3D button aswel, but not due to the resolution but due to the OS it's running. On XP it runs perfectlly and it adjusts to the button width, but the 3D presentation of the button on Windows 2000, is a bit different snd it messes up a bit.
To correct this problem and hide the 3D section of the button (Same as your posted image) I did the following extra modification:
[C# .NET]
public static void PaintFlatDropDown(Control ctrl, Graphics g)
{
int myWidth = SystemInformation.VerticalScrollBarWidth;
myWidth += SystemInformation.Border3DSize.Width ;
Rectangle rect = new Rectangle(ctrl.Width-myWidth, 0, myWidth, ctrl.Height);
ControlPaint.DrawComboButton(g, rect, ButtonState.Flat);
}
As you can see I've added the 3D border default width, this hides completelly the button... I've used the variable "myWidth" as you should add a check in order to add the border Width if windows themes are disabled, and if they are enabled we just skip this check.
Best wishes and hope you find it usefull.
And just a question: You're running the control on a Pre-Windows XP, right?
|
|
|
|
|
Hi all,
This maybe a bit late, but I have properly fix this now in the latest version. if you have a chance, give it a try and let me know.
Fadrian
|
|
|
|
|