I have two tables, employees and project, in listbox2, I show all the employees and in listbox1 all the projects, now obviously one employee can be involved in many projects and one project could have many employee. So I have this EmployeeProject that maps the many to many relation that exists. What I want is, if user click a project name in first listbox, then all employees in that project should be selected in listbox2. Also, when a user clicks a item in listbox2, (an employee) all project of which that employee is a part should be selected in listbox1
But If I use ListBox.SelectedIndexChanged event for this process, and select even a single value in listbox2 then it would trigger the SelectedIndexChagned for listbox2, and that would start working by selecting all items in listbox1 that current employee is a part of, but again, as soon as even one item in listbox1 is selected, it would fire up its SelectedIndexChanged event, and it would go on forever like this. So what's the solution of this? So far, I've done this..
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
// Load the list of employees
cmd.CommandText =
"SELECT EmpName FROM Employee WHERE EmpID IN(SELECT EmpID FROM EmployeeProject WHERE ProjectID =(SELECT ProjectID FROM Project WHERE ProjectName = '" +
listBox1.SelectedItem.ToString() + "')) ORDER BY EmpId";
var rdr = cmd.ExecuteReader();
listBox2.Items.Clear();
// right now, I am doing this to escape this recursive loop, but thats not what I want
while (rdr.Read())
{
listBox2.Items.Add(rdr.GetString(0));
}
rdr.Close();
this.AutoScroll = true;
}
private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
{
// Load the list of projects
cmd.CommandText =
"SELECT ProjectName FROM Projects WHERE ProjectID IN(SELECT ProjectID FROM EmployeeProject WHERE EmpId=(SELECT EmpId FROM Employee WHERE EmpName= '" +
listBox2.SelectedItem.ToString() + "')) ORDER BY ProjectID";
var rdr = cmd.ExecuteReader();
listBox1.Items.Clear();
// again, I don't want to clear, but select all those employee in listbox1 that are involved in this selected project, but can't do it because it would cause infinite recursion of these
while (rdr.Read())
{
listBox2.Items.Add(rdr.GetString(0));
}
rdr.Close();
this.AutoScroll = true;
}
So? What should I do to achieve what I want to achieve? And how would I avoid that recursion? I know this way also works what I just showed, but I don't want to clear up and show up again, (this might confuse a simple user). I want for each selection, values corresponding to that selection be selected in other listbox (without causing recursion of course!). How do I do it?
EDIT I don't know how can I select multiple items in listbox programmatically, so if you could tell that, it would be great!
There is a design pattern called Balking, I think it applies here.
http://en.wikipedia.org/wiki/Balking_pattern
The idea is to introduce an auxiliary state variable to control the operation:
private bool doesProcessing { get; set; }
private void listBox1_SelectedIndexChanging( ... )
{
// signal the beginning of processing
if ( doesProcessing )
return;
else
doesProcessing = true;
try
{
// your logic goes here
}
finally
{
// signal the end of processing
doesProcessing = false;
}
}
and the same for listBox2.
When you change the selected items of one listbox, you could temporarily disable the events on the other. For example:
// Store the event handlers in private member variables.
private System.EventHandler selectedEmployeeChanged = new System.EventHandler(this.lbEmployees_SelectedIndexChanged);
private void lbProjects_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
// Remove your event so that updating lbEmployees doesn't cause
// lbEmployees_SelectedIndexChanged to get fired.
lbEmployees.SelectedIndexChanged -= selectedEmployeeChanged;
// other event handler logic
}
finally
{
// Ensure that the handler on lbEmployees is re-added,
// even if an exception was encountered.
lbEmployees.SelectedIndexChanged += selectedEmployeeChanged;
}
}
Note: I have renamed your listBoxes (and the associated events) to be more readable. As per your description, listBox1 is now lbEmployees and listBox2 is now lbProjects.
As for programatically selecting multiple items, if you know the index for each one, you could use the ListBox.SetSelected Method. For example:
listBox1.SetSelected(1, true);
listBox1.SetSelected(3, true);
causes listBox1 to select the items at 1 and 3.
In your case, I would recomend only using the queries to get what values to select (not clearing your listboxes and then just adding back the values that should be selected). Below is my suggestion for how you would rewrite one of your handlers:
// Store the event handlers in private member variables.
private System.EventHandler selectedEmployeeChanged = new System.EventHandler(this.lbEmployees_SelectedIndexChanged);
private void lbProjects_SelectedIndexChanged(object sender, EventArgs e)
{
// Declare this outside of try so that we can close it in finally
DbDataReader reader = null;
try
{
// Remove your event so that updating lbEmployees doesn't cause
// lbEmployees_SelectedIndexChanged to get fired.
lbEmployees.SelectedIndexChanged -= selectedEmployeeChanged;
// Deselect all items in lbEmployees
lbEmployees.ClearSelected();
cmd.CommandText = "SELECT ProjectName FROM Projects WHERE ProjectID IN(SELECT ProjectID FROM EmployeeProject WHERE EmpId=(SELECT EmpId FROM Employee WHERE EmpName= '#EmpName')) ORDER BY ProjectID";
cmd.Parameters.AddWithValue("#EmpName", lbProjects.SelectedItem.ToString());
reader = cmd.ExecuteReader();
// For each row returned, find the index of the matching value
// in lbEmployees and select it.
while (rdr.Read())
{
int index = lbEmployees.FindStringExact(rdr.GetString(0));
if(index != ListBox.NoMatches)
{
lbEmployees.SetSelected(index, true);
}
}
this.AutoScroll = true;
}
finally
{
// Ensure that the reader gets closed, even if an exception ocurred
if(reader != null)
{
reader.Close();
}
// Ensure that the handler on lbEmployees is re-added,
// even if an exception was encountered.
lbEmployees.SelectedIndexChanged += selectedEmployeeChanged;
}
}
As a side note, you may want to look into using a JOIN operator in your query string, rather than multiple nested SELECTS.
You can remove the attached event to one of the controls when you are in the other, before doing any modifications.
then reattach at the end.
Have one eventMask integer variable and use the pattern below to protect your event each other.
Using a bool as suggested in another answer is enough for this scenario. However, I prefer to use an integer rather than a bool : it can handle nested and stacked events calls correctly.
It is particularly useful when I have to deal with selection/deselection events in treeviews and listviews for example.
int eventMask = 0;
void items_PropertyChanged(object sender, PropertyChangedEventArgs e) // for example...
{
if (eventMask > 0)
return;
try
{
eventMask++;
// processing
// it happens some other events can be called. Including this one.
}
finally
{
// put this in finally, so there is no lock risk
eventMask--;
}
}
The logic of the OP code (if working as OP expect) contains a problem.
Suppose you fill the two lists with all possible values.
Then, when the user clicks on one item, the second list is emptied and filled only with the item linked to the other list. But clicking between the two list could remove forever one or more item from the other list. It is better to leave the two list filled and trying to highlight the line of the corresponding items
For example (supposing ListBox.SelectionMode = SelectionMode.MultiSimple or MultiExtended)
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
........
try
{
......
// Remove the event handler, do your selection, No event on the listbox2 will be fired....
listBox2.SelectedIndexChanged -= new System.EventHandler(this.listBox2_SelectedIndexChanged);
// Do not remove the items , instead clear previous selection
listBox2.ClearSelected();
while (rdr.Read())
{
int index = listBox2.FindString(rdr.GetString(0), -1);
if(index != -1) listBox2.SetSelected(index, true));
}
....
}
finally
{
// before exit, reapply the event handler
listBox2.SelectedIndexChanged += new System.EventHandler(this.listBox2_SelectedIndexChanged);
}
}
of course you need to be sure to reapply the event, so use a try/finally block
The same approach will be valid to stop the event firing for listBox1 when you fill it in the listBox2.SelectedIndexChanged event,
Related
I have a DataGridView and the DataGridView's DataSource is a BindingList I got from the Entity Framework (V6) via context.Person.Local.ToBindingList().
After I set the DataSource to this BindingList, I dispose the context, because I read that keeping the context open would be bad practice.
So, if I wanted to add a new row, I would click on the "add" button that comes with the BindingNavigator that got created when I dragged the "people" object data source to my Windows Form.
Every time I click the "add" button, I get an exception that tells me that the context has been disposed.
Do I need to keep the context open all the time when using DataGridView? Oh and: the DataSource might change during runtime depending on the selection of a ListBox Item.
Also, when the context has been disposed and I edited one row from the DataGridView, how could I find out (after multiple changes) which row has changed?
I tried to do:
foreach(DataGridViewRow row in peopleDataGridView.Rows)
{
People item = (People)row.DataBoundItem;
if (item != null)
{
db.People.Attach(item);
}
}
db.SaveChanges();
...but SaveChanges() did not recognize any changes. However, if I force every attached item to a "modified" state, it works. But I do not want to change 100 items to "modified", if only one got actually modified.
Any ideas?
EDIT 1
Oh well, so I changed my code to keep the context open all the time (or at least as long as the form gets displayed).
Now, I ran into a different problem (people may have many jobs):
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
People p = (People)listBox1.SelectedItem;
if(p != null)
{
//jobBindingSource.Clear(); this caused another error at runtime...
db.Entry(p).Collection(b => b.Job).Load();
jobBindingSource.DataSource = db.Job.Local.ToBindingList();
}
}
The DataGridView that is bound to this jobBindingSource instance shows the correct jobs for a person, but in addition to the jobs from the previously selected person. I tried to Clear() the entries, but if I do this and click on the same person twice, the datagridview starts to sometimes show no entries at all. A strange behaviour.
What am I doing wrong now?
EDIT 2
Okay... I found a solution myself. But I refuse to accept that this is the correct way to do it:
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
People p = (People)listBox1.SelectedItem;
if(p != null)
{
db.Dispose();
db = new PeopleJobsEntities();
db.People.Attach(p);
db.Entry(p).Collection(person => person.Job).Load();
jobBindingSource.DataSource = db.Job.Local.ToBindingList();
}
}
Only if I dispose the context and open it anew, the whole thing works. The reason is that if I clear the local cache (of db.Job.Local), its entries will not be reloaded again even if I use the Load() method. Is there some way to force the reloading of entities?
While I try not to keep the DBContext open for a long period of time, with datagrids you don't have much choice. I set my grid's DataSource property to IQueryable<T> and then all the edits, deletes and additions are taken care of by the grid and context itself. You just have to call dbContext.SubmitChanges() whenever you want to persist the changes. You can save each time a user leaves a row by saving on the RowLeave or the RowValidated event. Or you can save when you close the form. But also make sure you call dbContext.Dispose() when you close the form as well.
To find out which rows change you can view the ChangeSet that is returned by doing the following:
var changes = dbContext.GetChangeSet();
dbContext.SubmitChanges();
Be sure if your item is not null.
Check your connection string.
And, try this :
db.People.Add(item);
Instead of :
db.People.Attach(item);
Ok, thanks to #jaredbaszler I came up with this solution that works fine for me.
I decided to keep the DbContext alive all the time. To clear the local cache, I detached every entity inside in a loop. I think this is a very disgusting way to do it. There must be a better way...
This is what I have:
PeopleJobsEntities db;
public FormTest()
{
InitializeComponent();
db = new PeopleJobsEntities();
db.Database.Log = Console.Write;
db.People.Load();
List<People> peoplelist = db.People.Local.ToList();
listBox1.DataSource = peoplelist;
}
private void FormTest_FormClosing(object sender, FormClosingEventArgs e)
{
if (db != null)
db.Dispose();
}
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
People p = (People)listBox1.SelectedItem;
if(p != null)
{
List<Job> oldlist = db.Job.Local.ToList();
foreach (Job j in oldlist)
{
db.Entry(j).State = EntityState.Detached;
}
db.Entry(p).Collection(b => b.Job).Load();
jobBindingSource.DataSource = db.Job.Local.ToBindingList();
}
}
private void jobBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
foreach(DataGridViewRow row in jobDataGridView.Rows)
{
if(row != null && row.DataBoundItem != null)
{
Job j = (Job)row.DataBoundItem;
if(db.Entry(j).State == EntityState.Added)
{
if(j.People.Count == 0)
{
People people = (People)listBox1.SelectedItem;
if (people != null)
j.People.Add(people);
}
}
}
}
db.SaveChanges();
}
Editing entries works
Adding new entries works
Deleting entries works
I am now creating an app, that allows user to create and retrieve data from local database. There is one column named LIKE with a default value of int 0. I have a button, but is there anyway that I can press that button then the default value of int 0 will become 1 ? Everytime I press it will add one to the default value int. How can I do that ?
Assuming your database context is set as in the tutorial I mentioned in the comment, your button click event can look like the following:
private void IncrementLikes_Click(object sender, RoutedEventArgs e)
{
//where Likes is your ObservableCollection keeping data from database
LikeItem oneAndOnly = Likes.First() as LikeItem;
//you pulled one and only LikeItem object, now you increment the likes count
oneAndOnly.LikesCount += 1;
//and here you tell the database that the changes need to be saved.
//This call can be delayed to the OnNavigatedFrom event,
// depending on your needs.
LikeDB.SubmitChanges();
}
and here is some supporting code that belongs to the .cs file of the page:
//your database context
private YourDatabaseDataContext LikeDB;
// Define an observable collection property that controls can bind to.
private ObservableCollection<LikeItem> _likes;
public ObservableCollection<LikeItem> Likes
{
get
{
return _likes;
}
set
{
if (_likes != value)
{
_likes = value;
NotifyPropertyChanged("Likes");
}
}
}
// Constructor
public MainPage()
{
InitializeComponent();
LikeDB = new YourDatabaseDataContext(YourDatabaseDataContext.DBConnectionString);
this.DataContext = this;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// Define the query to gather all of the to-do items.
var likesFromDB = from LikeItem like in LikeDB.Likes
select like;
// Execute the query and place the results into a collection.
Likes = new ObservableCollection<LikeItem>(likesFromDB);
// Call the base method.
base.OnNavigatedTo(e);
}
Using the database for just one value may be too much, so you might want to look at other alternatives, such as Windows Phone settings.
I have a program that people can leave comments on a video. The comments come is as in queue status. The admin can go into the admin section and mark the comments as either approved or removed. They want to be able to automatically go to the next item marked in queue when they press either the previous or next buttons, as well as if they approve or remove a comment. I do not know jQuery or JavaScript well enough to know if it is possible to do it using those, or how to do it through the code behind (this is in C# .NET). Any help would be appreciated:
Status and value:
In queue = 0
Approved = 1
Removed = 2
Here is the code-behind. The status changes work, the only thing I cannot do is have it go to the next record marked in queue. The first two events are blank because I do not know how to fill them, but simply put, all the need to do too is go to the next record marked in queue.
If you need any more code, please let me know...
protected void previous_clicked(object sender, EventArgs e)
{
}
protected void next_clicked(object sender, EventArgs e)
{
}
protected void approve_clicked(object sender, EventArgs e)
{
currentMessage = new videomessage(Request["id"].ToString());
status.SelectedValue = "1";
currentMessage.status = "1";
currentMessage.Save();
}
protected void remove_clicked(object sender, EventArgs e)
{
currentMessage = new videomessage(Request["id"].ToString());
status.SelectedValue = "2";
currentMessage.status = "2";
currentMessage.Save();
}
Sounds more like an architectural challenge to me.
I recommend using a Queue. This is a collection type following a first-in, first-out (FIFO) approach. You put objects into the queue and get them back out in the same order. An object that was received out of this queue is automatically is removed from the queue, so you can be sure that you do not handle the same element twice.
Your described workflow then would work as these simple steps:
Whenever a message arrives, you put the object into your queue.
When the admin clicks on the next button, you request the first object out of the queue.
Your admin does his administrative tasks and approves the message.
Clicking on Next start with above item 1 again.
[EDIT]
Oops, I realized that my Queue approach would not allow for navigating back to previous items.
In this case I suggest using a simple List collection. This list can be accessed via the 0-based position in the list. This makes it easy to implement a forward/ backward navigation.
For my sample code, please bear in mind that there is a lot that I cannot know about your environment, so my code make a lot assumptions here.
You need to somwhere store a collection that contains your messages to be approved:
private IList<videomessage> _messagesToApprove = new List<videomessage>();
You will also need some variable that keeps track of the current position in your collection:
// Store the index of the current message
// Initial value depends on your environment. :-)
private int _currentIndex = 0;
To begin with, you will need a starting point where new messages are added to that collection, like subscribing to some event or so. Whenever a message arrives, add it to the collection by calling a method like:
// I made this method up because I do not know where your messages really come from.
// => ADJUST TO YOUR NEEDS.
private void onNewMessageArriving(string messageId)
{
videomessage arrivingMessage = new videomessage(messageId);
_messagesToApprove.Add(arrivingMessage);
}
The you can easily implement the navigation by incrementing/ decrementing the position index:
private void previous_Click(object sender, EventArgs e)
{
// Check that we do not go back further than the beginning of the list
if ((_currentIndex - 1) >= 0)
{
_currentIndex--;
this.currentMessage = this._messagesToApprove[_currentIndex];
}
else
{
// Do nothing if the position would be invalid
return;
}
}
private void next_Click(object sender, EventArgs e)
{
// Check if we have new messages to approve in our list.
if ((_currentIndex + 1) < _messagesToApprove.Count)
{
_currentIndex++;
currentMessage = _messagesToApprove[_currentIndex];
}
else
{
// Do nothing if the position would be invalid
return;
}
}
private void approve_Click(object sender, EventArgs e)
{
// Sorry, I don't know where exactly this comes from, needs to be adjusted to your environment
status.SelectedValue = "1";
this.currentMessage.status = "1";
this.currentMessage.Save();
// If you want to remove items that have been checked by the admin, delete it from the approval list.
// Otherwise remove this line :-)
this._messagesToApprove.RemoveAt(_currentIndex);
}
private void remove_Click(object sender, EventArgs e)
{
// Sorry, I don't know where exactly this comes from, needs to be adjusted to your environment
status.SelectedValue = "2";
this.currentMessage.status = "2";
this.currentMessage.Save();
// If you want to remove items that have been checked by the admin, delete it from the approval list.
// Otherwise remove this line :-)
this._messagesToApprove.RemoveAt(_currentIndex);
}
Save the id of current comment in session or viewstate get it back on next or previous button click and display the accordingly:
Session["id"] = 2;
int id = (int) Session["id"];
New to entity framework, but I would think this should be pretty simple.
My form load creates a context from my Entities. I create a list of clients and have a binding source that I assign clients to. The binding source is assigned to a Binding Navigator - clientBindingNavigator.
private void ClientExtForm_Load (object sender, EventArgs e)
{
_context = new IDVisitorEntities ();
List<IDVM.Client> clients = _context.Clients.ToList ();
clientBindingSource.DataSource = clients;
}
excerpt from ClientExtForm.Designer.cs
//
// clientBindingNavigator
//
this.clientBindingNavigator.AddNewItem = this.bindingNavigatorAddNewItem;
this.clientBindingNavigator.BindingSource = this.clientBindingSource;
this.clientBindingNavigator.CountItem = this.bindingNavigatorCountItem;
this.clientBindingNavigator.DeleteItem = this.bindingNavigatorDeleteItem;
this.clientBindingNavigator.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.bindingNavigatorMoveFirstItem,
this.bindingNavigatorMovePreviousItem,
this.bindingNavigatorSeparator,
this.bindingNavigatorPositionItem,
this.bindingNavigatorCountItem,
this.bindingNavigatorSeparator1,
this.bindingNavigatorMoveNextItem,
this.bindingNavigatorMoveLastItem,
this.bindingNavigatorSeparator2,
this.bindingNavigatorAddNewItem,
this.bindingNavigatorDeleteItem,
this.clientBindingNavigatorSaveItem});
When I click the Delete Button on the navigator tool bar the the ClientBindingSource.Count has been reduced by 1 .
private void clientBindingNavigatorSaveItem_Click (object sender, EventArgs e)
{
this.OnSave ();
}
public override void OnSave ()
{
foreach (ObjectStateEntry entry in _context.ObjectStateManager.GetObjectStateEntries (EntityState.Deleted))
{
// nothing shows up in this
}
foreach (ObjectStateEntry entry in _context.ObjectStateManager.GetObjectStateEntries (EntityState.Modified))
{
// when modified
}
foreach (ObjectStateEntry entry in _context.ObjectStateManager.GetObjectStateEntries (EntityState.Added))
{
// when adding this finds it
}
clientBindingSource.EndEdit ();
visitorHostsBindingSource.EndEdit ();
_context.SaveChanges ();
base.OnSave ();
}
It appears as though the navigator is removing the item from the collection.
Added info: It appears that in the navigator the DeleteItem button corresponds to the RemoveCurrent method (on click event calls it). Not sure how to tie in before the RemoveCurrent does it's thing.
What are my options for preforming the delete?
Removing an item from the clientBindingSource will have no effect on the item at the database level. You have to explicitly call _context.Clients.DeleteObject(deletedClient); You perform must all CRUD operations through the ObjectContext.
After looking around found some blogs that suggest to not use the default DeleteItem.
this.clientBindingNavigator.DeleteItem = null;//= this.bindingNavigatorDeleteItem;
In my case to make it clear for the BindingNavigator I replaced this.bindingNavigatorDeleteItem with a new button this.toolStripButton1 in the Items list.
this.clientBindingNavigator.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.bindingNavigatorMoveFirstItem,
this.bindingNavigatorMovePreviousItem,
this.bindingNavigatorSeparator,
this.bindingNavigatorPositionItem,
this.bindingNavigatorCountItem,
this.bindingNavigatorSeparator1,
this.bindingNavigatorMoveNextItem,
this.bindingNavigatorMoveLastItem,
this.bindingNavigatorSeparator2,
this.bindingNavigatorAddNewItem,
this.toolStripButton1,
this.clientBindingNavigatorSaveItem});
Creation of the new button looks like this:
//
// toolStripButton1
//
this.toolStripButton1.Image = ((System.Drawing.Image) (resources.GetObject ("bindingNavigatorDeleteItem.Image")));
this.toolStripButton1.RightToLeftAutoMirrorImage = true;
this.toolStripButton1.Name = "toolStripDeleteItem";
this.toolStripButton1.Size = new System.Drawing.Size(23, 22);
this.toolStripButton1.Text = "Delete";
this.toolStripButton1.Click += new System.EventHandler(this.toolStripButton1_Click);
The Click event then calls the RemoveCurrent (just like the default does) but I can get the current entity and stash it in an arrraylist for use on save.
private void toolStripButton1_Click (object sender, EventArgs e)
{
var currentclient = (Client) clientBindingSource.Current;
clientstodelete.Add (currentclient);
clientBindingSource.RemoveCurrent ();
}
I didn't need to create a new button, I just needed to have the this.clientBindingNavigator.DeleteItem not tied to a button. Because the DeleteItem creates a click event under the hood that calls the BindingSource.RemoveCurrent(). I might change the button back to the default one created but for illustration wanted everyone to see what was happening.
I agree it seemed odd to have to delete the record from the table directly if I had just deleted using RemoveCurrent(). But it is what it is... This took care of the record in the datagridview and the datasource in one clean sweep.
Here is how I solved the problem:
t_StaffDaysOff sdo = (t_StaffDaysOff)t_StaffDaysOffbindingSource.Current;
t_StaffDaysOffbindingSource.RemoveCurrent();
t_StaffDaysOffbindingSource.EndEdit();
db.t_StaffDaysOff.Remove(sdo);
db.SaveChanges();
I have a listbox control that has items dynamically added to and manually removed from (due to 'remove item' button). When the number of items is changed, I would like to update other parts of the user interface - namely a caption that says 'You must choose some files.' and an item count caption.
How can an event handler or effectively an event handler be added to fire when the number of items is changed - e.g. an ItemAdded or ItemRemoved or ItemsChanged
Note: This is nothing to do with the user selecting items in the listbox.
Thanks very much!
You can try using a BindingList<> as your DataSource, and then you act on that list instead of your ListBox-- it will get the updates automatically from the BindingList.
The BindingList has a ListChanged event.
The ListChanged event has a ListChangedEventArgs that includes a ListChangedType enumerator:
BindingList<string> list = new BindingList<string>();
list.ListChanged += new ListChangedEventHandler(list_ListChanged);
void list_ListChanged(object sender, ListChangedEventArgs e) {
switch (e.ListChangedType){
case ListChangedType.ItemAdded:
break;
case ListChangedType.ItemChanged:
break;
case ListChangedType.ItemDeleted:
break;
case ListChangedType.ItemMoved:
break;
// some more minor ones, etc.
}
}
In the code for the "Remove Item" button, also update the other parts of the UI. This doesn't have to violate coding principles; you can do something like this:
void UpdatePartsOfTheUI() {
// Do something here
if(myListBox.Items.Count == 0) {
myLabel.Text = "You must choose some files!";
} else {
myLabel.Text = String.Empty;
}
}
/* ... */
void myButton_Click(object sender, EventArgs e) {
if(myListBox.SelectedIndex > -1) {
// Remove the item
myListBox.Items.RemoveAt(myListBox.SelectedIndex);
// Update the UI
UpdatePartsOfTheUI();
}
}
This works if you don't have many buttons that change the ListBox. If you do, just wrap the ListBox's Items.Add/Items.Insert/Items.Remove in methods that include your other code and call those from your button handlers instead.
Create a method to which you can do what you want with the ListBox Item.
for example:
my program receives data feeds from a server upon request or request for stream.
In my winform1.cs my control for adding data to a list box is as followed.
public void AddData(string data)
{
if (this.ResponseData.InvokeRequired)
BeginInvoke(new AddDataDelegate(AddData), new object[] { data });
else
{
this.ResponseData.Items.Insert(0, data);
DataDistro();
}
}
DataDistro Is what I called My Method for doing work with the new data. Also Note by inserting at index value 0, the new item will Always be on top.
If you are using winForm this is a lot easier, Also, Because the adding of an item is handled by a delegate, the main thread is still open. If your not using a method that is adding all the data to the listbox this will not work. And using the bindingsource method mentioned above would be the next best thing.
here is an example of my DataDistro method: My response strings look like this: [Q],ATQuoteLastPrice,value
[B],datetime,open,high,low,close,volume
private void DataDistro()
{
string data = ListBox.Items[0].ToString();
string[] split = data.Split(new string[] {","}, stringsplitoptions.None);
if(spit[0] == "[Q]")
{
//do some work
}
if(split[0] == "[B]")
{
//Do some work
}
}
In your case you would call your method at end of the remove item button click. I would also suggest to make a delegate, or backgroundWorker if the work is extensive. As Calling from a button click Event will be handled by UI Thread.
Everytime the AddData method is called upon, the DataDistro method is also called after data is added.