C#で作成したアプリケーションで壁紙を変更することができます。そこで一定の時間が経過すると壁紙を変更するアプリをつくることにします。
Contents
壁紙を変更するクラスを定義する
壁紙を変更するためにはWindowsAPI関数を使わなければなりません。そこで壁紙を変更するクラスを定義します。
このクラスではSystemParametersInfo関数を呼び出して壁紙を変更しています。Changeメソッドは画像ファイルのパスを渡すと壁紙を変更してくれます。しかしファイルのパスが画像ファイルではないものだと問題を起こします。そこで引数として渡されたパスは画像ファイルかどうかを確認しています。Image.FromFileメソッドでImageを取得できれば画像ファイルです。そうでない場合は例外処理をおこなっています。
また空文字列を渡せば壁紙を無効にすることができます。処理が成功したらtrue、失敗したらfalseを返しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
using System.Drawing; using System.Runtime.InteropServices; using System.Text; public class WallPaper { const uint SPI_SETDESKWALLPAPER = 20; const uint SPIF_UPDATEINIFILE = 1; const uint SPIF_SENDWININICHANGE = 2; const uint SPI_GETDESKWALLPAPER = 0x73; const int MAX_PATH = 260; [DllImport("user32.dll")] private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, StringBuilder pvParam, uint fWinIni); public static bool Change(string filePath) { if (filePath != null) { try { Image image = Image.FromFile(filePath); image.Dispose(); StringBuilder sb = new StringBuilder(filePath); SystemParametersInfo(SPI_SETDESKWALLPAPER, (uint)sb.Length, sb, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE); return true; } catch { return false; } } else { StringBuilder sb = new StringBuilder(""); SystemParametersInfo(SPI_SETDESKWALLPAPER, (uint)sb.Length, sb, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE); return true; } } } |
Form1クラスの定義
デザイナで以下のようなものを作ります。
最初に壁紙に使う画像ファイルが入ったフォルダをユーザーに選ばせます。それからこれは常駐アプリにしたいのでフォームの右上の×ボタンをクリックしてもフォームが非表示になるだけで終了しないようにします。またタスクトレイにアイコンを表示させ、非表示になったフォームを再表示させたり終了させることができるようにします。
1 2 3 4 5 6 7 8 9 10 11 |
using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Windows.Forms; using System.Xml.Serialization; public partial class Form1 : Form { } |
コンストラクタの前に、コンストラクタ内で実行される処理を先に示します。
通知アイコンの初期化
InitNotifyIconメソッドは通知アイコンの初期化をおこないます。通知アイコンが使えるようにするためにはアイコンの設定、通知アイコンの可視化、通知アイコンを右クリックしたときにコンテキストメニューが表示されるようにしなければなりません。
CreateIconメソッドはNotifyIcon.IconプロパティにセットするIconを生成するためのものです。ここでは青地に「壁」という文字が書かれたアイコンを生成しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public partial class Form1 : Form { Icon CreateIcon() { Bitmap bitmap = new Bitmap(32, 32); Graphics graphics = Graphics.FromImage(bitmap); graphics.Clear(Color.Blue); graphics.DrawString("壁", new Font("MS ゴシック", 20, FontStyle.Bold), Brushes.White, new Point(-1, 1)); graphics.Dispose(); return Icon.FromHandle(bitmap.GetHicon()); } } |
CreateContextMenuStripメソッドは通知アイコンを右クリックしたときに表示されるメニューを生成するためのものです。[フォームを表示する][終了する]の二つのメニューを表示させます。[フォームを表示する]がクリックされたときはthis.Visible = true;を実行し、[終了する]がクリックされたときはフィールド変数_isEnd を trueにしてアプリケーションを終了させます。_isEndがfalseのままだと終了処理を行なおうとしてもフォームが非表示になるだけだからです(この点については後述)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class Form1 : Form { bool _isEnd = false; ContextMenuStrip CreateContextMenuStrip() { ContextMenuStrip contextMenuStrip = new ContextMenuStrip(); contextMenuStrip.Items.Add("フォームを表示する", null, (sender, e) => { this.Visible = true; }); contextMenuStrip.Items.Add("終了する", null, (sender, e) => { _notifyIcon.Visible = false; _isEnd = true; Application.Exit(); }); return contextMenuStrip; } } |
InitNotifyIconメソッドはNotifyIconを初期化します。アイコンをセットしてコンテキストメニューが表示されるようにします。
1 2 3 4 5 6 7 8 9 10 11 |
public partial class Form1 : Form { NotifyIcon _notifyIcon = new NotifyIcon(); void InitNotifyIcon() { _notifyIcon.Icon = GetIcon(); _notifyIcon.Visible = true; _notifyIcon.ContextMenuStrip = CreateContextMenuStrip(); } } |
プロパティの定義
次にプロパティを示します。プロパティは壁紙に使う画像ファイルがあるフォルダのパス、どれだけ時間が経過したら次の壁紙に変更するか(分・秒)を管理するためのものです。これらの値や文字列が変更された場合はLabelに変更された値と文字列を表示されるようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public partial class Form1 : Form { string _selectedWallPaperFolderPath = ""; string SelectedWallPaperFolderPath { set { _selectedWallPaperFolderPath = value; LabelSelectedWallPaperFolderPath.Text = _selectedWallPaperFolderPath; } get { return _selectedWallPaperFolderPath; } } int _intervalMinutes = 0; int IntervalMinutes { set { _intervalMinutes = value; LabelInterval.Text = $"{_intervalMinutes} 分 {_intervalSeconds} 秒"; } get { return _intervalMinutes; } } int _intervalSeconds = 0; int IntervalSeconds { set { _intervalSeconds = value; LabelInterval.Text = $"{_intervalMinutes} 分 {_intervalSeconds} 秒"; } get { return _intervalSeconds; } } } |
設定ファイルの読み込みと保存
次回、起動したときに設定を一からやり直すのは面倒なので設定が変更されたらファイルにその情報を書き込みます。ファイルの保存場所は実行ファイルがあるフォルダと同じでファイル名はconfig.xmlです。
初回起動時は設定情報が記録されたファイルが存在しないので、そのときは壁紙が切り替わる時間は5分間隔、壁紙に使う画像のフォルダは未選択ということにしておきます。
もし設定ファイルが存在する場合は、ファイルに記録されている情報を読み出して、IntervalMinutesプロパティ、IntervalSecondsプロパティ、SelectedWallPaperFolderPathプロパティに読み出したデータをセットします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public partial class Form1 : Form { readonly string _configPath = Application.StartupPath + "\\config.xml"; FolderBrowserDialog _folderBrowserDialog = new FolderBrowserDialog(); public class Config { public string SelectedWallPaperFolderPath = ""; public int IntervalMinutes = 0; public int IntervalSeconds = 0; } void LoadConfig() { if (File.Exists(_configPath)) { XmlSerializer serializer = new XmlSerializer(typeof(Config)); StreamReader sr = new StreamReader(_configPath); Config config = (Config)serializer.Deserialize(sr); sr.Close(); SelectedWallPaperFolderPath = config.SelectedWallPaperFolderPath; _folderBrowserDialog.SelectedPath = _selectedWallPaperFolderPath; IntervalMinutes = config.IntervalMinutes; IntervalSeconds = config.IntervalSeconds; } else { IntervalMinutes = 5; IntervalSeconds = 0; SelectedWallPaperFolderPath = ""; } } } |
これは設定が変更されたときに、その設定をファイルに保存するメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form1 : Form { void SaveConfig() { Config config = new Config(); config.SelectedWallPaperFolderPath = SelectedWallPaperFolderPath; config.IntervalMinutes = IntervalMinutes; config.IntervalSeconds = IntervalSeconds; XmlSerializer serializer = new XmlSerializer(typeof(Config)); StreamWriter sw = new StreamWriter(_configPath); serializer.Serialize(sw, config); sw.Close(); } } |
壁紙を変更する処理
壁紙を次々と変更していくための処理を示します。
フィールド変数_wallPaperFilePathに壁紙に使える画像ファイルのパスが格納されているので、_wallPaperFilePath.Count == 0のときは何もしない、_indexをインクリメントしながら、画像ファイルのパスが_wallPaperFilePath[_index]であるものを壁紙にしています。_index と _wallPaperFilePath.Countが同じになったら1周したことになるので_indexを0にリセットしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class Form1 : Form { List<string> _wallPaperFilePath = new List<string>(); int _index = 0; void ChangeWallPaper() { if (_wallPaperFilePath.Count == 0) return; _index++; if (_index >= _wallPaperFilePath.Count) _index = 0; WallPaper.Change(_wallPaperFilePath[_index]); } } |
コンストラクタ
以上を前提にしてコンストラクタを示します。通知アイコンを初期化してタイマーが動作しているときはユーザーが指定した一定の間隔でChangeWallPaperメソッドを呼び出して壁紙を変更できるようにします。また設定ファイルを読み出して各プロパティにセットします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class Form1 : Form { Timer _timer = new Timer(); public Form1() { InitializeComponent(); InitNotifyIcon(); _timer.Tick += (sender2, e2) => ChangeWallPaper(); LoadConfig(); Font font = LabelInterval.Font; LabelInterval.Font = new Font(font, FontStyle.Bold); LabelSelectedWallPaperFolderPath.Font = new Font(font, FontStyle.Bold); font.Dispose(); } } |
×ボタンをクリックしても終了させない処理
ユーザーによって右上の×ボタンが押されたときの処理を示します。_isEndがfalseの場合はフォームを非表示にするだけです。_isEndがtrueの場合は通常の終了処理がおこなわれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public partial class Form1 : Form { protected override void OnFormClosing(FormClosingEventArgs e) { if (!_isEnd) { e.Cancel = true; this.Visible = false; } base.OnFormClosing(e); } } |
壁紙に使うフォルダを設定する処理
壁紙に使える画像ファイルが保存されているフォルダを指定するときの処理を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class Form1 : Form { private void ButtonSelectFolder_Click(object sender, EventArgs e) { SetSelectedWallPaperFolderPath(); } bool SetSelectedWallPaperFolderPath() { if (_folderBrowserDialog.ShowDialog() == DialogResult.OK) { SelectedWallPaperFolderPath = _folderBrowserDialog.SelectedPath; SaveConfig(); return true; } else return false; } } |
壁紙を次々と変更する処理
ShowWallPapersメソッドが呼び出されたとき、SelectedWallPaperFolderPathプロパティが空文字列のとき、またはそのようなフォルダが存在しない場合はメッセージボックスとSelectedWallPaperFolderPathプロパティを設定するための処理がおこなわれます。
SelectedWallPaperFolderPathプロパティが適切に設定されている場合は、そのフォルダのなかの画像ファイルを_tempWallPaperFolderPathで指定されたフォルダ内にコピーします。
このときに前の設定で別の画像ファイルが壁紙に使われないように_tempWallPaperFolderPathで指定されたフォルダをいったん削除して作り直す処理をおこないます。
それからデスクトップサイズよりも大きな画像を壁紙に設定すると画像がはみだしてしまい全部が表示されないので、サイズ変更をした状態でコピーします。
処理が終わったらChangeWallPaperメソッドを呼び出し、いったん停止していたタイマーをスタートさせます。これで一定間隔でChangeWallPaperメソッドが実行されるので一定間隔で壁紙を変更することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
public partial class Form1 : Form { readonly string _tempWallPaperFolderPath = Application.StartupPath + "\\wall-paper"; private void ButtonShowWallPapers_Click(object sender, EventArgs e) { ShowWallPapers(); } void ShowWallPapers() { if (SelectedWallPaperFolderPath == "" || !Directory.Exists(SelectedWallPaperFolderPath)) { MessageBox.Show("壁紙として使う画像ファイルが保存されているフォルダが指定されていません"); if (!SetSelectedWallPaperFolderPath()) return; } if (!Directory.Exists(_tempWallPaperFolderPath)) Directory.CreateDirectory(_tempWallPaperFolderPath); else { Directory.Delete(_tempWallPaperFolderPath, true); Directory.CreateDirectory(_tempWallPaperFolderPath); } _timer.Stop(); _wallPaperFilePath.Clear(); _index = 0; string[] paths = Directory.GetFiles(SelectedWallPaperFolderPath, "*.*", SearchOption.AllDirectories); //ディスプレイの高さと幅を取得 Screen screen = Screen.FromControl(this); int h = screen.Bounds.Height; int w = screen.Bounds.Width; foreach (string path in paths) { try { Image image = Image.FromFile(path); double d1 = 1.0; double d2 = 1.0; // ディスプレイの幅・高さをよりも大きな画像であれば縮小してコピーする if (h < image.Height) d1 = (double)h / image.Height * 0.7; if (w < image.Width) d2 = (double)w / image.Width * 0.7; double d = Math.Min(d1, d2); if (d < 1) { int newWidth = (int)(image.Width * d); int newHeight = (int)(image.Height * d); Bitmap bitmap = new Bitmap(newWidth, newHeight); Graphics graphics = Graphics.FromImage(bitmap); graphics.DrawImage(image, new Rectangle(0, 0, newWidth, newHeight)); graphics.Dispose(); image.Dispose(); image = bitmap; } // 背景が透過した画像だとその部分が黒く表示されてしまうので、 // その部分はデスクトップの色に変更する { Bitmap bitmap = new Bitmap(image.Width, image.Height); Graphics graphics = Graphics.FromImage(bitmap); graphics.Clear(SystemColors.Desktop); graphics.DrawImage(image, new Point(0, 0)); graphics.Dispose(); image.Dispose(); image = bitmap; } // ファイル名は元の画像ファイルと同じものを使う // ただこれだと配下のフォルダに同じファイル名の画像があると上書きされて消えてしまうので // 通し番号にしたほうがいいかもしれない。 FileInfo info = new FileInfo(path); string newFilePath = _tempWallPaperFolderPath + "\\" + info.Name; image.Save(newFilePath, System.Drawing.Imaging.ImageFormat.Png); image.Dispose(); _wallPaperFilePath.Add(newFilePath); } catch { continue; } } ChangeWallPaper(); _timer.Interval = (IntervalMinutes * 60 + IntervalSeconds) * 1000; _timer.Start(); } } |
壁紙変更停止と壁紙無効の処理
一定間隔で壁紙を変更する処理を停止する処理を示します。これはタイマーを停止するだけです。
1 2 3 4 5 6 7 |
public partial class Form1 : Form { private void ButtonStopChange_Click(object sender, EventArgs e) { _timer.Stop(); } } |
設定されている壁紙を無効にする処理を示します。WallPaper.Changeメソッドに空文字列を渡せば壁紙は無効になります。
1 2 3 4 5 6 7 |
public partial class Form1 : Form { private void ButtonClearWallPaper_Click(object sender, EventArgs e) { WallPaper.Change(""); } } |
壁紙を変更する間隔を設定する処理
まず別のフォーム(Form2クラスは後述)をダイアログとして表示させてそこで時間を設定します。このダイアログがOKボタンで閉じられたらその設定をIntervalMinutesとIntervalSecondsに反映させます。そしてこれをファイルとして保存します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { Form2 _form2 = new Form2(); private void ButtonSetInterval_Click(object sender, EventArgs e) { _form2.IntervalMinutes = IntervalMinutes; _form2.IntervalSeconds = IntervalSeconds; if (_form2.ShowDialog() == DialogResult.OK) { IntervalMinutes = _form2.IntervalMinutes; IntervalSeconds = _form2.IntervalSeconds; SaveConfig(); } } } |
Form2クラスの定義
Form2をデザイナで以下のように作ります。
numericUpDown2は秒を設定するNumericUpDownコントロールなので設定できる値は0~59です。OKボタンがクリックされたらButtonOK_Clickメソッドが実行されてダイアログが消えるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form2 : Form { public int IntervalMinutes = 0; public int IntervalSeconds = 0; public Form2() { InitializeComponent(); buttonOK.DialogResult = DialogResult.OK; buttonOK.Click += ButtonOK_Click; numericUpDown1.Maximum = 1000; numericUpDown2.Maximum = 59; } } |
ダイアログが表示されたらIntervalMinutesとIntervalSecondsの値をNumericUpDownコントロールに反映させます。
1 2 3 4 5 6 7 8 |
public partial class Form2 : Form { private void Form2_Load(object sender, EventArgs e) { numericUpDown1.Value = IntervalMinutes; numericUpDown2.Value = IntervalSeconds; } } |
OKボタンがクリックされたらNumericUpDown.ValueをIntervalMinutesとIntervalSecondsに代入します。このときに5秒以下の設定がされている場合は5秒になるように調整します。
1 2 3 4 5 6 7 8 9 10 11 |
public partial class Form2 : Form { private void ButtonOK_Click(object sender, EventArgs e) { IntervalMinutes = (int)numericUpDown1.Value; IntervalSeconds = (int)numericUpDown2.Value; if (IntervalMinutes == 0 && IntervalSeconds < 5) IntervalSeconds = 5; } } |