3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# Formの表示領域、座標変換、キャプチャ等に関するメモ

Last updated at Posted at 2023-11-26

WindowsForm(.NET Framework4.8)の表示、座標、画面キャプチャ等に関するメモ
記載のソースコードはusing System.Windows.Forms;前提です。

座標、領域

用語説明

座標

種類 内容
スクリーン座標 プライマリモニタの左上隅を原点(0,0)とした座標。マルチモニタ環境では座標がマイナスになることもあるようです。仮想画面参照。
クライアント座標 ウィンドウ領域内の描画可能な領域(=クライアント領域)の左上隅の点を原点とした座標

coordinate.png

参考URL

Point
point.png

領域(四角形)

Rectangle
rectangle.png

フォームの領域

form_coordinate.png

スクリーン座標でのクライアント領域

Rectangle rectangle = form.RectangleToScreen(form.ClientRectangle);

スクリーン座標でのウィンドウ領域

Rectangle rectangle = form.Bounds;

スクリーン座標でのウィンドウ可視領域

[DllImport("dwmapi.dll")]
private static extern int DwmGetWindowAttribute(IntPtr hWnd, uint dwAttribute, out RECT pvAttribute, int cbAttribute);

[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

RECT rect;
DwmGetWindowAttribute(form.Handle, DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(typeof(RECT)));
Rectangle rectangle = new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);

サンプルコード(本項目の最初に表示してる画像用の全コード)

using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private Label label = new Label { AutoSize = true, Location = new Point(10, 10) };

        public Form1()
        {
            InitializeComponent();

            //画面の最左端に表示
            StartPosition = FormStartPosition.Manual;
            Location = new Point(0, 0);

            //ラベル追加
            Controls.Add(label);

            //フォーム表示時にウィンドウ領域情報を表示
            Shown += (sender, e) =>
            {
                label.Text += RectToString(WindowSizeMethod.GetWindowArea(this), "ウィンドウ領域");
                label.Text += RectToString(WindowSizeMethod.GetWindowVisibleArea(this), "ウィンドウ可視領域");
                label.Text += RectToString(WindowSizeMethod.GetClientArea(this), "クライアント領域");
            };
        }

        private string RectToString(Rectangle rectangle, string name)
        {
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.AppendLine(name);
            stringBuilder.Append(" Top=");
            stringBuilder.Append(rectangle.Top.ToString());
            stringBuilder.Append(" Left=");
            stringBuilder.Append(rectangle.Left.ToString());
            stringBuilder.Append(" Width=");
            stringBuilder.Append(rectangle.Width.ToString());
            stringBuilder.Append(" Height=");
            stringBuilder.Append(rectangle.Height.ToString());
            stringBuilder.AppendLine("");
            stringBuilder.AppendLine("");

            return stringBuilder.ToString();
        }
    }

    //ウィンドウ領域取得
    public static class WindowSizeMethod
    {
        private const uint DWMWA_EXTENDED_FRAME_BOUNDS = 9;

        private static class NativeMethods
        {
            [DllImport("dwmapi.dll")]
            internal static extern int DwmGetWindowAttribute(IntPtr hWnd, uint dwAttribute, out RECT pvAttribute, int cbAttribute);
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        /// <summary>
        /// フォームのスクリーン座標でのクライアント領域
        /// </summary>
        public static Rectangle GetClientArea(Form form)
        {
            return form.RectangleToScreen(form.ClientRectangle);
        }

        /// <summary>
        /// フォームのスクリーン座標でのウィンドウ領域
        /// </summary>
        public static Rectangle GetWindowArea(Form form)
        {
            return form.Bounds;
        }

        /// <summary>
        /// フォームのスクリーン座標でのウィンドウ可視領域
        /// </summary>
        /// <returns>取得できなかった場合はウィンドウ領域を返す</returns>
        public static Rectangle GetWindowVisibleArea(Form form)
        {
            RECT rect;
            int ret = NativeMethods.DwmGetWindowAttribute(form.Handle, DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(typeof(RECT)));
            if (ret == 0)
            {
                return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
            }

            return GetWindowArea(form);
        }
    }
}

参考URL

コントロールの領域

スクリーン座標でのコントロールのクライアント領域

Rectangle rectangle = control.RectangleToScreen(control.ClientRectangle);

スクリーン座標でのコントロールの領域

Rectangle rectangle = control.Parent.RectangleToScreen(control.Bounds);

ディスプレイの領域

フォームのあるディスプレイ領域の取得

Rectangle displayArea = Screen.GetBounds(this);//thisはformを指す

フォームのあるディスプレイの作業領域(タスクバーなどを除いた領域)の取得

Rectangle workingArea = Screen.GetWorkingArea(this);//thisはformを指す

マルチディスプレイでの各ディスプレイ領域の取得

foreach (var screen in Screen.AllScreens)
{
    Rectangle rectangle = screen.Bounds;//各ディスプレイの領域
}

