When I set the position of my game window using form.DesktopLocation(new Point(X, Y);, it works, but then XNA automatically resets it to the center of the screen. I tried googling and looking on stack overflow, but I can't seem to figure out how to prevent it centering. Any ideas?
Here's my code manipulating the GraphicsDevice, GraphicsDeviceManager, and the Form controlling the game:
// in the initialiser
window = (Form)Form.FromHandle(Window.Handle);
GraphicsDeviceManager = new GraphicsDeviceManager(this);
// in the draw method
g.PreferredBackBufferWidth = Width;
g.PreferredBackBufferHeight = Height;
g.ApplyChanges();
f.DesktopLocation = new System.Drawing.Point(X, Y);
And that's literally everything. I tried setting the desktop location more than once each frame, before and after the g.ApplyChanges(), and that doesn't change anything.
However, just now, I tried removing the g.ApplyChanges() and it doesn't do it anymore. Odd. Why would running a g.ApplyChanges() before changing the form only sometimes recenter the form AFTER I positioned the form?
There is no solution to this question.
XNA automatically resets the position of the window to the center of the screen on g.ApplyChanges, and whenever it hits another window-related operation, such as resizing, or moving the window from one monitor to another.
This is a problem in XNA, not with XNA code. XNA was written with no way to prevent this because their focus is on content, not windows. Therefore, if you want greater control over your forms, it's a better idea to not use XNA at all, or use XNA's output in a form you set up.
Related
I’m working on a little overlay for Diablo 3 (For personal use only!)
I want just to draw a Text string (We’ll see later for font) in the middle of the screen.
But with XNA I cannot find how to put background to transparent…
My code so far is :
GraphicsDevice.Clear(new Color(0, 0, 0, 255));
spriteBatch.Begin();
spriteBatch.DrawString(font, this.TestToShow, new Vector2(23, 23), Color.White);
spriteBatch.End();
So I only need 1 thing: make this black transparent!
You do not seem to understand what GraphicsDevice.Clear(Color) does.
XNA opens a Windows Window and draws with DirectX into it.
GraphicsDevice.Clear(Color) clears the buffer drawn with DirectX, but doesn't have anything to do with the window.
To make the window transparent, you have to modify the underlaying window.
To do that you have to first add references to System.WIndows.Forms and System.Drawing.
In the Constructor of your Game1 class, you do the following:
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IntPtr hWnd = Window.Handle;
System.Windows.Forms.Control ctrl = System.Windows.Forms.Control.FromHandle(hWnd);
System.Windows.Forms.Form form = ctrl.FindForm();
form.TransparencyKey = System.Drawing.Color.Black;
}
Let's go through this line by line:
Well, the first two are auto generated and we don't care about these.
IntPtr hWnd = Window.Handle;
This line gets you the pointer to the underlaying Window that is registered in Windows.
System.Windows.Forms.Control ctrl = System.Windows.Forms.Control.FromHandle(hWnd);
This line gets the WindowsForms-Control in the given Window.
System.Windows.Forms.Form form = ctrl.FindForm();
This line gets you the Form to which the Control belongs to.
form.TransparencyKey = System.Drawing.Color.Black;
This last line sets the key-Color, that identifies one single Color-value to not be drawn at all. I used Black, but you could also choose CornflowerBlue.
This makes your window internally transparent for that Color. I suggest you should choose the same Color as your clear-Color.
Two things to note:
Best practice is to cache your Form so you can set the TransparencyKey from anywhere.
You can also make your Window borderless this way:
form.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
Hope I could help.
Edit:
I just realized this was asked years ago and had no answer. So feel free to use this, if you stumble upon it.
I currently have my XNA PC game running in a borderless windowed mode which has worked well until recently on my new laptop. It seems to start drawing a few hundred pixels off the screen to the top left. This leaves a gap at the bottom of the screen and on the right where you can see the desktop below the game. All the mouse coords are accurate to where you should have to mouse over and click on things even though it's just not drawing in the right place.
My older laptop it drew correctly but this new one I just got it seems to be off somehow. I am using David Amador's Resolution class to set up a virtual resolution via:
graphics = new GraphicsDeviceManager(this);
Resolution.Init(ref graphics);
control = Control.FromHandle(this.Window.Handle);
Resolution.SetVirtualResolution(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height);
Resolution.SetResolution(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height, false);
I set fullscreen to false and then once I create the game screen I do the following to set it to borderless:
Form gameForm = (Form)Form.FromHandle(curGame.Window.Handle);
gameForm.FormBorderStyle = FormBorderStyle.None;
I tried doing "gameForm.Location = new System.Drawing.Point(0, 0);" to try and force set it to start drawing at (0,0) but with my current set up that doesn't seem to do anything because I can set it to (150, 150) and it won't start drawing there. Here is a screenshot from my laptop that shows how it's moved the window and cut off the left and top side exposing the desktop:
Does anyone have any idea why it could be happening now on my new laptop when it hasn't happened on my desktop or old laptop ever before?
How would I draw something on a Canvas in C# for Windows Phone?
Okay, let me be a little more clear.
Say the user taps his finger down at 386,43 on the canvas. (the canvas is 768 by 480)
I would like my application to be able to respond by placing a red dot at 386,43 on the canvas.
I have no prior experience with Canvas whatsoever.
If this is too complex to be answered in one question (which it probably is), please give me links to other websites with Canvas and Drawing articles.
There are various ways of doing this. Depending on the nature of the red dot, you could make it a UserControl. For a basic circle, you can simply handle your canvas' ManipulationStarted event.
private void myCanvas_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Ellipse el = new Ellipse();
el.Width = 10;
el.Height = 10;
el.Fill = new SolidColorBrush(Colors.Red);
Canvas.SetLeft(el, e.ManipulationOrigin.X);
Canvas.SetTop(el, e.ManipulationOrigin.Y);
myCanvas.Children.Add(el);
}
I think you need to approach the problem differently. (I'm not including code on purpose, because of that).
Forms and controls in an Windows applications (including Phone) can be refreshed for several reasons, at any time. If you draw on a canvas in response to a touch action, you have an updated canvas until the next refresh. If a refresh occurs the canvas repaints itself, you end up with a blank canvas.
I have no idea what your end goal is, but you likely want to either keep track of what the user has done and store that state somewhere and show it in a canvas on the repaint of the canvas. This could be done with storing all the actions and "replaying" them on the canvas, or simply storing the view of the canvas as a bitmap and reload the canvas with that bitmap when refreshed. But, in the later case I think using a canvas isn't the right solution.
I want to draw directly on the desktop in C#. From searching a bit, I ended up using a Graphics object from the Desktop HDC (null). Then, I painted normally using this Graphics object.
The problem is that my shapes get lost when any part of the screen is redrawn. I tried a While loop, but it actually ends up drawing as fast as the application can, which is not the update rate of the desktop.
Normally, I would need to put my drawing code in a "OnPaint" event, but such thing does not exist for the desktop.
How would I do it?
Example code: https://stackoverflow.com/questions/1536141/how-to-draw-directly-on-the-windows-desktop-c
I posted two solutions for a similar requirement here
Basically you have two options.
1- Get a graphics object for the desktop and start drawing to it. The problem is if you need to start clearing what you have previously drawn etc.
Point pt = Cursor.Position; // Get the mouse cursor in screen coordinates
using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
{
g.DrawEllipse(Pens.Black, pt.X - 10, pt.Y - 10, 20, 20);
}
2- The second option that I provide in the link above is to create a transparent top-most window and do all your drawing in that window. This basically provides a transparent overlay for the desktop which you can draw on. One possible downside to this, as I mention in the original answer, is that other windows which are also top-most and are created after your app starts will obscure your top most window. This can be solved if it is a problem though.
For option 2, making the form transparent is as simple as using a transparency key, this allows mouse clicks etc. to fall through to the underlying desktop.
BackColor = Color.LightGreen;
TransparencyKey = Color.LightGreen;
When you draw to HDC(NULL) you draw to the screen, in an unmanaged way. As you've discovered, as soon as windows refreshes that part of the screen, your changes are overwritten.
There are a couple of options, depending upon what you want to achieve:
create a borderless, possibly
non-rectangular window. (Use
SetWindowRgn to make a window
non-rectangular.) You can make this a child of the desktop window.
subclass the desktop window. This is not straightforward, and involves
injecting a DLL into the
Explorer.exe process.
To get an OnPaint for the desktop you would need to subclass the desktop window and use your drawing logic when it receives a WM_PAINT/WM_ERASEBKGND message.
As the thread you linked to says, you can only intercept messages sent to a window of an external process using a hook (SetWindowsHookEx from a DLL).
As mentioned a transparent window is another way to do it, or (depending on the update frequency) copying, drawing and setting a temporary wallpaper (as bginfo does).
This is difficult to do correctly.
It will be far easier, and more reliable, to make your own borderless form instead.
I'm making a .NET 3.5 app with a form that draws a partially transparent black background. I'm overriding OnPaintBackground to accomplish this:
protected override void OnPaintBackground( PaintEventArgs e )
{
using ( Brush brush = new SolidBrush( Color.FromArgb( 155, Color.Black ) ) )
{
e.Graphics.FillRectangle( brush, e.ClipRectangle );
}
}
It works, but occasionally the form draws over itself without clearing the screen, making the transparency darker than it should be. I've tried playing with Graphics.Flush() and Graphics.Clear(), but it either doesn't help or completely removes transparency. Any suggestions?
Edit:
Here's what it looks like, after starting the app on the left, and after the form redraws itself a few times (in response to tabbing from one control to another) on the right:
Transparency Issue http://www.quicksnapper.com/files/5085/17725729384A10347269148_m.png
Edit 2:
I was trying a few things out this morning and noticed that when the desktop behind the transparent portions change, it's not actually being redrawn. For example, if I open Task Manager and put it behind the window, you don't see it refreshing itself. This makes sense with what I've been seeing with the transparency levels. Is there a function to make Windows redraw the area behind your window?
Edit 3:
I've tried changing a few properties on the form, but they all result in the form drawing non-transparent black:
this.AllowTransparency = true;
this.DoubleBuffered = true;
this.Opacity = .99;
I'm going to try creating a separate window for the transparent portion as overslacked mentioned, but any other ideas are still welcome.
I think I would call this expected behavior, actually. What I would do is render my background to an in-memory bitmap and, in the paint event, copy that to the form (basic double-buffering).
If I'm way off base, could you post a screenshot? I don't know that I'm imagining what you're describing correctly.
EDIT:
I'm wondering about your use of OnPaintBackground... pre-.NET, if you were doing double-buffering you'd catch and ignore the WM_ERASKBKGND message (to prevent flicker), render your image to an offscreen buffer, and copy from the buffer to the screen on WM_PAINT. So, try changing from the OnPaintBackground to OnPaint.
I haven't done too much of this kind of thing in .NET, but I had a pretty good handle on it before; I just don't know if it'll translate well or not!
EDIT 2:
Marc, the more I think about what you're trying to do, the more problems appear. I was going to suggest creating a background thread dedicated to capturing the screen and rendering it darkened; however, in order to remove your own form you'd have to set the visibility to false which would create other problems....
If you're unwilling to give up, I would suggest creating two windows and "binding" them together. Create a semi-opaque window (by setting opacity) for your background window, and create a second "normal" window for the foreground. Use SetWindowRgn on the foreground window to cut away the background and position them on top of each other.
Good luck!
Is Graphics.CompositingMode set to CompositingMode.SourceCopy? That should cause painting the background twice to be equivalent to painting it once, since it will replace the existing alpha/color data instead of compositing over it.