How to Create a Custom Looking Window [duplicate] - c#

I am looking to create a custom shaped form in c#.
I have a background image (png) that has is transparent in some places.
Is there anyway of making the form shape to be the shape of this image instead of the 'usual' rectangle?
I am only asking this as I wish to design a custom skin for my PC (a bit like rainmeter/rocketdock combined, but in a 'compressed' way).
I have heard of using a 'transparency key', but this would remove a colour from the background (I will be using a colour picker in a later stage, and so if the user chose that specific colour, it would not show).
As always, any help would be much appreciated.

The TransparencyKey approach, discussed here on MSDN is the simplest way to do this. You set your form's BackgroundImage to an image mask. The image mask has the regions to be transparent filled with a certain color—fuchsia is a popular choice, since no one actually uses this horrible color. Then you set your form's TransparencyKey property to this color, and it is essentially masked out, rendering those portions as transparent.
But I guess in a color picker, you want fuchsia to be available as an option, even if no one ever selects it. So you'll have to create custom-shaped forms the other way—by setting a custom region. Basically, you create a Region object (which is basically just a polygon) to describe the desired shape of your form, and then assign that to the form's Region property.
Do note that you are changing the shape of the entire window when you do this, not just the client area, so your design needs to account for that. Also, regions cannot be anti-aliased, so the result tends to be pretty ugly if you're using a shape that does not have straight edges.
And another caveat…I strongly recommend not doing this. It takes quite a bit of work to get it right, and even once you get finished, the result is usually gaudy and user-hostile. Even when everything goes just right, you'll end up with something that looks like this—and no one wants that. Users are quite accustomed to boring old rectangular application windows. Applications shouldn't try to be exact digital replicas of real-world widgets. It seems like that would make them intuitive or easy to use, but it really doesn't. The key to good design is identifying the user's mental model for your application and figuring out a good way of meshing that with the standards set by your target windowing environment.
I noticed this tab still open and had a few spare moments, so I tried to bang out a quick sample. I made the "form" consist of two randomly-sized circles, just to emphasize the custom shape effect and the transparency—don't read anything into the design or get any crazy ideas! Here's what I came up with:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public class MyCrazyForm : Form
{
private Size szFormSize = new Size(600, 600);
private Size szCaptionButton = SystemInformation.CaptionButtonSize;
private Rectangle rcMinimizeButton = new Rectangle(new Point(330, 130), szCaptionButton);
private Rectangle rcCloseButton = new Rectangle(new Point(rcMinimizeButton.X + szCaptionButton.Width + 3, rcMinimizeButton.Y), SystemInformation.CaptionButtonSize);
public MyCrazyForm()
{
// Not necessary in this sample: the designer was not used.
//InitializeComponent();
// Force the form's size, and do not let it be changed.
this.Size = szFormSize;
this.MinimumSize = szFormSize;
this.MaximumSize = szFormSize;
// Do not show a standard title bar (since we can't see it anyway)!
this.FormBorderStyle = FormBorderStyle.None;
// Set up the irregular shape of the form.
using (GraphicsPath path = new GraphicsPath())
{
path.AddEllipse(0, 0, 200, 200);
path.AddEllipse(120, 120, 475, 475);
this.Region = new Region(path);
}
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
// Force a repaint on activation.
this.Invalidate();
}
protected override void OnDeactivate(EventArgs e)
{
base.OnDeactivate(e);
// Force a repaint on deactivation.
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw the custom title bar ornamentation.
if (this.Focused)
{
ControlPaint.DrawCaptionButton(e.Graphics, rcMinimizeButton, CaptionButton.Minimize, ButtonState.Normal);
ControlPaint.DrawCaptionButton(e.Graphics, rcCloseButton, CaptionButton.Close, ButtonState.Normal);
}
else
{
ControlPaint.DrawCaptionButton(e.Graphics, rcMinimizeButton, CaptionButton.Minimize, ButtonState.Inactive);
ControlPaint.DrawCaptionButton(e.Graphics, rcCloseButton, CaptionButton.Close, ButtonState.Inactive);
}
}
private Point GetPointFromLParam(IntPtr lParam)
{
// Handle 64-bit builds, which we detect based on the size of a pointer.
// Otherwise, this is functionally equivalent to the Win32 MAKEPOINTS macro.
uint dw = unchecked(IntPtr.Size == 8 ? (uint)lParam.ToInt64() : (uint)lParam.ToInt32());
return new Point(unchecked((short)dw), unchecked((short)(dw >> 16)));
}
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x112;
const int WM_NCHITTEST = 0x84;
const int WM_NCLBUTTONDOWN = 0xA1;
const int HTCLIENT = 1;
const int HTCAPTION = 2;
const int HTMINBUTTON = 8;
const int HTCLOSE = 20;
// Provide additional handling for some important messages.
switch (m.Msg)
{
case WM_NCHITTEST:
{
base.WndProc(ref m);
Point ptClient = PointToClient(GetPointFromLParam(m.LParam));
if (rcMinimizeButton.Contains(ptClient))
{
m.Result = new IntPtr(HTMINBUTTON);
}
else if (rcCloseButton.Contains(ptClient))
{
m.Result = new IntPtr(HTCLOSE);
}
else if (m.Result.ToInt32() == HTCLIENT)
{
// Make the rest of the form's entire client area draggable
// by having it report itself as part of the caption region.
m.Result = new IntPtr(HTCAPTION);
}
return;
}
case WM_NCLBUTTONDOWN:
{
base.WndProc(ref m);
if (m.WParam.ToInt32() == HTMINBUTTON)
{
this.WindowState = FormWindowState.Minimized;
m.Result = IntPtr.Zero;
}
else if (m.WParam.ToInt32() == HTCLOSE)
{
this.Close();
m.Result = IntPtr.Zero;
}
return;
}
case WM_SYSCOMMAND:
{
// Setting the form's MaximizeBox property to false does *not* disable maximization
// behavior when the caption area is double-clicked.
// Since this window is fixed-size and does not support a "maximized" mode, and the
// entire client area is treated as part of the caption to enable dragging, we also
// need to ensure that double-click-to-maximize is disabled.
// NOTE: See documentation for WM_SYSCOMMAND for explanation of the magic value 0xFFF0!
const int SC_MAXIMIZE = 0xF030;
if ((m.WParam.ToInt32() & 0xFFF0) == SC_MAXIMIZE)
{
m.Result = IntPtr.Zero;
}
else
{
base.WndProc(ref m);
}
return;
}
}
base.WndProc(ref m);
}
}
Here it is running on Windows XP and 7, side-by-side:
Whew! It does work, but it's a long way from complete. There are lots of little things that still need to be done. For example:
The caption buttons do not "depress" when clicked. There is a built-in state for that that can be used with the DrawCaptionButton method, but you need to either force a redraw when one of the buttons is clicked, or do the repaint directly on the form right then and there.
It doesn't support Visual Styles. This is a limitation of the ControlPaint class; it was written before Visual Styles were invented. Implementing support for this will be a lot more work, but there is a WinForms wrapper. You will have to make sure that you write fallback code to handle the case where Visual Styles are disabled, too.
The caption buttons aren't actually centered—I just eyeballed it. And even if your eyeballs are better than mine, this is still a bad approach, because the caption buttons can be different sizes, depending on system settings and which version of the OS you're running (Vista changed the button shapes).
Other windows invoke the actions when the mouse goes up over the caption bar buttons. But when you try to use WM_NCLBUTTONUP (instead of WM_NCLBUTTONDOWN), you have to double-click the caption buttons to make them work. This is because the non-client area is capturing the mouse. I'm sure there's a solution, but I ran out of patience before I discovered what it was.
You don't get the pretty animation effects when the window is minimized (or restored), nor do you have the glow-on-hover for the caption buttons. There are tons of visual niceties that you get for free with the default styles that are missing-in-action here. Some of them can be easily added by writing more code, but for each line you write, the maintenance burden skyrockets—newer versions of Windows are likely to break things. And worse, some things are far from trivial to implement, so it probably isn't even worth it. And all this effort for what, again?
Repainting the entire form on activation/deactivation just to update the caption buttons is probably a bad idea. If you are painting anything else more complicated on the form, this is likely to slow down the entire system.
Once you start adding controls to the form, you might run into a problem. For example, even with a Label control, you won't be able to drag the form around by clicking and holding on top of that Label control. Label controls don't return HTTRANSPARENT in response to the WM_NCHITTEST message, so the message doesn't get passed on to the parent form. You can subclass Label to do so and use your subclass instead.
The code is completely untested with Windows 8, since I don't have a copy. Custom non-client areas tend to blow up with new OS updates that change the way the non-client area is rendered, so you're on your own to adapt the code accordingly. Even if it works, it certainly won't have the right Windows 8 look-and-feel.
Et cetera, et cetera.
You can also see that, like I cautioned above, the circular border is not anti-aliased, so it looks jagged. Unfortunately, that is unfixable.

