Blazorアプリケーションをリファクタリングしようとして失敗しているお話

# 概要
* コードをリファクタリングしてみたら意図通りに動作できなかった記録 (現在進行形)

# 宣伝
[C#とBlazorでホロライブファン向けの動画ビューワー『ホロビューワー』を開発しました](https://qiita.com/kawa0x0A/items/24ce1bda92d596f51926)

# 何した
* 『ホロビューワー』のコードをリファクタリングしてた。
* 下記のように同じようなコードが何度も現れるのをなんとかしたかった。

リリース時 (コードの一部を抜粋)
画面分割モードに応じて、GridとGridCellを使用してアプリケーションのUIを生成する処理

```C#
@switch (ApplicationScreenMode.CurrentScreenMode)
{
    case ScreenMode.Single:
        <Grid HorizontalOptions="LayoutOptions.FillAndExpand" VerticalOptions="LayoutOptions.FillAndExpand">
            <StackLayout>
                <WebViewToolbar @ref="WebViewToolbar1" />
                <BlazorWebView @ref="BlazorWebView1" VerticalOptions="LayoutOptions.FillAndExpand">
                    <HoloViewer.WebUI.App />
                </BlazorWebView>
            </StackLayout>
        </Grid>
        break;

    case ScreenMode.SplitHorizontal2:
        <Grid RowDefinitions="*, *" HorizontalOptions="LayoutOptions.FillAndExpand" VerticalOptions="LayoutOptions.FillAndExpand">
            <GridCell Row="0">
                <StackLayout>
                    <WebViewToolbar @ref="WebViewToolbar1" />
                    <BlazorWebView @ref="BlazorWebView1" VerticalOptions="LayoutOptions.FillAndExpand">
                        <HoloViewer.WebUI.App />
                    </BlazorWebView>
                </StackLayout>
            </GridCell>
            <GridCell Row="1">
                <StackLayout>
                    <WebViewToolbar @ref="WebViewToolbar2" />
                    <BlazorWebView @ref="BlazorWebView2" VerticalOptions="LayoutOptions.FillAndExpand">
                        <HoloViewer.WebUI.App />
                    </BlazorWebView>
                </StackLayout>
            </GridCell>
        </Grid>
        break;
// 以下同じようなコードが続いていた
```

GridCellの設定はまとめられることに気がつく。 (最初と同じ辺りの処理を抜粋)

```C#
case ScreenMode.Single:
    <Grid HorizontalOptions="LayoutOptions.FillAndExpand" VerticalOptions="LayoutOptions.FillAndExpand">
        <StackLayout>
            <WebViewToolbar @ref="WebViewToolbars" />
            <BlazorWebView @ref="BlazorWebViews" VerticalOptions="LayoutOptions.FillAndExpand">
                <HoloViewer.WebUI.App />
            </BlazorWebView>
        </StackLayout>
    </Grid>
    break;

case ScreenMode.SplitHorizontal2:
    <Grid RowDefinitions="*, *" HorizontalOptions="LayoutOptions.FillAndExpand" VerticalOptions="LayoutOptions.FillAndExpand">
        @for (int i = 0; i < 2; i++)
        {
            <GridCell Row="@i">
                <StackLayout>
                    <WebViewToolbar @ref="WebViewToolbars" />
                    <BlazorWebView @ref="BlazorWebViews" VerticalOptions="LayoutOptions.FillAndExpand">
                        <HoloViewer.WebUI.App />
                    </BlazorWebView>
                </StackLayout>
            </GridCell>
        }
    </Grid>
    break;
```

ちょっとマシになった。 (ここまでは正常に動作していた)

Gridの設定もバラバラに行うのではなく一箇所にまとめられることに気がつく。(同じ辺りのコードを抜粋。ここから失敗コース)

```C#
<Grid @ref="grid" RowDefinitions="@GridSettings[ApplicationScreenMode.CurrentScreenMode].rowDefinitions" ColumnDefinitions="@GridSettings[ApplicationScreenMode.CurrentScreenMode].columnDefinitions" HorizontalOptions="LayoutOptions.FillAndExpand" VerticalOptions="LayoutOptions.FillAndExpand">
    @for (int i = 0; i < GridSettings[ApplicationScreenMode.CurrentScreenMode].cellCount; i++)
    {
        <GridCell Row="@GridSettings[ApplicationScreenMode.CurrentScreenMode].rowIndies[i]" Column="@GridSettings[ApplicationScreenMode.CurrentScreenMode].columnIndies[i]" ColumnSpan="@GridSettings[ApplicationScreenMode.CurrentScreenMode].columnSpanCounts[i]">
            <StackLayout>
                <WebViewToolbar @ref="WebViewToolbars" />
                <BlazorWebView @ref="BlazorWebViews" VerticalOptions="LayoutOptions.FillAndExpand">
                    <HoloViewer.WebUI.App />
                </BlazorWebView>
            </StackLayout>
        </GridCell>
    }
</Grid>

@code
{
    public class GridSetting
    {
        public readonly string rowDefinitions;
        public readonly string columnDefinitions;
        public readonly int cellCount;
        public readonly int[] rowIndies;
        public readonly int[] columnIndies;
        public readonly int[] columnSpanCounts;

        public GridSetting (string rowDefinitions, string columnDefinitions, int cellCount, int[] rowIndies, int[] columnIndies, int[] columnSpanCounts)
        {
            this.rowDefinitions = rowDefinitions;
            this.columnDefinitions = columnDefinitions;
            this.cellCount = cellCount;
            this.rowIndies = rowIndies;
            this.columnIndies = columnIndies;
            this.columnSpanCounts = columnSpanCounts;
        }
    }

    public static readonly Dictionary<ScreenMode, GridSetting> GridSettings = new Dictionary<ScreenMode, GridSetting>()
    {
        { ScreenMode.Single, new GridSetting("*", "*", 1, new int[]{ 0 }, new int[]{ 0 }, new int[]{ 1 }) },

        { ScreenMode.SplitHorizontal2, new GridSetting("*, *", "*", 2, new int[]{ 0, 1 }, new int[]{ 0, 0 }, new int[]{ 1, 1 }) },
        { ScreenMode.SplitVertical2, new GridSetting("*", "*, *", 2, new int[]{ 0, 0 }, new int[]{ 0, 1 }, new int[]{ 1, 1 }) },

        { ScreenMode.SplitHorizontal3, new GridSetting("*, *, *", "*", 3, new int[]{ 0, 1, 2 }, new int[]{ 0, 0, 0 }, new int[]{ 1, 1, 1 }) },
        { ScreenMode.SplitVertical3, new GridSetting("*", "*, *, *", 3, new int[]{ 0, 0, 0 }, new int[]{ 0, 1, 2 }, new int[]{ 1, 1, 1 }) },
        { ScreenMode.SplitCustom3_1, new GridSetting("*, *", "*, *", 3, new int[]{ 0, 1, 1 }, new int[]{ 0, 0, 1 }, new int[]{ 2, 1, 1 }) },
        { ScreenMode.SplitCustom3_2, new GridSetting("*, *", "*, *", 3, new int[]{ 0, 0, 1 }, new int[]{ 0, 1, 0 }, new int[]{ 1, 1, 2 }) },

        { ScreenMode.SplitHorizontal4, new GridSetting("*, *, *, *", "*", 4, new int[]{ 0, 1, 2, 3 }, new int[]{ 0, 0, 0, 0 }, new int[]{ 1, 1, 1, 1 }) },
        { ScreenMode.SplitVertical4, new GridSetting("*", "*, *, * ,*", 4, new int[]{ 0, 0, 0, 0 }, new int[]{ 0, 1, 2, 3 }, new int[]{ 1, 1, 1, 1 }) },
        { ScreenMode.SplitCustom4, new GridSetting("*, *", "*, *", 4, new int[]{ 0, 0, 1, 1 }, new int[]{ 0, 1, 0, 1 }, new int[]{ 1, 1, 1, 1 }) },
    };
```

いざビルドして動かしてみると、以前と動作が違う。
例えば画面分割モードを横2分割の後に縦2分割を選択すると、以下の画像のように直前の変更が残ってしまうようになった。

* 変更前 (縦に2分割して、2つのページが横に並んでいる正常な状態)
![HoloViewer_Before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74640/95e054aa-8165-0990-4159-f8a6780abc85.png)

* 変更後 (縦に2分割したいのに、直前の横2分割操作の影響を受けて、2*2の4分割モードになってしまっている)
![HoloViewer_After.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74640/50d1ae0f-62af-6029-9b96-0180bfffbed9.png)

# やってみたけど効果がなかったこと
* Gridプロパティを覗いてみると、RowDefinitionsとColumnDefinitionsが反映されているのにChildrenが変更されずに残ったままになっていたので、Grid.NativeControl.Children.Clear()でコントロールを削除してみた。
    * 画面分割モードを変更してもページが再生成されず、ページが表示されなかった。
    * ページが表示されないとダメなのでこの方法はボツ
* Xamarin関連のパッケージをプレビュー版も含めてアップデートした。
    * 2020/11/28時点での最新版にアップデートしても変化がなかった。
    * こちらも効果がなかったのでボツ

# 結論
* コードをまとめるのは諦める。
* 一旦コードはこのままにしてフレームワークなどの更新を待ってみる。
* Blazorへの理解を深めて対応できる問題なのかもう少し調査したい。