ぽにょろん

思いついたこととメモ

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:暴論

2016年の振り返りなど

特に恒例でもないですが、折角なので振り返りエントリでも。
すっかり幾日か経ってはいますが。

目次

2016年

個人的に色んな経験が出来た年だったと思います。

半年以上無職な生活を送りまして、怠惰な毎日を楽しく過ごしてました。
その間、色んな本を読んだり、プログラムを書いたり、ゲーム漬けになったりしてました。

2015年から活動を始めたでがらし会でも、定期的な勉強会のほかにFAMを作ったり、C91に向けて
同人誌を作成・頒布するという経験もできました。
Firebirdのアドカレにも一本記事を載せれたのはよかったです。
本当はもう何本か書くつもりではいたのですが。。

夏頃から転職活動を始め、無事、ASP.NET MVCを書くお仕事にありつけています。
Firebirdは仕事で全く使わなくなりました!
これまでとは全く違う環境になったので、新しい発見があってとても刺激的です。

プライベートでも色んな出来事があり、色々と考えさせられたので、本当すごい一年だったなぁ
という気がします。

ブログ

年間で17エントリなので、前年よりも少ないですねw
仕事していると色んなネタが転がり込んでくる一方で、自分でネタを発見するって結構大変だなと
思った次第です。
FAMというアプリを作っていたこともあって、Firebird成分多めでしたね。

今年はどれくらいネタ見つけられるかなぁ。

2017年

あまり気負わず、今やれていることは継続しつつも新しいことに挑戦していきたいと思います。

とりあえずは、少しフロントエンドをかじろう。
それと最近、色々と思うところもあるので、ポエムでもブログにのせてみようかと考え中。

C#Firebirdネタは引き続き続けていこうと思っているので、よろしくお願いします!

C91でFirebird Embeddedの記事を寄稿した本が出ます

気が付いたら本を作ることになっていたので、その宣伝です。

目次

同人誌作りました

昨年から、栃木県宇都宮市を中心に活動する「でがらし会」なるものに参加しているのですが、
C91で同人誌を出すことになりました。

本を作るのが初めてなメンバーがほとんどで、四苦八苦しながらもなんとか製本までこぎつけました!
Rasbery Pi、Xamarin、Firebird、ソフトウェアデザインというラインナップになっています。

私は記事を一本と、表紙の編集等を担当しました。

私の記事

例のごとく、Firebird Embeddedの入門記事を書いています。
サンプルコードもC#で載せています。
需要はほとんど無いはずですが、何らかの気の迷いで本を手に取ってくださった方が、
興味持ってくれると良いなと思っています。

1日目西み23a

1日目西み23aが頒布場所となります。私も当日ブースにいる予定です。
本は1冊だけです。
おまけとして小冊子もあるのですが、個人的にはこいつがおすすめです(笑)

ここを見てくださる方の中で興味ある方がいれば、ぜひ当日おこし下さい!

C#でFirebirdのストアドを書く

本エントリは、Firebird AdventCalender 2016 の14日目です。

今回は、Firebirdの.NET ProviderのコミッターであるJiri Cincura氏がブログで公開している、FbNetExternalEngineについて書きます。

目次

FbNetExternalEngineってなに?

簡単に言うと、C#Firebirdのストアドがかけるようなpluginです。

Firebird3では新しいplugin機構が追加され、ストアドや関数、トリガー等がFirebirdの内部エンジン以外で動作できるようになりました。
基本的にはC++でpluginを書くことが想定されているようです。
そんな中Jiri Cincura氏が、C#用のplugin機構を仮実装したのが「FbNetExternalEngine」です。
ちなみに、procedureだけの対応です。
もっともお試し実装なので、Productionでは使えません。

Early (very) preview of stored procedures (functions, triggers) in .NET in Firebird | Jiří {x2} Činčura

簡単な例

実際にどんな風になるかというと。
こんなC#の関数を用意して、

public static IEnumerator<Tuple<string>> HellowWorld(string name)
{
    yield return new Tuple<string>($"Hello World!!, Hello {name}!!");
}

SQL投げると、書いた処理が返ってくる。

f:id:kowill:20161211015954p:plain

実装

Firebird Embeddedで実際に試してみた。

検証環境

  • C# (.NET 4.6.1)
  • Firebird .NET Data Provider 5.6.0
  • Firebird 3.0.0 (Embedded)
    Firebirdのバージョンが古いですが、手元にあったのでそのままw

