開発備忘録

WPF(XAML+C#)の話題を中心に.NET/Android/CIに関するブログです

Jenkinsでインストーラーを作成する方法(Visual Studio 2017)

Jenkins でVisual Studio 2017 のInstaller Project を使用したインストーラーの自動ビルド方法について、これまでと若干異なる手順を踏む必要があったのでメモ

ちなみにVisual Studio 215 までは以下の方法でインストーラーの作成が可能です↓↓↓↓↓

iyemon018.hatenablog.com

これまでと異なる箇所

VS 2015 までは、以下のようなレジストリの値が最初から存在しました。

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0_Config\MSBuild\EnableOutOfProcBuild

VS 2017 ではこのようなレジストリの値がなくなっています。
そのため、何もせずにインストーラーを実行しようとすると以下のメッセージが表示されます。

ERROR: An error occurred while validating.  HRESULT = '8000000A'

対策

無いものは作ればいい。ということでレジストリの値を作ってしまいましょう。
レジストリエディタを起動して以下のキーを追加します。

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\15.0_XXXX_Config\MSBuild\EnableOutOfProcBuild

設定する値は"DWORD:0" です。

15.0_XXXX_Config となっている箇所は、"XXXX" 部分に何らかのコードが入ります。
バージョン番号でしょうか?私の環境では以下のようになっていました。
f:id:iyemon018:20181113173021p:plain

このコード部分は一致させないとビルドは失敗します。
このコードが環境によって異なるのか、バージョンによって異なるのかわかっていません。

もしかするとこのコードは可変値の場合があるため、状況によっては手動でレジストリキーを変える必要があるかもしれませんのでご注意ください。

(WPF)タッチキーボードを表示する -その2-

先日、WPF でタッチキーボードを表示する方法について書いたのですが、実際にアプリに組み込む場合はこの方法だけでは不足していたので、もうちょい実用的なコードとしてまとめてみます。

iyemon018.hatenablog.com

動作環境

前回と同じ

問題点

  1. タッチキーボードを表示してフォーカスが離れてもタッチキーボードが表示されたままになる。
  2. TextBox がフォーカスを取得した際に何らかの原因でタッチキーボードが即非表示になる。
    →これについては明確な原因はわかっていません。2 in 1 タブレット環境のみ発生する(?)ようです。

対策

問題点1. については、TextBox のLostFocus 時にTabTip.exe プロセスへSendMessage すればOK です。
問題点2. は、原因がわかっていないのでベストプラクティスではない可能性がありますが、フォーカス取得時に起動中のTabTip.exe プロセスをKill することで回避可能です。

具体的なコード

今回は既存のプログラムにも追加が簡単にできるようにBehavior で作ってみました。
なお、Behavior は、System.Windows.Interactivity.dll の参照を追加する必要があります。

続きを読む

(WPF)タッチキーボードを表示する

Windows10 + WPF でTextBox を選択したときにタッチキーボードが表示されない現象についてハマったのでメモ

動作環境

タッチキーボードが表示されない

通常、Windows10 環境であれば入力領域を選択するとタッチキーボードが表示されます。
しかし、動作環境のタブレットでTextBox を選択してもタッチキーボードが表示されないケースがありました。
この現象が発生したのがASUS R105H という2 in 1 タブレットです。
また、このタブレットで"タブレットモードOFF" にするとタッチキーボードが表示されなくなります。
"タブレットモードON" だとタッチキーボードは表示されます。
対象のコントロールはTextBox とPasswordBox です。

原因

いろいろ調査した結果、以下の設定が原因のようでした。
[Windows の設定] - [デバイス] - [入力] から以下の設定が OFF だと、この現象が発生します。(Windows10 1709 ではこのメニューでした)
f:id:iyemon018:20181019184434p:plain

複数の端末を確認したところ、

  • デスクトップはデフォルトOFF
  • キーボード付きタブレットはデフォルトOFF
  • キーボードなしタブレットはデフォルトON

のようです。
ただし、OS バージョンや環境によっては設定が変わっている場合もあります。

対策

単純に「タブレットモードON にする」「上記の設定をON にする」であれば回避可能です。
ただし、不特定多数の端末にアプリをインストールする必要がある場合はアプリ側で回避する必要があります。
実際には以下の手順を実行します。

  1. レジストリエディタからタッチキーボードをON にする。
    HKEY_CURRENT_USER\Software\Microsoft\TabletTip\1.7\EnableDesktopModeAutoInvoke を 1 にする。
  2. タッチキーボードのプロセスを実行する。
    タッチキーボードのプロセスは"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe"

具体的なコード

namespace KeyboardTest
{
    using System.Diagnostics;
    using System.Windows;
    using Microsoft.Win32;

    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void UIElement_OnGotFocus(object sender, RoutedEventArgs e)
        {
            // レジストリの値が 1 であればタッチキーボードは ON になっている。
            int value = (int)(Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\TabletTip\1.7", "EnableDesktopModeAutoInvoke", -1) ?? -1);
            if (value != 1)
            {
                Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\TabletTip\1.7", "EnableDesktopModeAutoInvoke", 1);
            }

            Process.Start(@"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe");
        }
    }
}

ただし、デスクトップなどでUSB により物理キーボードを接続している場合はタッチキーボードが表示されますが、2 in 1 タブレットなど予めキーボードが付属しているものはタッチキーボードが表示されません。

これらのレジストリやプロセスは今後のWindows Update によって変更になる or 使用できなくなる可能性が充分あるため、もう少しスマートな回避策がほしいところですが、私の調べた限りではこれ以上の情報がありませんでした…。

ちなみに、以下のページで.NET Framework 4.6.2 にアップデートするとタッチキーボードが自動で表示できるようになるよ という情報を見ましたが、実際にはそのように動作しませんでした。
環境によるのでしょうか?

blog.mzikmund.com

このあたりの情報をお持ちの方がいらっしゃいましたら教えてくださいm( )m

(WPF + MVVM)Material Design In XAML でモダンなダイアログを表示する

先日、業務でMaterial Design In XAML を使用したダイアログ表示機能を作ったのでメモ。

Material Design In XAML って?

GoogleAndroid OS に採用されているデザインシステムである"Materail Design" をMicrosoftXAML で実現しますよというOSS プロダクトです。

参考ページ:http://materialdesigninxaml.net/

使用可能なGUI アーキテクチャにはWPF, UWP があります。
ここまでであればよくあるXAML 向けテーマなのですが、Material Design In XAMLAndroid の外観とほとんど同じ見た目にするだけでなく、XAML 向けアーキテクチャ専用のエラー通知や外観をカスタマイズするためのDataTemplate ももちろん使用できます。
さらに、使用したいスタイルやコントロールはデモプロジェクトを実行すればいつでも閲覧してコピペが可能です。

デモプロジェクトを実行するにはGitHub のMainDemo.Wpf を実行すればOKです。 要ビルドなのでリポジトリをクローンするなり、ZIP でダウンロードするなりでやってみてください。


余談ですが、個人的には上記のMainDemo.Wpf を実行しながらAdobe XD のAndoid のUI キットを使って画面設計したりワイヤーフレームを作ると実際に製作段階に入ったときにシームレスに移行できるのでオススメです。
もちろんXAML の知識は必要ですし、XD で独自のUI を作る場合はその工数も必要ではあります。
XAML + Adobe XD が使えて画面設計書と実際の画面デザインの差を埋めることに困っているという方向けですね。

動作環境

ダイアログの作成

デスクトップアプリにおいて必須といっても過言ではないダイアログ表示ですが、エンタープライズ向けだとこんなことがままあります。

Windows 標準でないダイアログを表示したい」

標準ダイアログでは実現できない使用がある場合は仕方ないですね。
そこでMaterial Design In XAML の登場です。

Material Design In XAML のダイアログは DialogHost を使用します。 DialogHost はダイアログをホストする(表示する領域、パネル)もので、ダイアログを表示するための方法をいくつか提供しています。
ダイアログの表示方法はMainDemo.Wpf を見ればわかりますのでここでは割愛します。
開発時に作るべきはこのダイアログの外観とViewModel です。
外観はUserControl を使用し、ViewModel はバインド可能な情報をプロパティとして持っていればOK です。
以下はダイアログ(UserControl) とそのViewModel です。

gist793013159cfac93d76e930dcfa9525d1

gistb8083d846942569d7e7d6374edc2dd8c

これらのダイアログを表示するためにウィンドウにDialogHost を配置します。
このDialogHost の配置の仕方次第でダイアログの表示領域が決まるのですが、今回はサンプルなので全体を覆うように表示するものとします。

gist17d320f707bb8bcf5bd92f621716c62c

DialogHost のIdentifier プロパティは複数のDialogHost が存在する場合にどこにホストするかの識別子です。
一意な名前であれば何でもいいですが、後で使用するということは覚えておきましょう。
なお、Identifier を使用しない方法もありますが、今回は説明しません。

ViewModel からダイアログを表示する

ダイアログが作成できたのでViewModel から表示してみましょう。
そのまま表示するのではMainDemo.Wpf と同じなので、MVVM アーキテクチャを使用して表示するようにします。
MVVM を使用したダイアログ(メッセージ)表示といえばServices パターンですね。
ということでIDialogService インターフェースとその具象クラスを作っていきます。

gist4df100caa40a1cfd2cc40ae88aecc55d

gist8f129f635c3738c8b6e1c472db367fa4

とりあえずQuestion メソッドを一つ作って、質問メッセージを表示するようにします。
他種別のメッセージを表示したい場合は、新たにメソッドを追加していく形になります。
ここで重要なのは戻り値がTask<bool> であること。
Windows 標準のMessageBox とは異なり、Material Design In XAML のダイアログは非同期で表示されます。
この辺りはUWP とおなじですね。
また、戻り値は以下の方法で返しています。

object result = await DialogHost.Show(dialog, _identifier);
return (result is bool selectedResult) && selectedResult;

DialogHost.Show の戻り値はobject 型ですが、この値はどこから設定されるのか?
正解は先ほど作成したダイアログのXAML にある以下の行です。

<Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
        Content="はい"
        IsDefault="True"
        Style="{StaticResource MaterialDesignFlatButton}">
    <Button.CommandParameter>
        <system:Boolean>True</system:Boolean>
    </Button.CommandParameter>
</Button>

DialogHost.CloseDialogCommand というコマンドが用意されていて、そのCommandParameter に設定した値がDialogHost.Show の戻り値となります。
今回のケースではXAML に直接bool 型の値を設定し、"はい"ボタンと"いいえ"ボタンのどちらを押下したかを識別できるようにしています。

さて、DialogService が作れたので、これを画面のViewModel で使用できるようにしましょう。

gist793fbb7ca9006cca375a48e92ed4f495

ダイアログの表示はIDialogService.Question メソッドを非同期で呼び出せばOK です。
アプリの終了処理もMVVM で書けばよかったんですが、ちょっと手抜きです。(ゴメンナサイ


余談その2なんですが、WPF はView のDataContext にViewModel を指定する際、Visual Studio の[書式]メニューから一覧を表示してViewModel を選択すると思います。
しかし、このとき一覧に表示されるにはViewModel にデフォルトコンストラクタが実装されていなければなりません。
デフォルトコンストラクタを実装してXAML デザイナーからViewModel を指定したい、けど、必要なインターフェースはコンストラクタの引数から引き渡したいというときは私はいつもデフォルトコンストラクタにObsolete 属性を指定しています。
こうすることでコード上ではViewModel のデフォルトコンストラクタ呼び出しができなくなります。
ただ、なぜXAML デザイナー上ではエラーが発生しないのかはよくわかりませんが、WPF がこれ以上進化しないことを考えればこの回避方法はわりとありな気がしています。


最後にViewModel の構築部分です。
今回の例ではPrism を使用しているのでBootstrapper でViewModel をインスタンス化しています。

gist43bbb1b00cc5e305126b27f38a4f7a25

DialogService のコンストラクタで渡している"MessageDialogHost" はView で指定したDialogHost のIdentifier プロパティの識別子です。

これで実行して、右上の閉じるボタンをクリックするとこんなメッセージダイアログが表示されます。
f:id:iyemon018:20181007191308p:plain

このようにMaterial Design In XAML を使用すれば、独自ダイアログも割と簡単に表示することが可能です。
また、ダイアログとして表示するUserControl、ViewModel をカスタマイズすればいろいろと応用することもできます。
Material Design In XAML はほかにもモダンなUI を構築するための仕組みがそろっているので、WPF を使用するのであれば一度見てみることをお勧めします。

今回使用したプロジェクトはこちら↓↓↓↓↓ github.com

Google Cloud Platform Vision API を.NET で使ってみた

先日、Google Cloud Platform のVision API を使用してOCR を試したんですが、思いのほか簡単にできたのでその使い方についてメモメモ。

※なお、この記事は 2018年5月13日 時点の内容です。最新の情報とは異なる可能性がありますのでご注意ください。

Google Cloud Platform - Vision API について

Google Cloud Platform は、Google 社が提供するクラウドサービスで、Vision API はそのGCP で提供されているサービスの一つです。

GCP のサービスは様々な言語から使用することができ、そのサンプルも充実しています。
今回は.NET(C#) でその機能を試してみます。

OCR の種類

Vision APIOCR を行うにあたって文字認識機能は以下の2種類が存在します。

  • TEXT_DETECTION
  • DOCUMENT_TEXT_DETECTION

これらの2つの最大の違いは解析する文字単位の精度です。
TEXT_DETECTION は単語単位、DOCUMENT_TEXT_DETECTION は文字単位で解析されます。
なので、より高精度な解析が必要な場合は DOCUMENT_TEXT_DETECTION を選択することになります。

料金体系について

クラウドサービスを使用するうえで料金体系は重要な要素の一つです。
Vision API の料金体系は以下のページを参照してください。

https://cloud.google.com/vision/pricingcloud.google.com

料金支払いの単位は"ユニット"と呼び、これは一つの解析機能を使用した場合、"1"ユニットとなります。
そして、大体の機能がひと月1,000 ~ 5,000,000(500万)ユニットまで使用すれば、1,000 ユニットにつき$1.50 です。
計算めんどくせーって方は計算ツールも提供さているので以下のページからどうぞ。
cloud.google.com

続きを読む

(Android)APK をCI ツールで生成する方法メモ

Android アプリのAPK ファイルの生成をCI ツールを使用して自動化する際のメモです。
Android Studio だとメニューからAPK を生成できますが、例えばGit のmaster ブランチへpush されたらAPK を生成する といったことができれば、デプロイも簡単になります。

今回は以下のような環境下でどうすればAPK の生成を自動化できるか?という観点で調査・実践した結果をまとめています。

開発環境

開発用PC
  • OS : Windows10 Enterprise 64bit
  • 開発環境 : Android Studio 3.1.1
  • Gradle : 3.3
サーバー(オンプレミス)

目的

業務用のAndroid アプリ開発で、APK ファイルの生成を自動化したい。

アプリの署名について

Android アプリの全てのAPK は、デジタル署名されている必要があります。
署名に関する情報は以下のページを参照してください。

アプリの署名 | Android Studio

この署名はAndroid Studio から行うことができます。
上記URL の[キーとキーストアを生成する] の手順を行えば、キーストアが作成され、以降はパスワードを入力するとAPK ファイルを作成することができます。

ただし、これはあくまで"手動"でAPK を作成する方法です。

続きを読む

(Android)Lint 解析対象から特定のファイルを除外する方法

Android の開発中にデバッグ用のActivity やもう古くなって使用しないファイルをLint の解析対象外にしたいことがあります。 検索方法が間違っていたのか、調べても簡単には出てこなかったのでメモ。

開発環境

  • 開発環境 : Android Studio 3.0
  • Compile Sdk Version : API26 Android8.0 (O)
  • Min Sdk Version : API23 Android6.0 (Mashmallow)
  • Gradle : 2.3.3

Lint 解析の除外方法

特定のファイルをLint 解析から除外するには"lint.xml" ファイルに除外するファイルを記載する必要があります。
lint.xml を参照するには、/app/build.gradle に以下の内容を記述します。

android {
    lintOptions {
        // 任意のファイル名でOK
        lintConfig file('lint.xml')
    }
}

次に/app フォルダに"lint.xml" ファイルを追加します。 lint.xml の記載例は以下のとおりです。

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <!--  id="all" とすることで全ての警告カテゴリが対象となる。  -->
    <issue id="all">
        <!--  以下のファイルは全てのLint 警告を無視するものとする。  -->
        <ignore path="**/activity_debug_*.xml" />
        <ignore path="**/Debug*.java" />
    </issue>
</lint>

ポイントは以下の2つ

  1. issue タグの id 属性に"all"を設定する。
    今回はデバッグ用のファイルは全てのLint 警告を無視するようにしたかったのでこの様にしています。
    ここは各プロダクトによって適宜変更する必要があるかもしれません。

  2. ignore タグの path 属性に除外対象のフィアルパスを設定する。
    除外対象のファイルは、"**/" で全フォルダが対象となります。
    上記の設定では、activity_debug~.xml のようなデバッグActivity レイアウトファイルを解析対象外としています。
    また、デバッグ用のActivity クラスも同様に解析対象外としています。

何でもかんでもLint 検証対象外にしてしまうのは良くないので、開発が本格的に動き出す前に予めこのあたりのルールは決めておきたいところですね。