ListPicker SelectionChanged gets called multiple times - c#

the ListPicker is a Control from the WP8 Toolkit.
Code:
private void field_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Make sure we don't handle the event during initiation.
if (e.RemovedItems != null && e.RemovedItems.Count > 0)
{
if (this.field.SelectedItems != null)
{
if (this.field.SelectedIndex != -1)
{
ListPicker_SelectionChanged(sender, e);
//Make needed proffesions visable:
profls.Clear();
foreach (ListPickItem item in field.SelectedItems)
switch (item.Tag)
{
default:
foreach (ListPickItem iitem in profl[9])
profls.Add(iitem);
break;
case 90017:
foreach (ListPickItem iitem in profl[0])
profls.Add(iitem);
break;
case 9000:
foreach (ListPickItem iitem in profl[1])
profls.Add(iitem);
break;
}
}
}
}
}
Please notice that a profession ListPicker's ItemsSource is Data Binded to the profls var.
I modified the Listpicker so that I can also set the SelectedItems property and not only read from it (following this guide) and it works great.
Problem:
The field_SelectionChanged event gets called multiple times whenever I change the field listpicker's selecteditems. (i want it to be called only once..) Another wierd thing is that on one of the last calls the field_SelectedItems is equal to the old selectedItems (the ones before the "change")..
Is it a bug or my problem? (How do I fix it?)
EDIT:
I checked and it appears that it gets called only once if the are no selected items in the listpicker before I select items. (I mean that SelectedItems is empty before I select new items)

Fixed :)
I used the code suggested in this answer: listPicker not updating selection in full mode
if (MyListPicker.SelectedIndex != -1)
{
//Code..
}

I had the same issue of the selectedchange event being called twice.At the end of the listPicker_selectedchange event, set the listpicker selected index to -1.
private void listpicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Code
listpicker.SelectedIndex = -1;
}
Thank you Dan Barzilay!!

Related

How to compare WPF ListBox Items with a String?

I've seen some examples with sorting and filtering using colllectionView but none doing in a comparative logical way.
example:
// Button event to send an item from LB1 to LB2
private void BaddProduct_Click(object sender, RoutedEventArgs e)
{
if(Lb2.Items.Contains("item"))
{
MessageBox.Show("This item is already there!");
}
//second example
if(Lb2.Items.StartWith("item"))
{
MessageBox.Show("This item is already there!");
}
}
The code works for winforms. Is there an approach for WPF?
Thank you!
This code would work in the .xaml.cs file as well, as the 'code behind' has direct reference to all named, non-template WPF items.
If you would like the data sources to be a little more dynamic, I suggest using IEnumerable collections to bind your ItemSource to, and then you can perform all filtering using LINQ.
(edit: fixed typo 'datasources' to 'data sources')
For anyone that needs this approach:
//called by the collectionView
private bool UserFilter(object item)
{
string produtoItem;
//my LB1. With dynamic type i am gettting the item selected.
dynamic selectProdutoItem = LbProdutoPlano.SelectedItem;
produtoItem = selectProdutoItem.produtoPlanoNome;
if (String.IsNullOrEmpty(produtoItem))
{
return true;
}
else
{
//this will do a comparative logic on my selected item on LB 1. fichaProduto is my class and fichaProdutoProdutoNome a string of that class that is the same string or the item in LB1, produtoItem.
return ((item as fichaProduto).fichaProdutoProdutoNome.IndexOf(produtoItem, StringComparison.OrdinalIgnoreCase) >= 0);
}
}
// a button to add an item from LB1 to LB2.
private ButtonAdd_Click()
{
//created the collectionView in here having the itemSource the LB2 that is already binded.
CollectionView viewFiltro = (CollectionView)CollectionViewSource.GetDefaultView(LbProdutoPlanoEscolhido.ItemsSource);
// this is the key of this logic. The View will do a comparative logic from the retur of the UserFilter method.
viewFiltro.Filter = UserFilter;
// so if the View found it, it will count 1.
if (viewFiltro.Count == 1)
{
MessageBox.Show("This product is already in the LB2.");
}
else
{
// add the item into LB 2.
}
}
In this way, we donĀ“t have to compare observableĀ“s Collection for none of the ListBoxes. Just use the predicate Filter of the CView and check the result of it using the property COUNT. If it is 1, that means the filter seach for an item in LB1 and found it on the LB 2.

Remove something added by a particular event - c#