Related

form slow loading when go to another form in C# [duplicate]

In my application i am constantly moving from one control to another. I have created no. of user controls, but during navigation my controls gets flicker. it takes 1 or 2 sec to update. I tried to set this
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
but it didn't help... Each control has same background image with different controls.
So what is the solution for it..
Thanks.
It is not the kind of flicker that double-buffering can solve. Nor BeginUpdate or SuspendLayout. You've got too many controls, the BackgroundImage can make it a lot worse.
It starts when the UserControl paints itself. It draws the BackgroundImage, leaving holes where the child control windows go. Each child control then gets a message to paint itself, they'll fill in the hole with their window content. When you have a lot of controls, those holes are visible to the user for a while. They are normally white, contrasting badly with the BackgroundImage when it is dark. Or they can be black if the form has its Opacity or TransparencyKey property set, contrasting badly with just about anything.
This is a pretty fundamental limitation of Windows Forms, it is stuck with the way Windows renders windows. Fixed by WPF btw, it doesn't use windows for child controls. What you'd want is double-buffering the entire form, including the child controls. That's possible, check my code in this thread for the solution. It has side-effects though, and doesn't actually increase painting speed. The code is simple, paste this in your form (not the user control):
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
There are many things you can do to improve painting speed, to the point that the flicker isn't noticeable anymore. Start by tackling the BackgroundImage. They can be really expensive when the source image is large and needs to be shrunk to fit the control. Change the BackgroundImageLayout property to "Tile". If that gives a noticeable speed-up, go back to your painting program and resize the image to be a better match with the typical control size. Or write code in the UC's OnResize() method to create a properly sized copy of the image so that it doesn't have to be resized every time the control repaints. Use the Format32bppPArgb pixel format for that copy, it renders about 10 times faster than any other pixel format.
Next thing you can do is prevent the holes from being so noticeable and contrasting badly with the image. You can turn off the WS_CLIPCHILDREN style flag for the UC, the flag that prevents the UC from painting in the area where the child controls go. Paste this code in the UserControl's code:
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
The child controls will now paint themselves on top of the background image. You might still see them painting themselves one by one, but the ugly intermediate white or black hole won't be visible.
Last but not least, reducing the number of child controls is always a good approach to solve slow painting problems. Override the UC's OnPaint() event and draw what is now shown in a child. Particular Label and PictureBox are very wasteful. Convenient for point and click but their light-weight alternative (drawing a string or an image) takes only a single line of code in your OnPaint() method.
This is a real issue, and the answer Hans Passant gave is great for saving the flicker. However, there are side effects as he mentioned, and they can be ugly (UI ugly). As stated, "You can turn off the WS_CLIPCHILDREN style flag for the UC", but that only turns it off for a UC. The components on the main form still have issues.
Example, a panel scroll bar doesn't paint, because it is technically in the child area. However the child component doesn't draw the scroll bar, so it doesn't get painted until mouse over (or another event triggers it).
Also, animated icons (changing icons in a wait loop) doesn't work. Removing icons on a tabPage.ImageKey doesn't resize/repaint the other tabPages appropriately.
So I was looking for a way to turn off the WS_CLIPCHILDREN on initial painting so my Form will load nicely painted, or better yet only turn it on while resizing my form with a lot of components.
The trick is to get the application to call CreateParams with the desired WS_EX_COMPOSITED/WS_CLIPCHILDREN style. I found a hack here (https://web.archive.org/web/20161026205944/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) and it works great. Thanks AngryHacker!
I put the TurnOnFormLevelDoubleBuffering() call in the form ResizeBegin event and TurnOffFormLevelDoubleBuffering() call in the form ResizeEnd event (or just leave it WS_CLIPCHILDREN after it is initially painted properly.)
int originalExStyle = -1;
bool enableFormLevelDoubleBuffering = true;
protected override CreateParams CreateParams
{
get
{
if (originalExStyle == -1)
originalExStyle = base.CreateParams.ExStyle;
CreateParams cp = base.CreateParams;
if (enableFormLevelDoubleBuffering)
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
else
cp.ExStyle = originalExStyle;
return cp;
}
}
public void TurnOffFormLevelDoubleBuffering()
{
enableFormLevelDoubleBuffering = false;
this.MaximizeBox = true;
}
If you are doing any custom painting in the control (i.e. overriding OnPaint) you can try the double buffering yourself.
Image image;
protected override OnPaint(...) {
if (image == null || needRepaint) {
image = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(image)) {
// do any painting in image instead of control
}
needRepaint = false;
}
e.Graphics.DrawImage(image, 0, 0);
}
And invalidate your control with a property NeedRepaint
Otherwise the above answer with SuspendLayout and ResumeLayout is probably what you want.
Put the code bellow in your constructor or OnLoad event and if you're using some sort of custom user control that having sub controls, you'll need to make sure that these custom controls are also double buffered (even though in MS documentation they say it's set to true by default).
If you're making a custom control, you might want to add this flag into your ctor:
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Optionally you can use this code in your Form/Control:
foreach (Control control in Controls)
{
typeof(Control).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, control, new object[] { true });
}
We iterating through all the controls in the form/control and accessing their DoubleBuffered property and then we change it to true in order to make each control on the form double buffered. The reason we do reflection here, is because imagine you have a control that has child controls that are not accessible, that way, even if they're private controls, we'll still change their property to true.
More information about double buffering technique can be found here.
There is another property I usually override to sort this problem:
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
return parms;
}
}
WS_EX_COMPOSITED - Paints all descendants of a window in bottom-to-top painting order using double-buffering.
You can find more of these style flags here.
Hope that helps!
Try BeginUpdate/EndUpdate OR SuspendLayout/ResumeLayout methods.
See following
How to fix nested winform control flicker issues
Flickering during updates to Controls in WinForms (e.g. DataGridView)
Just to add to the answer Hans gave:
(TLDR version: Transparency is heavier than you think, use only solid colors everywhere)
If WS_EX_COMPOSITED, DoubleBuffered and WS_CLIPCHILDREN did not solve your flicker (for me WS_CLIPCHILDREN made it even worse), try this: go through ALL your controls and all your code, and wherever you have Any transparency or semi-transparency for BackColor, ForeColor, or any other color, just remove it, use only solid colors. In most of the cases where you think you just have to use transparency, you don't. Re-design your code and controls, and use solid colors.
I had terrible, terrible flickering and the program was running sluggish. Once I removed transparency it sped up significantly, and there is 0 flicker.
EDIT: To add further, I just discovered that WS_EX_COMPOSITED doesn't have to be window-wide, it could be applied just to specific controls! This saved me a lot of trouble. Just make a custom control inherited from whatever control you need, and paste the already posted override for WS_EX_COMPOSITED. This way you get low-level double-buffer on this control only, avoiding the nasty side-effects in the rest of the application!
On the main form or user control where background image resides set the BackgroundImageLayout property to Center or Stretch. You will notice a big difference when the user control is rendering.
I tried to add this as a comment but I don't have enough points. This is the only thing that's ever helped my flickering problems so many thanks to Hans for his post. For anyone that's using c++ builder like myself here's the translation
Add the CreateParams declaration to your application's main form .h file e.g.
class TYourMainFrom : public TForm
{
protected:
virtual void __fastcall CreateParams(TCreateParams &Params);
}
and add this to your .cpp file
void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
Params.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
TForm::CreateParams(Params);
}
I know this question is very old, but want to give my experience on it.
I had a lot of problems with Tabcontrol flickering in a form with overrided OnPaint and/or OnPaintBackGround in Windows 8 using .NET 4.0.
The only think that worked has been NOT USE the Graphics.DrawImage method in OnPaint overrides, in other words, when draw was done directly to the Graphics provided by the PaintEventArgs, even painting all the rectangle, the flickering dissapeared. But if call the DrawImage method, even drawing a clipped Bitmap, (created for double buffering) the flicker appears.
Hope it helps!
I combined this flicker fix and this font fix, then I had to add a bit of my own code to start a timer on paint to Invalidate the TabControl when it goes offscreen and back, etc..
All three make this:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TabControlEx:TabControl
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private const int WM_PAINT = 0x0f;
private const int WM_SETFONT = 0x30;
private const int WM_FONTCHANGE = 0x1d;
private System.Drawing.Bitmap buffer;
private Timer timer = new Timer();
public TabControlEx()
{
timer.Interval = 1;
timer.Tick += timer_Tick;
this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
void timer_Tick(object sender, EventArgs e)
{
this.Invalidate();
this.Update();
timer.Stop();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PAINT) timer.Start();
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs pevent)
{
this.SetStyle(ControlStyles.UserPaint, false);
base.OnPaint(pevent);
System.Drawing.Rectangle o = pevent.ClipRectangle;
System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control);
if (o.Width > 0 && o.Height > 0)
DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height));
pevent.Graphics.DrawImageUnscaled(buffer, 0, 0);
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
buffer = new System.Drawing.Bitmap(Width, Height);
}
protected override void OnCreateControl()
{
base.OnCreateControl();
this.OnFontChanged(EventArgs.Empty);
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
IntPtr hFont = this.Font.ToHfont();
SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
this.UpdateStyles();
}
}
I'm not the creator but from what I understand the bitmap does all the bug bypassing.
This was the only thing that definitively solved TabControl (with Icons) flicker for me.
difference result video: vanilla tabcontrol vs tabcontrolex
http://gfycat.com/FineGlitteringDeermouse
ps. you will need to set HotTrack = true, because this fixes that bug too
Did you try Control.DoubleBuffered Property?
Gets or sets a value indicating whether this control should redraw its surface using a secondary buffer to reduce or prevent flicker.
Also this and this might help.
There is no need of any Double buffering and all that stuff guys...
A Simple solution...
If you are using MDI Interface, just paste the code below in the main form. It will remove all flickering from the pages. However some pages which require more time for loading will showup in 1 or 2 secs. But this is better than showing a flickering page in which each item comes one by one.
This is the only best solution for whole application. See the code to put in the main form:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}

