Foreach control item in panel not selecting all items - c#

My problem is that i've got a panel called PanelNewFriend, where i'm dynamically creating buttons from all users from a mysql database. So when someone clicks on a user and sends an invite , what should be happening is that it should remove all buttons (Except for the back button which has a tag of "1") and then place all the buttons again for the new list of all users.
When creating the buttons i assigned a tag with value "0" to them. All buttons have it even though i don't think the problem lies in the tag.
I'm using this code to remove all buttons in the panel.
foreach (Button item in PanelNewFriend.Controls.OfType<Button>())
{
if (item.Tag == "0")
{
PanelNewFriend.Controls.Remove(item);
}
}
I've tried multiple things, ranging from foreach Control item and oftype Control. To changing the item.Tag != "1" and item.Tag == "". None of this worked
But when i actually execute the code and add breaking points I can see that there are 4 buttons in the panel. But when i go through each foreach cycle, it only selects 2 of the 4 buttons. So since it only selects 2 buttons it is only deleting 2 and leaving the other 2 alone.
Any idea what could be causing this and how to fix it?

Classic collection modification problem here. You're modifying the collection as you're iterating over it. That will result in unexpected behavior. Ideally you should be getting InvalidOperationException but sadly ControlCollection doesn't implement this check.
You have two options. Either take a copy of the collection and iterate over the copy or use a reverse for loop.
var buttonsToRemove = PanelNewFriend.Controls
.OfType<Button>()
.Where(x=> x.Tag == "0")
.ToArray();//Take a copy
foreach (Button item in buttonsToRemove)
{
PanelNewFriend.Controls.Remove(item);
}

Try like this
List<Button> removeList = new List<Button>();
foreach (Button item in PanelNewFriend.Controls.OfType<Button>())
{
if (item.Tag == "0")
{
removeList.Add(item);
}
}
foreach (Button item in removeList)
PanelNewFriend.Controls.Remove(item);

Related

How do I change the properties of a control within a list dynamically (C# Winform)?

I have a list of checkbox controls
public List<CheckBox> _checkBoxes = new List<CheckBox>();
I add a certain amount during runtime using a switch case with the properties such as location and visibility.
This all works fine, but if I want the checkboxes to disappear or become checked at some point the GUI doesn't get updated, the list is updated with the new data but just not visually.
* I ended up doing this*
I created a bunch of checkboxes on the winfrom.Created a method which you can choose which ones are visible. Then created a method to fill a list of type checkbox. Then you can search the panel for control types, once you found the control, it can be manipulated. This isn't elegant and probably isn't the best way, but i am new to custom controls and winforms.
//search panel for checkboxes
foreach (Control c in panel1.Controls)
{
if (c is CustomControls.RoundedCheckBox)
{
CustomControls.RoundedCheckBox checkBox = c as CustomControls.RoundedCheckBox;
//if it is checked add to list
if(checkBox.Checked)
{
_checkBoxes.Add(checkBox);
}
}
}
If you have something like this:
_checkBoxes.Add(new CheckBox());
_checkBoxes[0].Parent = this;
then you should be able to manipulate your checkboxes from the list:
_checkBoxes[0].Checked = false;
But, the problem may occur if you do it in some kind of loop and want to see the results immediately.
In Windows there is something called message loop. Application simply works like that (pseudocode: TL;DR)
while(true)
{
message = GetFirstMessage();
if(message != null)
{
if(message.Id == CloseApplication)
break;
DispatchMessage(message);
RemoveFirstMessage();
}
}
So, application takes message from queue, then process it. A message is everything - button click, mouse move, paint... Everything.
So when a message is dispatched it looks for the control that should receive this message and then it does some work.
So, for example if you have something like that:
foreach(var ch in _checkBoxes)
{
ch.Checked = false;
DoSomeWorkThatTakesTime();
ch.Checked = true;
}
You won't see the change, because you are "trapped" in DispatchMessage. When you set Checked, you really sending a message. But this message cannot be Dispatched right now, because you are inside the foreach loop.
So the only thing you can do here is to tell your application - now, please DO READ message queue. In WinForms it's called "DoEvents", so this will do the work:
foreach(var ch in _checkBoxes)
{
ch.Checked = false;
Application.DoEvents();
DoSomeWorkThatTakesTime();
ch.Checked = true;
Application.DoEvents();
}

UWP - Listbox scrolls to top after navigating within a frame on the same page

OK, tricky one to explain, but its driving me crazy... Its a UWP App on Windows 10:
I have a main page of which a large part is a Frame (where the main content pages are displayed) on the right is a user control which has a list box in it - when selecting an item in the list it loads the main content page into the frame using a user control event which then calls the Navigate method on the frame - all works fine, except... if you have scrolled down the list then click on an item, the page loads but the listbox scrolls to the top of list - really frustrating!! I can't see why it does this or understand what is going on - can anyone shed some light please?
I know its not reloading the contents and the selecteditem remains selected and does not change.
I'm not familiar with Unity, but after some research in your project, I think that each time you select one Item, you reload all your items in ListBox. For example you can take a look at your UserControl named "PersonPicker":
private void cbCategory_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (isLoaded)
people.AddFilterAndOrder("Person Category," + ((ViewModel.SystemConfiguration.SystemData.WorkCategory)cbCategory.SelectedItem).PluralTitle, loadModel: true);
}
Then I found your AddFilterAndOrder method in BaseListVM:
public void AddFilterAndOrder(string filter = "", string order = "", bool loadModel = false)
{
if (filter != "")
{
string[] items = filter.Split(';');
foreach (string i in items)
{
string[] pair = i.Split(',');
if (pair[1] == "")
filters.Remove(pair[0]);
else
if (filters.Keys.Contains(pair[0]))
filters[pair[0]] = pair[1];
else
filters.Add(pair[0], pair[1]);
}
}
if (order != "")
{
string[] items = order.Split(';');
foreach (string i in items)
{
string[] pair = i.Split(',');
if (pair[1] == "")
orders.Remove(pair[0]);
else
if (orders.Keys.Contains(pair[0]))
orders[pair[0]] = pair[1];
else
orders.Add(pair[0], pair[1]);
}
}
if (loadModel) LoadModel();
}
Since you passed "loadModel" as true to this method, LoadModel() method will be executed, I won't paste your LoadModel() method here again, but in your LoadModel method, you clear the Items and reload Items again. This is why I said you probably have refreshed your list.
So, maybe you can try:
people.AddFilterAndOrder("Person Category," + ((ViewModel.SystemConfiguration.SystemData.WorkCategory)cbCategory.SelectedItem).PluralTitle, loadModel: false);
when one Item is selected.

