Binding dictionary to combobox in C# - c#

I have this in Load of my form which contains combobox (cmbTip)
EventTypeRepository tip = new EventTypeRepository();
cmbTip.DataSource = new BindingSource(tip.FindAll(), null);
cmbTip.DisplayMember = "Value";
cmbTip.ValueMember = "Key";
(FindAll() is a method in EventTypeRepository which returns Dictionary(string, EventType>))
For some reason this displays MyProject.Model.EventType as all combobox items. I even added:
public string toString()
{
return _name + "(" + _id + ")";
}
in my EventType class, but it stills displays names as MyProject.Model.EventType (there are as many items as there are event types, so I think it works fine expect for displaying names). I have no idea how to fix this...

You should override ToString method (keep in mind C# is case-sensitive language):
public override string ToString()
{
return String.Format("{0}({1})", _name, _id);
}
Also it's better to set DisplayMember and ValueMember before you set DataSource.

If you ever find your self in a situation where you can't override ToString(), another option is to use the Format event built in to the combo box. First you must set FormattingEnabled to true on the combo box, then subscribe to the Format event and use code similar to the following.
private void cmbTip_Format(Object sender, ListControlConvertEventArgs e)
{
var item = (EventType) e.ListItem;
e.Value = String.Format("{0}({1})", Name, Id);
}
This assumes that _name and _id have corresponding public properties Name and Id.

Related

Display object propery in ComboBox

I have a custom object which holds details about a project resource.
Properties are PersonName, Position and Id
If the resource isn't filled, PersonName is set to 'Unassgined'.
To add an object to a Combobox, I do:
var avail = s.GetUnassignedPrintRoles(SprintId);
foreach (var o in avail)
{
cmbRoles.Items.Add(o);
}
This is fine when displaying a list of resources. My object has an overridden ToString() method:
public override string ToString()
{
if(AssignedPerson != null)
return ResourceType + " - " + AssignedPerson.Firstname + " " + AssignedPerson.Surname;
return "Unassigned";
}
But, I have a screen that shows a list of roles that are not assigned. So, I get a list, where the Person is NULL.
But, I want to display the 'Role' in the ComboxBox.
But, my object's ToString shows 'Unassigned'. How can I make it display the Role property? Is there a way to save the object in the Comboxbox item, but display a different propery in the display, other than what I have set in the ToString override?
With regards to my comment, it might be needed to set the DisplayMember and ValueMember properties of the ComboBox, like so;
cmbRoles.DisplayMember = "Role";
cmbRoles.ValueMember = "Id";
cmbRoles.DataSource = avail;
This way your ComboBox will Display the role, but the underlying data will be the ID, So when you select via the SelectedValue property, you'll get the ID.
Have you tried using the DisplayMember property to distinguish the displayed value and the actual value? If you do that, you should be able to set the Role as the displayed entry on the combobox.
cmbRoles.DisplayMember = "" + Role;
cmbRoles.ValueMember = "Id";
cmbRoles.DataSource = avail;
I'm not sure what Role is in your code but you should be able to get the gist from that.
Add this ,
private void InitializeComponent()
{
cmbRoles.ValueMember = "id";
cmbRoles.DisplayMember = "discription";
}
You can remove ToString() altogether by using read-only property:
public string FullInfo
{
get
{
return ResourceType + " - " + AssignedPerson.Firstname + " " + AssignedPerson.Surname;
}
}
then
comboBox.DisplayMember = "FullInfo";
comboBox.ValueMember = "Id";
comboBox.DataSource = avail;
and you can do any kind of properties like this.

How to store the actual underlying DB value in a combobox while displaying a more user-friendly value?

I need to display values like "Surreptitiously" and "Discreetly" in a comboBox but thereafter be able to set the comboboxes' SelectedItem based on the underlying DB values for those words (e.g., "S" and "D").
I reckon I can use the comboBoxes' DisplayMember and ValueMember properties for this somehow, but can I subsequently do something analagous to the following with the actual (valuemember) values:
comboBoxAdverbs.SelectedIndex = comboBoxAdverbs.Items.IndexOf(weirdAdverbs[CURRENT_ADVERB]);
As "weirdAdverbs[CURRENT_ADVERB]" contains the values like "S" and "D" it, of course, doesn't find and set the SelectedIndex when the comboBox contains the values "Surreptitiously" and "Discreetly"
If I set the combobox Item Tag value to "S" and "D" (assuming that's possible), I can loop through those values, but I'm hoping there's a one-line way of doing it similar to the "IndexOf()" above.
I use a template class for this and it comes in pretty darn handy. The combo box will show whatever text you want and you can store a value with it.
public class cboItem<T>
{
public cboItem(string name, T value)
{
this.Name = name;
this.Value = value;
}
public string Name { get; set; }
public T Value { get; set; }
public override string ToString()
{
return Name == null ? "" : Name;
}
}
Combo box items can be anything, including classes/structs. By default it will use the ToString() implementation to display items, but if you populate a set of objects you can use DisplayMember and ValueMember to great effect.
As a simple example to give you some ideas we'll bind the combo box to a set of KeyValuePair instances for your weird verb codes and their descriptive names. Alternatively you can use linq to compose anonymous types, or create your own suitable classes/structs.
private void populateCombo()
{
comboBoxAdverbs.Items.Clear();
comboBoxAdverbs.Items.Add( new Tuple<string, string>( "S", "Surreptitiously" ) );
comboBoxAdverbs.Items.Add( new Tuple<string, string>( "D", "Discreetly" ) );
comboBoxAdverbs.DisplayMember = "Item2";
}
Then in your code where you want to select an item matching a provided code: (I.e. "D")
var item = comboBoxAdverbs.Items
.OfType<Tuple<string,string>>()
.FirstOrDefault(i => string.Compare(i.Item1, textBox1.Text, true) == 0);
if (item != null)
comboBoxAdverbs.SelectedItem = item;
This attempts to find the matching item by comparing the key against whatever input (in this case a textbox value) and if it finds a match, sets the SelectedItem to tell the combo box to select it.
** Edit: Whups, had originally use KeyValuePair which I didn't realize was a struct so no Null check-ability. Changed to Tuple (Essentially Pair)
What I found that works for me is to store the selectedindex value, after it's set, into the combobox's Tag property. Then, if the user attempts to change the selectedIndex when it is supposed to be in a "readonly" state, simply change it back to the selectedIndex value stored in the Tag property:
comboBoxPlatypusId.SelectedIndex = comboBoxPlatypusId.Items.IndexOf(DuckbillVals[Duckbill_PlatypusID]);
comboBoxPlatypusId.Tag = comboBoxPlatypusId.SelectedIndex;
...
private void comboBoxFunnyMammals_SelectedValueChanged(object sender, EventArgs e) {
var cb = sender as ComboBox;
if (cb != null)
{
int validSelection = Convert.ToInt32(cb.Tag);
if (cb.SelectedIndex != validSelection) {
cb.SelectedIndex = validSelection;
}
}
}

Non-String values in DataGridViewComboBoxColumn causing DataErrors in DataGridView

I have a programmtically filled DataGridView with some DataGridViewComboBoxColumn entries. These ComboBoxes hold custom types that have their ToString() overwritten to hide their internal guts from the user.
The Problem: whenever I change the value of the ComboBox and select another cell, the CellValidating is called, the FormattedValue is the string-result of my custom FooBar.ToString() (should be ok). In CellEndEdit, the ValueType is suddenly a string and not my custom type anymore and I get error either watching row.Cell-Member in VS or doing a mouse-over on the changed cell (translated from german):
"DataGridView-Exception: System.ArgumentException: The DataGridViewComboBoxCell-Value is invalid. Handle the DataError-Exception to replace this dialog"
This is how the column is constructed - colFoo is a DataGridViewComboBoxColumn:
dataGridInstance.Columns[1].CellTemplate.Value = new FooBar("bla", 31);
dataGridInstance.Columns[1].CellTemplate.ValueType = typeof(FooBar);
foreach (FooBar foo in FooBarArray)
colSymbol.Items.Add(sym);
The Grid gets filled like this:
foreach (FooLine line in SomeFooLineArray)
{
DataGridViewRow newRow = ((DataGridViewRow)dataGridInstance.RowTemplate.Clone());
newRow.Cells[1].Value = line.Foo; // FooBar instance field
dataGridInstance.Rows.Add(newRow);
}
Any experiments in CellValidating or CellFormatting failed, I always get this message.
How can I add custom types into the existing DataGridView without overwriting too much? Almost every other Component (including dropboxes) supports custom types just fine, only the step of copying the value from the Combo to the grid seams to be the problem.
As asked, the FooBar-implementation looks like this:
internal class FooBar {
private readonly int id;
private readonly string name;
internal FooBar(string n, int i)
{
id = i; name = n;
}
public override string ToString()
{
return name;
}
}
It does a little bit more, but that is the meaningful functionality encapsulated.
have you tried changing the
internal FooBar(string n, int i)
{
id = i; name = n;
}
to read like this
internal FooBar(string n, int i)
{
this.id = i; this.name = n;
}
I know this is an old question, but I've just encountered this problem and I hope the solution is useful.
Firstly, DataGridViewComboBoxColumn works great if you are using strings. The idea of having a ComboBox using objects is great (and just what I wanted) but if that's what you want then there are a few hurdles you need to jump through (possibly because the developers forgot).
The first one is to set the ValueType of the column (which you are doing in your code), i.e.:
dataGridInstance.Columns[1].CellTemplate.ValueType = typeof(FooBar);
or you can set it directly on the column:
dataGridInstance.Columns[1].ValueType = typeof(FooBar);
The second bit (and this is what you are missing) is that you need a way of converting from a string back to an instance of your data class FooBar. You do this by providing a custom event for CellParsing, which should look something like this:
private void dataGridInstance_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
if (e.DesiredType == typeof(ListItem))
{
if (e.Value != null)
{
DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)dataGridInstance.Rows[e.RowIndex].Cells[e.ColumnIndex];
foreach (object item in comboCell.Items)
{
if (item.ToString() == e.Value.ToString())
{
e.Value = item;
e.ParsingApplied = true;
return;
}
}
}
}
}
That's it.
One gotcha: yes, if two objects have the same .ToString() representation then this won't tell them apart (but that's a limitation inherent with the control). So if you can then make sure the string representation is unique.
Your columns should have same data type
Let Say
TableForComboColumn
id Int32
name Varchar
TableWithComboColumn
id Int32
TableForComboColumn_id Int16 --> this should be Int32 also

How can I add an item to a ListBox in C# and WinForms?

I'm having trouble figuring out how to add items to a ListBox in WinForms.
I have tried:
list.DisplayMember = "clan";
list.ValueMember = sifOsoba;
How can I add ValueMember to the list with an int value and some text for the DisplayMember?
list.Items.add(?)
Btw. I can't use ListBoxItem for any reasons.
ListBoxItem is a WPF class, NOT a WinForms class.
For WPF, use ListBoxItem.
For WinForms, the item is a Object type, so use one of these:
1. Provide your own ToString() method for the Object type.
2. Use databinding with DisplayMemeber and ValueMember (see Kelsey's answer)
list.Items.add(new ListBoxItem("name", "value"));
The internal (default) data structure of the ListBox is the ListBoxItem.
In WinForms, ValueMember and DisplayMember are used when data-binding the list. If you're not data-binding, then you can add any arbitrary object as a ListItem.
The catch to that is that, in order to display the item, ToString() will be called on it. Thus, it is highly recommended that you only add objects to the ListBox where calling ToString() will result in meaningful output.
You might want to checkout this SO question:
C# - WinForms - What is the proper way to load up a ListBox?
DisplayMember and ValueMember are mostly only useful if you're databinding to objects that have those properties defined. You would then need to add an instance of that object.
e.g.:
public class MyObject
{
public string clan { get; set; }
public int sifOsoba { get; set; }
public MyObject(string aClan, int aSif0soba)
{
this.clan = aClan;
this.sif0soba = aSif0soba;
}
public override string ToString() { return this.clan; }
}
....
list.Items.Add(new MyObject("hello", 5));
If you're binding it manually then you can use the example provided by goggles
The way I do this - using the format Event
MyClass c = new MyClass();
listBox1.Items.Add(c);
private void listBox1_Format(object sender, ListControlConvertEventArgs e)
{
if(e.ListItem is MyClass)
{
e.Value = ((MyClass)e.ListItem).ToString();
}
else
{
e.Value = "Unknown item added";
}
}
e.Value being the Display Text
Then you can attempt to cast the SelectedItem to MyClass to get access to anything you had in there.
Also note, you can use anything (that inherits from object anyway(which is pretty much everything)) in the Items Collection.
If you just want to add a string to it, the simple answer is:
ListBox.Items.Add("some text");
You have to create an item of type ListBoxItem and add that to the Items collection:
list.Items.add( new ListBoxItem("clan", "sifOsoba"));
If you are adding integers, as you say in your question, this will add 50 (from 1 to 50):
for (int x = 1; x <= 50; x++)
{
list.Items.Add(x);
}
You do not need to set DisplayMember and ValueMember unless you are adding objects that have specific properties that you want to display to the user. In your example:
listbox1.Items.Add(new { clan = "Foo", sifOsoba = 1234 });

Get the combobox text in C#

I filled up a combobox with the values from an Enum.
Now a combobox is text right? So I'm using a getter and a setter. I'm having problems reading the text.
Here's the code:
public BookType type
{
get
{
return (BookType)Enum.Parse(typeof(BookType), this.typeComboBox.Text);
}
set
{
this.typeComboBox.Text = value.ToString();
}
}
For some reason, this.typeComboBox.Text always returns an empty string when I select an item on the combobox.
Does someone see what I'm doing wrong?
EDIT: I have come to the conclusion that the problem lies in timing.
The point in time at which I summon the text is indeed after I changed the combobox, but still before that value is parsed as a value.
Problem fixed in a different way now, thanks for all the ideas.
string selectedText = this.ComboBox.GetItemText(this.ComboBox.SelectedItem);
The GetItemText method analyzes the item and returns the text of the bound to that item.
Set the DropDownStyle of the ComboBox to DropDownList. This will ensure that only the elements already in the list can be selected (no need to check that the text actually is a valid value).
Then if you use Enum.GetValues(typeof(BookType)) to fill the combobox then typeComboBox.SelectedItem property will be a value of BookType. So you can use this in the property getter and setter.
So to summarize. You don't have to bind the combobox to a list of text values as long as you use the DropDownList style. Use the SelectedItem property to get an item of the wanted type instead of checking the Text property.
Edit: You may have to check the SelectedItem property for null
The combobox starts at index -1, which has no text, thus an empty string: ""
I then change the index to a BookType that I need and then I get the wrong output...
Have you tried using this.typeComboBox.SelectedText instead of typeComboBox.Text ?
this.typeComboBox.SelectedItem.ToString()
I just created a simple windows form, and everything worked okay for me. Here is the code.
public enum Test
{
One, Two, Three
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.comboBox1.DataSource = Enum.GetNames(typeof(Test));
}
public Test Test
{
get
{
return (Test)Enum.Parse(typeof(Test), this.comboBox1.Text);
}
set
{
this.comboBox1.Text = value.ToString();
}
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(this.Test.ToString());
this.Test = Test.Two;
MessageBox.Show(this.Test.ToString());
}
}

Categories

Resources