Hi Sarah,
I correct and change your code (without Filter):
<Window x:Class="WpfApp007.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:WpfApp007"
mc:Ignorable="d"
Title="Sarah-3412_220306" Height="450" Width="800">
<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>
<local:LimitedTextBox Grid.Row="0" Grid.Column="0" Height="25" Text="{Binding FilterContact, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
<ListView x:Name="View" 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="WpfApp007.Views.AddEditContactView"
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:WpfApp007"
mc:Ignorable="d"
Title="AddEditContact"
Height="200"
Width="450"
WindowStartupLocation="CenterOwner"
local:AddEditContactViewModel.AttProp = "True">
<Window.Resources>
<!-- Error Template to change the default behaviour -->
<ControlTemplate x:Key="ErrorTemplate">
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<!-- To display tooltip with the error-->
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<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}" Validation.ErrorTemplate="{StaticResource ErrorTemplate}" 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}" Validation.ErrorTemplate="{StaticResource ErrorTemplate}" HorizontalContentAlignment="left" VerticalContentAlignment="Center"/>
<Label Content="{Binding Error}" Margin="5" Foreground="Red" />
</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 Cmd}" CommandParameter="Cancel"/>
<Button Height="30" Width="80" Content="Save" Margin="10,0,5,0" Command="{Binding Cmd}" CommandParameter="Save"/>
</StackPanel>
</Grid>
</Window>
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using WpfApp007.Views;
namespace WpfApp007
{
public class ContactListingViewModel : BaseClassPropertyChanged
{
AppDbContext db = new AppDbContext();
public ContactListingViewModel()
{
db.tblContact.Load();
ContactsList = 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>();
public ContactModel Contact { get; set; }
#region Commands
public ICommand Cmd { get => new DelegateCommand(CmdExec, CanExec); }
private AddEditContactView addEditContactView;
private bool? diagResult;
private void CmdExec(object parameter)
{
switch (parameter.ToString())
{
case "Add":
AddEditContactViewModel addEditContactViewModel = new AddEditContactViewModel() { Contact = new ContactModel() };
addEditContactView = new AddEditContactView() { DataContext = addEditContactViewModel };
diagResult = addEditContactView.ShowDialog();
if (diagResult.HasValue && diagResult.Value)
{
db.tblContact.Add(addEditContactViewModel.Contact);
db.SaveChanges();
}
break;
case "Edit":
if (Contact != null)
{
addEditContactView = new AddEditContactView() { DataContext = new AddEditContactViewModel() { Contact = Contact } };
diagResult = addEditContactView.ShowDialog();
if (diagResult.HasValue && diagResult.Value)
{
db.SaveChanges();
}
else
{
db.Entry(Contact).State = EntityState.Unchanged;
cvs.View.Refresh();
OnPropertyChanged(nameof(View));
}
}
break;
case "Delete":
if (Contact != null)
{
db.tblContact.Remove(Contact);
db.SaveChanges();
}
break;
default:
break;
}
}
private bool CanExec(object parameter)
{
switch (parameter.ToString())
{
case "Add":
return true;
default:
return (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
}
public class AddEditContactViewModel : BaseClassPropertyChanged
{
public ContactModel Contact { get; set; }
public ICommand Cmd { get => new DelegateCommand(CmdExec, CanCmdExec); }
public void CmdExec(Object parameter)
{
switch (parameter.ToString())
{
case "Save":
var count = (new AppDbContext()).tblContact.Count(t => t.Firstname == Contact.Firstname && t.Lastname == Contact.Lastname);
if (count == 0) Contact.SetAddionalError("dublicate", string.Empty); else Contact.SetAddionalError("dublicate", "Dublicate Contact");
OnPropertyChanged(nameof(Cmd));
if (count == 0) wnd.DialogResult = true;
break;
case "Cancel":
wnd.DialogResult = false;
break;
default:
break;
}
}
public bool CanCmdExec(object parameter) =>
parameter.ToString() != "Save" || (Contact != null && string.IsNullOrEmpty(Contact.Error));
#region attached property
public Window wnd;
public static readonly DependencyProperty AttPropProperty =
DependencyProperty.RegisterAttached("AttProp", typeof(bool), typeof(AddEditContactViewModel), 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 d, DependencyPropertyChangedEventArgs e)
{
Window wnd = d as Window;
if (wnd == null) return;
wnd.Loaded += (s, e) => ((AddEditContactViewModel)(wnd.DataContext)).wnd = wnd;
}
#endregion
}
public class BaseClassPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (!string.IsNullOrEmpty(propertyName))
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class LimitedTextBox : TextBox
{
public LimitedTextBox()
{
this.PreviewKeyDown += LimitedTextBox_PreviewKeyDown;
DataObject.AddPastingHandler(this, OnPaste);
}
private KeyConverter kc = new KeyConverter();
private void LimitedTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
string sText = this.Text;
string sKey = kc.ConvertToString(e.Key);
bool bCapsLock = Keyboard.IsKeyToggled(Key.CapsLock);
bool bShift = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
bool bCharacter = false;
if ((!bCapsLock && !bShift) || (bCapsLock && bShift)) sKey = sKey.ToLower();
if (sKey != null && sKey.Length == 1 && char.IsLetterOrDigit(sKey[0]))
{
sText += sKey[0];
bCharacter = true;
}
// System.Windows.Controls.TextBoxView
var nActualWidthView = this.ActualWidth;
var sz = MeasureString(sText);
if (bCharacter && sz.Width >= nActualWidthView)
{
e.Handled = true;
Console.Beep(1000, 10);
}
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
e.Handled = true;
e.CancelCommand();
}
public Size MeasureString(string s)
{
FormattedText formattedText = new FormattedText(
s,
System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, this.FontStretch),
this.FontSize,
new SolidColorBrush(Colors.Black),
VisualTreeHelper.GetDpi(this).PixelsPerDip);
return new Size(formattedText.Width, formattedText.Height);
}
}
public class AppDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder.UseSqlServer(WpfApp1.Properties.Settings.Default.cnSQL);
public DbSet<ContactModel> tblContact { get; set; }
}
public class ContactModel : BaseClassPropertyChanged, IDataErrorInfo
{
public int Id { get; set; }
private string _firstname = string.Empty;
public string Firstname
{
get => this._firstname;
set { this._firstname = value; ErrorCollection[nameof(Firstname)] = this[nameof(Firstname)]; OnPropertyChanged(); }
}
private string _lastname = string.Empty;
public string Lastname
{
get => this._lastname;
set { this._lastname = value; ErrorCollection[nameof(Lastname)] = this[nameof(Lastname)]; OnPropertyChanged(); }
}
#region ErrorInfo
[NotMapped]
public Dictionary<string, string> ErrorCollection { get; private set; } = new Dictionary<string, string>();
[NotMapped]
public string Error
{
get
{
String result = String.Empty;
foreach (var item in ErrorCollection)
if (!string.IsNullOrEmpty(item.Value)) result += (string.IsNullOrEmpty(result)) ? item.Value : Environment.NewLine + item.Value;
return result;
}
}
public void SetAddionalError(string errKey, string errMessaage)
{
if (string.IsNullOrEmpty(errMessaage)) ErrorCollection.Remove(errKey); else ErrorCollection[errKey] = errMessaage;
OnPropertyChanged(nameof(Error));
}
[NotMapped]
public string this[string propertyName]
{
get
{
string result = String.Empty;
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;
}
return result;
}
}
#endregion ErrorInfo
}
public class DelegateCommand : ICommand
{
#pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
#pragma warning disable CS8612 // Nullability of reference types in type doesn't match implicitly implemented member.
private readonly Predicate<object> _canExecute;
private readonly Action<object> _action;
public DelegateCommand(Action<object> action) : this(action, null) { }
public DelegateCommand(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; }
}
#pragma warning restore CS8612 // Nullability of reference types in type doesn't match implicitly implemented member.
#pragma warning restore CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
}
}