Pluginの作成

ポイントは以下の通り。

  • static クラス
  • static メソッド
  • 戻り値はIEnumerator
    IEnumerableではありません!私はよく間違えて動かない (゚A゚;)ってなってます。
  • 引数は必ず用意する
    引数無しだと上手く動かせなかった...
  • 引数/戻り値がPrimitive&Value Typeの場合死ぬので、Nullableにする

こう見ると、割と制限が多い。

サンプル

namespace FbExternalSample
{
    public static class Procedures
    {
        public static IEnumerator<Tuple<string>> HellowWorld(string name)
        {
            yield return new Tuple<string>($"Hello World!!, Hello {name}!!");
        }

        public static IEnumerator<Tuple<int?>> GetNumbers(int? count)
        {
            return Enumerable.Range(0, count.Value).Select(x => Tuple.Create((int?)x)).GetEnumerator();
        }

        public static IEnumerator<Tuple<int?, string>> GetDemo(string txt)
        {
            yield return new Tuple<int?, string>(1, "sample1");
            yield return new Tuple<int?, string>(2, "sample2");
            yield return new Tuple<int?, string>(3, $"{txt}");
            yield return new Tuple<int?, string>(4, $"Done!!");
        }
    }
}

Pluginを呼ぶほう

まずは普通にEmbeddedが動く環境整備

適当に必要なDLLを配置する準備だけしておきます。
f:id:kowill:20161211015957p:plain
よくわからなければ、過去記事を参照。

ponyoth.hateblo.jp

FbNetExternalEngineを配置

下記ブログ記事から、必要なアセンブリを取得します。

External procedures in Firebird in .NET done(-ish) | Jiří {x2} Činčura

ちなみに、7zで圧縮されてます。解凍して、中身をpluginsフォルダに突っ込みます。 f:id:kowill:20161211015959p:plain

plugins.confの作成

Firebirdのplugin設定を変更するために、plugins.confを配置又は作成します。 f:id:kowill:20161211020001p:plain
中身はこんな感じで。

Plugin = FBNETEXTERNALENGINE {
    Module = $(dir_plugins)/FbNetExternalEnginePlugin
    Config = FBNETEXTERNALENGINE_config
}

Config = FBNETEXTERNALENGINE_config {
}

DBにprocedure作成しておく

FirebirdのDBファイルを適当に作成して、そこにprocedureのcreate文を流し込みます。

recreate procedure HellowWorld (in_name varchar(300))  
  returns (out_0 varchar(300))   
  external name 'FbExternalSample!FbExternalSample.Procedures.HellowWorld'  
  engine FbNetExternalEngine;  
recreate procedure GetNumbers (in_count integer)  
  returns (out_0 integer)  
  external name 'FbExternalSample!FbExternalSample.Procedures.GetNumbers'  
  engine FbNetExternalEngine; 
recreate procedure GetDemo (in_txt varchar(300))  
  returns (out_0 integer,out_1 varchar(300))   
  external name 'FbExternalSample!FbExternalSample.Procedures.GetDemo'  
  engine FbNetExternalEngine; 

作成したPluginを配置する

上記で作成した、PluginのDLLもFirebirdアセンブリがあるPluginsフォルダにぶち込みます。
f:id:kowill:20161211021008p:plain

実行

あとは、SQLを実行するだけ。
結構雑なコードですけど、こんな感じで確認すると、

var sqls = new[] { "SELECT * FROM HellowWorld('Taro')",
                   "SELECT * FROM GetNumbers(5)",
                   "SELECT * FROM GetDemo('やったぜ!')" };

using (var con = new FbConnection(builder.ConnectionString))
using (var command = con.CreateCommand())
{
    con.Open();
    foreach (var sql in sqls)
    {
        Console.WriteLine("- SQL -");
        Console.WriteLine(sql);
        Console.WriteLine("- 実行結果 -");

        command.CommandText = sql;
        var reader = command.ExecuteReader();
        while (reader.Read())
        {
            var result = "";
            for (var i = 0; i < reader.FieldCount; i++)
            {
                result += $" {reader[i]}";
            }
            Console.WriteLine(result);
        }
        Console.WriteLine("------------------------------------");
    }
}

こんな結果になります。
f:id:kowill:20161211021715p:plain

最後に