Looping throughTextBoxes in a Panel

I have a panel that contains 5 textboxes, I am trying to loop through the Panel and insert the value of the textbox into the database if it is not 0.
my panel name is Panel1
I honestly do not know from where to start or how to loop in a Panel that contains textfields, any suggestions are appreciated:
Here is my try which it does not compile (I am not sure how to write a loop that loops through a panel)
const string query = "INSERT INTO deductible (number) VALUES (#yes)";
using (var command = new SqlCommand(query, conn))
{
foreach (Panel1 c in this.Controls)
{
if (c.ToString() != "0")
{
command.Parameters.AddWithValue("#yes", c.Text);
command.ExecuteNonQuery();
}
}
I also attached a screenshot of my Panel1.
Thank you.
The simple answer is
foreach(Control control in this.Controls) {
if (control is TextBox) {
// The code here ...
}
}
The problem though is that you then need to make sure that the order the textboxes are looped over is correct, which adds more maintenance work that is entirely unnecessary. A better approach would be to learn about data binding. Or even more simply, just name your textboxes and assign them directly. Either of those is preferable to using a loop I think.
One way is to iterate each control within your Panel that is a TextBox.
foreach (TextBox tb in Panel1.Controls.OfType<TextBox>())
{
if (tb.Text != "0")
{
}
}
You're trying to loop through items of type Panel1 in this.Controls. What you want to do is loop through items of type TextBox in Panel1.Controls.
foreach(Control c in Panel1.Controls) {
var textbox = Control As TextBox;
if(textbox != null){
// do stuff...
}
}
You also want to look at the Text property of the TextBox, not call ToString on it.
if(textbox.Text != "0"){ //do stuff... }
And you add a #yes parameter to the same command multiple times within the loop, without clearing out the parameters list. I'm not certain if that will work, but if it causes a problem, you should just be able to call command.Parameters.Clear to clear the old parameter before adding the new one.

How to find item by name from listbox

I have listbox with Buttons. Every button have specific name -> button.Name = "button1".
I want to find specific button in listbox by Name.
I tried something like this:
if (listBox.Items.Contains(new Button().Name = "button2"))
{
MessageBox.Show("TEST");
}
But it doesnt work.
How to find it?
You need to check: 1. If the item is a Button 2. If its name is the same (use == not = as in your code)
foreach(var i in listBox.Items)
{
if (i is Button && (i as Button).Name=="button2")
{
MessageBox.Show("TEST");
}
}
If you have your ItemsControl item with you then you can iterate its Visualtree to reach to your button using VisualTreeHelper
Recursive find child is explained in this post How can I find WPF controls by name or type?

How to disable only the checkboxes (and not the full checkedboxlist) of devexpress?

In non-edit mode, I need the user to be able to browse the list (scroll etc.) but not be able to select the checkboxes.
If I do checkedboxlist.enabled = false, then the whole list becomes disabled. Only I need to disable checkboxes so that user doesn't interact (in edit way) in non-edit mode.
EDIT
I just assign the list of strings to the checkedboxlist's datasource.
this.UserSelectedMsgTypes.DataSource = userSelectedMsgs;
this.UserAvailableMsgTypes.DataSource = availableMsgTypeList;
currently enabling/disabling whole list by doing
this.UserSelectedMsgTypes.Enabled = true/false;
this.UserAvailableMsgTypes.Enabled = true/false;
I tried #James solution earlier, doesnt work. Because somehow the 'ItemCount' is 0 even though there are items. in the datasource it shows that there are 6 items, but in list it shows 0.
Try this:
foreach (DevExpress.XtraEditors.Controls.CheckedListBoxItem item in checkedListBoxControl1.Items)
{
item.Enabled = false;
}
It's a bit of a dirty work around but how about this?
private IEnumerable<DevExpress.XtraEditors.Controls.CheckedListBoxItem> GetCheckItems(string[] myStringArray)
{
foreach(string s in myStringArray)
{
DevExpress.XtraEditors.Controls.CheckedListBoxItem item = new DevExpress.XtraEditors.Controls.CheckedListBoxItem();
item.Description = s;
yield return item;
}
}
Call with:
checkedListBoxControl1.Items.AddRange(GetCheckItems(new string[] {"test1","test2","test3"}).ToArray());
Then apply the first answer with the foreach loop (or set the ENabled = false in the GetCheckItems method).

Categories