Winformでもこんなデータ表示ができる

Winformでもこんなデータ表示ができる

winform開発の過程で、データ表示機能が必要になることがよくあります。これまではgridcontrolコントロールを使用していましたが、今日は例を通して、winform blazor hybridでant design blazorのtableコンポーネントを使ってデータ表示を行う方法を紹介します。

最終更新 2024/02/29 5:42
DotNet学习交流
読了目安 6 分
カテゴリ
Winform Blazor
タグ
.NET C# Blazor Winform 混合アプリケーション

1、はじめに ✨

Winform 開発において、データ表示機能は頻繁に必要となります。これまでは GridControl コントロールを使用していましたが、今回は Winform Blazor Hybrid で Ant Design Blazor の Table コンポーネントを使ってデータ表示を行う方法をサンプルを通してご紹介します。

2、効果 ✨

まずは実装の効果をご覧ください:

データ表示効果

3、具体的な実装 ✨

Winform Blazor Hybrid プロジェクトで Ant Design Blazor を使用する方法については、前回の記事をご覧ください。

Ant Design Blazor の Table コンポーネントを導入します:

<table
  TItem="IData"
  DataSource="@datas"
  OnRowClick="OnRowClick"
  @ref="antTableRef"
>
  <PropertyColumn Property="c=>c.StationName"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Weather"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Tem_Low"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Tem_High"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Wind"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Visibility_Low"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Visibility_High"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Fog"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Haze"> </PropertyColumn>
  <PropertyColumn Property="c=>c.Date"> </PropertyColumn>
</table>

このうち:

TItemDataSource 内の単一項目の型を表します。0.16.0 以降、Table は普通のクラス、record、インターフェース、抽象クラスを DataSource の型としてサポートしています。

ここでは TItemIData というインターフェースに設定しています。その定義は以下の通りです:

public interface IData
{
    [DisplayName("站名")]
    public string? StationName { get; set; }
    [DisplayName("天气")]
    public string? Weather { get; set; }
    [DisplayName("最低温度/℃")]
    public string? Tem_Low { get; set; }
    [DisplayName("最高温度/℃")]
    public string? Tem_High { get; set; }
    [DisplayName("风力风向")]
    public string? Wind { get; set; }
    [DisplayName("最低可见度/km")]
    public string? Visibility_Low { get; set; }
    [DisplayName("最高可见度/km")]
    public string? Visibility_High { get; set; }
    [DisplayName("雾")]
    public string? Fog { get; set; }
    [DisplayName("霾")]
    public string? Haze { get; set; }
    [DisplayName("日期")]
    public DateTime? Date { get; set; }
}

[DisplayName("站名")] はプロパティまたはメンバーに対するメタデータ注釈であり、より親しみやすい表示名を提供します。Ant Design Blazor はこれを自動的に使用して名前を表示します。

DataSource はテーブルのデータソースを表し、型は IEnumerable です:

ここでは DataSource="@datas" とすることで、datas というデータソースを Table コンポーネントの DataSource プロパティに割り当てています。

datas の定義は以下の通りです:

WeatherData[] datas = Array.Empty<WeatherData>();

WeatherData はカスタムクラスで、IData インターフェースを実装しています:

   public class WeatherData : IData
    {
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
        public string? StationName { get; set; }
        public string? Weather { get; set; }
        public string? Tem_Low { get; set; }
        public string? Tem_High { get; set; }
        public string? Wind { get; set; }
        public string? Visibility_Low { get; set; }
        public string? Visibility_High { get; set; }
        public string? Fog { get; set; }
        public string? Haze { get; set; }
        public DateTime? Date { get; set; }
    }
}

ここで疑問に思われるかもしれません。先ほど TItemDataSource 内の単一項目の型を表すと説明しましたが、ここでは DataSourceWeatherData[] であり、単一項目の型は WeatherData であって、設定した IData ではありません。これで問題ないのでしょうか?

以下の簡単な例を見れば、疑問が解けるかもしれません:

public interface IFlyable
{
    void Fly();
}
public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("The bird is flying.");
    }

}
class Program
{
    // 主方法
    static void Main()
    {
       Bird myBird = new Bird();
       IFlyable flyableObject = myBird; // 类型转换

        // 调用接口方法
        flyableObject.Fly();
    }
}

