はじめに
今回はVisualStudio 2022を使用して、Windowsフォームアプリケーションで画面上の好きな場所の色(カラーコード)を取得できるカラーピッカーを作ってみました。
勉強も兼ねて作成したものなので簡素な作りになっています。
備忘録として作り方を残しておきます。
アプリの概要
今回は以下のような動作のアプリを作ります。
- 画面上のどこでも、クリックした位置のカラーコードを取得する
- マウス座標と選択中の色が常に表示される
- アプリは常に最前面に表示される
- カラーコードはクリップボードにセットされ、そのまま張り付けて使える
使い方は以下の通りです。
- スタートボタンをクリック
- カラーコードを取得したい位置でクリック
- (1に戻る)
作り方
プロジェクトの作成
言語はC#、テンプレートはWindowsフォームアプリケーション(.NET Framework)で作成します。
画面レイアウト
フォームにコントロールを5つ配置しています。
それぞれの名前は以下の通りです。
番号 | 名前 | コントロール |
---|---|---|
① | startBtn | Button |
② | modeLabel | Label |
③ | cursorPosLabel | Label |
④ | colorBox | Label |
⑤ | colorCodeText | TextBox |
スクリプト
スクリプト全文はこちら。
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ColorPicker
{
public partial class Form1 : Form
{
// クリック判定(GetKeyState)のために必要
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern short GetKeyState(int nVirtkey);
Timer timer;
public Form1()
{
InitializeComponent();
// ウィンドウを最前面に表示
this.TopMost = true;
timer = new Timer();
timer.Interval = 30; // タイマーの間隔
timer.Tick += new EventHandler(TimerEventProcessor);
}
/// <summary>
/// タイマーで実行するイベント
/// </summary>
private void TimerEventProcessor(object sender, EventArgs e)
{
// マウス座標を更新
cursorPosLabel.Text = $"マウス座標:{Cursor.Position}";
// 選択中の色を取得・表示
Color color = GetScreenColor(Cursor.Position);
colorBox.BackColor = color;
// カラーコードを表示
string colorCode = string.Format("#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B);
colorCodeText.Text = colorCode;
// マウスクリックされたら
if (IsClickDown())
{
// タイマーストップ
timer.Stop();
// クリップボードにカラーコードをセット
Clipboard.SetDataObject(colorCode);
// スタートボタンを押下可能にする
startBtn.Enabled = true;
modeLabel.Text = "待機中";
}
}
/// <summary>
/// クリック判定
/// </summary>
private bool IsClickDown()
{
return GetKeyState(0x01) < 0;
}
/// <summary>
/// 画面の色を取得
/// </summary>
/// <param name="pos">マウス座標</param>
private Color GetScreenColor(Point pos)
{
// Bitmapの作成
Bitmap bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// Graphicsの作成
Graphics g = Graphics.FromImage(bmp);
// マウス座標にある画面をコピー
g.CopyFromScreen(pos.X, pos.Y, 0, 0, new Size(1, 1));
// ピクセルの色を取得
Color pixelColor = bmp.GetPixel(0, 0);
// Graphics解放
g.Dispose();
return Color.FromArgb(pixelColor.A, pixelColor.R, pixelColor.G, pixelColor.B);
}
/// <summary>
/// スタートボタンクリック
/// </summary>
private void startBtn_Click(object sender, EventArgs e)
{
// タイマースタート
timer.Start();
// スタートボタンを押下不可にする
startBtn.Enabled = false;
modeLabel.Text = "実行中";
}
}
}
1つずつ見ていきます。
// クリック判定(GetKeyState)のために必要
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern short GetKeyState(int nVirtkey);
Timer timer;
public Form1()
{
InitializeComponent();
// ウィンドウを最前面に表示
this.TopMost = true;
timer = new Timer();
timer.Interval = 30; // タイマーの間隔
timer.Tick += new EventHandler(TimerEventProcessor);
}
一番上に書かれているDLLのインポートは後述のクリック判定(GetKeyState
)の時に必要になります。
Form1()
内では、画面をクリックしたときにウィンドウが隠れてしまわないようにthis.TopMost = true;
で最前面に表示しています。
画面の情報更新頻度を変えたい時はtimer.Interval
の値を変更します。
値が大きいほど更新がゆっくりになります。
/// <summary>
/// タイマーで実行するイベント
/// </summary>
private void TimerEventProcessor(object sender, EventArgs e)
{
// マウス座標を更新
cursorPosLabel.Text = $"マウス座標:{Cursor.Position}";
// 選択中の色を取得・表示
Color color = GetScreenColor(Cursor.Position);
colorBox.BackColor = color;
// カラーコードを表示
string colorCode = string.Format("#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B);
colorCodeText.Text = colorCode;
// マウスクリックされたら
if (IsClickDown())
{
// タイマーストップ
timer.Stop();
// クリップボードにカラーコードをセット
Clipboard.SetDataObject(colorCode);
// スタートボタンを押下可能にする
startBtn.Enabled = true;
modeLabel.Text = "待機中";
}
}
主に画面情報の更新を行っている部分です。
左クリックされたらタイマーをストップしてクリップボードにカラーコードをセットします。
タイマーがストップしている間は情報が更新されなくなります。
/// <summary>
/// クリック判定
/// </summary>
private bool IsClickDown()
{
return GetKeyState(0x01) < 0;
}
左クリックされたかどうかを判定します。0x01
がマウスの左クリックに対応している仮想キーコードです。
/// <summary>
/// 画面の色を取得
/// </summary>
/// <param name="pos">マウス座標</param>
private Color GetScreenColor(Point pos)
{
// Bitmapの作成
Bitmap bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// Graphicsの作成
Graphics g = Graphics.FromImage(bmp);
// マウス座標にある画面をコピー
g.CopyFromScreen(pos.X, pos.Y, 0, 0, new Size(1, 1));
// ピクセルの色を取得
Color pixelColor = bmp.GetPixel(0, 0);
// Graphics解放
g.Dispose();
return Color.FromArgb(pixelColor.A, pixelColor.R, pixelColor.G, pixelColor.B);
}
CopyFromScreen
で画面の指定した範囲をキャプチャし、作成したBitmapから色を取得します。
/// <summary>
/// スタートボタンクリック
/// </summary>
private void startBtn_Click(object sender, EventArgs e)
{
// タイマースタート
timer.Start();
// スタートボタンを押下不可にする
startBtn.Enabled = false;
modeLabel.Text = "実行中";
}
スタートボタンをクリックしてタイマーをスタートします。
CopyFromScreenのずれを修正
コードが出来たので実行してみると、片方のモニター上ではきちんとカーソル位置の色が表示されましたが、もう片方のモニターではずれた位置の色が表示されてしまいました。
おそらく解像度が異なるモニターを使用しているのが原因だと思われます。
Microsoft Learnに書かれていた以下の対処法を試してみると、無事修正できました。
①マニフェストファイル(app.manifest
)を追加
②app.manifest
で以下の記述を探し、Windows10部分のコメントアウトを解除
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 compatibility ↓この行のコメントを外す -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
③App.config
の<configuration>
内に以下の記述を追加
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
④アプリケーションのエントリポイントでEnableVisualStyles
を呼びだす
※私の場合は最初からProgram.cs
のMain()
に書かれていました。
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
動作確認
最後に、カラーコードで色を調べられる原色大辞典というサイトを使ってアプリの動作確認をしてみます。
画像の水色の部分をクリックして取得したカラーコードを入力してみると、取得した色と同じ色が表示されました。
コメント