Setting the start value of an enum ComboBox - c#

been searching for a solution for this problem, found plenty solutions, but nothing changed my code's behaviour. This is in WinForms.
I am loading a form with a ComboBox, that contains the values and names of this enum, that is inside a class named "Node".
class Node
{
public enum NodeType { Yield, Home, Parking, Light, None, Inbound, Outbound }
public NodeType Type;
}
Then in my form, I have a ComboBox named "Type", which is set up like so (from the constructor):
Node node = new Node();
node.Type = Node.NodeType.Home;
Type = new ComboBox();
Type.Location = new Point(77, 41);
Type.Size = new Size(121, 24);
Type.DropDownStyle = ComboBoxStyle.DropDownList;
Type.DisplayMember = "Name";
Type.ValueMember = "Value";
Type.DataSource = Enum.GetValues(typeof(Node.NodeType));
Type.SelectedValue = node.Type;
Controls.Add(Type);
When the program runs, the list shows all the names, and on closing the form I am able to retrieve the selected value via. Type.SelectedValue. My problem is that the ComboBox doesn't start at the value that the Node is already set at. Essentially the line
Type.SelectedValue = node.Type;
doesn't do anything. I've tried using SelectedItem which didn't change anything, and
Type.SelectedIndex = (int)node.Type;
Which caused an ArgumentOutOfRangeException.
So, my question is: how do I set the start value of the ComboBox?

There are several mistakes in that code.
First, enum does not have Name and Value properties (in fact it does not have any property), so DisplayMember and ValueMember cannot be used and should be left blank (default). Which in turn means SelectedValue cannot be used and you need to use SelectedItem instead.
Second, you are using list data bound mode for the list portion of your ComboBox by setting DataSource property instead of populating Items, which is fine, but data binding occurs later in the process, so inside the constructor the Items property is empty and SelectedItem has no effect. In order to fix that, you need to move the data initialization part to your form Load event.
So, in your form constructor you'll have this:
Type = new ComboBox();
Type.Location = new Point(77, 41);
Type.Size = new Size(121, 24);
Type.DropDownStyle = ComboBoxStyle.DropDownList;
Controls.Add(Type);
and in your form Load event - this:
Node node = new Node();
node.Type = Node.NodeType.Home;
Type.DataSource = Enum.GetValues(typeof(Node.NodeType));
Type.SelectedItem = node.Type;

Type.SelectedIndex = index from your enum, for example the enum is {"apple", "pear", "pineapple"}, and you want pear by deafult, so Type.SelectedIndex = 1

Related

ComboBoxes are linked (and that is bad)

I am doing simple WinForms application, and I am facing some strange problem.
My form:
It is as easy as it can be: 3 comboboxes, and two buttons - OK and Cancel.
View:
private void applyOrderButton_Click(object sender, EventArgs e)
{
List<string> testList = new List<string>()
{
"A",
"B",
"C"
};
comboBox1st.DataSource = testList;
comboBox2nd.DataSource = testList;
comboBox3rd.DataSource = testList;
comboBox1st.SelectedIndex = 2;
comboBox2nd.SelectedIndex = 1;
comboBox3rd.SelectedIndex = 0;
//Presenter.DoTest();
}
What happens after caling method applyOrderButton_Click() (it happens after Ok button is clicked) all of my comboBoxes change selected position. However, each of those comboBoxes have the same selected index - in this particular case it will be "A".
Then I change change comboBox selectedIndex using my cursour (for example I choose 3rd comboBox to show "C") the change is performed for all three comboBoxes. What I am doing wrong?
You are running across something that is present in the background of WinForms called the "CurrencyManager".
Its job is to synchronize the "current record" across all bindable controls that refer to the same data source.
For instance, if you had added a label and bound it to the same list, and bound it so that it shows a property of one of the objects, it would always show the property value of the same object you had selected in the combobox.
One bonus of this is that you could easily add a form that edits a list of objects, binding textboxes and such to the properties of one of the objects and adding a navigator that allows you to move to the next or previous row. You would not have to manually ensure all textboxes refer to the correct object, the CurrencyManager would do all this for you.
However, in your case, since you bound the same data source to all three comboboxes, the CurrencyManager will ensure all three select the same row. If you select a new row in one of the comboboxes, the CurrencyManager will go and fix all the others to refer to the same row.
You can fix this in various ways:
You can override the binding context for each combobox:
comboBox2nd.BindingContext = new BindingContext();
comboBox3rd.BindingContext = new BindingContext();
Note that if you go this route you need to do this before assigning the SelectedIndex or SelectedItem properties, otherwise the CurrencyManager will have updated the other two comboboxes before you assigned new BindingContexts.
You can assign distinct data sources to each combobox:
combobox2nd.DataSource = testList.ToList();
combobox3rd.DataSource = testList.ToList();
Just assign new context to other ComboBoxes like that:
List<string> testList = new List<string>()
{
"A",
"B",
"C"
};
comboBox1st.DataSource = testList;
comboBox2nd.BindingContext = new BindingContext();
comboBox2nd.DataSource = testList;
comboBox3rd.BindingContext = new BindingContext();
comboBox3rd.DataSource = testList;
comboBox1st.SelectedIndex = 2;
comboBox2nd.SelectedIndex = 1;
comboBox3rd.SelectedIndex = 0;
The CurrencyManager is used to keep data-bound controls synchronized with each other (showing data from the same record). The CurrencyManager object does this by managing a collection of the bound data supplied by a data source. For each data source associated with a Windows Form, the form maintains at least one CurrencyManager. Because there may be more than one data source associated with a form, the BindingContext object manages all of the CurrencyManager objects for any particular form. More broadly, all container controls have at least one BindingContext object to manage their CurrencyManagers.

