ゼロからはじめるVisual C#日記.5 (データベースとか色々手直しとか)

前の日記
ゼロからはじめるVisual C#日記.4 (SQLite導入とか)

開発が軌道に乗ってきました 多分無敵です。
ある程度できてくるとモチベ永久機関になるから楽しい。逆にここまでが辛い。

データベース設計

画像1

前の仮データベースを投げ捨ててしっかり設計します。今回は外部キーは実装では使ってないです。設定方法がよくわからなかった。
Genreはカメラ機材や照明機材など、Machineは具体的な機材名、Equipmentは機材番号。
悩みポイントは、Equipment.IsLending。ある機材が貸出中かどうか知りたいときに、これが無いとIsReturnedがfalseのLendidで貸出中機材一覧を取得するか、LendingEquipmentからその機材のLendIdを取得してIsReturnedを調べていく必要があるので入れたんですけど、こういう一応取得できるものを追加するのはちょっと不安。
考えられるデメリットとしては機材返却時にLendとEquipment両方更新しないとバグる。これやっていいのかな、勉強不足なのでわからない。
まあ専門外なのでゆるして...
(図はこのサイトで作りました。便利だなぁ...)

色々作るよ!

まずFormData.csをデータベースに合わせて作り直し。

public class FormData
{
   public string IdDepartment { get; set; }
   public string IdYear { get; set; }
   public string IdNumber { get; set; }
   public string IdName { get; set; }
   public string FormClass { get; set; }
   public string FormDay { get; set; }
   public string FormPeriod { get; set; }
   public string FormTeam { get; set; }
   public string LendDate { get; set; }
   public string ReturnDueDate { get; set; }
   public bool IsClass { get; set; }
   public bool IsOrg { get; set; }
   public List<Equipment> EquipmentList { get; set; }
   public class Equipment
   {
       public string MachineId { get; set; }
       public string EquipmentId { get; set; }
       public string Remarks { get; set; }
   }
}

当たり前ですが、このList<Equipment> EquipmentListは初期化せずに.Add()するとエラーになるので気を付けましょう(1敗)。

SelectTeamも作り直し。団体名も選べるように。SelectTeam.xaml.csでいろいろがんばります。xaml側はスクショから想像できる通りです。

private void ClassBoxChecked(object sender, RoutedEventArgs e)
{
   ClassCard.Visibility = System.Windows.Visibility.Visible;
   OrgBox.IsEnabled = false;
   CheckForm(null, null);
}

private void ClassBoxUnChecked(object sender, RoutedEventArgs e)
{
   ClassCard.Visibility = System.Windows.Visibility.Collapsed;
   OrgBox.IsEnabled = true;
   NextButton.IsEnabled = true;
}

private void OrgBoxChecked(object sender, RoutedEventArgs e)
{
   OrgCard.Visibility = System.Windows.Visibility.Visible;
   ClassBox.IsEnabled = false;
   CheckForm(null, null);
}

private void OrgBoxUnChecked(object sender, RoutedEventArgs e)
{
   OrgCard.Visibility = System.Windows.Visibility.Collapsed;
   ClassBox.IsEnabled = true;
   NextButton.IsEnabled = true;
}

private void CheckForm(object sender, TextChangedEventArgs e)
{
   bool res = false;
   if ((bool)ClassBox.IsChecked)
   {
       bool formclass = (TextBoxClass.Text.Length > 0);
       bool period = Regex.IsMatch(TextBoxPeriod.Text, "[1-9]{1}");
       bool team = (TextBoxTeam.Text.Length > 0);
       res = formclass && period && team;
   }
   else if ((bool)OrgBox.IsChecked)
   {
       bool org = (TextBoxOrg.Text.Length > 0);
       res = org;
   }
   else
   {
       res = true;
   }
   NextButton.IsEnabled = res;
}

