Hi,
you add UserModel to ListOfUser in AddViewModel and show ListOfUser from UserViewModel. That are different lists.
How to update listView using MVVM pattern and ObservableCollection in WPF?
I want to update a listview in UserControl included in MainWindow when adding/removing an entry over opening a Window.
When clicking the save button, nothing happens
Can anyone please help me to see, what I'm doing wrong?
Best regards
Imilio
What I have done:
XAML - MainWindow
<Window x:Class="Add_Edit_Delete.ucWindow.Window1"
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:Add_Edit_Delete.ucWindow" xmlns:local1="clr-namespace:Add_Edit_Delete.ViewModel"
mc:Ignorable="d"
Title="Window1" Height="450" Width="400">
<Window.DataContext>
<local1:AddViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
<Label Content="Firstname:" Width="120" />
<TextBox x:Name="tbFirstname" Height="25" Width="230" Margin="0,0,5,0" Text="{Binding NewUser.Firstname}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
<Label Content="Firstname:" Width="120" />
<TextBox x:Name="tbLastname" Height="25" Width="230" Margin="0,0,5,0" Text="{Binding NewUser.Lastname}"/>
</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"/>
<Button Height="30" Width="70" Content="Save" Margin="10,0,0,0" Command="{Binding AddUserCommand}"/>
</StackPanel>
</Grid>
</Window>
C# - MainWindow
using Add_Edit_Delete.Usercontrol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Add_Edit_Delete
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void User_Click(object sender, RoutedEventArgs e)
{
UserControl1 oUser = new UserControl1();
Grid.SetColumn(oUser, 1);
MainGrid.Children.Add(oUser);
}
}
}
XAML - UserControl1
<UserControl x:Class="Add_Edit_Delete.Usercontrol.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Add_Edit_Delete.Usercontrol" xmlns:local1="clr-namespace:Add_Edit_Delete.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Background="White">
<UserControl.DataContext>
<local1:UserViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView Grid.Column="0">
<ListView.View>
<GridView>
<GridViewColumn Header="Firstname" x:Name="Firstname" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Firstname}" TextWrapping="Wrap"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Lastname" x:Name="Lastname" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Lastname}" TextWrapping="Wrap"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Top" Margin="20,0,0,0">
<Button Height="25" Width="50" Content="Add" Margin="10" Click="Add_Click"/>
<Button Height="25" Width="50" Content="Edit" Margin="10" Click="Eddit_Click"/>
<Button Height="25" Width="50" Content="Delete" Margin="10" Click="Delete_Click" />
</StackPanel>
</Grid>
</UserControl>
C# - UserControl1
using Add_Edit_Delete.ucWindow;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Add_Edit_Delete.Usercontrol
{
/// <summary>
/// Interaktionslogik für UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void Delete_Click(object sender, RoutedEventArgs e)
{
}
private void Eddit_Click(object sender, RoutedEventArgs e)
{
}
private void Add_Click(object sender, RoutedEventArgs e)
{
Window1 oAdd = new Window1();
oAdd.Owner = Application.Current.MainWindow;
oAdd.ShowDialog();
}
}
}
**XAML - Window1**
<Window x:Class="Add_Edit_Delete.ucWindow.Window1"
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:Add_Edit_Delete.ucWindow" xmlns:local1="clr-namespace:Add_Edit_Delete.ViewModel"
mc:Ignorable="d"
Title="Window1" Height="450" Width="400">
<Window.DataContext>
<local1:AddViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
<Label Content="Firstname:" Width="120" />
<TextBox x:Name="tbFirstname" Height="25" Width="230" Margin="0,0,5,0" Text="{Binding NewUser.Firstname}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10">
<Label Content="Firstname:" Width="120" />
<TextBox x:Name="tbLastname" Height="25" Width="230" Margin="0,0,5,0" Text="{Binding NewUser.Lastname}"/>
</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"/>
<Button Height="30" Width="70" Content="Save" Margin="10,0,0,0" Command="{Binding AddUserCommand}"/>
</StackPanel>
</Grid>
</Window>
C# - Window1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Add_Edit_Delete.ucWindow
{
/// <summary>
/// Interaktionslogik für Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
UserModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Add_Edit_Delete.Model
{
class UserModel
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
}
AddViewModel
using Add_Edit_Delete.Commands;
using Add_Edit_Delete.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Add_Edit_Delete.ViewModel
{
class AddViewModel : INotifyPropertyChanged
{
public AddViewModel()
{
this.AddUserCommand = new DelegateCommand((o) =>
{
this.ListOfUser.Add(NewUser);
});
}
UserModel newUser = new UserModel();
public UserModel NewUser
{
get => newUser;
set
{
if (newUser != value)
{
newUser = value;
this.RaisePropertyChanged(nameof(NewUser));
}
}
}
private ObservableCollection<UserModel> listOfUser = new ObservableCollection<UserModel>();
public ObservableCollection<UserModel> ListOfUser
{
get => listOfUser;
set
{
if (listOfUser != value)
{
listOfUser = value;
this.RaisePropertyChanged(nameof(ListOfUser));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public DelegateCommand AddUserCommand { get; set; }
}
}
UserViewModel
using Add_Edit_Delete.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Add_Edit_Delete.ViewModel
{
class UserViewModel : INotifyPropertyChanged
{
UserModel newUser = new UserModel();
public UserModel NewUser
{
get => newUser;
set
{
if (newUser != value)
{
newUser = value;
this.RaisePropertyChanged(nameof(NewUser));
}
}
}
private ObservableCollection<UserModel> listOfUser = new ObservableCollection<UserModel>();
public ObservableCollection<UserModel> ListOfUser
{
get => listOfUser;
set
{
if (listOfUser != value)
{
listOfUser = value;
this.RaisePropertyChanged(nameof(ListOfUser));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
DelegationCommand
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Add_Edit_Delete.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 event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public bool CanExecute(object parameter) => this.canExecute?.Invoke(parameter) ?? true;
public void Execute(object parameter) => this.execute?.Invoke(parameter);
}
}
10 answers
Sort by: Most helpful
-
Peter Fleischer (former MVP) 19,231 Reputation points
2022-01-12T06:18:09.037+00:00 -
Hui Liu-MSFT 40,266 Reputation points Microsoft Vendor
2022-01-13T08:47:29.567+00:00 As Peter said ,you could delete the DataContext in the xaml of Window1 and UserControl1. Then modify the code of UserControl1 as follows.
public partial class UserControl1 : UserControl { AddViewModel vm = new AddViewModel(); public UserControl1() { InitializeComponent(); this.DataContext=vm; } private void Add_Click(object sender, RoutedEventArgs e) { Window1 oAdd = new Window1(); oAdd.DataContext=vm; oAdd.Owner = Application.Current.MainWindow; oAdd.ShowDialog(); } }
Then bind the ItemsSource for the ListView .
UserControl1.xaml:<ListView Grid.Column="0" ItemsSource="{Binding ListOfUser}"> <ListView.View> <GridView> <GridViewColumn Header="Firstname" x:Name="Firstname" Width="200"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Firstname}" TextWrapping="Wrap"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="Lastname" x:Name="Lastname" Width="200"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Lastname}" TextWrapping="Wrap"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView> </ListView.View> </ListView>
If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread. -
Peter Fleischer (former MVP) 19,231 Reputation points
2022-01-13T16:51:46.32+00:00 Hi,
you can simplified your code like this:MainWindow:
<Window x:Class="WpfApp1.Window094" 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" xmlns:local1="clr-namespace:WpfControlLibrary094.Add_Edit_Delete.ViewModel;assembly=WpfControlLibrary1" xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1" mc:Ignorable="d" Title="Imilio-3053_220112" Height="450" Width="800"> <Grid> <StackPanel> <uc:Window094UC1 Margin="5"/> </StackPanel> </Grid> </Window>
XAML UserControl:
<UserControl x:Class="WpfControlLibrary1.Window094UC1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WpfControlLibrary1" xmlns:local1="clr-namespace:WpfControlLibrary094.Add_Edit_Delete.ViewModel" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <UserControl.DataContext> <local1:UserViewModel/> </UserControl.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <ListView Grid.Column="0" ItemsSource="{Binding ListOfUser}"> <ListView.View> <GridView> <GridViewColumn Header="Firstname" x:Name="Firstname" Width="200"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Firstname}" TextWrapping="Wrap"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="Lastname" x:Name="Lastname" Width="200"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Lastname}" TextWrapping="Wrap"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView> </ListView.View> </ListView> <StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Top" Margin="20,0,0,0"> <Button Height="25" Width="50" Content="Add" Margin="10" Command="{Binding Cmd}" CommandParameter="Add_Click"/> <Button Height="25" Width="50" Content="Edit" Margin="10" Command="{Binding Cmd}" CommandParameter="Eddit_Click"/> <Button Height="25" Width="50" Content="Delete" Margin="10" Command="{Binding Cmd}" CommandParameter="Delete_Click" /> </StackPanel> </Grid> </UserControl>
XAML Window1:
<Window x:Class="WpfControlLibrary1.Window094A" 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:WpfControlLibrary1" xmlns:local1="clr-namespace:WpfControlLibrary094.Add_Edit_Delete.ViewModel" mc:Ignorable="d" Title="Window1" Height="150" Width="400"> <Grid> <StackPanel> <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10"> <Label Content="Firstname:" Width="120" /> <TextBox x:Name="tbFirstname" Height="25" Width="230" Margin="0,0,5,0" Text="{Binding NewUser.Firstname}"/> </StackPanel> <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,0,0,10"> <Label Content="Firstname:" Width="120" /> <TextBox x:Name="tbLastname" Height="25" Width="230" Margin="0,0,5,0" Text="{Binding NewUser.Lastname}"/> </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"/> <Button Height="30" Width="70" Content="Save" Margin="10,0,0,0" Command="{Binding AddUserCommand}"/> </StackPanel> </Grid> </Window>
Code:
using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using WpfControlLibrary094.Add_Edit_Delete.Commands; using WpfControlLibrary1; namespace WpfControlLibrary094.Add_Edit_Delete.ViewModel { public class UserModel { static int number = 1; public int Id { get; set; } public string Firstname { get; set; } public string Lastname { get; set; } public UserModel Copy() => new UserModel() { Id = number++, Firstname = this.Firstname, Lastname = this.Lastname }; } public class UserViewModel : INotifyPropertyChanged { public UserViewModel() => this.AddUserCommand = new DelegateCommand((o) => { this.ListOfUser.Add(NewUser.Copy()); }); public DelegateCommand AddUserCommand { get; set; } public UserModel NewUser { get; set; } = new UserModel(); public ObservableCollection<UserModel> ListOfUser { get; set; } = new ObservableCollection<UserModel>(); public ICommand Cmd { get => new DelegateCommand(null, CmdExec); } private void CmdExec(object obj) { switch (obj.ToString()) { case "Add_Click": Window094A oAdd = new Window094A(); oAdd.DataContext = this; oAdd.Owner = Application.Current.MainWindow; oAdd.ShowDialog(); break; default: break; } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } namespace WpfControlLibrary094.Add_Edit_Delete.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 event EventHandler CanExecuteChanged; public void RaiseCanExecuteChanged() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty); public bool CanExecute(object parameter) => this.canExecute?.Invoke(parameter) ?? true; public void Execute(object parameter) => this.execute?.Invoke(parameter); } }
Result:
-
Imilio 41 Reputation points
2022-01-14T19:47:18.967+00:00 Hi @Peter Fleischer (former MVP) , your code works correctly. What am I doing wrong that I overwrite the previous items with the new one? I would like to avoid the copy method.
I would like to separate the contents of Usercontrol1 and Window1. as follows:UserViewModel:
using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using WPF_App.Commands; using WPF_App.Model; using WPF_App.ucWindow; namespace WPF_App.ViewModel { public class UserViewModel { public ICommand Cmd { get => new DelegateCommand(null, CmdExec); } private void CmdExec(object obj) { switch (obj.ToString()) { case "Add_Click": Window1 oAdd = new Window1(); oAdd.DataContext = this; oAdd.Owner = Application.Current.MainWindow; oAdd.ShowDialog(); break; case "Edit_Click": //TBD break; case "Delete_Click": //TBD break; default: break; } } } }
AddViewModel:
using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using WPF_App.Commands; using WPF_App.Model; using WPF_App.ucWindow; namespace WPF_App.ViewModel { public class AddViewModel : INotifyPropertyChanged { public AddViewModel() => this.AddUserCommand = new DelegateCommand((o) => { this.ListOfUser.Add(NewUser.Copy()); }); public DelegateCommand AddUserCommand { get; set; } public UserModel NewUser { get; set; } = new UserModel(); public ObservableCollection<UserModel> ListOfUser { get; set; } = new ObservableCollection<UserModel>(); public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
if I do that, I have the original problem again. Nothing happens when I click on the Save button!
-
Peter Fleischer (former MVP) 19,231 Reputation points
2022-01-15T05:39:06.327+00:00 Hi,
if you need to use separate ViewModels for UserControl and Window1 try this code:namespace Add_Edit_Delete.ViewModel { public class UserModel { public int Id { get; set; } public string Firstname { get; set; } public string Lastname { get; set; } } public class UserViewModel : INotifyPropertyChanged { public ObservableCollection<UserModel> ListOfUser { get; set; } = new ObservableCollection<UserModel>(); public ICommand Cmd { get => new DelegateCommand(null, CmdExec); } private void CmdExec(object obj) { switch (obj.ToString()) { case "Add_Click": Window094A oAdd = new Window094A(); AddViewModel vm = new AddViewModel(this); oAdd.DataContext = vm; oAdd.Owner = Application.Current.MainWindow; oAdd.ShowDialog(); break; default: break; } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public class AddViewModel : INotifyPropertyChanged { public AddViewModel(UserViewModel vm) { mainVm = vm; this.AddUserCommand = new DelegateCommand((o) => { mainVm.ListOfUser.Add(NewUser); NewUser = new UserModel(); RaisePropertyChanged(nameof(NewUser)); }); } public DelegateCommand AddUserCommand { get; set; } private UserViewModel mainVm; public UserModel NewUser { get; set; } = new UserModel(); public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }