2021/06/06

FAC製作記 その2 ダイアログの表示をどう実装するか

前回に続きダイアログがテーマです。
前回はそもそもダイアログを表示できないところからスタートし、それを解決して表示できるようになりました。
今回はダイアログを表示できるところからスタートしますが、どこでどのように表示するかがポイントになります。
「いや、必要なタイミングで表示させるだけでしょ?」となりそうですが、その表示させる「だけ」にも考えなければならないことはあるわけです。

今回の課題と解法は以下のとおりです。

【課題】
MVVMの役割分担を壊さずにダイアログを表示したい。
.NET Framework時代はMVVMフレームワークの機能を使って実現していたが、.NET移行でライブラリを乗り換えたので、別の方法を探さなければいけない。

【解法】
ヘルパークラスを使う。
ヘルパークラスはM/V/VMいずれでもない名前空間に作る。

本題の前にMVVMについて。
MVVMは「Model」「View」「ViewModel」の頭文字を取ったもので、この3つでアプリケーションを構成します。
それぞれの役割は以下の通り。
  • Model:アプリケーションの本体、UIに関わらない内部の処理
  • View:ユーザーに見える部分、画面とか
  • ViewModel:ModelとViewの橋渡し
以下はWikipediaの図ですが、MとVM、VとVMがやり取りしてアプリの機能を実現します。
(作者:Ugaya40, CC BY-SA 3.0)

例えばFACの「ファイルを分類する」という機能の場合は以下になります。
1. ユーザーがVの実行ボタンをクリックする
2. VからVMに実行ボタンが押されたことが伝わる
3. VMはMのファイル分類を実行する
4. MからVMにファイル分類の結果が返される
5. VMからVにファイルの分類が完了したことが伝わる
6. Vがユーザーにファイルの分類が完了したことを見せる

MVVMは必ず従わなければいけない規則というわけではありません。
しかし、1箇所に全部まとめてしまうとメンテナンスも大変なので、FACでもMVVMパターンを採用しています。

ではMVVM的にダイアログはどう扱うべきでしょうか?
ダイアログはユーザーに見える部分ですから、Vが扱うべきである、と考えることができます。
しかしVは表示とユーザーからの入力を受け付けることが役割ですから、「いつ」「何を」表示するのか決めるのは越権行為です。
「いつ」「何を」表示するか決めるのはM、またはMとVMの役目と言えます。
ではMとVMで扱うのが正しいのかというと、今度はMやVMが直接画面を触るという越権行為が発生します。

もう1点、MVVMの解説図に「Data Binding」というのがあります。
日本語でもそのまま「データバインディング」と言われます。
Vでイベント(例:ボタンのクリック)が発生したときにVMのどの処理を実行するか、Vにどんなものを表示するのか、など、VとVMを紐付ける仕組みです。
MVVMパターンを使う上で重要なポイントではあるのですが、.NETや.NET Framework標準ではこの機能が用意されていません。
ですので、MVVMパターンを使うときは、データバインディングを含めてフレームワークのライブラリを使うことが一般的です。

ではFACでは.NET Framework時代にどうしていたのか、.NETではどうするのか、1つずつ書いていきます。

【.NET Framework時代】
MVVMフレームワーク:MVVM Light Toolkit
MVVM Light Toolkitはその名の通り薄い軽いフレームワークでした。
シンプルですが、データバインディングを始め必要なものは用意されています。

この時代はダイアログの表示をMVVM + Messengerで実現していました。
Messengerはその名の通り、VMからのメッセージをVに渡す役割をします。
VはMessengerに「xxというメッセージが来たらこの処理を呼んでくれ」と登録します。
VMは必要なときにMessengerにxxというメッセージを送ります。
これでVM→(メッセージ)→Messenger→(登録された処理の呼び出し)→Vという流れができます。
なお、本来はMessengerも自分で作らなければならないのですが、MVVM Light ToolkitにはMessengerも用意されています。

今回の場合、VがMessengerに登録する処理でダイアログを表示しています。
こうすることでVMから直接Vを触ることなく、つまりMVVMの役割分担を壊さずにダイアログを表示することができます。