Visibilityでフォームの切替と、誤操作でバグらないよう丁寧に表示を切り替えます。どんな人が最初に触った時でもバグらずやりたいことができる、というのを基本方針にしてます。

画像2

そして機材入力画面のScanTagを作っていきます。まずはxaml

<Grid Grid.Column="0">
   <Grid.RowDefinitions>
       <RowDefinition Height="400" />
       <RowDefinition Height="150" />
   </Grid.RowDefinitions>
   <materialDesign:Card Margin="20,0,20,0">
       <StackPanel Grid.Row="0" Orientation="Vertical" HorizontalAlignment="Right" Margin="0,0,25,0">
           <StackPanel Height="80" Orientation="Horizontal" HorizontalAlignment="Right">
               <TextBlock Style="{DynamicResource MaterialDesignTextBlock}"
                      VerticalAlignment="Center"
                      FontSize="24">機材種別:</TextBlock>
               <ComboBox Grid.Row="0" x:Name="GenreComboBox"
                     Width="240" VerticalAlignment="Center"
                     FontSize="24"
                     SelectedValuePath="Id"
                     DisplayMemberPath="Name" SelectionChanged="GenreComboBox_SelectionChanged"/>
           </StackPanel>
           <StackPanel Height="80" Orientation="Horizontal" HorizontalAlignment="Right">
               <TextBlock Style="{DynamicResource MaterialDesignTextBlock}"
                      VerticalAlignment="Center" Margin="0,0,0,0"
                      FontSize="24">機材名:</TextBlock>
               <ComboBox Grid.Row="0" x:Name="MachineComboBox"
                     Width="240" VerticalAlignment="Center"
                     FontSize="24"
                     IsEnabled="False"
                     SelectedValuePath="Id"
                     DisplayMemberPath="Name" SelectionChanged="MachineComboBox_SelectionChanged"/>
           </StackPanel>
           <StackPanel Height="80" Orientation="Horizontal" HorizontalAlignment="Right">
               <TextBlock Style="{DynamicResource MaterialDesignTextBlock}"
                      VerticalAlignment="Center" Margin="0,0,0,0"
                      FontSize="24">機材番号:</TextBlock>
               <ComboBox Grid.Row="0" x:Name="EquipmentComboBox"
                     Width="240" VerticalAlignment="Center"
                     FontSize="24"
                     IsEnabled="False"
                     SelectedValuePath="Id"
                     DisplayMemberPath="Name" SelectionChanged="EquipmentComboBox_SelectionChanged"/>
           </StackPanel>
           <StackPanel Height="80" Orientation="Horizontal" HorizontalAlignment="Right">
               <StackPanel Orientation="Horizontal">
                   <TextBlock Style="{DynamicResource MaterialDesignTextBlock}"
                      VerticalAlignment="Center" Margin="0,0,0,0"
                      FontSize="24">バッテリー数:</TextBlock>
                   <TextBox Margin="0,0,0,0" VerticalAlignment="Center"
                                Width="50" FontSize="24" x:Name="TextBoxBattery"
                                materialDesign:HintAssist.Hint="1個"/>
               </StackPanel>
               <StackPanel Orientation="Horizontal" Margin="20,0,0,0">
                   <TextBlock Style="{DynamicResource MaterialDesignTextBlock}"
                      VerticalAlignment="Center"
                      FontSize="24">三脚:</TextBlock>
                   <TextBox Margin="0,0,0,0" VerticalAlignment="Center"
                                Width="100" FontSize="24" x:Name="TextBoxTripod"
                                materialDesign:HintAssist.Hint="大:0個"/>
               </StackPanel>
           </StackPanel>
           <StackPanel Height="80" Orientation="Horizontal" HorizontalAlignment="Right">
               <TextBlock Style="{DynamicResource MaterialDesignTextBlock}"
                      VerticalAlignment="Center" Margin="0,0,0,0"
                      FontSize="24">備考:</TextBlock>
               <TextBox Margin="0,0,0,0" VerticalAlignment="Center"
                                Width="300" FontSize="24" x:Name="TextBoxRemarks"/>
           </StackPanel>
       </StackPanel>
   </materialDesign:Card>
   <Button Grid.Column="0" Grid.Row="1"
           x:Name="AddButton" Content="追加する"
           Style="{DynamicResource MaterialDesignRaisedLightButton}"
           Width="200" Height="100" FontSize="24"
           IsEnabled="False" 
           Click="Add_Click"/>
