ぽにょろん

思いついたこととメモ

WindowsでiOS Safariの実機デバッグをする

仕事でちょいと使う機会があったので、メモとして残します。
因みに原因はただのクエリセレクタの記法ミスだったのですが、
何故かChromeでは動いてしまうというアレなアレでした。

目次

TL;DR

  • ios-webkit-debug-proxy*1を使う
  • ios-webkit-debug-proxyのReadMeに従う
  • 端末の設定-Safari-詳細の「Webインスペクタ」をOn
  • iTunesがインストールされていること

検証環境

  • iOS11/iOS9
  • ios-webkit-debug-proxy 1.8.4
  • Windows10 1809

モチベーション

iOS実機SafariでのWebページの挙動をWindows環境で確認したい。

AndroidChromeやPC版Chromeでは問題なく動くページが、Safariではダメだったという状況。

iOS WebKit Debug Proxyとは

MobileSafariやUIWebViewに対してデバッグ用にProxyしてくれるツールです。
Windows用のほか、MacOSLinux向けにも使えます。
エミュレーターに対しても使えるっぽいです。(未確認)

インストール

ReadMeを見るとscoop*2を使う手順が紹介されているので、それに従いましょう。簡単です。
※scoopなかなかいいですよね。
職場で試したときはscoopでやりましたが、スクショとるためにもう一度自宅環境で試したときは、GithubのReleasesページから適当に落としてきました。

手順

  1. 端末をPCに接続
  2. 端末の設定を確認
    設定-Safari-詳細の「Webインスペクタ」をOn
  3. Safariデバッグしたいページを開く
  4. コンソールから、ios_webkit_debug_proxyをたたく
    必要な引数はGithubのReadMe*3を参照
    f:id:kowill:20190514020012p:plain ※ 問題ないと思いますが、一応モザイクいれてます
  5. コンソール上にListing device on: 9221と出ているので、http://localhost:9221/Chromeで開き表示される端末をクリック
    f:id:kowill:20190514020129p:plain

  6. ページURLが表示されるのでクリック
    f:id:kowill:20190514020819p:plain

  7. 見慣れたページに似た画面が出てくるので、必要事項を確認
    f:id:kowill:20190514020206p:plain

最後に

iTunesは必ずインストールしておきましょう。 iOS WebKit Debug Proxyのページに特に書いてなかった(気がする)ので、最初入れないままやろうとしてました(汗

NPOIでセルの値を文字列で取得する(C#)

忘れたいシリーズということで、ブログに残すことで頭から追い出したいというトピックです。

目次

TL;DR

  • Excelファイルをprogrammableに扱いたいということ自体が辛み
  • CellTypeを見て、参照プロパティを切り替える

検証環境

  • .NET Framework 4.8
  • DotNetCore.NPOI -Version 1.2.1
  • LINQPad5 v5.37.06(AnyCPU)

NPOIとは

OfficeファイルやOOXMLフォーマットのファイルを読み書きするライブラリです。
POIdotnetバージョンのようです。

Install-Package

nugetにdotnet core対応バージョンがあるので、特に理由がなければそれを使いましょう。

Install-Package DotNetCore.NPOI

ファイル読み込み

ここでの対象は、xlsxのみです。
書き込む必要がなければ、Readオンリーがおすすめです。

XSSFWorkbook LoadXSSFWorkBook(string path)
{
    using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        return new XSSFWorkbook(fs);
    }
}

セルの値を文字列化

NPOIの場合、入力値を指定されているデータ型で取ることが可能です。
そのため、それぞれに合ったプロパティを参照する必要があります。
大きな注意点がいくつかあります。

  • 数値と時刻を明確に切り離せないため、入力しているFormatに応じてロジックを作る必要がある
  • 関数が埋め込まれている場合には単純にデータが取り出せない(ケースがある)
  • NPOIで使われているビルドインフォーマットが英語基準
    • CultureInfo等での切り替えはない
    • 特に日付回りで、日本語環境とは書式が違うので注意

記事執筆時点の私が使ってる変換ロジックは以下の通りになりました。
汎用的には使えない可能性もありますが、少なくとも私のユースケースではこれで十分でした。

string GetValueString(ICell cell, IFormulaEvaluator eval = default, Func<ICell, CellType> condition = default)
{
    if (cell == null) return default;
    if (condition == null) condition = c => c.CellType;

    switch (condition(cell))
    {
        case CellType.String:
            return cell.StringCellValue;
        case CellType.Formula:
            if (cell.CachedFormulaResultType == CellType.Formula)
            {
                eval = eval ?? cell.Sheet.Workbook.GetCreationHelper().CreateFormulaEvaluator();
                eval.EvaluateInCell(cell);
            }
            return GetValueString(cell, eval, c => c.CachedFormulaResultType);
        case CellType.Numeric:
            if (IsBuildinDateFormat(cell.CellStyle.DataFormat))
            {
                return cell.DateCellValue.ToString(GetBuildinFormatString(cell.CellStyle));
            }
            return cell.NumericCellValue.ToString();
        case CellType.Boolean:
            return cell.BooleanCellValue.ToString();
        default:
            return default;
    }
}

string GetBuildinFormatString(ICellStyle style)
{
    switch (style.DataFormat)
    {
        case 14: return "yyyy/M/d"; //yyyy/m/d
        case 22: return "yyyy/M/d H:mm"; //yyyy/m/d h:mm
        case 55: return "yyyy年M月"; //yyyy年m月
        case 56: return "M月d日"; //m月d日
        case 57: return "yyyy.M.d"; //[$-411]ge.m.d
        case 58: return "yyyy年M月d日"; //[$-411]ggge年m月d日
     default: return style.GetDataFormatString();
    }
}


bool IsBuildinDateFormat(int formatIndex)
    => (55 <= formatIndex && formatIndex <= 58) || DateUtil.IsInternalDateFormat(formatIndex);

xlsxの入力値を全部参照

上記、メソッドを用意したうえで、LINQPad上で次の通り実行すると、
指定xlsx内に記載された値が文字列として表示される。

void Main()
{
    var path = @"D:\test.xlsx";
    var book = LoadXSSFWorkBook(path);
    foreach (IRow row in book.GetSheetAt(0))
        foreach (var cell in row)
        {
            GetValueString(cell).Dump();
        }
}

困ったときは

DotNetCore.NPOIはソースコードGithub*1にあるので、素直にそれを読みましょう。
また、基本的に考え方やAPI設計が同様のため、オリジナルのJava版や多言語での例や実装を参考にすることも可能です。

技術書典5に参加してきた

技術書典5に行ってきた

2018/10/08(月)に開催された「技術書典5」に「でがらし会」の一員として参加してきました。
今回も例のごとく事前告知とかしないまま本番を迎えることになりました。 ちょっと遅いですが、参加レポです。

techbookfest.org

目次

お品書き

今回は新刊(でがらし BOOK vol.3)と既刊2冊の構成でした!!
諸事情で、新刊は電子版のみのため、DLカードを用意して参加しました。

私は、「DB管理ツールと作った話」という題で、FAManagementStudioというツールを作成するにあたって考えてたことや、ツールの紹介などを載せました。
ようやく書けた(Blog記事にもしていない)という思いもあるので、FAManagementStudioをVer3に向けて改修していきたいと思います。

反省

何はともあれ、新刊が1部しか頒布できなかったことですね('ω')
技術書典が終わってから色々な参加報告を読んでいたのですが、「全然売れなかった!」というところでも、「めっちゃ出てるやん。。」という気持ちが芽生えてきました(笑) *1

内容自体が手に取って下さった方の興味に添わなかったということもありますが、何より、当日のディスプレイの仕方や見せ方が悪かったり、呼び込みができてなかったなぁと思います。*2
沢山売りたいという気持ちは(個人的には)薄いのですが、もっと刺さる人にちゃんとリーチさせる努力をしないとだなぁと反省してます。

メンバー内でもちょいとゴタゴタしてて*3、全体的に準備不足で当日を迎えたのは良くなかったですね!*4

Re:Viewを使ってみて

今回メンバーの提案により、Re:Viewを使って執筆活動しました。
前回まではMarkdownで書いていたのですが、Re:View記法にも特に難なく移行できたと思います。

一方で、初めてだったので、組版を変えたいのだがどうすれば?とかは結構あって、文章のほうを工夫することで逃げた部分もあります(笑)
.styとかを含めて、全体的な仕組みが分かってきた頃には、今から全体を直すのはちょっとやめようという時期になってました。

ビルドについては、編集長役のメンバーのマシンでやることにしました。
ただ、手元で確認したいことも多いので、Dockerfile書いて、ワンポチScriptを用意しました。
vvakameさんが公開されているdockerイメージを使わせて貰いました。便利。

github.com

当日の感想

「でがらし会」は第2回の技術書典にも参加していたのですが、とにかく人の量と流れがすごかったです。
盛り上がってるなぁというのと、こんなに技術に興味ある人がいるのか!と驚きの気持ちが大きかったです。

ただ薄着で会場に行ってしまったので、会場が寒かったです。*5

最後に

競争率が高かったこと、参加サークルさんの本のレベルの高さを考えると、新刊1冊しか頒布できないサークルで正直すまんかった。という気持ちはありますが、参加できたのは単純に楽しかったし、よかったです。
内容がニッチすぎる感はあるのですが、手に取ってくださる方も沢山いたので、つい欲しくなる熱意をそこにつぎ込んでいかないとな!と思います。

ちなみに今回の本のおすすめは「あなたの成果は資産なのか費用なのか」です。
(↓この人が書いてます)

hutoncallsme.azurewebsites.net

電子版で作ってるのに、DL販売の予定は今のところないです。*6
冬コミか次の機会には、少し改定が入った上で製本する予定ですので、その際はどうぞよろしくお願いいたします!!

*1:事前想定が全然違うのでそりゃそうなんですが

*2:お隣さんは上手だった

*3:エモいやり取りがあったとメンバー間で評判に

*4:直前まで、事前見本誌提出期限を把握してなかったとかwww

*5:人が多すぎて、空調入れて換気しないとやばい。。ということだったようです

*6:そういうとこだぞ

C92に参加してきました

C92に参加してきました

8/11(金)に開催されたC92に「でがらし会」の一員として参加してきました。
事前に告知とかすればよかったのですが、ちょっと余裕がなくて何もしてませんorz

目次

お品書き

今回は新刊(DEGARASHI BOOK vol.2)と既刊2冊の構成で参加しました。
私は新刊用にFbExternalEngineの解説記事を書いて掲載しています。
C91に続いてFirebirdネタです。
本文にも書いていますが、おそらく世界初のFbExternalEngineの解説本です!!!!
それくらい興味持つ人が少ないということでもありますが。。

f:id:kowill:20170814003107j:plain:w300

反省

C91に続いて2回目の参加ということもあって、慣れが出てきたのでしょうか。
スケジュールがボロボロに(苦笑)
GitHubで原稿を管理しているのですが、7月末になってようやく本格化する感じでした。(反省)
※switchとイカを入手していたらどうなっていたことやら。

FbExternalEngineにもちょっとした更新が入り、ValueTapleじゃないとだめになったりと
てんやわんやした感じですね。

当日の感想

売り子として当日現地にいたのですが、興味も持っていただける方、購入される方、お話した方、
色々なタイプの方がいて楽しかったのです。
「でがらし会」のブースだと、HoloRenseに興味持つ人が多かった印象です。
やはり最新ガジェットは強い!!
また、C91や技術書典で素通りされてた方が興味持ってくれたりすると、ちょっと嬉しいですね。

いかに興味持ってもらえるかも考えないといけないなと感じました。
特に表紙とかww

最後に

技術系同人誌の作成は意外と楽しいです。原稿書いている時は辛いですけどね。
C93へも参加申し込みするようなので、当選すればまたFirebirdネタでも乗せようと思います。
自分の記事を読んでFirebirdユーザーが増えるのかは分かりませんが(笑)
仕事でFirebird使わなくなってしまったので、ネタ探しが続くかは不明ですが、 細々と頑張っていきます( ˘ω˘ )

WPF / DataGridでデータがNullの場合の表示について

WPFのDataGridでCellにバインドされているデータがNullの場合に、あれこれ表示を切り替えたかったのですが、手間取ったのでメモしておきます。

曲者はこいつでした。

AutoGenerateColumns="True"

目次

実現したいこと

WPF + DataGrid でバインドしているデータがNullの場合に、適当な文字列を表示し、フォント色を変えたい。

何も考えずにDataGridにデータバインドした場合、CellにNullが入ると該当セルは空白表示になります。
それでも問題ない場合は良いのですが、特に空文字とNullを判別したい場合に困ります。

解決策

AutoGenerateColumns="False" の場合

こちらはちょっと検索すれば似たような事例がたくさん出てくると思います。
データがNullの場合に特定の文字を出すには、「TargetNullValue」プロパティに値を入れておきましょう。
文字色を変えるには、ElementStyleにDataTrrigerをセットしておけば解決です。

f:id:kowill:20170606011930p:plain

<DataGrid Width="250" ItemsSource="{Binding View1}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Id" Binding="{Binding Id}"/>
        <DataGridTextColumn Header="Name" Binding="{Binding Name, TargetNullValue='Nullだよーー'}">
            <DataGridTextColumn.ElementStyle>
                <Style TargetType="TextBlock">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Name}" Value="{x:Null}">
                            <Setter Property="Foreground" Value="Red"></Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </DataGridTextColumn.ElementStyle>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

AutoGenerateColumns="True" の場合

最初から表示するデータが固定で決まっている場合は上記の方法で何の問題もないです。
しかし、表示するデータが動的に変わる場合は多いですし、適当なDataTableをポンと渡せばそれなりに表示してくれるのがDataGridの良いところです。*1

AutoGenerateColumnsをTrueにする場合、XAML側で指定する方法はなさそうなので、
動的に解決してあげる必要があります。

今回は、AutoGeneratingColumnイベントを実装したBehaviorで対処する方法をとりました。

public class NullValueBehavior: Behavior<DataGrid>
{
    public NullValueBehavior() { }
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.AutoGeneratingColumn += AssociatedObject_AutoGeneratingColumn;
    }
    private const string NullString = "Nullだよーー";
    private void AssociatedObject_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        var propertyName = e.Column.Header.ToString();
        ((DataGridBoundColumn)e.Column).Binding.TargetNullValue = NullString;

        if (e.Column is DataGridTextColumn column)
        {
            var style = new Style(typeof(TextBlock));
            var trigger = new DataTrigger { Binding = new Binding(propertyName), Value = null };
            trigger.Setters.Add(new Setter(TextBlock.ForegroundProperty, new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Blue)));
            style.Triggers.Add(trigger);
            column.ElementStyle = style;
        }
    }
    protected override void OnDetaching()
    {
        AssociatedObject.AutoGeneratingColumn -= AssociatedObject_AutoGeneratingColumn;
        base.OnDetaching();
    }
}

みたいなのを作っておけばあとは、XAMLにBehavior関連付けるだけ。
f:id:kowill:20170606011934p:plain

<DataGrid Width="250" ItemsSource="{Binding View}" AutoGenerateColumns="True" CanUserAddRows="False" CanUserDeleteRows="False">
    <i:Interaction.Behaviors>
        <local:NullValueBehavior />
    </i:Interaction.Behaviors>
</DataGrid>

最後に

良いやり方はどうかはいまだに自信がありませんが、うまいこと動いてくれています。
WPF難しいですね。ハイ。。

*1:暴論