LoginSignup
2
0

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

Last updated at Posted at 2023-11-26

WindowsForm(.NET Framework4.8)の表示、座標、画面キャプチャ等に関するメモ

座標、領域

用語説明

座標

スクリーン座標 : 画面の左上隅を原点とした座標。マルチモニタ環境では座標がマイナスになることもあるようです。
クライアント座標 : フォームの描画可能なクライアント領域の左上隅の点を原点とした座標

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 displayArea = Screen.GetBounds(this);//thisはformを指す

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

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

簡易タスクバー有無確認

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 SetControlImageToClipboard(Control control)
{
    using (Bitmap bitmap = new Bitmap(control.ClientRectangle.Width, control.ClientRectangle.Height))
    {
        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            graphics.CopyFromScreen(control.PointToScreen(new Point(0, 0)), new Point(0, 0), bitmap.Size);
        }
        Clipboard.SetImage(bitmap);
    }
}

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;
}
2
0
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
2
0