2013年2月28日木曜日

【WPF】カラーピッカーを作ろう


ほら,Windowsにはカラーピッカーコントロールがあるし…
あれ?ないの?
WPFから見える標準コントロールにないの?

よくみるColorDialogはSystem.Windows.Formsなのか.
まぁ参照追加すればいいはなしなんだけどね.
ここでやってる.
http://d.hatena.ne.jp/tanjoin/20110118/1295352343
役に立ったじゃん.

そこで今回はWPF上で独自のカラーピッカーを作っちゃおうという作戦.
これも何千番煎じかわからないけど突き進むぜ.

目標としては
ここbloggerのWYSIWYGエディタのカラーピッカー.
bloggerのカラーピッカー
こんなやつ.
これを目指す.

ComboBoxを拡張して作ることにした.
そしてカスタムコントロールを作成し
使い方は呼び出せば終わりという風にしたい.
できるだけ単純に,簡潔に,明瞭に.

そしてできたのがこのコントロール.


コードの半分以上が色設定という.
使い方はComboBox準拠.
なので,当然SelectionChangedなども使える.
色はBrushとColorで取得できる.
一応16進文字列でも取得できる.
今回作成したカラーピッカーコントロール
手段は飽くまで手段であり,これが目的ではない.

2013年2月22日金曜日

【WPF】RichTextBoxのテキスト取得


RichTextBoxで絶賛悩み中.
こんな質めんどくさいものは使いたくはないが
入力できて装飾もできるコントロールはこれだからなぁ.

まずRichTextBoxの構造が分からなかった.
普通のTextBoxみたいにそのままテキストを出し入れできるわけでなく,
コンテナみたいなかんじでいろいろな要素を中に持っている.
UIとしては綺麗にまとまっているが,中身はすさまじい.
MSDNでわかりやすい図があった.

http://msdn.microsoft.com/ja-jp/library/ee681613%28v=vs.95%29.aspxより転載

なるほど,RichTextBoxというのはBlockというものの集合体であり,
BlockというものはInlineというものの集合体なのか.

で,RichTextBoxというコントロールの中ではBlockは
Blockを継承したParagraphというクラスを用いる.

InlineBoldItalicなどといったクラスを用いると.

ここで,Blockは改行ごとに作られる.
例えば
aaaaaaaa
bbbbbbbbbb
ccccccc
というテキストを入力すれば,

aaaaaaaaというブロック,
bbbbbbbbbbというブロック,
cccccccというブロック

がRichTextBoxの下にあるということになる.


そしてブロックの構成要素であるInlineは
装飾を施したごとに作成される.
abcdefghijklmn
というブロックであれば,

abというインライン(ノーマル)
cdeというインライン(ボールド)
fgというインライン(ボールド+イタリック)
hijkというインライン(イタリック)
lmnというインライン(ノーマル)

となるようだ.
なるほど.



次にBlockやInlineのテキストを取得する方法.
これにはTextRangeというクラスを用いる.
Block系のクラスにもInline系のクラスにも
Textパラメータなぞなく,TextBlockなどでやってた取得方法が通用しない.


これでブロックのテキストが取得される.
インラインのテキストを取得する場合は↑ソースのblockをInlineとして考えればいい.

RichTextBoxのすべてのテキストを取得する場合は


さらに細かくなるが,すべてのInline要素を取得していくとしよう.
BlockからInline要素の取得は
Paragraph.Inlinesプロパティを使う
あとはBlockと同様の扱い方となる.

RichTextBoxの構造がネスト構造になっているおかげで
取得する方もネストして行かないといけない.


これで装飾ごとにRichTextBoxのテキストを取得できた.

2013年2月20日水曜日

【WPF】RichTextBoxでショートカットキーを抑制する


RichTextBox祭り.
RichTextBox(System.Windows.Controls)は
配置するだけでショートカットがいろいろ設定される.
例えばCtrl + Bで太字になるし,
Ctrl + Iで斜体になる.
一覧は
http://msdn.microsoft.com/ja-jp/library/system.windows.documents.editingcommands.aspx
にある.

