WPFでColorPickerを作った話(前編)

はじめに

こんにちは。山本れきです。
開発の実装の段階になると、オープンソース全盛のこの時代においては巷にあるコンポーネントを拝借することは多いと思います。あるいは有償のコンポーネント製品を頼ることになるケースもあると思います。
しかし、それらは表現力が不足しているため採用できなかったり、必要な要求に対応できないことに気づくのもよくあることです。
となると自作、あるいはオープンソースコンポーネントの改良・カスタマイズするという選択肢が浮上してきます。

今回の記事では、自作コンポーネントを作る際に考慮した点、またそれをWPFのMVVM(Model-View-ViewModel)などのフレームワーク側のパターンに当てはめる際に気にかけたことなど、ColorPickerコンポーネントを開発するシーンで体験したことを扱います。
今回は実装よりも要求や仕様の方向から見ていきたいと思います。(すでにオープンソース化は済んでいますが、最新のソースコードの紹介と解説は後編となります。)

コンピューターにおける色とは

色を扱うコンポーネントを作るには、まず色を知らねばなりません。

色を表現する方法は幾つかありますが、私たちになじみ深い光の三原色RGB(レッド、グリーン、ブルー)で表現することが一般的です。RGBは簡単に言えば真っ暗なところに赤・青・緑の光源を配置することで色を加算して殆どの色を表現するものです。私たちが電子機器を使う際に見るモニターの多くはこの形式です。

他に、色の三原色と呼ばれるものがあります。これは白い(すべての色の光を反射できる)物体に対して、CMY(シアン、マゼンダ、イエロー)の塗料を塗ることで、物体をほとんどの色に塗り分けることができます。この3色はRGBのそれぞれと補色関係にあり、Cは赤を反射しない、Mは緑を反射しない、Yは青を反射しない色となり、印刷物ではこれを用いるシーンが多いでしょう。
なお印刷物に関して言えば黒を特別扱いしており、プリンターのインクなどがCMYKとなっているのはそのためです。

RGBとHSV

HSVを入力するコンポーネント

色の表現方法は他にもあり、HSV/HSB(色相(Hue)、彩度(Saturation)、明度(Value/Brightness))もその1つです。色相は、明度100%、彩度100%の場合の光の三原色とそのうち2色を用いた中間色で表現する「色相環」と呼ばれるものとなります。色を決めた後に彩度を下げると色は濁って灰色に近づきます。明度を下げると黒に近づいていきます。右図は私が作成したコントロールです。外側の環から色を選び、内側の三角形は左右が彩度、上下が明度です。
ここで、大切なことを覚えておいてください。HSVからRGBへの変換は必ず一意の値となりますが、RGBからHSVへの変換は一意の値とならない可能性があるということです。例えば、RBG=000(黒)はHSVにおいてH=不定、S=不定、V=0 となります。

このように色と一口に言っても色々な表現方法が存在します。

また、これら色そのものに加え「透過率」を設定できるようにする必要があるかもしれません。

要求を整理する

作り始める前に、このコンポーネントへの要求に着目する必要があります。
要求とはコンポーネントの主機能についてユーザーが必要としていることを整理したものです。
コンポーネントを開発するには要求をベースに確固たるモデルとユーザーインターフェースをデザインをする必要があります。

実際このColorPickerにはこれだけの要求がありました。

  • WPFのコントロールであること
    • イベントを発行できること
    • データバインディングできること
    • ICommandをバインディングできること
    • 単にコントロールとしてだけではなく、MVVMで扱うためのViewModelを提供していること
  • 色はRGBで扱い、WPFのColorを用いること(ただし内部的にRGB/Colorであることを要求しているものではない)
  •  全ての色を自由に表現可能であること
    • 正確なカラーコードをユーザーが把握できること
    • 透過率を扱うかは使用者が選べること
  • 簡素な選択と詳細な選択を切り替えられること
  • ユーザーが指定したカラーセットのテーブルを使用できること(使用しないこともできること)
  • なるべく流行(または今時できて当たり前)のUIを採用したい
    • 類似色や近似色などを選択できるようにしたい
    • デフォルトのカラーセットを用いて、代表的な色を選択できるようにしたい
    • 色相環+彩度+明度を選択するコントロールも欲しい
  • 過去に使用した色を記録して再利用できること
  • コンボボックスのように用いることも、静的に配置するコントロールとして用いることもできること
  • ユーザーから見て自然な挙動をすること

このように整然とした要求が上がってくることは実は滅多にありません。実際には「あれみたいなことがしたい」「こんな風だと嬉しい」という、見たことのあるものの組み合わせのようなものが要望として出てきました。そしてそれを整理したものが「要求」のリストとなります。

要求から仕様とモデルを起こすまで

よく見てみるUX(ユーザーエクスペリエンス)的な要求とシステム的な要求が混ざっていることに気づくでしょう。つまりコンポーネントへの要求はまず以下に区別されます。

  1. エンドユーザーのUXに影響するインターフェース
  2. コンポーネントを用いるエンジニアにとって必要なインターフェース

前者はGUIとしていかに優れた体験を提供するか。
後者はコンポーネントのモデルがどのようなソフトウェア的インターフェースを提供するか。
という情報になります。当然ですが、両方に影響する要求もあります。また、よく見てみるとコンポーネントの責務を超えた要求も混在しています。(例えば「過去に使用した色を記録して再利用できること」とは、いつまでのものでしょうか。「履歴はアプリケーション内全体で共有なのか、画面ごとに違うものを保持するのか。これらをコンポーネントが定めてしまってよいのか」という問題があります。こういった部分は、拡張性のあるインターフェースを提供するにとどめるのが良いはずです。)

はっきりしているのは、コンポーネントというものは基本的に最も高い表現力(汎用性・自由度・拡張性)を持つことを目指すべきであり、そこにビジネスロジックやUXに影響する制約や機能を付加するのは使用する側の責務ということです。

そのため、要求の中にある曖昧性を排除し、責務をはっきりさせていく必要があります。

UXの観点から仕様を精査する

WPFのコントロールとしてこのコンポーネントを提供するにあたり、UXの観点から最初に仕様を考えます。

一般的に色はRGBで扱うものですが、ユーザー観点からはHSVの方が色選択をしやすいケースがあります。また、色選択の操作でキーボードを用いたいと考える人は多くありません。マウスやペンで操作したい人が多いでしょう。(とはいえ、すべての操作をキーボードで行うエキスパートのことも考慮する必要はあります。)
全く自由に色を選ぶ機能も必要ですが、代表的な色から選択したいニーズは必ずあります。また、特定のカラーセットや、その色から無彩色(白や黒)までを階層的に選択したい場合もあるでしょう。
色を選択するとすぐに確定するコントロールは必要ですが、「OK」ボタンを押下するまで確定したくないケースもありそうです。

これはまだかなり曖昧です。要求から曖昧な部分を明確化することと、使いやすいUIをデザインすることが必要です。

「全ての色を自由に表現可能であること」とは

要求リストのこの一言に込められている意味をUX観点から考える必要があります。この要求は2つの面を持っています。

  • 表現できる全ての色を選択できること
  • 使いたい色を容易に選択できること

自由であるということは、同じことを容易に繰り返せるという意味も持ちます。
同じ色を選択するために、精密な1ピクセルの選択をユーザーに毎回要求するとしたら、自由すぎて逆に不自由ということです。
つまり「狙った色を調合するレシピのような機能」をコントロールが持っている必要があります。となると、後にある要求の大半はこの要求について、より具体化したものだと考えられます。

もう1つ、要求の中に曖昧な項目があります。「ユーザーから見て自然な挙動をすること」とは、何をしたらよいのでしょうか。これを検討するには、必要なコントロールの当たりを付ける必要があります。

必要となるコントロールを検討する

コントロールの全貌をデザインします。

  1. 一番簡単なコントロールはRGBやHSVを数値入力できるテキストボックスかもしれません。α値を含めると4値入力できれば事足ります。ただしこれは、ユーザーが極めて正確に色を入力・確認したいときのみ必要です。
  2. ボタンに設定されている色を選択するコントロールがあれば、そのボタンを規則的に並べるだけでカラーセット系のニーズは全て満たせると考えられます。
  3. 代表色から選択するコントロールとしては六角形のパレットなども良いでしょう。六角形の当たり判定を持つボタンを作れば事足りそうです。

    六角形ピッカー

    六角パレット

  4. 特定の色から無彩色までのグラデーションを選択できるコントロールもニーズがありそうです。同系統の色を階層的に用いる図はよく見かけます。これも、何段階かの色選択ボタンを配置すれば良いでしょう。
  5. HSVの色相環と明度と彩度を選択するコントロールもポピュラーなので取り入れたいところです。
  6. HSVを選択するコントロールは微調整ができるようにRGBやHSVを入力するコントロールと同期する必要があります。また他のパレットでの色選択とも同期すべきです。

まずは、これら一つ一つを個別のコントロール、またはコントロールの組み合わせとして動作するような設計が必要です。そして、簡素な選択と詳細な選択を切り替えることができるようにしたいという要求に応じます。六角パレットとHSVパレットは、レイアウトの都合上、タブで切り替えるようにしました。

結果として、このようなコントロールをデザインしました。

デザインしたカラーピッカー

デザインしたカラーピッカー

1つ1つの機能はありふれたものですが、これらの機能を個別のコントロールとして独立して使えるようにデザインしました。そのため、不要な部分を削ることも、足りない機能を足すことも容易です。

「ユーザーから見て自然な挙動をすること」とは

どんなものを作るかイメージした後は、改めてUXを検討します。つまり、このコントロールの自然な挙動について深堀りします。

前節で「ボタンに設定されている色を選択するコントロール」の組み合わせで実現することを考えましたが、標準コントロールのボタンをそのまま使うと、望ましい動きになりません。例えば六角パレット上や代表色の配列上では、ボタンを押下したままカーソルをドラッグした場合、カーソルの下の色を選んだ状態にしたいはずです。通常のボタンとは挙動が異なります。

選択色が有彩色から無彩色に変った場合、HSVの色相環は元の位置にあって欲しいと感じるのはごく自然と思えます。

カラーセットから選ぶときは選んだだけで確定して欲しいと感じるのは自然です。しかし、詳細な色を入力するときはOKを押下するまで確定してほしくありません。その場合はカラーセットから色を選んで微調整もできると考えるのは自然です。

現在選択中の色と同じものがパレット上に存在した場合、その色も選択状態になると「ユーザーが選んでいる色」がどの色なのか、より明確になります。

これで曖昧な部分をかなり排除できたと感じます。

モデルを考える

ここからは、ソフトウェアとしてのインターフェースを考えます。

ColorPicker本体のモデルの構成は非常に小さなものです。カラーセットの機能やヒストリーはUX向上のための拡張的な部分です。
1つ1つのコントロールが最小限必要としているのは。下記の1~2までです。3以降はカラーセット機能のためのものと言えます。

  1. 内部的には、現在の色をHSVで保持します。前述のとおり、RGB→HSVの変換は一意ではないためです。
  2. 「選択途中の色」と「確定した色」を別のプロパティとして持ちます。これらはWPFのColor構造体で読み書き可能です。
  3. カラーセットはプロパティを公開しており、読み書きが可能です。
  4. 「最近使った色」については内部で保持していますが読み書きが可能です。つまりシーンに合わせてヒストリーを使い分けることが可能です。

こうしたコンポーネントにおける「モデル」はMVVMで言うところのViewModelに相当します。MVVMのモデルをデザインする責務はアプリケーションの実装者のものであり、コンポーネントはその表示と操作を責務としているためです。また、この部分がViewModelであるため、DataTemplateとして使うことができます。

ここまで来れば、設計・実装に進めそうです。サンプルコードやユニットテストも書くことができる状態と言えます。

まとめ

WPFでColorPickerを作った話(前編)はここまでです。

コンポーネント開発の前半はUXを重視した要求の整理と、仕様を決定していくまでの流れを振り返りました。要点をまとめると以下のようになると思います。

  1. まずは要求を列挙すること
  2. 曖昧な部分を鮮明化すること(自由とか自然という言葉は曖昧)
  3. デザインをある程度進めないと解消できない曖昧性もあること
  4. コンポーネントにおけるモデルはMVVMのViewModel相当の部分であること

後編では実際のコンポーネント実装時のことを扱います。

最近の記事

  • 関連記事
  • おすすめ記事
  • 特集記事

アーカイブ

カテゴリー

PAGE TOP