WPF:カスタムウィンドウ

先日、長い休憩の後、WPFで作業する必要があり、Windows 7の迷惑な標準ビューを、Visual Studio 2012のスタイルなど、より刺激的なものに置き換えたいという要望がありました。







メトロのようなライブラリへのリンクをプロジェクトに追加してそれらを処理するのと同じように、これのためにWindows 8に切り替えたくありませんでした。これが次のステップになります。 その間、夕方を過ごして、作業コードへの最小限の変更でそのような結果を達成することは興味深いものでした。 将来的には、計画どおり、結果はかなりきれいであることがわかりました。明確にするためにいくつかの属性が欠けていることを除いて、次のコードのフラグメントは、最初のスクリーンショットのウィンドウです。 すべての変更はスタイルのタスクに限定されていました。



12月3日更新:.Net 4.5(WindowChrome.Demoプロジェクト)の新しいクラスを使用した代替実装がリポジトリに追加され、WinAPIによるネイティブプログラミングの大部分が回避されました。


<Window ... Style="{StaticResource VS2012WindowStyle}"> <DockPanel> <StatusBar> <TextBlock>Ready</TextBlock> <StatusBarItem HorizontalAlignment="Right"> <ResizeGrip /> </StatusBarItem> </StatusBar> <TextBox Text="Hello, world!" /> </DockPanel> </Window>
      
      







次に、ウィンドウスタイルを作成する際の重要なポイントと落とし穴に焦点を当てます。 ソースを自分で処理したい場合や、詳細に触れずにこのスタイルを使用したい場合は、 githubでデモプロジェクトを利用できます。



主な問題



WPFはNCエリアでは機能しません。 NC、「非クライアント領域」、「非クライアント部分」、クローム、下位レベルで処理されます。 ウィンドウ要素の1つ(境界線、アイコン、タイトル、またはボタン)を変更する場合、検索時に最初出てくるヒントは、ウィンドウスタイルを削除して、すべてを自分で作り直すことです。 全体。



 <Window AllowsTransparency="true" WindowStyle="None"> ...
      
      





WPFの開発の全歴史において、この点に関してほとんど変更はありません。 幸いなことに、Alex YakhninのOffice 2007のスタイリングに関する古い投稿からソースコードを入手しました。MicrosoftでWPFを普及させるデモプロジェクトに取り組んでいるときに書いたので、ゼロから始めることを恐れませんでした。



その結果、1つのスタイルを取得する必要があり、可能であれば、追加のコントロールなしで:XAMLプロジェクトツリーとスタイルコードはCustomizedWindowディレクトリにあり、メインウィンドウはプロジェクトルートにあります。



プロジェクトに新しいライブラリを追加することは避けたかったのですが、そのような構造を決定した別のアプリケーションにスタイルを簡単に転送できるようにしたかったのです。



スタイルを作成する



ウィンドウのスタイルは、WPFの他のコントロールと同様に、ControlTemplateを使用して設定されます。 ウィンドウのコンテンツはContentPresenterによって表示され、c#コードでより簡単に実行できる機能は、ResourceDictionaryのx:Class属性を介して接続されます。 すべてがXAMLの標準です。



 <ResourceDictionary x:Class="Whush.Demo.Styles.CustomizedWindow.VS2012WindowStyle"> <Style x:Key="VS2012WindowStyle" TargetType="{x:Type Window}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Window}"> <!-- XAML     ,    --> <ContentPresenter /> <!--  XAML   --> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
      
      





Studio 2012のスタイルでウィンドウコントロールボタンをすぐに定義します。これは、アプリケーションでこのようなボタンを後で使用する場合にのみ追加のグローバルスタイルになります。







通常のボタンの機能が必要ですが、非常に原始的なレンダリングが必要です。実際には、背景とコンテンツのみです。

Xamlスタイルのボタン
 <Style x:Key="VS2012WindowStyleTitleBarButton" TargetType="{x:Type Button}"> <Setter Property="Focusable" Value="false" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Grid> <Border x:Name="border" Background="Transparent" /> <ContentPresenter /> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="border" Property="Background" Value="#FFF" /> <Setter TargetName="border" Property="Opacity" Value="0.7" /> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter TargetName="border" Property="Background" Value="{StaticResource VS2012WindowBorderBrush}"/> <Setter TargetName="border" Property="Opacity" Value="1" /> <Setter Property="Foreground" Value="#FFF"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
      
      





ボタン上の画像は、「ベクター内」で作成するのが最も簡単です。 たとえば、最大化は次のようになります。

 <Path StrokeThickness="1" RenderOptions.EdgeMode="Aliased" Data="M0,0 H8 V8 H0 V0 M0,1 H8 M0,2 H8" />
      
      





タイトルテキストには、標準のSegoe UIフォントを使用します。 ここでの唯一の機能は、テキストがぼけずに描画されることを確認することです。そうしないと、ウィンドウタイトルが表示されます...スクリーンショットの2行目のように、見た目が悪くなります。







ちなみに、同じ目的のボタンのパスには、EdgeMode =“ Aliased”が使用され、

