How to update database automatically from ObervableCollection

Sarah 186 Reputation points
2022-02-12T10:57:30.343+00:00

Hi, I want to update the database automatically every time I make a change in ObervableCollection.
Until now I have updated the Databese directly after the changes in ObservableCollection. Is there a better way?

After editing an item in the ListView, the Database was not updated. Can someone tell me why this is not happening?

The connection to Databese takes a long time. am I doing something wrong? Or is there a better way?

Thanks
Sarah

MainWindow

<Window x:Class="MyContacts.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:MyContacts"
        mc:Ignorable="d"
        Title="My Contcats" Height="450" Width="500">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="400"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>

        <ListView Grid.Column="0" x:Name="ContactListView" ItemsSource="{Binding View}" Margin="5">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="CadetBlue" BorderThickness="0,2,0,2" Width="375" Padding="5">
                        <StackPanel>
                            <TextBlock Text="{Binding Firstname}" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding Lastname}" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding Birthday, StringFormat={}{0:dd/MM/yyyy} }" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding Address}" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding Email}" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding Phone}" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding Specialty}" TextWrapping="Wrap"/>
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <StackPanel Grid.Column="1" Orientation="Vertical" VerticalAlignment="Top" Margin="0,0,0,0">
            <Button Height="30" Width="60" Content="Add" Margin="10" Click="Add_Click"/>
            <Button Height="30" Width="60" Content="Edit" Margin="10" Click="Edit_Click"/>
            <Button Height="30" Width="60" Content="Remove" Margin="10" Click="Remove_Click"/>
        </StackPanel>
    </Grid>
</Window>

Code

using System.Windows;


namespace MyContacts
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            ViewModel.Instance.FillList();
            this.DataContext = ViewModel.Instance; 
        }

        private void Add_Click(object sender, RoutedEventArgs e)
        {
            var addContactWindow = new AddContactWindow();
            ContactDetail contactData = new ContactDetail();
            addContactWindow.DataContext = contactData;
            addContactWindow.ShowDialog();
            if (addContactWindow.DataContext != null)
            {
                ViewModel.Instance.ContactDetails.Add(contactData);
                ViewModel.Instance.AddContactToDB(contactData);
                ViewModel.Instance.OnPropertyChanged("View");
            }
        }

        private void Edit_Click(object sender, RoutedEventArgs e)
        {
            if (ContactListView.SelectedItem != null)
            {
                ContactDetail contactData = ContactListView.SelectedItem as ContactDetail;
                if (contactData == null)
                {
                    return;
                }
                else
                {
                    var addContactWindow = new AddContactWindow();
                    contactData.Cache();
                    addContactWindow.DataContext = contactData;
                    addContactWindow.ShowDialog();
                    if (addContactWindow.DataContext == null)
                    {
                        contactData.Restore();
                    }
                }
            }
        }

        private void Remove_Click(object sender, RoutedEventArgs e)
        {
            if (ContactListView.SelectedItem != null)
            {
                MyContacts.ViewModel.Instance.ContactDetails.Remove((ContactDetail)ContactListView.SelectedItem);
                ViewModel.Instance.RemoveContactFromDB((ContactDetail)ContactListView.SelectedItem);
            }
        }
    }
}

AddContactWindow

<Window x:Class="MyContacts.AddContactWindow"
        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:MyContacts"
        mc:Ignorable="d"
        Title="Contact" Height="350" Width="400">
    <Grid>
        <StackPanel>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,10,0,10">
                <Label Content="Firstname:" Width="120" />
                <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Firstname, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
                <Label Content="Lastname:" Width="120" />
                <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Lastname, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
                <Label Content="Birthday:" Width="120" />
                <DatePicker x:Name="Birthday" SelectedDateFormat="Short"  Height="25" Width="230" FirstDayOfWeek="Monday" IsTodayHighlighted="True" SelectedDate="{Binding Birthday, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
                <Label Content="Address:" Width="120" />
                <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Address, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
                <Label Content="Phone:" Width="120" />
                <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Phone, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
                <Label Content="Email:" Width="120" />
                <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Email, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
                <Label Content="Specialty:" Width="120" />
                <ComboBox x:Name="Specialty" Text="{Binding Path=Specialty, UpdateSourceTrigger=PropertyChanged}" Height="25" Width="230" Margin="0,0,5,0" >
                    <ComboBoxItem Content="A"/>
                    <ComboBoxItem Content="B"/>
                    <ComboBoxItem Content="C"/>
                </ComboBox>
            </StackPanel>
        </StackPanel>

        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="10,0,10,10">
            <Button Height="30" Width="70" Content="Close" Margin="10,0,0,0" Click="Close_Click" />
            <Button Height="30" Width="70" Content="Save" Margin="10,0,0,0" Click="Save_Click" />
        </StackPanel>
    </Grid>
