前々回、WPFで作るウィンドウには一つのコンテンツしか入れられないので、コントロールが一つしか付けられないことが分かりました。また前回は(WinFormと対比しながら)ウィンドウの"Content"に入れるものの動作や特徴についても書きました。今回は先ずこれを深堀してみようと思います。
WPFはウィンドウをwebのページのように扱い、ページに表示するものをUI(User Interface)としてとらえているようです。webで色々とみていると、こんな図を見つけました。
「UI要素のクラス階層:FrameworkElementからControl、Decorator、Panel等へ」
そして、コントロールから派生するContentControlクラス(System.Windows.Controls名前空間)について、この図の出ている記事では「単一のデータに対するデータ・テンプレートの適用」という表現をして、「ボタンなどのように、コンテンツを1つだけ持つコントロールの基底クラスである。アプリケーション本体となることの多いWindowクラスもContentControlクラスから派生(解説:従って、コンテンツは一つしか持てない)している。」とし、「コンテンツを表すContentプロパティはobject型になっていて、何でも格納することができる。格納した型によって、以下のように、異なる表示が行われる。」とした後、
「・UIElementクラスの場合、そのままUI要素として表示が行われる(OnRenderメソッドで描画を行う)
・そのほかのクラスの場合、データ・テンプレートが設定されていればテンプレートを使った表示が行われる
・データ・テンプレートも設定されていない場合、ToStringメソッドを使って文字列化された結果が表示される」
と書いていますが、前回書いたContentに入れたオブジェクトの動作、即ち
(1)文字列は普通に表示される
(2)画像はそのサイズにより、縦か横一杯に表示される
(3)コントロールはクライアントエリア(ページエリア?)いっぱいに表示される
に対応しているようです。(オブジェクト毎にデータテンプレートが設定されているようです。)
更にその後、
「ContentControlクラスの派生クラスの中に複数のUI要素を並べたい場合、
(解説:①)まず<Grid>要素などのパネルを置いて、
(解説:②)その中にUI要素を並べる。」
という「解法」が述べられていました。
では、この「<Grid>要素などのパネル」とは何なのか、を↑の図で見ると、FrameworkElementから派生するPanelクラスに"Grid, StackPanel, WrapPanel, DockPanel, Canvas"というクラスが並んでいます。(注)
注:名前空間はSystem.Windows.Controlsにあります。実はパネルには、この他にも
System.Windows.Controls.Primitives.TabPanel
System.Windows.Controls.Primitives.ToolBarOverflowPanel
System.Windows.Controls.Primitives.UniformGrid
System.Windows.Controls.Ribbon.Primitives.RibbonContextualTabGroupsPanel
System.Windows.Controls.Ribbon.Primitives.RibbonGalleryCategoriesPanel
System.Windows.Controls.Ribbon.Primitives.RibbonGalleryItemsPanel
System.Windows.Controls.Ribbon.Primitives.RibbonGroupItemsPanel
System.Windows.Controls.Ribbon.Primitives.RibbonQuickAccessToolBarOverflowPanel
System.Windows.Controls.Ribbon.Primitives.RibbonTabHeadersPanel
System.Windows.Controls.Ribbon.Primitives.RibbonTabsPanel
System.Windows.Controls.Ribbon.Primitives.RibbonTitlePanel
System.Windows.Controls.VirtualizingPanel
というものがあるそうな。
こいつらは皆ベースとなるPanel基底クラスのChildrenというUIElementCollection(注)のプロパティを持ち、Collectionものに共通のAddメソッドで子UIElement(UI要素-コントロールのみならず、画像や文字列等)を複数持てるようにしています。
注:要すれば、同じUIElementの配列を子供らとして持つようなもの-C++でいえば配列へのポインターのようなもの。
例えば簡単なStackPanelは、縦または横方向(注)に、コントロールを"<StackjPanel名>.Children.Add(<コントロール名>)”のように追加してゆきます。WrapPanelやDockPanelは親ウィンドウのサイズを変化させたときの動作が異なりますが、概ねStackPanelと同様の取り扱いでよいです。
注:OrientationというプロパティにHorizontalまたはVerticalを設定する。
Gridはエクセルのように列と行でできた容れ物で、最初に列と行を作ってやる必要があります。後はコントロール等UIElementをマトリクスのどこに入れるか指定し(注)、同じく"<Grid名>.Children.Add(<コントロール名>)”で子分にして終わりです。
注:SetColumn/SetRow(<UIElement名>, <0ベースの順番>)というメソッドを使います。
CanvasはWin32APIやWinFormの相対座標に慣れた方にやさしい容れ物で、ウィンドウのContentにCanvasを指定するとクライアントエリアいっぱいに広がるので、後は入れるUIElementをの位置を指定(注)し、"<Canvas名.Children.Add(<UIElement名>)”で子分にしてやります。
注:Canvas.SetTop(<UIElement名>, 上からのオフセット)と Canvas.SetLeft(<UIElement名>, 左からのオフセット)というメソッドを使います。
どうでしょう?
イメージがわきましたか?
要すれば、インターネットとの親和性を高めるために、コントロールや図形、イメージ、文字列等コンテンツの柔軟なレイアウト、区割り、配置を行えるようにし、その為にGrid, StackPanel, WrapPanel, DockPanel, Canvas等が採用され、ウィンドウをwebページのようにデザインするプロセスにしたんですね。その必然の結果としてUI(View)についてはXaml表記が導入され、その裏方処理("Code-Behind"(注)によるModel)はC#表記という「分離コード」へ発展したということのようです。
注:ASP.NETというMicrosoftのウェブプログラム開発フレームワークで使われる概念です。("Code-behind is a concept commonly used in ASP.NET web development.")webデザインのHTMLとは別の言語による処理コードを意味します。("Code-behind refers to code for your ASP.NET page and allows a clean separation of your HTML from your presentation logic.")
まぁ、そんなこたぁどうでもよくって、
問題はどうサンプルを書いたらいいのか、ということになりますね。そいつは次回に回しましょう。
【ご参考】