【.NET時代】
MVVMフレームワーク:Prism
Prismはある意味MVVM Light Toolkitとは逆で、盛り沢山なフレームワークです。
機能的にはMVVM Light Toolkitで十分なのですが、最新版のリリースが2018年9月と古く、.NETに対応する様子もないため、.NETへの移行を期にPrismに乗り換えることにしました。

PrismにもMessengerに使えるEventAggregatorが用意されていますが、調べてみるとMVVM Light ToolkitのMessengerと全く同じというわけにはいかないようです。
人によっては「EventAggregatorはVM間の通信を実現する」と言い切っているくらいで、VとVM間の通信に使うのはちょっと怖くなってきました。

ではどうするのか。ここで先に書いた解法です。
FACのフォルダ/名前空間構成は以下のようになっています。
「Models」「ViewModels」「Views」にMVVMのそれぞれにあたるクラスが入ります。
そして、選択して色が変わっている「Helpers」というフォルダがあります。
ここにダイアログを表示するDialogHelperというクラスを入れてあります。(IDialogHelperはDialogHelperで実装する内容を定めたインターフェースです)

ダイアログを表示するときはVMがDialogHelperを呼び出します。
VM→(処理の呼び出し)→Helperという流れですね。
この方式でも、VMから直接Vを触ることなくダイアログを表示することができます。

【残った課題】
Helperクラスを使うことで、「VMが直接ダイアログを表示したりVを触ったりしない」という条件はクリアできました。
しかし「表示はVが受け持つ」という条件はクリアできていません。
.NETに移行するときにはライブラリの乗り換えなど大きな問題が積み重なっていたため、「VMに表示処理が入っていなければヨシ!」と現場猫化しました。
ということで、.NET Framework時代と同じく、ダイアログの表示はVに戻せないか、後日再チャレンジして見るかもしれません。

【参考資料】

2021/05/30

FAC製作記 その1 ダイアログを表示するときに例外発生

先日の通り、FACを作るときに遭遇したあれやこれやを早速書いていきます。

本日のお題はダイアログです。
GUIアプリケーションであれば、ダイアログを使わないもののほうが少ないのではないでしょうか。
FACでも結果の通知とフォルダーの選択にダイアログを使っています。

今回の現象と解決策だけ書くと以下のとおりです。

【何があったのか】
.NET Framework 4.7.2から.NET 5に移行するとき、ダイアログの表示で例外が発生した。

【どうしたのか】
ダイアログのライブラリを乗り換えた。

それぞれについて詳しく書く前に、.NET Framework 4.7.2時代のダイアログについて説明します。

【コトが起きる前】
.NET Framework 4.7.2時代、FACはダイアログとしてWindows API Code Packを使っていました。
これはもともとMicrosoftが提供していたもののようですが、俺が知ったときにはすでにMicrosoft直々の公開は終了していて、いろいろなユーザーがかつて公開されていたものをそのままor独自のモディファイを加えて公開している状況でした。
上記のリンクはそれらのうち俺が使っていたものです。

たかだかダイアログでなぜライブラリを使っているのかというと、標準では足りなかったからです。
FACはGUIフレームワークとしてWPFを使っています。
WPFにもダイアログは用意されているのですが、問題があります。
用意されているのは通常の通知用ダイアログのみで、フォルダーを選択するダイアログはないのです。(FACでは使っていませんが、ファイル選択ダイアログもない)

WPFでフォルダー選択ダイアログを使いたい場合、Windows API Code Packは定番と言っていいでしょう。
それについて解説しているブログもすぐ見つかるくらいなので、王道と言ってもいいかもしれません。
Windows API Code Packには通常のダイアログ(※)も用意されているので、ダイアログはすべてWindows API Code Packを使っている、というのが.NET Framework時代のFACでした。
※正確には機能が強化されたTaskDialogなのですが、通知用であれば同じ感覚で使えます。

【起きたこと】
FACを.NET 5に移行するため、事前検証としてテスト用アプリを作って、色々と動作を確認していたとき、事件は起こりました。
Windows API Code PackのGitHubのreadmeにも「.NET 5をサポートしてるよ!」と書いてあるので、同じようにコード書けば動くだろー、と楽観視していました。

まずは通常のダイアログの動作を確認します。


例外が発生しました。
サポートしてるんじゃなかったんかい。

次にフォルダー選択ダイアログの動作を確認します。


こちらは.NET Framework 4.7.2のときと同じように表示されます。
なんでやねん。

