$30 off During Our Annual Pro Sale. View Details »

Road to UI Library

Grabacr07
December 01, 2014

Road to UI Library

Room metro #28 XAML Day で使用した資料です。
コントロール自作、そしてライブラリ化するための方法や注意点など。

Grabacr07

December 01, 2014
Tweet

More Decks by Grabacr07

Other Decks in Technology

Transcript

  1. Subject • UI Library 作ってました − MetroRadiance (最近弄ってない…) − UserControl

    / CustomControl の使いどころ共有 デモ & ライブコーディング中心 • UI Library によって − コントロール / UI 資産の再利用性を高める − アプリのブランド化 あたりに興味を持って頂けると!
  2. Self Introduction • Work − 株式会社グラニ − Unity + C#

    でゲーム開発、まれに WPF • Private activity − Web: http://grabacr.net/ − Twitter: @Grabacr07 (ぐらばく) − めとべや東京勉強会スタッフ − Microsoft MVP for Visual C# (2014/04 ~)
  3. KanColleViewer • Windows Desktop app − .NET Framework 4.5 −

    Visual C# + WPF • 艦これ プレイングツール Internet Explorer Shell + FiddlerCore • 2013/12 公開 (約 11 ヶ月) − 1,650,000 downloads − Azure の料金がやゔぁい!
  4. KanColleViewer • 常にエゴサーチ (twitter) − メンタル鍛えられます • ユーザーの反応 − 機能に対する反応・要望

    − UI に対する反応・要望 • デベロッパーの反応 − 「この見た目どうやって作んの」 UI ライブラリ自作しました
  5. MetroRadiance • WPF カスタム コントロール ライブラリ − カスタム コントロール&スタイル群 −

    別アプリでも同じ外観を再現できる ライブラリ (アセンブリ) を参照し、App.xaml の ResourceDictionary でマージするだけ − 自分で作ってるアプリのブランド化 社内向けツールとかも <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/MyControlLibrary;component/Styles/Controls.xaml" /> <ResourceDictionary Source="pack://application:,,,/MyControlLibrary;component/Styles/Icons.xaml" /> <ResourceDictionary Source="pack://application:,,,/MyControlLibrary;component/Themes/Dark.xaml" /> <ResourceDictionary Source="pack://application:,,,/MyControlLibrary;component/Themes/Accents/Purple.xaml" /> </ResourceDictionary.MergedDictionaries>
  6. UI Libraries • 最近のリッチなデスクトップ アプリ JetBrains 社のインストーラー • 独自 Style

    のコントロール群 • 標準 Chrome なし • Dark っぽいテーマ • 進行状況不定バー (WinRT のアレ) <ProgressBar IsIndeterminate="True"> • ウィンドウの枠が光る • ウィンドウの枠が光る
  7. Elysium • お手軽モダン UI ライブラリ これ使うだけでそれっぽく見える − http://elysium.codeplex.com/ − Zune

    風コントロール・スタイル群 再現度はあまり高くない… − Visual Studio 2010 or 2012 が必要
  8. Elysium • お手軽モダン UI ライブラリ これ使うだけでそれっぽく見える − http://elysium.codeplex.com/ − Zune

    風コントロール・スタイル群 再現度はあまり高くない… − インストーラーに弾かれる VS2010 or 2012 が必要って言われる − 独自のトースト通知システム Windows 7 でも通知できるが…
  9. Original Control • NumericUpDown − WPF にない鉄板コントロールということでひとつ − ボタンが押しにくく、 使い勝手悪くなりがち

    … を、今ここで実際に作ってみましょう (!) 要件 • ユーザーによるテキスト入力 • ボタン (値 +1) • ボタン (値 - 1)
  10. Case 1: User Control • csproj > 追加 > ユーザー

    コントロール • NumericUpDown.xaml
  11. Case 1: User Control • csproj > 追加 > ユーザー

    コントロール • NumericUpDown.xaml <DockPanel> <StackPanel DockPanel.Dock="Right"> <Button Content="▲" Click="CountUp" /> <Button Content="▼" Click="CountDown" /> </StackPanel> <TextBox Text="{Binding ElementName=Root, Path=Value, Mode=TwoWay}" /> </DockPanel>
  12. • csproj > 追加 > ユーザー コントロール • NumericUpDown.xaml.cs (UserControl

    からの派生) public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(0)); private void CountUp(object sender, RoutedEventArgs e) { this.Value++; } Case 1: User Control 依存関係プロパティ
  13. Case 1: User Control • 完成! … と、思うじゃろ? − そのときしか使わない場合は、それで完成。

    − 他のプロジェクトでも使い回したい場合は…? そして、他のプロジェクトでは外観を変更したい場合は…? 次のアプリでも使うよ ただしボタンをデカくしてくれ 大変申し訳ございません早急に 影響調査し対応致しますので今 暫くお待ち頂きますよう宜しk
  14. Feature of User Control • UserControl 型からの派生 − アプリケーション構築と同じ方法で作れる ▫

    Window / Page を作成するのと同じ要領で、ポトペタ開発 ▫ 既存のコンポーネントだけで構成されている − 複雑なカスタマイズをサポートしない Background や BorderBrush を外部から指定する、程度の簡単なカスタマイズはでちる しかし、DataTemplate や ControlTemplate による外部からのカスタマイズはできない
  15. Case 2: Custom Control • csproj > 追加 > 新しい項目

    > WPF > カスタム コントロール (Universal apps の場合は「テンプレート コントロール」) − NumericUpDown2.cs − Themes/Generic.xaml <Style TargetType="{x:Type local:NumericUpDown2}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:NumericUpDown2}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> 操作ロジック (コントロールの機能の実装) 視覚的表現 (コントロールの外観の実装) NumericUpDown2 は どうやって外観の定義まで 辿り着くか?
  16. Feature of Custom Control • Control 型からの派生 − ロジックと外観の完全な分離 ▫

    コントロールの振る舞いを、ロジック (.cs) に ▫ コントロールの外観を、Style/Template (Generic.xaml) に − .NET Framework が提供するコントロールと同じ実装 − コントロールの外観をカスタマイズ可能 ControlTemplate により、あらゆる外観に設定可能
  17. Parts and States Model • 視覚的な構造と動作は ControlTemplate で定義 − 外観を直接ロジックで書くのはダメゼッタイ

    − NumericUpDown の場合 負の値のときは 文字を赤くしたい… TextBox の Foreground を SolidColorBrush の Red に…
  18. Parts and States Model • 視覚的な構造と動作は ControlTemplate で定義 − 外観を直接ロジックで書くのはダメゼッタイ

    − NumericUpDown の場合 負の値のときは 文字を赤くしたい… TextBox の Foreground を SolidColorBrush の Red に… ValueStates • Positive • Negative 正負を VisualState で保持 負の値でどうなるか、は ControlTemplate で自由に実装
  19. Parts and States Model • ControlTemplate とロジックの接点 − コントロールの要件 (命題)

    を満たすうえで必要なパーツ − NumericUpDown の場合 ▫ Click イベントを購読するため、ロジックから参照しなければならない Value を増減させるための Button が必ず必要 名前付きパーツと OnApplyTemplate メソッド
  20. Parts and States Model • 名前付きパーツ − 例えば、System.Windows.Controls.ComboBox の場合 http://msdn.microsoft.com/ja-jp/library/ms752094.aspx

    <Grid x:Name="templateRoot" SnapsToDevicePixels="true"> <!-- 中略 --> <Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom"> <!-- 中略 --> <Border x:Name="border" Background="{StaticResource TextBox.Static.Background}" Margin="{TemplateBinding BorderThickness}"> <TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" Style="{StaticResource ComboBoxEditableTextBox}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" /> </Border> </Grid> ComboBox の ControlTemplate (default)
  21. Parts and States Model • OnApplyTemplate メソッド − ControlTemplate の

    Visual Tree が構築されたとき実行される ロジックから ControlTemplate の実装にアクセスできる最も早いタイミング • GetTemplateChild メソッド − ControlTemplate 内の名前付き要素を返す ControlTemplate 内にある名前付きパーツのインスタンスを取得できる!
  22. Parts and States Model • コントロール コントラクト − ロジックが使用する視覚的要素 名前付きパーツ

    − 視覚的に作用する public Property NumericUpDown でいう Value プロパティなど − コントロールの状態とグループ VisualStates / VisualStatesGroup
  23. Parts and States Model • コントロール コントラクト − ロジックが使用する視覚的要素 名前付きパーツ

    − 視覚的に作用する public Property NumericUpDown でいう Value プロパティなど − コントロールの状態とグループ VisualStates / VisualStatesGroup [TemplatePart(Name = "PART_UpButton", Type = typeof(Button))] [TemplatePart(Name = "PART_DownButton", Type = typeof(Button))] public class CustomControl1 : Control { private Button upButton; private Button downButton; public override void OnApplyTemplate() { base.OnApplyTemplate(); this.upButton = this.GetTemplateChild("PART_UpButton") as Button; if (this.upButton != null) { this.upButton.Click += (sender, e) => this.Value++; } // 以下略
  24. Custom Control • リソースを検索する順序 1. 要素レベル 参照する要素からルート要素まで辿って検索 2. アプリケーション レベル

    Application (App.xaml) 内で定義されたリソース 3. テーマ レベル Themes フォルダー内。つまり Generic.xaml Luna.NormalColor.xaml とか使わんし…
  25. Custom Control • リソースを検索する順序 1. 要素レベル 参照する要素からルート要素まで辿って検索 2. アプリケーション レベル

    Application (App.xaml) 内で定義されたリソース 3. テーマ レベル Themes フォルダー内。つまり Generic.xaml Luna.NormalColor.xaml とか使わんし… <UserControl> <Grid> <StackPanel> <Border> <local:NumericUpDown /> </Border> </StackPanel> </Grid> </UserControl> NumericUpDown.Resources、 Border.Resources、StackPanel.Resources、 Grid.Resources、… の順に検索
  26. Custom Control • リソースを検索する順序 1. 要素レベル 参照する要素からルート要素まで辿って検索 2. アプリケーション レベル

    Application (App.xaml) 内で定義されたリソース 3. テーマ レベル Themes フォルダー内。つまり Generic.xaml Luna.NormalColor.xaml とか使わんし… <Applicaton> <Appliction.Resources> <!-- ここにあるやつ --> </Appliction.Resources> </Applicaton> ※ ただしアプリケーションのみ ライブラリ (.dll) は Application を 持たないので
  27. Custom Control • リソースを検索する順序 1. 要素レベル 参照する要素からルート要素まで辿って検索 2. アプリケーション レベル

    Application (App.xaml) 内で定義されたリソース 3. テーマ レベル Themes フォルダー内。つまり Generic.xaml Luna.NormalColor.xaml とか使わんし…
  28. Without creating a new control • 新しいコントロールを自作しないための方法 作らないで済むならそれに越したことはなく。 CircleButton、とか作りたくなってくるが… マウス

    オーバーと クリック時に 全体の色を変える… 円と矢印、 押しやすいように Margin 広めで… Dark テーマに 対応…
  29. Without creating a new control • 新しいコントロールを自作しないための方法 作らないで済むならそれに越したことはなく。 CircleButton、とか作りたくなってくるが… マウス

    オーバーと クリック時に 全体の色を変える… 円と矢印、 押しやすいように Margin 広めで… <Button> <Grid> <Ellipse /> <Path Data="" /> </Grid> </Button> Button + リッチ コンテンツ + Style で実現可能 カスタム コントロールを作る必要は、ない Dark テーマに 対応…
  30. Find the best possible choice • コントロールの価値命題 − こそが重要 −

    外観ではなく、コントロールの原理 / 振る舞い XAML Platform では、外観はいくらでも作り込めるので、一切関係ない TabControl ListBox ItemsControl 見た目は同じでも、要件が異なるので、それぞれ違うコントロールをベースにしている
  31. Road to UI Library • 作ったコントロールの再利用性を高めるために − UserControl はワンオフの実装、その時だけに使える −

    CustomControl は、WPF などと同じコントロールの実装 − 再利用性を見込んで、の実装となると 断然カスタム コントロール (テンプレート コントロール)