メインフォームを作る : RSSリーダを.NET Framework 2.0で作る (第11回)
第9回 プロジェクトを作る で作成したプロジェクトを元にメインフォームを作成します。
メインフォームは、常に最前面に表示される小さいステータス通知用のウィンドウでMicroRssReaderアプリケーションの生存期間中 常時表示されます。
タイトルバーもフレーム(枠)も消したウィンドウ(フォーム)です。
今回のテクニカル トピック
- フォームの最前面表示
- タイトル無しウィンドウ
- 枠無しウィンドウ
- タイトル無しウィンドウをマウスで移動する
- Settingsによる表示位置の復元
- 画面外に出てしまった場合の自動復元
メインフォームのクラス名の変更
クラス名の変更方法の紹介として、自動生成された "Form1.cs" を利用してメインフォームを作成します。
Form1というクラス名をIconFormに変更 する方法として、ソリューション エクスプローラでファイル名を変更する方法を紹介します。 Visual Studio 2005 では、ファイル名の変更操作をクラス名の変更として処理することが出来ます。
方法:ソリューション エクスプローラで "Form1.cs" のファイル名を変更すると次のメッセージが表示されます。

つまり、 Form1 を参照しているコードを IconForm に変更するかどうかの質問なので[はい]を選択します。
(メッセージおかしいですけどね)
メインフォームのコードを見ると、Form1 だった部分が IconForm に変わっています。
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new IconForm());
}
ファイル名と含まれるクラス名が異なる場合はこの処理は行われません。
メインフォームのプロパティ変更
常に最前面の小さいアイコンのみ表示されるウィンドウにするため次のように変更します。
| Text | MicroRssReader |
| FormBorderStyle | None |
| StartPosition | Manual |
| Size | 16,16 |
| AutoValidate | Disable |
| TopMost | True |
| ShowInTaskbar | False |
| Opacity | 0% |
| ControlBox | False |
| BackgroundImageLayout | Stretch |
| BackgroundImage |
また、フォームのサイズを123x34より小さくするため次のようにOnLoadをオーバーライドする。 詳しくは第3回を見てください。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
Size = new Size(16,16);
base.OnLoad(e);
this.Opacity = 0.8d;
}
}
ウィンドウ位置を次回起動時復元する
ウィンドウの位置を記憶するアプリケーションは、それだけで使いやすいものです。
記憶方法はいろいろありますが、Visual Studio 2005で追加されたSettingsを使用します。
フォームのLocationプロパティにバインドする には、フォームのプロパティを表示して "(ApplicationSettings)" を開きます。 "Location" をドロップダウンして<新規>リンクをクリックします。DefaultValueに 200,4 、Nameに IconFormLocation を設定して[OK]ボタンを押します。
Settingsを確認する には、ソリューション エクスプローラでプロジェクト下の "Properties" を開き、"Settings.settings" をダブルクリックします。 表示されたリソース デザイナで次のように1行追加されています。
| 名前 | IconFormLocation |
| 型 | System.Drawing.Point |
| スコープ | ユーザ |
| 値 | 200,4 |
2006.8.7 訂正:プロパティとバインドした場合随時更新されているようで、次の処理は不要でした。
あとは、移動終了後にSettingsを更新します。 OnClosingで行う手もありますが Application.Exit の場合呼び出されないため、OnMove をオーバーライドして座標を更新します。protected override void OnMove(EventArgs e)
{
base.OnMove(e);
Properties.Settings.Default.IconFormLocation = Location;
}
タイトル無しウィンドウをマウスで移動する
ここはやはりコードを書く必要があります。 タイトルバーの無いフォームを移動と後で使うリサイズを処理をするように再利用したいのでフォームクラスとは分離します。ソースがごちゃ混ぜになると嫌なので、プロジェクトの下にディレクトリ"UiController"を追加します。(プロジェクトを選択して<追加>→<新しいフォルダ>を実行して名前を"UiController"にします)
ソースを追加します。"UiController" を選択して、<追加>→<クラス>を実行します。クラス名は"MoveResizeController.cs" にしましょう。
MoveResizeControllerクラスの概要
- コンストラクタに指定されたフォームのマウスによる移動・リサイズを行います。
- タイトルバーに当たる位置で移動を、枠の位置でリサイズを行います。
- 斜め方向と上方向のリサイズ処理は省略しています。
- タイトルバーおよび枠の幅と高さはコンストラクタに指定されたフォームのPadding値で決定します。
- フォームのPadding値が0の場合、全域を移動対象として移動のみサポートします。この判断はコンストラクタ内で行われ変更されません。
フォームのMouseUp、MouseMove、MouseDownイベントを受けて処理を行うだけです。
コードは、記事末を参照してください。
IconFormクラスに組み込む には、OnLoad メソッドに MoveResizeController のインスタンス生成を追加します。
protected override void OnLoad(EventArgs e)
{
Size = new Size(16, 16);
base.OnLoad(e);
this.Opacity = 0.8d;
if (DesignMode)
return;
new MoveResizeController(this);
}
「画面外に出てしまった場合の自動復元」は次回。
コードサンプル
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace MicroRssReader.UiController
{
/// <summary>
/// タイトルバー・フレームの無いフォームに対して「移動」「リサイズ」
/// 機能を提供する。
/// </summary>
class MoveResizeController
{
/// <summary>
/// 処理対象のフォーム
/// </summary>
readonly Form _target;
readonly bool _isMoveOnly;
/// <summary>
/// 現在アクティブなアクション
/// </summary>
IResizeActionController _resizeAction;
private const int _resizeCornerSize = 20;
enum ResizeMode
{
None,
MoveForm,
ResizeWidth,
ResizeHeight,
RisizeSize,
}
enum ResizePosition
{
None,
Left,
Right,
Bottom,
LeftBottom,
RightBottom,
}
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="targetForm">対象のフォーム</param>
public MoveResizeController(Form targetForm)
{
if (targetForm == null)
throw new ArgumentNullException("targetForm");
_target = targetForm;
_isMoveOnly = (_target.Padding.All == 0);
_target.MouseDown += new MouseEventHandler(_target_MouseDown);
_target.MouseMove += new MouseEventHandler(_target_MouseMove);
}
private int TitleHeight
{
get
{
if (_isMoveOnly)
{
return _target.Height;
}
return _target.Padding.Top;
}
}
/// <summary>
/// マウス座標から、何のアクションをするべきか決めるための情報
/// を取得する
/// </summary>
/// <param name="mousePos">マウス座標</param>
/// <param name="resizeMode">実行すべきアクション</param>
/// <param name="resizePosition">イベント対象の位置</param>
private void CalcResizeMode(Point mousePos,
out ResizeMode resizeMode,
out ResizePosition resizePosition)
{
if (_isMoveOnly || _target.Padding.Top > mousePos.Y)
{
resizeMode = ResizeMode.MoveForm;
resizePosition = ResizePosition.None;
return;
}
// 下かどうか
if (_target.Height - _target.Padding.Bottom < mousePos.Y)
{
resizeMode = ResizeMode.ResizeHeight;
resizePosition = ResizePosition.Bottom;
return;
}
// 右かどうか
if (_target.Width - _target.Padding.Right < mousePos.X)
{
resizeMode = ResizeMode.ResizeWidth;
resizePosition = ResizePosition.Right;
return;
}
// 左かどうか
if (_target.Padding.Left > mousePos.X)
{
resizeMode = ResizeMode.ResizeWidth;
resizePosition = ResizePosition.Left;
return;
}
resizeMode = ResizeMode.None;
resizePosition = ResizePosition.None;
}
/// <summary>
/// リサイズ処理実行中かどうか
/// </summary>
public bool IsContinueResizeAction
{
get
{
return (_resizeAction != null);
}
}
#region マウスイベント処理
void _target_MouseMove(object sender, MouseEventArgs e)
{
if (IsContinueResizeAction)
return;
// マウス座標からカーソル形状を決定する
ResizeMode resizeMode;
ResizePosition resizePosition;
CalcResizeMode(new Point(e.X, e.Y), out resizeMode,
out resizePosition);
switch (resizeMode)
{
case ResizeMode.MoveForm:
_target.Cursor = Cursors.SizeAll;
break;
case ResizeMode.ResizeWidth:
_target.Cursor = Cursors.SizeWE;
break;
case ResizeMode.ResizeHeight:
_target.Cursor = Cursors.SizeNS;
break;
case ResizeMode.RisizeSize:
if (resizePosition == ResizePosition.RightBottom)
_target.Cursor = Cursors.SizeNWSE;
else
_target.Cursor = Cursors.SizeNESW;
break;
default:
_target.Cursor = Cursors.Default;
break;
}
}
void _target_MouseDown(object sender, MouseEventArgs e)
{
if (IsContinueResizeAction)
return;
// マウス座標から何をするか決める
ResizeMode resizeMode;
ResizePosition resizePosition;
Point downPosition = new Point(e.X, e.Y);
CalcResizeMode(downPosition, out resizeMode,
out resizePosition);
switch (resizeMode)
{
case ResizeMode.MoveForm:
_resizeAction = new MoveAction(_target, downPosition);
break;
case ResizeMode.ResizeWidth:
_resizeAction = new ResizeWidthAction(_target,
resizePosition, downPosition);
break;
case ResizeMode.ResizeHeight:
_resizeAction = new ResizeHeightAction(_target,
resizePosition, downPosition);
break;
default:
_target.Cursor = Cursors.Default;
break;
}
// アクションが何も無ければ、何もしない
if (_resizeAction == null)
return;
// 処理完了時アクション参照を null にして完了状態にする
_resizeAction.Accomplished += delegate
{
_resizeAction = null;
};
}
#endregion
/// <summary>
/// リサイズの処理を行うコントローラが実装するインターフェース
/// </summary>
private interface IResizeActionController
{
/// <summary>
/// 処理が完了したときに発生するイベント。
/// </summary>
event EventHandler Accomplished;
}
#region 移動処理
/// <summary>
/// マウスによるフォームの移動用アクションコントローラ
/// </summary>
private class MoveAction : IResizeActionController
{
/// <summary>
/// 移動対象のフォーム
/// </summary>
Form _fm;
/// <summary>
/// マウス位置からフォームのLocationプロパティを変更するとき
/// に使用する補正値
/// </summary>
Point _offset;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="fm">移動対象のフォーム</param>
/// <param name="offset">マウス位置からフォームのLocation
/// プロパティを変更するときに使用する補正値</param>
public MoveAction(Form fm, Point offset)
{
_fm = fm;
_offset = offset;
_fm.Capture = true;
_fm.MouseMove += new MouseEventHandler(_fm_MouseMove);
_fm.MouseUp += new MouseEventHandler(_fm_MouseUp);
}
/// <summary>
/// マウスUPのイベントハンドラ<br/>
/// 移動を完了する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void _fm_MouseUp(object sender, MouseEventArgs e)
{
_fm.Capture = false;
_fm.MouseMove -= new MouseEventHandler(_fm_MouseMove);
_fm.MouseUp -= new MouseEventHandler(_fm_MouseUp);
if (Accomplished == null)
return;
Accomplished(this, EventArgs.Empty);
}
/// <summary>
/// マウス移動のイベントハンドラ<br/>
/// リアルタイムにフォームを移動する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void _fm_MouseMove(object sender, MouseEventArgs e)
{
_fm.Location = _fm.PointToScreen(
new Point(e.X - _offset.X, e.Y - _offset.Y));
}
#region IResizeActionController メンバ
/// <summary>
/// 処理完了イベント
/// </summary>
public event EventHandler Accomplished;
#endregion
}
#endregion
#region 幅リサイズ処理
/// <summary>
/// マウスによるフォームの幅変更アクションコントローラ
/// </summary>
private class ResizeWidthAction : IResizeActionController
{
/// <summary>
/// 移動対象のフォーム
/// </summary>
Form _fm;
/// <summary>
/// マウス位置からフォームのLocationプロパティを変更する
/// ときに使用する補正値
/// </summary>
Point _offset;
bool _isLeftResize = false;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="fm">移動対象のフォーム</param>
/// <param name="posMode"></param>
/// <param name="offset">マウス位置からフォームのLocation
/// プロパティを変更するときに使用する補正値</param>
public ResizeWidthAction(Form fm, ResizePosition posMode,
Point offset)
{
_fm = fm;
_offset = offset;
_fm.Capture = true;
_fm.MouseMove += new MouseEventHandler(_fm_MouseMove);
_fm.MouseUp += new MouseEventHandler(_fm_MouseUp);
if (posMode == ResizePosition.Left)
_isLeftResize = true;
else
_offset.X = fm.Width - offset.X;
}
/// <summary>
/// マウスUPのイベントハンドラ<br/>
/// 移動を完了する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void _fm_MouseUp(object sender, MouseEventArgs e)
{
_fm.Capture = false;
_fm.MouseMove -= new MouseEventHandler(_fm_MouseMove);
_fm.MouseUp -= new MouseEventHandler(_fm_MouseUp);
if (Accomplished == null)
return;
Accomplished(this, EventArgs.Empty);
}
/// <summary>
/// マウス移動のイベントハンドラ<br/>
/// リアルタイムにフォームを移動する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void _fm_MouseMove(object sender, MouseEventArgs e)
{
if (_isLeftResize)
{
Rectangle oldRect = _fm.Bounds;
Point newLoc = _fm.PointToScreen(
new Point(e.X - _offset.X, 0));
Rectangle newRect = new Rectangle(
newLoc,
new Size(oldRect.Right - newLoc.X, oldRect.Height));
if (newRect.Width < _fm.MinimumSize.Width)
return;
_fm.Bounds = newRect;
}
else
{
Rectangle oldRect = _fm.Bounds;
Point screenMousePos = _fm.PointToScreen(
new Point(e.X - _offset.X, 0));
Rectangle newRect = new Rectangle(
oldRect.Location,
new Size(screenMousePos.X - oldRect.Left,
oldRect.Height));
if (newRect.Width < _fm.MinimumSize.Width)
return;
_fm.Bounds = newRect;
}
}
#region IResizeActionController メンバ
/// <summary>
/// 処理完了イベント
/// </summary>
public event EventHandler Accomplished;
#endregion
}
#endregion
#region 高さリサイズ処理
/// <summary>
/// マウスによるフォームの高さ変更用アクションコントローラ
/// </summary>
private class ResizeHeightAction : IResizeActionController
{
/// <summary>
/// 移動対象のフォーム
/// </summary>
Form _fm;
/// <summary>
/// マウス位置からフォームのLocationプロパティを変更するとき
/// に使用する補正値
/// </summary>
Point _offset;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="fm">移動対象のフォーム</param>
/// <param name="posMode"></param>
/// <param name="offset">マウス位置からフォームのLocation
/// プロパティを変更するときに使用する補正値</param>
public ResizeHeightAction(Form fm, ResizePosition posMode,
Point offset)
{
_fm = fm;
_offset = offset;
_fm.Capture = true;
_fm.MouseMove += new MouseEventHandler(_fm_MouseMove);
_fm.MouseUp += new MouseEventHandler(_fm_MouseUp);
_offset.Y = fm.Height - offset.Y;
}
/// <summary>
/// マウスUPのイベントハンドラ<br/>
/// 移動を完了する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void _fm_MouseUp(object sender, MouseEventArgs e)
{
_fm.Capture = false;
_fm.MouseMove -= new MouseEventHandler(_fm_MouseMove);
_fm.MouseUp -= new MouseEventHandler(_fm_MouseUp);
if (Accomplished == null)
return;
Accomplished(this, EventArgs.Empty);
}
/// <summary>
/// マウス移動のイベントハンドラ<br/>
/// リアルタイムにフォームを移動する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void _fm_MouseMove(object sender, MouseEventArgs e)
{
Rectangle oldRect = _fm.Bounds;
Point screenMousePos = _fm.PointToScreen(
new Point(0, e.Y - _offset.Y));
Rectangle newRect = new Rectangle(
oldRect.Location,
new Size(oldRect.Width, screenMousePos.Y - oldRect.Top));
if (newRect.Height < _fm.MinimumSize.Height)
return;
_fm.Bounds = newRect;
}
#region IResizeActionController メンバ
/// <summary>
/// 処理完了イベント
/// </summary>
public event EventHandler Accomplished;
#endregion
}
#endregion
}
}

Comments