WPF 4+のテキストの場合、「理想的なデバイス」ではなくディスプレイに表示されることを示す待望の機会が現れました。これにより、非理想的な画面で許容できる明瞭さを実現できました。



 <TextBlock TextOptions.TextRenderingMode="ClearType" TextOptions.TextFormattingMode="Display" > ...
      
      





別の興味深い機能は、ウィンドウをフルスクリーンで開くときの「Windows 7のジオメトリ」に関連付けられています。 ウィンドウは、ウィンドウのサイズを変更することでチートを実行し、境界線が画面の境界線を完全に超えて、ウィンドウのクライアント部分のみがモニターに残るようにします。 当然のことながら、Windowsは境界線を描画しなくなり、標準のウィンドウではすべてが期待どおりに機能します。 WPFはこれを解決しません。また、私たちのようなウィンドウでは、画像の一部を失ったり、接続されている場合は隣接するモニターに描画を開始したりするリスクがあります。



残りの詳細はそれほど重要ではありませんが、興味がある場合は、 ソースへようこそ。



ウィンドウを復活させる



.Net 4.0



ボタンとアイコンへの反応に加えて、タイトル、エッジ、コーナーをドラッグすると、ウィンドウが移動およびサイズ変更されます。 対応するホットゾーンは、非表示のコントロールを使用して最も簡単に設定できます。 左上(北西)コーナーの例。



 <Rectangle x:Name="rectSizeNorthWest" MouseDown="OnSizeNorthWest" Cursor="SizeNWSE" Fill="Transparent" VerticalAlignment="Top" HorizontalAlignment="Left" Width="5" Height="5" />
      
      





リソースにClass属性が存在する場合、このクラスのメソッドは、使用した通常のイベントハンドラーとして名前で簡単に呼び出すことができます。 MinButtonClickやOnSizeNorthWestなどのハンドラー自体は、次のようになります。



 void MinButtonClick(object sender, RoutedEventArgs e) { Window window = ((FrameworkElement)sender).TemplatedParent as Window; if (window != null) window.WindowState = WindowState.Minimized; } void OnSizeNorthWest(object sender) { if (Mouse.LeftButton == MouseButtonState.Pressed) { Window window = ((FrameworkElement)sender).TemplatedParent as Window; if (window != null && window.WindowState == WindowState.Normal) { DragSize(w.GetWindowHandle(), SizingAction.NorthWest); } } }
      
      





次に、DragSizeはWinAPI( source )を呼び出し、Windowsを強制的に前のようにウィンドウサイズ変更モードに切り替えます。



.Net 4.5

4.5では、便利なクラスSystemCommandsWindowChromeが登場しました 。 ウィンドウに追加されると、WindowChromeはウィンドウのサイズ、位置、状態を変更する機能を引き継ぎ、より「グローバルな」問題を残します。



  <Setter Property="WindowChrome.WindowChrome"> <Setter.Value> <WindowChrome NonClientFrameEdges="None" GlassFrameThickness="0" ResizeBorderThickness="7" CaptionHeight="32" CornerRadius="0" /> </Setter.Value> </Setter>
      
      





必要に応じて、.Net 4.0でWindowChromeを使用できますが、WPFShellなどの追加のライブラリを追加する必要があります(ヘルプについてはafshermanに感謝します)。



ほぼ完了。 ウィンドウの状態が変化したときにインターフェイスの変更を制御するトリガーを設定します。 XAMLに戻って、たとえば、Window.IsActiveの値に応じてStatusBarsの色を変更します。

StatusBarのXAML
 <Style.Resources> <Style TargetType="{x:Type StatusBar}"> <Style.Triggers> <DataTrigger Value="True" Binding="{Binding IsActive, RelativeSource={RelativeSource AncestorType=Window}}"> <Setter Property="Foreground" Value="{StaticResource VS2012WindowStatusForeground}" /> <Setter Property="Background" Value="{StaticResource VS2012WindowBorderBrush}" /> </DataTrigger> <DataTrigger Value="False" Binding="{Binding IsActive, RelativeSource={RelativeSource AncestorType=Window}}" > <Setter Property="Foreground" Value="{StaticResource VS2012WindowStatusForegroundInactive}" /> <Setter Property="Background" Value="{StaticResource VS2012WindowBorderBrushInactive}" /> </DataTrigger> </Style.Triggers> </Style> </Style.Resources>
      
      





このスタイルはウィンドウテンプレートには影響しませんが、ウィンドウに配置されたコントロールには影響することに注意してください。 カスタムコードを使用した最初のスニペットを覚えていますか?



 <Window ... Style="{StaticResource VS2012WindowStyle}"> ... <StatusBarItem HorizontalAlignment="Right"> ... </Window>
      
      





これは、現在設定しているこのStatusBarのスタイルです。 必要に応じて、他のクラスのコントロールのスタイルを設定することもできます。たとえば、目的のスタイルに一致するようにScrollBarを微調整することもできます。 しかし、これはすでに次の無料の夕方のエクササイズになります。



すべてをまとめる



それだけです アプリケーションリソースを介してスタイルをプロジェクトに接続するだけです。



 <Application ... StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Styles/CustomizedWindow/VS2012WindowStyle.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
      
      





また、どのウィンドウでも使用できます。





-D



PSもう一度、彼女のためにすぐにスクロールダウンした人のためのgithubのソースへのリンク



All Articles