I have a piece of code that will add the selected item of a combobox to a listbox when a checkbox is checked. I would like to remove that selected item to be removed from the listbox when the checkbox is unchecked.
My problem is I cant simply repeat the code for removal to be the same as add because the combobox selection will different or empty when unchecked.
This is how it currently looks:
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked == true)
{
listBox2.Items.Add(comboBox1.SelectedItem);
}
if (checkBox1.Checked == false)
{
listBox2.Items.Remove(comboBox1.SelectedItem);
}
So I need a way of removing whatever was added by that check change instead of removing the selected index of the combobox. Please consider that there may be multiple lines in the listbox added by multiple different checkboxes.
You simply need to store the added item and remove it.
private object addedItem;
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
addedItem = comboBox1.SelectedItem;
listBox2.Items.Add(addedItem);
}
else
{
listBox2.Items.Remove(addedItem);
}
}
You may also need to check SelectedItem is null before adding/removing the item.
Focusing on the part where you said there might be multiple different checkboxes,
you need to store one item per checkbox.
You can write your own child class of the checbox control to add this feature, or simply use the Tag property.
You can also indicate which checkbox is linked to which combobox in the same way. Either child class or use the Tag property.
In my example, I'll assume you've referenced the combobox from the checkbox using the Tag property.
You can do it manually like this
checkBox1.Tag = comboBox1;
or hopefully you can automate it if you are generating these on the fly.
Here is the general idea of how the checkbox event should look.
The event is is utilising the sender argument, which means you should hook up all your checkboxes CheckedChanged events to this one handler. No need to create separate handlers for each.
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
var checkBox = (CheckBox)sender;
var comboBox = (ComboBox)checkBox.Tag;
if (checkBox.Checked && comboBox.SelectedItem != null)
{
listBox2.Items.Add(comboBox.SelectedItem);
comboBox.Tag = comboBox.SelectedItem;
}
if (!checkBox.Checked && comboBox.Tag != null)
{
listBox2.Items.Remove(comboBox.Tag);
}
}

CheckedListBox Only One Checked But Keep Last

I am having some difficulty getting a CheckedListBox to behave the way I want it to. What I am trying to accomplish is getting a CheckedListBox with the first box checked on PageLoad. I want only one checkbox checked at any given time but I also don't want the user to uncheck the last checked box. I can do one or the other but I can't seem to do both.
Here is some code snippets that I have used to accomplish the task of having only one checkbox checked. The problem with these are the last selection can be unchecked where there are no checkboxes checked.
1st Snippet:
if(e.NewValue == CheckState.Checked)
{
// Uncheck the other items
for (int i = 0; i < defCheckedListBox.Items.Count; i++)
{
if (e.Index != i)
{
this.defCheckedListBox.SetItemChecked(i, false);
}
}
}
2nd Snippet
// Ensure that we are checking an item
if (e.NewValue != CheckState.Checked)
{
return;
}
// Get the items that are selected
CheckedListBox.CheckedIndexCollection selectedItems = this.defCheckedListBox.CheckedIndices;
// Check that we have at least 1 item selected
if (selectedItems.Count > 0)
{
// Uncheck the other item
this.defCheckedListBox.SetItemChecked(selectedItems[0], false);
}
Here is what I have used to prevent the last checked box to be "unchecked"
if (laborLevelDefCheckedListBox.CheckedItems.Count == 1)
{
if (e.CurrentValue == CheckState.Checked)
{
e.NewValue = CheckState.Checked;
}
}
I know this has got to be simple but I think because I've had a long week and I have looked at this too long it is just not coming to me. Any help with this is super appreciated! If I solve this over the weekend I will be sure to post my solution. BTW Happy Holidays to those here in the States :)
Chris makes a good point in the comments that this feels like you are re-inventing radio buttons but you are almost there with the code you have posted if you really want it to work with a CheckedListBox. I have adapted the code from your 1st Snippet which I think does the trick:
//remove the event handler so when we change the state of other items the event
//isn't fired again.
defCheckedListBox.ItemCheck -= defCheckedListBox_ItemCheck;
if (e.NewValue == CheckState.Checked)
{
// Uncheck the other items
for (int i = 0; i < defCheckedListBox.Items.Count; i++)
{
if (e.Index != i)
{
this.defCheckedListBox.SetItemChecked(i, false);
}
}
}
else
{
//the state was not checked.
//as only one item can ever be Checked inside the event
//handler the state of not checked is invalid for us; set the state back to Checked.
e.NewValue = CheckState.Checked;
}
//re-add the event handler
defCheckedListBox.ItemCheck += defCheckedListBox_ItemCheck;
Essentially the only new parts are the else where we reset the state if the state was not Checked and the removing and re-adding of the event to prevent it firing again when we manually set the state of other items (this could be handled with a global bool if you prefer).
// Use CheckBoxList Event
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) {
// Stops DeInitialize Event
checkedListBox1.ItemCheck -= checkedListBox1_ItemCheck;
// Get the new value old value my be in Indeterminate State
checkedListBox1.SetItemCheckState(e.Index, e.NewValue);
// Start ReInitialize Event
checkedListBox1.ItemCheck += checkedListBox1_ItemCheck;
}

