Currently, I'm using WinAPI in a c# project that deal with Dual-Screen configurations. My question is really simple: how to get the list of all window handle that are about 75% of their size on a specific monitor?
Best regards,
To get the screen with the largest portion of the window you can use this:
System.Windows.Forms.Screen screen = System.Windows.Forms.Screen.FromHandle(form.Handle);
Then you just have to calculate how many % there is on this screen.
Rectangle screenBounds = screen.Bounds;
Rectangle formBounds = form.Bounds;
Rectangle intersection = formBounds.Intersect(screenBounds);
int formArea = formBounds.Width * formBounds.Height;
int intersectArea = intersection.Width * intersection.Height;
int percent = intersectArea * 100 / formArea;
Ok, I created a more generic version, that is able to use any window handle.
Thank you all for your answers!
//Get the Screen object where the Hwnd handle is
System.Windows.Forms.Screen screen = System.Windows.Forms.Screen.FromHandle(Hwnd);
//Create rectangles object
Rectangle screenBound = screen.Bounds;
RECT handleRect = new RECT();
//Get dimensions of the Hwnd handle /!\ don't pass a Rectangle object
if (!GetWindowRect(Hwnd, ref handleRect))
{
//ERROR
}
//Getting the intersection between the two rectangles
Rectangle handleBound = new Rectangle(handleRect.Left, handleRect.Top, handleRect.Right-handleRect.Left, handleRect.Bottom-handleRect.Top);
Rectangle intersection = Rectangle.Intersect(screenBound, handleBound);
//Get the area of the handle
int formArea = handleBound.Width * handleBound.Height;
//Get the area of the intersection
int intersectArea = intersection.Width * intersection.Height;
//Calculate percentage
int percentage = intersectArea * 100 / formArea;
This is the RECT structure:
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
Related
I cannot inflate the original drawn rectangle through for-loop.
I wanted to maybe store the original drawn rectangle into an array and from their loop it, but it did not work properly.
loop_txtbx.Text = 5
parameter_txtbx.Text = 20
int[] rec = new int[loops];
int xCenter = Convert.ToInt32(startX_coord_txtbx.Text);
int yCenter = Convert.ToInt32(startY_coord_txtbx.Text);
int width = Convert.ToInt32(width_txtbx.Text);
int height = Convert.ToInt32(height_txtbx.Text);
//Find the x-coordinate of the upper-left corner of the rectangle to draw.
int x = xCenter - width / 2;
//Find y-coordinate of the upper-left corner of the rectangle to draw.
int y = yCenter - height / 2;
int loops = Convert.ToInt32(loop_txtbx.Text);
int param = Convert.ToInt32(parameter_txtbx.Text);
// Create a rectangle.
Rectangle rec1 = new Rectangle(x, y, width, height);
// Draw the uninflated rectangle to screen.
gdrawArea.DrawRectangle(color_pen, rec1);
for (int i = 0; i < loops; i++)
{
// Call Inflate.
Rectangle rec2 = Rectangle.Inflate(rec1, param, param);
// Draw the inflated rectangle to screen.
gdrawArea.DrawRectangle(color_pen, rec2);
}
Only 2 drawn rectangles are shown, while it was supposed to be 5. I cannot manage to modify rec2
You are using the same rec1 as a base to inflate. So after the first loop you get always the same size for the new rectangle.
You need to use rec2
Rectangle rec2 = rec1;
for (int i = 0; i < loops; i++)
{
rec2 = Rectangle.Inflate(rec2, param, param);
....
}
But with this approach you should invert the order of the calls to draw the initial rectangle
Rectangle rec2 = rec1;
for (int i = 0; i < loops; i++)
{
// Draw the current rectangle to screen.
gdrawArea.DrawRectangle(color_pen, rec2);
// Call Inflate.
rec2 = Rectangle.Inflate(rec2, param, param);
}
I am using the following code to get the WindowRect for a process on my machine (been testing with the Windows 8.1 calculator).
RECT rc;
GetWindowRect(hWnd, out rc);
var bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
var gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap = gfxBmp.GetHdc();
PrintWindow(hWnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
gfxBmp.Dispose();
But on Windows 8.1, the bitmap produced is cropped (in the case of the calculator, by about 100 pixels width and 150 height).
RECT is defined as:
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
I've been pulling my hair out with this, can anyone spot what I'm doing wrong?
Cheers
Rich
I have up to 4 rectangles on an image, for each of these rectangles I know their top left X,Y coordinate and their width,height. I want to create an Int32Rect with dimensions from most top left rectangle to the most bottom right rectangle. The main issue is that you can only create a System.Windows.Int32Rect with x,y,width,height parameters. Any ideas how I can accomplish this with my currently known values?
EDIT:
Trying to clarify, I want to create a Int32Rect that is the dimensions of all other "rectangles" on my image. So one large Int32Rect that starts from the "rectangle" at the top-left portion of the image and stretches to the "rectangle" that is at the bottom-right portion of the image.
Here's some code that does this for a single rectangle:
var topLeftOfBox = new Point(centerOfBoxRelativeToImage.X - Box.Width/2,
centerOfBoxRelativeToImage.Y - Box.Height/2);
return new CroppedBitmap(originalBitmap, new Int32Rect(topLeftOfBox.X, topLeftOfBox.Y, Box.Width, Box.Height));
Thanks for the help and ideas everyone, I found Aybe's answer to work the best for me.
You need to grab x/y mins/maxs for each rectangle and build a rectangle out of these values:
using System.Linq;
using System.Windows;
internal class Class1
{
public Class1()
{
var rect1 = new Int32Rect(10, 10, 10, 10);
var rect2 = new Int32Rect(30, 30, 10, 10);
var rect3 = new Int32Rect(50, 50, 10, 10);
var rect4 = new Int32Rect(70, 70, 10, 10);
var int32Rects = new[] { rect1, rect2, rect3, rect4 };
var int32Rect = GetValue(int32Rects);
}
private static Int32Rect GetValue(Int32Rect[] int32Rects)
{
int xMin = int32Rects.Min(s => s.X);
int yMin = int32Rects.Min(s => s.Y);
int xMax = int32Rects.Max(s => s.X + s.Width);
int yMax = int32Rects.Max(s => s.Y + s.Height);
var int32Rect = new Int32Rect(xMin, yMin, xMax - xMin, yMax - yMin);
return int32Rect;
}
}
Result:
{10,10,70,70}
The question is somewhat unclear and so I will start of by stating my understanding of what you are looking for. As I read your question, you have a collection of rectangles and are looking to find the smallest rectangle that contains all rectangles in your collection.
Here is what you need to know in order to do this:
The top left of a rect is (X, Y) and the bottom right is (X+Width, Y+Height).
The width of a rectangle is given by Right - Left. The height is given by Bottom - Top.
Now that you can convert between these quantities the rest is easy. Find the minimum top value, the minimum left value, the maximum right value and the maximum bottom value. Feed those values into the equations in the second bullet point above and you are done.
You can save some effort, and avoid being concerned about having "world knowledge" of which Rectangle is bottom-most/top.most left-most/right-most:
public static class RectangleExtensions
{
public static Rectangle RectsGetBounds(this Rectangle rect, params Rectangle[] rects)
{
Rectangle rbase = rect;
for (int i = 0; i < rects.Length; i++)
{
rbase = Rectangle.Union(rbase, rects[i]);
}
return rbase;
}
public static Rectangle ControlsGetBounds(this Control cntrl, params Control[] cntrls)
{
return RectsGetBounds(cntrl.Bounds, cntrls.Select(c => c.Bounds).ToArray());
}
}
I am fairly new to xna. I just created a sprite with transparent background(magenta). Problem is my Rectangle is reading the coordinates of whole sprite not of visible one. How do I make it read only the visible sprite.
myrectangle = new Rectangle(0, 0, box.Width, box.Height);
I want to place my visible part not transparent at that position. Thanks in advance.
To transform a color to transparent, go to the texture properties, content processor, and enable Color Key, and set the key Color to magenta.
Then to positioning the sprite where you want, you need to set the proper origin.
To set the ship center in the desired position, is needed to set the origin as shown:
So when you draw it, you need doing similar to this:
var origin = new Vector2(40,40);
spritebatch.Draw(shipTexture, shipPosition, null, Color, origin, ...)
You can change your texture rectangle source too:
var texSource = new Rectangle( 25,25, 30,30);
spritebatch.Draw(shipTexture, shipPosition, texSource, Color)
Although you may need to change the origin if you want to position the ship at its center
You need to manually measure the offset of the point you need using a program like Paint and then set that offset in the parameter Origin in the Draw method.
A better idea is to measure the size in pixel of your sprite (without the background) and the set it as the sourceRectangle in the Draw method.
spritebatch.Draw(textureToDraw, Position, sourceRectangle, Color.White)
SourceRectangle is nullable, its defalut value is null, and in that case XNA will draw the whole texture, and you don't need that.
Using transparent color coding like Magenta is very old-fashioned. Nowadays we use the alpha in the images to achieve this.
I guess the only real way to do what you want to do is to search through the color-data to find the smallest and the largest x and y coordinates which have alpha > 0, or != Color.Magenta in your case.
Texture2D sprite = Content.Load<Texture2D>(.....);
int width = sprite.Width;
int height = sprite.Height;
Rectangle sourceRectangle = new Rectangle(int.Max, int.Max, 0, 0);
Color[] data = new Color[width*height];
sprite.GetData<Color>(data);
int maxX = 0;
int maxY = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int index = width * y + x;
if (data[index] != Color.Magenta)
{
if (x < sourceRectangle.X)
sourceRectangle.X = x;
else if (x > maxX)
maxX = x;
if (y < sourceRectangle.Y)
sourceRectangle.Y = y;
else if (y > maxY)
maxY = y;
}
}
}
sourceRectangle.Width = maxX - sourceRectangle.X;
sourceRectangle.Height = maxY - sourceRectange.Y;
I use a cheat method in VB.Net, which I assume you could make work in C#:
Private Function MakeTexture(ByVal b As Bitmap) As Texture2D
Using MemoryStream As New MemoryStream
b.Save(MemoryStream, System.Drawing.Imaging.ImageFormat.Png)
Return Texture2D.FromStream(XNAGraphics.GraphicsDevice, MemoryStream)
End Using
End Function
As long as your bitmap is loaded with a transparent color, this works slick.
I need a way to center the current window on the screen.
So for example, if a user pushes a button, I want the window to center itself on the screen.
I know you can use the startposition property, but I cannot figure out a way to use that other than when the application first starts up.
So how do I center the form on the screen?
Use Form.CenterToScreen() method.
Using the Property window
Select form → go to property window → select "start position" → select whatever the place you want.
Programmatically
Form form1 = new Form();
form1.StartPosition = FormStartPosition.CenterScreen;
form1.ShowDialog();
Note: Do not directly call Form.CenterToScreen() from your code. Read here.
A single line:
this.Location = new Point((Screen.PrimaryScreen.WorkingArea.Width - this.Width) / 2,
(Screen.PrimaryScreen.WorkingArea.Height - this.Height) / 2);
In Windows Forms:
this.StartPosition = FormStartPosition.CenterScreen;
In WPF:
this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
That's all you have to do...
If you want to center your windows during runtime use the code below, copy it into your application:
protected void ReallyCenterToScreen()
{
Screen screen = Screen.FromControl(this);
Rectangle workingArea = screen.WorkingArea;
this.Location = new Point() {
X = Math.Max(workingArea.X, workingArea.X + (workingArea.Width - this.Width) / 2),
Y = Math.Max(workingArea.Y, workingArea.Y + (workingArea.Height - this.Height) / 2)};
}
And finally call the method above to get it working:
ReallyCenterToScreen();
Centering a form in runtime
1.Set following property of Form:
-> StartPosition : CenterScreen
-> WindowState: Normal
This will center the form at runtime but if form size is bigger then expected, do second step.
2. Add Custom Size after InitializeComponent();
public Form1()
{
InitializeComponent();
this.Size = new Size(800, 600);
}
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace centrewindow
{
public partial class Form1 : Form
{
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
[DllImport("user32.dll")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(HandleRef hwnd, out RECT lpRect);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
CentreWindow(Handle, GetMonitorDimensions());
}
private void CentreWindow(IntPtr handle, Size monitorDimensions)
{
RECT rect;
GetWindowRect(new HandleRef(this, handle), out rect);
var x1Pos = monitorDimensions.Width/2 - (rect.Right - rect.Left)/2;
var x2Pos = rect.Right - rect.Left;
var y1Pos = monitorDimensions.Height/2 - (rect.Bottom - rect.Top)/2;
var y2Pos = rect.Bottom - rect.Top;
SetWindowPos(handle, 0, x1Pos, y1Pos, x2Pos, y2Pos, 0);
}
private Size GetMonitorDimensions()
{
return SystemInformation.PrimaryMonitorSize;
}
}
}
Centers any window you can get the handle of
Use this:
this.CenterToScreen(); // This will take care of the current form
Might not be completely relevant to the question. But maybe can help someone.
Center Screen non of the above work for me. Reason was I was adding controls dynamically to the form. Technically when it centered it was correct , based on the form before adding the controls.
So here was my solution. ( Should work with both scenarios )
int x = Screen.PrimaryScreen.Bounds.Width - this.PreferredSize.Width;
int y = Screen.PrimaryScreen.Bounds.Height - this.PreferredSize.Height;
this.Location = new Point(x / 2, y / 2);
So you will notice that I am using "PreferredSize" instead of just using Height / Width.
The preferred size will hold the value of the form after adding the controls. Where Height / Width won't.
Hope this helps someone .
Cheers
Use Location property of the form. Set it to the desired top left point
desired x = (desktop_width - form_witdh)/2
desired y = (desktop_height - from_height)/2
Working sample
private void barButtonItem1_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
AccountAddForm f = new AccountAddForm();
f.StartPosition = FormStartPosition.CenterScreen;
f.Show();
}
You can use the Screen.PrimaryScreen.Bounds to retrieve the size of the primary monitor (or inspect the Screen object to retrieve all monitors). Use those with MyForms.Bounds to figure out where to place your form.
In case of multi monitor and If you prefer to center on correct monitor/screen then you might like to try these lines:
// Save values for future(for example, to center a form on next launch)
int screen_x = Screen.FromControl(Form).WorkingArea.X;
int screen_y = Screen.FromControl(Form).WorkingArea.Y;
// Move it and center using correct screen/monitor
Form.Left = screen_x;
Form.Top = screen_y;
Form.Left += (Screen.FromControl(Form).WorkingArea.Width - Form.Width) / 2;
Form.Top += (Screen.FromControl(Form).WorkingArea.Height - Form.Height) / 2;