Hi @Peter Fleischer (former MVP) , I fixed the bugs by adding a static class (ContactRef) to get the Contact property in all ViewModels(ContactListingViewModel and AddContactViewModel).
The code is below.
XAML
<Window x:Class="cManagement.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:cManagement.ViewModels"
mc:Ignorable="d"
Title="cManagement"
Height="530" MinHeight="530" Width="725" MinWidth="725"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:ContactListingViewModel/>
</Window.DataContext>
<Grid Margin="10" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Height="25" Text="{Binding FilterContact, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" ToolTip="{Binding ErrorCollection[FilterContact]}"/>
<ListView x:Name="ContactListView" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding View}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding Contact}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left">
<TextBlock Width="200" Text="{Binding Firstname}" TextWrapping="Wrap" />
<TextBlock Width="200" Text="{Binding Lastname}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Vertical" VerticalAlignment="Top" HorizontalAlignment="Center" >
<Button Height="30" Width="80" Content="Add" Margin="0,5,0,10" Command="{Binding Cmd}" CommandParameter="Add"/>
<Button Height="30" Width="80" Content="Edit" Margin="10" Command="{Binding Cmd}" CommandParameter="Edit"/>
<Button Height="30" Width="80" Content="Delete" Margin="10" Command="{Binding Cmd}" CommandParameter="Delete"/>
</StackPanel>
</Grid>
</Window>
******
<Window x:Class="cManagement.Views.AddContactView"
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:cManagement.Views"
xmlns:local1="clr-namespace:cManagement.ViewModels"
xmlns:Sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="AddContact"
Height="200"
Width="450"
WindowStartupLocation="CenterOwner"
local1:AddContactViewModel.AttProp = "True">
<Grid Margin="0,10,0,0">
<StackPanel DataContext="{Binding Contact}">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" >
<Label Content="Firstname:" FontWeight="SemiBold"/>
<TextBox Height="25" Width="290" MaxLength="91" Margin="0,0,5,0" Text="{Binding Firstname, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" ToolTip="{Binding ErrorCollection[Firstname]}" HorizontalContentAlignment="left" VerticalContentAlignment="Center"/>
<Label Content="Lastname:" FontWeight="SemiBold"/>
<TextBox Height="25" Width="290" MaxLength="91" Margin="0,0,5,0" Text="{Binding Lastname, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" ToolTip="{Binding ErrorCollection[Lastname]}" HorizontalContentAlignment="left" VerticalContentAlignment="Center"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="10,0,10,10">
<Button Height="30" Width="80" Content="Cancel" Margin="10,0,0,0" Command="{Binding CmdCancel}"/>
<Button Height="30" Width="80" Content="Save" Margin="10,0,5,0" Command="{Binding CmdSave}"/>
</StackPanel>
</Grid>
</Window>
Code
using cManagement.Commands;
using cManagement.Models;
using cManagement.Services;
using cManagement.Views;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
namespace cManagement.ViewModels
{
class ContactListingViewModel : BaseViewModel, IDataErrorInfo
{
AppDbContext db = new AppDbContext();
public ContactListingViewModel()
{
if (ContactRef.Contact == null)
{
ContactRef.Contact = new ContactModel();
}
db.tblContact.Load();
cvs.Source = db.tblContact.Local.ToObservableCollection();
cvs.Source = ContactsList;
}
public ICollectionView View { get => cvs.View; }
private CollectionViewSource cvs = new CollectionViewSource();
public ObservableCollection<ContactModel> ContactsList { get; set; } = new ObservableCollection<ContactModel>();
#region Commands
public DelegateCommand Cmd { get => new DelegateCommand(CanExec , CmdExec); }
private AddContactView addContactView;
private bool? diagResult;
private void CmdExec(object parameter)
{
switch (parameter.ToString())
{
case "Add":
AddContactViewModel vm = new AddContactViewModel();
addContactView = new AddContactView();
addContactView.DataContext = vm;
diagResult = addContactView.ShowDialog();
if(diagResult.HasValue && diagResult.Value)
{
db.tblContact.Add(ContactRef.Contact);
db.SaveChanges();
}
break;
case "Edit":
if (ContactRef.Contact != null)
{
AddContactViewModel viewModel = new AddContactViewModel();
addContactView = new AddContactView() ;
addContactView.DataContext = viewModel;
diagResult = addContactView.ShowDialog();
if (diagResult.HasValue && diagResult.Value)
{
db.SaveChanges();
}
else
{
db.Entry(ContactRef.Contact).State = EntityState.Unchanged;
onPropertyChanged(nameof(View));
}
}
break;
case "Delete":
if (ContactRef.Contact != null)
{
db.tblContact.Remove(ContactRef.Contact);
db.SaveChanges();
}
break;
default:
break;
}
}
private bool CanExec(object parameter)
{
bool res = false;
switch (parameter.ToString())
{
case "Add":
res = true;
break;
case "Edit":
res = (ContactRef.Contact != null);
break;
case "Delete":
res = (ContactRef.Contact != null);
break;
default:
break;
}
return res;
}
public bool CanCreateContact => HasContact;
private bool HasContact => (ContactRef.Contact != null);
#endregion Commands
#region Filter
private string filterContact;
public string FilterContact
{
get
{
return filterContact;
}
set
{
filterContact = value;
onPropertyChanged();
cvs.View.Refresh();
}
}
private void ContactFilter(object sender, FilterEventArgs e)
{
if (string.IsNullOrWhiteSpace(filterContact))
{
//no filter when no search text is entered
e.Accepted = true;
}
else
{
ContactModel contact = (ContactModel)e.Item;
if (contact.Firstname.StartsWith(filterContact, true, null) ||
contact.Firstname.Contains(filterContact, StringComparison.InvariantCultureIgnoreCase) ||
contact.Lastname.StartsWith(filterContact, true, null) ||
contact.Lastname.Contains(filterContact, StringComparison.InvariantCultureIgnoreCase)
)
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}
}
#endregion Filter
#region ErrorInfo
public Dictionary<string, string> ErrorCollection { get; private set; } = new Dictionary<string, string>();
public string Error { get => String.Empty; }
public string this[string propertyName]
{
get
{
string result = null;
switch (propertyName)
{
case "FilterContact":
if (string.IsNullOrWhiteSpace(ContactRef.Contact.Firstname))
result = "Please enter firstname or lastname";
else if (ContactRef.Contact.Firstname.Length < 5)
result = "Firstname must be a minimum of 5 characters.";
break;
}
if (ErrorCollection.ContainsKey(propertyName))
{
ErrorCollection[propertyName] = result;
}
else if (result != null)
{
ErrorCollection.Add(propertyName, result);
}
onPropertyChanged("ErrorCollection");
return result;
}
}
#endregion ErrorInfo
}
}
*****
using cManagement.Commands;
using cManagement.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
namespace cManagement.ViewModels
{
class AddContactViewModel : BaseViewModel, IDataErrorInfo
{
public AddContactViewModel()
{
if (ContactRef.Contact == null)
{
ContactRef.Contact = new ContactModel();
}
}
public DelegateCommand CmdSave { get => new DelegateCommand(CanCreateContact, CmdExecSave); }
public void CmdExecSave(Object parameter)
{
WindowRef.DialogResult = true;
}
public DelegateCommand CmdCancel { get => new DelegateCommand(CmdExecCancel); }
public void CmdExecCancel(Object parameter)
{
WindowRef.DialogResult = false;
}
private bool CanCreateContact(Object parameter) =>
HasFirstname &&
HasLastname &&
ContactRef.Contact != null;
private bool HasFirstname => !string.IsNullOrEmpty(Firstname);
private bool HasLastname => !string.IsNullOrEmpty(Lastname);
#region Propertyname
public string Firstname
{
get { return ContactRef.Contact.Firstname; }
set
{
ContactRef.Contact.Firstname = value;
onPropertyChanged();
onPropertyChanged(nameof(CanCreateContact));
CmdSave.onCanExecuteChanged();
}
}
public string Lastname
{
get { return ContactRef.Contact.Lastname; }
set
{
ContactRef.Contact.Lastname = value;
onPropertyChanged();
onPropertyChanged(nameof(CanCreateContact));
CmdSave.onCanExecuteChanged();
}
}
#endregion Propertyname
#region ErrorInfo
public Dictionary<string, string> ErrorCollection { get; private set; } = new Dictionary<string, string>();
public string Error { get => String.Empty; }
public string this[string propertyName]
{
get
{
string result = null;
switch (propertyName)
{
case "Firstname":
if (string.IsNullOrWhiteSpace(Firstname))
result = "Firstname cannot be empty";
else if (Firstname.Length < 5)
result = "Firstname must be a minimum of 5 characters.";
break;
case "Lastname":
if (string.IsNullOrWhiteSpace(Lastname))
result = "Lastname cannot be empty";
else if (Firstname.Length < 5)
result = "Lastname must be a minimum of 5 characters.";
break;
}
if (ErrorCollection.ContainsKey(propertyName))
{
ErrorCollection[propertyName] = result;
}
else if (result != null)
{
ErrorCollection.Add(propertyName, result);
}
onPropertyChanged("ErrorCollection");
return result;
}
}
#endregion ErrorInfo
#region Attached Property
public Window WindowRef;
public static readonly DependencyProperty AttPropProperty =
DependencyProperty.RegisterAttached("AttProp", typeof(bool), typeof(AddContactViewModel), new PropertyMetadata(false, OnPropChanged));
public static bool GetAttProp(DependencyObject obj) => (bool)obj.GetValue(AttPropProperty);
public static void SetAttProp(DependencyObject obj, bool par) => obj.SetValue(AttPropProperty, par);
private static void OnPropChanged(DependencyObject depObjet, DependencyPropertyChangedEventArgs e)
{
Window WindowRef = depObjet as Window;
if (WindowRef == null) return;
WindowRef.Loaded += (s, e) => ((AddContactViewModel)(WindowRef.DataContext)).WindowRef = WindowRef;
}
#endregion Attached Property
}
}
*****
namespace cManagement.Models
{
public class ContactModel
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
}
*****
namespace cManagement.Models
{
public class ContactRef
{
public static ContactModel Contact { get; set; }
}
}
*****
using System;
using System.Windows.Input;
namespace cManagement.Commands
{
public class DelegateCommand : ICommand
{
readonly Action<object> execute;
readonly Predicate<object> canExecute;
public DelegateCommand(Predicate<object> canExecute, Action<object> execute) =>
(this.canExecute, this.execute) = (canExecute, execute);
public DelegateCommand(Action<object> execute) : this(null, execute) { }
public void onCanExecuteChanged() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => this.canExecute?.Invoke(parameter)?? true;
public void Execute(object parameter) => this.execute?.Invoke(parameter);
}
}