2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

WPF(C#) .NET6.0 LocalDB のデータを ListView で表示しよう

Last updated at Posted at 2022-04-06

前回までに、WPF の ListView と LocalDB 接続を行ったので、今回は LocalDB のデータの更新と、ListView にリアルタイムに反映させる部分を作成します。

View の作成

  • <Grid.RowDefinitions> でデータ追加ボタンの行とListViewの行を作成
<Grid.RowDefinitions>
    <RowDefinition Height="30"/>
    <RowDefinition Height="*"/>
</Grid.RowDefinitions>
  • <StackPanel> で <TextBox><Button>コントロールを横並びに
<StackPanel Grid.Row="0" Orientation="Horizontal">
  • <TextBox> のデータは ViewModelの変数をバインド
<TextBox x:Name="UpdateFirstName" Text="{Binding InsertFirstName}" />
  • 「Insert」ボタンのコマンドは、ICommand で ViewModel の関数にバインド
<Button Content="Insert" Command="{Binding InsertCommand}" />
  • ListView 各行の4カラム目に削除ボタン実装
    コマンドのパラメータとして選択した行の Id をバインド
<Button Content="Delete" Command="{Binding DataContext.ListTrashCommand, ElementName=SampleListView}" CommandParameter="{Binding Id}" />

以下、今回作成したソースコードになります。

MainWindows.xaml
<Window x:Class="SampleWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SampleWPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <Label Content="First Name:" Width="70" Height="30" Margin="5,0,0,0" />
            <TextBox x:Name="UpdateFirstName" Text="{Binding InsertFirstName}" Width="100" Height="20" Margin="0,0,0,0" />
            <Label Content="Last Name:" Width="70" Height="30" Margin="5,0,0,0" />
            <TextBox x:Name="UpdateLastName" Text="{Binding InsertLastName}" Width="100" Height="20" Margin="0,0,0,0" />
            <Button Content="Insert"  Height="20" Width="60" Margin="5,0,0,0" Command="{Binding InsertCommand}" />
        </StackPanel>

        <ListView Grid.Row="1" x:Name="SampleListView" ItemsSource="{Binding UserList}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="30" Header="Id"  DisplayMemberBinding="{Binding Id}" />
                    <GridViewColumn Width="100" Header="First Name"  DisplayMemberBinding="{Binding FirstName}" />
                    <GridViewColumn Width="100" Header="Last Name"  DisplayMemberBinding="{Binding LastName}" />
                    <GridViewColumn Width="60">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Button Content="Delete" Height="20" Width="45" Command="{Binding DataContext.ListTrashCommand, ElementName=SampleListView}" CommandParameter="{Binding Id}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

前回のLocalDB へ初期データ投入部分は削除

  • View と ViewModel との連携部分のみに戻します。
this.DataContext = new MainWindowVM();
MainWindows.xaml.cs
MainWindows.xaml.cs
using SampleWPF.Models;
using SampleWPF.ViewModels;
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace SampleWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // ViewModel と連携
            this.DataContext = new MainWindowVM();
        }
    }
}

View - ViewModel のコマンドを連携

ViewModels フォルダ内に ICommand を継承した ForwardedCommand クラスを実装

  • パラメータ有りのジェネリッククラスとパラメータ無し版を実装
  • 「CS8767 参照型の NULL 値の許容」警告を抑制
ViewModels\ForwardedCommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace SampleWPF.ViewModels
{
    public class ForwardedCommand : ICommand
    {
        private Action action;
        private Func<bool> canExecute;
        public ForwardedCommand(Action a) : this(a, () => true) { }
        public ForwardedCommand(Action a, Func<bool> cx)
        {
            action = a;
            canExecute = cx;
        }
        public event EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
#pragma warning disable CS8767
        public bool CanExecute(object parameter)
#pragma warning restore CS8767
        {
            return canExecute();
        }
#pragma warning disable CS8767
        public void Execute(object parmeter)
#pragma warning restore CS8767
        {
            action();
        }

    }

    // Parameter 付き Command
    public class ForwardedCommand<T> : ICommand
    {
        private Action<T> action;
        private Func<bool> canExecute;
        public ForwardedCommand(Action<T> a) : this(a, () => true) { }

        public ForwardedCommand(Action<T> a, Func<bool> cx)
        {
            action = a;
            canExecute = cx;
        }

        public event EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

#pragma warning disable CS8767
        public bool CanExecute(object parameter)
#pragma warning restore CS8767
        {
            return canExecute();
        }

#pragma warning disable CS8767
        public void Execute(object parameter)
#pragma warning restore CS8767
        {
            action((T)parameter);
        }
    }

}

