Reputation: 309
I'm attempting to delete a dynamically created button from a listbox using the ListBox.Items.Remove, but I keep getting the error of "Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead." Problem is, ItemsControl.ItemsSource is not a valid option in my code.
A little run-down on the code: I have a MainWindow that contains a ListBox and the "Add" and "Delete" buttons. Adding a button sends you to a window where you can input a firstname and lastname. Clicking "Done" adds the newly-created profile's Button to the Listbox (you can access the profile by clicking on said button). I didn't include the Profile code as its empty except for the firstname and lastname being bound to labels there.
How would I access/modify the the button/profile in order to delete them? I know it has to do with the databinding, but I'm thoroughly confused on how to delete the item.
Any help would be much appreciated. I've included the MainWindow and ProfileCreator code below.
<Window x:Class="SavingButtons.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="UserTemplate">
<StackPanel Orientation="Horizontal">
<Button Name="TestAddButton" Click="TestAddButton_Clicked" Content="{Binding FirstName}" Width="100" Height="40"></Button>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Name="AddProfileButton" Content="Add Profile" HorizontalAlignment="Left" Margin="22,29,0,0" VerticalAlignment="Top" Width="75" Click="AddProfileButton_Click"/>
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
<Button Name="DeleteUserButton" Click="DeleteUserButton_Click" Content="Delete User" HorizontalAlignment="Left" Margin="246,69,0,0" VerticalAlignment="Top" Width="105"/>
</Grid>
namespace SavingButtons
{
public partial class MainWindow : Window
{
NewProfile np;
public int buttonNumberID;
public MainWindow()
{
InitializeComponent();
np = new NewProfile(this);
}
private void AddProfileButton_Click(object sender, RoutedEventArgs e)
{
np.Show();
}
//adds button to listbox
internal void TestAddButton_Clicked(object sender, RoutedEventArgs e)
{
Button cmd = (Button)sender;
if (cmd.DataContext is User)
{
//Profile is where the finished information is displayed//
Profile pro = new Profile();
pro.DataContext = cmd.DataContext;
pro.Show();
}
}
//this is where confusion ensues
private void DeleteUserButton_Click(object sender, RoutedEventArgs e)
{
//error occurs here
ButtonHoldersListbox.Items.Remove(ButtonHoldersListbox.SelectedItem);
}
}
}
The Profile Creator:
<Window x:Class="SavingButtons.NewProfile"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewProfile" Height="300" Width="500">
<Grid>
<Label Content="FirstName" HorizontalAlignment="Left" Margin="64,44,0,0" VerticalAlignment="Top"/>
<Label Content="LastName" HorizontalAlignment="Left" Margin="64,97,0,0" VerticalAlignment="Top"/>
<Button Name="UploadImageButton" Click="UploadImageButton_Click" Content="Upload Image" HorizontalAlignment="Left" Margin="64,146,0,0" VerticalAlignment="Top" Width="75"/>
<TextBox Name="FirstNameTextBox" HorizontalAlignment="Left" Height="23" Margin="126,47,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
<TextBox Name="LastNameTextBox" HorizontalAlignment="Left" Height="23" Margin="126,99,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
<Image Name="imgPhoto" HorizontalAlignment="Left" Height="100" Margin="173,146,0,0" VerticalAlignment="Top" Width="100"/>
<Button Name="ProfileFinishedLaunch" Content="Done" HorizontalAlignment="Left" Margin="360,232,0,0" VerticalAlignment="Top" Width="75" Click="ProfileFinishedLaunch_Click"/>
</Grid>
namespace SavingButtons
{
public partial class NewProfile : Window
{
public ObservableCollection<User> ProfileList;
public MainWindow mMain;
public NewProfile(MainWindow main)
{
InitializeComponent();
ProfileList = new ObservableCollection<User>();
mMain = main;
}
//loads image
private void UploadImageButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog op = new OpenFileDialog();
op.Title = "Select a picture";
op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png|" +
"JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
"Portable Network Graphic (*.png)|*.png";
if (op.ShowDialog() == true)
{
imgPhoto.Source = new BitmapImage(new System.Uri(op.FileName));
}
}
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
mMain.buttonNumberID++;
ProfileList.Add(new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source });
mMain.ButtonHoldersListbox.DataContext = ProfileList;
mMain.Show();
this.Hide();
}
Upvotes: 0
Views: 473
Reputation: 309
Thanks to Miklos, I did get my problem solved, however, the binding is still pretty confusing. Mainly: how does the ListBox know to bind the ObservableCollection ProfileList? In Mikalos version, he explicitly binds the ObservableCollection to the Listbox in the XAML(NOTE: Mikalos observable collection is named "Profile")
ItemsSource="{Binding Profiles}"
That would seem the most explicit. Instead, the only way I was only able to make it work was this way(ProfileList is the name I used for the observable collection):
ItemsSource="{Binding}"
Not sure how it knows to bind to my observableCollection. I will include my working code below.
<Window x:Class="SavingButtons.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="UserTemplate">
<StackPanel Orientation="Horizontal">
<Button Name="TestButton" Click="cmdDeleteUser_Clicked" Content="{Binding FirstName}" Width="100" Height="40"></Button>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Name="AddProfileButton" Content="Add Profile" HorizontalAlignment="Left" Margin="22,29,0,0" VerticalAlignment="Top" Width="75" Click="AddProfileButton_Click"/>
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
<Button Name="DeleteUserButton" Click="DeleteUserButton_Click" Content="Delete User" HorizontalAlignment="Left" Margin="246,69,0,0" VerticalAlignment="Top" Width="105"/>
</Grid>
My ProfileCreator Cs:
public partial class NewProfile : Window
{
public MainWindow mMain;
public NewProfile(MainWindow main)
{
InitializeComponent();
mMain = main;
}
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
////Mikalos CODE-----------------------------------------------------------//
var newUser = new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source };
mMain.ProfileList.Add(newUser);
mMain.ButtonHoldersListbox.DataContext = mMain.ProfileList;//Mikalo suggested not putting ListBox.DataContext here,
//however, this is the only place it works.
mMain.Show();
this.Hide();
//---------------------------------------------------------------//
}
Upvotes: 0
Reputation: 1372
if you set itemsource to usercontrol you can't operate it's items directly. Edit it's itemsource instead. give you a simple example.
public partial class MainWindow : Window
{
ObservableCollection<int> ProfileList;
public MainWindow()
{
InitializeComponent();
ProfileList = new ObservableCollection<int>();
this.DataContext = ProfileList;
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
Random r = new Random();
int num = r.Next(100);
ProfileList.Add(num);
//lstShow.Items.Add(num); error!
}
private void btnDel_Click(object sender, RoutedEventArgs e)
{
if (lstShow.SelectedIndex > -1)
{
ProfileList.Remove((int)lstShow.SelectedItem);
//lstShow.Items.Remove((int)lstShow.SelectedItem); error!
}
}
}
Upvotes: 0
Reputation: 2202
You are setting your
Listbox` to the others window property and you do it every time after a new item was added.
The error occurs, because the listbox items were set through binding to the ItemsSource
property and in this case the ListBox.Items
is read only so you can't remove or add item directly.
Instead of what you have now, add an ObservableCollection<User>
property to your MainWindow
class and bind the ListBox
to this property. In the NewProfile
window you need to add the new User item to this collection. The delete operation will work with removing the item from that collection (actually the senders DataContext
)
public partial class MainWindow : Window
{
public ObservableCollection<User> Profiles {get; set;}
//...
private void DeleteUserButton_Click(object sender, RoutedEventArgs e)
{
var removable = ButtonHoldersListbox.SelectedItem as User;
if(removable != null)
Profiles.Remove(removable);
}
}
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Profiles}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
public partial class NewProfile : Window
{
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
mMain.buttonNumberID++;
var newUser = new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source };
mMain.Profiles.Add(newUser);
//Don't set the listbox.DataContext here
mMain.Show();
this.Hide();
}
Upvotes: 1