【例外のメッセージから調べる】
気を取り直して、例外のメッセージを確認してみます。

System.NotSupportedException: 'TaskDialog feature needs to load version 6 of comctl32.dll but a different version is current loaded in memory.'
EntryPointNotFoundException: Unable to find an entry point named 'TaskDialogIndirect' in DLL 'Comctl32.dll'.

「TaskDialogはcomctl32.dllのバージョン6以上が必要だが、そうでないバージョンがロードされている。」「comctl32.dllに'TaskDialogIndirect'というエントリーポイントがない。」とあります。
調べてみると、dllファイルのバージョンをマニフェストファイルで指定すれば良い、とのこと。

しかし、ここで俺は以下のように思いました。
「.NET Frameworkではマニフェストファイルなんか不要だった。.NETになって必要になるのは納得いかない」
今思い返すと逆ギレですが、「じゃあ別のダイアログを探そう」という発想に行き着きました。

【乗り換え先のダイアログを探す】
「.NET 5対応」「NuGetでインストール可能」を条件に探してみると、Ookii.Dialogsというものが見つかりました。
余談ですが、こちらの作者の方は東大に留学経験があり、サイトのトップにも「Ookii -発音は/oːkiː/(だいたい"oh-key")- は日本語でbigという意味で、私のラストネームと同じ意味です」とあるので、「大きい」を意識した命名です。
さっそくインストールして、Windows API Code Packのダイアログ類を使っていたところをOokii.Dialogsに差し替えてみます。

まずは通常のダイアログから。


問題なく表示されますね。
続いてファイル選択ダイアログも。


こちらも問題なく表示されました。

ということで、この件はWindows API Code PackからOokii.Dialogsへの乗り換えという形で解決しました。

【原因と解決策の考察】
精査したわけではないので、俺の想像であることをご承知おきください。
すでに書いたように、Windows API Code PackはもともとMicrosoft謹製です。
名前から想像するに、WindowsのAPIをごく薄くラップしたライブラリ類なのでしょう。
したがって、.NET Frameworkと .NET 5の差分(標準で参照するcomctl32.dllのバージョンが違う?)を隠蔽しきれなかったのではないかと思います。

Ookii.DialogsはTaskDialogクラスを独自に実装しています。
そのためにcomctl32.dllを参照する必要がなく、Windows API Code Packで地雷になった箇所の影響を受けなかったのではないでしょうか。

【参考資料】

今日の1曲:


2021/05/22

作ってるもの

ブログの記事としてはお久しぶりです。
前回の記事が2016年の10月ですので、約4年半ぶりです。

この間特に何かあったとかではないのですが、
「1週間空いてしまった、来週は書こう」
「そうこうしてるうちに1ヶ月空いてしまった、来月こそ書こう」
「1年空いたし、もうよくね?」
という感じで、ずるずると4年半以上何も更新がないという事態になっていました。
これからは不定期でも年に1回くらいは更新したいな、と思っています。

今回のタイトルの「作っているもの」ですが、パソコン上で動かす簡単なツールを作っています。
名前を「Fur Affinity Classifier」としました。以下では頭文字取って「FAC」と記載します。
ソースコードや実行ファイルはGitHubにおいてあります。
READMEは英語で書いてありますが、今回は日本語での説明兼宣伝をさせてください。

【何これ?】
Fur Affinity(以下FA)からダウンロードしたファイルを、ユーザーのIDごとにフォルダーに分類するツールです。
FAについてはご存じの方が多いと思うので、詳しい説明は省略しますが、ケモノに特化したpixivと考えていただいてよいと思います。
classify (分類) に -er を付けて「分類機」という意味合いで名付けました。

【なんで作ったの?】
俺はFur Affinityで気に入ったイラストなどがあると自分のパソコンに保存するのですが、ファイルの分類が手間でした。
ファイルを分類するためには
1. ファイル名を見て
2. ユーザーIDを確認して
3. ユーザーIDに応じたフォルダーを開いて
4. ファイルを移動する
という手順が必要でした。
これがファイル2~3個とかならまだなんとかなりますが、5個10個となると面倒、20を超えると分類に割く時間がもったいないくらいになります。
じゃあ自分の勉強も兼ねて、自動でやってくれるツールを作ってしまえ、というのがスタートラインになっています。