Jiri Cincura氏のブログ読んで貰えればわかりますが、実際はまだまだ試作段階です。
制限も多いし手順も割と面倒ではありますが、まぁ、とりあえずできるよ!という状況です。
とはいえ、どれくらい存在するのかわからない、Firebird + C#erには嬉しいので、色々と試してみてはいかがでしょうか。

記事を書くのに殴り書いたサンプルも置いておきます。
Sample/FbExternalSample at master · kowill/Sample · GitHub

HTML5 CONFERENCE 2016 に参加してきた

HTML5 CONFERENCE 2016 に参加してきました。
参加時と後から録画をちらっと見たメモを記載しておきます。

公式HP
events.html5j.org

YouTube - ライブ配信録画 www.youtube.com

目次

基調講演

中村さん

  • Webという名の Distributed Operating System
    • インターネットで世界中のコンピュータが繋がってる
    • Webページは世界中のドキュメント
    • みんなで分散システムを作ってるみたいなもん
  • Block Chain に関するワークショップ
    分散システムにおけるTrustをどうやって作るか
    参加者の合意でTrust作れるんじゃないの?という可能性

及川さん

  • 実はIE7担当してました
  • HTML5というキーワードはもういいんじゃないの? → Webそのもの
  • バイルWebはUI/UXが悪いことが多くなってきた
    キュレーションアプリとかが流行ってきた理由の一つでもある
  • ProgressiveWebApp
    よりアプリ化されたWeb
  • バイルWebの課題対応について
    Accelerated Mobile Pages
  • Webは何度も死んでるw
    が、生き返る
    medium.com

ブラウザ用のCPUをつくるよ!WebAssemblyで

  • WebAssemlby は WASM VM上で動くよ
  • WebAssembly の Working Grop Mozzila, Microsoft, Google, Apple の各社から人が来てる
  • asm.js と比較すると WebAssembly の方が Binary Size が小さい
  • WASM はコンパイルの生成物
    Nativeではなく、parsingが終わってるだけ。
    ASTのバイト表現。
  • まだまだこれからの技術

資料
speakerdeck.com

Webのグラフィックス前編: WebGL事例、パフォーマンス

Webのグラフィックス2016

  • WebGLを実務で使ってる人 → 昨年より増えている感じ。(参加者の)
  • 3D、難しい、大規模コンテンツ というイメージが強い 2Dでも使えるし、Three.js等を使うことで、割と簡単にできる
  • 表現を広げる選択肢になるうる
  • 小さいコンテンツから始めると良い
    ものによってはCSS等で頑張るよりも、簡単なケースもある

資料

www.slideshare.net

WebGL最適化の勘所

  • GPU処理のおさらい
  • ボトルネックになりやすい場所
    JS & WebGLドライバ パフォーマンスチェックして最適化しよう
  • まずはCPUボトルネックを疑おう Windows版のChrome等では、WebGL実装がDirectXに命令に変換してたりするのもある
  • WebGLプロファイラ
    • WebGL専用ではないけど、おすすめ
      tracing-framework by Google
    • プロファイラ使ってるときは、とうぜんその負荷もプラスされるので、相対値で考える
  • WebGL独自のTips
    gl.TRIANGLE_FANは避ける
    頂点アトリビュートは4byte境界に揃える
    etc...
  • ライブラリ使ってるときは?
    WebGL命令が隠蔽される とにかくドキュメント読め!
    stackoverflowとかでノウハウ集めよう → 有名なライブラリ使ったほうが楽

資料
WebGL最適化の勘所

Webのグラフィックス後編: WebGL2、そしてWebVR

WebGL 2.0

  • 1.0と比較して、劇的進化しているわけではない
    普段から WebGL をぺろぺろしてないと、そこまで恩恵はないかも
  • WebGL 2.0 がきたら
    • より 美しく現実的/高速で効率的 なレンダリングができる
    • リソースやデータ方が柔軟になる
    • テクスチャの画像データサイズが2の累乗でなくてもよくなる
  • 既存のコンテンツはそのままでも大丈夫 実装が別
  • 学習が難しくなるか?
    そもそも3DCG全般ムツカシイ
    日本語情報はほぼない

資料
WebGL 2.0 現実的な影響は?

そしてWebVR

  • WebVRとは
    VR体験のためのデバイスやセンサーから情報取得するAPIのこと
    → 3Dのコンテンツ部分はWebGLで作る
  • まだサポートブラウザがほぼない
    これからの技術

資料

www.slideshare.net

