antsk blog

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  1. --/--/--(--) --:--:--|
  2. スポンサー広告

続・WPFのValueConverterをXAMLにラムダ式で書いてみたりとか

WPFのValueConverterをXAMLにラムダ式で書いてみたりとか
で、Expressionがシリアライズできればなー、とか書いたけど
ふと見てみたらTypeやMemberInfoはシリアライズできるのですな。

とゆーことはExpressionサブクラスのプロパティを全部コピって
Serializable属性をくっつけたクラスをつくってやればいいんじゃなかろうか。

……と思ってしまったので作ってみる。
DynamicCompile.mp3(拡張子mp3のzip)

こんな感じで使えるようになった。


……が。しかし。うーーん。

正直使い物にならん。



うーん。あきらめるしかないかなあ?
スポンサーサイト

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2010/01/08(金) 00:27:11|
  2. WPF
  3. | トラックバック:0
  4. | コメント:0

WPFのValueConverterをXAMLにラムダ式で書いてみたりとか

ネタ元:DelegateConverter みたいなもの

記事中でも言及されてるけどイベントだと分離コード側に変換処理を書かないといけなくて
いまいちありがたみがない。

XAMLに直接変換処理をラムダ式の形で書けるといーなー。
……というわけで調べてみたらCodeDOMで文字列を実行時にコンパイルして実行できちゃうみたい。
[サンプル] 式木の利用例
なんで、これを参考に作ってみた。

DynamicCompile.mp3 ※ 例によって拡張子変えているzip


使い方はこんな感じ。年齢⇔生まれた年の単純な変換。


……便利そうだ……が、この実装だとデリゲート1つだけを保持したアセンブリを
ばんばん作って読み込むことになるなぁ。いいのかなぁ?

Expressionがシリアライズできるんであれば別AppDomainでアセンブリ生成してExpression treeを構築した後、
メイン側のAppDomainでFuncに変換すればアセンブリを生成したドメインをアンロードできるんだけど。



しかしC#でCodeDOMやExpression Treeを使った実行時の構築は
かなり黒魔術ちっくだなー。作るのにずいぶんと時間を使ってしまった。

Lispなんかの動的言語に慣れてる人はこんなのをふつーに使いこなしてるのだろうか?

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2009/11/19(木) 23:55:59|
  2. WPF
  3. | トラックバック:0
  4. | コメント:0

WPFのLabel.Target

って、いちいちBindingで指定しなきゃいかんのがめんどくさい……。



Bindingの中身はインテリセンスの補完がないんで
決まりきった内容のわりにタイプ量が多くてうんざりする。

……というか、毎回同じ内容なら共通化すればいいんじゃん。
と思ったので名前を指定したらBindingを設定する添付プロパティを作ってみた。

こうやって使う。


……あんまり文字数は減ってないなー。
実際の入力は補完が効くからタイプ量は減るんだけどね。



さてしかし、これでもまだ不満だ。

MVVMで作ってるとあんまりコントロールのインスタンスって参照しなんで、
LabelのTargetに指定するためだけにNameプロパティを設定することになったりする。

XAMLのNameプロパティは変更しようと思ったときにコピペしなきゃいかんし、
できればNameを使わなずにTargetを指定したい。

っていうか、WinFormは「Labelの次のコントロール」がアクセスキーの対象だったし、
XAMLでもよほど捻くれてない限りLabelとそのTargetは隣接して記述するんだから
「論理ツリーの次の要素」でいいだろ?

次の要素がPanelとかならその内部をしらべて先頭要素をターゲットにすればよろし。

……そんな感じで作った。

LabelTarget.cs


もう名前が要らない。

使い方サンプル。

LabelTarget_Window1.xml
(xamlファイルだけどxamlがアップロードできないのでxml)

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2009/10/01(木) 23:18:04|
  2. WPF
  3. | トラックバック:0
  4. | コメント:0

WPFコントロールのテンプレートをいじりたい

WPFの標準コントロールテンプレートはまだこなれてないというか、
ControlTemplateをいじって問題を解決しなきゃいけない場面がある。

ListView のカラムヘッダの幅を固定する
とか、
Horizontal stretch on TreeViewItems
とか。


……で、上記のページでは解決法が示されてるんだけど、
これだとTemplateを丸ごと置き換えなきゃいけない。

そうすると、イチからアプリケーションテーマを作りこんでるならともかく、
標準テーマでやってる場合はシステムのほかの部分のコントロールから
見た目が浮いてしまう。

テンプレートは標準のままで、問題になってる一部分を修正して使いたい。
……というわけで、だいぶ限定的だけど、そんな感じのものを作った。

TemplateOperation.cs