</Grid>

スクリーンショットの左のフォームですが、だいぶ書き方が上手くなってきました。他のページも作り直したい。インデントとかが雑なのは許してください。

画像3

ここから先はScanTag.xaml.csです。長いので分割

private ObservableCollection<Clct> _genre = new ObservableCollection<Clct>();
private ObservableCollection<Clct> _machine = new ObservableCollection<Clct>();
private ObservableCollection<Clct> _equipment = new ObservableCollection<Clct>();

private void CreateComboBox()
{
   using (SQLiteConnection conn = new SQLiteConnection("Data Source=MdiSyS.db"))
   {
       conn.Open();
       SQLiteCommand cmd = conn.CreateCommand();
       cmd.CommandText = "SELECT * FROM Genre";
       using (SQLiteDataReader reader = cmd.ExecuteReader())
       {
           while (reader.Read())
           {
               _genre.Add(new Clct()
               {
                   Id = reader["GenreId"].ToString(),
                   Name = reader["GenreName"].ToString()
               });
           }
       }
       conn.Close();
   }
   GenreComboBox.ItemsSource = _genre;
}

private void GenreComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
   MachineComboBox.SelectedIndex = -1;
   _machine.Clear();
   EquipmentComboBox.IsEnabled = false;
   if (GenreComboBox.SelectedIndex == -1)
   {
       MachineComboBox.IsEnabled = false;
       return;
   }
   else
   {
       MachineComboBox.IsEnabled = true;
   }
   using (SQLiteConnection conn = new SQLiteConnection("Data Source=MdiSyS.db"))
   {
       conn.Open();
       SQLiteCommand cmd = conn.CreateCommand();
       cmd.CommandText = "SELECT * FROM Machine WHERE GenreId=" + GenreComboBox.SelectedValue;
       using (SQLiteDataReader reader = cmd.ExecuteReader())
       {
           while (reader.Read())
           {
               _machine.Add(new Clct()
               {
                   Id = reader["MachineId"].ToString(),
                   Name = reader["MachineName"].ToString() + " (" + reader["MachineDescription"].ToString() + ")"
               });
           }
       }
       conn.Close();
   }
   MachineComboBox.ItemsSource = _machine;
}

private void MachineComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
   EquipmentComboBox.SelectedIndex = -1;
   _equipment.Clear();
   if (MachineComboBox.SelectedIndex == -1)
   {
       EquipmentComboBox.IsEnabled = false;
       return;
   }
   else
   {
       EquipmentComboBox.IsEnabled = true;
   }
   using (SQLiteConnection conn = new SQLiteConnection("Data Source=MdiSyS.db"))
   {
       conn.Open();
       SQLiteCommand cmd = conn.CreateCommand();
       cmd.CommandText = "SELECT * FROM Equipment WHERE MachineId=" + MachineComboBox.SelectedValue;
       using (SQLiteDataReader reader = cmd.ExecuteReader())
       {
           while (reader.Read())
           {
               if(reader["IsLending"].ToString() == "false")
               {
                   _equipment.Add(new Clct()
                   {
                       Id = reader["EquipmentId"].ToString(),
                       Name = "No." + reader["EquipmentId"].ToString() + " (" + reader["EquipmentRemarks"].ToString() + ")"
                   });
               }
           }
       }
       conn.Close();
   }
   List<Select> tempList = GridList.Where(x => x._MachineId == MachineComboBox.SelectedValue.ToString()).ToList();
   foreach (var item in tempList)
   {
       _equipment.Remove(_equipment.Where(i => i.Id == item._EquipmentId).Single());
   }
   EquipmentComboBox.ItemsSource = _equipment;
}

