さびぬきのネタ

Rubyをよく使います。最近はC#もやや使います。C言語も少し使います。Reactは勉強中です。

SKBitmapをbindingさせるならSkiaImageViewを使おう

WPFで作成(読込み)したBitmap形式のデータをViewとbindingしてUIに表示しよう😁 さて、System.Drawing.Commonを使って、、おや?

learn.microsoft.com

推奨アクション これらの APIクロスプラットフォーム アプリで使用するには、次のいずれかのライブラリに移行してください。 SkiaSharp Microsoft.Maui.Graphics

なるほど。。 クロスプラットフォームアプリを開発するわけじゃないけど、SkiaSharpが気になるので触ってみよう。

View

色々省略
<Image x:Name="hogebitmap" Source="{Binding HogeBitmap}" Stretch="UniformToFill" />

ViewModel

色々省略
public SKBitmap HogeBitmap { get; set; }

うーん。View側にSKBitmapの画像が表示されないぞ。🤔 SKBitmapから別の型に変換しないといけないのかな?🤔🤔

と思っていたら、SkiaImageViewというライブラリがありました! SkiaImageViewを使えばSKBitmap型のプロパティをbindingすればUIに表示されるとのこと。

github.com

今回はWindows10 Visual Studio 2022 + WPFで開発していたので、SkiaImageView.WPFをNuGetからインストールしました。

Viewのxamlファイルにxmlns:siv="https://github.com/kekyo/SkiaImageView"を追加して、siv:SKImageViewタグを使うとbindingできました!

<UserControl
    (色々省略)
    xmlns:siv="https://github.com/kekyo/SkiaImageView">
    <Grid>
        <siv:SKImageView Source="{Binding HogeBitmap}" Stretch="Uniform" />
    </Grid>
</UserControl>

今回は真っ黒なBitmapイメージを生成していたので、↓の画像が表示されました。

binding
bindingした画像

ピクセルでBitmapを生成する場合は、下記の記事が参考になります。 他にもSkiaSharpを使用した記事がサイドメニューから参照できます。

learn.microsoft.com

Docker環境でContact Form 7の自動生成pタグを消す

WordPress+Dockerでとあるページを作成していました。 お問い合わせ用のフォームを作成するにはContact Form 7というプラグインを利用すると良さそうなので使ってみました。

Contact Form 7をインストールして有効化したあとにサイドメニューに表示されるお問い合わせからお問い合せページを新規追加してみます。

Bootstrapを使用して適当にformを作ってみました。

<div class="row">
<label for="your-name" class="col-sm-2 col-form-label">氏名</label>
<div class="col-sm-10">[text* your-name]</div>
</div>

<div class="row">
<label for="your-email" class="col-sm-2 col-form-label">メールアドレス</label>
<div class="col-sm-10">[email* your-email]</div>
</div>

<div class="row">
<label for="your-message" class="col-sm-2 col-form-label">お問い合わせ内容</label>
<div class="col-sm-10">[textarea* your-message]</div>
</div>

<div class="col-auto">
[submit "送信"]
</div>

↓こんなformになりました。 contact-form

あら、labelとinputが横並びになっていない。。DOMを確認してみると謎のpタグが生成されている。。

dom-img

Contact Form 7の公式ページを見てみると下記の記載がありました。

WPCF7_AUTOP

この定数の値が false のとき(デフォルト: true)、Contact Form 7 はフォームの内容に “autop” フィルタを適用しません。”autop” フィルタは連続した改行をパラグラフ要素に置き換えます。

contactform7.com

なるほど、wp-config.phpdefine ('WPCF7_AUTOP', false);を設定すれば良さそうですね。 ただ個人的にこの辺のファイル編集はできれば避けたい&Gitで管理できた方が何かと安心なので、別の方法で対応しようと思います。

今回はDockerを使用しているので、docker-compose.ymlからWordPress環境変数を設定して対応します。

version: '3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: hogeroot
       MYSQL_DATABASE: hoge
       MYSQL_USER: hoge
       MYSQL_PASSWORD: hoge

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: fuga
       WORDPRESS_DB_PASSWORD: fuga
       WORDPRESS_CONFIG_EXTRA: "define ('WPCF7_AUTOP', false);"
     volumes:
       - ./themes/hoge:/var/www/html/wp-content/themes/fuga
volumes:
    db_data:

WORDPRESS_CONFIG_EXTRA: "define ('WPCF7_AUTOP', false);"の一行をdocker-compose.ymlに追加してみました。

composeの更新を反映させると...labelとinputが横並びになりました! contact-form-new

不要なpタグはちゃんと消えてますね。 no-p-dom

ScottPlotはデータバインディングでのMVVMパターンをサポートしていない

WPFで円グラフで表示したかったのでチャート作成ライブラリのScottPlotを使ってみたところ、MVVMのデータバインディングはサポートしていないようでした。

github.com

Githubで公開されているScottPlot Demoアプリでは、サンプルコードが記載されていますが、 データバインディングしてるサンプルがない。。

試しにContentでバインディングしてみたりと試行錯誤するが、円グラフが描画できない。

ScottPlotの公式ドキュメントを見ていたらFAQにMVVM and Data Bindingの記事がありました。 scottplot.net

原文から一部を抜粋

Many charting libraries use MVVM and data binding patterns to interact with plots. ScottPlot does not. This intentional decision allows ScottPlot to be more performant by giving the user raw access to array values used for plotting, and also enabling users to have total control over when new frames are rendered (a potentially costly operation).

Google翻訳すると↓

多くのチャートライブラリは、MVVMとデータバインディングパターンを使用してプロットと対話します。 ScottPlotはそうではありません。 この意図的な決定により、ScottPlotは、プロットに使用される配列値へのrawアクセスをユーザーに提供し、新しいフレームがレンダリングされるタイミングをユーザーが完全に制御できるようにすることで、パフォーマンスを向上させることができます(コストがかかる可能性のある操作)。

パフォーマンスを向上させるためにデータバインディングをサポートしない方針でした。

FAQの原文に記載されている通り、WPFでScottPlotを使用する場合は個別にユーザーコントロールを作成して、ウィンドウから呼び出すように使用するのがよさそうですね。

WPFでコンボボックスの値をenumでBindingするMarkup拡張を実装する

WPFでUIの表示切替のモードを選択するコンボボックスを作成するときに、コンボボックスの値をenumで定義できれば、内部処理の条件判定などがいい感じになるのでは?と思ったので色々と調べてみたところ、PrismのコミッターであるBrian Lagunasさんの解説動画がYoutubeにありました。 マークアップ拡張について、何となくの理解でいると応用が利かなそうなので、細かいところを勉強用にメモを残します。

Brian Lagunasさんの解説動画

詳しい解説やソースコードはBrian Lagunasさんの動画を参照してください。

  1. enumとコンボボックスのバインドについて解説されています。
    https://www.youtube.com/watch?v=Bp5LFXjwtQ0

  2. enumローカライズについて解説されています。
    https://www.youtube.com/watch?v=T1mhORJCDsY

enumとコンボボックスをBindingする

解説動画1では、MarkupExtensionを継承したEnumBindingSourceExtensionクラスを作成しています。 MarkupExtensionを継承したクラスはサフィックスにExtensionを付けるとExtensionを除いた名前で参照することができます。 そのため、解説動画ではxamlからはEnumBindingSourceで参照されています。

<ComboBox ItemSource="{Binding Source={local:EnumBindingSource {x:Type local:Status}}}"/>

EnumBindingSourceExtensionクラスのコンストラクタに引数としてx:Type local:StatusでStatusの型を渡しています。

MarkupExtensionクラスにはターゲットプロパティ値を返却するProvideValueメソッドが定義されています。 ProvideValueメソッドは抽象メソッドなので、派生クラスでは処理の実装を定義しないといけません。今回はenumの値すべてをコンボボックスの要素としてバインドしたいので、Enum.GetValues(EnumType)を返却しています。

public override object ProvideValue(IServiceProvider serviceProvider)
{
    return Enum.GetValues(EnumType);
}

解説動画1の内容では、enumの値がそのままコンボボックスの選択肢に表示されます。

↓のenumの型をEnumBindingSourceExtensionのコンストラクタに引数で渡すとコンボボックスの選択肢はHoge Fuga HogeFugaになります。

public enum Hoge
{
    Hoge,
    Fuga,
    HogeFuga,
}

Description属性からコンボボックスに表示する文字を設定する

解説動画1では、enumの値がそのままコンボボックスに選択肢で表示されます。 このままでも利用はできますが、正直使い勝手は悪いです。おそらく日本語の選択肢で表示できる方が何かと都合がよさそうですよね。

解説動画2では、enumに定義したDescription属性の文字列をコンボボックスに表示する処理を実装しています。

↓の例だとコンボボックスの選択肢がほげ ふが ほげふがになります。

[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum Hoge
{
    [Description("ほげ")]
    Hoge,
    [Description("ふが")]
    Fuga,
    [Description("ほげふが")]
    HogeFuga,
}

enumの値をDescriptionに定義されている文字列で返却したいので、enumからstringに値の返却時に型を変換しないといけません。

今回はstring型に変換するEnumDescriptionTypeConverterクラスを作成しています。 EnumDescriptionTypeConverterクラスはTypeConverter属性からenumの値を変換するため、EnumConverterクラスを継承しています。 ConvertToメソッドは対象のvalueオブジェクトを任意の型に変換するメソッドです。 enum値のFieldInfoを参照して、DescriptionAttribute(Description属性)があればその値をstringに変換して返却して、DescriptionAttributeが取得できなかった場合はenum値をstringに変換して返却しています。

まとめ

viewとenumなどを紐づける場合は下記の実装プロセスになる。

MarkupExtensionの機能によるviewとenum値をバインドするenumにDescription属性で値に文字列のメタ情報を付与するTypeConverterを使用してenumの返却値をstring型に変換してDescription属性の文字列を返却する

WPFではこういった拡張を実装することが多いですよね。