Best way to use Combobox (Bind display text and database text together) - c#

I am creating a new window called AMTI which is using comboboxes to display different selections for the user. The selection text could for example be "Analytical", in which case the value "E04" should be stored in appropriate column in the AMTI-table in the database. I read about using enums for databinding, but here a text and a numeric value are tied together, which can then be databound in the combobox. What is the easiest (or correct) approach for mapping a display text in a combobox with a value to be stored in the database?

You can bind ComboBox to any collection which elements expose public properties, including datatable, or, if you don't have a collection ready and need key-value objects, you can use Dictionary.
Dictionary<string, int> dict = new Dictionary<string, int>();
// fill the dictionary here
mycomboBox.DataSource = new BindingSource(dict, null);
mycomboBox.DisplayMember = "Key";
mycomboBox.ValueMember = "Value";
if(mycomboBox.SelectedIndex!=-1)
int currentlySelected = (int)mycomboBox.SelectedValue;
... or make your own class of objects for binding:
class NameValueHolder
{
public string Name{get;set;}
public int Value{get;set;}
public NameValueHolder(){}//so you can use it in linq
public NameValueHolder(string name, int value)
{
this.Name=name;
this.Value=value;
}
}
BindingList<NameValueHolder> list = new BindingList<NameValueHolder>();
list.Add(new NameValueHolder("object 1", 1);
list.Add(new NameValueHolder("object 2", 2);
list.Add(new NameValueHolder("object 3", 3);
mycomboBox.DataSource = new BindingSource(list, null);
mycomboBox.DisplayMember = "Name";
mycomboBox.ValueMember = "Value";
if(mycomboBox.SelectedIndex!=-1)
NameValueHolder currentlySelected = (NameValueHolder)mycomboBox.SelectedValue;
You can also bind ComboBox to Linq query result:
var qResult = from a in yourDataSource
where (/*some condition*/)
select new {Name = a.someName, Value = a.someValue};
mycomboBox.DataSource = qResult.ToList();
mycomboBox.DisplayMember = "Name";
mycomboBox.ValueMember = "Value";
Those are just some of the posibilities.

Related

Can't sort C# DataGridView column as integers

I have a DataGridView gridFilas in my windows form application with two columns and the second column always contains strings that can be converted to integers. When I click to sort it, it is sorted as strings resulting in something like:
1, 11, 2, 22
But I need it to be sorted as integers, like:
1, 2, 11, 22
I've already tried all the answers of this question but none worked, btw the accepted answer, it didn't worked because the SortCompare event wasn't being fired because of this.
So what I've tried so far is adding a ColumnHeaderMouseClick and sort it with:
private void gridFilas_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
// this for loop has been added in a vain hope of converting all elements to integer to see if it works...
for (int i = 0; i < gridFilas.Rows.Count; i++)
{
string v = gridFilas.Rows[i].Cells[1].Value.ToString();
gridFilas.Rows[i].Cells[1].Value = Convert.ToInt32(v);
}
if (queuesSortedAscending)
{
gridFilas.Sort(gridFilas.Columns["Tamanho_Fila"], System.ComponentModel.ListSortDirection.Descending);
}
else
{
gridFilas.Sort(gridFilas.Columns["Tamanho_Fila"], System.ComponentModel.ListSortDirection.Ascending);
}
queuesSortedAscending = !queuesSortedAscending;
}
The datasource is set on the creation (the constructor of the Form) with:
dsComponentes = new DataSet();
// ... more code
gridFilas.DataSource = dsComponentes.Tables["Queue"];
Whenever I add a new line to the DataSource I assure it is parsed to int:
DataRow qlinha = dsComponentes.Tables["Queue"].NewRow();
qlinha["Nome_Fila"] = process;
qlinha["Tamanho_Fila"] = Int32.Parse(status);
dsComponentes.Tables["Queue"].Rows.Add(qlinha);
I've also tried changing the data type of the column beforehand:
dsComponentes.Tables["Queue"].Columns["Tamanho_Fila"].DataType = typeof(int);
So, I don't know what else to do, I just need to order it as integers not strings. Any solution is welcome.
You never show where you're creating the DataTable that you're binding to (dsComponentes.Tables["Queue"]), but when you add that column you should be able to indicate that it's an int.
var dataTable = new DataTable();
dataTable.Columns.Add("ColumnName", typeof(int));
This will cause the bound DataGridView to sort that column as integers.
It looks like there is a DataType in the Columns Collection Editor as well, if you're using the designer to create your DataSet. You can set the type to System.Int32 on that column and it will sort as you expect.
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var frm = new Form()
{
Text = "Data Grid View Form",
Name = "DataGridViewForm",
Size = new System.Drawing.Size(400, 400)
};
var dataGridView1 = new DataGridView();
var dataTable = new DataTable();
dataGridView1.DataSource = dataTable;
dataGridView1.Dock = DockStyle.Fill;
dataTable.Columns.Add("Id", typeof(int));
dataTable.Columns.Add("IdAsString", typeof(string));
var r1 = dataTable.NewRow();
r1["Id"] = 1;
r1["IdAsString"] = "1";
dataTable.Rows.Add(r1);
var r2 = dataTable.NewRow();
r2["Id"] = 11;
r2["IdAsString"] = "11";
dataTable.Rows.Add(r2);
var r3 = dataTable.NewRow();
r3["Id"] = 2;
r3["IdAsString"] = "2";
dataTable.Rows.Add(r3);
var r4 = dataTable.NewRow();
r4["Id"] = 22;
r4["IdAsString"] = "22";
dataTable.Rows.Add(r4);
frm.Controls.Add(dataGridView1);
Application.Run(frm);
}
}
}
The easiest method would be to use a nuget package: Equin ApplicationFramework BindingListView
It is very similar to a standard .net BindingList, except that it contains functions to sort.
Import the BindingListView nuget into your project
Use Visual Studio designer to create your form
Use the toolbox to add a BindingSource to your form
Go to the properties of this BindingSource, select DataSource, Add Project Data Source, select Object and add the class of items that you want to show in your DataGridView
Now use the toolbox to add your DataGridView
In the properties of the DataGridView choose the BindingSource as the datasource.
Now all public properties of your class will be shown as column in the DataGridView. Edit the columns accordingly: remove the columns that you won't use.
In my example I will sort a sequence of Persons:
class Person
{
public int Id {get; set;}
public string Name {get; set;}
public DateTime BirthDate {get; set;}
}
Go to your Form1 class. We'll add a BindingListView from the nuget package as a member.
In the constructor we'll assign this to the bindingsource that is assigned to the DataGridView.
class Form
{
// the BindingListView from the nuget package:
private readonly BindingListView<Person> sortableBindingListView;
// constructor
public Form1()
{
InitializeComponent();
// make sure there is a Components Container, that will dispose
// the components upon disposal of the form
if (this.components == null)
{
this.components = new System.ComponentModel.Container();
}
// construct the sortable BindingListView for Persons:
this.sortableBindingListView = new BindingListView<Person>(this.components);
// create some Persons:
var persons = new Person[]
{
new Person{Id = 1, Name = "F", BirthDate = new DateTime(2000, 1, 1)},
new Person{Id = 2, Name = "A", BirthDate = new DateTime(1998, 4, 7)},
new Person{Id = 3, Name = "C", BirthDate = new DateTime(2011, 3, 8)},
new Person{Id = 4, Name = "Z", BirthDate = new DateTime(1997, 2, 3)},
new Person{Id = 5, Name = "K", BirthDate = new DateTime(2003, 9, 5)},
};
// Assign the DataSources:
this.sortableBindingListView.DataSource = persons;
this.dataGridView1.DataSource = this.sortableBindingListView;
}
}
This is enough to get your sorting working. You don't have to add anything extra. Upon clicking the columns the column is filtered.
Some interesting functions:
private Person SelectedPerson
{
get {return ((ObjectView<Person>)this.SortableBindingSource.Current)?.Object; }
}
private void DisplayPersons (IEnumerable<Person> personsToDisplay)
{
this.SortableBindingSource.DataSource = personsToDisplay.ToList();
this.SortableBindingSource.Refresh(); // this will update the DataGridView
}
private IEnumerable<Person> DisplayedPersons
{
get {return this.SortableBindingSource; }
// BindingListview<T> implements IEnumerable<T>
}
This is all. You don't need to create special functions to sort on mouse clicks. The sorting will be done automatically inclusive deciding on the sort order and displaying the correct sorting glyphs.
If you want to sort programmatically:
// sort columnPropertyA in descending order:
this.SortableBindingSource.Sort(this.columnPropertyA.ListsortDirection.Descending);
One of the nice things about the BindingListView is the filtering option:
// show only items where Name not null:
this.SortableBindingSource.ApplyFilter(person => person.Name != null);
// remove the filter:
this.SortableBindingSource.RemoveFilter();
I'm not sure if a Refresh() is needed after applying or removing a filter.

