I'm trying to get something set up in a DataGridView. It seems like this should be pretty straightforward but I'm having trouble. I want to display three columns:
CodeID
CodeName
ComboBox with DisplayMember of TypeName, ValueMember of TypeID
I want to be able to select from all possible values of TypeName. Here's my dilemma:
If I load all of this into one DataTable and set the DataGridView as the DataSource, I can display the existing TypeName for that record, but the combo box will not include any other values. If I set the DataSource for the DataGridViewComboBoxColumn to a separate DataTable that includes all possible TypeNames, the existing value is not displayed.
DataGridView is really annoying to work with so either a solution for this or a viable alternative would be appreciated.
Edit: it appears the issue is caused by my wanting to have a separate item for DisplayMember and ValueMember. The following works, if I don't worry about setting the ID as the ValueMember:
var typeColumn = new DataGridViewComboBoxColumn
{
DataSource = typeList,
DisplayMember = "Type",
ValueMember = "Type",
DataPropertyName = "Type"
}
If I do the following, the right types are selected, but I can't change the selection in the combo box:
var typeColumn = new DataGridViewComboBoxColumn
{
DataSource = typeList,
DisplayMember = "Type",
ValueMember = "TypeID",
DataPropertyName = "TypeID"
}
If I use the following I get a FormatException error as it's trying to populate:
var typeColumn = new DataGridViewComboBoxColumn
{
DataSource = typeList,
DisplayMember = "Type",
ValueMember = "TypeID",
DataPropertyName = "Type"
}
edit: typeList is a simple DataTable populated by the following:
SELECT DISTINCT IT.InsuranceTypeID, IT.[Type]
FROM InsuranceType IT
WHERE IT.ClientID = #ClientID
ORDER BY [Type]
I had a similar (I think) issue, and the solution for me was to set the DataSource for the DataGridViewComboBoxColumn before setting the DataSource for the DataGridView.
In my case, my DataSources are a List<T> and a BindingList<T> respectively but it should work the same with DataTables:
DataGridViewComboBoxColumn categoryColumn = (DataGridViewComboBoxColumn)_ItemsGrid.Columns["CategoryID"];
categoryColumn.DataSource = categories;
_ItemsGrid.DataSource = items;
Ok, I came up with an example ClientInfo and InsuranceDetails that I think might mimic what you are trying to do. Let me know if these details arent quite right. This example will populate the DataGridViewComboBox and set the value based on the InsuranceDetails (specifically at: InsurDetailz = all_insurance_types[2])
public partial class Form1 : Form
{
private ClientInfo _myClient;
private BindingList<InsuranceDetails> all_insurance_types =
new BindingList<InsuranceDetails>();
public Form1()
{
InitializeComponent();
DataGridView grid = new DataGridView();
grid.Dock = DockStyle.Fill;
grid.AutoGenerateColumns = true;
all_insurance_types.Add(new InsuranceDetails(1, "Health"));
all_insurance_types.Add(new InsuranceDetails(2, "Home"));
all_insurance_types.Add(new InsuranceDetails(3, "Life"));
var col = new DataGridViewComboBoxColumn
{
DataSource = all_insurance_types,
HeaderText = "Insurance Type",
DataPropertyName = "InsurDetailz",
DisplayMember = "ItType",
ValueMember = "Self",
};
_myClient = new ClientInfo {
InsurDetailz = all_insurance_types[2], Name = "Jimbo" };
grid.Columns.Add(col);
grid.DataSource = new BindingList<ClientInfo> { _myClient };
this.Controls.Add(grid);
this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
}
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// make sure its updated
InsuranceDetails c = _myClient.InsurDetailz;
string name = _myClient.Name;
// Place breakpoint here to see the changes in _myClient
throw new NotImplementedException();
}
}
class ClientInfo
{
public string Name { get; set; }
public InsuranceDetails InsurDetailz { get; set; }
}
class InsuranceDetails
{
public int InsuranceTypeID { get; set; }
public String ItType { get; set; }
public InsuranceDetails Self { get { return this; } }
public InsuranceDetails(int typeId, String itType)
{
this.InsuranceTypeID = typeId;
this.ItType = itType;
}
}
Related
For my current problem I want to create a DataGridView and use a list of objects as the datasource.
The problem I'm having is that the objects itself contains two lists, which are supposed to fill combobox columns. The DataGridView should contain three columns, each corresponding to my sample object below. The first column is a simple text column, while the other two are combo box columns.
Currently I'm receiving the error:
System.ArgumentException: The value DataGridViewComboBoxCell is invalid.
I've been looking for other solutions on SO, but can't seem to get it right.
public class SampleObject
{
public SampleObject(string name, IList<TimeSpan> startTimes, IList<Activity> completedActivities)
{
this.Name = name;
this.StartTimes = startTimes;
this.CompletedActivities = completedActivities;
}
public string Name { get; }
public IList<TimeSpan> StartTimes { get; }
public IList<Activity> CompletedActivities { get; }
}
Activity object:
public class Activity
{
public Activity(string activityName)
{
ActivityName = activityName;
}
public string ActivityName { get; }
public override string ToString()
{
return ActivityName;
}
}
And the code for adding the columns to my grid:
private void FillGrid()
{
sampleGrid.AutoGenerateColumns = false;
var columnName = new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(SampleObject.Name),
HeaderText = "Name",
Width = 160,
ReadOnly = true
};
sampleGrid.Columns.Add(columnName);
var columnStartTimes = new DataGridViewComboBoxColumn()
{
ValueType = typeof(TimeSpan),
HeaderText = "StartTimes",
Width = 120,
ReadOnly = false
};
sampleGrid.Columns.Add(columnStartTimes);
var columnCompletedActivities = new DataGridViewComboBoxColumn
{
ValueType = typeof(Activity),
HeaderText = "Completed activities",
Width = 120,
ReadOnly = false
};
sampleGrid.Columns.Add(columnCompletedActivities);
}
And populating the grid:
List<SampleObject> myObjects = GetSampleObjectsBasedOnValue(value);
sampleGrid.DataSource = myObjects;
FillComboBoxesInDGV(myObjects);
Method for filling the comboboxes:
private void FillComboBoxesInDGV(IList<SampleObject> sampleObjects)
{
for (int i = 0; i < sampleObjects.Count; i++)
{
DataGridViewRow row = sampleGrid.Rows[i];
var firstBox = row.Cells[1] as DataGridViewComboBoxCell;
firstBox.DataSource = sampleObjects[i].StartTimes;
var secondBox = row.Cells[2] as DataGridViewComboBoxCell;
secondBox.DataSource = sampleObjects[i].CompletedActivities;
}
}
I still not sure why you do not keep the original IList<string> CompletedActivities instead of introducing the Activity class? Using the list in this manner worked in my tests.
However, since you did use the class Activity, I would assumed it would work as expected. However, I ran into the same issue you described. After some hair pulling (and I don’t have much), I found some help from the SO post… DataGridViewComboBoxCell Binding - “value is not valid” … this was not obvious to me and I am guessing you also, thinking that overriding the Activity class’s ToString method would be enough. This is obviously not the case.
It appears that setting the DataGridViewComboBoxColumns’s ValueMember property to the name of the property in the Activity class should eliminate the error.
var columnCompletedActivities = new DataGridViewComboBoxColumn {
HeaderText = "Completed activities",
ValueMember = "ActivityName",
Width = 120,
};
sampleGrid.Columns.Add(columnCompletedActivities);
I hope this works for you.
I am trying to bind list of objects as a data source for data grid, on of the columns should be a combobox where selected value of the combobox should be value in the filed related in my object.
This is how I set the datasource of gridview :
var s = new BindingSource();
s.DataSource = dbc.GetResults();
dataGridView.DataSource = s;
dbc.GetResults(id) will return List and myClass which is
Class myClass
{
int productID{ set; get; }
int price{ set; get; }
}
And then I am initialling data grid view like this :
dataGridView.AutoGenerateColumns = false;
DataGridViewComboBoxColumn dgvc = new DataGridViewComboBoxColumn();
dgvc.HeaderText = "Products";
dgvc.DataSource = dbc.GetAllProducts();
dgvc.DisplayMember = "Name";
dgvc.ValueMember = "ID";
dataGridView.Columns.Add(dgvc);
dataGridView.Columns[0].DataPropertyName = "productID";
and dbc.GetAllProducts() returns list and :
Class Products
{
int ID;
string Name;
}
Here I expect that data grids get productID from myClass and pass it to combobox on each row, then the combo box gets the productID matches it with the ID in products column and then shows the Name property of it.
when I run this I get the error that DataGridViewComboBoxCell value is not valid.
You should use a dictionary to bind your property to your combo :
private Dictionary<int, string> cbProductVals= new Dictionary<int, string>();
Populate your dictionary with product objects and bind it on your grid column.
In your datagrid AutoGeneratingColumn event :
if (e.PropertyName == "productID")
{
DataGridComboBoxColumn cb = new DataGridComboBoxColumn();
e.Column = cb;
cb.ItemsSource = cbProductVals;
cb.DisplayMemberPath = "Value";
cb.SelectedValuePath = "Key";
cb.SelectedValueBinding = new Binding("productID");
e.Column.Header = "product price";
}
I've created a datagrid, which will create a column whenever a user decides to, with that in mind what should I bind the column to ?
I've tried creating a class :
public class Column
{
public List<List<string>> DataList { get; set; }
public Column()
{
DataList = new List<List<string>>();
}
}
and binding the column like so :
DataGridTextColumn item = new DataGridTextColumn
{
Header = "Index1",
Width = 200,
Binding = new Binding("DataList[0]") { Mode = BindingMode.TwoWay }
};
but it seems to not work.
I'm sorry if this question is common, and if so I apologize, but I've tried googling it and can't get it to work.
listBox2.DataSource = listBox1.SelectedItems;
listBox2.DisplayMember = "Descripcion";
listBox2.ValueMember = "Id";
After using the above code, I am not able to select one by one. Help!
some one please post codes to remove too
First, you have to define a model for your Listbox :
public class MyModel
{
public int Id { get; set; }
public string Description { get; set; }
}
After you can set items like this :
listBox2.Items.Add(new MyModel() { Id = 1, Description = "description" });
listBox2.DisplayMember = "Description";
listBox2.ValueMember = "Id";
And now, your listbox will show the description property. If you select an item, the SelectedValue in listbox2 will be the value of id property
if (listBox1.SelectedItem != null)
{
listBox2.Items.Add(listBox1.SelectedItem);
}
listBox2.DisplayMember = "Descripcion";
listBox2.ValueMember = "Id";
this is working fine....
Use SqlDataAdapter to dump data into the listbox from the data source and based on the data selected in one of the listbox use listbox.add() method, Use a "for" loop to dump the data from one listbox to another using an index.
for(int i=0; i<listBox1.SelectedItems.Count; i++)
{
listBox2.Items.Add(listbox1.SelectedItems[i].toString());
}
How can I bind a text file to a datagrid with C# WPF? The idea is to have a row in the text file to show as a row in datagrid.
I don't think you can bind text directly to a datagrid
What you can do however is bind an objet to a datagrid
create an objet representing your text file.
-- content --
text1, param1, param2
text2, param1, param2
class OneLine{
string text {get;set;}
string param { get;set; }
...
}
You can then bind those objects to the datagrid with a BindingList, which is mostly a List. The magic lies in the Properties of the object. The BindingList will try to get each property of the object and display them in the grid.
BindingList<OneLine> myList = new BindingList<OneLine>();
myList.Add(oneObject);
DataGrid myGrid = new DataGrid();
myGrid.DataSource = myList;
In my project I use the following approach
Create class that represent row in text file for example
public class cls_syslog_record
{
public DateTime? f1 {get;set;}
public string f2 {get;set;}
public string f3 {get;set;}
public string f4 {get;set;}
}
Create IEnumerable that used as source for DataGrid
public IEnumerable<cls_syslog_record> get_line_seq_text()
{
cls_mvs_syslog_parser parser = new cls_mvs_syslog_parser();
foreach (string record_line in File.ReadLines(this.filename))
{
cls_syslog_record text_record = parser.parse_syslog_text(record_line);
if (text_record == null)
{
continue;
}
yield return text_record;
}
}
set my IEnumerable object as source
static private DataGrid make_text_viewer(string p_filename)
{
logger.Debug("start");
DataGrid table_viewer;
cls_file_line_seq fl_seq = new cls_file_line_seq(p_filename);
table_viewer = new DataGrid();
table_viewer.CanUserAddRows = false;
table_viewer.CanUserDeleteRows = false;
table_viewer.Columns.Add(create_column("Date Time", "timestamp"));
table_viewer.Columns.Add(create_column("LPAR Name", "lpar_name"));
table_viewer.Columns.Add(create_column("JOB ID", "job_id"));
table_viewer.Columns.Add(create_column("Message", "message"));
table_viewer.HeadersVisibility = DataGridHeadersVisibility.All;
table_viewer.ItemsSource = fl_seq.get_line_seq_text();
return table_viewer;
}
Then setup binding
static private DataGridColumn create_column(string header, string p_property_name)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Header = header;
column.Binding = new Binding(p_property_name);
return column;
}