これで提供される TemplateOperation.AddStyle 添付プロパティを使って
<GridViewColumnHeader>
<TemplateOperation.AddStyle>
<ResourceDictionary>
<Style x:Key="PART_HeaderGripper">
<Setter Property="Control.Visibility"
Value="Collapsed" />
</Style>
</ResourceDictionary>
</TemplateOperation.AddStyle>
</GridViewColumnHeader>

と書くと、テンプレート内の "PART_HeaderGripper"要素を
非表示にするのでユーザーは列サイズを変更できなくなる。

……ま、テンプレートの要素名がいつも同じとは限らないわけだが
少なくとも現在PresentationFrameworkにくっついてる標準Templateでは
どのテーマも同じ名前を各要素につけてるから大丈夫なはず。
※ AeroとClassicしか試してない

たぶんMSDNのサンプルテーマは標準テーマと同じ内容のはずで、
これを基に作った多くのアプリケーションテーマでもそれなりに利用できる、といいなあ。


以下利用サンプルzip.(拡張子変えてアップしてる)
XAMLUtil.mp3

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2009/09/12(土) 00:41:14|
  2. WPF
  3. | トラックバック:0
  4. | コメント:0

WPFパネルの使い方まとめ

しばらくWPFを触ってて、当初はWinFormとかなり異なる作り方に難儀したけど
だいぶ理解してきたのでレイアウト(というか、パネルの使い方)の基本(と、自分が勝手に思っていること)をメモ。

基本



レイアウトはGridが基本。
Gridにできないことなど、あんまりない。
いろいろと試行錯誤した結果、少なくともWinFormのダイアログと同じような画面を作る場合は
以下の単純なルールが良さそう。


ようするに、


というのを再帰的に行っていけば、WPFがうまい具合に要素を配置してくれる。

パターン



2分割



縦か横に2分割する場合、上(左)部のサイズを固定にするか、下(右)部のサイズを固定にするかでちょっと違う。

上または左が固定



例:
fixtop.jpgfixleft.jpg

テンプレート

上が固定の場合:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<SubPanel Grid.Row="0">
上のほうのコントロール
</SubPanel>
<SubPanel Grid.Row="1">
下のほうのコントロール
</SubPanel>
</Grid>


左が固定の場合:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<SubPanel Grid.Column="0">
左側のコントロール
</SubPanel>
<SubPanel Grid.Column="1">
右側のコントロール
</SubPanel>
</Grid>


一般的な「上から下」、「左から右」へのコントロール配置。

このパターンでは常に DockPanel をかわりに使うことができる。

上が固定の場合:
<DockPanel>
<SubPanel DockPanel.Dock="Top">

</SubPanel>
<SubPanel>

</SubPanel>
</DockPanel>


左が固定の場合:
<DockPanel>
<SubPanel DockPanel.Dock="Left">

</SubPanel>
<SubPanel>

</SubPanel>
</DockPanel>


DockPanelの方が記述量が減るのでオススメ。

下または右が固定



例:
fixbuttom.jpgfixright.jpg

下が固定の場合:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<SubPanel Grid.Row="0">
上のほうのコントロール
</SubPanel>
<SubPanel Grid.Row="1">
下のほうのコントロール
</SubPanel>
</Grid>


右が固定の場合:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<SubPanel Grid.Column="0">
左側のコントロール
</SubPanel>
<SubPanel Grid.Column="1">
右側のコントロール
</SubPanel>
</Grid>


「OK」「キャンセル」などのボタンは、
たいていダイアログウインドウの下 or 右に配置するのでこのパターンになる。

このパターンではDockPanelを使えない場合が多いことに注意。

DockPanelは「最後の要素」を残りの領域全体に割り当てるので、どうしても
<DockPanel>
<SubPanel DockPanel.Dock="Buttom">
下のほうのコントロール
</SubPanel>
<SubPanel>
上のほうのコントロール
</SubPanel>
</DockPanel>

と、下や右のコントロールを先に書かなければいけない。

タブオーダーは明示的にTabIndexを指定しなければXAMLの記述順になるから、
これだとTabキーで移動したときの動きがわけわからなくなる。

結局、ステータスバーやコンボボックスのドロップダウンボタンのように、
下や右のコントロールがフォーカスを受け取らない場合にのみDockPanelが使える。

3分割



3分割では、中央を * 幅にする。

menuandstatus.jpg
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<SubPanel Grid.Row="0">
上のほうのコントロール
</SubPanel>
<SubPanel Grid.Row="1">
中央のコントロール
</SubPanel>
<SubPanel Grid.Row="2">
下のほうのコントロール
</SubPanel>
</Grid>


やはりこれも、下や右のコントロールがフォーカスを受け取らなければDockPanelを使うことができる。
<DockPanel>
<SubPanel DockPanel.Dock="Top">
上のほうのコントロール
</SubPanel>
<SubPanel DockPanel.Dock="Buttom">
下のほうのコントロール
</SubPanel>
<SubPanel>
中央のコントロール
</SubPanel>
</DockPanel>