How to create an 1 pixel wide window using C# and WinForm

I would like to create an application that have a small window displayed at the bottom corner of desktop. On startup, the window shall be very small and ideally, just a couple of pixels in width.
Here is the code I used to do it:
public partial class DurationT64 : Form
{
private Size fullSize;
private Point fullPos;
private Point compactPos;
public DurationT64()
{
InitializeComponent();
var workingArea = Screen.PrimaryScreen.WorkingArea;
this.MinimumSize = new Size(0, this.Height);
this.MaximumSize = new Size(this.Width, this.Height);
// fullPos: the window location when it is in full size form.
fullPos = new Point(workingArea.Right - this.Width, workingArea.Bottom - this.Height);
this.Location = fullPos;
// fullSize: the size of the windown when it is in full size form.
fullSize = new Size(this.Width, this.Height);
this.Size = fullSize;
// compactPos: the window location when it is in compact size form.
compactPos = new Point(workingArea.Right - 30, fullPos.Y);
this.Width = 1;
this.Location = compactPos;
}
}
As you can see that in this example, I intended to create a window of just 1 pixel in width, placed closed to the right edge of the primary monitor.
However, I realized that the window doesn't go as small as I was expected. It goes down to 20 pixels wide but no less than that. Please refer to this screen capture image below for example:
an image shows that the window is wider than it suppose to be
I did some research regards to this problem and noticed that there was a solution proposed by Zach Johnson (#zach-johnson) back in 2009. Here is the link to it Overcome OS Imposed Windows Form Minimum Size Limit.
However, nether methods proposed in that link (the intercepting WM_ message one proposed by Zach and the SetBoundsCore one proposed by #Ace) works for me.
Can anyone please give me some solution to this question? Preferably, a solution purely based on C#/Winform and does not rely on native Win32 window message loop, if possible.
Many thanks!
It is rather straight-forward, Winforms ensures that the window cannot be made smaller than the system-imposed minimum size of a window, exposed as the SystemInformation.MinWindowTrackSize property in .NET. This is a "safety" setting, it ensures that the user cannot make the window too small when he resizes it, thus losing control over it. Same consideration applies to code.
Bypassing this limit requires no magic, you need to do two things:
Set the FormBorderStyle property to None so the user cannot resize the window.
Set the size after the window is created. The Load event is best.
Some comments about your existing code: be careful about tinkering with the Width/Height/Size properties, you are doing too much of it in your constructor and it cannot work correctly. In the constructor they don't yet match the actual size of the window. And will not be close at all on modern machines with high-resolution monitors, auto-scaling to match the DPI of the video adapter is important today. You have to postpone until the window is created and scaling is complete, the Load event is the proper place for code like this. One of the few reasons to actually use Load.
And note that your Location property calculation is inadequate, it does not consider the location of the taskbar. It doesn't work on my machine, I like the taskbar on the right.
Minimum repro:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
}
protected override void OnLoad(EventArgs e) {
this.Width = 1;
base.OnLoad(e);
}
}
Do keep in mind that you'll need hawk-eyes to find it back on the screen :)