【何ができるの?】
「何これ?」の通り、FAからダウンロードしたファイルを、フォルダーに分類することができます。
FAからダウンロードしたファイルは所定の形式になっていますので、後述する対象のフォルダー内のファイルを走査して、合致するファイルを移動します。
また、「このユーザーはIDと違うフォルダーに振り分ける」という設定もできます。
日本のような非ラテン文字圏だとこういう事態は起きやすいのではないかと思います。

【どうすれば使えるの?】
FACの実行には.NET 5が必要になります。
また、FAC内でも設定が必要です。
実際に俺が使っているときのスクリーンショットを以下に貼ります。
フォルダー名は黒で潰してありますがご了承ください。


スクショのうち「フォルダー設定」、つまり「ダウンロードしたファイルが入っているフォルダー」「IDごとのフォルダーが入っているフォルダー」の設定が必須になります。
IDと違うフォルダーに振り分けたい場合は、スクショで「IDと異なるフォルダーに分類する」となっている一覧に入力することで、「ID」列のIDは「フォルダー」列のフォルダーに振り分けるようになります。

【注意することとかある?】
あくまでも「俺が使うためのツール」を公開したものであるので、要望をいただいた場合は検討しますが、対応をお約束することはできません。
ライセンス(MIT)のとおり、保証とかは一切ありません。

今後はFACを作る上で困ったこととその解決法とか、そういうことも書いていきたいなと思っています。
「え、こんな簡単なモンでもそんなんあるの?」と思われるかもしれませんが、あるんだなこれが。

今日の1曲:


2016/10/08

ニコニコ町会議函館

ニコニコ町会議、過去、北海道では札幌や室蘭などで開催したことがあるようですが、今年は函館開催なので、遊びに行ってきました。
と言っても、開催を知ったのは、開催まで1週間を切った9/25でした。
ヴァンガードの対戦動画を投稿しており、蔦屋書店で毎週のように会っているガランドウさんが「次の土曜町会議あるから~」という感じで言っていたのです。

で、家に帰ってから町会議のページを見てみると、コスプレ用の更衣スペースも用意されるとのこと。
これは着ぐるみ持参で突撃するしかねえ、と計画を立てたは良いのですが、問題が2つあったのです。

1.6月に静岡に行ったとき、スーツケースのキャスターが壊れた
2.開催日の午後一で予定を入れていた

2.については11:30会場、18:00閉会なので、まあそこそこ時間はできるだろうと判断しました。
深刻なのが1.で、キャスターの壊れたスーツケースを引きずって移動なんかしたくないです。
ということで、開催前日の退勤時に、寄り道して新しいスーツケースを買ってきました。

前日はスーツケースの宣伝タグなどを外して使えるようにして、どうやって収納するかのシミュレーションをやってました。
このときに気づいたんですが、新しい(2代目)スーツケースは初代よりも大きくて、初代のときは斜めに入れていた尻尾が、垂直に入れられるようになりました。
万が一同じスーツケースを使っている人がいたときのために、識別用にチェーン付きの小さいぬいぐるみをぶら下げて、前日準備は完了としました。

明けて町会議当日。
買い出しを済ませたあと、用事が済んだらすぐに出られるように、荷物を整えました。
14時までには用事が完了する予定だったんですが、完了したのが13:40過ぎ。
町会議会場に行けるバスに間に合うかの瀬戸際で、走ってなんとか間に合ったものの、息を整えられたのが降りる数分前という体たらく。

それでも会場には迷うことなく到着しました。
しかし、それからコスプレ更衣室を探すのに5分くらいかかりました。
メインの会場から道路を挟んだところ、というのは会場マップでわかっていたんですが、その道路がどこかわからなかった。
自分が住んでいる町とはいえ、普段通らないところだとこうなるというダメなパターンですね。

バスを降りたところで、今回参加する友人に連絡したところ、一旦会場を離れており、また会場入りするとのこと。
で、更衣室に入りまして、余裕を持って獣化。
獣化完了して更衣室を出たのが、15時ちょっと前だったと思います。
ぶっちゃけたところ特に目的もなくほっつき歩いていたところ、親子連れの方に声をおかけいただいて、お子さんと一緒に写真撮りました。
さらに、地図にある「町シンボルカー」の係の方に「よかったら撮っていきませんか?」と声をかけられて、こんな写真を撮っていただきました。