</Window>

Code

using System.Windows;

namespace MyContacts
{
    /// <summary>
    /// Interaktionslogik für Contact.xaml
    /// </summary>
    public partial class AddContactWindow : Window
    {
        public AddContactWindow()
        {
            InitializeComponent();
        }

        private void Close_Click(object sender, RoutedEventArgs e)
        {
            this.DataContext = null;
            Close();
        }

        private void Save_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }
    }
}

ContactDetail

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Data;

namespace MyContacts
{
    public class ContactDetail : INotifyPropertyChanged
    {
        private int _Id;
        private string _Firstname;
        private string _Lastname;
        private string _Address;
        private string _Phone;
        private string _Email;
        private DateTime _Birthday = DateTime.Now;
        private string _Specialty;


        public ContactDetail() { }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));


        #region Properties Getters and Setters

        public int Id
        {
            get { return _Id; }
            set { _Id = value; OnPropertyChanged(); }
        }

        public string Firstname
        {
            get { return _Firstname; }
            set { _Firstname = value; OnPropertyChanged(); }
        }

        public string Lastname
        {
            get { return _Lastname; }
            set { _Lastname = value; OnPropertyChanged(); }
        }

        public string Address
        {
            get { return _Address; }
            set { _Address = value; OnPropertyChanged(); }
        }

        public string Phone
        {
            get { return _Phone; }
            set { _Phone = value; OnPropertyChanged(); }
        }

        public string Email
        {
            get { return _Email; }
            set { _Email = value; OnPropertyChanged(); }
        }

        public DateTime Birthday
        {
            get { return _Birthday; }
            set { _Birthday = value; OnPropertyChanged(); }
        }

        public string Specialty
        {
            get { return _Specialty; }
            set { _Specialty = value; OnPropertyChanged(); }
        }

        #endregion Properties Getters and Setters

        private int _Id_Cache;
        private string _Firstname_Cache;
        private string _Lastname_Cache;
        private string _Address_Cache;
        private string _Phone_Cache;
        private string _Email_Cache;
        private DateTime _Birthday_Cache;
        private string _Specialty_Cache;

        public void Cache()
        {
            _Id_Cache = _Id;
            _Firstname_Cache = _Firstname;
            _Lastname_Cache = _Lastname;
            _Address_Cache = _Address;
            _Phone_Cache = _Phone;
            _Email_Cache = _Email;
            _Birthday_Cache = _Birthday;
            _Specialty_Cache = _Specialty;
        }
        public void Restore()
        {
            Id = _Id_Cache;
            Firstname = _Firstname_Cache;
            Lastname = _Lastname_Cache;
            Address = _Address_Cache;
            Phone = _Phone_Cache;
            Email = _Email_Cache;
            Birthday = _Birthday_Cache;
            Specialty = _Specialty_Cache;
        }
    }

    public class ViewModel : INotifyPropertyChanged
    {

        private static ViewModel _instance;
        static ViewModel()
        {
            _instance = new ViewModel();
            _instance.ContactDetails = new ObservableCollection<ContactDetail>();
            _instance.cvs.Source = _instance.ContactDetails;
        }

        public static ViewModel Instance { get => _instance; }

        public ICollectionView View { get => cvs.View; }
        private CollectionViewSource cvs = new CollectionViewSource();

        public ObservableCollection<ContactDetail>? ContactDetails { get; set; }


        public event PropertyChangedEventHandler? PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string name = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

        AppDbContext db = new AppDbContext();

        public void FillList()
        {

            ContactDetails.Clear();
            var contactData = db.tblContact.ToList();

            foreach (var contact in contactData)
            {
                ContactDetails.Add(contact);
            }
        }

        public void RemoveContactFromDB(ContactDetail contact)
        {
                db.tblContact.Remove(contact);
                db.SaveChanges();
        }

        public void AddContactToDB(ContactDetail contact)
        {
                db.tblContact.Add(contact);
                db.SaveChanges();
        }

    }
}

