web-dev-qa-db-ja.com

汎用的で再利用可能なグリッドビューモデルを使用することについての考え

私は wpf ベースのアプリケーションを開発しており、現在、繰り返し発生するシナリオをサポートする最善の方法を見つけるのに苦労しています。

このアプリはグリッドを多用しています。ドラッグアンドドロップ、複数行の編集、条件付き行の色付けなど、グリッドで使用するために必要な機能がたくさんありますが、同時に、データオブジェクトをUIコンテキストを認識せず、スタンドアロンエディターを備えた単一のフォームで使用できる特定のデータ指向クラス。

したがって、私はDataGridViewModelクラスを2つのコレクション(RowsDataGridRowViewModelとして、ColumnsDataGridColumnViewModelとして公開)に公開したいと考えています。行ビューモデルはactualデータオブジェクトになるRowDataプロパティを公開しますが、列ビューモデルはPropertyDescriptorSystem.ComponentModel名前空間から)を持ちます。列の対象となるデータオブジェクトフィールドを示します。

DataGridRowViewModelは、「実際の」行オブジェクトのコンテナーと考え​​ることができます。

目的は、グリッド固有の機能(行の高さ、色、選択などを制御する)とデータ固有の機能の間の分離を可能にすることです。これにより、一方が他方について知る必要がなくなります。理想的には、3番目のコンポーネントが両方を認識し、2つのレイヤー間の適切な通信を保証します(たとえば、データフィールドを観察し、xまたはyの条件で行の色を変更します)。

このアプローチは意味がありますか?これにはどのような複雑な問題が予想されますか(バインドの問題、パフォーマンスなど)?

6
Crono

私はしばらくグリッドを使用していて、似たようなアイデアとシナリオがありました。このアプローチには欠点があります。グリッドに多くの機能が必要ですが、これは非常にさまざまな種類のデータが含まれることを意味します。したがって、グリッドはこのすべてのデータを視覚化して編集する必要があります。

これ自体は問題ではありませんが、グリッド自体が「神オブジェクト」になるということです。これを解決するには別のアプローチがあります。グリッドを「薄い」コンテナとして作成します。グリッドをテキスト付きのセルとは考えないでください。

レイアウトと考えてください。

次に:

  • グリッドは実際には行のリストです。各行canにはセルがあるため、列の幅を同期するためのメカニズムを作成する必要がありますが、一部の行は特別な場合があります(グループ化、合計、詳細、セパレータ、スパンなど)。
  • 各セルは実際には他のコンポーネントのパネルです。これは非常に複雑になる可能性があるため、テキストだけではなく、2つのボタンと1つの画像による編集が可能です。
  • この方法では、グリッドと行は、サイズと位置を除いて、セルが何であるかを認識しません。
  • つまり、データをセルにバインドすることは、データを値の2次元配列にバインドするのではなく、行の配列をオブジェクトの配列にバインドするということです。
  • 行は、オブジェクトのフィールドへのセルのバインドを処理する必要がありますまたは一部のフィールドの一部の計算値
  • セルはバインディングを子コンポーネントに渡す必要があります
  • そして、セル内のコンポーネント(最も単純で最も一般的なケースではtexbox)が最終的にバインディングを処理します。

私が提案したアプローチは、実装するのが非常に複雑です。多分あなたはそのような複雑なケースを必要としないでしょう。また、これだけ多くのコンポーネントを使用するには最適化が必要であることにも注意してください(特に単純で最も一般的な場合)。ただし、結果として、グリッドを使用するすべてのシナリオに対応できるスイスナイフソリューションを利用できます。

Proposialと比較すると、いくつかの利点があります(proposialにはこの領域に制限があります)。オブジェクト内の深いフィールドからデータを取得でき、計算された値を持つことができるため、オブジェクトを "RowData @、 1つのセルで複数の空腹なコンポーネントを処理できます。

「これで大丈夫ですか?」 - はい、そうです。あなたのやり方でグリッドを作成するいくつかの大きなフレームワークがあります。今頭に浮かんだことから、Qtはこの方法でバインドしています。したがって、あなたのアプローチは、いくつかの既知の問題と制限がありますが、いずれにせよ、よく知られている良い習慣です。

2
Yuri Yaryshev

これからどのような合併症が予想されますか

アプリのUIの動作には、非常に深い一貫性があります。そしてそれはgridviewが1つである基本的なビルディングブロックから来ています。この基本的なレベルでは、誰かがデザインで非常に素晴らしい仕事をしました。これらの非常に基本的なビルディングブロックは、抽象度が低く、一般的な機能領域である基本クラスのベース(および基本!)です。

非常に難解でありながら有用な「レゴ」のセットの鍵は、具体的なビルダーの設計とプログラマーのトレーニングです。

これらのアーティファクトを処理する方法をコーダーにトレーニングする必要があります。抽象化がどのように構築されているかは明らかではありません。いくつかのボタン、一般的なグリッドビュー(実際のデザイナーはありません)だけのウィンドウベースから始めて、ビッグバンが発生するようです。 3つのドロップダウンがあり、フィルタリング、選択、および場合によっては相互のやり取りがある2つのグリッドにインタラクティブに入力するウィンドウ。

ビルダーコードは合理的に設計する必要があります。最も単純なOOアプリケーションで十分な場合がよくありますが、コーダーが既存のデザインを理解していない場合、あなたはうんざりしています。コードのレビューがあるといいのですが、ビルドプロセスのコードが深いほど、その欠陥が痛くなります。

たとえば、具体的なグリッドビューを構築するためのメタデータであるクラスがあります。これは文字列プロパティにすぎません-真のDTOポスターの子です。 1つはDBテーブルの列名のCSVリストです(1つ目はデフォルトのソートで、誰が知っていましたか)、もう1つはグリッドビューの見出し、いくつかはSQLスニペットです。引用符付きの値とそうでない値があります。一重引用符もあれば、二重引用符もあります。そして、SQLの残りの部分はどこですか?一部のプロパティは一緒に使用されましたが、常に使用されているわけではありません。たくさんのコードを読み、たくさんのデバッガトレースを行うまで、私は何も理解しませんでした。どのような馬鹿がコンストラクタが何のためにあるのか知らないのですか?メソッドパラメータ?くだらない。クライアントクラスには、これらの値を分解する手続き型コードが散らばっています。悪いこととないことOO設計は、それ自体がコールスタックの奥深くにあると感じさせます。

新しい機能で使用するために、このクラスの拡張メソッドをたくさん書いてしまいました。ユニットテストがたくさん! クラスは約100か所で使用されており、構築プロセスのかなり奥にあったため、あえてクラスを書き直すことはありませんでした

0
radarbob

私たちはWebベースのUIプロジェクトで同様のことを行うことを目指しているので、あなたが達成しようとしていることは私には完全に理にかなっています。ビューモデルはフラットでなければならないため、バインディングは簡単に実現できます。

今回のケースでは、プロジェクト全体でデータのリストを使用していることがわかりましたが、リストのユースケースは3つしかありませんでした:「これらのアイテムの1つを選択」(行ごとに単一の選択ボタン)、「1つ以上を選択これらのアイテム」(チェックボックスと送信ボタン)、および「このアイテムのリストを管理する」(上部にある[新規追加]ボタン、行ごとに[編集]ボタンと[削除]ボタン)。いずれの場合も、データの列は読み取り専用です。

必要なリストのタイプを説明するパラメーターと、1〜4列の表示データと参照としてのIdを持つビューモデルオブジェクトを使用するコンストラクターを備えたコンポーネントの開発に取り組んでいます。コンポーネントは、そこからリストがどのように見えるかを把握します。ビューモデルを装飾するカスタムアノテーションクラスを作成しました。

[System.AttributeUsage(System.AttributeTargets.Property)]

public class ListViewColumn : System.Attribute
{
    private string _columnName;
    public int ColumnOrder;
    public string FormatString;

    public ListViewColumn(string columnName)
    {
        _columnName = columnName;
        ColumnOrder = 1;
        FormatString = null;
    }
}

使用法:

    [ListViewColumn("Option Code", ColumnOrder = 1)]
    public string OptionCode { get; set; }

    [ListViewColumn("Version", ColumnOrder = 2)]
    public float Version { get; set; }

    [ListViewColumn("Description", ColumnOrder = 3)]
    public string Description { get; set; }

「1つ選択」メタファーのリストを生成している場合(クラス属性がそれを駆動できる)、これは次のようなリストになります。

        Option Code   Version   Description
Select  101-10G       4.2       Whatever this thing is
Select  234-567       4.35      A clear description of this other thing
Select  456-987       2.9       Something completely different

同じクラスを使用して「管理」リストを生成すると、次のようになります。

                                                                 Add New
Option Code   Version   Description
101-10G       4.2       Whatever this thing is                   Edit | Delete
234-567       4.35      A clear description of this other thing  Edit | Delete
456-987       2.9       Something completely different           Edit | Delete
0
J.D. Ray