I have the following code to drag the form and make it transparent when its getting dragged. The problem is that it flickers and isn't dragging smooth. I have a picture on the form, not sure if that's what's causing this. How can I make it not flicker. If I remove the opacity then it's getting dragged fast/smooth.
[DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam,
int lParam);
[DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
public void Drag(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.Opacity = 0.9;
ReleaseCapture();
SendMessage(Handle, 0xA1, 0x2, 0);
this.Opacity = 1;
}
}
private void Body_MouseDown(object sender, MouseEventArgs e)
{
Drag(e);
}
There are a number of properties of the Form and Control class that are "heavy", requiring a great deal of change in the underlying native Windows window. They are the properties that are associated with the style flags that are passed to the native CreateWindowEx() call. The Opacity property, along with the TransparencyKey property are like that, when you change them from the default then the window needs another style flag, WS_EX_LAYERED.
That's a problem, given that this style flag is specified when you create the window. Windows has some support for changing them after the window is created with SetWindowsLongPtr() but that's always been spotty. Particularly so for WS_EX_LAYERED, a lot of stuff happens under the hood when that's turned on. It is implemented by taking advantage of a hardware feature in the video adapter called "layers". A layer is a separate chunk of video memory whose pixels are combined with the main memory. The mixer that supports that provides the opacity effect (multiply) and the transparency key effect (omit).
So changing the Opacity property on the fly, after the window is created is quite difficult. So much so that WPF completely forbids it. But Winforms doesn't, it needed to deal with the limitations of Windows 98. Which also made it difficult to change properties like ShowInTaskbar, RightToLeft, FormBorderStyle. It uses a trick to permit changing these properties, it completely destroys the native window and recreates it, now using the new style flags.
Problem solved, but this does have noticeable side effects. Inevitably, the window you look at disappears and the new window gets created and painted in the same spot. That causes the flicker you complained about. Also, destroying the window causes a lot of internal state to be lost. Winforms does its best to restore that state as well as it can for the new window, but the "I'm currently being moved" state cannot be restored. The modal move loop already terminated.
The workaround for this problem is crude but simple. Set the Opacity property in the Properties window to 99%. And change your code to restore it to 99 instead of 100. Now the style bit never has to be changed so you won't get these artifacts anymore.
Related
No matter what i do, i cant figure out how to make an XNA game fully transparent.
http://vitrite.vanmiddlesworth.org/vitrite/shot-full.png
here is an example of what i mean. In that the command prompt is partially transparent and im trying to do that myself.
Is it even possible in XNA?
That's very difficult to make it truly transparent without using an interface which XNA does not support due to its D3D version, but there are workarounds that sort of get what you want.
You can get similar appearance by hooking into the Windows API and grabbing an image of the last rendered "client area" of the window (the desktop is just a special window). In your case you could get all other windows that yours was overlapping, grab the client areas, clip them using your relative position/size, then mix them together in a shader. Warning: This could be performance-intensive on update, depending on how many windows you had "beneath" your own. I suggest hooking into updates from the window so you're only capturing when there's a change or when your window moves, etc.
Check out these in the Windows API:
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags);
Be aware that this will not help give your window border any faked transparency though, only your render area.
Also see: Transparent window layer that is click-through and always stays on top
You might be able to use some of the information in that thread as well.
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
First of all I'm not sure if ".exe window" is the proper term. It's the window that pops up when you start the application.
I'm a game programmer, and when I'm debugging, I very rapidly start it up, look at the problem, then close it down again to make minor changes in the code, then start it again etc. I do this like once per minute, so it happens a lot. My problem is that the .exe window always appears at the middle of my main screen (where I'm coding), and I'm running double monitors, and I'd like the game window to appear on my second screen instead of my main screen (obscuring my code).
Can I change where the exe window appears in VS2010? I've looked around everywhere, it feels like. Or is it something that will have to be managed by a 3rd party program? If so, what program?
Edit:
OK, OK, I found the solution. I did a really dumb mistake where I didn't mention that I am using XNA, and not using winforms. Sorry for misleading you guys. Here's how I solved it:
First off I had to include:
using System.Runtime.InteropServices;
Then at the top of my main class I created a tiny class:
public static class User32
{
[DllImport("user32.dll")] public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
}
Then in my Initialize function I simply call:
#if DEBUG
User32.MoveWindow(Game.Window.Handle, 2000, 400, 600, 480, true);
#endif
It's a little ugly, but it's only for debugging and only called once, so psh.
Original solution found here: http://social.msdn.microsoft.com/forums/en-US/xnagamestudioexpress/thread/bc9588a9-542f-425b-9025-d69fe2b0b182/
You can set the Form.StartPosition property, or just manually write to the Left and Top properties of the form you want to move.
Option 1: You could set the appropriate properties on the window/form if a debugger is attached.
if (System.Diagnostics.Debugger.IsAttached)
{
// Set the window/form's top/left properties.
}
Option 2: Add a command line switch, use that as startup parameter (Properties->Debug->Commandline arguments), and then set the appropriate properties in the window/form:
private void Application_Startup(object sender, StartupEventArgs e)
{
if (e.Args.Any(arg => arg.Equals("/debugmode", StringComparison.OrdinalIgnoreCase))
// Set some value which you check in your main window.
}
Although you are not using winforms, you still change it in Xna by using winforms objects. I know you found a solution but here is how to change it without using interop.
Add a reference to System.Windows.Forms and System.Drawing to the References in the game project.
Resist the temptation to add using statements for these as it can cause ambiguity with some Xna objects (Point, for instance, which in Xna uses floats).
In the Game.Initialize method:
System.Drawing.Point p = new System.Drawing.Point(2000, 400);// or wherever you want
System.Windows.Forms.Control c = Control.FromHandle(this.Window.Handle);
c.Location = p;
the game window will now start at the screen 2000,400 location.
I would just call it the "main application window". Anyway, assuming you're using WinForms, this would put the window in the top left corner of the first screen that isn't your primary screen:
void Form1_Load(object sender, EventArgs e)
{
#if DEBUG
Location = Screen.AllScreens.First(s => !s.Primary).Bounds.Location;
#endif
}
If you've only got two monitors hooked up, it'll work fine. You could also get more creative and center the application window on the other monitor, maximize it, whatever. The #if could be substituted with if (System.Diagnostics.Debugger.IsAttached) as suggested by #Daniel if you wanted. I used the former just to present another alternative.
I have written a custom control that renders some graphics. The graphics itself is fairly expensive to render but once rendered it rarely changes. The problem I have is that if you move the mouse really fast over the graphics surface it keeps calling the control's overridden Paint method which then incurs a high CPU penalty:
private void UserControl1_Paint(object sender, PaintEventArgs e)
What techniques could be used to avoid this situation or minimise any unnecessary redrawing since the graphics/image underneath the mouse pointer is not actually changing?
EDIT: After seeing your edit, I can assure you that OnPaint is not called by default when the mouse moves over a control. Something in your code is definitely causing the re-paint, you just don't see it yet. Perhaps posting some of your code would help us find the problem.
Are you invalidating your control on MouseMove? That is likely a bad idea, and if you really needed to do it (i.e., you are making a graphics editor or something), you would have to be smart about how large a region was actually re-drawn. So, solution; don't paint your control in MouseMove.
Otherwise, I would not expect OnPaint to fire when the mouse moves over the control. You can also just generate the image once and then blt it to the Graphics object until it needs to be re-generated.
You can use a buffer image on which to draw when something is changed and in the Paint method just copy the image on the screen. Should be pretty fast. Also you can use the clip region to copy only the part that needs updating. This should reduce CPU usage.
You can also use an IsDirty flag to know when to update the buffer image (i.e. fully redraw it) if needed.
You should not call Paint directly.
Instead, call Invalidate (Control.Invalidate). This queues the need to be repainted, and Windows will take care of the call itself. This way, a lot of rapid invalidations (repaint requests) can be serviced by one call to Paint.
One of the things that can cause high CPU load in Paint method is improper (unmanaged) resource release. Make sure you do Dispose() all of the pens, brushes and so forth (probably all of the System.Drawing classes instances has some unmanaged resource tied to them).
Basically you should Dispose() of the object as soon as you have finished working with it. Do not cache or anything - GDI+ resources are system resources and should be returned to system as soon as possible. Acquiring them (creating new Brush class instance for example) should be pretty quick, but I do not have anything to back this statement up at the moment.
Also check the Clip Rectange in the PaintArgs event argument so that you only repaint the area that needs to get updated instead of re-drawing the entire image.
You can use this code to supress and resume redrawing of a control :]
Good luck.
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace pl.emag.audiopc.gui {
// ************************************************************************
//`enter code here`
// ************************************************************************
public class PaintingHelper {
// ********************************************************************
//
// ********************************************************************
public static void SuspendDrawing(Control parent) {
SendMessage(parent.Handle, WM_SETREDRAW, false, 0);
}
// ********************************************************************
//
// ********************************************************************
public static void ResumeDrawing(Control parent) {
SendMessage(parent.Handle, WM_SETREDRAW, true, 0);
parent.Refresh();
}
// ********************************************************************
//
// ********************************************************************
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, Int32 wMsg,
bool wParam, Int32 lParam);
// ********************************************************************
//
// ********************************************************************
private const int WM_SETREDRAW = 11;
}
}
I have a Windows Form (C# .NET 3.5) with a number of buttons and other controls on it, all assigned to a topmost Panel which spans the whole Form. For example, the hierarchy is: Form -> Panel -> other controls.
As soon as I assign a BackgroundImage to the Panel, the controls draw very slowly. I have the same effect if I use the Form's BackgroundImage property and set the Panel's BackgroundColor to "transparent". It appears as if the window with the background is drawn first, then each control is added one-by-one each with a slight delay before the next is drawn. In other words, you can actually follow the order in which each control is drawn to the Form. Once all Controls have been drawn once this effect doesn't happen anymore but the responsiveness of the Form is still slow.
In Visual Studio's designer I get the same effect, especially noticeable when moving controls around. Sometimes the form's drawing stops completely for a second or two which makes working with BackgroundImage a total drag, both in the designer and the resulting application.
Of course, I tried using DoubleBuffered = true, and I also set it on all controls using reflection, to no effect.
Also, here's the forms loading code because it's a bit unusual. It copies all controls from another form onto the current form. This is done in order to be able to edit each screen's visual appearance separately using the designer while sharing a common form and common code basis. I have a hunch that it may be the cause of the slowdowns, but it still doesn't explain why the slowdowns are already noticeable in the designer.
private void LoadControls(Form form)
{
this.SuspendLayout();
this.DoubleBuffered = true;
EnableDoubleBuffering(this.Controls);
this.BackgroundImage = form.BackgroundImage;
this.BackColor = form.BackColor;
this.Controls.Clear();
foreach (Control c in form.Controls)
{
this.Controls.Add(c);
}
this.ResumeLayout();
}
As you can see, SuspendLayout() and ResumeLayout() are used to avoid unnecessary redraw.
Still, the form is "slow as hell" once a BackgroundImage is used. I even tried converting it to PNG, JPG and BMP to see if that makes any difference. Also, the image is 1024x768 in size, but smaller images have the same slowdown effect (although slightly less).
What should I do?
SuspendLayout() and ResumeLayout() do not suspend drawing, only layout operations. Give this guy a shot:
public static class ControlHelper
{
#region Redraw Suspend/Resume
[DllImport("user32.dll", EntryPoint = "SendMessageA", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
private const int WM_SETREDRAW = 0xB;
public static void SuspendDrawing(this Control target)
{
SendMessage(target.Handle, WM_SETREDRAW, 0, 0);
}
public static void ResumeDrawing(this Control target) { ResumeDrawing(target, true); }
public static void ResumeDrawing(this Control target, bool redraw)
{
SendMessage(target.Handle, WM_SETREDRAW, 1, 0);
if (redraw)
{
target.Refresh();
}
}
#endregion
}
Usage should be pretty self-explanatory, and the syntax is identical to SuspendLayout() and ResumeLayout(). These are extension methods that will show on any instance of Control.
I also faced the same problem and could solve it by reducing the resolution of the background picture. When you use big sized (eg:1280X800) pictures as the background, it will take time to draw controls on the form.
It is better to open the picture in 'Paint' re size it smaller than your form and then save in 'bmp' format. Now try to add this picture as your form's background.
Another very simple way to avoid permanent redraws while adding your controls is to make the parent control invisible before you add controls to it. Afterwards you make the parent control (for example, a panel) visible and there it is without all those repaints. :-)
panelParent.visible = false;
for(...) {
// Add your controls here:
panelParent.Controls.Add(...);
}
panelParent.visible = true;
For me, the post Form load is slow if adding a background image solved the problem:
Make sure your backgroundcolor is not set to 'transparent'. Set it to 'white' for better performance.
Also, do BackgroundImageLayout to be either 'Center' or 'Stretch' to increase performance. This will enable the double buffer on the form.
I solved same problem using PictureBox. Just add PictureBox to your container, choose "Dock it in parent container" (or property Dock = Fill) and set it's Image. It will looks just like BackGroundImage of the parent control.
I realize this is an old thread, but I found it while searching for info on the same issue, so in case it's useful to someone at some point:
My situation: I have a 13 x 12 grid of panels, which have a background image set dynamically, and regularly changed, based on user selections. Each Panel also has a text label control added to it. In order for the text to overlay the background image, it has to be set to Transparent (btw - my experience was that BackgroundImageLayout of Zoom, Stretch, Center had little to no effect. BackColor set to transparent or white also had little effect).
Each draw of the grid of panels (including resizing) was taking about a second - not bad but very visible, and a usability issue on slower machines.
My images were not huge,but somewhat oversized.
What I found: By resizing my set of images to the exact panel size before setting the backgroundimage, the draw time went down dramatically - somewhere around 0.1 seconds. Since my program dynamically resizes panels based on windows size, I dynamically resize the set of images one time on windows resize event before setting the background of the 156 panels.
In hindsight, its an obvious optimization... resize 8 images once instead of repeatedly 156 times.