How to set selectedValue to Controls.Combobox in c#?

I have a combobox and I see that I am not able to set SelectedValue like this:
cmbA.SelectedValue = "asd"
So I tried to do this
cmbA.SelectedIndex = cmbA.FindString("asd");
Based on How to set selected value from Combobox?
I realised that my combobox is a System.Windows.Controls.ComboBox and not a System.Windows.Forms.ComboBox.
That means that FindString() is not available.
Based on User Control vs. Windows Form I get that forms are the container for controls, but I dont get why the Controls.ComboBox does not implement FindString().
Do I have to write my own code to do what FindString() does for Forms.ComboBox?
WPF ComboBoxes are not the same as WinForms ones. They can display a collection of objects, instead of just strings.
Lets say for example if I had
myComboBox.ItemsSource = new List<string> { "One", "Two", "Three" };
I could just use the following line of code to set the SelectedItem
myComboBox.SelectedItem = "Two";
We're not limited to just strings here. I could also say I want to bind my ComboBox to a List<MyCustomClass>, and I want to set the ComboBox.SelectedItem to a MyCustomClass object.
For example,
List<MyCustomClass> data = new List<MyCustomClass>
{
new MyCustomClass() { Id = 1, Name = "One" },
new MyCustomClass() { Id = 2, Name = "Two" },
new MyCustomClass() { Id = 3, Name = "Three" }
};
myComboBox.ItemsSource = data;
myComboBox.SelectedItem = data[0];
I could also tell WPF I want to consider the Id property on MyCustomClass to be the identifying property, and I want to set MyCombbox.SelectedValue = 2, and it will know to find the MyCustomClass object with the .Id property of 2, and set it as selected.
myComboBox.SelectedValuePath = "Id";
myComboBox.SelectedValue = 2;
I could even set the Display Text to use a different property using
myComboBox.DisplayMemberPath = "Name";
To summarize, WPF ComboBoxes work with more than just Strings, and because of the expanded capabilities, FindString is not needed. What you are most likely looking for is to set the SelectedItem to one of the objects that exist in your ItemsSource collection.
And if you're not using ItemsSource, then a standard for-each loop should work too
foreach(ComboBoxItem item in myComboBox.Items)
{
if (item.Content == valueToFind)
myComboBox.SelectedItem = item;
}
I don't know what you are trying to do but I think it would be easier to just do
cmbA.Text = "String";
That way you get your selected item
Else I found an intersting article that could help you out:
Difference between SelectedItem, SelectedValue and SelectedValuePath

How do I update a pre-formatted DataGridView bound to a list?

