I have this code in my MainWindow. The user enters Name, Phone and Email in the fields provided, selects Location and then the Name appears in the listbox, lstClients.
I'm trying to write a method to remove a selected name from the listbox.
namespace WpfApp_Employment_Help
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
// client list
List<Client> ClientList = new List<Client>();
public MainWindow()
{
InitializeComponent();
}
// method to select location via radio button
public string GetSelectedLocation()
{
string selected = string.Empty;
if (RBLocE.IsChecked == true) { selected = "Edinburgh"; }
else if (RBLocG.IsChecked == true) { selected = "Glasgow"; }
else if (RBLocO.IsChecked == true) { selected = "Other"; }
return selected;
}
// method to create a new client on click
private void newClient(object sender, RoutedEventArgs e)
{
Client c = new Client(boxClientName.Text, boxClientPhone.Text, boxClientEmail.Text, GetSelectedLocation());
boxClientName.Clear();
boxClientPhone.Clear();
boxClientEmail.Clear();
ClientList.Add(c);
lstClients.ItemsSource = null;
lstClients.ItemsSource = ClientList;
}
// method to id selected client
private void AssignID(object sender, RoutedEventArgs e)
{
Client c = lstClients.SelectedItem as Client;
if (c != null)
{
c.AssignID();
}
lstClients.ItemsSource = null;
lstClients.ItemsSource = ClientList;
}
// method to remove selected client
private void RemoveClient(object sender, RoutedEventArgs e)
{
lstClients.Items.Remove(lstClients.SelectedItem);
}
}
}
When I run this code, I get Unhandled Exception: System.InvalidOperationException: 'Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.'
how can I rewrite my RemoveClient method? my code for the Client class is this:
public partial class Client
{
public string Name { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Location { get; }
public bool IDed { get; private set; }
public Client(string n, string p, string e, string l)
{
Name = n;
Phone = p;
Email = e;
Location = l;
}
}
I have Visual Studio 2022 which has been recently updated.
I have also tried the following solution but it gives me another unhandled error? It looks like I need to change List </string/> and string to something else. but what?
private void RemoveClient(object sender, EventArgs e)
{
if (lstClients.Items.Count >= 1)
{
if (lstClients.SelectedValue != null)
{
var items = (List<string>)lstClients.ItemsSource;
var item = (string)lstClients.SelectedValue;
lstClients.ItemsSource = null;
lstClients.Items.Clear();
items.Remove(item);
lstClients.ItemsSource = items;
}
}
else
{
System.Windows.Forms.MessageBox.Show("No ITEMS Found");
}
}
System.InvalidCastException: 'Unable to cast object of type 'System.Collections.Generic.List`1[WpfApp_Employment_Help.Client]' to type 'System.Collections.Generic.List`1[System.String]'.'
CodePudding user response:
As the error message suggests, you can't modify the ItemsControl
via the collection view returned from the ItemsControl.Items
property.
WPF is generally designed to work on the data sources instead of handling the data related controls (data presentation). This way data and data presentation (GUI) are cleanly separated and code will become a lot simpler to write.
In case of the ListView
(or ItemsControl
in general), simply modify the source collection.
To improve performance, the source collection should be a INotifyCollectionChanged
implementation, for example ObservableCollection<T>
, especially when you expect to modify the source collection.
This makes invalidating the ItemsSource
e.g. by assigning null
, just to set it again redundant and significantly improves the performance.
public partial class MainWindow : Window
{
// client list
public ObservableCollection<Client> ClientList { get; } = new ObservableCollection<Client>();
// method to create a new client on click
private void newClient(object sender, RoutedEventArgs e)
{
Client c = new Client(boxClientName.Text, boxClientPhone.Text, boxClientEmail.Text, GetSelectedLocation());
boxClientName.Clear();
boxClientPhone.Clear();
boxClientEmail.Clear();
ClientList.Add(c);
// The following lines are no longer needed
// as the GUI is now notified about the collection changes (by the INotifyCollectionChanged collection)
//lstClients.ItemsSource = null;
//lstClients.ItemsSource = ClientList;
}
// method to id selected client
private void AssignID(object sender, RoutedEventArgs e)
{
Client c = lstClients.SelectedItem as Client;
// Same as the if-statement below
c?.AssignID();
//if (c != null)
//{
// c.AssignID();
//}
// The following lines are no longer needed
// as the GUI is now notified about the collection changes (by the INotifyCollectionChanged collection)
//lstClients.ItemsSource = null;
//lstClients.ItemsSource = ClientList;
}
// method to remove selected client
private void RemoveClient(object sender, RoutedEventArgs e)
{
var clientToRemove = lstClients.SelectedItem as Client;
this.ClientList.Remove(clientToRemove);
}
}
CodePudding user response:
If you change the type of ClientList
from List<Client>
to ObservableCollection<Client>
, you could simply remove the item directly from the source collection:
public partial class MainWindow : Window
{
ObservableCollection<Client> ClientList = new ObservableCollection<Client>();
public MainWindow()
{
InitializeComponent();
}
...
private void RemoveClient(object sender, RoutedEventArgs e)
{
ClientList.Remove(lstClients.SelectedItem as Client);
}
}