I am trying to render a simple List into a Grid like
var sr = new BindingSource();
sr.DataSource = str;
dataGridView1.DataSource = sr;
I am not getting any error but not able to display the list in Grid. Here is the entire code
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Enum
{
public enum Sex {Male, Female, Other };
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
List<Sex> str = new List<Sex>();
str.Add(Sex.Female);
str.Add(Sex.Male);
var sr = new BindingSource();
sr.DataSource = str;
dataGridView1.DataSource = sr;
}
}
}
DataGridView cannot bind to a list of primitive values (like int, decimal, DateTime, enum, string etc.) because it requires a list containing objects with properties.
The easiest way is to use LINQ projection to an anonymous type with single property like this (BindingSource is not needed at all):
private void button1_Click(object sender, EventArgs e)
{
List<Sex> str = new List<Sex>();
str.Add(Sex.Female);
str.Add(Sex.Male);
dataGridView1.DataSource = str.Select(value => new { Sex = value }).ToList();
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class Person
{
public string Name { get; set; }
public string Lastname { get; set; }
public Sex Sex { get; set; }
}
public enum Sex { Male, Female, Other };
private void button1_Click(object sender, EventArgs e)
{
BindingList<Person> persons = new BindingList<Person>();
persons.Add(new Person() { Name = "Joe", Lastname = "Doe" , Sex = Sex.Male});
persons.Add(new Person() { Name = "Nancy", Lastname = "Foo" , Sex = Sex.Female});
dataGridView1.DataSource = persons;
}
}
I don't think you can bind an enum to GridView. This is what I could get working
public class Person
{
public Sex Gender { get; set; }
}
You need to use BindingList as list does not implement IBindingList
var list = new List<Person>()
{
new Person { Gender = Sex.Male, },
new Person { Gender = Sex.Female, },
};
var bindingList = new BindingList<Person>(list);
var source = new BindingSource(bindingList, null);
dataGridView1.DataSource = source;
Related
why this loop Return A Duplicate Result
Duplicate Result When Trying To Fill Datagridview From Checklistbox With ValueMember And DisplayMember
private void btn_ShowDetails_Click(object sender, EventArgs e)
{
for (int i = 0; i < clb_SubItems.Items.Count; i++)
{
if (clb_SubItems.GetItemChecked(i))
{
foreach (var item in clb_SubItems.CheckedItems.OfType<SP_SelectDriverItem_Result>())
{
dgv_BOQItems.Rows.Add(item.SubCostItemID, clb_SubItems.GetItemText(clb_SubItems.Items[i]));
}
}
}
}
You might consider setting up the DataGridView with a preset columns and set the DataSource to a List.
Example
Backing class, ToString will be the DisplayMember of the CheckedListBox.
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString() => Name;
}
Mocked data
public class Mocked
{
public static List<Company> Companies => new List<Company>()
{
new Company() {Id = 1, Name = "A"},
new Company() {Id = 2, Name = "B"},
new Company() {Id = 3, Name = "C"},
new Company() {Id = 4, Name = "D"},
};
}
Extension method to get checked companies in a CheckedListBox
public static class CheckedListBoxExtensions
{
public static List<T> CheckedList<T>(this CheckedListBox source)
=> source.Items.Cast<T>()
.Where((item, index) => source.GetItemChecked(index))
.Select(item => item)
.ToList();
}
Form
One CheckedListBox
One DataGridView with columns setup for Id and Name in Company class
One button to get checked items and add each one if not currently in the DataGridView.
Code
public partial class Form1 : Form
{
private readonly BindingSource bindingSource = new BindingSource();
protected BindingList<Company> bindingList = new BindingList<Company>();
public Form1()
{
InitializeComponent();
dataGridView1.AutoGenerateColumns = false;
}
private void Form1_Load(object sender, EventArgs e)
{
bindingSource.DataSource = bindingList;
dataGridView1.DataSource = bindingSource;
checkedListBox1.DataSource = Mocked.Companies;
}
private void PopulateGridButton_Click(object sender, EventArgs e)
{
var result = checkedListBox1.CheckedList<Company>();
if (result.Count <= 0) return;
foreach (var company in result)
{
if (!bindingList.Contains(company))
{
bindingList.Add(company);
}
}
}
}
I am designing a winform in which I have a datagridview with predefined columns, the first column is a combobox and rest 3 columns are textboxes.
I am not binding datagridview to any datasource since the user is going to fill the last column "Qty", all I want to achieve is, when user clicks on the combobox it should show 3 columns from the database table (item code, item name and uom) and when user select any particular "item code" corresponding "item name" and "uom" should be displayed in second column and third column of the datagridview. Likewise the user should be able to enter as many row as he require. After data entry the data will be saved in a table named "purchase requisition".
I have not done any coding so far and have designed only the form.
See if the following will provide base code to roll with. There are several classes to mock up data and a extension method which should be move to own files.
DataGridView was not configured in the designer, just in code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace UnboundDataGridViewComboBox
{
public partial class Form1 : Form
{
private ComboBox _cbo;
private string _comboColumnName = "ItemCodeColumn";
private List<Item> _items => Mocked.Items;
public Form1()
{
InitializeComponent();
Shown += OnShown;
}
private void OnShown(object sender, EventArgs e)
{
var column1 = new DataGridViewComboBoxColumn
{
DataSource = _items.Select(x => x.ItemCode).ToArray(),
DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing,
Name = _comboColumnName,
HeaderText = "Item Code",
SortMode = DataGridViewColumnSortMode.NotSortable
};
var column2 = new DataGridViewTextBoxColumn
{
Name = "ItemNameColumn",
HeaderText = "Item Name"
};
var column3 = new DataGridViewTextBoxColumn
{
Name = "UomColumn",
HeaderText = "UOM"
};
var column4 = new DataGridViewTextBoxColumn
{
Name = "QuanityColumn",
HeaderText = "Quanity"
};
ItemsDataGridView.Columns.AddRange(column1, column2, column3, column4);
ItemsDataGridView.EditingControlShowing += DataGridView1OnEditingControlShowing;
}
private void DataGridView1OnEditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (!ItemsDataGridView.CurrentCell.IsComboBoxCell()) return;
if (ItemsDataGridView.Columns[ItemsDataGridView.CurrentCell.ColumnIndex].Name != _comboColumnName) return;
_cbo = e.Control as ComboBox;
_cbo.SelectedIndexChanged -= ItemCodeColumnComboSelectionChanged;
_cbo.SelectedIndexChanged += ItemCodeColumnComboSelectionChanged;
}
private void ItemCodeColumnComboSelectionChanged(object sender, EventArgs e)
{
DataGridViewComboBoxEditingControl control = sender as DataGridViewComboBoxEditingControl;
Item item = _items.FirstOrDefault(x => x.ItemCode == control.EditingControlFormattedValue.ToString());
if (item == null)
{
return;
}
ItemsDataGridView.CurrentRow.Cells[1].Value = item.Name;
ItemsDataGridView.CurrentRow.Cells[2].Value = item.UOM;
}
}
#region Place classes into their own files
public static class Extensions
{
public static bool IsComboBoxCell(this DataGridViewCell sender)
=> sender.EditType != null &&
sender.EditType == typeof(DataGridViewComboBoxEditingControl);
}
// represents a table in a database
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string ItemCode { get; set; }
public string UOM { get; set; }
public override string ToString() => ItemCode;
}
class Mocked
{
// simulate data from database
public static List<Item> Items => new List<Item>()
{
new Item() {Id = 1,Name = "P1", ItemCode = "A100", UOM = "Q1"},
new Item() {Id = 2,Name = "P2", ItemCode = "A200", UOM = "W1"},
new Item() {Id = 3,Name = "P3", ItemCode = "A300", UOM = "B1"},
new Item() {Id = 4,Name = "P4", ItemCode = "A400", UOM = "H1"}
};
}
#endregion
}
I have a dataset created in "visual studio".
when i open for with data grid it fills the data grid and i can see the data i have entered.
whan loading the form again the data is gone
I have used this code to create new data row.
foreach (var ticket in esTicket)
{
databaseDataSet1.ES.AddESRow(ticket.esNum, ticket.title, ticket.link, ticket.open, ticket.escalated, ticket.downTime, ticket.startWork, ticket.endWork, ticket.toolID, ticket.toolType, ticket.openedBy);
}
and also:
foreach (var ticket in esTicket)
{
databaseDataSet1.ES.Rows.Add(ticket.esNum, ticket.title, ticket.link, ticket.open, ticket.escalated, ticket.downTime, ticket.startWork, ticket.endWork, ticket.toolID, ticket.toolType, ticket.openedBy);
}
the inserted data just does not show
It seems that your esTicket is a list. If so, I suggest that you could use another way to fill the datagridview without using dataset.
Code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
List<Student> list = new List<Student>();
list.Add(new Student { Age=12,Name="Test1",Title="Hello" });
list.Add(new Student { Age = 13, Name = "Test2", Title = "Hi" });
list.Add(new Student { Age = 15, Name = "Test5", Title = "Greeting" });
dataGridView1.DataSource = list;
}
private void button1_Click(object sender, EventArgs e)
{
List<Student> list = (List<Student>)dataGridView1.DataSource;
list.Add(new Student { Age = 16, Name = "Test6", Title = "Hello1" });
dataGridView1.DataSource = null;
dataGridView1.DataSource = list;
}
}
public class Student
{
public int Age { get; set; }
public string Name { get; set; }
public string Title { get; set; }
}
Result:
I am working in WinForms with a ListBox. I have questions about the DisplayMember property.
If I understand correctly - if I do not provide a DisplayMember property then the ListBox uses the objects ToString() method. However if I specify DisplayMember then the ListBox will show the specified property of the objects in the collection.
However, is it possible to add some static text?
What I mean is, if my Property returns a first name such as "Dave", is there a way for me to prefix this with "First Name: Dave" through using the DisplayMember property or some other means?
I ask because the collection of objects I am working with is from a class I don't have control over - therefore I can't easily create a new property or override the ToString() method.
I thought briefly about extending the class and providing my own overridden ToString() method. However in my case I would need the ability to cast the parent class to the child class (which is not possible). I thought shortly about writing a static method that would accept the parent class as a parameter, copy its contents and return the child class but I just don't know if that is considered clean and best practice.
Any thoughts?
The best way to do this is to create a separate wrapper object that you'll use. You can make that object generic and use it on every place where you have that same problem. What I'll do is something like that.
namespace Demo
{
using System;
public class Person
{
public string FirstName { get; set;}
public string LastName { get; set; }
}
public class ItemWrapper<T>
{
public T Item { get; private set; }
public string DisplayMember { get; private set; }
public ItemWrapper(T item, Func<T, string> displayFactory) {
if (item == null) {
throw new ArgumentNullException("item");
}
if (displayFactory == null) {
throw new ArgumentNullException("displayFactory");
}
this.Item = item;
this.DisplayMember = displayFactory(item);
}
public override string ToString() {
return this.DisplayMember;
}
/// <summary>
/// This method is just an example and should be removed
/// </summary>
public static void Example() {
var person1 = new Person() { FirstName = "Johny", LastName = "Bravo" };
var person2 = new Person() { FirstName = "Johny2", LastName = "Bravo" };
var person3 = new Person() { FirstName = "Johny3", LastName = "Bravo" };
var item1 = new ItemWrapper<Person>(person1, p => "First Name: " + p.FirstName);
var item2 = new ItemWrapper<Person>(person2, delegate(Person p) { return "First Name: " + p.FirstName; });
var item3 = new ItemWrapper<Person>(person3, DisplayFactory);
}
private static string DisplayFactory(Person p) {
return "First Name" + p.FirstName;
}
}
}
Lets say that you have a form with a ListBox on it. Then you can use it like that:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var p1 = new Person() {FirstName = "Johny", LastName = "Bravo"};
var p2 = new Person() {FirstName = "Alex", LastName = "Flo"};
var list = new List<ItemWrapper<Person>>
{
new ItemWrapper<Person>(p1, p => p.FirstName),
new ItemWrapper<Person>(p2, p => p.FirstName)
};
var bs = new BindingSource(list, "Item");
this.listBox1.DataSource = bs;
}
private void button1_Click(object sender, EventArgs e)
{
var person = this.listBox1.SelectedValue as Person;
}
}
}
Suppose i have
class Person
{
public int Id {get;set;}
public string Name {get;set;}
public List<Person> All {get;set;}
public Person()
{
}
public List<Person> GetAll()
{
//fills the list with person and returns
}
}
and that i have:
class Address
{
public int PersonId {get;set;}
public string theAddress {get;set;}
public List<Address> All {get;set;}
//constructor, etc
public List<Address> GetAll()
{
//fills the address list and returns
}
}
What im trying to do is exactly the following:
//filling the maintemplate with data
radGridView1.DataMember = "Person";
radGridView1.DataSource = new Person().GetAll();
//address template, the child one
GridViewTemplate template = new GridViewTemplate();
template.DataSource = new Address().GetAll();
template.DataMember = "Address";
radGridView1.MasterTemplate.Templates.Add(template);
//now the relation between those 2 classes
GridViewRelation relation = new GridViewRelation(radGridView1.MasterTemplate);
relation.ChildTemplate = template;
relation.RelationName = "PersonAddress"; //just a name
relation.ParentColumnNames.Add("Id"); //field to be "joined" to create the relation
relation.ChildColumnNames.Add("PersonId"); //same as above
radGridView1.Relations.Add(relation);
and what i get is exactly a gridview with a "+" sign by the side of each Person
The problem is, the "child" grid is EMPTY, and if i try to add data (its, by default, allowed with an empty constructor in the class) i throw an NullArgumentException
Any ideas? im almost giving up. My problem is: i use custom objects on all projects, its not like "yo use datasets, its ready to use etc", i know that, but i would like to know if there's a way to use CUSTOM OBJECTS, or if im done and should try datasets...
Thanks guys
It looks like you are using the WinForms implementation. If that's right, then this works for me fine. Please give this a go
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Telerik.WinControls.UI;
namespace RadGridView_Hierarchy_CS
{
public partial class Form1 : Form
{
private List<Person> people = new List<Person>();
private List<Address> addresses = new List<Address>();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
FillPeople();
FillAddresses();
radGridView1.DataSource = people;
GridViewTemplate template = new GridViewTemplate();
template.DataSource = addresses;
radGridView1.MasterTemplate.Templates.Add(template);
GridViewRelation relation = new GridViewRelation(radGridView1.MasterTemplate);
relation.ChildTemplate = template;
relation.RelationName = "PersonAddress";
relation.ParentColumnNames.Add("Id");
relation.ChildColumnNames.Add("PersonId");
radGridView1.Relations.Add(relation);
}
private void FillPeople()
{
Person richard = new Person();
richard.Name = "Richard";
richard.Id = 1;
people.Add(richard);
Person bob = new Person();
bob.Name = "Bob";
bob.Id = 2;
people.Add(richard);
Person mike = new Person();
mike.Name = "Mike";
mike.Id = 3;
people.Add(mike);
}
private void FillAddresses()
{
Address house1 = new Address();
house1.PersonId = 1;
house1.Id = 1;
house1.theAddress = "1 The Mews";
addresses.Add(house1);
Address house2 = new Address();
house2.PersonId = 2;
house2.Id = 2;
house2.theAddress = "2 The Mews";
addresses.Add(house2);
}
}
class Person
{
public int Id {get;set;}
public string Name {get;set;}
public Person()
{
}
}
class Address
{
public int Id { get; set; }
public int PersonId {get;set;}
public string theAddress {get;set;}
public Address()
{
}
}
}
stumbled over you post when searching for a solution to this, so ill add my solution in case someone needs it ... ( using version Q1 2011 ).
in some initialisation method of your UC/Grid you could do something like
//setup the template
GridViewTemplate subtemplate = new GridViewTemplate();
subtemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
subtemplate.EnableFiltering = false;
subtemplate.EnableGrouping = false;
subtemplate.AutoGenerateColumns = false;
//define / add the cols
GridViewTextBoxColumn atextcol = new GridViewTextBoxColumn("Name");
//further properties of atextcol
//add the cols to the template
subtemplate.Columns.Add(atextcol);
//add the template to the grid
thegrid.Templates.Add(subtemplate);
//add a HierarchyDataProvider && subscribe to the RowSourceNeeded-Event
subtemplate.HierarchyDataProvider = new GridViewEventDataProvider(subtemplate);
thegrid.RowSourceNeeded += new GridViewRowSourceNeededEventHandler(thegrid_RowSourceNeeded);
then, in the eventhandler fill the row/rows
protected void thegrid_RowSourceNeeded(object sender, GridViewRowSourceNeededEventArgs e)
{
e.Template.Rows.Clear();
patentdata cparent = e.ParentRow.DataBoundItem as patentdata;
foreach (subdataobject sub in parentdata.subs)
{
GridViewRowInfo row = e.Template.Rows.NewRow();
row.Tag = sub;
row.Cells["Name"].Value = sub.Name;
e.SourceCollection.Add(row);
}
}
so, that would be it. critics ?