I have a DataGridView that I am binding to the Values collection of a dictionary. In my form's constructor, I can create the dictionary and bind the columns of the DataGridView to fields in the structure that will be contained in the dictionary:
m_tasks = new Dictionary<int,OperationsTaskLabeledData>();
var taskArray = from task in m_tasks.Values select new {
StartDate = task.m_start_task_date.ToString("M/dd H:mm"),
EndDate = task.m_task_date.ToString("M/dd H:mm"),
Description = task.m_short_desc,
Object = task.m_device_id,
InitialLocation = task.m_initial_location,
FinalLocation = task.m_final_location };
dgvFutureTasks.DataSource = taskArray.ToArray();
I want this code in the form's constructor so that the columns can be formatted, and I won't have to reformat them every time data in the grid is updated.
When I actually have data to display in this datagridview, what do I do with it? I will call this function:
private void DisplayFutureTasks(IEnumerable<OperationsTaskLabeledData> tasks)
But I don't know what to do inside this function.
I can just re-bind the datagridview control and reformat all of the columns each time the control is updated, but that seems very wasteful. I'm sure there's a better way to do it, and I'd much rather do this in some reasonable fashion instead of using ugly brute force.
I have now figured out how to do what I want to do.
I know the columns I will need at design time, so in the IDE I add the columns to my datagridview and format them as desired. I then set the AutoGenerateColumns property of the grid view to false. For some unknown reason, that property is not available in the designer and has to be set in code. Finally, I can set the DataPropertyName of each column to the name of the corresponding field in the structure I will be linking to. For example, here is the LINQ code I will be using to generate the data source:
taskArray = from task in tasks select new {
StartDate = task.m_start_task_date.ToString("M/dd H:mm"),
EndDate = task.m_task_date.ToString("M/dd H:mm"),
Description = task.m_short_desc,
Object = task.m_device_id,
InitialLocation = task.m_initial_location,
FinalLocation = task.m_final_location };
.DataSource = taskArray.ToArray();
And here is the code in my form's constructor to set the DataPropertyName properties:
dgvFutureTasks.AutoGenerateColumns = false;
dgvFutureTasks.Columns["colStartTime"].DataPropertyName = "StartDate";
dgvFutureTasks.Columns["colFinishTime"].DataPropertyName = "EndDate";
dgvFutureTasks.Columns["colDescription"].DataPropertyName = "Description";
dgvFutureTasks.Columns["colObject"].DataPropertyName = "Object";
dgvFutureTasks.Columns["colInitialLocation"].DataPropertyName = "InitialLocation";
dgvFutureTasks.Columns["colFinalLocation"].DataPropertyName = "FinalLocation";
At this point, the DataGridView displayed the data as expected.
RobR

C# Combobox and TabControl woes

enter code hereI have a TabControl on a Form and in the TabPages there are ComboBoxes.
When the form OnLoad, I populate the ListItems in the ComboBoxes and the attempt to set default values to string.Empty.
However, the ComboBox.SelectedText = string.Empty only works for the first TabPage. The other ComboBoxes ignore the command and take the default value as the first item in the list. Why is this so? How can I overcome it?
The ComboBoxes are all set up by this function
public static void PrepareComboBox(ComboBox combobox, FieldValueList list)
{
combobox.DropDownStyle = ComboBoxStyle.DropDown;
combobox.AutoCompleteSource = AutoCompleteSource.ListItems;
combobox.AutoCompleteMode = AutoCompleteMode.Suggest;
combobox.DataSource = list.DataSource;
combobox.DisplayMember = list.DisplayMember;
combobox.ValueMember = list.ValueMember;
combobox.Text = string.Empty;
combobox.SelectedText = string.Empty;
}
I found that the reason could be that the ComboBox is not "active" until they are at least shown once. You can see that when the TabPage is selected for the first time, it takes a slightly longer time to load. I suppose it is creating/initialising the child controls for the first time.
For that, I call tabControl.SelectTab() before modifying the value properties and it worked... although it feels like a hack.
This is due to databinding. Not much you can do about it, except prefix the datasource with an empty/dummy entry.

How do you bind a data field value to a combobox index?

This may have been already asked but I can't seem to find this specific question, so here goes...
I have a form in C# where every textbox is bound to a field in a row. Rows can be cycled through by some buttons on the bottom but all the data displayed at a time in the from is from one row. Any changes that are made get updated back to the database when the user clicks "update"
One field (class) is an enumeration (0,1,2) where only the value is stored in the database, but doesn't mean much to the user. I was asked to make this more obvious to the user, so I decided to go with a dropdown style combo box. Since the database didn't have any reference to what the values meant, I decided to use the DataBindings instead of DataSource so I could just use the index as the data bind, but it seems that SelectedItem or Value are not the way to do this.
Here is my goal:
1 exists in database, so "B" is selected in combo box.
User selects "C" and updates the database, 2 is now stored in the database.
Any thoughts on what I need to get this working?
I assume you have a BindingSource on your form to bind to the data. You can bind the SelectedIndex property of the ComboBox as follows:
comboBox.DataBindings.Add("SelectedIndex", bindingSource, "PropertyInTheDataSource");
Actually I was able to bind it to a custom Object. It's a little too much work for such a simple task. But you have complete control on Display/Value pairs. Anyway, I thought I'd share and you decide:
Create a new class (say CustomItem) with 2 fields:
Public int Value{get;set;}
public string Title {get;set;}
Then in you form:
var item1 = new CustomItem() { Title = "A", Value = 10 };
var item2 = new CustomItem() { Title = "B", Value = 20 };
var item3 = new CustomItem() { Title = "C", Value = 30 };
var lst = new List<CustomItem>();
lst.Add(item1);
lst.Add(item2);
lst.Add(item3);
comboBox1.DataSource = lst;
comboBox1.DisplayMember = "Title";
comboBox1.ValueMember = "Value";
Now You have a databound combobox in case you don't have BndingSource in your form.
Just remember to define your class's Title and Value as properties otherwise it wouldn't work.

Categories