Search listview items using textbox

I am working on a simple application (phonebook) in C# and in my project I have got a listview filled with contacts. I have been trying to implement the possibility to automatically (instantly) search through a listview using a textbox. I have managed to make it work, but not in the desired way. You will realise the actual problem if I give you an example. Let's say that I have got a contact named Bill Gates and when I try searching for it - it gets found and that part is OK. But, the problem is when I try to search for another contact. In that case, I have to clear the textbox before I type another name, but it is possible to remove only letter by letter. When I start removing the whole name, after removing a first letter it acts like I have just entered the name - it selects the item (and focuses as well) - actually there is no time to remove the whole name before it finds a contact again. I have to remove a first letter, then switch back to the textbox, remove another letter etc. Is there any solution for searching to be automatic - as it is now, but on the other hand for removing (clearing the textbox) without selecting contacts to be possible.
Take a look at the code:
private void txt_Search_TextChanged(object sender, System.EventArgs e)
{
if (txt_Search.Text != "")
{
foreach (ListViewItem item in listView1.Items)
{
if (item.Text.ToLower().Contains(txt_Search.Text.ToLower()))
{
item.Selected = true;
}
else
{
listView1.Items.Remove(item);
}
}
if (listView1.SelectedItems.Count == 1)
{
listView1.Focus();
}
}
else
{
LoadContacts();
RefreshAll();
}
}
There are some things wrong in your code, firstly when modifying a collection in a loop through it, we should not use foreach although in some case it seems to work but not really, it will surely be strange in future and confuse you. We should use a for loop instead and loop in the reverse order. The second wrong thing is you set the Selected to true which may cause your textBox lose focus to the listView. The solution is we have to use some other way to indicate that the item is selected, such as by using BackColor instead:
private void txt_Search_TextChanged(object sender, System.EventArgs e)
{
if (txt_Search.Text != "") {
for(int i = listView1.Items.Count - 1; i >= 0; i--) {
var item = listView1.Items[i];
if (item.Text.ToLower().Contains(txt_Search.Text.ToLower())) {
item.BackColor = SystemColors.Highlight;
item.ForeColor = SystemColors.HighlightText;
}
else {
listView1.Items.Remove(item);
}
}
if (listView1.SelectedItems.Count == 1) {
listView1.Focus();
}
}
else
LoadContacts();
RefreshAll();
}
}
Also after user focusing the ListView, all the BackColor and ForeColor should be reset, we can handle the Enter event of ListView:
//Enter event handler for listView1
private void listView1_Enter(object sender, EventArgs e){
foreach(ListViewItem item in listView1.Items){
item.BackColor = SystemColors.Window;
item.ForeColor = SystemColors.WindowText;
}
}
EDIT
you better not use Text_Changed, rather try Key_Down method as follows
private void txt_Search_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) //apply your search only when pressing ENTER key
{
// you do your search as it was before
// i personally don't have suggestions here
if (!txt_Search.AutoCompleteCustomSource.Contains(txt_Search.Text)) txt_Search.AutoCompleteCustomSource.Add(txt_Search.Text);
//the line above will save all your searched contacts and display it in a beautiful format
}
else if (txt_Search.Text == "")
{
LoadContacts();
RefreshAll();
}
}
Of course don't forget to set the properties of txt_Search
AutoCompleteMode = SuggestAppend and AutoCompleteSource = CustomSource
This kind of feels like a hack, but you could track the length of text that has been typed into the textbox, and only perform your searching and focus logic if the length of text is greater than the previous time the event was called. That way if someone deletes a letter, the searching and focusing won't occur. Something like:
// declare lastSearchLength as a int outside of your TextChanged delegate
if (!String.IsNullOrEmpty(txt_Search.Text) && txt_Search.Text.Length > lastSearchLength)
{
foreach (ListViewItem item in listView1.Items)
{
if (item.Text.ToLower().Contains(txt_Search.Text.ToLower()))
{
item.Selected = true;
}
else
{
listView1.Items.Remove(item);
}
}
if (listView1.SelectedItems.Count == 1)
{
listView1.Focus();
}
lastSearchLength = txt_Search.Text.Length;
}
else
{
LoadContacts();
RefreshAll();
}
}
You are doing a postback with every key press. When the page reloads, it will not retain focus where you would expect. I recommend implementing this in JavaScript on the client, or using a search button instead of TextChanged event.