プライマリディスプレイ領域の取得

Rectangle rectangle = Screen.PrimaryScreen.Bounds;

仮想画面領域の取得

Rectangle rectangle = GetVirtualScreen();

//仮想画面の取得
private Rectangle GetVirtualScreen()
{
    return GetCombinedScreen(Screen.AllScreens);
}

//結合スクリーン領域の取得
private Rectangle GetCombinedScreen(Screen[] screens)
{
    int top = 0, left = 0, bottom = 0, right = 0;
    bool first = true;
    foreach (var screen in screens)
    {
        if (first)
        {
            first = false;
            top = screen.Bounds.Top;
            left = screen.Bounds.Left;
            bottom = screen.Bounds.Bottom;
            right = screen.Bounds.Right;
        }

        if (screen.Bounds.Top < top)
        {
            top = screen.Bounds.Top;
        }
        if (screen.Bounds.Left < left)
        {
            left = screen.Bounds.Left;
        }
        if (bottom < screen.Bounds.Bottom)
        {
            bottom = screen.Bounds.Bottom;
        }
        if (right < screen.Bounds.Right)
        {
            right = screen.Bounds.Right;
        }
    }

    return new Rectangle(left, top, right - left, bottom - top);
}

簡易タスクバー有無確認

Rectangle displayArea = Screen.GetBounds(this);
Rectangle workingArea = Screen.GetWorkingArea(this);
bool isTaskbarExist = !(displayArea.Height == workingArea.Height && displayArea.Width == workingArea.Width);

簡易タスクバー位置判定

Rectangle displayArea = Screen.GetBounds(this);
Rectangle workingArea = Screen.GetWorkingArea(this);

//タスクバーが下にあるか
bool isTaskbarPositionBottom = displayArea.Height != workingArea.Height && displayArea.Width == workingArea.Width && workingArea.Top == 0;

//タスクバーが上にあるか
bool isTaskbarPositionTop = displayArea.Height != workingArea.Height && displayArea.Width == workingArea.Width && workingArea.Top != 0;

//タスクバーが左にあるか
bool isTaskbarPositionLeft = displayArea.Height == workingArea.Height && displayArea.Width != workingArea.Width && workingArea.Left != 0;

//タスクバーが右にあるか
bool isTaskbarPositionRight = displayArea.Height == workingArea.Height && displayArea.Width != workingArea.Width && workingArea.Left == 0;

参考URL

座標変換

スクリーン座標をクライアント座標に変換:PointToClientRectangleToClient
クライアント座標をスクリーン座標に変換:PointToScreenRectangleToScreen

スクリーン座標でのマウスカーソル位置

Point mouse = Cursor.Position;

コントロール内にマウスカーソルがあるか

//実装例1
private bool IsCursorInControl(Control control)
{
    Point mouse = Cursor.Position;
    return control.ClientRectangle.Contains(control.PointToClient(mouse));
}

//実装例2
private bool IsCursorInControl(Control control)
{
    Point mouse = Cursor.Position;
    return control.RectangleToScreen(control.ClientRectangle).Contains(mouse);
}

//参考)ウィンドウ領域にマウスカーソルがあるか
private bool IsMouseInWindow => Bounds.Contains(Cursor.Position);

コントロールの左上隅の原点をスクリーン座標で取得

//Bounds:親コントロールに対する相対的なサイズおよび位置
Point point = control.Parent.PointToScreen(control.Bounds.Location);

参考URL

フォームの表示関連

コード内のformフォームを指します。Form form = new Form();

フォームが表示されているか、存在しているか

formが表示されているか

bool isFormVisible => form?.Visible ?? false;//Hideで隠れている場合はfalse

formが存在しているか

bool isFormExist => form?.IsHandleCreated ?? false;//Hideで隠れている場合もtrue

formが存在していない場合は表示、存在している場合は閉じる

Form form = null;
bool isFormExist => form?.IsHandleCreated ?? false;

private void button_Click(object sender, EventArgs e)
{
    if (isFormExist)
    {
        form.Close();
    }
    else
    {
        form = new Form();
        form.Show();
    }
}

参考URL

モーダルフォーム、モーダレスフォーム

モーダルフォーム:フォームを閉じないと他の操作ができない

//フォームの中央に表示
Form form = new Form();
form.StartPosition = FormStartPosition.CenterParent;
form.ShowDialog(this);
form.Dispose();

モードレスフォーム:フォーム閉じなくても他の操作ができる

//フォームの中央に表示
Form form = new Form { StartPosition = FormStartPosition.Manual };
form.Left = this.Location.X + (this.Width - form.Width) / 2;
form.Top = this.Location.Y + (this.Height - form.Height) / 2;
form.Show();

参考URL

画面キャプチャ

高DPI対応

表示スケールが100%でないと正しくキャプチャできないため事前にApp.configを編集します。

