How to populate combobox in base form - c#

I have a base form in where I populate a combox with the following code:
private void FillComboBoxItemsFromResources(ComboBox comboBox, bool firstIsEmpty, bool selectFirst)
{
var res = new Resources(System.Threading.Thread.CurrentThread.CurrentCulture);
var cbEntries = res.GetComboBoxItems(comboBox.Name);
if (firstIsEmpty)
{
cbEntries[0] = new KeyValuePair<int, string>(0, string.Empty);
}
comboBox.DataSource = cbEntries.ToArray();
comboBox.DisplayMember = "Value";
comboBox.ValueMember = "Key";
if (selectFirst && cbEntries.Count > 0)
{
comboBox.SelectedIndex = 0;
}
}
When I run the code and show the base form. -> All works.
When I inherit another form from my base form the code compiles but crashes with "Items-Collection cannot be modified when the data source is set" in Windows form designer generated code.
How to work around this ?
Any help is highly aprreciated.
TIA acki

Well after hours of trying around with rebuilding base form, rebuilding inherited form I just deleted the combobox in base form. Readded combobox into base form, uncommented code to fill combobox and now it works all as expected...

Related

How to display controls based on Combobox Selection in Windows Application at edit time?

Value type 1
I am using Windows application and created one form for Add and Edit mode. The issue is on Add it works fine, but on edit, controls are not displaying based on combobox selection. As per my combobox selection change event, I have hidden the controls. But my combobox is not selecting values and not triggering change event also. Code is :
//Edit Mode
public CompanyAddEdit(MainForm form, string id)
{
InitializeComponent();
passedForm = form;
var cmbList = BindCompanyType();
isEdit = true;
xmlDocPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Companies.xml");
xDocument = XDocument.Load(xmlDocPath);
Id = Convert.ToInt32(id);
XElement company = xDocument.Descendants("Company").FirstOrDefault(p => p.Element("Id").Value == Id.ToString());
if (company != null)
{
var type = company.Element("DataSourceType").Value;
cmbbx_companyType.SelectedItem = type;
}
}
I am binding Combobox using this method :
private Array BindCompanyType()
{
var companyTypeList = Enum.GetValues(typeof(CompanyType));
cmbbx_companyType.DataSource = companyTypeList;
return companyTypeList;
}
How can I fixed this?
Any help will be appreciated.
So you have populated the combobox with items of type CompanyType enum. so the selected item should also the same type. Hope that you are getting a string from company.Element("DataSourceType").Value; so you can modify the code like the following:
cmbbx_companyType.SelectedItem = Enum.Parse(typeof(CompanyType),type);
Please make a try and let me know whether it solve the issues or not.

Updating data from other form

I have tow form,ListFrom, and DetailForm
In the ListForm I have a devexpress grid and some button(add, delete, edit)
In the DetailForm I have some textboxes and some button(save,delete,next,previous)
well I have to senario
1 - I open the ListForm and I click on a product to modify it a got the DetailForm opened, I make some modification and I save,then i should have my grid in the ListForm refreshed with the new value.for this I have this code
In the ListFrom
FrmProduit frm = new FrmProduit(monProduit.Id) { MdiParent = this.MdiParent};
frm.updateDataInGridView += new System.Action(refereshGridView);
frm.Show();
in the detailform
if (updateDataInGridView != null)
updateDataInGridView();
well in this scenario everything is OK
second scenario
If I open the detailFrom,and after that I open the listForm, I make some change in the detailFrom and I click save updateDataInGridView in this case is null and then the grid is not refreshed
anyone have suggestion?
I would create a shared BindingSource that both forms would use to show data. If any item is changed in BindingSource it takes care to notify all controls bind to it and so it would refresh grid automatically.
Second approach is to make refereshGridView method public and in DetailForm on save click do this:
var lists = Application.OpenForms.OfType<Form>().Where(x => x.GetType() == typeof(ListFrom));
foreach (var listform in lists)
{
listform.refereshGridView();
}
I did not use FirstOrDefault as maybe there is more than one listform opened.
EDIT about Binding Source
Here is quite good tutorial so please take a look.
Below is a fast-written far from best example of stretch I did:
internal static class DataSources
{
private static BindingSource bs;
public static BindingSource CerateDataSource(List<object> yourObjects)
{
bs = new BindingSource();
bs.DataSource = yourObjects;
}
public static BindingSource GetDataSource()
{
return bs;
}
public static void Reset()
{
bs.ResetBindings(false);
}
}
and then in your listview
dataGridView1.DataSource = DataSources.GetData();
and in detailsview where you are editing one of the objects from BindingSource on save you would have to call: DataSources.Reset();. This is just a markup, but hopefully you get the idea :).
You must always be sure you are referring to the current instance of detailform, thus declare on your listForm
detailform obj = (detailform)Application.OpenForms["detailform"];
And every time you call detailform from listForm do it by obj e.g:
obj.Show()