How can I check for a selectedIndex among dozens of list boxes in order to edit/delete?

I have dozens of list boxes on my form. I want to be able to edit/delete items from those list boxes using only one edit button and only one delete button. Should a loop be created so I don't have to code an if statement for each list box? Maybe a custom method? Kind of lost here.
Thanks for the input.
This is the code for one of the list boxes to for edit:
private void btnEdit_Click(object sender, EventArgs e)
{
//Edit an item in the list box
//If there are no appointments OR no appointment is selected, inform the user and cancel the operation
if ((!(appointmentList.Count > 0)) || lstDayView.SelectedIndex == -1)
{
MessageBox.Show("Error! You need to select an appointment!");
return;
}
else
{
int index = lstDayView.SelectedIndex;
var myForm = new Form2(appointmentList[index] as Appointment);
if (myForm.ShowDialog() == DialogResult.OK && myForm.Tag is Appointment)
{
Appointment appoint = myForm.Tag as Appointment;
//lstDayView.Items.RemoveAt(index);
appointmentList.RemoveAt(index);
appointmentList.Insert(index, appoint);
//appoint.toListBox(lstDayView, index);
this.setCal();
}
}
And this is for delete:
private void btnDeleteApp_Click_1(object sender, EventArgs e)
{
if ((!(appointmentList.Count > 0)) || lstDayView.SelectedIndex == -1)
{
MessageBox.Show("Error! You need to make and/or select an appointment!");
return;
}
else
{
if (MessageBox.Show("Are you sure you wish to delete?", "Confirm Delete", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
appointmentList.RemoveAt(lstDayView.SelectedIndex); //Issue this is removed the index number from the list not the appointmentList
this.setCal();
}
}
As ThunderGr suggests, you can attach the same event handler to all Listboxes so that the same logic can be applied. When it comes to iterating (both to assign the event handlers initially, and to subsequently work on the collection as a whole), I would set the Tag property of each control to something relavent (e.g. "List"), and then you can do:
foreach(var control in Controls.Where(c => c.Tag == "List"))
{
// work with control here (casting to ListBox appropriately).
}
You could even make this a method of your form that returns IEnumerable<ListBox> like this:
public IEnumerable<ListBox> ListBoxes
{
get
{
return Controls.Where(c => c.Tag == "List").Cast<ListBox>();
}
}
You are removing another ListItem (lstDayView) from appointmentList List.
Replace This :
appointmentList.RemoveAt(lstDayView.SelectedIndex);
With following :
appointmentList.RemoveAt(appointmentList.SelectedIndex);
You can use the Form.Controls collection in a foreach loop to check for and manipulate the listboxes.
Another thing you can do is to assign the same selectedindexchanged event to all the listboxes you want to manipulate. The "sender" parameter holds the object that caused the event to fire. You can cast it to a listbox and manipulate it.
EDIT:Your basic problem is that you do not know which listbox was last selected. You need to define a variable that will keep the last listbox entered by the user(using the Enter event) and use that variable to delete the item from the listbox.
Example:
ListBox lastEnteredListbox = null;
private void aListboxName_Enter(object sender, EventArgs e)
{
lastEnteredListbox=(ListBox)sender;
}
private void theButton_Click(object sender, EventArgs e)
{
if(lastEnteredListbox == null || lastEnteredListbox.SelectedIndex == -1)
{
MessageBox.Show("You need to select an Item");
return;
}
lastEnteredListbox.RemoveAt(lastEnteredListbox.SelectedIndex);
}
You need to set the Enter event of all listboxes you wish to manipulate to the aListBoxName_Enter method.
In case that you want to simply delete the same index from all listboxes, you just loop through the controls collection of the form and do an if(control is ListBox) ((ListBox)control).RemoveAt(index);

Categories