スポンサーサイト
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
新しい記事を書く事で広告が消せます。
C#でAnimateWindowを使ってフォーム表示をアニメーション
C#でフォーム表示時のアニメーションを制御する方法について調べたものの
検索しても希望した代物が出てこなかったのでまとめてみた。
Windowsでフォーム(ウィンドウ)を表示する際や閉じる際にアニメーション表示するためにはAnimateWindowを使うらしい。
こいつの説明がわかりにくくて
1. AnimateWindowで事前にアニメーション効果を設定、画面表示はShowWindowを使う
2. AnimateWindowをShowWindowの替わりに呼び出す
の二通りに取れるが2の解釈が正しかった。
ShowWindowを呼んでる部分でAnimateWindowを実行するとよい。
AnimateWindowをC#で使用する場合の宣言は以下のようになる
注:AW_NONEは元のWin32APIにはないが便宜上追加した。
MSDNの説明を見てもらうとわかるが
AW_HOR_POSITIVE~AW_BLEND の組み合わせには制限がある。
の3種類から1つずつ選ばないといけないようだ。
これをそのまま使うのは不便なので矛盾のない組み合わせだけを指定できるようにenum型を宣言することにした
FormAnimationStyle → AnimationStyle変換表
FormAnimationDirection → AnimationStyle変換表
ShowWindowを呼んでる部分はコードを追った結果Win32イベントのWM_SHOWWINDOWだと判明した。
ここの処理を変更するとShow()/Hide()等を実行したときにフォームがアニメーション表示される。
Show()やHide()にアニメーション動作用引数を追加することはできないので、アニメーション表示設定をプロパティとして持たせることにした。
後の処理での扱いやすさの関係上配列にしている
0がフォームを閉じるとき、1がフォームを開くときのアニメーション設定である
対応するプロパティは以下のようにした
やっと肝心のWM_SHOWWINDOWのフックである。
WndProc に詰め込むと可読性が落ちるので肝心の処理は WMShowWindow というメソッドを宣言してそちらに分離した。
WM_SHOWWINDOW の詳細はMSDNを参照のこと。
LParamの値が0以外の場合はアニメーション処理を行わないことにしている。
なお、WParamの値が0の場合はフォームを閉じる、1の場合はフォームを開く処理が要求されている。
Win32API でいろいろ書くのに慣れているせいで結局 WndProc に頼ってしまった。
本来なら .NET Framework が提供しているイベントで書くべきだと思ったので、
適切なイベントを探したのだがどうやら提供していないようなので仕方あるまい。
追記: WMShowWindowの中でAnimateWindowを呼び出すタイミングを調整した
検索しても希望した代物が出てこなかったのでまとめてみた。
Windowsでフォーム(ウィンドウ)を表示する際や閉じる際にアニメーション表示するためにはAnimateWindowを使うらしい。
こいつの説明がわかりにくくて
1. AnimateWindowで事前にアニメーション効果を設定、画面表示はShowWindowを使う
2. AnimateWindowをShowWindowの替わりに呼び出す
の二通りに取れるが2の解釈が正しかった。
ShowWindowを呼んでる部分でAnimateWindowを実行するとよい。
AnimateWindowの宣言
AnimateWindowをC#で使用する場合の宣言は以下のようになる
[Flags]
enum AnimationStyle {
AW_NONE = 0,
AW_HOR_POSITIVE = 0x00000001,
AW_HOR_NEGATIVE = 0x00000002,
AW_VER_POSITIVE = 0x00000004,
AW_VER_NEGATIVE = 0x00000008,
AW_CENTER = 0x00000010,
AW_HIDE = 0x00010000,
AW_ACTIVATE = 0x00020000,
AW_SLIDE = 0x00040000,
AW_BLEND = 0x00080000
}
[DllImport("user32.dll")]
static extern bool AnimateWindow(IntPtr hWnd, UInt32 dwTimeMSec,
AnimationStyle dwFlags);注:AW_NONEは元のWin32APIにはないが便宜上追加した。
MSDNの説明を見てもらうとわかるが
AW_HOR_POSITIVE~AW_BLEND の組み合わせには制限がある。
| アクション | フォームを開く(AW_ACTIVATE) フォームを閉じる(AW_HIDE) |
|---|---|
| アニメーションの種類 | ロール(無指定) スライド(AW_SLIDE) フェードイン/アウト(AW_BLEND) |
| アニメーションする方向 | 上(AW_VER_NEGATIVE) 下(AW_VER_POSITIVE) 左(AW_HOR_NEGATIVE) 右(AW_HOR_POSITIVE) 上下と左右を組み合わせた斜め方向 中心から広がる(AW_CENTER) |
の3種類から1つずつ選ばないといけないようだ。
これをそのまま使うのは不便なので矛盾のない組み合わせだけを指定できるようにenum型を宣言することにした
アニメーションの種類を指定するenum型 FormAnimationStyle の宣言
public enum FormAnimationStyle {
None, // アニメーションなし
Roll, // ロール
Slide, // スライド
Blend // フェードイン/フェードアウト
}
アニメーションする方向を指定するenum型 FormAnimationDirection の宣言
public enum FormAnimationDirection {
None, // 無視定
Center, // 中央
LeftToRight, // →方向
RightToLeft, // ←方向
TopToBottom, // ↓方向
BottomToTop, // ↑方向
LeftTopToRightBottom, // 右下方向へ
LeftBottomToRightTop, // 右上方向へ
RightTopToLeftBottom, // 左下方向へ
RightBottomToTopLeft // 左上方向へ
}
それぞれのenum値とAnimationStyleの対応表を配列で宣言
FormAnimationStyle → AnimationStyle変換表
static AnimationStyle[] FormAnimationStyleToStyle = {
AnimationStyle.AW_NONE, // None
AnimationStyle.AW_NONE, // Roll
AnimationStyle.AW_SLIDE, // Slide
AnimationStyle.AW_BLEND, // Blend
};
FormAnimationDirection → AnimationStyle変換表
static AnimationStyle[] FormAnimationDirectionToStyle = {
AnimationStyle.AW_NONE, // None
AnimationStyle.AW_CENTER, // Center
AnimationStyle.AW_HOR_POSITIVE, // LeftToRight
AnimationStyle.AW_HOR_NEGATIVE, // RightToLeft
AnimationStyle.AW_VER_POSITIVE, // TopToBottom
AnimationStyle.AW_VER_NEGATIVE, // BottomToTop
AnimationStyle.AW_HOR_POSITIVE | AnimationStyle.AW_VER_POSITIVE, // LeftTopToRightBottom
AnimationStyle.AW_HOR_POSITIVE | AnimationStyle.AW_VER_NEGATIVE, // LeftBottomToRightTop
AnimationStyle.AW_HOR_NEGATIVE | AnimationStyle.AW_VER_POSITIVE, // RightTopToLeftBottom
AnimationStyle.AW_HOR_NEGATIVE | AnimationStyle.AW_VER_NEGATIVE, // RightBottomToTopLeft
};
表示処理部をカスタマイズ
ShowWindowを呼んでる部分はコードを追った結果Win32イベントのWM_SHOWWINDOWだと判明した。
ここの処理を変更するとShow()/Hide()等を実行したときにフォームがアニメーション表示される。
Show()やHide()にアニメーション動作用引数を追加することはできないので、アニメーション表示設定をプロパティとして持たせることにした。
FormAnimationStyle[] _animationStyle = new FormAnimationStyle[2] {
FormAnimationStyle.None, FormAnimationStyle.None
};
FormAnimationDirection[] _animationDirection = new FormAnimationDirection[2] {
FormAnimationDirection.None, FormAnimationDirection.None
};
UInt32[] _animationSpeedMSec = new UInt32[2]{0, 0};
後の処理での扱いやすさの関係上配列にしている
0がフォームを閉じるとき、1がフォームを開くときのアニメーション設定である
対応するプロパティは以下のようにした
/// <summary>
/// 表示時アニメーションの種類
/// </summary>
[DefaultValue(FormAnimationStyle.None)]
public FormAnimationStyle ShowAnimationStyle {
get { return _animationStyle[1]; }
set { _animationStyle[1] = value; }
}
/// <summary>
/// 表示時アニメーションの方向
/// </summary>
[DefaultValue(FormAnimationDirection.None)]
public FormAnimationDirection ShowAnimationDirection {
get { return _animationDirection[1]; }
set { _animationDirection[1] = value; }
}
/// <summary>
/// 表示時アニメーションの速度(単位:ミリ秒)
/// </summary>
[DefaultValue(0)]
public UInt32 ShowAnimationSpeedMSec {
get { return _animationSpeedMSec[1]; }
set { _animationSpeedMSec[1] = value; }
}
/// <summary>
/// 閉じる時のアニメーションの種類
/// </summary>
[DefaultValue(FormAnimationStyle.None)]
public FormAnimationStyle HideAnimationStyle {
get { return _animationStyle[0]; }
set { _animationStyle[0] = value; }
}
/// <summary>
/// 閉じる時のアニメーションの方向
/// </summary>
[DefaultValue(FormAnimationDirection.None)]
public FormAnimationDirection HideAnimationDirection {
get { return _animationDirection[0]; }
set { _animationDirection[0] = value; }
}
/// <summary>
/// 閉じる時のアニメーションの速度(単位:ミリ秒)
/// </summary>
[DefaultValue(0)]
public UInt32 HideAnimationSpeedMSec {
get { return _animationSpeedMSec[0]; }
set { _animationSpeedMSec[0] = value; }
}
WM_SHOWWINDOWのフック
やっと肝心のWM_SHOWWINDOWのフックである。
const int WM_SHOWWINDOW = 0x0018;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_SHOWWINDOW) {
WMShowWindow(ref m);
} else {
base.WndProc(ref m);
}
}
WndProc に詰め込むと可読性が落ちるので肝心の処理は WMShowWindow というメソッドを宣言してそちらに分離した。
WM_SHOWWINDOW の詳細はMSDNを参照のこと。
LParamの値が0以外の場合はアニメーション処理を行わないことにしている。
なお、WParamの値が0の場合はフォームを閉じる、1の場合はフォームを開く処理が要求されている。
/// <summary>
/// Windowのアニメーション表示を行う
/// </summary>
void WMShowWindow(ref Message m) {
base.WndProc(ref m);
if (!DesignMode && m.LParam.ToInt32() == 0) {
TryAnimateWindow(m.WParam.ToInt32() != 0);
}
return;
}
/// <summary>
/// アニメーション処理本体。
/// valueがtrueの場合はフォームを開き、falseの場合はフォームを閉じる
/// アニメーション処理を行った場合はtrueを返す
/// </summary>
bool TryAnimateWindow(bool value) {
if (AnimationEnabled(value)) {
int v = value ? 1 : 0;
AnimationStyle flags = (value ? AnimationStyle.AW_ACTIVATE : AnimationStyle.AW_HIDE)
| FormAnimationStyleToStyle[(int)_animationStyle[v]]
| FormAnimationDirectionToStyle[(int)_animationDirection[v]];
return AnimateWindow(Handle, _animationSpeedMSec[v], flags);
}
return false;
}
/// <summary>
/// アニメーション処理をする必要があるかどうかを判定
/// </summary>
bool AnimationEnabled(bool value) {
int v = value ? 1 : 0;
return (_animationStyle[v] != FormAnimationStyle.None)
&& (_animationDirection[v] != FormAnimationDirection.None);
}
以上をまとめた結果が以下のAnimationFormクラスである
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Project1 {
/// <summary>
/// アニメーションの種類
/// </summary>
public enum FormAnimationStyle {
/// <summary>
/// アニメーションなし
/// </summary>
None,
/// <summary>
/// ロール
/// </summary>
Roll,
/// <summary>
/// スライド
/// </summary>
Slide,
/// <summary>
/// フェードイン/フェードアウト
/// </summary>
Blend
}
/// <summary>
/// アニメーションする方向
/// </summary>
public enum FormAnimationDirection {
/// <summary>
/// 無指定
/// </summary>
None,
/// <summary>
/// 中央
/// </summary>
Center,
/// <summary>
/// →方向
/// </summary>
LeftToRight,
/// <summary>
/// ←方向
/// </summary>
RightToLeft,
/// <summary>
/// ↓方向
/// </summary>
TopToBottom,
/// <summary>
/// ↑方向
/// </summary>
BottomToTop,
/// <summary>
/// 右下方向へ
/// </summary>
LeftTopToRightBottom,
/// <summary>
/// 右上方向へ
/// </summary>
LeftBottomToRightTop,
/// <summary>
/// 左下方向へ
/// </summary>
RightTopToLeftBottom,
/// <summary>
/// 左上方向へ
/// </summary>
RightBottomToTopLeft
}
/// <summary>
/// フォームを開いたとき/閉じたときのアニメーションを指定できるクラス
/// </summary>
public class AnimationForm: Form {
#region 変数宣言
private FormAnimationStyle[] _animationStyle = new FormAnimationStyle[2]{
FormAnimationStyle.None, FormAnimationStyle.None
};
private FormAnimationDirection[] _animationDirection = new FormAnimationDirection[2]{
FormAnimationDirection.None, FormAnimationDirection.None
};
private UInt32[] _animationSpeedMSec = new UInt32[2]{0, 0};
/// <summary>
/// 表示時アニメーションの種類
/// </summary>
[DefaultValue(FormAnimationStyle.None)]
public FormAnimationStyle ShowAnimationStyle {
get {
return _animationStyle[1];
}
set {
_animationStyle[1] = value;
}
}
/// <summary>
/// 表示時アニメーションの方向
/// </summary>
[DefaultValue(FormAnimationDirection.None)]
public FormAnimationDirection ShowAnimationDirection {
get {
return _animationDirection[1];
}
set {
_animationDirection[1] = value;
}
}
/// <summary>
/// 表示時アニメーションの速度(単位:ミリ秒)
/// </summary>
[DefaultValue(0)]
public UInt32 ShowAnimationSpeedMSec {
get {
return _animationSpeedMSec[1];
}
set {
_animationSpeedMSec[1] = value;
}
}
/// <summary>
/// 閉じる時のアニメーションの種類
/// </summary>
[DefaultValue(FormAnimationStyle.None)]
public FormAnimationStyle HideAnimationStyle {
get {
return _animationStyle[0];
}
set {
_animationStyle[0] = value;
}
}
/// <summary>
/// 閉じる時のアニメーションの方向
/// </summary>
[DefaultValue(FormAnimationDirection.None)]
public FormAnimationDirection HideAnimationDirection {
get {
return _animationDirection[0];
}
set {
_animationDirection[0] = value;
}
}
/// <summary>
/// 閉じる時のアニメーションの速度(単位:ミリ秒)
/// </summary>
[DefaultValue(0)]
public UInt32 HideAnimationSpeedMSec {
get {
return _animationSpeedMSec[0];
}
set {
_animationSpeedMSec[0] = value;
}
}
#endregion
#region Windowメッセージに依存する処理
[Flags]
private enum AnimationStyle {
AW_NONE = 0,
AW_HOR_POSITIVE = 0x00000001,
AW_HOR_NEGATIVE = 0x00000002,
AW_VER_POSITIVE = 0x00000004,
AW_VER_NEGATIVE = 0x00000008,
AW_CENTER = 0x00000010,
AW_HIDE = 0x00010000,
AW_ACTIVATE = 0x00020000,
AW_SLIDE = 0x00040000,
AW_BLEND = 0x00080000
}
[DllImport("user32.dll")]
private static extern bool AnimateWindow(IntPtr hWnd, UInt32 dwTimeMSec, AnimationStyle dwFlags);
private const int WM_SHOWWINDOW = 0x0018;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_SHOWWINDOW) {
WMShowWindow(ref m);
} else {
base.WndProc(ref m);
}
}
#endregion
#region AnimateWindowを使ったフォームの表示/非表示時のアニメーション処理
private static AnimationStyle[] FormAnimationStyleToStyle = {
AnimationStyle.AW_NONE, // None
AnimationStyle.AW_NONE, // Roll
AnimationStyle.AW_SLIDE, // Slide
AnimationStyle.AW_BLEND, // Blend
};
private static AnimationStyle[] FormAnimationDirectionToStyle = {
AnimationStyle.AW_NONE, // None
AnimationStyle.AW_CENTER, // Center
AnimationStyle.AW_HOR_POSITIVE, // LeftToRight
AnimationStyle.AW_HOR_NEGATIVE, // RightToLeft
AnimationStyle.AW_VER_POSITIVE, // TopToBottom
AnimationStyle.AW_VER_NEGATIVE, // BottomToTop
AnimationStyle.AW_HOR_POSITIVE | AnimationStyle.AW_VER_POSITIVE, // LeftTopToRightBottom
AnimationStyle.AW_HOR_POSITIVE | AnimationStyle.AW_VER_NEGATIVE, // LeftBottomToRightTop
AnimationStyle.AW_HOR_NEGATIVE | AnimationStyle.AW_VER_POSITIVE, // RightTopToLeftBottom
AnimationStyle.AW_HOR_NEGATIVE | AnimationStyle.AW_VER_NEGATIVE, // RightBottomToTopLeft
};
private bool AnimationEnabled(bool value) {
int v = value ? 1 : 0;
return (_animationStyle[v] != FormAnimationStyle.None)
&& (_animationDirection[v] != FormAnimationDirection.None);
}
private bool TryAnimateWindow(bool value) {
if (AnimationEnabled(value)) {
int v = value ? 1 : 0;
AnimationStyle flags = (value ? AnimationStyle.AW_ACTIVATE : AnimationStyle.AW_HIDE)
| FormAnimationStyleToStyle[(int)_animationStyle[v]]
| FormAnimationDirectionToStyle[(int)_animationDirection[v]];
return AnimateWindow(Handle, _animationSpeedMSec[v], flags);
}
return false;
}
private void WMShowWindow(ref Message m) {
base.WndProc(ref m);
if (!DesignMode && m.LParam.ToInt32() == 0) {
TryAnimateWindow(m.WParam.ToInt32() != 0);
}
return;
}
#endregion
}
}
まとめ
Win32API でいろいろ書くのに慣れているせいで結局 WndProc に頼ってしまった。
本来なら .NET Framework が提供しているイベントで書くべきだと思ったので、
適切なイベントを探したのだがどうやら提供していないようなので仕方あるまい。
追記: WMShowWindowの中でAnimateWindowを呼び出すタイミングを調整した
スポンサーサイト
コメント
コメントの投稿