I want to have a drop down list with 12 choices.
I found that ComboBox is what I need (if there is a better control kindly tell me).
I dragged and drop a combo box into a panel using VS2012 and then clicked on the left arrow that appears on the combo box. The following wizard shows:
As you can see, I am just able to type the name of the choice but not the value of it.
My question is how to get the value of these choices?
What I have tried
I built an array with the same length as the choices, so when the user selects any choice, I get the position of that choice and get the value from that array.
Is there a better way?
You need to use a datatable and then select the value from that.
eg)
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Description", typeof(string));
dt.Load(reader);
//Setting Values
combobox.ValueMember = "ID";
combobox.DisplayMember = "Description";
combobox.SelectedValue = "ID";
combobox.DataSource = dt;
You can then populate your datatable using:
dt.Rows.Add("1","ComboxDisplay");
Here, the DisplayMember(The dropdown list items) are the Descriptions and the Value is the ID.
You need to include a 'SelectedIndexChanged' Event on your combobox (If using VS then double click the control in Design Mode) to get the new values. Something like:
private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
int ID = Combobox.ValueMember;
string Description = ComboBox.DisplayMember.ToString();
}
You can then use the variables in the rest of your code.
You cannot use the wizard to store values and text. To store DisplayText/Value pair the combobox needs to be connected to some data.
public class ComboboxItem
{
public string DisplayText { get; set; }
public int Value { get; set; }
}
There are two properties on the combobox - DisplayMember and ValueMember. We use these to tell the combobox that - show whats in DisplayMember and the actual value is in ValueMember.
private void DataBind()
{
comboBox1.DisplayMember = "DisplayText";
comboBox1.ValueMember = "Value";
ComboboxItem item = new ComboboxItem();
item.DisplayText = "Item1";
item.Value = 1;
comboBox1.Items.Add(item);
}
To get the value -
int selectedValue = (int)comboBox1.SelectedValue;
I got problem while try to filter LongListSelector with textbox in WP7. I want to
Group A:
AABBCC
AABBDD
AABBEE
Group B:
BBCCDD
BBDDEE
BBEEFF
When i type the text "BBCC" in textbox, the list will be display
Group A:
AABBCC
Group B:
BBCCDD
I have sucessed implement this filter with ListBox.
ItemSearch = new CollectionViewSource();
Item = new ObservableCollection<ItemViewModel>();
ItemSearch.Source = Item;
For filter i use this code
ItemSearch.View.Filter = FilterList
private bool FilterList(object obj)
{
return ...... ;
}
But in LongListSelector, i don't know how to filter the list with model view like
Item = new ObservableCollection<Group<ItemViewModel>>()
Anyone have any suggest for me.
Thanks
I have a WPF User Control with a ListView in it that is created based on the DataSet that is passed to it:
public void PopulateList(DataSet ds) {
listView.View = CreateGridViewColumns(ds.Tables[0]);
listData.DataContext = ds.Tables[0];
}
private GridView CreateGridViewColumns(DataTable dt) {
// Create the GridView
var gv = new GridView {AllowsColumnReorder = true};
// Create the GridView Columns
foreach (DataColumn item in dt.Columns) {
var gvc = new GridViewColumn
{
DisplayMemberBinding = new Binding(item.ColumnName),
Header = item.ColumnName,
Width = Double.NaN
};
gv.Columns.Add(gvc);
}
return gv;
}
Now I create the user control in code and call it's PopulateList with the appropriate dataset and this is where the problems are starting:
If I pass in a dataset that was created from a call to the database the list view shows all the data but if i pass in a DataSet that i created in code the ListView shows the Columns but will not show the data
//This is a function that hides the DB call return type is DataSet
var dsPatientSmokingStatusHistory = DataRepository.PatientSmokingStatusProvider.GetHistory(PatientId);
//radGridViewPatientSmokingStatus.DataSource = dsPatientSmokingStatusHistory.Tables[0];
var dt = new DataTable();
string c1 = "Date".PadLeft(23).PadRight(23);
string c2 = "Status".PadLeft(20).PadRight(50);
dt.Columns.Add(c1);
dt.Columns.Add(c2);
int i = 0;
foreach (DataRow row in dsPatientSmokingStatusHistory.Tables[0].Rows) {
var dataRow = dt.NewRow();
dataRow[c1] = ((DateTime)row["Date"]).ToString("MM/dd/yyyy");
dataRow[c2] = row["Status"].ToString();
dt.Rows.Add(dataRow);
dt.Rows[i].AcceptChanges();
i++;
}
DataSet ds = new DataSet();
dt.TableName = "Table";
ds.Tables.Add(dt);
ds.AcceptChanges();
smokingStatusGrid.GridWidth = 455;
smokingStatusGrid.GridHight = 97;
//This line does not show data
smokingStatusGrid.PopulateGrid(ds);
//This line will show data
smokingStatusGrid.PopulateGrid(dsPatientSmokingStatusHistory);
Is there a difference between these two datasets that i don't know about that is preventing me from databinding to it?
Also the user control is being used as an ElementHost in a WinForms application (not sure if this makes a difference)
Your code says:
DisplayMemberBinding = new Binding(item.ColumnName)
This binding constructor takes a string paramter which as per MSDN is "The initial Path for the binding" and is of datatype System.Windows.PropertyPath. I guess, since system tries to find a property with the same name in your class, and your string (item.ColumnName) has spaces at start, it runs into a problem (properties can't start with a space).
Would recommend you to take off the padding that you are doing in column name of your table. Apply any padding/margins in the Header of your GridView.
In the code below, the combo box named "ConnectionType" shows the selected item, but one cannot change the selected item (it seems like there is only one item in the combo box). If I comment out the line
typeCol.DataPropertyName = "ConnectionTypeName";
then the combo box is selectable, but the correct item is not selected, of course. What am I doing wrong??
Thanks.
private void LoadConnectionsGrid()
{
_dc = new EnterpriseEntities();
dataGridViewConnections.AutoGenerateColumns = false;
dataGridViewConnections.DataSource = _dc.Connection.Include("ConnectionType");
DataGridViewComboBoxColumn typeCol =
(DataGridViewComboBoxColumn)dataGridViewConnections.Columns["ConnectionType"];
typeCol.DataPropertyName = "ConnectionTypeName";
var qry = from c in _dc.ConnectionType
select c.Type;
typeCol.DataSource = qry;
DataGridViewTextBoxColumn nameCol =
(DataGridViewTextBoxColumn)dataGridViewConnections.Columns["ConnectionName"];
nameCol.DataPropertyName = "Name";
DataGridViewTextBoxColumn connStrCol =
(DataGridViewTextBoxColumn)dataGridViewConnections.Columns["ConnectionString"];
connStrCol.DataPropertyName = "ConnectionString";
}
Ultimately, I could not get data binding, the entity framework, and comboboxes to play nice and I just created the data grid brute force (code below). This means that I handle inserts, updates and deletes by hand.
private void LoadConnectionsGrid()
{
DataGridViewComboBoxColumn typeCol =
(DataGridViewComboBoxColumn)dataGridViewConnections.Columns["ConnectionType"];
var qry = from c in _dc.ConnectionType
select c.Type;
typeCol.DataSource = qry;
dataGridViewConnections.Rows.Clear();
foreach (Connection conn in _dc.Connection.Include("ConnectionType"))
{
dataGridViewConnections.Rows.Add(conn.Name,
conn.ConnectionType.Type, conn.ConnectionString);
}
}
This really seems like a bug to me, but perhaps some databinding gurus can enlighten me? (My WinForms databinding knowledge is quite limited.)
I have a ComboBox bound to a sorted DataView. When the properties of the items in the DataView change such that items are resorted, the SelectedItem in my ComboBox does not keep in-sync. It seems to point to someplace completely random. Is this a bug, or am I missing something in my databinding?
Here is a sample application that reproduces the problem. All you need is a Button and a ComboBox:
public partial class Form1 : Form
{
private DataTable myData;
public Form1()
{
this.InitializeComponent();
this.myData = new DataTable();
this.myData.Columns.Add("ID", typeof(int));
this.myData.Columns.Add("Name", typeof(string));
this.myData.Columns.Add("LastModified", typeof(DateTime));
this.myData.Rows.Add(1, "first", DateTime.Now.AddMinutes(-2));
this.myData.Rows.Add(2, "second", DateTime.Now.AddMinutes(-1));
this.myData.Rows.Add(3, "third", DateTime.Now);
this.myData.DefaultView.Sort = "LastModified DESC";
this.comboBox1.DataSource = this.myData.DefaultView;
this.comboBox1.ValueMember = "ID";
this.comboBox1.DisplayMember = "Name";
}
private void saveStuffButton_Click(object sender, EventArgs e)
{
DataRowView preUpdateSelectedItem = (DataRowView)this.comboBox1.SelectedItem;
// OUTPUT: SelectedIndex = 0; SelectedItem.Name = third
Debug.WriteLine(string.Format("SelectedIndex = {0:N0}; SelectedItem.Name = {1}", this.comboBox1.SelectedIndex, preUpdateSelectedItem["Name"]));
this.myData.Rows[0]["LastModified"] = DateTime.Now;
DataRowView postUpdateSelectedItem = (DataRowView)this.comboBox1.SelectedItem;
// OUTPUT: SelectedIndex = 2; SelectedItem.Name = second
Debug.WriteLine(string.Format("SelectedIndex = {0:N0}; SelectedItem.Name = {1}", this.comboBox1.SelectedIndex, postUpdateSelectedItem["Name"]));
// FAIL!
Debug.Assert(object.ReferenceEquals(preUpdateSelectedItem, postUpdateSelectedItem));
}
}
To clarify:
I understand how I would fix the simple application above--I only included that to demonstrate the problem. My concern is how to fix it when the updates to the underlying data rows could be happening anywhere (on another form, perhaps.)
I would really like to still receive updates, inserts, deletes, etc. to my data source. I have tried just binding to an array of DataRows severed from the DataTable, but this causes additional headaches.
Just add a BindingContext to the ComboBox :
this.comboBox1.DataSource = this.myData.DefaultView;
this.comboBox1.BindingContext = new BindingContext();
this.comboBox1.ValueMember = "ID";
this.comboBox1.DisplayMember = "Name";
By the way, try not keeping auto-generated names for your widgets (comboBox1, ...), it is dirty. :-P
The only promising solution I see at this time is to bind the combo box to a detached data source and then update it every time the "real" DataView changes. Here is what I have so far. Seems to be working, but (1) it's a total hack, and (2) it will not scale well at all.
In form declaration:
private DataView shadowView;
In form initialization:
this.comboBox1.DisplayMember = "Value";
this.comboBox1.ValueMember = "Key";
this.shadowView = new DataView(GlobalData.TheGlobalTable, null, "LastModified DESC", DataViewRowState.CurrentRows);
this.shadowView.ListChanged += new ListChangedEventHandler(shadowView_ListChanged);
this.ResetComboBoxDataSource(null);
And then the hack:
private void shadowView_ListChanged(object sender, ListChangedEventArgs e)
{
this.ResetComboBoxDataSource((int)this.comboBox1.SelectedValue);
}
private void ResetComboBoxDataSource(int? selectedId)
{
int selectedIndex = 0;
var detached = new KeyValuePair<int, string>[this.shadowView.Count];
for (int i = 0; i < this.shadowView.Count; i++)
{
int id = (int)this.shadowView[i]["ID"];
detached[i] = new KeyValuePair<int, string>(id, (string)this.shadowView[i]["Name"]);
if (id == selectedId)
{
selectedIndex = i;
}
}
this.comboBox1.DataSource = detached;
this.comboBox1.SelectedIndex = selectedIndex;
}
Must detach event handler in Dispose:
this.shadowView.ListChanged -= new ListChangedEventHandler(shadowView_ListChanged);
Your example sorts the data on the column it updates. When the update occurs, the order of the rows changes. The combobox is using the index to keep track of it's selected items, so when the items are sorted, the index is pointing to a different row. You'll need to capture the value of comboxBox1.SelectedItem before updating the row, and set it back once the update is complete:
DataRowView selected = (DataRowView)this.comboBox1.SelectedItem;
this.myData.Rows[0]["LastModified"] = DateTime.Now;
this.comboBox1.SelectedItem = selected;
From an architecture perspective, the SelectedItem must be cleared when rebinding the DataSource because the DataBinder don't know if your SelectedItem will persist or not.
From a functional perspective, the DataBinder may not be able to ensure that your SelectedItem from you old DataSource is the same in your new DataSource (it can be a different DataSource with the same SelectedItem ID).
Its more an application feature or a custom control feature than a generic databinding process.
IMHO, you have theses choices if you want to keep the SelectedItem on rebind :
Create a reusable custom control / custom DataBinder with a persistance option which try to set the SelectedItem with all your data validation (using a DataSource / item identification to ensure the item validity)
Persist it specifically on your Form using the Form/Application context (like ViewState for ASP.NET).
Some controls on the .NET market are helping you by rebinding (including selections) the control from their own persisted DataSource if the DataSource is not changed and DataBind not recalled. That's the best pratice.