Webパフォーマンス最前線

  • Webがアプリのように振る舞うことが求められている
    Progressive Web Apps
  • パフォーマンスに対する優先度
    • Webサイト
      LoadやAnimationが重要
    • Progressive Web Apps
      ResponseやAnimationが重要
  • GPUを活用しよう
    ex) translate, will-change -> 常時使うと電池とかに悪影響なので、上手く使おう
  • 通信量を気にするところから、UIを早くしようという方向にきてる

最後に

所用で最後までは参加せずに帰ったのですが、とても勉強になりました。
率直に思ったのは以下の通り。

WebページがどんどんNativeっぽくなってる側面は確かにあるなぁというのが実感。
あと、俺、Webの知識が足りないなぁ。というのも。頑張ります。

FAManagementStudioなるものを作ってます

以前からFirebird Embedded用のGUIツールを作成、公開しています。
名前は「FAManagementStudio」です。

github.com

その宣伝記事になります。

目次

背景

Firebird Embeddedを以前から使っていたのですが、個人的に使い勝手の良いGUIツールにめぐり合わなかったので、簡易なものを作ってみました。というのが背景です。
定番ツールのFlameRobinも悪くないのですが、ちょっと馴染めなかった。
ちなみにWindows環境のみ対応。
Firebird2.5/3の両方で使用可。

ツールの概要

とにかくお手軽に使えるようにという方針です。
DBファイルをドロップ、SQLを実行(F5)、結果を確認するといったイメージです。 f:id:kowill:20160804221908p:plain

詳しい使い方

GithubWikiを少しづつメンテしているので、そちらを参照ください。*1

Home · degarashi0913/FAManagementStudio Wiki · GitHub

開発環境

VS2015 + C# + WPF です。
Ver1の時は.NET3.5で作成していましたが、Ver2で.NET4.5に変更しました。

最後に

Firebird Embeddedを使っている方がいれば、ぜひ一度試してみてください。
まだまだ機能が足りなかったり、バギーだったりしますがその際はFB、PRお待ちしております。

*1:主に他のメンバーが

WPFでRadioButtonにEnumをBindする

目次

環境

  • C# + WPF
  • .NET Framwork4.5.2

実現したいこと

RadioButtonにViewModelのプロパティ値(Enum)をBindする。

実装の方針

  • 各RadioButtonのIsCheckedにEmun値をバインドさせる。
  • Converterを用意して、Bool値に変換する。

実装

Converter

検索すると色々な方法があるみたいですが、簡単にこんな感じで作ってみました。

    [ValueConversion(typeof(Enum), typeof(bool))]
    public class RadioButtonConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || parameter == null) return false;
            return value.ToString() == parameter.ToString();
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || parameter == null) return Binding.DoNothing;
            if ((bool)value)
            {
                return Enum.Parse(targetType, parameter.ToString());
            }
            return Binding.DoNothing;
        }
    }

Bindingしない時は Binding.DoNothing を使うようです。

Binding.DoNothing フィールド (System.Windows.Data)

VMEnum

VMEnumの定義は適当に。

    public class ViewModel
    {
        public TestEnum TestKind { get; set; } = TestEnum.Test4;
    }
    public enum TestEnum { Test1, Test2, Test3, Test4 }

View

RadioButtonはGroupNameを付けておく。
ConverterParameterを指定する。

<Window x:Class="WpfRadioButtonSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfRadioButtonSample"
        mc:Ignorable="d"
        Title="MainWindow" Height="100" Width="100">
    <Window.Resources>
        <local:RadioButtonConverter x:Key="EnumConverter" />
    </Window.Resources>
    <StackPanel>
        <RadioButton GroupName="Test" Content="Test1" IsChecked="{Binding TestKind, ConverterParameter=Test1, Converter={StaticResource EnumConverter}}"/>
        <RadioButton GroupName="Test" Content="Test2" IsChecked="{Binding TestKind, ConverterParameter=Test2, Converter={StaticResource EnumConverter}}"/>
        <RadioButton GroupName="Test" Content="Test3" IsChecked="{Binding TestKind, ConverterParameter=Test3, Converter={StaticResource EnumConverter}}"/>
        <RadioButton GroupName="Test" Content="Test4" IsChecked="{Binding TestKind, ConverterParameter=Test4, Converter={StaticResource EnumConverter}}"/>
    </StackPanel>
</Window>

ちょっとばかし嵌ってしまったので、メモ。