さて,ここで困ったのが
勝手にショートカットキーが登録されているから
必要もない…というかUI的にあってはいけないコマンドが存在し得ること.
リスト表示とか上付き下付きは存在してほしくない場合もあるのだ.
しかし,標準でCtrl + Shift + Nでリスト化されてしまう.

今回はこれを抑制する.
使用するのはRichTextBox.CommandBindingsというプロパティ.

System.Windows.FormsのほうのRichTextBoxにはShortcutsEnabledなんてプロパティがあったりするが,
Controlsのほうには存在しないのだ.

RichTextBoxのオブジェクトを_richTextBoxとして
このメソッドを呼び出してあげる.

TextBox_CommandHookイベントは空メソッドでいい.

BoldやItalic,Underlineを別扱いしているのは
後々必要になった時に拒否リストから除外して
ショートカットを復活させるため.
必要がないのであれば他と同じようにしてもいい.

後々必要になった場合の除外の仕方は
である.

ショートカットをフックするため,そのコマンドで別の動作をさせることもできる.
本来はそのためのものだな.

【WPF】RichTextBoxの行間が広い


【WPF】RichTextBoxの行間が広い

普通にRichTextBoxを作っていると
なぜか行間が異様に広い.

こんなに行間を要求していない!

今回は普通にオブジェクトのプロパティなので
書くまでのことでもないかもしれないが

RichTextBoxをどんだけいじってもこの行間は変わらない.
RichTextBoxが持っているFlowDocumentもしくはParagraphの
LineHeightを変更してやる.

これはXAMLからでもC#からでもいける.

値は好きなようにすればいいがとりあえず1にしてみた.

ちなみに0だとerrorがでる模様.

【WPF】RichTextBoxの幅が0な件


正確にはRichTextBoxの下にあるFlowDocumentの幅が0な件.
RichTextBoxに適当な幅を与えているのに
FlowDocumentの幅が0なせいで
改行されているわけではないが1文字で次の行にいってしまう.


こんなコードだと
入力しても1文字ごとに下へ下へと伸びていく.
幅を無視して下へと伸びていくの図


FlowDocumentのPageWidthに適当な固定値を与えてやれば当然そこまでは幅がのびるのだが
実際はウインドウサイズを変えたりいろいろあるので固定幅はいただけない.

FlowDocumentにはRichTextBoxと同じ幅であってほしいのだ.


…というわけで解決策.
XAMLだけで記述する場合.


FlowDocumentにはRichTextBoxと同じ幅であってほしいので
PageWidthにはRichTextBoxの幅をバインド.
これだけでOK.

続いて,C#だけで記述する場合.
特にカスタムコントロールにRichTextBoxを配置する場合.
コンストラクタでRichTextBoxを設定することになるだろうが,
コンストラクタ内ではまだUIに配置されていないので
幅が決まっていない.
つまりコンストラクタ内でRichTextBlockの幅を取得しても0なのである.


ここでFlowDocumentの幅を設定しても固定値となってしまうので
ウインドウサイズの変更に対応できない.

C#で書く場合はこう書けばOK.


RichTextBoxのサイズ変更時にイベントを発行させて
その都度FlowDocumentのサイズも変更してやればいい.

使いこなせれば便利なんだろうけど
今のところ不便だぞRichTextBox!

2013年2月19日火曜日

【WPF】MediaElementを指定時間で止めてサムネイルを作成する


さて,今回はWPF.
ここにきて初めてWPFを触った.
WindowsMetroアプリケーションと同じ感じで作れるね.
っていうか根本的にはWindowsMetroがWPFと同じ感じで作れるってことなんだろうけど.

今回やりたいのは
MediaElementにムービーを設定して,
指定した時間のサムネイルが取得したい.

例えばソースを読み込んだ後,Playボタンが押されるまでは再生せずに(自動再生しない)
MediaElementには5秒ぐらいの再生位置で一時停止,表示させたい.

残念ながら完璧にはいかなかったので
うまいやりかたがあるのなら教示いただきたい.

SyntacsHilighter使うまでもないくらいなのだが.
UIに設置するMediaElement


自動再生が行われないようにLoadedBehaviorをManualにしている.
こいつにソースを設定する.


ソースを設定する際に直後にMediaElement.Pause()を呼び出してやれば
MediaOpenedイベントが発行される.
つまり,ここでいうところのmedia_MediaOpenedメソッドが実行される.

さて,ここで一旦話を変えて
クラスフィールドとしてデリゲートを作ってしまおう.


実際にMediaElementを操作する関数を作る.


なぜこのようになったかというと
・Pause中にPositionを変更しても再生するまでUIには反映されない
・Play()/Pause()を連続して呼び出した場合,早すぎて再生準備中に停止が発行される

ただし,このメソッドをUIスレッドで動かすと
ムービーの再生準備からスレッドをがっちりロックして
指定位置にくるまで離さないので当然別スレッドで動かしたくなる.

今回はXP SP3でも動作させるために.net 4.5などというハイカラなものは使わずに
.net 3.5を指定しているためasync/awaitなぞ使えない.

そのため,別スレッドで動かしつつディスパッチャにてUIスレッドに送り込む
先に作ったのはそのためのデリゲートだった.

というわけで,サムネイル(語弊あるけど)を作成するためのメソッドを作る


あとはMediaOpenedのタイミングでこのGetThumbnailを呼び出す


以上.
本当にサムネイル(ビットマップ)が欲しい場合は
http://ameblo.jp/nakdoll/entry-11133396303.html
このあたりを参考に~

2013年2月12日火曜日

【WinRT】ListView(GridView)にボタンを配置


要するにSkyDriveの転送ポップアップの×ボタンみたいにしたいわけだ.
あのポップアップがListViewかどうかは置いといて.
SkyDriveのアップロードポップアップ


例としてこのSkyDriveと同じようにローカルからWebへの転送を考える.
DataContextを用いたデータバインドも説明するけどわかりづらいかもしれない.
本題の回答は最後に書いてある.

ここでボタンを押さずにリストクリックでキャンセル…なら,ListViewのItemClickイベントに任せるだけでOKだった.
ItemClickイベントではどのリストがクリックされたかを返してくれるし,そのリストにひもづけられたアイテムも得ることができた.
今回はListView内にボタンを配置し,ボタンクリックでキャンセル出来るようにする.

まずはListViewにバインドするためのアイテムクラスを作る.
つまりこのクラスのオブジェクト1つがListViewのビュー1つ分になる.

かなーり簡略化.必要なところのみ.
本来ならプログレスバー用パーセントだとか進捗(サイズ表記)だとか
現在転送待ちなのか転送中なのか転送完了なのか失敗済みなのか等というステータスだとか
キャンセルするメソッドにしてもステータスを確認してからキャンセルさせるだとかいろいろ必要だが
混乱のもとなので.

次にそのアイテムクラスをバインドするUIはListViewとして,
ListViewやGridViewにはItemTemplateといって各リストに対してテンプレートを適用させることができる.
ListViewを作ってやるとすると

このような感じで指定する.
そのListViewのテンプレート:ItemTemplateの引数TransferTemplateを作る.
今回はファイル名とボタンのみ作成するが,先にも述べたとおり
プログレスとかサイズとかの表記もあったほうが当然親切である.

そして,このUIクラスのソースコードの方で

こんなかんじでデータのバインドを行う.

AndroidからWinRT(WPF)へ来た時にこのDataContextというバインド方法が
とてつもなく便利に思えた一方で,
理解するのに時間がかかったし,多分未だにちゃんと理解できていない.

ここのDataContext = collectionがUI側では
ListViewのItemsSource={Binding}で受け止められる.
すなわち,
ObservableCollection <TransferItem> collection ⇒ ListView
となる.

ListViewでObservableCollection <TransferItem> collectionが展開されて,
たくさんのTransferItemオブジェクトが一つ一つのリストになる.

さて,TransferTemplateのButtonにも{Binding}があるが,
ListViewの{Binding}とは受け取る情報が違う.
ListViewの{Binding}は前述したとおり,ObservableCollection <TransferItem>を受け取っていたが,
TransferTemplateのButtonにつけた{Binding}はListViewで展開された一つのTransferItemを受け取る.

つまり,ButtonのTagフィールドにはTransferItemのオブジェクトが代入される
DataTemplate下でのBindingはListViewに与えたリストを展開した(Foreach?)1つ分である.
Tagフィールドはobjectクラスのフィールドで,なんでも適当に入れていいよという程度に捉えている.

TextBlockの{Binding Name}ではTransferItem.Nameが代入される
ListViewで展開されたTransferItemのNameフィールドが入れられるということ.

{Binding}がキモで,場所場所に依って何を指すかが変わってくるが,
理解出来ればCSファイル上で代入しなくてすむため,強力なツールとなる.


話を本題に戻して,
ButtonにつけたTagフィールドが今回の解決法で
クリックした時のイベントButton_Clickedメソッドで
このように記述するとボタンクリックでキャンセルさせることができる.

多少可読性は下がるが一行でこう書ける.

2013年2月8日金曜日

【WinRT】Buttonをクリックしたときにボタンの下にポップアップを出す

いや別に下じゃなくてもいいんだけどね
日経新聞の上部バークリックして出てくるこのポップアップとか

SkyDriveのアプリバーから出てくるポップアップとか


どうやったら動的に位置を変動させてポップアップを表示させてるのかと思っていた.
コントロールの座標ってとれないしね.

っと思ってたんだが,ちょっとした方法でクリックしたコントロールの座標が取れた.
ButtonのOnClick時に呼ばれるハンドラ
TappedEventHandlerデリゲート
まぁあまりこのデリゲート自体には意識はしてないと思うんだけど
こいつには引数が2つ.
objectとTappedRoutedEventArgs.
このTappedRoutedEventArgsにGetPositionというメソッドがあった.

このTappedRoutedEventArgs::GetPositionというメソッドはどういうもんかというと
コントロールをTapした位置を引数に与えたコントロールの座標軸で返してくれるというもの.
たとえばTapしたButtonを引数に与えてGetPositionすると

こういうことである.

便利なのか不便なのかよくわからないメソッドではあるが,これを駆使することで
Tapしたコントロールの現座標を取得することができる.

まずXAMLを大まかにだがこういう構成にしてやる

つまり,クリックしてポップアップを表示させたいButtonと
一番ベースとなっているGridとに名前を付ける.
このベースとなっているGridは画面全体を表しているのがポイント.

そしてそのButtonをクリックしたときのメソッドで

ベースGridとButtonとそれぞれからGetPositionをして
X座標,Y座標それぞれで減算をしてやればいい.
「Gridの原点」=「画面の原点」なのでこの減算結果が座標となる.

あとはこの座標を元にPopupを表示させてやればOK.
ならないとは思うが負の値にならないよう念の為に
Math.Max()で最低値を設定してやるのが漢のマナー.
できればWindowサイズも取得してそこを超えないようにもしたい.


このエントリのコードは適当に書いてるので意図だけ汲んでいただければ.
このコードのままだとボタンの原点座標(ボタンの左上角)と同位置にポップアップが出てくることになるので
Y座標までわざわざ合わさずにY座標は固定orある程度の値を加算することになると思う.
そこは状況に応じて柔軟に.

2013年2月5日火曜日

【WinRT】AppBarについて


右クリックすると出てくるナゾの物体,AppBar.
今回はこいつについてUIを中心に軽く書いていこうと思う.


AppBarのボタンについて
いろいろなアプリを見て気づく通り,
AppBarには丸で囲まれたボタンを使う.
はるか昔のエントリで書いたが,
多くのアプリでこの中に使っているものは実は文字である.
SkyDriveのAppBar

Segoe UI Symbolという標準で搭載されているフォントを使い,
私用領域に格納されているシンボル文字を使うことが多いのである.

