2012
09
24
09
24
WPF: ZuneライクなGUIのアプリケーションを作る
To Make a Zune-like Window
Windows7を使い始めたころ、ウィンドウのフレームに透過処理ができてそれはまあきれいでかっこいいものだと思った。だけど少し時間が経つと、そのためにCPUリソースを消費するのもなんだかなと考えて切るようになった。
最近、ZuneやSteamのクライアントを使ってみて、これはかっこいいと思った。これらにはウィンドウのフレームがないのだ。

WPFにはこのウィンドウを再現できるテーマがデフォルトでは入っていないようで、DLLを落としてなおかつXAMLを少しいじる必要があった。そのメモ。
さきほどのウィンドウを作るのに一番簡単な手立ては、WPFでウィンドウの宣言時にフレームをなしにしてしまうことだ。これならば追加のDLLはいらない。ただ、そうするとこのフレームなしウィンドウはほかのデスクトップ要素に影を落とさなくなってしまう。だからその方法は却下。
ウィンドウの外周を透明にして影を描画し、その中にGridを配置してそれをウィンドウとして扱うやり方も見つけた。だがこれは常に描画にCPUを消費せねばならなくなるので、この方法も却下。
ではどうするかというと、Microsoft.Windows.Shell.dllを落として使えばいい。このツールは個人のものではなくMicrosoft製のようだ。DLLの配布場所と使い方の参考になるものを合わせてリンクしておく。
http://archive.msdn.microsoft.com/WPFShell
http://stackoverflow.com/questions/7369115/maximum-custom-window-loses-drop-shadow-effect
リンク先の真似をすれば、フレームなしの影付きウィンドウが作れる。あともう一歩、ウィンドウをドラッグするためのタイトルバーと、クローズや最大化に使うボタン群をつけたい。以下を参考にした。
http://msdn.microsoft.com/ja-jp/library/microsoft.windows.shell(v=vs.100)
http://www.kanazawa-net.ne.jp/~pmansato/wpf/wpf_ctrl_windowchrome.htm
これまでのステップを踏むと、以下のようなウィンドウが作れる。IronPythonから使う場合は、スクリプトにDLLへの参照を記すること。
clr.AddReference('Microsoft.Windows.Shell')
ボタンに使う×記号などは画像を用意しなくても、Marlettというフォントを使えば表現できる。指でタップすることを考え、既存のアプリと比較してボタンの間隔を開けてある。参考XAMLを記事の末尾に付けた。

質問をいただいたので追記
> 最大化ボタンを押すと、そのあとずっとボタンが点滅しっぱなしなのです。
ButtonコントロールにデフォルトでStyleが適用されていて、
おそらくフォーカス絡みのイベントでアニメーションが働いている。
一つの解としては自分でStyleを定義して、Buttonにそれを適用。
上記をWindow.Resourcesの入れ子にしておいて、下記のようにButtonを定義する。
Windows7を使い始めたころ、ウィンドウのフレームに透過処理ができてそれはまあきれいでかっこいいものだと思った。だけど少し時間が経つと、そのためにCPUリソースを消費するのもなんだかなと考えて切るようになった。
最近、ZuneやSteamのクライアントを使ってみて、これはかっこいいと思った。これらにはウィンドウのフレームがないのだ。

WPFにはこのウィンドウを再現できるテーマがデフォルトでは入っていないようで、DLLを落としてなおかつXAMLを少しいじる必要があった。そのメモ。
さきほどのウィンドウを作るのに一番簡単な手立ては、WPFでウィンドウの宣言時にフレームをなしにしてしまうことだ。これならば追加のDLLはいらない。ただ、そうするとこのフレームなしウィンドウはほかのデスクトップ要素に影を落とさなくなってしまう。だからその方法は却下。
ウィンドウの外周を透明にして影を描画し、その中にGridを配置してそれをウィンドウとして扱うやり方も見つけた。だがこれは常に描画にCPUを消費せねばならなくなるので、この方法も却下。
ではどうするかというと、Microsoft.Windows.Shell.dllを落として使えばいい。このツールは個人のものではなくMicrosoft製のようだ。DLLの配布場所と使い方の参考になるものを合わせてリンクしておく。
http://archive.msdn.microsoft.com/WPFShell
http://stackoverflow.com/questions/7369115/maximum-custom-window-loses-drop-shadow-effect
リンク先の真似をすれば、フレームなしの影付きウィンドウが作れる。あともう一歩、ウィンドウをドラッグするためのタイトルバーと、クローズや最大化に使うボタン群をつけたい。以下を参考にした。
http://msdn.microsoft.com/ja-jp/library/microsoft.windows.shell(v=vs.100)
http://www.kanazawa-net.ne.jp/~pmansato/wpf/wpf_ctrl_windowchrome.htm
これまでのステップを踏むと、以下のようなウィンドウが作れる。IronPythonから使う場合は、スクリプトにDLLへの参照を記すること。
clr.AddReference('Microsoft.Windows.Shell')
ボタンに使う×記号などは画像を用意しなくても、Marlettというフォントを使えば表現できる。指でタップすることを考え、既存のアプリと比較してボタンの間隔を開けてある。参考XAMLを記事の末尾に付けた。