private void EquipmentComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
   AddButton.IsEnabled = (EquipmentComboBox.SelectedIndex != -1);
}

ページ読み込み時にCreateComboBox()で機材種別のComboBoxにデータを入れます。これはGenreテーブルから持ってきます。Clctは前作ったGakubuクラスの名前を変えたものです。

そしてGenreComboBoxが変更されたとき、機材名と機材番号をリセットし、選ばれてる機材種別が空白じゃなければ、その種別の機材名をデータベースから持ってきてMachineComboBoxに入れます。

これと同じことをMachineComboBox変更時にもします。ここでは、既に選択されている機材番号を削除する処理を最後にしています。
ここで使っているMachineComboBox.SelectedValueが便利だった。というかこれまでめんどくさいやり方をしていた。こういうのを調べるのが意外と難しい。。。

EquipmentComboBoxは変更時に空白じゃなければ追加ボタンを有効にします。

public class Select
{
   public string _Machine { get; set; }
   public string _MachineId { get; set; }
   public string _Equipment { get; set; }
   public string _EquipmentId { get; set; }
   public string _Remarks { get; set; }
}

private void Add_Click(object sender, RoutedEventArgs e)
{
   string remarks = ((TextBoxBattery.Text.Length > 0) ? "バッテリー:" + TextBoxBattery.Text + " " : "") +
                    ((TextBoxTripod.Text.Length > 0) ? "三脚:" + TextBoxTripod.Text + " " : "") + 
                     "備考:" + ((TextBoxRemarks.Text.Length > 0) ? TextBoxRemarks.Text : "なし");
   GridList.Add(new Select()
   {
       _Machine = ((Clct)MachineComboBox.SelectedItem).Name,
       _MachineId = ((Clct)MachineComboBox.SelectedItem).Id,
       _Equipment = ((Clct)EquipmentComboBox.SelectedItem).Name,
       _EquipmentId = ((Clct)EquipmentComboBox.SelectedItem).Id,
       _Remarks = remarks
   });
   GenreComboBox.SelectedIndex = -1;
   TextBoxBattery.Text = "";
   TextBoxTripod.Text = "";
   TextBoxRemarks.Text = "";
   NextButton.IsEnabled = true;
}

private void Next_Click(object sender, RoutedEventArgs e)
{
   foreach(var item in GridList)
   {
       FormData.EquipmentList = new List<FormData.Equipment>();
       FormData.EquipmentList.Add(new FormData.Equipment()
       {
           MachineId = item._MachineId,
           EquipmentId = item._EquipmentId,
           Remarks = item._Remarks
       });
   }
   Confirm page = new Confirm()
   {
       FormData = FormData
   };
   NavigationService.Navigate(page);
}

追加ボタンと次ページにデータ渡す処理。
ここでは日記.3の疑問を解決することができました。MachineComboBox.SelectedItem.Nameができない、という疑問でしたが、((Clct)MachineComboBox.SelectedItem).NameのようにSelectedItemをしっかりキャストしてあげるとうまくいきました。

ここまで来て、とんでもないミスがあって悲しくなりました。

画像4

🤔...
これ、自動入力で勝手にミスが広がって修正が大変でした。気を付けよう...
そもそも変数名のつけ方が下手すぎるので勉強したいわね。。

さいごに

完成しそうでなかなか完成しない。まだ1/3ぐらいな気がするのでがんばりたい。
うにてぃも触りたい。いつまで学校オンライン授業なのかな。夏休みまでとかだったら何かしらは出来そう。

リングフィット抽選販売、落選!w
900台に応募20万件って何事ですか???

次の日記
ゼロからはじめるVisual C#日記.6

この記事が気に入ったらサポートをしてみませんか?