どのようなシンボル文字が登録されているかというと
MSDN(http://msdn.microsoft.com/ja-jp/library/windows/apps/jj841126.aspx)
に明示されているし,IMEから文字一覧を開いても見ることができる.

AppBarにButtonを配置し,
WinRTプロジェクトを作成した時にCommonフォルダ下にできている
StandardStyles.xamlにある
AppBarButtonStyleというスタイルを使用して
Contentにこのシンボル文字を放り込んでやると
AppBarに置くフォーマットのボタンが出来上がる.
もしくは,StandardStyles.xamlにあるSkipBackAppBarButtonStyleみたいに
専用のスタイルを作成してそれをButtonに適用してもいい.

ちなみに,WindowsPhoneやWinRTにおいて,
ボタンというものはこういった丸囲みボタンがデフォルトである.
AppBar以外でもボタンといえばこれ,といった認識を持っておいたほうがいいだろう.


AppBarに載せてはいけないモノ
AppBarに載せたくなるが,載せてはいけないモノがある.
それは以下のとおりである.

  • 検索
  • 共有
  • 設定

この3つ.
さて,気づいたであろうか.
実はこの3つはWindowsキーを押したり画面右端からスワイプしたりして表示させる
チャームの中に存在する項目である.
チャーム

検索をさせたい,設定をさせたい,共有をさせたい
といったときはAppBarでなく,チャームから行わせることになる.
それぞれ専用のUIが必要になってくるので,実はそれほど軽くない.
がんばれ.


各ボタンの配置について
ボタンにはいろいろな特性がある.
画面上の特定オブジェクトに対する機能を果たすボタン
画面全体に対する機能を果たすボタン
ページ遷移をする機能を果たすボタン
AppBarではそれぞれで配置が決められている.

まず,画面上の特定オブジェクトに対する機能を果たすボタン
例えばSkyDriveにおいて[選択した画像ファイルを削除する]機能であったり,
新聞アプリにおいて[選択した記事をお気に入り登録する]機能であったり.
つまり,GridViewやListViewなんかで右クリックして選択したアイテムに対する機能である.
こういったボタンは下AppBarの左側に配置する.

次に,画面全体に対する機能を果たすボタン
例えば[画面全体の更新]機能であったり,
[ログイン]機能であったり.
こういったボタンは下AppBarの右側に配置する.

最後に,ページ遷移をする機能を果たすボタン
例えば,[お気に入り登録した記事へ遷移する]機能であったり,
[ホーム画面へ戻る]機能であったり.
こういったボタンは上AppBarに配置する.
[ホーム画面へ戻る]機能は下AppBarに配置されてることが多いが,
実はこれは誤りで,本来は上AppBarに配置しなければならない.

ただし,こうしろと書いてあるものの,
当のMS謹製アプリなんぞでも全然沿っていなかったり,
特にアプリ審査には関係ないようである.
すべてのアプリがこうだったら感覚的に操作できるようになるよねっていう
MSの思惑であり,それが望ましいという程度のようだ.

とはいっても前項目の載せてはいけない検索,共有,設定は禁忌である.
リジェクト要因に十分成りうる.

なお,今後このUI規定がどの程度までリジェクト要因として関わってくるかは
測定不能であるため,従えるなら従っていたほうがいいに決まっている.


なんか書こうと思ったことの半分くらいしか書けていないが
長くなったのでここらでポストしようと思う.
冒頭で軽くと書いたのに軽くなくなった.
また気が向いたら書く.

【WinRT】オブジェクトのアニメーションの設定


WinRTではいたるところにアニメーションを付けるようになっている.
ポップアップや設定パネルの表示時にアニメーションを仕様すべきである.
しかし,コントロールオブジェクトを作っただけでは動いてくれない.
どういったアニメーションがWinRTで定義されているかは
MSDNのページ(http://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/hh452701.aspx)
が詳しい.


少し例を挙げる.
WinRT用の朝日新聞アプリと日経新聞アプリとがある.

まず朝日新聞アプリを見てみる.
設定ペインから「記事の更新間隔」という項目がある.
この「記事の更新間隔」を選択するとノーモーションでコントロールが出てくる.
もしくは,記事一覧ページ(ホーム)でAppBarを開くと「文字サイズ」というボタンがある.
これを押してもポップアップがノーモーションで出てくる.
ともに消えるときもノーモーションである.
このアプリではアニメーションが実装されていない.

一方で,日経新聞アプリを見てみる.
設定ペインから「フォントサイズ設定」を選択すると
画面右端からコントロールがアニメーションして出てきて,
閉じるときも右端へとアニメーションしていく.

WinRTアプリではアニメーションするのが望ましいため,
動作的には日経新聞アプリのほうが良いと考えられる.


このアニメーションをどうやって設定するのか.
実はXAMLの指定だけでできるように作ることができる.

この<親コントロール.Transitions>タグに囲まれた部分がアニメーションを設定している.
とはいっても,アニメーションの種類を予めSDKのほうで持ってくれてるようで
デベロッパーが気にするのはどのアニメーションを適用するか程度である.
どのアニメーションを,というのは上記のXAMLコードで言うところの
<PopupThemeTransition />
の部分であり,ここを違うタグに変更すると違ったアニメーションになる.

どういったアニメーションが用意されているかというと
前述したMSDNのページで探してもらえばいいが,そんなに多くない.
コントロールの表示/非表示時に関するアニメーションに限ると
AddDeleteThemeTransition
ContentThemeTransition
EdgeUIThemeTransition
EntranceThemeTransition
PaneThemeTransition
PopupThemeTransition
ReorderThemeTransition
RepositionThemeTransition
の8パターンである.

例で挙げた設定パネルのようなパネルの表示/非表示であればPaneThemeTransition
ポップアップの表示/非表示であればPopupThemeTransition
AppBarのようなオブジェクトの表示/非表示ならEdgeUIThemeTransition
などといったように用途に応じてこのTransitionを選択することで
簡単に他のアプリと見た目,動作を統一させることができる.

さて,ここで1つだけ注意しないといけないのは
例に挙げたXAMLコードでは<UserControl>にTransitionを設定したが,
実は<Border>にもTransitionを設定することができる.
StackPanelやGrid,挙げ句の果てはTextBlockなどにも
Transitionは指定できる.
しかし,こういった下位コントロールにTransitionを指定した場合
表示時にはアニメーションを行うが,
上位コントロールを閉じた場合に同時に瞬時に消えることになるので
非表示時にはアニメーションが行われないことになる.

つまり,コントロールの最上位階層にTransitionを指定する必要がある.

C#で組めて,WPFと同じような感覚で作れるため自由度が高く感じるWinRTアプリケーションであるが,
UIの制約は覗いてみると結構うるさかったりする.

2013年2月4日月曜日

【WinRT】テーブルを動的生成する

前回の続き.
さすがにアレだけじゃあつまらないので
今度はテーブルを動的生成しようと思う.

ただし,機能表みたいな感じで横は2コラムのみ.
縦に伸ばしていく感じ.

Dictionary<String, String>のデータを喰わしてやると
展開して表にしてくれるようなものを作る.

XAMLは使わない.それでこそ動的生成.

以下がコード.
Gridクラスを継承して作っている.
コンストラクタにDictionary<String, String>形式のデータを喰わせると
展開して表にまでしてくれる.

これで出来上がり.
WinRTでのプログラム側からUIを制御する方法の勉強にもなった.
そういえばこれまであまりプログラムから動的にUIを制御してなかったので.

で,これの使い方は

こんなかんじ.
*****のところは必要な形にする.
実行すると以下の様なテーブルが生成される.

【WinRT】WinRTでテーブル

WinRTのコントロールにはTableがない.
が,XAML上でテーブルを作ることはできる.
HTMLのTABLEほど簡単とはいかないが,
それほど難しいわけでもない.

RowDefinitionsとColumnDefinitionsを使う.
StackPanelを入れ子にしてもできなくはないだろうが,
こちらのほうが明確であり感覚的である.
下手に文字で説明するよりXAMLコードを示したほうが早いだろう.

このコードを実行すると以下のようになる.


TextBlockには背景色や枠の指定がないのでそういう指定をするならBorderなどに入れないといけない.
らくちんらくちん

次回はテーブルの動的生成をする.