耳と髪隠れちゃってるやん。

その後、会場に戻ってきた友人たちと合流。
で、こんな写真撮りました。
差し出されてるのはラムのザンギです。
このあと美味しくいただきました。

その後も時折「写真いいですか?」と声をかけていただいて、女子中学生(たぶん)とか、お子さんとかと写真を撮りました。
握手して、手の肉球をぷにぷにされたりとかもありましたね。

コスプレ更衣室があるということは、当然レイヤーさんもおられるわけで、レイヤーさんにも「一緒に写真いいですか?」と何度かお声掛けいただきました。
で、写真をトリミングしてまとめてアップロードしていた方がいたので、「うちの狐とのツーショット単体でいただけませんか?」とお願いして、ありがたく頂戴しました。
今回の町会議は、俺にとっては初めてのことのオンパレードでした。
初めての「着ぐるみオフ以外での獣化」「屋外での獣化」「レイヤーさんがいる中での行動」というのがとても大きかったです。
さらに、2時間ぶっ続けで獣化してたのも、最長記録を更新したと思います。
オンパレードというか、初でないものがなかったという方が正しいかも。
そんな状況であっても楽しめたのは、たぶんニコニコという、ある意味何でもありな場だったからだと思います。
スタッフの皆様、参加された皆様、お疲れさまでした&ありがとうございました!

今日の1曲:

2016/04/23

「ズートピア」を見てきた

日本では本日公開、「ズートピア」を見てまいりました。
映画館に行くのは約15年ぶり、自分だけで行くのは初めてです。

最初は、今年のG.W.に実家に帰る予定があるので、その時に札幌で見ようと思っていました。
しかし調べてみると、函館でも上映されるとのこと。

ということで、販売最終日に前売り券を買ってきて、今日を待ったのです。


今日は9:00、12:10、15:20、20:55の4回上映。
9:00はバディファイト見たあとに出ると、死ぬ気で自転車こがないと間に合わないのでパスして、12:10に狙いを定めて行きました。
結果、劇場の入場券がこうなりました。


右下の3桁の数字の順に10人ずつ入場なので、最初に入れるということでした。

その後、パンフレット買って読んで、まだ時間あるなということでグッズの棚を覗いてたんですが、思った以上にあって、「見終わったらゆっくり選んで買おう」ということにしました。

で、映画本編。
あまり多く書くとネタバレになるので、どこまで書けばいいものやら。
そして、俺の文章でうまくまとめられるものやら。

多くの動物が共存するにあたって、「ああ、確かにこういうのあったほうがいいだろうなあ」というのが、見てわかるように出てきました。
例えば、主人公・ジュディが故郷からズートピアに向かうときに乗った列車に、大きい動物用、小さい動物用のドアが準備されているところ。
また、ズートピアの一角にネズミ及び同じくらいの大きさの動物用のエリアがあるところ。
見せられると納得なんですが、それを自分で思いつけるかというとNOな俺にとっては、「やっぱり世界を作る人ってのはすげーな」となりました。

動物の習性やステレオタイプに基づいたシーンもあちこちありました。
レミングが一列になって勤め先から出てくるところとか、その典型じゃないかなと思います。
パンフレットによると、動物園、博物館、自然の中での調査に18ヶ月かけたそうです。
そしてその調査の結果を受けて、「固定観念や偏見を扱ったストーリーにたどり着いた」(パンフレットのPRODUCTION NOTESページより)とのこと。
だから「ウサギらしく」「キツネらしく」がパンフレットや公式サイトにクオーテーション付きで出てくるわけですね。
ストーリーの中核になる箇所だからこそ、それに対するスタンスが真逆の主人公が生まれたんかなあ、という感じがします。

個人的に思ったのが、英語の言葉遊びがある、というところ。
上の入場券のとおり吹き替え版を見たんですが、1箇所だけ、「今のって英語だと韻を踏んでるんじゃないか?」と思った箇所があります。
それを確認するためにも字幕版を見たいんですが、北海道では、少なくとも函館・札幌・旭川ではやってないのです。
こればっかりはDVDを待つしかないですかね。