How to display a property instead of the value in a databound DataGridViewComboBox?

I'm new to C# and .NET
I need to display the matching name of a value in a databound DatagridViewComboBox, but I can't figure out how to do that.
I have the following code:
bs = new BindingSource();
bs.DataSource = typeof(CR);
dataGridView1.AutoGenerateColumns = false;
Column1.Width = 400;
Column1.DataPropertyName = "CR_NAME";
Column2.DataPropertyName = "CR_STATE_S";
Column2.ValueMember = "CR_STATE_S";
Column2.DisplayMember = "GetStateName";
Column2.Items.Add("0"); // how to set the matching value here?
Column2.Items.Add("1");
Column2.Items.Add("2");
dataGridView1.DataSource = bs;
GetStateName is a property of the CR Class that returns the matching name of the CR state. I need to display the state name in the combo box. How to do that? Thanks.
If you want to display something different from the values the cells shall contain, then you can't simply load one thing into the Items of the ComboBoxCells.
Instead you need a DataSource that has at least different fields for the two things you want to use:
A field for the visible representation of the data, called DisplayMember
And a field for the actual data values, called ValueMember
These fields can sit in a DataTable but you can also use any other collection with suitable properties.
Lets create a very simple class and have a List of that class:
class itemClass
{
public string display { get; set;}
public string value { get; set; }
public itemClass(string d, string v)
{ display = d; value = v;}
}
List<itemClass> myItems = new List<itemClass>();
private void loadButton_Click(object sender, EventArgs e)
{
// load the list with all values:
myItems.Add(new itemClass("zero", "0"));
myItems.Add(new itemClass("one", "1"));
myItems.Add(new itemClass("two", "2"));
myItems.Add(new itemClass("three", "3"));
myItems.Add(new itemClass("four", "4"));
myItems.Add(new itemClass("five", "5"));
myItems.Add(new itemClass("six", "6"));
// prepare the DataGridView 'DGV':
DGV.Columns.Clear();
DataGridViewComboBoxCell cCell = new DataGridViewComboBoxCell();
DataGridViewComboBoxColumn cCol = new DataGridViewComboBoxColumn();
DGV.Columns.Add(cCol);
cCol.DisplayMember = "display";
cCol.ValueMember = "value";
cCol.DataSource = myItems;
cCol.ValueType = typeof(string);
// add a few rows, for testing:
DGV.Rows.Add(7);
for (int i = 0; i < DGV.Rows.Count;
i++) DGV.Rows[i].Cells[0].Value = i + "";
}
In the example I load the Items manually. Usually you will want to pull the values from the database or some other source. You can do that either by loading the datasource list as above or you can have a lookup table either independently or in in a DataSet.
If all cells need to have individual lookup values you need to load them separately and not use the column but each of the cells cast to DataGridViewComboBoxCell.