IFlyable インターフェースと、そのインターフェースを実装した Bird クラスを定義し、Main 関数で Bird クラスをインスタンス化し、そのオブジェクトをインターフェース型に暗黙的に変換し、インターフェースを介して実装クラスのメソッドを呼び出しています。出力結果は次のとおりです:

The bird is flying.

これは、C# ではクラスがインターフェースを実装している場合、そのクラスのインスタンスが暗黙的にインターフェース型に変換できることを示しています。ここでは WeatherDataIData に暗黙的に変換されます。

DataSource についても同様です。公式ドキュメントには型が IEnumerable と書かれていますが、ここでは WeatherData[] でも問題ありません。これも ArrayIEnumerable インターフェースを実装しているためです:

OnRowClick は行のクリックイベントを表し、型は EventCallback<RowData> です。この例では実際には使用していません。

Blazor において、@ref は、Blazor コンポーネント内で HTML 要素またはコンポーネントインスタンスを参照するためのディレクティブです。@ref を使用することで、Blazor コンポーネント内で DOM 要素や子コンポーネントへの参照を取得し、コード内で操作したり、そのプロパティやメソッドにアクセスしたりできます。

ここでは Table コンポーネントに @ref="antTableRef" を追加し、コード領域に:

Table<IData>? antTableRef;

と追加することで、Table コンポーネントインスタンスを参照しています。

<PropertyColumn> はプロパティ列、つまり表示する列を表します。その Property プロパティはバインドするプロパティを指定し、型は Expression<Func<TItem, TProp>> です。

ここで疑問に思うかもしれません:Expression<Func<TItem, TProp>> とは一体何でしょうか?

Expression<Func<TItem, TProp>> は C# の式ツリーであり、TItem 型のパラメータを受け取り、TProp 型の値を返すラムダ式を表します。

分解してみると、Expression<T> はラムダ式のツリー構造を表すクラスで、T はデリゲート型です。詳細については公式ドキュメントをご覧ください:

Func<TItem, TProp> はジェネリックデリゲート型で、1つの入力パラメータと1つの出力パラメータを持つメソッドを表します。詳細については公式ドキュメントをご覧ください:

ここでも簡単な例で説明します:

Expression<Func<Person, string>> getNameExpression = person => person.Name;

getNameExpression はラムダ式を表します。どのようなラムダ式かというと、入力パラメータ型が Person(ここでは person)、出力型が string(ここでは person.Name)であるラムダ式です。

したがって、次のコード:

<PropertyColumn Property="c=>c.StationName"> </PropertyColumn>

は理解できるでしょう。Property の型は、入力パラメータ型が TItem(ここでは IData)、出力型が TProp(ここでは string)であるラムダ式 c=>c.StationName です。

以上を理解した上で、この部分のコードを見てみましょう:

コードは以下の通りです:

<GridRow>
  <Space>
    <SpaceItem>
      <Text Strong>开始日期:</Text>
    </SpaceItem>
    <SpaceItem>
      <DatePicker TValue="DateTime?" Format="yyyy/MM/dd" Mask="yyyy/dd/MM"
      Placeholder="@("yyyy/dd/MM")" @bind-Value = "Date1"/>
    </SpaceItem>
    <SpaceItem>
      <Text Strong>结束日期:</Text>
    </SpaceItem>
    <SpaceItem>
      <DatePicker TValue="DateTime?" Format="yyyy/MM/dd" Mask="yyyy/dd/MM"
      Placeholder="@("yyyy/dd/MM")" @bind-Value = "Date2"/>
    </SpaceItem>
    <SpaceItem>
      <Text Strong>站名:</Text>
    </SpaceItem>
    <SpaceItem>
      <AutoComplete
        @bind-Value="@value"
        Options="@options"
        OnSelectionChange="OnSelectionChange"
        OnActiveChange="OnActiveChange"
        Placeholder="input here"
        Style="width:150px"
      />
    </SpaceItem>
    <SpaceItem>
      <button type="@ButtonType.Primary" OnClick="QueryButton_Clicked">
        查询
      </button>
    </SpaceItem>
  </Space>
</GridRow>

駅名の自動補完:

<AutoComplete
  @bind-Value="@value"
  Options="@options"
  OnSelectionChange="OnSelectionChange"
  OnActiveChange="OnActiveChange"
  Placeholder="input here"
  Style="width:150px"
/>

この実装は前回の記事ですでに紹介しているため、ここでは繰り返し説明しません。

2つの日付選択コンポーネントはデータバインディングを使用しています:

<SpaceItem>
  <DatePicker TValue="DateTime?" Format="yyyy/MM/dd" Mask="yyyy/dd/MM"
  Placeholder="@("yyyy/dd/MM")" @bind-Value = "Date1"/>
</SpaceItem>

<SpaceItem>
  <DatePicker TValue="DateTime?" Format="yyyy/MM/dd" Mask="yyyy/dd/MM"
  Placeholder="@("yyyy/dd/MM")" @bind-Value = "Date2"/>
</SpaceItem>

このうち:

TValue は値の型を表します。ここでは DateTime? に設定しています。

@bind-Value でデータバインディングを行い、日付選択コンポーネントの値を Date1 および Date2 にバインドします:

DateTime? Date1;
 DateTime? Date2;

検索ボタン:

<button type="@ButtonType.Primary" OnClick="QueryButton_Clicked">查询</button>

クリックイベントのコード:

async void QueryButton_Clicked()
{
    if (Date1 != null && Date2 != null && value != null)
    {
        var cofig = new MessageConfig()
            {
                Content = "正在更新中...",
                Duration = 0
            };
        var task = _message.Loading(cofig);
        var condition = new Condition();
        condition.StartDate = (DateTime)Date1;
        condition.EndDate = (DateTime)Date2;
        condition.StationName = value;
        datas = weatherServer.GetDataByCondition(condition).ToArray();
        StateHasChanged();
        task.Start();
    }
    else
    {
        await _message.Error("请查看开始日期、结束日期与站名是否都已选择!!!");
    }
}

条件が成立する場合、Condition 型を作成し、開始日、終了日、駅名を設定します。Condition クラスの定義は次のとおりです:

public class Condition
 {
     public DateTime StartDate{ get; set; }
     public DateTime EndDate { get; set; }
     public string? StationName { get; set; }
 }

次に、ビジネスロジック層の weatherServer の GetDataByCondition メソッドを呼び出します:

datas = weatherServer.GetDataByCondition(condition).ToArray();

weatherServer の GetDataByCondition メソッドは以下の通りです:

public List<WeatherData> GetDataByCondition(Condition condition)
 {
     return dataService.GetDataByCondition(condition);
 }

データベースの読み書きが含まれるため、データアクセス層の dataService の GetDataByCondition メソッドを呼び出しています。

データアクセス層の dataService の GetDataByCondition メソッドは以下の通りです:

 public List<WeatherData> GetDataByCondition(Condition condition)
  {
      return db.Queryable<WeatherData>()
               .Where(x => x.Date >= condition.StartDate &&
                           x.Date < condition.EndDate.AddDays(1) &&
                           x.StationName == condition.StationName).ToList();
  }

再クエリ時には:

StateHasChanged();

このメソッドを呼び出すとコンポーネントが更新されます。Blazor において、StateHasChanged は Blazor フレームワークに対してコンポーネントとその子コンポーネントを再レンダリングするよう通知するメソッドです。Blazor コンポーネントの UI レンダリングはコンポーネントの状態に基づいており、状態が変化した場合に StateHasChanged メソッドを呼び出してフレームワークに再レンダリングを通知する必要があります。

var cofig = new MessageConfig()
            {
                Content = "正在更新中...",
                Duration = 0
            };
 var task = _message.Loading(cofig);
 task.Start();

これはユーザーへの情報通知です。

4、まとめ ✨

以上、完全なサンプルを通じて、Winform において GridControl だけでなく、Ant Design Blazor の Table を使用してもデータ表示が可能であることを説明しました。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2024/02/29

Winformの画面も綺麗にできる?

先日、winformでblazor hybridを使用することを紹介しました。また、blazorのUIを組み合わせることでwinformプログラムのデザインをより美しくできると言いました。今回はwinform blazor hybridで描画する例を挙げて説明します。参考になれば幸いです。

続きを読む