I have a method that adds items to my listbox called refreshInterface which is called as soon as the programe starts, adding names of homeforms in the listbox using the FormItems class, here is the rereshInterface method below
public void refreshInterface()
{
//int number = 0;
foreach (DataSet1.xspGetAnalysisUsageTypesRow homeForms in myDataSet.xspGetAnalysisUsageTypes)
{
var forms = new FormItems(homeForms);
listBox1.Items.Add(forms);
}
}
The FormItems class is this below
public class FormItems
{
public DataSet1.xspGetAnalysisUsageTypesRow types { get; set; }
public FormItems(DataSet1.xspGetAnalysisUsageTypesRow usageTypes)
{
types = usageTypes;
}
public override string ToString()
{
// returns the rows that are relating to types.xlib_ID
var libtyps = types.GetxAnalysisUsageRows();
var cnt = 0;
foreach (DataSet1.xAnalysisUsageRow ty in libtyps)
{
//returns true if ty is null
bool typeNull = ty.Isxanu_DefaultNull();
// if its false, if xanu_Default is set
if (!typeNull)
{
cnt += 1;
}
}
var ret = String.Format("set {0} [Set: {1}]", types.xlib_Desc, cnt);
//return this.types.xlib_Desc;
return ret;
}
}
Each listbox (the listbox is on the left of the homeform) item has a number of reports that can be added to it, so for instance, i select an homeform from my listbox, there are 12 textboxes on the right hand side and each textbox has a pair of buttons which are Browse and Clear. If I click on the browse button a new form appears, and i select a report from that form and add it to a particular textbox, the count for that homeform should update, and i clear a textbox for a particular homeform, the count should also update.
At the moment when i debug the application, it shows me the count of each Homeform depending on the amount of reports added to the homeform, but while the programe is running, if i add a new report to a homeform, the count does not update until i restart the debug session. I was told about using a Databinding method but not sure of how i could use it here
How do i ge my listbox item to update ?
You should probably look into binding. Here is a good place to start:
http://www.codeproject.com/Articles/140621/WPF-Tutorial-Concept-Binding
If you want a GUI to respond to data changes then binding is your best friend.
You should bind List Box component source to Observable Collection, every update you do to Observable Collection will update List Box data.
Might not be exact but should give you an idea.
public void refreshInterface()
{
Dictionary<int,string> items = new Dictionary<int,string>();
//int number = 0;
foreach (DataSet1.xspGetAnalysisUsageTypesRow homeForms in myDataSet.xspGetAnalysisUsageTypes)
{
var formitem = new FormItems(homeForms);
items.Add(formitem.someprop, formitem.toString());
}
listbox.DataSource = items;
listbox.DisplayMember = "Value";
listbox.ValueMember = "Key";
}
Related
I have a WinForms Dialog with 2 ListBox controls. In the application under test, doubleClicking any items in one of the listbox controls (I'll call this the CONTROL LISTBOX) results in selecting the matching item in the other listbox (SLAVE LISTBOX).
My test causes multiple entries to be made in the CONTROL LISTBOX. The test then performs a ListBox.SelectedItem.DoubleClick() on each of the CONTROL lISTBOX items, comparing the ListBox.SelectedItemText from both listbox controls.
In the application UI, this ALWAYS works, but the test of the call to ListBox.SelectedItemText for SLAVE LISTBOX returns the text matching what is slected in the UI correctly ONLY on the initial iteration of the doubleclick\compare.
Can anybody help me figure out what I'm doing wrong? Thanks!
Here is my code:
public bool SelectMainEventViaErrorEvent(int eventIdx)
{
bool bSuccess = false;
errorEvents.Items.Select(eventIdx);
System.Threading.Thread.Sleep(1000);
errorEvents.Items.SelectedItem.DoubleClick();
System.Threading.Thread.Sleep(1000);
if (eventIdx > 0)
{
IVScrollBar vertScroll = mainEvents.ScrollBars.Vertical;
vertScroll.ScrollDownLarge();
}
if (errorEvents.SelectedItemText == mainEvents.SelectedItemText)
{
bSuccess = true;
}
log.Info($"SelectMainEventViaErrorEvent({eventIdx}) selected error event = {errorEvents.SelectedItemText}");
log.Info($"SelectMainEventViaErrorEvent({eventIdx}) selected main event = {mainEvents.SelectedItemText}");
return bSuccess;
}
As you can see, by the following image, the text in both list boxes are identical. However, the call to ListBox.SelectedItemText for the top listbox (SLAVE LISTBOX) returns the value from the first iteration, which matched the first item in the bottom listbox (CONTROL LISTBOX) during the first iteration of the doubleclick/compare.
Proof that the text of the selected listbox items match
Comparing with plain text is bad idea since "Text" != "Text ". What you could use in yor case is DisplayMember and ValueMember properties.
I will demonstrate it for you with manually populating listboxes but you do it from database or however you do it.
First of all create class that will store your values and it's ID's. I usually create it like this (so i am able to use that class later for something else)
public class Int_String
{
public int _int { get; set; } // Important to be declared like properties and not like variables
public string _string { get; set; }
}
Now let's populate our listbox like this:
public YourForm()
{
List<Int_String> list = new List<Int_String>();
list.Add(new Int_String { _int = 1, _string = "Some text" }); // I am populating it manually but you will populate it from DB or somewhere else
list.Add(new Int_String { _int = 2, _string = "Some other text" });
list.Add(new Int_String { _int = 3, _string = "One more text" });
// Now when we have list we need to bind it to our listbox.
// IMPORTANT!!!!!
// Display member and Value member properties SHOULD be able to be added before and after assigning datasource to control (like combobox) BUT for some reason on listbox it only works when you assign it AFTER you bind your datasource to listbox.
// If you ever work with other controls and use these values, ALWAYS declare display member and value member BEFORE you bind datasource. Why? For now let's just say it is much faster but explanation is for other question
myListBox1.DataSource = list;
myListBox1.DisplayMember = "_string"; // When you start typing .DisplayMember, you will not get that property in recommendation since it is hidden so do not think there is not that property there.
myListBox1.ValueMember = "_int"; // Same as above
}
Now when you populate listbox like this and second one on same way with same id's you could simply do if(listbox1.SelectedValue == listbox2.SelectedValue) and compare them even if their text is not equal but id is.
BONUS:
Also what you can do is expand class like this:
public class Int_String
{
public int _int { get; set; }
public string _string { get; set; }
public string SomethingOther = "AsD";
public bool IsTrue()
{
return true;
}
}
then bind it on same way and do this:
Int_String item = listbox1.SelectedItem as Int_String;
bool check = item.IsTrue();
MessageBox.Show(item.SomethingOther);
So basically you bind whole class for each item in listbox, display to user one of the variables (in our case _string), set ValueMember to other unique variable so it is easy to search whole listbox and when needed get whole class from that item.
I wasn't ever able to get this working by iterating forward through the errorEvent listbox in my automated test code, but it DOES work when iterating backwards through the errorEvent listbox.
Calling code:
for (int i = eventViewer.GetErrorEventsCount() - 1; i >= 0; i--)
{
bResult = eventViewer.SelectMainEventViaErrorEvent(i);
if (!bResult)
{
break;
}
System.Threading.Thread.Sleep(2000);
}
Verification Code:
public bool SelectMainEventViaErrorEvent(int eventIdx)
{
bool bSuccess = false;
DisableToolTips(true);
errorEvents.Select(eventIdx);
errorEvents.SelectedItem.DoubleClick();
System.Threading.Thread.Sleep(1000);
log.Info($"SelectMainEventViaErrorEvent({eventIdx}) selected error event = {errorEvents.SelectedItemText}");
log.Info($"SelectMainEventViaErrorEvent({eventIdx}) selected main event = {mainEvents.SelectedItemText}");
if (errorEvents.SelectedItemText == mainEvents.SelectedItemText)
{
bSuccess = true;
}
return bSuccess;
}
public Core()
{
InitializeComponent();
Archive.IntializeObjects();
Data.Load();
adultBox.DataSource = display;
}
void UpdateUI()
{
display.Clear();
foreach (Lesson l in lessons)
{
display.Add(l);
}
MessageBox.Show(lessons.Count.ToString());
adultBox.DisplayMember = "Title";
}
This method updates a separate list to filter from a larger list and then post it to a ListBox. Though the lessons list updates properly and contains multiple objects, only one item is ever shown in the box.
What am I missing?
Use this:
display.Items.Add(l);
or
display.Items.AddRange(l.ToArray());
I have a BindingList< KeyValuePair < string, string > > that is bound to a ComboBox control. Based on some conditions, the BindingList will be added a new KeyValuePair. Now, the Newly added item shows up at index 0 of the Combobox, instead of at the end.
While debugging, I found that the BindingList has got the right order. (i.e, the new KeyValuePair is appended)
Also, I check the SelectedValue of the ComboBox in it's SelectedIndexChanged handler and it seems to be not of the ListItem that got selected. Instead, it is that of the supposed ListItem, if the ComboBox had got the right order as in its DataSource, - the BindingList..
The code is a small part of a large project.. Plz let me know if the question is not clear. I can put the relevant parts of the code as per our context.
How could something like this happen? What can I do differently?
I have this class something like this.
public class DropdownEntity
{
//removed all except one members and properties
private string frontEndName
public string FrontEndName
{
get {return this.frontEndName; }
set {this.frontEndName= value; }
}
//One Constructor
public DropdownEntity(string _frontEndName)
{
this.FrontEndName = _frontEndName;
//Removed code which initializes several members...
}
//All methods removed..
public override string ToString()
{
return frontEndName;
}
}
In my windows form, I have a tab control with several tabs. In one of the tabs pages, I have a DataGridView. The user is supposed to edit the cells and click on a Next - button. Then, some processing will be done, and the TabControl will be navigated to the next tab page.
The next tab page has the combobox that has the problem I mentioned. This page also has a back button, which will take back.. the user can modify the gridview cells again.. and click on the next button. This is when the order gets messed up.
I am posting here the Click event handler of the Next Button.. Along with the class, with the rest of the code removed.
public partial class AddUpdateWizard : Form
{
//Removed all members..
BindingList<KeyValuePair<string, string>> DropdownsCollection;
Dictionary<string, DropdownEntity> DropdownsDict;
//Defined in a partial definition of the class..
DataGridView SPInsertGridView = new DataGridView();
ComboBox DropdownsCmbBox = new ComboBox();
Button NextBtn2 = new Button();
Button BackBtn3 = new Button();
//Of course these controls are added to one of the panels
public AddUpdateWizard(MainForm mainForm)
{
InitializeComponent();
DropdownsDict = new Dictionary<string, DropdownEntity>();
}
private void NextBtn2_Click(object sender, EventArgs e)
{
string sqlArgName;
string frontEndName;
string fieldType;
for (int i = 0; i < SPInsertGridView.Rows.Count; i++)
{
sqlArgName = "";
frontEndName = "";
fieldType = "";
sqlArgName = SPInsertGridView.Rows[i].Cells["InsertArgName"].Value.ToString().Trim();
if (SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value != null)
{
frontEndName = SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value.ToString().Trim();
}
if (SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value != null)
{
fieldType = SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value.ToString().Trim();
}
//I could have used an enum here, but this is better.. for many reasons.
if (fieldType == "DROPDOWN")
{
if (!DropdownsDict.ContainsKey(sqlArgName))
DropdownsDict.Add(sqlArgName, new DropdownEntity(frontEndName));
else
DropdownsDict[sqlArgName].FrontEndName = frontEndName;
}
else
{
if (fieldType == "NONE")
nonFieldCount++;
if (DropdownsDict.ContainsKey(sqlArgName))
{
DropdownsDict.Remove(sqlArgName);
}
}
}
//DropdownsCollection is a BindingList<KeyValuePair<string, string>>.
//key in the BindingList KeyValuePair will be that of the dictionary.
//The value will be from the ToString() function of the object in the Dictionary.
DropdownsCollection = new BindingList<KeyValuePair<string,string>>(DropdownsDict.Select(kvp => new KeyValuePair<string, string>(kvp.Key, kvp.Value.ToString())).ToList());
DropdownsCmbBox.DataSource = DropdownsCollection;
DropdownsCmbBox.DisplayMember = "Value";
DropdownsCmbBox.ValueMember = "Key";
//Go to the next tab
hiddenVirtualTabs1.SelectedIndex++;
}
private void BackBtn3_Click(object sender, EventArgs e)
{
hiddenVirtualTabs1.SelectedIndex--;
}
//On Selected Index Changed of the mentioned Combobox..
private void DropdownsCmbBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (DropdownsCmbBox.SelectedValue != null)
{
if (DropdownsDict.ContainsKey((DropdownsCmbBox.SelectedValue.ToString())))
{
var dropdownEntity = DropdownsDict[DropdownsCmbBox.SelectedValue.ToString()];
DropdownEntityGB.Text = "Populate Dropdowns - " + dropdownEntity.ToString();
//Rest of the code here..
//I see that the Datasource of this ComboBox has got the items in the right order.
// The Combobox's SelectedValue is not that of the selected item. Very Strange behavior!!
}
}
}
}
The very first time the user clicks the Next Button, it's fine. But if he clicks the Back Button again and changes the Data Grid View cells.. The order will be gone.
I know, it can be frustrating to look at. It's a huge thing to ask for help. Any help would be greatly appreciated!
Please let me know if you need elaboration at any part.
Thanks a lot :)
I think you have two problems here.
First, if you want to retain the order of the items you should use an OrderedDictionary instead of a regular one. A normal collection will not retain the order of the items when you use Remove method. You can see more info about this related to List here.
You could use such dictionary like this:
DropDownDict = new OrderedDictionary();
// Add method will work as expected (as you have it now)
// Below you have to cast it before using Select
DropDownCollection = new BindingList<KeyValuePair<string, string>>(DropDownDict.Cast<DictionaryEntry>().Select(kvp => new KeyValuePair<string, string>(kvp.Key.ToString(), kvp.Value.ToString())).ToList());
The second problem could be that you change the display name (FrontEndName) of already existing items, but the key is preserved. When you add a new item, try to remove the old one that you're not using anymore and add a new item.
The Sorted Property of the Combobox is set to True! I didn't check that until now. I messed up. Terribly sorry for wasting your time Adrian. Thanks a lot for putting up with my mess here.. :)
I have a DataBound CheckedListBox, I "check" few items on list box(source), then I need to clone it to new Checked List Box(target). It need to have all the data, with checked state. I have tried with following function. It is properly flowing through this function.
But finally I can see items on target CheckedListBox but none of the items in target is checked.
private void CloneCheckedListBox(CheckedListBox source, CheckedListBox target)
{
foreach (int checkedItemIndex in source.CheckedIndices)
{
target.SetItemChecked(checkedItemIndex, true);
}
}
Edit:
I have a User control which I have placed on a TabPage, on that User Control there is a "CheckedListBox", I do need to create a new TabPage with the user entered value on selected(current) TabPage(on User Control)
So, what I have done is, create a new Tab Page, get a Copy of the User Control calling it's "Clone()" method.
In "Clone()" method need to have CheckedListBox cloning feature.
Here is my Cloning Code, which is on User Control...
public SearchMain Clone()
{
SearchMain smClone = new SearchMain();
smClone.txtManufacturers.Text = this.txtManufacturers.Text;
smClone.udPriceFrom.Value = this.udPriceFrom.Value;
smClone.udPriceTo.Value = this.udPriceTo.Value;
smClone.chkOld.Checked = this.chkOld.Checked;
smClone.chkPrx.Checked = this.chkPrx.Checked;
smClone.chkDisc.Checked = this.chkDisc.Checked;
smClone.chkStock.Checked = this.chkStock.Checked;
smClone.chkFirstDes.Checked = this.chkFirstDes.Checked;
smClone.chkFirstPN.Checked = this.chkFirstPN.Checked;
smClone.txtSuppPN.Text = this.txtSuppPN.Text;
smClone.txtManuPN.Text = this.txtManuPN.Text;
smClone.txtManufacturers.Text = this.txtManufacturers.Text;
smClone.meDesAND.Text = this.meDesAND.Text;
smClone.meDesOR.Text = this.meDesOR.Text;
smClone.meDesNOT.Text = this.meDesNOT.Text;
smClone.lbManufacSelected.Items.AddRange(this.lbManufacSelected.Items);
smClone.lbSearchWithIn.Items.AddRange(this.lbSearchWithIn.Items);
**CloneCheckedListBox(this.clbLang, smClone.clbLang);**
// CloneCheckedListBox(this.clbTypes, smClone.clbTypes);
return smClone;
}
You can see correct answere here..
Programatically Checking DataBound CheckListBox
try set
source.DataSource = target.DataSource;
target.DisplayMember = "YourDisplayItem";
target.ValueMember = "YourValueItem";
foreach (int checkedItemIndex in source.CheckedIndices)
{
target.SetItemChecked(checkedItemIndex, true);
}
I have a datagridview which we will call dataGridViewExample.
My object (the uncommon datatypes is because my database is SQLite):
class MyObject
{
public Int64 Vnr { get; set; }
public string Name { get; set; }
public Single Price { get; set; }
public int Amount { get; set; }
}
Here is the relevant code:
//This form gets called with a .ShowDialog(); in my form1.
private List<MyObjecte> ExampleList = new List<MyObject>();
public MyForm()
{
dataGridViewExample.DataSource = OrdreInkøbsListe;
}
private void AddtoDataGridViewExample()
{
//Add a new MyObject to the list
ExampleList.Add(new myObject()
{
Vnr = newVnr,
Amount = newAmount,
Price = newPrice,
Name = newName
});
//refresh datasource
dataGridViewExample.DataSource = null;
dataGridViewExample.Refresh();
dataGridViewExample.DataSource = OrdreInkøbsListe;
ddataGridViewExample.Refresh();
}
When MyForm gets called with a .ShowDialog, it shows up fine and displays my DataGridView example just fine. As you can read from the code, the ExampleListis initially empty, so it just shows an empty datagridview with 4 columns: Vnr, Name, Price & Amount. If I click inside it etc. nothing happens - so everything is working as planned, so far.
Everytime I call AddtoDataGridViewExample() it adds the new object to the Datagridview, and the datagridview does update, listing all the objects added so far (they show themself as rows, again according to plan).
Now, remember that I just said that nothing happened if you clicked inside DataGridViewExample before I have called AddtoDataGridViewExample()?
Well, after having called AddtoDataGridViewExample() once or more, the program will crash if I click inside DataGridViewExample (for example: the users wants to select a row). It throws an IndexOutOfRangeException and talks about an -1 index.
It also throws the exception in the other form, on the line where I call MyForm with .ShowDialog();
I really am stuck on this, do you guys have any idea what is wrong??
My only clue is that I do believe the refresh of DataGridViewExample's datasource might be the cause of the problem.
Another important note: I have yet bound any events to my DataGridViewExample. So you can rule that idea out.
Here is all DataGridViewExample's properties:
this.dataGridViewExample.AllowUserToAddRows = false;
this.dataGridViewExample.AllowUserToDeleteRows = false;
this.dataGridViewExample.AllowUserToResizeColumns = false;
this.dataGridViewExample.AllowUserToResizeRows = false;
this.dataGridViewExample.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dataGridViewExample.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridViewExample.Location = new System.Drawing.Point(591, 53);
this.dataGridViewExample.MultiSelect = false;
this.dataGridViewExample.Name = "dataGridViewExample";
this.dataGridViewExample.ReadOnly = true;
this.dataGridViewExample.RowHeadersVisible = false;
this.dataGridViewExample.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridViewExample.ShowEditingIcon = false;
this.dataGridViewExample.Size = new System.Drawing.Size(240, 150);
this.dataGridViewExample.TabIndex = 31;
I guess the click event tries to get the currently selected row and do something with it, while dataGridViewExample.DataSource = null; clears the datasource, and the currently selected row becomes null.
If you set the DataGridView.DataSource to the list, you don't need to reset it to null, refresh, and reset it to the list again (and refresh again) to see the changes. It will be enough to just refresh the DataGridView.
You can also just try using an BindingList<T> object instead of a List<T>, which will automatically notify your grid of its internal changes (Adding and removing elements), and there's also an INotifyPropertyChanged interface you can implement on your MyObject class, that will make every property change in an object show on the grid (For any changes made to the object in the code, and not through the grid itself).
Have you tried running the debugger and break when InedxOutOfRangeException is thrown to see where the exception is thrown?
Select Debug > Exceptions then there's a Find button on the dialog so you don't have to browse through all of the possibilities.
I had similar situation. I assigned generic list of certain object to DataGridView. Then I was setting null to DataSource and after that refresh. After that I assign list of objects to DataSource. While clicked on grid while runtime error occured IndexOutOfRange. My solution was to assign new empty list of my object to that grid and refresh and after changes on my working list I do assign to DataSource and call Refresh. Now, it is working without any crashes. Please look on my code before:
grid.DataSource = null;
grid.Refresh();
if(cases.Count() > 0)
{
grid.DataSource = cases;
grid.Refresh();
}
And now on my code after:
grid.DataSource = new List<MyCase>();
grid.Refresh();
//do something with cases
if(cases.Count() > 0)
{
grid.DataSource = cases;
grid.Refresh();
}