インフラジスティックス・ジャパン株式会社Blog

インフラジスティックス・ジャパン株式会社のチームメンバーが技術トレンド、製品Tips、サポート情報からライセンス、日々の業務から感じることなど、さまざまなトピックについてお伝えするBlogです。

オープンソース版の Ignite UI for Blazor で作る、タイルマネージャーを使ったカスタマイズ可能なダッシュボード [前編]

先日、インフラジスティックスが開発・提供する UI コンポーネントライブラリ Ignite UI の一部がオープンソース化され、MIT License のもと、商用・非商用を問わず無償で利用できるようになりました。Blazor 向けである Ignite UI for Blazor もその対象に含まれています。

そこで、本記事では Ignite UI のオープンソース化を機会に、オープンソース版の Ignite UI for Blazor に含まれるユニークなコンポーネントのひとつ、タイルマネージャーを使った、ユーザーが自分でレイアウトをカスタマイズできるダッシュボードの作成方法について解説します。

はじめに

本記事では Windows 上で Visual Studio 2026 を使用して Blazor WebAssembly アプリケーションを新規作成し、その上にオープンソース版の Ignite UI for Blazor で提供されるタイルマネージャーコンポーネントを用いたダッシュボードを構築する手順を、ステップ・バイ・ステップで解説します。

本記事はその前編です。(後編はこちら)

完成イメージ

本記事で最終的に完成するダッシュボードアプリケーションは、下図のように、ユーザーが自分でタイルを追加・削除したり、レイアウトを変更したりできるサンプルアプリケーションになります。タイルの状態はブラウザのローカルストレージに保存され、ページを再読み込みしても前回の状態が復元されます。

本記事で作成するアプリの完成イメージ - タイルマネージャーを使って自由にタイルを配置、サイズ変更をしている様子

以下のリンク先で、実際に動く様子を確認頂けます。

本記事で作成するダッシュボードアプリケーションのソースコードは以下の GitHub リポジトリで公開しています。このリポジトリには、この記事で解説している手順ごとにコミットした履歴も含まれています。

それでは始めていきましょう。

Blazor WebAssembly のプロジェクトを新規作成する

まずはオープンソース版の Ignite UI for Blazor を組み込んだ、まっさらな Blazor WebAssembly アプリケーションのプロジェクトを新規作成して、そこから始めていきましょう。

この手順については、下記ブログ記事にて詳しく解説しています。下記リンク先の手順に沿って、オープンソース版の Ignite UI for Blazor がインストールされた Blazor WebAssembly アプリケーションプロジェクトを作成してください。

blogs.jp.infragistics.com

タイルマネージャーを配置してみる

オープンソース版の Ignite UI for Blazor を組み込んだ Blazor WebAssembly アプリケーションプロジェクトができましたら、早速ですが試しにタイルマネージャーコンポーネントを使ってみましょう。Pages/Home.razor を開き、内容を以下のコードにすべて置き換えます。

@* Pages/Home.razor *@

@page "/"

<IgbTileManager>
    <IgbTile>
        <p>Tile 1</p>
    </IgbTile>
    <IgbTile>
        <p>Tile 2</p>
    </IgbTile>
    <IgbTile>
        <p>Tile 3</p>
    </IgbTile>
</IgbTileManager>

以上でこの Blazor WebAssembly アプリケーションを実行すると、ブラウザ上に 3 つのタイルが表示されるのが確認できるはずです。

タイルマネージャー内に3つのタイルが並んでいる様子

このように、Ignite UI for Blazor のタイルマネージャーは、 <IgbTileManager> コンポーネントの中に、 <IgbTile> コンポーネントで個々のタイルを配置することで使用します。

動的にタイルを追加できるようにする

タイルに追加できるサンプルコンポーネントの作成

それではここから順に、機能を追加していきましょう。まず、タイルを動的に追加できるようにしていきますが、その前に、タイルとして追加できるサンプルのコンポーネントを 2 つ作成しておきます。Components フォルダーを新たに作成し、その中に

  • Counter.razorCounter.razor.css
  • Emoji.razorEmoji.razor.css

というコンポーネントを以下のとおり作成します。サンプルとして 2 つのコンポーネントを用意するだけなので、詳細については割愛します。

Counter コンポーネントのソースコード

@* Components/Counter.razor *@

<section>

    <h3>Counter: @_currentCount</h3>

    <IgbButton @onclick="() => _currentCount++">
        Click me
    </IgbButton>

</section>

@code {
    private int _currentCount = 0;
}
@* Components/Counter.razor.css *@
section {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

Emoji コンポーネントのソースコード

@* Components/Emoji.razor *@
<section>
    ♥️
</section>
@* Components/Emoji.razor.css *@
section {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: 48px;
}

Counter コンポーネントはクリックするたびにカウントが増えるボタンを表示し、Emoji コンポーネントはハートの絵文字を表示するだけのシンプルなコンポーネントです。あくまでもタイルマネージャーの使い方をデモンストレーションするためのサンプルコンポーネントなので、この程度の簡単なコンポーネントを 2 つ用意するだけにとどめます。

あまり "ダッシュボード" っぽくないコンポーネントですが、グリッドやチャート、メッセージリストといった、より実用的なコンポーネントをタイルとして追加する場合も、基本的な実装手順は同じです。核心部分を説明するためのサンプルコンポーネントですので、ご了承ください。

"分離された CSS" の有効化と名前空間のインポート

なお、これらサンプルのコンポーネントは Blazor の CSS の分離 ("Isolated CSS") 機能を使用してスタイルを定義しています。そのため、wwwroot/index.html の <head> タグ内でコメントアウトされている部分を解除して、以下のように Blazor の CSS 分離機能を有効にしておきます。

<!DOCTYPE html>
<html lang="en">
<head>
  ...
  <link rel="stylesheet" href="css/app.css" />

  <!-- 👇️ この行をコメント解除して有効化 -->
  <link href="BlazorApp1.styles.css" rel="stylesheet" />
  ...
</head>
...

また、これらサンプルコンポーネントを Home コンポーネントで簡単に使用できるようにするために、その名前空間を開いておきます。_Imports.razor を編集し、以下のコードを追加しておきます。

@* _Imports.razor *@
...
@using BlazorApp1
@using BlazorApp1.Components @* 👈️ 追加 *@
@using BlazorApp1.Layout
...

それでは続けて、タイルを動的に追加できるようにしていきましょう。

タイルを動的に配置するためのデータ構造を定義

さて、ユーザーが動的にタイルを追加できるようにするには、.razor ファイル内に固定で <IgbTile> を記述するわけにはいきません。そこで、タイルの情報を格納するためのデータ構造を定義し、そのデータ構造に基づいてタイルを動的に生成する仕組みを実装します。

まずは、

  • 追加したタイルを個別に識別できる ID と、
  • タイルに表示するコンポーネントの識別名

を格納するためのクラス TileDescriptor を定義します。TileDescriptor.cs という名前でプロジェクトのルートに新規に .cs ファイルを作成し、以下のコードを記述します。

/* TileDescriptor.cs */
public class TileDescriptor
{
    public string TileId { get; set; } = Guid.NewGuid().ToString("N");

    public required string ComponentId { get; set; }
}

こうして作成したタイル情報の集合を、Home コンポーネントのフィールド変数に用意します。Pages/Home.razor を編集し、@code { } ブロックを追加して、以下のコードを記述します。

@* Pages/Home.razor *@
...
@code
{
    private List<TileDescriptor> _tileDescriptors = [];
}

続けて、タイル情報クラスに含まれる、タイル内に描画するコンポーネントの識別名から、該当するコンポーネントの型を返すメソッドを、Home コンポーネントの @code { } ブロック内に追加します。

@* Pages/Home.razor *@
...
@code
{
    ...
    private Type GetComponentType(TileDescriptor descriptor)
    {
        return descriptor.ComponentId switch
        {
            "Counter" => typeof(Counter),
            "Emoji" => typeof(Emoji),
            _ => throw new InvalidOperationException("Unknown component type")
        };
    }
}

これだけだと、タイル情報の集合である _tileDescriptors に要素が何も追加されず、タイルは 1 つも表示されません。そこで、タイルを追加するメソッドを以下のように追加します。

@* Pages/Home.razor *@
...
@code
{
    ...
    private void AddCounter()
    {
        _tileDescriptors.Add(new() { ComponentId = "Counter" });
    }

    private void AddEmoji()
    {
        _tileDescriptors.Add(new() { ComponentId = "Emoji" });
    }
}

UI 側の実装

ロジックはだいたいできましたので、あとは UI 側を実装していきます。Pages/Home.razor のマークアップ部分をいったんすべて削除して書き換えていきましょう。レイアウトを調整しながら、タイルを追加するボタンを追加していきます。<main> タグを配置し、その中に以下のコードを記述します。

@* Pages/Home.razor *@
@page "/"

<main>
  <div class="actions">
    <IgbButton @onclick="AddCounter">
        カウンターを追加
    </IgbButton>
    <IgbButton @onclick="AddEmoji">
        絵文字を追加
    </IgbButton>
  </div>
</main>

@code
{
    ...

ボタンがクリックされると、先ほど実装したタイル追加のメソッドが呼び出される仕組みですね。

続けて、タイル情報の集合 _tileDescriptors を基にタイルを動的に生成するためのコードを <main> タグ内に追加します。以下のコードを <div class="actions"> の直後に追加します。

@* Pages/Home.razor *@
  ...
  <IgbTileManager ColumnCount="6"
                  MinColumnWidth="100px">
    @foreach (var descriptor in _tileDescriptors)
    {
      <IgbTile @key="descriptor.TileId"
               id="@descriptor.TileId"
               DisableFullscreen
               DisableMaximize>
        <DynamicComponent Type="GetComponentType(descriptor)" />
      </IgbTile>
    }
  </IgbTileManager>
</main>

@code
{
    ...

<IgbTileManager> の中で @foreach ディレクティブを使って _tileDescriptors の要素を順に処理し、各要素に対応する <IgbTile> を生成しています。タイルの中身は <DynamicComponent> を使って、コンポーネントの型から動的にコンポーネントを描画しています。

そのほか、タイルマネージャーの分割数を 6 列に設定し、各タイルの最小幅を 100px に設定しています。また、このデモンストレーションでは、各タイルのフルスクリーン表示と最大化は無効に設定しておくことにしました。そのために、<IgbTile> コンポーネントに DisableFullscreen および DisableMaximize パラメーターを指定しています。

レイアウト調整のために CSS も追加しておきましょう。Pages/Home.razor.css を新規に作成し、以下のコードを記述します。

/* Pages/Home.razor.css */
main {
    height: calc(100vh - 18px);
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.actions {
    display: flex;
    gap: 12px;
}

::deep igc-tile-manager {
    flex: 1;
}

以上でこの Blazor WebAssembly アプリケーションを実行すると、画面上に「カウンターを追加」ボタンと「絵文字を追加」ボタンが表示され、各ボタンをクリックすると、それぞれ対応するコンポーネントがタイルとして追加されるのが確認できます。

タイル追加ボタンをクリックすると、タイルマネージャー内に動的にタイルが追加される様子

タイルのサイズ・配置を編集できるようにする

ただ追加できるだけではおもしろくないので、"編集モード" を実装し、ユーザーがタイルのサイズや配置を自由に変更できるようにしてみましょう。

まず、編集モードの状態を保持するための bool 型のフィールド変数 _inEditMode を Home コンポーネントの @code { } ブロック内に追加します。

@* Pages/Home.razor *@
...
@code
{
    private bool _inEditMode = false;
    ...

続けて、編集モードの切り替えを行うボタンを <div class="actions"> 内に追加し、タイルを追加するボタンは、編集モードのときだけ表示されるようにします。<div class="actions"> 内のコードを以下のように追加・変更します。

@* Pages/Home.razor *@
  ...
  <div class="actions">

    <!-- 👇️ 編集モード切り替えのボタンを追加して、 -->
    <IgbButton @onclick="() => _inEditMode = !_inEditMode">
        @(_inEditMode ? "編集終了" : "編集開始")
    </IgbButton>

    <!-- 👇️ タイル追加のボタンは if 文の中に移動 -->
    @if (_inEditMode)
    {
        <IgbButton @onclick="AddCounter">
            カウンターを追加
        </IgbButton>
        <IgbButton @onclick="AddEmoji">
            絵文字を追加
        </IgbButton>
    }
  </div>
  ...

最後に、タイルマネージャーコンポーネントの DragMode および ResizeMode パラメーターを編集モードの状態に応じて切り替えるように設定します。以下のように <IgbTileManager> コンポーネントの DragMode および ResizeMode パラメーターに設定するコードを追加します。

@* Pages/Home.razor *@
  ...
  <IgbTileManager ColumnCount="6"
                  MinColumnWidth="100px"
                  DragMode="@(_inEditMode? TileManagerDragMode.Tile: TileManagerDragMode.None)"
                  ResizeMode="@(_inEditMode ? TileManagerResizeMode.Always : TileManagerResizeMode.None)"
                  >
    ...

以上でこの Blazor WebAssembly アプリケーションを実行すると、画面上に「編集開始」ボタンが表示されます。このボタンをクリックすると編集モードに切り替わり、タイルのドラッグ移動とリサイズが可能になります。また、タイル追加用のボタンも表示されるようになります。編集モードを終了するには、「編集終了」ボタンをクリックします。

編集モードが追加され、タイルの追加に加えて、配置やサイズを変更している様子

タイルの削除ができるようにする

タイルが追加できるだけだとタイルが増える一方なので、タイルの削除機能も追加しておきましょう。タイルの削除は編集モードのときだけ可能にします。 まず、タイルを削除するためのメソッドを Home コンポーネントの @code { } ブロック内に追加します。

@* Pages/Home.razor *@
...
@code
{
    ...
    private void RemoveTile(TileDescriptor descriptor)
    {
        _tileDescriptors.Remove(descriptor);
    }
}

続けて、各タイルの右上に削除ボタンを配置します。削除ボタンは編集モードのときだけ表示されるようにします。以下のコードを <IgbTile> コンポーネント内に追加します。

@* Pages/Home.razor *@
  ...
  <IgbTile ...>

    @* 👇️この if ブロックを追加 *@
    @if (_inEditMode)
    {
        <div class="tile-actions">
          <button class="material-icons"
                  @onclick="() => RemoveTile(descriptor)">
            delete
          </button>
        </div>
    }

   <DynamicComponent Type="GetComponentType(descriptor)" />
  </IgbTile>
  ...

また、削除ボタンのスタイルを調整するために、Pages/Home.razor.css の末尾に以下のコードを追加します。

/* Pages/Home.razor.css */
...

.tile-actions {
    position: absolute;
    top: 4px;
    right: 4px;
    z-index: 1;
}

.tile-actions button {
    width: 32px;
    height: 32px;
    padding: 0;
    margin: 0;
    border: none;
    background: transparent;
    color: var(--ig-error-500);
}

削除ボタンには Material Icons フォントを使用してゴミ箱のアイコンを表示しますので、wwwroot/index.html を編集し、Google Fonts からの Material Icons フォントの読み込みも追加します。

<!DOCTYPE html>
<html lang="en">
<head>
  ...
  <!-- 👇️ この行を追加 -->
  <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons&display=block" />

  <script type="importmap"></script>
</head>
...

以上でこの Blazor WebAssembly アプリケーションを実行すると、編集モードのときに各タイルの右上に削除ボタンが表示されるようになります。

しかし残念なことに、この削除ボタンをクリックしてもタイルは削除されません。というのも、編集モード時はタイルのドラッグが有効になっており、削除ボタン上でマウスボタンを押しても、タイルのドラッグ操作として扱われてしまうためです。

削除ボタンをクリックしても、タイルの移動になってしまう様子

そこで JavaScript の力を借りて、削除ボタン上でのマウスダウンイベントをタイルマネージャーに勘付かれないようにする細工を施します。wwwroot/index.html を編集し、<body> タグを閉じる直前に以下のとおり script タグを追加します。

<!-- wwwroot/index.html -->
  ...
  <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>

  <!-- 👇️ この script タグを追加 -->
  <script>
    document.addEventListener("pointerdown", e => {
      // タイル内の削除ボタンがクリックされた場合は...
      if (e.target.matches(".tile-actions *"))
        // イベントがタイルマネージャーに伝播するのを阻止する
        e.stopPropagation();
    },
    // キャプチャリングフェーズで処理するため、第三引数に true を指定する
    true);
</script>

</body>
</html>

以上の処置を施すことで、編集モードで削除ボタンをクリックすると、該当するタイルが削除されるようになります。

削除機能が完成し、期待どおりにタイルの削除が動作している様子

これでタイルマネージャーを使ったダッシュボードの基本的な操作、つまり、タイルの追加、配置とサイズの変更、削除、をすべて実装できました。

前編はここまでとします。お疲れさまでした。

前編のまとめ

基本操作の実装が完了

本記事では、オープンソース版の Ignite UI for Blazor に含まれるタイルマネージャーコンポーネントを使って、ユーザーが自分でレイアウトをカスタマイズできるダッシュボードを作成する方法について、基本的な操作ができるところまで解説しました。

  • 動的に増減するタイルを管理するモデルを実装、モデルの状態を UI に反映するよう構築
  • タイルの配置変更とサイズ変更の可否は、IgbTileManager コンポーネントの DragMode および ResizeMode パラメーターで制御

タイルマネージャーコンポーネントを使うことで、ユーザーが自由にタイルの追加・削除、サイズ変更、配置変更を行える柔軟なダッシュボードを簡単に実装できることがお分かりいただけたかと思います。

後編の内容

ところで、タイルの追加・削除、配置やサイズの変更、といった操作はすべてクライアント側で行われます。そのため、ここまでの実装ですと、ページをリロードするたびに、すべての状態が初期化されてしまいます。そこで、タイルの状態をブラウザのローカルストレージに保存し、ページの再読み込み時に復元する仕組みを、後編で実装していきたいと思います。

blogs.jp.infragistics.com

他のフレームワークでの利用について

本記事では作業手順を具体的にするために、Blazor WebAssembly アプリケーションを対象とした説明を行いましたが、Blazor Server アプリケーションでも今回ご紹介と同様の手順でタイルマネージャーを利用できます。

また、Blazor に限らず、Ignite UI for React や Ignite UI for Angular にもタイルマネージャーコンポーネントが含まれています。つまり、JavaScript/TypeScript を用いたクライアント Web アプリケーション開発、例えば React や Angular アプリケーションでもタイルマネージャーを無償で商用利用可能です。Ignite UI for WebComponents を使用すれば、あらゆる JavaScript フレームワークでタイルマネージャーを利用できます。

ぜひこの機会に Ignite UI をお試しください!

参考情報