表示スケールの確認
[設定]-[システム]-[ディスプレイ]
scale.png

App.configの編集

dpiapp.png

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<startup>
		<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
	</startup>
	<!--ここから追加-->
	<System.Windows.Forms.ApplicationConfigurationSection>
		<add key="DpiAwareness" value="PerMonitorV2" />
	</System.Windows.Forms.ApplicationConfigurationSection>
	<!--ここまで-->
</configuration>

補足
app.manifestファイルを編集して対応するやり方もあるようです。

  1. Visual Stuidoで[プロジェクト]-[新しい項目の追加]-[アプリケーション マニフェスト ファイル(Windows のみ)]でapp.manifestファイルを追加。
  2. 以下のコメントを解除
  <!--
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
    </windowsSettings>
  </application>
  -->

参考URL

キャプチャ

CopyFromScreen を使用します。

指定領域をキャプチャしてクリップボードにコピーする関数

//指定領域のキャプチャ
private void CaptureRectangle(Rectangle rectangle)
{
    if (rectangle.Width != 0 && rectangle.Height != 0)
    {
        using (Bitmap bmp = new Bitmap(rectangle.Width, rectangle.Height))
        {
            using (Graphics graphics = Graphics.FromImage(bmp))
            {
                //領域キャプチャ
                graphics.CopyFromScreen(rectangle.Location, new Point(0, 0), bmp.Size);

                //クリップボードにコピー
                Clipboard.SetImage(bmp);
            }
        }
    }
}

指定領域をキャプチャしてクリップボードにコピーする関数(マウスカーソルもキャプチャする場合)

using System.Runtime.InteropServices;

//マウスカーソルの取得
internal static class NativeMethods
{
    [DllImport("user32.dll")]
    public static extern bool GetCursorInfo(ref CURSORINFO pci);
}

[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
    public uint cbSize;
    public uint flags;
    public IntPtr hCursor;
    public POINT ptScreenPos;
}

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public int x;
    public int y;
}

//指定領域のキャプチャ
private void CaptureRectangle(Rectangle rectangle)
{
    if (rectangle.Width != 0 && rectangle.Height != 0)
    {
        using (Bitmap bmp = new Bitmap(rectangle.Width, rectangle.Height))
        {
            using (Graphics graphics = Graphics.FromImage(bmp))
            {
                //領域キャプチャ
                graphics.CopyFromScreen(rectangle.Location, new Point(0, 0), bmp.Size);

                //マウスカーソル取得
                CURSORINFO cursorinfo = new CURSORINFO();
                cursorinfo.cbSize = (uint)Marshal.SizeOf(cursorinfo);
                if (NativeMethods.GetCursorInfo(ref cursorinfo))
                {
                    Cursor cursor = new Cursor(cursorinfo.hCursor);
                    Point hotSpot = cursor.HotSpot;
                    Point cursorScreenPosition = Cursor.Position;

                    //マウスカーソルを描画
                    graphics.DrawIcon(Icon.FromHandle(cursorinfo.hCursor), cursorScreenPosition.X - hotSpot.X - rectangle.X, cursorScreenPosition.Y - hotSpot.Y - rectangle.Y);
                }

                //クリップボードにコピー
                Clipboard.SetImage(bmp);
            }
        }
    }
}

上記CaptureRectangleの使用例

//コントロールの表示領域を画像としてクリップボードにコピー
CaptureRectangle(control.Bounds);

//プライマリモニタのスクリーンを画像としてクリップボードにコピー
CaptureRectangle(Screen.PrimaryScreen.Bounds);

コントロールのコピーあればControl.DrawToBitmapを使用できます。 ※高DPI対応不要

private void SetControlImageToClipboard(Control control)
{
    int height = control.Size.Height;
    int width = control.Size.Width;

    using (Bitmap bitmap = new Bitmap(width, height))
    {
        control.DrawToBitmap(bitmap, new Rectangle(0, 0, width, height));
        Clipboard.SetImage(bitmap);
    }
}

参考URL

色の取得

マウスカーソル位置の色を取得

private Color GetCursorPositionColor()
{
    Color color = Color.Empty;
    using (Bitmap bitmap = new Bitmap(1, 1))
    {
        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            graphics.CopyFromScreen(Cursor.Position, new Point(0, 0), bitmap.Size);
        }
        color = bitmap.GetPixel(0, 0);
    }

    return color;
}

マウスカーソル位置の色を画像の透過色に設定

//フォーム上にpictureBoxがあり画像が表示されている前提
private void pictureBox_MouseClick(object sender, MouseEventArgs e)
{
    //現在の画像
    Bitmap transbmp = new Bitmap(pictureBox.Image);

    //透過色設定
    transbmp.MakeTransparent(GetCursorPositionColor());

    //画像切り替え
    if (pictureBox.Image != null)
    {
        pictureBox.Image.Dispose();
    }
    pictureBox.Image = transbmp;
}
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?