WindowsForm(.NET Framework4.8)の表示、座標、画面キャプチャ等に関するメモ
座標、領域
用語説明
座標
スクリーン座標 : 画面の左上隅を原点とした座標。マルチモニタ環境では座標がマイナスになることもあるようです。
クライアント座標 : フォームの描画可能なクライアント領域の左上隅の点を原点とした座標
参考URL
点
領域(四角形)
フォームの領域
スクリーン座標でのクライアント領域
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
- C# Win32API完全入門 #C# - Qiita
- 【Windows/C#】なるべく丁寧にDllImportを使う - Qiita
- Control.ClientRectangle Property (System.Windows.Forms) | Microsoft Learn
- Control.Bounds プロパティ (System.Windows.Forms) | Microsoft Learn
ディスプレイの領域
フォームのあるディスプレイ領域の取得
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
スクリーン座標とクライアント座標の変換
スクリーン座標をクライアント座標に変換:PointToClient、RectangleToClient
クライアント座標をスクリーン座標に変換:PointToScreen、RectangleToScreen
スクリーン座標でのマウスカーソル位置
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
- Control.Visible プロパティ (System.Windows.Forms) | Microsoft Learn
- Control.IsHandleCreated プロパティ (System.Windows.Forms) | Microsoft Learn
モーダルフォーム、モーダレスフォーム
モーダルフォーム:フォームを閉じないと他の操作ができない
//フォームの中央に表示
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
を編集します。
表示スケールの確認
[設定]-[システム]-[ディスプレイ]
App.config
の編集
<?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
ファイルを編集して対応するやり方もあるようです。
- Visual Stuidoで[プロジェクト]-[新しい項目の追加]-[アプリケーション マニフェスト ファイル(Windows のみ)]で
app.manifest
ファイルを追加。 - 以下のコメントを解除
<!--
<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
- C#で拡大率が違うマルチディスプレイ1画面キャプチャ: 黄昏のスペシャルパンダのブログ
- 高 DPI サポート - Windows Forms .NET Framework | Microsoft Learn
- c# - ディスプレイのカスタマイズでサイズの倍率を変更すると、スクリーンサイズが正しく取得できない - スタック・オーバーフロー
キャプチャ
コントロールの表示領域を画像としてクリップボードにコピー
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;
}