そしてパンフレットや公式サイトにも載ってるんですが、ニュースキャスターの1匹が、国ごとに違います。
アメリカ、カナダなどではヘラジカですが、日本ではタヌキになってます。
たしかに日本ぽいわなー、と思っていたのですが、今になって、ニックがキツネだからか?とも思いました(たぶん違う)

と、つたない文章ですが、少しでも興味を持ってくださる方が増えれば、と思います。
映画の感想は人それぞれ、ぜひ見てみてください!

今日の1曲:


2015/09/05

特に一貫したテーマはない日記

バカルディのキューバ・リブレを飲みつつ書いております。
コーラ割りだから炭酸強い。

やっぱり一定のタイミングで書いてないと、ついつい間隔が数ヶ月単位で空いちゃいますので、できるだけ毎週土曜に更新するようになればいいなーと思っています。

先週カフェイン酔いのことについて書きましたが、 昨日レッドブルを買って飲んでみました。
確かに、前よりも強めの酔いがやってきましたが、翌日に持ち越すようなこともなかったです。
よくわからないですが、やっぱりコーヒーが一番ダメで、エナジードリンクはまだなんとかなるのかな?と。
控えるに越したことはないですね。
酒のレッドブル割もやめようっと・・・

今日のところはとりあえず、バカルディの275ml瓶をあけて、体がどうなるか実験してみます。
これで過去よりも強く酔いが出たり、残り方がひどかったりするようだったら、アルコールの摂取も考えないといけないですからね。
実はもうすでにほとんど残ってないのですがw

2015/08/29

カフェイン酔い

どうも、また2ヶ月更新してませんでした。
ほんとどうしようもねえな。

2ヶ月の間に、ジャパンカップ北海道大会があったものの、日程を調べ忘れてて準備全くしてなかったのでスルーしたり、ヴァンガードのイベントで札幌行ったりとかしてました。

ですが、今回はそういうもろもろすっ飛ばして、カフェインのお話。
よく「カフェインは眠気を払う」みたいなこと聞きますが、そうとばかりも言ってられないよ、と。

実は俺は「コーヒーを飲むと眠くなる」という体質です。
さらに、「エナジードリンクを飲むと酔っ払う」もあります。
これらに共通するものといえば、カフェインですよね。
日本人はカフェインに強い傾向があるので、コーヒーが眠気覚ましになるんだとか。
しかし国外に目を転じると、欧米ではアルコールに強い代わりにカフェインに弱い傾向があるそうです。
カフェインに弱い人がカフェインを摂取するとどうなるかというと、まるでアルコールを摂取した時のように酔っ払ってしまう。
俺はアルコールで酔うと眠くなるので、「これカフェイン酔いだろうなあ」ということで納得していました。
「傾向があれば例外もある。アルコールにもカフェインにも弱くてもいいやん」と。

そういうこともあって、できるだけコーヒーは避けていました。
しかし、どうしてもコーヒーを買わなくてはならないシチュエーションが先日発生したのです。
ローソンで、SHOW BY ROCK!の携帯クリーナーがもらえるキャンペーンというのをやってます。
で、キャンペーンの対象商品がコーヒーのボトル缶で、2本でクリーナー1つもらえるんですね。
全5種類なので、単純計算で10本買わねばなりません。
とりあえず2本買って1つもらったはいいですが、問題はコーヒーのほう。
捨てるなど言語道断ですので、飲みました。
荷物になるので、2本一気に。
これがオーバードーズだったようで、すさまじい眠気、倦怠感、得体のしれない気持ち悪さに襲われました。
まともに動けるようになるのに2日、全快するのに5日かかりました。

自分がカフェインに弱いということを把握しておきながら、コーヒーを一気に大量に飲んだのが一番の過ちでしたが、まさかここまでとは思いませんでした。
これから先、コーヒーはおそらく飲まないでしょう。
エナジードリンクはどうなるのか試していないのでわかりませんが、とりあえず適当なのを1本飲んでみて、酔いの度合いが重症化していたら控えようと思います。

欧米ではデカフェ、つまりカフェインの含まれていないコーヒーとかエナジードリンクもあるそうですが、日本ではあまり普及してないような気が・・・
そこまでして飲みたいかというと否なので、おとなしく断ったほうが安全な気がします。

自分の体で思い知ったことですので、そうそう簡単に同じような過ちはやらかさないと思います。
やらかしちゃ困るわな。


今日の1曲: