質問するログイン新規登録

Q&A

解決済

3回答

386閲覧

[C#, .NET MAUI] 共通のコードを配列にしたいけど、コードがわからないので手伝ってほしい。

normal1980

総合スコア24

.NET MAUI

.NET MAUIは、「.NET Multi-platform App UI」の略。単一コードで複数のプラットフォームに対応するクロスプラットフォームフレームワークです。Xamarin.Formsの進化系とされており、XAMLやMVVM に加え、MVUもサポートされています。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

1グッド

0クリップ

投稿2026/03/07 09:10

編集2026/03/08 12:50

1

0

実現したいこと

アプリとしては、クイズゲームのひな形ができました。
しかし、無駄というか、完全にひな形のコピーをしているだけなので、
今のコードを配列であらわしたいのですが、*.cs, *.xaml の配列化がわかりません。

cs, xaml の配列化を実現するコードを教えてください。

発生している問題・分からないこと

cs, xaml の配列化がわかりません。
トップレベルにコードを書きたいのですが、xamlとcsの配列を、コードでどうやって書くのかが
わかりません。
そもそも、xaml が配列で表現できるのかが、わかりません。

該当のソースコード

xaml

1[Section_0100_100.xaml] 2<?xml version="1.0" encoding="utf-8" ?> 3<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 4 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 5 x:Class="dic1980_game.Section_0100.Section_0100_100"> 6 <Grid Padding="12"> 7 <!-- 上部を自動高さ(Auto)で積み、最後を残り領域(*)にする --> 8 <Grid.RowDefinitions> 9 <RowDefinition Height="Auto" /> 10 <!-- タイトル --> 11 <RowDefinition Height="Auto" /> 12 <!-- 区分 --> 13 <RowDefinition Height="Auto" /> 14 <!-- 問題数 --> 15 <RowDefinition Height="Auto" /> 16 <!-- 問題 --> 17 <RowDefinition Height="Auto" /> 18 <!-- 選択ボタン群(縦に並べる)--> 19 <RowDefinition Height="Auto" /> 20 <!-- 結果/解説/次へボタン --> 21 <RowDefinition Height="*" /> 22 <!-- スクロール領域(残りを占有)--> 23 </Grid.RowDefinitions> 24 25 <Label Text="時代:縄文時代" HorizontalOptions="Center" Grid.Row="0" /> 26 <Label Text="区分:縄文01_社会" HorizontalOptions="Center" Grid.Row="1" /> 27 <Label x:Name="QuestionCountLabel" HorizontalOptions="Center" Grid.Row="2" /> 28 <Label x:Name="QuestionLabel" HorizontalOptions="Center" Grid.Row="3" /> 29...

xaml

1[Section_0100_200.xaml] 2<?xml version="1.0" encoding="utf-8" ?> 3<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 4 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 5 x:Class="dic1980_game.Section_0100.Section_0100_200"> 6 <Grid Padding="12"> 7 <!-- 上部を自動高さ(Auto)で積み、最後を残り領域(*)にする --> 8 <Grid.RowDefinitions> 9 <RowDefinition Height="Auto" /> 10 <!-- タイトル --> 11 <RowDefinition Height="Auto" /> 12 <!-- 区分 --> 13 <RowDefinition Height="Auto" /> 14 <!-- 問題数 --> 15 <RowDefinition Height="Auto" /> 16 <!-- 問題 --> 17 <RowDefinition Height="Auto" /> 18 <!-- 選択ボタン群(縦に並べる)--> 19 <RowDefinition Height="Auto" /> 20 <!-- 結果/解説/次へボタン --> 21 <RowDefinition Height="*" /> 22 <!-- スクロール領域(残りを占有)--> 23 </Grid.RowDefinitions> 24 25 <Label Text="時代:縄文時代" HorizontalOptions="Center" Grid.Row="0" /> 26 <Label Text="区分:縄文02_時代背景" HorizontalOptions="Center" Grid.Row="1" /> 27 <Label x:Name="QuestionCountLabel" HorizontalOptions="Center" Grid.Row="2" /> 28 <Label x:Name="QuestionLabel" HorizontalOptions="Center" Grid.Row="3" /> 29...

xaml

1[Section_0100_300.xaml] 2<?xml version="1.0" encoding="utf-8" ?> 3<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 4 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 5 x:Class="dic1980_game.Section_0100.Section_0100_300"> 6 <Grid Padding="12"> 7 <!-- 上部を自動高さ(Auto)で積み、最後を残り領域(*)にする --> 8 <Grid.RowDefinitions> 9 <RowDefinition Height="Auto" /> 10 <!-- タイトル --> 11 <RowDefinition Height="Auto" /> 12 <!-- 区分 --> 13 <RowDefinition Height="Auto" /> 14 <!-- 問題数 --> 15 <RowDefinition Height="Auto" /> 16 <!-- 問題 --> 17 <RowDefinition Height="Auto" /> 18 <!-- 選択ボタン群(縦に並べる)--> 19 <RowDefinition Height="Auto" /> 20 <!-- 結果/解説/次へボタン --> 21 <RowDefinition Height="*" /> 22 <!-- スクロール領域(残りを占有)--> 23 </Grid.RowDefinitions> 24 25 <Label Text="時代:縄文時代" HorizontalOptions="Center" Grid.Row="0" /> 26 <Label Text="区分:縄文03_文化" HorizontalOptions="Center" Grid.Row="1" /> 27 <Label x:Name="QuestionCountLabel" HorizontalOptions="Center" Grid.Row="2" /> 28 <Label x:Name="QuestionLabel" HorizontalOptions="Center" Grid.Row="3" /> 29...

c#

1[Section_0100_100.xaml.cs] 2#pragma warning disable CS0618 3 4using System; 5using System.Collections.Generic; 6using System.IO; 7using System.Text; 8using System.Threading.Tasks; 9using Microsoft.Data.Sqlite; 10using Microsoft.Maui.Storage; 11using Microsoft.Maui.Controls; 12 13namespace dic1980_game.Section_0100; 14 15public partial class Section_0100_100 : ContentPage 16{ 17 // 「時代」ー「部分」で異なる部分の定義 18 // DBファイル名(必要に応じて変更) 19 public const string dbFileName = "history_2026_01_24.sqlite3"; 20 // デバッグ用に絶対パスを指定している場合はそのまま、通常は FileSystem.AppDataDirectory を使う 21 public static string dbPath = Path.Combine(FileSystem.AppDataDirectory, dbFileName); 22 // テーブル名とカラム名をプロジェクトで指定された日本語のスキーマに合わせる 23 public static string read_SQL = 24 @"SELECT ""ID"", ""時代"", ""部分"", ""問題"", ""選択肢1"", ""選択肢2"", ""選択肢3"", ""選択肢4"", ""答え"", ""難易度"", ""解説"" 25 FROM ""クイズ_問題"" 26 WHERE ""部分"" = '縄文01_社会' 27 ORDER BY ""ID"";"; 28 29 // クラス全体で使用する変数を定義 30 public static int m_iSelectNumber = 0; 31 public static int m_i現在の問題の番号 = 0; // 0から始まる 32 public static int m_i正解数 = 0; 33 public static int m_i不正解数 = 0; 34...
[Section_0100_200.xaml.cs] #pragma warning disable CS0618 namespace dic1980_game.Section_0100; public partial class Section_0100_200 : ContentPage { // 「時代」ー「部分」で異なる部分の定義 // DBファイル名(必要に応じて変更) public const string dbFileName = "history_2026_01_24.sqlite3"; // デバッグ用に絶対パスを指定している場合はそのまま、通常は FileSystem.AppDataDirectory を使う public static string dbPath = Path.Combine(FileSystem.AppDataDirectory, dbFileName); // テーブル名とカラム名をプロジェクトで指定された日本語のスキーマに合わせる public static string read_SQL = @"SELECT ""ID"", ""時代"", ""部分"", ""問題"", ""選択肢1"", ""選択肢2"", ""選択肢3"", ""選択肢4"", ""答え"", ""難易度"", ""解説"" FROM ""クイズ_問題"" WHERE ""部分"" = '縄文02_時代背景' ORDER BY ""ID"";"; // クラス全体で使用する変数を定義 public static int m_iSelectNumber = 0; public static int m_i現在の問題の番号 = 0; // 0から始まる public static int m_i正解数 = 0; public static int m_i不正解数 = 0; ...

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

上のソースで、省略している部分は同じです。 なので、文字の都合もありますが、省略しています。

Section_0100_100.xaml
x:Class="dic1980_game.Section_0100.Section_0100_100">
<Label Text="時代:縄文時代" HorizontalOptions="Center" Grid.Row="0" />
<Label Text="区分:縄文01_社会" HorizontalOptions="Center" Grid.Row="1" />

Section_0100_200.xaml
x:Class="dic1980_game.Section_0100.Section_0100_200">
<Label Text="時代:縄文時代" HorizontalOptions="Center" Grid.Row="0" />
<Label Text="区分:縄文02_時代背景" HorizontalOptions="Center" Grid.Row="1" />

Section_0100_300.xaml
x:Class="dic1980_game.Section_0100.Section_0100_300">
<Label Text="時代:縄文時代" HorizontalOptions="Center" Grid.Row="0" />
<Label Text="区分:縄文03_文化" HorizontalOptions="Center" Grid.Row="1" />

[Section_0100_100.xaml.cs]
public partial class Section_0100_100 : ContentPage
{
@"SELECT ""ID"", ""時代"", ""部分"", ""問題"", ""選択肢1"", ""選択肢2"", ""選択肢3"", ""選択肢4"", ""答え"", ""難易度"", ""解説""
FROM ""クイズ_問題""
WHERE ""部分"" = '縄文01_社会'
ORDER BY ""ID"";";

[Section_0100_200.xaml.cs]
public partial class Section_0100_100 : ContentPage
{
public static string read_SQL =
@"SELECT ""ID"", ""時代"", ""部分"", ""問題"", ""選択肢1"", ""選択肢2"", ""選択肢3"", ""選択肢4"", ""答え"", ""難易度"", ""解説""
FROM ""クイズ_問題""
WHERE ""部分"" = '縄文02_時代背景'
ORDER BY ""ID"";";

[Section_0100_300.xaml.cs]
public partial class Section_0100_100 : ContentPage
{
public static string read_SQL =
@"SELECT ""ID"", ""時代"", ""部分"", ""問題"", ""選択肢1"", ""選択肢2"", ""選択肢3"", ""選択肢4"", ""答え"", ""難易度"", ""解説""
FROM ""クイズ_問題""
WHERE ""部分"" = '縄文03_文化'
ORDER BY ""ID"";";

の部分だけ違います。

現在、Section_1700 まであって、修正が大変なので、改善したいと思ってます。

補足

[環境]OS:Windows 11 (とりあえずwindowsが動けば良い、androidは、windows で動作確認できてから)
.NET SDK 10
.NET MAUI 10
SQLite 3
Visual Stduio 2026 Community

質問後追加

TN8001さん、質問をご覧の方
>遷移が問題?
う~ん、何といえばいいのか。
ソースコードをご覧いただければわかると思いますが、ほとんどのソースコードが同じなのです。
なので、共通部分をまとめて、配列を使ってアクセスできないのか、改善したいということです。

[Section_0100_100.xaml]
[Section_0100_100.xaml.cs]
[Section_0100_200.xaml]
[Section_0100_200.xaml.cs]
[Section_0100_300.xaml]
[Section_0100_300.xaml.cs]
をそれぞれ配列化できなのか
イメージとしては、
Section_0100_xaml[100]->View();
Section_0100_cs[100]->View();
のような感じです。

TN8001さんがおっしゃる、MVVMについては、まだ勉強していないので、ごめんなさい。

ソースコード一式

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

TN8001

2026/03/07 11:11

> 今のコードを配列であらわしたいのですが、*.cs, *.xaml の配列化がわかりません。 Section_0100_100 Section_0100_200 Section_0100_300 がほとんど同じなのが無駄というかめんどくさいという話でしょうか? 本来MVVMで作っていればこの種のContentPageはひとつだけで、BindingContextを切り替えるだけで済むはずです。 > 現在、Section_1700 まであって、修正が大変なので、改善したいと思ってます。 え、、既に17*3個ぐらいContentPageがもうあるってことですね? どのように遷移しているかが不明ですが、遷移時にデータを渡すことは可能です。 差分の「縄文01_社会」「縄文02_時代背景」「縄文03_文化」を渡して、SQLとラベルテキストを設定することはできると思います。 もう少し詳細なコードがあればサンプルを回答できますが、現状どうなっているか不明なのでコメントで^^;
normal1980

2026/03/08 12:51

回答ありがとうございます。 ソースコードをアップロードしました。 そちらをご覧いただければと思います。
TN8001

2026/03/08 15:31 編集

素敵なアプリですね^^ Navigationのほうだったですか、2択が外れましたw dic1980さんのコードに合わせた新たな回答をしました。
jimbe

2026/03/08 18:14 編集

折角クイズをDBに入れているのに、クイズ毎に画面を作ってしまわれたんですね。 これだとクイズを追加する毎に画面を増やし続け、アプリのコードが肥大し続けることになります。(これは例え配列に出来たとしても変わらないでしょう。) 理想的には、データベースにクイズを追加するだけ(コードは増えず)で、ボタンが増えてきちんと機能することです。 そこで、今はクイズ毎にコードが持っている情報でDBから読んでitemを作ったりしていますがそうではなく、最初に全クイズを読んで"時代"-"部分"のツリー構造をつくり、それに基づいてナビのボタンを作成・表示してユーザーに選択してもらい、クイズ画面はその時代・部分(部分は時代を含んでいるようなので、部分だけでも良い?)を受け取ってDBデータを読んでクイズを出す…という形にすれば、理想的な形になるのではないでしょうか。 データベースから選択するキーとなるデータさえもデータベースから得ることで、データ値をコードが持つ必要が無くなり、従ってデータ値毎にボタンや画面を作る必要も無くなります。 (C#は何にも分からないので具体的にどう出来るのかは全く書けませんごめんなさい)
dodox86

2026/03/09 04:04

まぁ、まずは今のコードをベースとして動作する丁寧な回答をいただいていると思うので、その線で進めて、いったんは望むかたちで動くものを完成させてしまった方が精神的にも良いと思います。 汎用的なデータや制御、モデル構造でも慣れないプログラミング言語やフレームワークを使うとそれを適用させるのは(ベテランでも)最初は違和感があっててこずるものです。 内部のつくりについては業務でプロとして作るのと趣味とでは重要度も違うでしょうし。
normal1980

2026/03/09 04:19

>jimbe なるほど、クイズデータをもとに、選択肢などを生成するということでしょうか? ただ、私の頭では、それをコードにあらわせそうにないです。ごめんなさい。 今後の課題としておきます。 >dodox86さん そうですね。最初は無駄なコードだとわかっていましたが、とりあえず 動くものを作ろうとしていました。 プロは違うのでしょうか。 私は趣味なので、動けばOKという形です。
jimbe

2026/03/09 07:25

今の状態だと大変だから何とかしようとされたこと自体が、良い方向ではないかと思っております。 『プログラマは楽をするためなら苦労を厭わない』なんて言葉もあるくらいですのでw "C# MAUI" で検索しましたら、 axml を使わなくてもコードでレイアウトを組み立てられるという記事がありました。 これならデータベースからの件数分のボタンを作るとかも出来そうですので、何かの機会にでも試されては如何かと思います。
TN8001

2026/03/09 10:05

@jimbe 2026/03/09 03:14 編集 > (C#は何にも分からないので具体的にどう出来るのかは全く書けませんごめんなさい) @dic1980 2026/03/09 13:19 > 今後の課題としておきます。 呼ばれた気がしたので回答しました(暇つぶしなんで気にしないでくださいw わかりやすさ優先で都度問い合わせる形にしました。 ツリー構造で持てばセクション一覧を簡単に表示できるんですが、今のUIとは合わないのかな?と [CollectionView でグループ化されたデータを表示する - .NET MAUI | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/maui/user-interface/controls/collectionview/grouping) @jimbe 2026/03/09 16:25 > これならデータベースからの件数分のボタンを作る TopStackLayout.Add(new Button { Text = "縄文時代" }); みたいなことも全然できちゃいます。 でもClickedイベントをインラインで書けないのが辛いんですよねぇ... [[Proposal]: Compound assignment in object initializer and `with` expression · Issue #9896 · dotnet/csharplang](https://github.com/dotnet/csharplang/issues/9896) それよりもDataTemplateで「文字列がボタンに変わっちゃう!」みたいのが驚きというか面白いポイントで、わたしは大好きなんですがうまく伝えられないのがもどかしいです^^;
dodox86

2026/03/10 04:36

更にていねいで優しいコメント・回答が追加されていたのでびっくりw dic1980さんの「2026/03/09 13:19」のコメントより: > プロは違うのでしょうか。 > 私は趣味なので、動けばOKという形です。 とりあえず聞かれていたのでコメントしておきます。 個人的な意見ではありますが、趣味ならそれはそれでOKだと思います。例えばイラストを描くのが趣味で、単に描けたら楽しいというときに横から「これはデッサンが狂ってる」とか「パースがおかしい、センス無い」とかは余計なお世話ですよね。ですが、仕事、納品物品だと違います。製品の性質にもよりますが、多くのコードは改修や漸進的な改良、機能追加、保守等で長く付き合うことになります。そんな場合は安易な設計、コードにすると後で"自分が"痛い目にあうことを経験者は知っているので、最初からみすみす悪いコードにはしません。ただ、プロトタイプとか数回のデモ用、あるいはとにかく市場に早く投入しなければならないようなケースでは或いは後で作り直すことも前提に入れてあえて簡単に作ることはありますね。
guest

回答3

0

選択画面の改善案

最初の回答のようにCollectionViewの使い方の話になります。
CollectionView - .NET MAUI | Microsoft Learn

基本的には何かのコレクション(Listでも配列でも)をItemsSourceに入れると、それを一覧に出してくれるコントロールです。
並べ方や見せ方のカスタマイズができるので、例えばstring[]を3列のボタンで表示させることができます。
CollectionView レイアウトの指定 - .NET MAUI | Microsoft Learn
CollectionView へのデータの入力 - .NET MAUI | Microsoft Learn

cs:MainPage.xaml.cs

1namespace dic1980_game; 2 3public partial class MainPage : ContentPage 4{ 5 public MainPage() 6 { 7 InitializeComponent(); 8 9 Application.Current!.Windows[0].Width = 600; 10 Application.Current!.Windows[0].Height = 600; 11 } 12 13 private async void OnCounterClicked(object? sender, EventArgs e) 14 => await Navigation.PushAsync(new SelectPeriod()); 15 16 private void Button_EXIT(object? sender, EventArgs e) 17 => Application.Current?.Quit(); 18}

xml:SelectPeriod.xaml

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage 3 x:Class="dic1980_game.SelectPeriod" 4 xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 5 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> 6 <ScrollView> 7 <VerticalStackLayout> 8 <Label 9 HorizontalOptions="Center" 10 Text="クイズを行う時代を選んでください。" 11 VerticalOptions="Center" /> 12 13 <CollectionView x:Name="ChoicesCollectionView" Margin="10"> 14 <CollectionView.ItemsLayout> 15 <!-- 3列表示 --> 16 <GridItemsLayout 17 HorizontalItemSpacing="10" 18 Orientation="Vertical" 19 Span="3" 20 VerticalItemSpacing="10" /> 21 </CollectionView.ItemsLayout> 22 <CollectionView.ItemTemplate> 23 <DataTemplate> 24 <!-- ItemsSourceがList<Item>なので、ここでのBindingContextはItem単体 --> 25 <Button Clicked="Button_Clicked" Text="{Binding Display}" /> 26 </DataTemplate> 27 </CollectionView.ItemTemplate> 28 </CollectionView> 29 30 <Button 31 Clicked="ExitButton_Clicked" 32 HorizontalOptions="Center" 33 Text="終了する" /> 34 </VerticalStackLayout> 35 </ScrollView> 36</ContentPage>

cs:SelectPeriod.xaml.cs

1using Microsoft.Data.Sqlite; 2 3namespace dic1980_game; 4 5public partial class SelectPeriod : ContentPage 6{ 7 /// <param name="Name">レコード名</param> 8 /// <param name="Display">表示名</param> 9 record Item(string Name, string Display); 10 11 private const string dbFileName = "history_2026_01_24.sqlite3"; 12 private readonly string dbPath = Path.Combine(FileSystem.AppDataDirectory, dbFileName); 13 private readonly string read_SQL; 14 private readonly List<Item> m_Items = []; 15 16 public SelectPeriod() 17 { 18 InitializeComponent(); 19 20 read_SQL = """ 21 SELECT DISTINCT "時代" 22 FROM "クイズ_問題" 23 ORDER BY "ID"; 24 """; 25 } 26 27 protected override async void OnAppearing() 28 { 29 base.OnAppearing(); 30 31 try 32 { 33 await LoadDatabaseAsync(); 34 } 35 catch (Exception ex) 36 { 37 await DisplayAlertAsync("DB読み込みエラー", ex.Message, "OK"); 38 } 39 40 ChoicesCollectionView.ItemsSource = m_Items; 41 } 42 43 44 private async Task LoadDatabaseAsync() 45 { 46 if (!File.Exists(dbPath)) 47 { 48 throw new FileNotFoundException($"データベースが見つかりません: {dbPath}"); 49 } 50 51 var connectionString = new SqliteConnectionStringBuilder 52 { 53 DataSource = dbPath, 54 Mode = SqliteOpenMode.ReadOnly, 55 }.ToString(); 56 57 await Task.Run(() => 58 { 59 using var connection = new SqliteConnection(connectionString); 60 connection.Open(); 61 62 using var command = connection.CreateCommand(); 63 64 command.CommandText = read_SQL; 65 66 using var reader = command.ExecuteReader(); 67 while (reader.Read()) 68 { 69 if (reader[0] is string item) 70 { 71 var disp = $"{item.Split('_')[1]}時代"; // 表示名(雑に作成w 72 m_Items.Add(new Item(item, disp)); 73 } 74 } 75 76 connection.Close(); 77 }); 78 } 79 80 private async void Button_Clicked(object? sender, EventArgs e) 81 { 82 if (sender is Button { BindingContext: Item item }) 83 { 84 await Navigation.PushAsync(new SelectSection(item.Name)); 85 } 86 } 87 88 private void ExitButton_Clicked(object? sender, EventArgs e) => Application.Current?.Quit(); 89}

xml:SelectSection.xaml

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage 3 x:Class="dic1980_game.SelectSection" 4 xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 5 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> 6 <ScrollView> 7 <VerticalStackLayout> 8 <Label HorizontalOptions="Center" Text="クイズを行うセクションを選んで下さい。" /> 9 10 <CollectionView x:Name="ChoicesCollectionView" Margin="10"> 11 <CollectionView.ItemsLayout> 12 <LinearItemsLayout ItemSpacing="10" Orientation="Vertical" /> 13 </CollectionView.ItemsLayout> 14 <CollectionView.ItemTemplate> 15 <DataTemplate> 16 <!-- ItemsSourceがList<Item>なので、ここでのBindingContextはItem単体 --> 17 <Button Clicked="Button_Clicked" Text="{Binding Display}" /> 18 </DataTemplate> 19 </CollectionView.ItemTemplate> 20 </CollectionView> 21 22 <Button 23 Clicked="BackButton_Clicked" 24 HorizontalOptions="Center" 25 Text="もどる" /> 26 27 </VerticalStackLayout> 28 </ScrollView> 29</ContentPage>

cs:SelectSection.xaml.cs

1using dic1980_game.Section_0100; 2using Microsoft.Data.Sqlite; 3using System.Text.RegularExpressions; 4 5namespace dic1980_game; 6 7public partial class SelectSection : ContentPage 8{ 9 /// <param name="Name">レコード名</param> 10 /// <param name="Display">表示名</param> 11 record Item(string Name, string Display); 12 13 private const string dbFileName = "history_2026_01_24.sqlite3"; 14 private readonly string dbPath = Path.Combine(FileSystem.AppDataDirectory, dbFileName); 15 private readonly string read_SQL; 16 private readonly List<Item> m_Items = []; 17 private readonly string period; 18 19 [GeneratedRegex("[0-9]+_")] private static partial Regex NumberRegex { get; } 20 21 public SelectSection(string period) 22 { 23 InitializeComponent(); 24 25 this.period = period; 26 read_SQL = $""" 27 SELECT DISTINCT "部分" 28 FROM "クイズ_問題" 29 WHERE "時代" = '{period}' 30 ORDER BY "ID"; 31 """; 32 } 33 34 protected override async void OnAppearing() 35 { 36 base.OnAppearing(); 37 38 try 39 { 40 await LoadDatabaseAsync(); 41 } 42 catch (Exception ex) 43 { 44 await DisplayAlertAsync("DB読み込みエラー", ex.Message, "OK"); 45 } 46 47 ChoicesCollectionView.ItemsSource = m_Items; 48 } 49 50 private async Task LoadDatabaseAsync() 51 { 52 if (!File.Exists(dbPath)) 53 { 54 throw new FileNotFoundException($"データベースが見つかりません: {dbPath}"); 55 } 56 57 var connectionString = new SqliteConnectionStringBuilder 58 { 59 DataSource = dbPath, 60 Mode = SqliteOpenMode.ReadOnly, 61 }.ToString(); 62 63 await Task.Run(() => 64 { 65 using var connection = new SqliteConnection(connectionString); 66 connection.Open(); 67 68 using var command = connection.CreateCommand(); 69 70 command.CommandText = read_SQL; 71 72 using var reader = command.ExecuteReader(); 73 while (reader.Read()) 74 { 75 if (reader[0] is string item) 76 { 77 var disp = NumberRegex.Replace(item, "時代の"); // 表示名(雑に作成w 78 m_Items.Add(new Item(item, disp)); 79 } 80 } 81 82 connection.Close(); 83 }); 84 } 85 86 private async void Button_Clicked(object? sender, EventArgs e) 87 { 88 if (sender is Button { BindingContext: Item item }) 89 { 90 // Section_0100_X00は↓の回答のContentPage 91 // https://teratail.com/questions/bz2ow7ged9ubqs#reply-25z46kxg4v4yr9 92 await Navigation.PushAsync(new Section_0100_X00(period, item.Name)); 93 } 94 } 95 96 private async void BackButton_Clicked(object? sender, EventArgs e) 97 => await Navigation.PushAsync(new SelectPeriod()); 98}

アプリ画像

投稿2026/03/09 10:03

TN8001

総合スコア10257

0

ベストアンサー

ソースコードをご覧いただければわかると思いますが、ほとんどのソースコードが同じなのです。

全部は見ていないですが
C#はWHERE ""部分"" = '縄文01_社会'ここだけ
xamlはここだけと考えていいんですかね?

<Label Text="時代:縄文時代" <Label Text="区分:縄文01_社会"

なので、共通部分をまとめて、配列を使ってアクセスできないのか、改善したいということです。

「ほぼ同じのSection_0X00_X00が多すぎる」というのはよくわかりましたが、「xamlを配列に」というのがすいませんがよくわかりません。

先の回答も意図は同じなのですが、dic1980さんのコードを使って共通化してみました。
Section_0100だけでやってみましたが、ほんとに全部同様なら全部まとめられます。

xml:Section_0100_X00.xaml

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage 3 x:Class="dic1980_game.Section_0100.Section_0100_X00" 4 xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 5 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 6 Title="Section_0100_X00"> 7 <Grid Padding="12" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*"> 8 9 <Label x:Name="時代Label" Grid.Row="0" HorizontalOptions="Center" /> 10 <Label x:Name="区分Label" Grid.Row="1" HorizontalOptions="Center" /> 11 <Label x:Name="QuestionCountLabel" Grid.Row="2" HorizontalOptions="Center" /> 12 <Label x:Name="QuestionLabel" Grid.Row="3" HorizontalOptions="Center" /> 13 14 <VerticalStackLayout Grid.Row="4" Spacing="8"> 15 <Button x:Name="SelectButton1" Clicked="OnSection_Select1" Text="1" /> 16 <Button x:Name="SelectButton2" Clicked="OnSection_Select2" Text="2" /> 17 <Button x:Name="SelectButton3" Clicked="OnSection_Select3" Text="3" /> 18 <Button x:Name="SelectButton4" Clicked="OnSection_Select4" Text="4" /> 19 </VerticalStackLayout> 20 21 <VerticalStackLayout Grid.Row="5" Spacing="8"> 22 <Label x:Name="ResultLabel" IsVisible="false" /> 23 <Label x:Name="解説Label" IsVisible="false" /> 24 <Button 25 x:Name="NextButton" 26 Clicked="OnSection_NextButton" 27 IsVisible="false" 28 Text="次の問題へ" /> 29 <Button 30 x:Name="トップへもどるButton" 31 Clicked="OnSection_BackToTopButton" 32 IsVisible="false" 33 Text="記録してトップへもどる" /> 34 </VerticalStackLayout> 35 36 <ScrollView 37 x:Name="結果ScrollView" 38 Grid.Row="6" 39 Margin="4" 40 IsVisible="false" 41 Orientation="Vertical" 42 VerticalScrollBarVisibility="Always"> 43 <Label 44 x:Name="結果Label" 45 HorizontalOptions="Fill" 46 LineBreakMode="WordWrap" 47 VerticalOptions="Start" /> 48 </ScrollView> 49 </Grid> 50</ContentPage>

cs:Section_0100_X00.xaml.cs

1using Microsoft.Data.Sqlite; 2 3namespace dic1980_game.Section_0100; 4 5public partial class Section_0100_X00 : ContentPage 6{ 7 private const string dbFileName = "history_2026_01_24.sqlite3"; 8 private readonly string dbPath = Path.Combine(FileSystem.AppDataDirectory, dbFileName); 9 private readonly string read_SQL; 10 11 private int m_i現在の問題の番号; 12 private int m_i正解数; 13 private int m_i不正解数; 14 15 private readonly int[] m_i記録_問題ID = new int[10]; 16 private readonly int[] m_i記録_選択した答え = new int[10]; 17 private readonly int[] m_i記録_正誤 = new int[10]; 18 private readonly string[] m_i記録_区分_時代 = new string[10]; 19 private readonly string[] m_i記録_区分_部分 = new string[10]; 20 private readonly int[] m_i記録_難易度 = new int[10]; 21 22 private readonly List<QuizItem> m_quizItems = []; 23 24 // コンストラクタ引数で差分(縄文時代、縄文01_社会)をもらう 25 public Section_0100_X00(string period, string section) 26 { 27 InitializeComponent(); 28 29 // 縄文01_社会を差し込む 30 read_SQL = $""" 31 SELECT "ID", "時代", "部分", "問題", "選択肢1", "選択肢2", "選択肢3", "選択肢4", "答え", "難易度", "解説" 32 FROM "クイズ_問題" 33 WHERE "部分" = '{section}' 34 ORDER BY "ID"; 35 """; 36 37 時代Label.Text = $"時代:{period}"; // "時代:縄文時代" 38 区分Label.Text = $"区分:{section}";// "区分:縄文01_社会" 39 } 40 41 protected override async void OnAppearing() 42 { 43 base.OnAppearing(); 44 45 try 46 { 47 await LoadQuizItemsFromDatabaseAsync(); 48 } 49 catch (Exception ex) 50 { 51 await DisplayAlertAsync("DB読み込みエラー", ex.Message, "OK"); 52 } 53 54 問題を出す(); 55 } 56 57 private async Task LoadQuizItemsFromDatabaseAsync() 58 { 59// 省略 60 } 61 62 private void 結果を表示() 63 { 64// 省略 65 } 66 67 private void 問題を出す() 68 { 69// 省略 70 } 71 72 private void 答え合わせ(int selectNumber) 73 { 74// 省略 75 } 76 77 private async void OnSection_BackToTopButton(object? sender, EventArgs e) 78 { 79 if (Navigation != null && Navigation.NavigationStack.Count > 1) 80 { 81 try 82 { 83 if (!File.Exists(dbPath)) 84 { 85 await DisplayAlertAsync("DB書き込みエラー", $"データベースが見つかりません: {dbPath}", "OK"); 86 } 87 else 88 { 89 var connectionString = new SqliteConnectionStringBuilder 90 { 91 DataSource = dbPath, 92 Mode = SqliteOpenMode.ReadWrite, 93 }.ToString(); 94 95 using var connection = new SqliteConnection(connectionString); 96 connection.Open(); 97 98 using var transaction = connection.BeginTransaction(); 99 using var command = connection.CreateCommand(); 100 command.CommandText = 101 @"INSERT INTO ""クイズ_解答履歴"" (""問題ID"", ""選択した答え"", ""正誤"", ""区分_時代"", ""区分_部分"", ""難易度"") 102 VALUES (@id, @selected, @correct, @zidai, @section, @nanido);"; 103 104 var pId = command.CreateParameter(); pId.ParameterName = "@id"; command.Parameters.Add(pId); 105 var pSelected = command.CreateParameter(); pSelected.ParameterName = "@selected"; command.Parameters.Add(pSelected); 106 var pCorrect = command.CreateParameter(); pCorrect.ParameterName = "@correct"; command.Parameters.Add(pCorrect); 107 var pZidai = command.CreateParameter(); pZidai.ParameterName = "@zidai"; command.Parameters.Add(pZidai); 108 var pSection = command.CreateParameter(); pSection.ParameterName = "@section"; command.Parameters.Add(pSection); 109 var pNanido = command.CreateParameter(); pNanido.ParameterName = "@nanido"; command.Parameters.Add(pNanido); 110 111 int maxRecords = Math.Min(10, m_quizItems.Count); 112 for (var i = 0; i < maxRecords; i++) 113 { 114 if (m_i記録_問題ID[i] == 0 && m_i記録_選択した答え[i] == 0) 115 continue; 116 117 pId.Value = m_i記録_問題ID[i]; 118 pSelected.Value = m_i記録_選択した答え[i]; 119 pCorrect.Value = m_i記録_正誤[i]; 120 pZidai.Value = m_i記録_区分_時代[i] ?? string.Empty; 121 pSection.Value = m_i記録_区分_部分[i] ?? string.Empty; 122 pNanido.Value = m_i記録_難易度[i]; 123 124 command.ExecuteNonQuery(); 125 } 126 127 transaction.Commit(); 128 connection.Close(); 129 } 130 } 131 catch (Exception ex) 132 { 133 await DisplayAlertAsync("DB書き込みエラー", ex.Message, "OK"); 134 } 135 136 await Navigation.PopAsync(); 137 } 138 } 139 140 private void OnSection_NextButton(object? sender, EventArgs e) 141 { 142 m_i現在の問題の番号++; 143 問題を出す(); 144 } 145 146 private void OnSection_Select1(object? sender, EventArgs e) => 答え合わせ(1); 147 private void OnSection_Select2(object? sender, EventArgs e) => 答え合わせ(2); 148 private void OnSection_Select3(object? sender, EventArgs e) => 答え合わせ(3); 149 private void OnSection_Select4(object? sender, EventArgs e) => 答え合わせ(4); 150}

cs:Section_0100_00.xaml.cs

1using dic1980_game.Section_0000; 2 3namespace dic1980_game.Section_0100; 4 5public partial class Section_0100_00 : ContentPage 6{ 7 public Section_0100_00() => InitializeComponent(); 8 9 private async void OnSection0100_Clicked(object? sender, EventArgs e) 10 // コンストラクタ引数で差分(縄文時代、縄文01_社会)を渡す 11 => await Navigation.PushAsync(new Section_0100_X00("縄文時代", "縄文01_社会")); 12 13 private async void OnSection0200_Clicked(object? sender, EventArgs e) 14 => await Navigation.PushAsync(new Section_0100_X00("縄文時代", "縄文02_時代背景")); 15 16 private async void OnSection0300_Clicked(object? sender, EventArgs e) 17 => await Navigation.PushAsync(new Section_0100_X00("縄文時代", "縄文03_文化")); 18 19 private async void OnSectionBACK_Clicked(object? sender, EventArgs e) 20 => await Navigation.PushAsync(new Section_0000_10()); 21}

MVVMについては、まだ勉強していないので、ごめんなさい。

どちらの回答もMVVMではないです^^;

メニュー(Section_0000_10等)の部分はCollectionView等を使えばだいぶスッキリします。
気が向いたら試してみてください^^

投稿2026/03/08 15:28

編集2026/03/08 16:21
TN8001

総合スコア10257

normal1980

2026/03/09 04:15

なるほど、コンストラクタ時に引数として渡すのですね。 とても参考になりました。 ありがとうございました。
guest

0

どのように遷移しているかが不明ですが、遷移時にデータを渡すことは可能です。

ShellNavigationPageかおそらく2択だと思うので、難しめのShellで作ってみました。

Shellで遷移している場合はこちら。
.NET MAUI Shell ナビゲーション - .NET MAUI | Microsoft Learn

NavigationPageの場合はコンストラクタで渡せばいいので話が早い。
NavigationPage - .NET MAUI | Microsoft Learn

出来る限りバインドせずにコードビハインドで(こちらの遷移あり版といったところ)
[C#] [MAUI] MAUIの情報(日本語)が少なくて困っています。[受付終了] | teratail

xml:AppShell.xaml

1<?xml version="1.0" encoding="UTF-8" ?> 2<Shell 3 x:Class="Qbz2ow7ged9ubqs.AppShell" 4 xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 5 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 6 xmlns:local="clr-namespace:Qbz2ow7ged9ubqs" 7 FlyoutBehavior="Disabled"> 8 9 <Shell.BackButtonBehavior> 10 <BackButtonBehavior IsEnabled="False" IsVisible="False" /> 11 </Shell.BackButtonBehavior> 12 13 <ShellContent ContentTemplate="{DataTemplate local:MainPage}" Route="MainPage" /> 14 15</Shell>

cs:AppShell.xaml.cs

1namespace Qbz2ow7ged9ubqs; 2 3public partial class AppShell : Shell 4{ 5 public AppShell() 6 { 7 InitializeComponent(); 8 9 Routing.RegisterRoute("MainPage/QuizPage", typeof(QuizPage)); 10 } 11}

xml:MainPage.xaml

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage 3 x:Class="Qbz2ow7ged9ubqs.MainPage" 4 xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 5 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> 6 7 <CollectionView x:Name="CategoriesCollectionView" Margin="20"> 8 <CollectionView.ItemsLayout> 9 <LinearItemsLayout ItemSpacing="10" Orientation="Vertical" /> 10 </CollectionView.ItemsLayout> 11 <CollectionView.ItemTemplate> 12 <DataTemplate> 13 <!-- ItemsSourceがCategory[]なので、ここでのBindingContextはCategory単体 --> 14 <Button Clicked="Button_Clicked" Text="{Binding}" /> 15 </DataTemplate> 16 </CollectionView.ItemTemplate> 17 </CollectionView> 18 19</ContentPage>

cs:MainPage.xaml.cs

1namespace Qbz2ow7ged9ubqs; 2 3// 遷移時に渡すデータ兼カテゴリ?表示 4public record Category(string Period, string Section) 5{ 6 public override string ToString() => $"{Period} {Section}"; 7} 8 9public partial class MainPage : ContentPage 10{ 11 public MainPage() 12 { 13 InitializeComponent(); 14 15 CategoriesCollectionView.ItemsSource = new Category[] { 16 new("縄文時代", "縄文01_社会"), // Section_0100_100 17 new("縄文時代", "縄文02_時代背景"), // Section_0100_200 18 new("縄文時代", "縄文03_文化"), // Section_0100_300 19 // ここを増やせば良いだけになる^^ 20 }; 21 } 22 23 private async void Button_Clicked(object? sender, EventArgs e) 24 { 25 // ButtonのBindingContextはCategory単体になっている 26 if (sender is Button { BindingContext: Category category }) 27 { 28 // Dictionary形式で必要なデータを渡す 29 var parameters = new ShellNavigationQueryParameters 30 { 31 { "時代", category.Period }, 32 { "部分", category.Section }, 33 }; 34 await Shell.Current.GoToAsync("QuizPage", parameters); 35 } 36 } 37}

xml:QuizPage.xaml

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage 3 x:Class="Qbz2ow7ged9ubqs.QuizPage" 4 xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 5 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 6 xmlns:local="clr-namespace:Qbz2ow7ged9ubqs"> 7 8 <Grid Padding="12" RowDefinitions="Auto,Auto,Auto,Auto,Auto"> 9 <Label x:Name="PeriodLabel" Grid.Row="0" HorizontalOptions="Center" /> 10 <Label x:Name="SectionLabel" Grid.Row="1" HorizontalOptions="Center" /> 11 <Label x:Name="QuestionCountLabel" Grid.Row="2" HorizontalOptions="Center" /> 12 <Label x:Name="QuestionLabel" Grid.Row="3" HorizontalOptions="Center" /> 13 14 <CollectionView x:Name="ChoicesCollectionView" Grid.Row="4"> 15 <CollectionView.ItemsLayout> 16 <LinearItemsLayout ItemSpacing="10" Orientation="Vertical" /> 17 </CollectionView.ItemsLayout> 18 <CollectionView.ItemTemplate> 19 <DataTemplate> 20 <!-- ItemsSourceがstring[]なので、ここでのBindingContextはstring単体 --> 21 <Button Clicked="AnswerButton_Clicked" Text="{Binding}" /> 22 </DataTemplate> 23 </CollectionView.ItemTemplate> 24 <CollectionView.EmptyView> 25 <Button Clicked="BackButton_Clicked" Text="戻る" /> 26 </CollectionView.EmptyView> 27 </CollectionView> 28 </Grid> 29 30</ContentPage>

cs:QuizPage.xaml.cs

1namespace Qbz2ow7ged9ubqs; 2 3// クイズ1問のデータ 4public class Quiz(string question, params string[] choices) 5{ 6 public string Question { get; } = question; 7 public string[] Choices => [.. choices.Shuffle()]; // 選択肢ランダム 8 public string Answer { get; } = choices[0]; 9} 10 11public static class QuizService 12{ 13 // DBから取ってきてるテイ 14 public static Quiz[] GetQuizzes(string 時代, string 部分) 15 { 16 Quiz[] quizzes = [ 17 new Quiz("史上初の家庭用ゲーム機とされるものは?", "オデッセイ(マグナボックス)", "ホーム・ポン(アタリ)", "カラーテレビゲーム15(任天堂)"), 18 new Quiz("スーパーファミコンソフト「ドラゴンクエスト6 幻の大地」の定価(税抜)は?", "11,400円", "14,800円", "9,700円"), 19 new Quiz("PlayStationの同時発売ソフトは?", "リッジレーサー(ナムコ)", "モータートゥーン・グランプリ(SCE)", "チョロQ(タカラ)"), 20 ]; 21 return [.. quizzes.Shuffle()]; // 問題順ランダム 22 } 23} 24 25// ↓「ApplyQueryAttributes使うよ」という合図 26public partial class QuizPage : ContentPage, IQueryAttributable 27{ 28 private Quiz[] quizzes = null!; 29 private int index; 30 private int correct; 31 32 public QuizPage() => InitializeComponent(); 33 34 public void ApplyQueryAttributes(IDictionary<string, object> query) 35 { 36 // 渡されたデータを取得 37 var period = (string)query["時代"]; 38 var section = (string)query["部分"]; 39 40 // DBから取ってきてるテイ 41 quizzes = QuizService.GetQuizzes(period, section); 42 43 // 各ラベルにセット 44 PeriodLabel.Text = $"時代:{period}"; 45 SectionLabel.Text = $"区分:{section}"; 46 QuestionCountLabel.Text = $"第{index + 1}問"; 47 QuestionLabel.Text = quizzes[index].Question; 48 49 // 選択肢の配列(string[])をItemsSourceにセット 50 ChoicesCollectionView.ItemsSource = quizzes[index].Choices; 51 } 52 53 private async void AnswerButton_Clicked(object? sender, EventArgs e) 54 { 55 // ButtonのBindingContextはstring単体になっている 56 if (sender is Button { BindingContext: string answer }) 57 { 58 if (answer == quizzes[index].Answer) 59 { 60 correct++; 61 await DisplayAlertAsync("", "正解!", "次へ"); 62 } 63 else 64 { 65 await DisplayAlertAsync("", "はずれ", "次へ"); 66 } 67 68 index++; 69 if (index < quizzes.Length) // 次の問題 70 { 71 QuestionCountLabel.Text = $"第{index + 1}問"; 72 QuestionLabel.Text = quizzes[index].Question; 73 ChoicesCollectionView.ItemsSource = quizzes[index].Choices; 74 } 75 else // 結果表示 76 { 77 QuestionCountLabel.Text = $"{quizzes?.Length}問中{correct}問正解"; 78 QuestionLabel.Text = ""; 79 80 // 空配列をセットしてEmptyViewを出す 81 ChoicesCollectionView.ItemsSource = Array.Empty<string>(); 82 } 83 } 84 } 85 86 private async void BackButton_Clicked(object? sender, EventArgs e) 87 => await Shell.Current.GoToAsync(".."); // MainPageに戻る 88}

アプリ動画

投稿2026/03/08 08:45

TN8001

総合スコア10257

normal1980

2026/03/08 12:51

回答ありがとうございます。 ソースコードをアップロードしました。 そちらをご覧いただければと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.29%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問