The BindingList Datasource of a Combobox refreshes correctly but the Combobox displays items in the wrong order

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.. :)

Independent drop down lists with shared data source

I have binding list:
BindingList<string> sampleList = new BindingList<string>();
sampleList.Add("aaa");
sampleList.Add("bbb");
used as data source for two combo boxes:
comboBox1.DataSource = sampleList;
comboBox2.DataSource = sampleList;
When I change selection in one of these combo boxes, second is affected as well. How can I keep them independent?
EDIT:
Due to some 'hot' comments I have to do some clarifications:
it is windows forms code
it is possible
there is no another logic / code behind it
I'm using .NET 2.0
full code source:
public partial class Form1 : Form
{
BindingList<string> sampleList;
public Form1()
{
InitializeComponent();
sampleList = new BindingList<string>();
sampleList.Add("aaa");
sampleList.Add("bbb");
comboBox1.DataSource = sampleList;
comboBox2.DataSource = sampleList;
}
}
try this
comboBox1.DataSource = sampleList.ToList();
comboBox2.DataSource = sampleList.ToList();
Use 2 separate Data sources. In this particular case 2 different BindingList instances per combo, may be the best solution, especially if we are talking about reasonably small lists.
If I'm not mistake every Form has it's default associated BindingSource and it, actually, manages the "linking" controls refered to the same collection. But I'm not very sure in this, honestly.
Regards.

ComboBox items.count doesn't match DataSource

I have a ComboBox that is bound to a DataSource. I want to dynamically add items to the ComboBox based on certain conditions. So what I've done is add the options to a new list, and then change the DataSource of the ComboBox like so:
cbo.DataSource = null;
cbo.DataSource = cbos;
cbo.DisplayMember = "Title";
cbo.ValueMember = "Value";
Then, I check cbo.Items.Count, and it has not incremented - it does not equal the count of the DataSource. Any ideas what I can do here?
Note this is WinForms and not ASP.NET.
Did you check the Count immediately or at a later time? There is the possibility that the ComboBox does not actually update it's contents until there is an operation such as a UI refresh and hence the count will be off until that time.
On case where this may happen is if you update the DataSource before the Handle is created for the ComboBox. I dug through the code a bit on reflector and it appears the items will not be updated in this case until the ComboBox is actually created and rendered.
If anyone experiences this problem on a dynamically added combobox, the answer is to ensure that you add the combobox to the controls of a container in the form.
By adding "this.Controls.Add(cbo);" to the code before setting the datasource, the problem goes away.
I've found the cause...
I took out the cbo.Datasource = null line.. and added a cbo.Invalidate() at the end. This has solved the problem.
Thanks all for the advice.
cbo.DataSource = null;
cbo.DataSource = cbos;
cbo.DisplayMember = "Title";
cbo.ValueMember = "Value";
Now before setting cbo.SelectedValue, or relying on Items to be up-to-date, call
cbo.CreateControl ;
and Items will be re-calculated.
The problem is that SelectedValue/SelectedIndex, which are WinForms properties, only accept the values that are legal according to the Items list, but that one is built only after GUI interaction, i.e. after instantiating a "real" Windows GUI combo box, i.e. after obtaining a Windows handle for the combobox.
CreateControl forces the creation of the Windows handle, no matter what.
ComboBox cbNew = new ComboBox();
cbNew.Name = "cbLine" + (i+1);
cbNew.Size = cbLine1.Size;
cbNew.Location = new Point(cbLine1.Location.X, cbLine1.Location.Y + 26*i);
cbNew.Enabled = false;
cbNew.DropDownStyle = ComboBoxStyle.DropDownList;
cbNew.DataSource = DBLayer.GetTeams(lineName).Tables[0];
cbNew.DisplayMember = "teamdesc";
cbNew.ValueMember = "id";
Console.WriteLine("ComboBox {0}, itemcount={1}", cbNew.Name, cbNew.Items.Count);
// The output displays itemcount = 0 for run-time created controls
// and >0 for controls created at design-time
gbLines.Controls.Add(cbNew);
TO
ComboBox cbNew = new ComboBox();
cbNew.Name = "cbLine" + (i+1);
cbNew.Size = cbLine1.Size;
cbNew.Location = new Point(cbLine1.Location.X, cbLine1.Location.Y + 26*i);
cbNew.Enabled = false;
cbNew.DropDownStyle = ComboBoxStyle.DropDownList;
Console.WriteLine("ComboBox {0}, itemcount={1}", cbNew.Name, cbNew.Items.Count);
// The output displays itemcount = 0 for run-time created controls
// and >0 for controls created at design-time
gbLines.Controls.Add(cbNew);
cbNew.DataSource = DBLayer.GetTeams(lineName).Tables[0];
cbNew.DisplayMember = "teamdesc";
cbNew.ValueMember = "id";
The DataSource, DisplayMember and ValueMember property must be set after the control has been added to its container.
By adding "this.Controls.Add(cbo);" to the code before setting the datasource, the problem goes away.
//Create dynamic combobox and add to Panel
ComboBox ddCombo = new ComboBox();
controls = new Control[1] { ddCombo };
panel.Controls.AddRange(controls);
//After creating add to table layout
tableLayoutPanel.Controls.Add(panel, 0, 0);
ddCombo .Name = "ddName";
ddCombo .Width = 200;
ddCombo .Location = new Point(x, y); ddCombo .DataSource = ds;//or any List
ddCombo .SelectedIndex = ddCombo .Items.Count - 1;
Just to clarify are you calling the count() method
After calling the databind() method
This code produces 2 in the message box for me, can you try it and see how it behaves for you?
You can paste it into a console application, and add a reference to System.Windows.Forms and System.Drawing.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
namespace SO887803
{
static class Program
{
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}
}
public partial class MainForm : Form
{
private Button _Button;
private ComboBox _ComboBox;
public MainForm()
{
_Button = new Button();
_Button.Text = "Test";
_Button.Location = new Point(8, 8);
_Button.Click += _Button_Click;
Controls.Add(_Button);
_ComboBox = new ComboBox();
_ComboBox.Location = new Point(8, 40);
Controls.Add(_ComboBox);
}
private void _Button_Click(object sender, EventArgs e)
{
List<Item> items = new List<Item>();
items.Add(new Item("A", "a"));
items.Add(new Item("B", "b"));
_ComboBox.DataSource = null;
_ComboBox.DataSource = items;
_ComboBox.DisplayMember = "Title";
_ComboBox.ValueMember = "Value";
MessageBox.Show("count: " + _ComboBox.Items.Count);
}
public class Item
{
public String Title { get; set; }
public String Value { get; set; }
public Item(String title, String value)
{
Title = title;
Value = value;
}
}
}
}
comboBox1.DataSource=somelist;
int c1=comboBox1.DataSource.Count; // still zero
BindingContext dummy = this.comboBox1.BindingContext;// Force update NOW!
int c2=comboBox1.DataSource.Count; // now it equals somelist.Count
I had the same problem (Im working with VS 2005).
What you need to do is set the DataSource to null, clear the items, reassign the datasource , display and value members.
Eg
cbo.DataSource = null;
cbo.Items.Clear();
cbo.DataSource = cbos;
cbo.DisplayMember = "Title";
cbo.ValueMember = "Value";
Old thread, but I tried some of these solutions, along with suspending/resuming the bindingcontext, binding to and resetting a binding source, and just plain reloading the form. None worked to update my control with the newly bound data at the time of my .datasource setting (my items.count was empty, just like the OP).
Then I realized that my combobox was on a tabpage that was getting removed at the start of the code, and later re-added (after my databinding). The binding event did not occur until the tabpage was re-added.
Seems obvious in retrospect, but it was very difficult to detect at runtime, due to the order of calls and inability to see when things were changing.
Try this code.
cbo.BindingContext = new BindingContext();
cbo.DataSource = null;
cbo.DataSource = cbos;
cbo.DisplayMember = "Title";
cbo.ValueMember = "Value";
Maybe your ComboBox`s BindingContext is null.
Ba salam,
you can simply refresh the UI by preformLayout() function;
Example:
comboBox1.performLayout();
regards
mohsen s
please try this:
cbo.Parent = <your panel control>;
cbo.DataSource = null;
cbo.DataSource = cbos; cbo.DisplayMember = "Title";
cbo.ValueMember = "Value";
MessageBox.Show(string.Format("itemcount is {0}", cbo.Items.Count);
I think your question sames like I met today.

Categories

Resources