ただ、このパターンは


という形で使うことがほとんどなので、
普通にDockPanelでいい場合のほうが多いかも。

いっぱい



単純に縦または横に並べるだけなら、幅Autoで必要な行数・列数だけ作る。

itemlist.jpg
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<SubPanel Grid.Row="0" >

</SubPanel>
<SubPanel Grid.Row="1" >

</SubPanel>
<SubPanel Grid.Row="2" >

</SubPanel>
<SubPanel Grid.Row="3">

</SubPanel>
....
</Grid>


このような場合は、StackPanelを代わりに使えばインデックスを指定しなくていいのでラクチン。
<StackPanel>
<SubPanel>

</SubPanel>
<SubPanel>

</SubPanel>
<SubPanel>

</SubPanel>
<SubPanel>

</SubPanel>
....
</StackPanel>


縦横揃え



左側にタイトルラベル、右側に入力コントロール、という形で縦に並んでいるパターン。

align.jpg
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<SubPanel Grid.Row="0" Grid.Column="0">
タイトル1
</SubPanel>
<SubPanel Grid.Row="0" Grid.Column="1">
入力1
</SubPanel>
<SubPanel Grid.Row="1" Grid.Column="0">
タイトル2
</SubPanel>
<SubPanel Grid.Row="1" Grid.Column="1">
入力2
</SubPanel>
....
</Grid>


恐るべき面倒くささを誇る。
何とかこれをうまい具合にRow/Column指定せずに済ませられればいいんだけど……。

位置あわせ



さて、上記のパネル指定だけだと、コントロールは隙間なくぴっちり配置されてしまう。

例:単にこれだけだと、
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Orientation="Horizontal">
<Button Content="Button1" />
<Button Content="Button2" />
<Button Content="Button3" />
</StackPanel>
<DockPanel Grid.Row="1">
<TextBlock DockPanel.Dock="Top"
Text="一覧" />
<ListBox>
<ListBoxItem Content="Item1" />
<ListBoxItem Content="Item2" />
<ListBoxItem Content="Item3" />
<ListBoxItem Content="Item4" />
<ListBoxItem Content="Item5" />
<ListBoxItem Content="Item6" />
</ListBox>
</DockPanel>
</Grid>


こんな感じ。
nopadding.jpg

このためPaddingやMarginを指定して微調整する必要がある。

このとき、個々のButtonなどには直接レイアウト情報を設定せずに
パネルのMarginやResourceのスタイル指定で調整すると
複数のコントロールをきれいにあわせることができる。

コントロールレベルの要素にはバインディングなんかのデータ情報だけ書いて
表示とデータの分離を進めておくと何かと良いかも。

例:こんな感じにすると
<Grid Margin="6" > <!-- Margin指定で全体を内側に寄せる -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Orientation="Horizontal">
<StackPanel.Resources>
<!-- ButtonのStyle指定で隙間と大きさを調整 -->
<Style TargetType="Button">
<Setter Property="Margin"
Value="3" />
<Setter Property="Padding"
Value="5 0" />
</Style>
</StackPanel.Resources>
<Button Content="Button1" />
<Button Content="Button2" />
<Button Content="Button3" />
</StackPanel>
<DockPanel Grid.Row="1"
Margin="0 3" ><!-- Margin指定でちょっとボタンと離す -->
<TextBlock DockPanel.Dock="Top"
Text="一覧" />
<ListBox>
<ListBoxItem Content="Item1" />
<ListBoxItem Content="Item2" />
<ListBoxItem Content="Item3" />
<ListBoxItem Content="Item4" />
<ListBoxItem Content="Item5" />
<ListBoxItem Content="Item6" />
</ListBox>
</DockPanel>
</Grid>


こうなる
padding.jpg

備考



ちなみに自分はBlendとか持ってないので
デザインツールを使用してXAMLを書くことはまったく考慮してない。

VSのWPFデザイナ?
あれは単にXAML編集結果をリアルタイムで表示してくれるビューです。

マウスでぺたぺたコントロールを貼り付けてUIを作る時代は終わった……。

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2009/09/10(木) 00:57:51|
  2. WPF
  3. | トラックバック:0
  4. | コメント:0
次のページ

プロフィール

antsk

Author:antsk
主にC#のプログラマ。

最新記事

最新コメント

最新トラックバック

月別アーカイブ

カテゴリ

未分類 (0)
.net (1)
WPF (8)

検索フォーム

RSSリンクの表示

リンク

このブログをリンクに追加する

ブロとも申請フォーム

この人とブロともになる

QRコード

QRコード

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。