MainWindowVM にDB接続+ボタンコマンド実装

今回は ObservableCollection 自体の更新のみなので INotifyPropertyChanged の実装は割愛しました。

  • ForwardedCommand で、View からのコマンドを ViewModel 関数へ通知
public ICommand InsertCommand { get; private set; }
public void ExecuteInsert()
{
    // コマンド実行時の処理
}

InsertCommand = new ForwardedCommand(ExecuteInsert);
  • ListView のデータ更新は ObservableCollection 型の UserList 自体を入れ替えれ更新を通知
using (var dbcx = new SampleDbContext())
{
    UserList.Clear();
    ObservableCollection<User> uList = new ObservableCollection<User>(dbcx.Users.ToList());
    foreach (var user in uList) this.UserList.Add(user);
}
  • 「CS8618 コンストラクターの終了時に null の可能性」警告をクラス変数の初期値を空代入して抑制
insertFirstName = "";
insertLastName = "";

以下がMainWindowVM.cs全体となります。

ViewModels\MainWindowVM.cs
using SampleWPF.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace SampleWPF.ViewModels
{
    public class MainWindowVM    {

        public static ObservableCollection<User>? userList { get; set; }
        public ObservableCollection<User> UserList
        {
#pragma warning disable CS8603 // Null 参照戻り値である可能性があります。
            get => userList;
#pragma warning restore CS8603 // Null 参照戻り値である可能性があります。
            set
            {
                userList = value;
            }
        }

        // Insert用データ
        private string insertFirstName;
        public string InsertFirstName
        {
            get => insertFirstName;
            set
            {
                insertFirstName = value;
            }
        }
        private string insertLastName;
        public string InsertLastName
        {
            get => insertLastName;
            set
            {
                insertLastName = value;
            }
        }

        private void UpdateUserList()
        {
            using (var dbcx = new SampleDbContext())
            {
                UserList.Clear();
                ObservableCollection<User> uList = new ObservableCollection<User>(dbcx.Users.ToList());
                foreach (var user in uList) this.UserList.Add(user);
            }

        }

        // ForwardedCommand
        public ICommand InsertCommand { get; private set; }
        public void ExecuteInsert()
        {
            using (var dbcx = new SampleDbContext())
            {
                // LocalDB 更新
                User insertUser = new()
                {
                    // Id は LocalDB で自動的に付与される
                    FirstName = InsertFirstName,
                    LastName = InsertLastName
                };
                dbcx.Users.Add(insertUser);
                dbcx.SaveChanges();
            }
            UpdateUserList();

        }

        public ICommand ListTrashCommand { get; private set; }
        public void ExecuteTrash(int id)
        {
            using (var dbcx = new SampleDbContext())
            {
                // LocalDB 更新
                var user = dbcx.Users.Single(x => x.Id == id);
                dbcx.Users.Remove(user);

                dbcx.SaveChanges();
            }
            UpdateUserList();
        }

        public MainWindowVM()
        {
            UserList = new ObservableCollection<User>();

            InsertCommand = new ForwardedCommand(ExecuteInsert);
            ListTrashCommand = new ForwardedCommand<int>(ExecuteTrash);

            // CS8618 対応
            insertFirstName = "";
            insertLastName = "";

            // UserList 初期化
            UpdateUserList();
        }

    }
}

SampleWPF を実行してみる

以下のように表示されれば成功です!
1.ListViewLocalDB.PNG
「Insert」「Delete」ボタンをを試してください。

次回予告

障害対応、デバッグに必須な Log4net を実装する予定です。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?