Handle Double Click Anywhere in the Form?

In my WinForm application I have drawn a rectangle (System.Drawing.Rectangle) on a form.
I need to handle double click anywhere on the form
I could attach MouseDoubleClick event handler to the form. It works only when double click was made outside the rectangular shape.
How do I achieve this?
EDIT:
I have drawn a rectangle which is center aligned and covers only 40% of the whole winform area. When user double clicks on the rectangle I need to expand rectangle size to occupy full screen. That's all!
EDIT 2:
My friends who have down voted, write a comment please so that I could improve, please!
The best way I know to do what you are looking for is to use the "WndProc" method. This allows you to collect "messages" (events) from the message queue before they are sent to the form. You then have the option of either responding to those events or allowing them to continue through the normal message process. For more information, take a look at the MSDN page here.
A brief example of how you might use this:
protected override void WndProc(ref Message m)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh454920(v=vs.85).aspx
// 0x210 is WM_PARENTNOTIFY
// 513 is WM_LBUTTONCLICK
if (m.Msg == 0x210 && m.WParam.ToInt32() == 513)
{
var x = (int)(m.LParam.ToInt32() & 0xFFFF);
var y = (int)(m.LParam.ToInt32() >> 16);
var childControl = this.GetChildAtPoint(new Point(x, y));
if (childControl == cancelButton)
{
// ...
}
}
base.WndProc(ref m);
}
Credit for this example goes to: this stack question.
Remember too, that "double click" is just two single clicks, so you are going to have to monitor and find the time between clicks yourself to decide what actually represents a double click.
Unfortunately Rectangle does not contain any fire any events (at all) as System.Drawing merely provides "grahpics" for a form.
There are options (such as provided by #drew_w) however such workarounds that require interaction with COM objects is usually indicate it's time to re-evaluate the requirements.

Preventing mouse clicks through a transparent Windows Form

I'm making a little tool for drawing onto the screen with the mouse after a 'pen' button is toggled in a floating sidebar.
I have done this (please don't laugh) by having a top-most windows form with its background as its transparency key cover the whole screen.
I need to make the mouse not click through the form onto the stuff below when I"m in drawing mode. I tried following this:
Windows form with a transparent background that cannot be clicked through
How to disable click through on transparent control?
which successfully stops the mouse but also un-maximises the form and drags it around with the mouse (using HTCAPTION IntPtr(2) this is) I tried using some of the other values listed on MSDN, but with no luck.
I'm way out of my depth, any help greatly appreciated (newbie friendly please!)
PS I'm using this right now..
//code for allowing clicking through of menus
protected override void WndProc(ref Message m)
{
if (penMode && m.Msg == 0x84)
{
m.Result = new IntPtr(2);
}
else
base.WndProc(ref m);
}
UPDATE: Now solved the problem by approaching it in another way entirely. It doesn't look like WndProc will work so I simply created a blank form over the whole screen the showed my main form (form.Show(this)) from within that. Then adjust the opacity of the blank form which sits underneath from 0% to 1% to allow/ prevent clicking through. Works!
Thanks to all answers, taught me a lot.
Actually, no need to laugh—it sounds to me like you're doing this the correct way already. Since you don't own the desktop, you shouldn't draw directly on it. Instead, you need to simulate it by overlaying a transparent form that you do own, and then drawing on that. Because you own the transparent overlay form, it's no problem to draw on it.
But beyond that, it sounds like you're just trying values randomly without a clear understanding of what they actually do. That's like throwing darts with your eyes closed. You won't have a very high hit count.
Let's start by understanding what your code does. The magic value 0x84 corresponds to the WM_NCHITTEST message, which is sent by Windows to a window to determine how mouse clicks on that window should be handled. In response to that message, you reply with one of the HT* values, given in the linked documentation. Each of those values has a particular meaning, also explained in the documentation. For example:
HTCAPTION (which has a value of 2) means that the clicked portion of the window should be treated as the window's caption/title bar. You know from using Windows that you can drag windows around on the screen using the title bar, so it makes sense that returning HTCAPTION in response to mouse clicks would allow your window to be draggable. You'll see this used on borderless forms (i.e., those with no title bar) to allow them to be movable.
HTTRANSPARENT (which has a value of -1) is another available value. This one's pretty simple. It just makes your window look transparent. It's like saying "don't mind me, there's no window here!" Mouse clicks are simply passed on to the window that lies below yours in the Z order as if you weren't there.
HTCLIENT (a value of 1) is the default result when the click occurs anywhere on the window's client area. You would return this (or simply call the default window procedure) when you want everything to work normally. Click events that return this value would go on to be processed normally by the framework, raising either the form's Click event, or getting passed on to child controls located on the form.
So, when you're not drawing, you probably want to return HTTRANSPARENT. When you are drawing, you probably want to return HTCLIENT so that your drawing code can see the mouse events and draw the result.
Fixing your code, then:
// Code for allowing clicking through of the form
protected override void WndProc(ref Message m)
{
const uint WM_NCHITTEST = 0x84;
const int HTTRANSPARENT = -1;
const int HTCLIENT = 1;
const int HTCAPTION = 2;
// ... or define an enum with all the values
if (m.Msg == WM_NCHITTEST)
{
// If it's the message we want, handle it.
if (penMode)
{
// If we're drawing, we want to see mouse events like normal.
m.Result = new IntPtr(HTCLIENT);
}
else
{
// Otherwise, we want to pass mouse events on to the desktop,
// as if we were not even here.
m.Result = new IntPtr(HTTRANSPARENT);
}
return; // bail out because we've handled the message
}
// Otherwise, call the base class implementation for default processing.
base.WndProc(ref m);
}
You might just want to set the visibility of your window to like 5% or so and leave the transparent key deactivated.
you basically won't notice it and jet it's there :D
hope this helps

How can I remove the border padding on container controls in WinForms?

I set margin and padding to 0 0 0 0 but that doesn't have any effect for my TabControls. Look:
Here is what I am talking about. I want to stick the borders together.
How can I do this?
#Henk Holterman - yes, what's wrong with it ?
There's a comment left in the source code for TabPage by an exasperated Microsoft programmer (edited to fit the page):
//HACK: to ensure that the tabpage draws correctly (the border will get
// clipped and gradient fill will match correctly with the tabcontrol).
// Unfortunately, there is no good way to determine the padding used
// on the tabpage.
// I would like to use the following below, but GetMargins is busted
// in the theming API:
//VisualStyleRenderer visualStyleRenderer = new VisualStyleRenderer(VisualStyleElement.Tab.Pane.Normal);
//Padding themePadding = visualStyleRenderer.GetMargins(e.Graphics, MarginProperty.ContentMargins);
Visual Styles have been a major bug factory, particularly so for TabControl. Check this answer for a way to selectively turn it off for the TabControl so you'll get the behavior you are used to. Of course it does change the appearance.
I agree with Henk. There's a border of the same size (9 pixels to my recollection) all the way around the container control. The reason it's there is to prevent you from squashing controls up too close to the edge. If you did that at the top, your control would be far too close to the tab headers at the top. It would look silly and confuse the user. WinForms is saving you from yourself here, and you don't even know it. Exactly the reason it was done in the first place.
Familiarize yourself with Microsoft's standard user interface guidelines, specifically the section on layout. Notice how all of the controls (the dialog box window itself, the tab control, etc.) have a border around them? It's 7 dialog units in the Visual C++ Resource Editor; WinForms uses a pixel specification.
Try This
public class TabControlEx : TabControl
{
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x1300 + 40)
{
RECT rc = (RECT)m.GetLParam(typeof(RECT));
rc.Left -= 0;
rc.Right += 3;
rc.Top -= 0;
rc.Bottom += 3;
Marshal.StructureToPtr(rc, m.LParam, true);
}
base.WndProc(ref m);
}
}
internal struct RECT { public int Left, Top, Right, Bottom; }

Categories

Resources