Class AppDbContext

using Microsoft.EntityFrameworkCore;

namespace MyContacts
{
    public class AppDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=...\ContactDB.mdf;Integrated Security=True");
        }

        public DbSet<ContactDetail> tblContact { get; set; }
    }
}
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,669 questions
0 comments No comments
{count} votes

5 answers

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,231 Reputation points
    2022-02-13T09:16:33.4+00:00

    Hi Sarah,
    use SaveChange after Dialog like in following demo with your code:

    XAML MainWindow:

    <Window x:Class="WpfApp1.Window004"
            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:WpfApp004"
            mc:Ignorable="d"
            Title="Sarah-3412_220212_MyContcats" Height="450" Width="1200">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="1000"/>
          <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
    
        <ListView Grid.Column="0" ItemsSource="{Binding View}" Margin="5" IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding CurrentContact}">
          <ListView.ItemTemplate>
            <DataTemplate>
              <Border BorderBrush="CadetBlue" BorderThickness="0,2,0,2" Width="375" Padding="5">
                <StackPanel>
                  <TextBlock Text="{Binding Firstname}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Lastname}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Birthday, StringFormat={}{0:dd/MM/yyyy} }" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Address}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Email}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Phone}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Specialty}" TextWrapping="Wrap"/>
                </StackPanel>
              </Border>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    
        <StackPanel Grid.Column="1" Orientation="Vertical" VerticalAlignment="Top" Margin="0,0,0,0">
          <Button Height="30" Width="60" Content="Add" Margin="10" Command="{Binding Cmd}" CommandParameter="Add_Click"/>
          <Button Height="30" Width="60" Content="Edit" Margin="10" Command="{Binding Cmd}" CommandParameter="Edit_Click"/>
          <Button Height="30" Width="60" Content="Remove" Margin="10" Command="{Binding Cmd}" CommandParameter="Remove_Click"/>
        </StackPanel>
      </Grid>
    </Window>
    

    XAML ContactWindow:

    <Window x:Class="WpfApp1.Window004A"
            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:WpfApp004"
            mc:Ignorable="d"
            Title="Sarah-3412_220212_Contact" Height="350" Width="400">
      <Grid>
        <StackPanel DataContext="{Binding CurrentContact}">
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,10,0,10">
            <Label Content="Firstname:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Firstname}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Lastname:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Lastname}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Birthday:" Width="120" />
            <DatePicker x:Name="Birthday" SelectedDateFormat="Short"  Height="25" Width="230" FirstDayOfWeek="Monday" IsTodayHighlighted="True" SelectedDate="{Binding Birthday}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Address:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Address}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Phone:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Phone}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Email:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Email}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Specialty:" Width="120" />
            <ComboBox x:Name="Specialty" Text="{Binding Path=Specialty}" Height="25" Width="230" Margin="0,0,5,0" >
              <ComboBoxItem Content="A"/>
              <ComboBoxItem Content="B"/>
              <ComboBoxItem Content="C"/>
            </ComboBox>
          </StackPanel>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="10,0,10,10">
          <Button Height="30" Width="70" Content="Close" Margin="10,0,0,0" Command="{Binding Cmd}" CommandParameter="Close_Click" />
          <Button Height="30" Width="70" Content="Save" Margin="10,0,0,0" Command="{Binding Cmd}" CommandParameter="Save_Click" />
        </StackPanel>
      </Grid>
    </Window>
    

    and classes:

    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Input;
    using WpfApp1;
    
    namespace WpfApp004
    {
      public class ViewModel : FrameworkElement, INotifyPropertyChanged
      {
        AppDbContext ctx = new AppDbContext();
        public ICollectionView View
        {
          get
          {
            ctx.tblContact.Load();
            cvs.Source = ctx.tblContact.Local.ToObservableCollection();
            cvs.View.Refresh();
            return cvs.View;
          }
        }
        private CollectionViewSource cvs = new CollectionViewSource();
        public ObservableCollection<ContactDetail>? ContactDetails { get; set; }
    
        private ContactDetail _currentContact;
        public ContactDetail CurrentContact { get => this._currentContact; set { this._currentContact = value; OnPropertyChanged(nameof(Cmd)); } }
    
        private Window004A addContactWindow;
        private bool? ret;
    
        public ICommand Cmd { get { return new RelayCommand(CmdExecute, CmdCanExecute); } }
    
        public event EventHandler? CanExecuteChanged;
        public void CmdExecute(object parameter)
        {
          switch (parameter.ToString())
          {
            case "Add_Click":
              addContactWindow = new Window004A() { DataContext = this };
              CurrentContact = new ContactDetail();
              ret = addContactWindow.ShowDialog();
              if (ret.HasValue && ret.Value) ctx.tblContact.Add(CurrentContact);
              else CurrentContact = null;
              ctx.SaveChanges();
              break;
            case "Edit_Click":
              if (CurrentContact != null)
              {
                addContactWindow = new Window004A() { DataContext = this };
                ret = addContactWindow.ShowDialog();
                if (ret.HasValue && ret.Value) ctx.SaveChanges();
                else
                {
                  ctx.Entry(CurrentContact).State = EntityState.Unchanged;
                  OnPropertyChanged(nameof(View));
                }
              }
              break;
            case "Remove_Click":
              if (CurrentContact != null) ctx.tblContact.Remove(CurrentContact);
              ctx.SaveChanges();
              break;
            case "Close_Click":
              addContactWindow.DialogResult = false;
              break;
            case "Save_Click":
              addContactWindow.DialogResult = true;
              break;
            default:
              break;
          }
        }
    
        public bool CmdCanExecute(object parameter) =>
          ((parameter.ToString() != "Edit_Click" && parameter.ToString() != "Remove_Click") || CurrentContact != null);
    
        public event PropertyChangedEventHandler? PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string name = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
      }
    
      public class AppDbContext : DbContext
      {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
          optionsBuilder.UseSqlServer(WpfApp1.Properties.Settings.Default.cnSQL);
    
        public DbSet<ContactDetail> tblContact { get; set; }
      }
    
      public class ContactDetail : INotifyPropertyChanged
      {
        #region private fields
        private string _Firstname = String.Empty;
        private string _Lastname = String.Empty;
        private string _Address = String.Empty;
        private string _Phone = String.Empty;
        private string _Email = String.Empty;
        private DateTime _Birthday = DateTime.Now;
        private string _Specialty = String.Empty;
        #endregion
    
        #region Properties Getters and Setters
    
        public int ID { get; set; }
    
        public string Firstname
        {
          get { return _Firstname; }
          set { _Firstname = value; OnPropertyChanged(); }
        }
    
        public string Lastname
        {
          get { return _Lastname; }
          set { _Lastname = value; OnPropertyChanged(); }
        }
    
        public string Address
        {
          get { return _Address; }
          set { _Address = value; OnPropertyChanged(); }
        }
    
        public string Phone
        {
          get { return _Phone; }
          set { _Phone = value; OnPropertyChanged(); }
        }
    
        public string Email
        {
          get { return _Email; }
          set { _Email = value; OnPropertyChanged(); }
        }
        public DateTime Birthday
        {
          get { return _Birthday; }
          set { _Birthday = value; OnPropertyChanged(); }
        }
    
        public string Specialty
        {
          get { return _Specialty; }
          set { _Specialty = value; OnPropertyChanged(); }
        }
    
        #endregion Properties Getters and Setters
    
        public event PropertyChangedEventHandler? PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string name = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
      }
    
      public class RelayCommand : ICommand
      {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _action;
        public RelayCommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
        public void Execute(object o) => _action(o);
        public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
        public event EventHandler CanExecuteChanged
        {
          add { CommandManager.RequerySuggested += value; }
          remove { CommandManager.RequerySuggested -= value; }
        }
      }
    }
    

  2. Peter Fleischer (former MVP) 19,231 Reputation points
    2022-02-14T06:51:32.433+00:00

    Hi Sarah,
    see following demo without ICommand and ViewModel, all code in CodeBehind:

    XAML MainWindow:

    <Window x:Class="WpfApp1.Window005"
            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:WpfApp1"
            mc:Ignorable="d"
            Title="Sarah-3412_220213_MyContcats" Height="450" Width="1200">
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="1000"/>
          <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
    
        <ListView Grid.Column="0" ItemsSource="{Binding View}" Margin="5" IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding CurrentContact}">
          <ListView.ItemTemplate>
            <DataTemplate>
              <Border BorderBrush="CadetBlue" BorderThickness="0,2,0,2" Width="375" Padding="5">
                <StackPanel>
                  <TextBlock Text="{Binding Firstname}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Lastname}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Birthday, StringFormat={}{0:dd/MM/yyyy} }" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Address}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Email}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Phone}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Specialty}" TextWrapping="Wrap"/>
                </StackPanel>
              </Border>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    
        <StackPanel Grid.Column="1" Orientation="Vertical" VerticalAlignment="Top" Margin="0,0,0,0">
          <Button Height="30" Width="60" Content="Add" Margin="10" Click="Add_Click"/>
          <Button Height="30" Width="60" Content="Edit" Margin="10" Click="Edit_Click"/>
          <Button Height="30" Width="60" Content="Remove" Margin="10" Click="Remove_Click"/>
        </StackPanel>
      </Grid>
    </Window>
    

    XAML ContactWindow:

    <Window x:Class="WpfApp1.Window005A"
            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:WpfApp1"
            mc:Ignorable="d"
            Title="Sarah-3412_220213_Contact" Height="350" Width="400">
      <Grid>
        <StackPanel DataContext="{Binding CurrentContact}">
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,10,0,10">
            <Label Content="Firstname:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Firstname}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Lastname:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Lastname}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Birthday:" Width="120" />
            <DatePicker x:Name="Birthday" SelectedDateFormat="Short"  Height="25" Width="230" FirstDayOfWeek="Monday" IsTodayHighlighted="True" SelectedDate="{Binding Birthday}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Address:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Address}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Phone:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Phone}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Email:" Width="120" />
            <TextBox Height="25" Width="230" Margin="0,0,5,0" Text="{Binding Path=Email}"/>
          </StackPanel>
          <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
            <Label Content="Specialty:" Width="120" />
            <ComboBox x:Name="Specialty" Text="{Binding Path=Specialty}" Height="25" Width="230" Margin="0,0,5,0" >
              <ComboBoxItem Content="A"/>
              <ComboBoxItem Content="B"/>
              <ComboBoxItem Content="C"/>
            </ComboBox>
          </StackPanel>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="10,0,10,10">
          <Button Height="30" Width="70" Content="Close" Margin="10,0,0,0" Click="Close_Click" />
          <Button Height="30" Width="70" Content="Save" Margin="10,0,0,0" Click="Save_Click" />
        </StackPanel>
      </Grid>
    </Window>
    

    CodeBehind MainWindow:

    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApp1
    {
      public partial class Window005 : Window
      {
        public Window005()
        {
          InitializeComponent();
          DataContext = this;
        }
    
        AppDbContext ctx = new AppDbContext();
        public ICollectionView View
        {
          get
          {
            ctx.tblContact.Load();
            cvs.Source = ctx.tblContact.Local.ToObservableCollection();
            return cvs.View;
          }
        }
        private CollectionViewSource cvs = new CollectionViewSource();
        public ObservableCollection<ContactDetail>? ContactDetails { get; set; }
    
        public ContactDetail CurrentContact { get; set; }
    
        private Window005A addContactWindow;
        private bool? ret;
    
        private void Add_Click(object sender, RoutedEventArgs e) => CmdExecute("Add_Click");
        private void Edit_Click(object sender, RoutedEventArgs e) => CmdExecute("Edit_Click");
        private void Remove_Click(object sender, RoutedEventArgs e) => CmdExecute("Remove_Click");
        public void CmdExecute(object parameter)
        {
          switch (parameter.ToString())
          {
            case "Add_Click":
              addContactWindow = new Window005A() { DataContext = this };
              CurrentContact = new ContactDetail();
              ret = addContactWindow.ShowDialog();
              if (ret.HasValue && ret.Value) ctx.tblContact.Add(CurrentContact);
              else CurrentContact = null;
              ctx.SaveChanges();
              break;
            case "Edit_Click":
              if (CurrentContact != null)
              {
                addContactWindow = new Window005A() { DataContext = this };
                ret = addContactWindow.ShowDialog();
                if (ret.HasValue && ret.Value) ctx.SaveChanges();
                else
                {
                  ctx.Entry(CurrentContact).State = EntityState.Unchanged;
                  cvs.View.Refresh();
                  OnPropertyChanged(nameof(View));
                }
              }
              break;
            case "Remove_Click":
              if (CurrentContact != null) ctx.tblContact.Remove(CurrentContact);
              ctx.SaveChanges();
              break;
            default:
              break;
          }
        }
    
        public event PropertyChangedEventHandler? PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string name = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    
        public class AppDbContext : DbContext
        {
          protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
            optionsBuilder.UseSqlServer(WpfApp1.Properties.Settings.Default.cnSQL);
    
          public DbSet<ContactDetail> tblContact { get; set; }
        }
    
        public class ContactDetail : INotifyPropertyChanged
        {
          #region private fields
          private string _Firstname = String.Empty;
          private string _Lastname = String.Empty;
          private string _Address = String.Empty;
          private string _Phone = String.Empty;
          private string _Email = String.Empty;
          private DateTime _Birthday = DateTime.Now;
          private string _Specialty = String.Empty;
          #endregion
    
          #region Properties Getters and Setters
    
          public int ID { get; set; }
    
          public string Firstname
          {
            get { return _Firstname; }
            set { _Firstname = value; OnPropertyChanged(); }
          }
    
          public string Lastname
          {
            get { return _Lastname; }
            set { _Lastname = value; OnPropertyChanged(); }
          }
    
          public string Address
          {
            get { return _Address; }
            set { _Address = value; OnPropertyChanged(); }
          }
    
          public string Phone
          {
            get { return _Phone; }
            set { _Phone = value; OnPropertyChanged(); }
          }
    
          public string Email
          {
            get { return _Email; }
            set { _Email = value; OnPropertyChanged(); }
          }
          public DateTime Birthday
          {
            get { return _Birthday; }
            set { _Birthday = value; OnPropertyChanged(); }
          }
    
          public string Specialty
          {
            get { return _Specialty; }
            set { _Specialty = value; OnPropertyChanged(); }
          }
    
          #endregion Properties Getters and Setters
    
          public event PropertyChangedEventHandler? PropertyChanged;
          protected void OnPropertyChanged([CallerMemberName] string name = "") =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
      }
    }
    

    CodeBehind ContactWindow:

    using System.Windows;
    
    namespace WpfApp1
    {
      public partial class Window005A : Window
      {
        public Window005A()
        {
          InitializeComponent();
        }
        private void Close_Click(object sender, RoutedEventArgs e)
        {
          this.DialogResult = false;
          this.Close();
        }
    
        private void Save_Click(object sender, RoutedEventArgs e)
        {
          this.DialogResult = true;
          this.Close();
        }
      }
    }
    

  3. Peter Fleischer (former MVP) 19,231 Reputation points
    2022-02-14T08:56:43.197+00:00

    Hi,
    if you want implement mixed code (MVVM without command binding and CodeBehind) try following demo:

    XAML MainWindow:

    <Window x:Class="WpfApp1.Window005"
            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:WpfApp005"
            mc:Ignorable="d"
            Title="Sarah-3412_220213_MyContcats" Height="450" Width="1200">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="1000"/>
          <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
    
        <ListView Grid.Column="0" ItemsSource="{Binding View}" Margin="5" IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding CurrentContact}">
          <ListView.ItemTemplate>
            <DataTemplate>
              <Border BorderBrush="CadetBlue" BorderThickness="0,2,0,2" Width="375" Padding="5">
                <StackPanel>
                  <TextBlock Text="{Binding Firstname}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Lastname}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Birthday, StringFormat={}{0:dd/MM/yyyy} }" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Address}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Email}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Phone}" TextWrapping="Wrap"/>
                  <TextBlock Text="{Binding Specialty}" TextWrapping="Wrap"/>
                </StackPanel>
              </Border>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    
        <StackPanel Grid.Column="1" Orientation="Vertical" VerticalAlignment="Top" Margin="0,0,0,0">
          <Button Height="30" Width="60" Content="Add" Margin="10" Click="Add_Click"/>
          <Button Height="30" Width="60" Content="Edit" Margin="10" Click="Edit_Click"/>
          <Button Height="30" Width="60" Content="Remove" Margin="10" Click="Remove_Click"/>
        </StackPanel>
      </Grid>
    </Window>
    

    and code:

    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    using WpfApp005;
    using WpfApp1;
    
    namespace WpfApp1
    {
      public partial class Window005 : Window
      {
        public Window005()
        {
          InitializeComponent();
        }
    
        private void Add_Click(object sender, RoutedEventArgs e) => 
          ((ViewModel)this.DataContext).CmdExecute("Add_Click");
        private void Edit_Click(object sender, RoutedEventArgs e) => 
          ((ViewModel)this.DataContext).CmdExecute("Edit_Click");
        private void Remove_Click(object sender, RoutedEventArgs e) => 
          ((ViewModel)this.DataContext).CmdExecute("Remove_Click");
      }
    }
    
    namespace WpfApp005
    {
      public class ViewModel
      {
        AppDbContext ctx = new AppDbContext();
        public ICollectionView View
        {
          get
          {
            ctx.tblContact.Load();
            cvs.Source = ctx.tblContact.Local.ToObservableCollection();
            return cvs.View;
          }
        }
        private CollectionViewSource cvs = new CollectionViewSource();
        public ObservableCollection<ContactDetail>? ContactDetails { get; set; }
    
        public ContactDetail CurrentContact { get; set; }
    
        private Window005A addContactWindow;
        private bool? ret;
    
        public void CmdExecute(object parameter)
        {
          switch (parameter.ToString())
          {
            case "Add_Click":
              addContactWindow = new Window005A() { DataContext = this};
              CurrentContact = new ContactDetail();
              ret = addContactWindow.ShowDialog();
              if (ret.HasValue && ret.Value) ctx.tblContact.Add(CurrentContact);
              else CurrentContact = null;
              ctx.SaveChanges();
              break;
            case "Edit_Click":
              if (CurrentContact != null)
              {
                addContactWindow = new Window005A() { DataContext = this };
                ret = addContactWindow.ShowDialog();
                if (ret.HasValue && ret.Value) ctx.SaveChanges();
                else
                {
                  ctx.Entry(CurrentContact).State = EntityState.Unchanged;
                  cvs.View.Refresh();
                  OnPropertyChanged(nameof(View));
                }
              }
              break;
            case "Remove_Click":
              if (CurrentContact != null) ctx.tblContact.Remove(CurrentContact);
              ctx.SaveChanges();
              break;
            default:
              break;
          }
        }
    
        public event PropertyChangedEventHandler? PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string name = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
      }
    
      public class AppDbContext : DbContext
      {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
          optionsBuilder.UseSqlServer(WpfApp1.Properties.Settings.Default.cnSQL);
    
        public DbSet<ContactDetail> tblContact { get; set; }
      }
    
      public class ContactDetail : INotifyPropertyChanged
      {
        #region private fields
        private string _Firstname = String.Empty;
        private string _Lastname = String.Empty;
        private string _Address = String.Empty;
        private string _Phone = String.Empty;
        private string _Email = String.Empty;
        private DateTime _Birthday = DateTime.Now;
        private string _Specialty = String.Empty;
        #endregion
    
        #region Properties Getters and Setters
    
        public int ID { get; set; }
    
        public string Firstname
        {
          get { return _Firstname; }
          set { _Firstname = value; OnPropertyChanged(); }
        }
    
        public string Lastname
        {
          get { return _Lastname; }
          set { _Lastname = value; OnPropertyChanged(); }
        }
    
        public string Address
        {
          get { return _Address; }
          set { _Address = value; OnPropertyChanged(); }
        }
    
        public string Phone
        {
          get { return _Phone; }
          set { _Phone = value; OnPropertyChanged(); }
        }
    
        public string Email
        {
          get { return _Email; }
          set { _Email = value; OnPropertyChanged(); }
        }
        public DateTime Birthday
        {
          get { return _Birthday; }
          set { _Birthday = value; OnPropertyChanged(); }
        }
    
        public string Specialty
        {
          get { return _Specialty; }
          set { _Specialty = value; OnPropertyChanged(); }
        }
    
        #endregion Properties Getters and Setters
    
        public event PropertyChangedEventHandler? PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string name = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
      }
    }
    

    take ContactWindows CodeBehind from my last posting.

    0 comments No comments

  4. Sarah 186 Reputation points
    2022-02-14T14:05:26.717+00:00

    Hi, Can you please help me move the CmdExecute method to Code Behind? It must be as follows:

    ViewModel

    namespace MyContacts
    {
        public class ViewModel
        {
            AppDbContext ctx = new AppDbContext();
            public ICollectionView View
            {
                get
                {
                    ctx.tblContact.Load();
                    cvs.Source = ctx.tblContact.Local.ToObservableCollection();
                    return cvs.View;
                }
            }
            private CollectionViewSource cvs = new CollectionViewSource();
            public ObservableCollection<ContactDetail>? ContactDetails { get; set; }
    
            public ContactDetail CurrentContact { get; set; }
    
            public event PropertyChangedEventHandler? PropertyChanged;
            internal void OnPropertyChanged([CallerMemberName] string name = "") =>
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    
    
        public class AppDbContext : DbContext
        {
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
              optionsBuilder.UseSqlServer(MyContacts.Properties.Settings.Default.cnSQL); \\  I get an error with your code here
    
            public DbSet<ContactDetail> tblContact { get; set; }
        }
    
        public class ContactDetail : INotifyPropertyChanged
        {
            #region private fields
            private string _Firstname = String.Empty;
            private string _Lastname = String.Empty;
            private string _Address = String.Empty;
            private string _Phone = String.Empty;
            private string _Email = String.Empty;
            private DateTime _Birthday = DateTime.Now;
            private string _Specialty = String.Empty;
            #endregion
    
            #region Properties Getters and Setters
    
            public int ID { get; set; }
    
            public string Firstname
            {
                get { return _Firstname; }
                set { _Firstname = value; OnPropertyChanged(); }
            }
    
            public string Lastname
            {
                get { return _Lastname; }
                set { _Lastname = value; OnPropertyChanged(); }
            }
    
            public string Address
            {
                get { return _Address; }
                set { _Address = value; OnPropertyChanged(); }
            }
    
            public string Phone
            {
                get { return _Phone; }
                set { _Phone = value; OnPropertyChanged(); }
            }
    
            public string Email
            {
                get { return _Email; }
                set { _Email = value; OnPropertyChanged(); }
            }
            public DateTime Birthday
            {
                get { return _Birthday; }
                set { _Birthday = value; OnPropertyChanged(); }
            }
    
            public string Specialty
            {
                get { return _Specialty; }
                set { _Specialty = value; OnPropertyChanged(); }
            }
    
            #endregion Properties Getters and Setters
    
            public event PropertyChangedEventHandler? PropertyChanged;
            protected void OnPropertyChanged([CallerMemberName] string name = "") =>
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    
        }
    }
    

    Code Behind

    namespace MyContacts
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                //ViewModel.Instance.FillList();
                this.DataContext = ViewModel.Instance; 
            }
    
            private void Add_Click(object sender, RoutedEventArgs e)
            {
                addContactWindow = new AddContactWindow() { DataContext = this };
                CurrentContact = new ContactDetail();
                ret = addContactWindow.ShowDialog();
                if (ret.HasValue && ret.Value) ctx.tblContact.Add(CurrentContact);
                ctx.SaveChanges();
            }
    
            private void Edit_Click(object sender, RoutedEventArgs e)
            {
                if (CurrentContact != null)
                {
                    addContactWindow = new AddContactWindow() { DataContext = this };
                    ret = addContactWindow.ShowDialog();
                    if (ret.HasValue && ret.Value) ctx.SaveChanges();
                    else
                    {
                        ctx.Entry(CurrentContact).State = EntityState.Unchanged;
                        cvs.View.Refresh();
                        OnPropertyChanged(nameof(View));
                    }
                }
            }
    
            private void Remove_Click(object sender, RoutedEventArgs e)
            {
                if (CurrentContact != null) ctx.tblContact.Remove(CurrentContact);
                ctx.SaveChanges();
            }
        }
    }
    

    Thanks!


  5. Sarah 186 Reputation points
    2022-02-19T09:08:34.947+00:00

    Hi @Peter Fleischer (former MVP) , I've got your code working now. It has a few errors. You can reproduce the error as follows:

    1. click add button
      2: Click directly Close Button(Without entering anything)
      3: Click Edit Button -> AddContactWiondow will be opened (But it must not be, there is nothing in the Database or View)
    2. click close button -> an item is entered (but this must not be the case)

    When deleting this item, an error will also be displayed