add item combobox with datasource

I have a method (LoadCustomers()) that returns a dictionary of elements as follows:
Dictionary<int, string>()
I connected it to the datasource of a combobox like this:
Myclass m = new Myclass();
combo1.DataSource = new BindingSource(m.LoadCustomers(), null);
combo1.DisplayMember = "Value";
combo1.ValueMember = "Key";
Now I would like to put in front of the list of the combobox an item like:
<select one customer>
How to do this in c# on winforms?
Tks a lot
Add this option to the Dictionary of customers
const int EMPTYCUSTOMERKEY = -1; //be sure Customers will not contain this value
const string EMPTYCUSTOMERVALUE = "<select one customer>";
Myclass m = new Myclass();
Dictionary<int, string> customerSource = m.LoadCustomers();
customerSource.Add(EMPTYCUSTOMERKEY, EMPTYCUSTOMERVALUE);
combo1.DataSource = new BindingSource(customerSource, null);
combo1.DisplayMember = "Value";
combo1.ValueMember = "Key";

Dynamically/programmatically adding values to DataGridView

Could anyone give a snippet of sample code for manually creating columns for DataGridView? I need to create columns with custom names and also manually select what values to show in the column cells. I have the DataGridView bound to a Collection<>
private void initialiseDataGridView(Part part, string batchNumber){
dataCollection = new DataCollection(part.name, batchNumber);
dataCollectionSource = new BindingSource(dataCollection, null);
serialConDataGrid.DataSource = dataCollectionSource;
serialConDataGrid.AutoGenerateColumns = false;
// Add columns
DataGridViewCheckBoxColumn selectedCol = new DataGridViewCheckBoxColumn(false);
selectedCol.HeaderText = "Selected";
DataGridViewColumn runNumberCol = new DataGridViewColumn();
runNumberCol.HeaderText = "Run Number";
serialConDataGrid.Columns.Clear();
serialConDataGrid.Columns.Add(selectedCol);
serialConDataGrid.Columns.Add(runNumberCol);
// How can I specify which values to populate into the column cells here?
}
This msdn sample seems to be empty.
Here is a simple example on how to do it.
Here is the class of objects you want to display in the DataGridView. The things you want to display needs to be properties:
public class Fruit
{
public string Name { get; set; }
public Color Color { get; set; }
public Fruit(string name, Color color)
{
Name = name;
Color = color;
}
}
And here is the code for binding this data to the DataGridView. You need to link the name of the property to the dataGridViewColumn.DataPropertyName property.
// The list of objects
List<Fruit> fruit = new List<Fruit>( )
{new Fruit("Apple",Color.Red),
new Fruit("Orange",Color.Orange),
new Fruit("Pear",Color.Green)};
BindingSource source = new BindingSource(fruit, null);
dataGridView1.AutoGenerateColumns = false;
DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
column.HeaderText = "Name Of Fruit";
column.DataPropertyName = "Name"; // Name of the property in Fruit
dataGridView1.Columns.Add(column);
DataGridViewTextBoxColumn colorColumn = new DataGridViewTextBoxColumn();
colorColumn.HeaderText = "Color";
colorColumn.DataPropertyName = "Color"; // Name of the property in Fruit
dataGridView1.Columns.Add(colorColumn);
dataGridView1.DataSource = source;
You can do that:
Programatically-add-new-column-to-datagridview
The columns need "DataPropertyName" Property to bind to field name.
DataGridViewTextBoxColumn

ASP.NET: Listbox datasource and databind

I have an empty listbox on .aspx page
lstbx_confiredLevel1List
I am generating two lists programatically
List<String> l1ListText = new List<string>(); //holds the text
List<String> l1ListValue = new List<string>();//holds the value linked to the text
I want to load lstbx_confiredLevel1List list box on .aspx page with above values and text. So i am doing following:
lstbx_confiredLevel1List.DataSource = l1ListText;
lstbx_confiredLevel1List.DataTextField = l1ListText.ToString();
lstbx_confiredLevel1List.DataValueField = l1ListValue.ToString();
lstbx_confiredLevel1List.DataBind();
but it does not load the lstbx_confiredLevel1List with l1ListText and l1ListValue.
Any ideas?
Why don't you use the same collection as DataSource? It just needs to have two properties for the key and the value. You could f.e. use a Dictionary<string, string>:
var entries = new Dictionary<string, string>();
// fill it here
lstbx_confiredLevel1List.DataSource = entries;
lstbx_confiredLevel1List.DataTextField = "Value";
lstbx_confiredLevel1List.DataValueField = "Key";
lstbx_confiredLevel1List.DataBind();
You can also use an anonymous type or a custom class.
Assuming that you have already these lists and you need to use them as DataSource. You could create a Dictionary on the fly:
Dictionary<string, string> dataSource = l1ListText
.Zip(l1ListValue, (lText, lValue) => new { lText, lValue })
.ToDictionary(x => x.lValue, x => x.lText);
lstbx_confiredLevel1List.DataSource = dataSource;
You'd better used a dictionnary:
Dictionary<string, string> list = new Dictionary<string, string>();
...
lstbx_confiredLevel1List.DataSource = list;
lstbx_confiredLevel1List.DataTextField = "Value";
lstbx_confiredLevel1List.DataValueField = "Key";
lstbx_confiredLevel1List.DataBind();
Unfortunately the DataTextField and DataValueField are not used like that. They are the text representation of the fields they're supposed to show of the current item that's being databound in the DataSource.
If you had an object that held both text and value, you'd make a list of it and set that to datasource like this:
public class MyObject {
public string text;
public string value;
public MyObject(string text, string value) {
this.text = text;
this.value = value;
}
}
public class MyClass {
List<MyObject> objects;
public void OnLoad(object sender, EventArgs e) {
objects = new List<MyObjcet>();
//add objects
lstbx.DataSource = objects;
lstbx.DataTextField = "text";
lstbx.DataValueField = "value";
lstbx.DataBind();
}
}

Categories

Resources