<?xml version="1.0" encoding="utf-8"?>
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title=""
FontFamily="Meiryo UI"
Width="800"
MinWidth="350"
Height="700"
Name="window"
WindowStartupLocation="CenterScreen"
WindowStyle="SingleBorderWindow"
ResizeMode="CanResizeWithGrip" >
<Window.CommandBindings>
<CommandBinding Command="{x:Static shell:SystemCommands.CloseWindowCommand}"
Executed="close_window"/>
<CommandBinding Command="{x:Static shell:SystemCommands.MaximizeWindowCommand}"
Executed="state_change"/>
<CommandBinding Command="{x:Static shell:SystemCommands.MinimizeWindowCommand}"
Executed="minimize_window"/>
</Window.CommandBindings>
<Window.Style>
<Style TargetType="Window">
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome GlassFrameThickness="1" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<Grid>
<Border BorderThickness="0.8" BorderBrush="Aqua">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,50" MappingMode="Absolute">
<GradientStop Offset="0" Color="DimGray"/>
<GradientStop Offset="1" Color="Black"/>
</LinearGradientBrush>
</Border.Background>
<ContentPresenter Margin="0" Content="{TemplateBinding Content}"/>
</Border>
<!-- Title プロパティを表示する -->
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Margin="34,8,0,0" Foreground="White"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}" />
<!-- 最小化、最大化、閉じるボタン -->
<StackPanel Orientation="Horizontal" Margin="0,6,6,0" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button shell:WindowChrome.IsHitTestVisibleInChrome="True"
Height="24" Margin="30,0,0,0" Width="28"
Background="Black"
BorderThickness="0"
Command="{x:Static shell:SystemCommands.MinimizeWindowCommand}"
CommandParameter="{Binding ElementName=window}">
<TextBlock Foreground="White">_</TextBlock>
</Button>
<Button shell:WindowChrome.IsHitTestVisibleInChrome="True"
Height="24" Margin="30,0,0,0" Width="28"
Background="Black"
Command="{x:Static shell:SystemCommands.MaximizeWindowCommand}"
CommandParameter="{Binding ElementName=window}">
<TextBlock Foreground="White" FontFamily="Marlett">1</TextBlock>
</Button>
<Button shell:WindowChrome.IsHitTestVisibleInChrome="True"
Height="24" Margin="30,0,20,0" Width="28"
Background="Black"
Command="{x:Static shell:SystemCommands.CloseWindowCommand}"
CommandParameter="{Binding ElementName=window}">
<TextBlock Foreground="White" FontFamily="Marlett">r</TextBlock>
</Button>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Style>
<StackPanel></StackPanel>
</Window>
質問をいただいたので追記
> 最大化ボタンを押すと、そのあとずっとボタンが点滅しっぱなしなのです。
ButtonコントロールにデフォルトでStyleが適用されていて、
おそらくフォーカス絡みのイベントでアニメーションが働いている。
一つの解としては自分でStyleを定義して、Buttonにそれを適用。
<Style
x:Key="ButtonStyle1"
TargetType="{x:Type Button}" >
<Setter
Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter
Property="VerticalContentAlignment"
Value="Center" />
<Setter
Property="Foreground"
Value="LightGray" />
<Setter
Property="TextBlock.TextAlignment"
Value="Center" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type Button}">
<Border
Background="AliceBlue"
BorderBrush="Gray"
BorderThickness="1"
Margin="0,0,0,0">
<Border.Triggers>
<EventTrigger
RoutedEvent="Border.MouseEnter">
<BeginStoryboard>
<Storyboard
TargetProperty="Background.Color">
<ColorAnimation
To="Azure"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger
RoutedEvent="Border.MouseLeave">
<BeginStoryboard>
<Storyboard
TargetProperty="Background.Color">
<ColorAnimation
To="AliceBlue"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
上記をWindow.Resourcesの入れ子にしておいて、下記のようにButtonを定義する。
<Button
Style="{StaticResource ButtonStyle1}"
Height="24"
Width="28" >
<TextBlock
Foreground="Black"
FontFamily="Marlett" >r</TextBlock>
</Button>
スポンサーサイト