I have a Database which has a table name CourseOffered that looks like this:
enter image description here
The only column i want to select (request to be returned) is Semester and add it to a dropdown list. Here's my code in the Repository Class.
public List<CoursesOffered> GetSemester()
{
List<CoursesOffered> SemestersName = null;
try
{
string sql = "SELECT DISTINCT Semester FROM CoursesOffered";
DataTable dt = _idac.GetManyRowsCols(sql, null);
SemestersName = DBList.ToList<CoursesOffered>(dt);
}
catch (Exception)
{
throw;
}
return SemestersName;
}
I'm calling GetManyRowsCols() from my DataAccess Class and I have a DBList and Entity Class that looks like this.
class DBList
{
public static List<T> ToList<T>(DataTable dt)
where T : IEntity, new()
{
List<T> TList = new List<T>();
foreach (DataRow dr in dt.Rows)
{
T tp = new T();
tp.SetFields(dr);
TList.Add(tp);
}
return TList;
}
}
public class EntityBase : IEntity
{
public void SetFields(DataRow dr)
{
// use reflection to set the fields from DataRow
Type tp = this.GetType();
foreach (PropertyInfo pi in tp.GetProperties())
{
if (null != pi && pi.CanWrite)
{
string nm = pi.PropertyType.Name.ToUpper();
if (nm.IndexOf("ENTITY") >= 0)
// In LINQ to SQL Classes, last properties are links to other tables
break;
if (pi.PropertyType.Name.ToUpper() != "BINARY")
pi.SetValue(this, dr[pi.Name], null);
}
}
}
}
This is how i'm trying to add it to the Drop down List,
private void CourseManagement_Load_1(object sender, EventArgs e)
{
try
{
List<CoursesOffered> semesterList = _ibusinessSemester.GetSemester();
cbCourseNumberDropDown.DataSource = semesterList;
cbCourseNumberDropDown.Refresh();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
but i keep getting an error that says Column 'CourseNum' Does not belong to the table
Based on your description, you want to convert one column to list.
Since I don't have the code about IEntity, I can not test your code.
However, I make the following code to make it.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var list = DBList.ToList<string>(table, "Semeter").Distinct().ToList();// the code I have modified
comboBox1.DataSource = list;
}
DataTable table = new DataTable();
private void Form1_Load(object sender, EventArgs e)
{
table.Columns.Add("Course",typeof(string));
table.Columns.Add("Semeter", typeof(string));
table.Columns.Add("MaxEncrollment", typeof(int));
table.Rows.Add("CPSC 410","Fall 2016",35);
table.Rows.Add("CPSC 411", "Fall 2016", 30);
table.Rows.Add("CPSC 440", "Fall 2016", 35);
table.Rows.Add("Math301", "Fall 2016", 35);
}
}
class DBList
{
public static List<T> ToList<T>(DataTable dt,string columnname)
{
List<T> TList = new List<T>();
TList = dt.AsEnumerable().Select(r => r.Field<T>(columnname)).ToList();
return TList;
}
}
Related
I have an IDictionary which holds an ever changing list of my class and I want to display the collection in a winforms UI probably using DataGridView or another control. I plan to have timer that refreshes the table every 1 to 2 seconds and a pause button. when any given row is clicked I need to be able to get the class from the dictionary or return the first field which be the key in the IDictionary.
So I create the IDictionary thus:
public static IDictionary<string, AClass> aList = new Dictionary<string, AClass>();
AClass is a simple collection of strings:
public class AClass
{
public string data1{ get; set; }
public string data2{ get; set; }
}
I add the AClass to the IDictionary thus:
if (!MainForm.aircraftList.ContainsKey(strMyData))
{
MainForm.aList[strMyData] = new AClass{ data1= strMyData};
}
How can I create the table with all the columns from AClass which are around 12 and rows from the IDictionary aList of which there is a variable number hovering around 100.
To possibly help you get started, below is code that uses a DataGridView to display data using a DataTable; a List<AClass> objects and also using a Dictionary<string, AClass>. As I said using a dictionary, you will have to do some extra steps to get the AClass Value from the dictionary. These extra steps indicate that the Dictionary is not necessarily the best data structure to use as a DataSource if there a multiple variables in your class.
The code below uses a DataGridView and four (4) buttons. There are buttons to display data into the DataGridView using a DataTable; a List<AClass> (added a key), and a Dictionary<string, AClass> and finally a button to clear the DataGridView
Hope this helps.
public Dictionary<string, AClass> DGVDictionary;
public DataTable DGVTable;
public List<AClass> DGVList;
public Form1() {
InitializeComponent();
}
private Dictionary<string, AClass> GetDictionary() {
Dictionary<string, AClass> dictionary = new Dictionary<string, AClass>();
for (int key = 0; key < 15; key++) {
AClass ac = new AClass();
ac.data1 = "data1" + key;
ac.data2 = "data2" + key;
dictionary.Add(key.ToString(), ac);
}
return dictionary;
}
private void CreateTable() {
DGVTable = new DataTable();
DGVTable.Columns.Add("key", typeof(int));
DGVTable.Columns.Add("data1", typeof(string));
DGVTable.Columns.Add("data2", typeof(string));
}
private void FillDataTable() {
for (int key = 0; key < 15; key++) {
AClass ac = new AClass();
ac.data1 = "data1" + key;
ac.data2 = "data2" + key;
DGVTable.Rows.Add(key, ac.data1, ac.data2);
}
}
private List<AClass> FillList() {
List<AClass> list = new List<AClass>();
for (int key = 0; key < 15; key++) {
AClass ac = new AClass();
ac.key = key;
ac.data1 = "data1" + key;
ac.data2 = "data2" + key;
list.Add(ac);
}
return list;
}
private void buttonDataTable_Click(object sender, EventArgs e) {
CreateTable();
FillDataTable();
dataGridView1.DataSource = DGVTable;
MessageBox.Show("DGV with a DataTable");
}
private void buttonList_Click(object sender, EventArgs e) {
DGVList = FillList();
dataGridView1.DataSource = DGVList;
MessageBox.Show("DGV with a List<AClass>");
}
private void buttonDictionary_Click(object sender, EventArgs e) {
DGVDictionary = GetDictionary();
dataGridView1.DataSource = DGVDictionary.ToList();
MessageBox.Show("DGV with a Dictionat<string, AClass>");
}
private void buttonClear_Click(object sender, EventArgs e) {
dataGridView1.DataSource = null;
}
Since you are using an IDictionary, the default binding mechanism will not be able to retrieve the individual entries nor the properties on the class AClass. You can create a custom BindingSource to handle these tasks.
The primary responsibility of custom BingSource is to supply a collection of ProperyDescriptors for the AClass type. These are retrieved using the TypeDescriptor.GetProperties Method. The BindingSource also needs to access the underlying DataSource items by index; this is handled in the indexer of the BindingSource.
To use this BindingSource, create an instance of it passing your IDictionary instance and then assign this BindingSource to the DataGridView's DataSource property.
internal class myBindingSource : BindingSource
{
private IDictionary<string, AClass> source;
private System.ComponentModel.PropertyDescriptorCollection props;
public myBindingSource(IDictionary<string, AClass> source)
{
this.source = source;
props = System.ComponentModel.TypeDescriptor.GetProperties(typeof(AClass));
this.DataSource = source;
}
public override System.ComponentModel.PropertyDescriptorCollection GetItemProperties(System.ComponentModel.PropertyDescriptor[] listAccessors)
{
return props;
}
public override object this[int index]
{
get {return this.source.Values.ElementAtOrDefault(index);}
set {}
}
public override bool AllowNew
{
get { return false; }
set{}
}
public override bool AllowEdit
{
get { return false; }
}
public override bool AllowRemove
{
get {return false;}
}
public override int Count
{
get {return ((this.source == null) ? -1 : this.source.Count);}
}
}
Edit: I have added an override of the Count property to the code to allow the the BindingSource.ResetBindings Method to work properly when called to force the bound control to re-read the values from the BindingSource. I have also updated the indexer code.
If you modify the custom BindingSource's underlying DataSource after assigning it as the DataGridView's DataSource, you will need to call the BindingSource.ResetBindings method for those changes to be reflected in the grid.
For Example:
private IDictionary<string, AClass> aList;
private myBindingSource bs;
public Form1()
{
InitializeComponent();
aList = new Dictionary<string, AClass>();
bs = new myBindingSource(aList);
dgv.DataSource = bs;
aList.Add("1", new AClass());
aList.Add("2", new AClass { data1 = "AA", data2 = "BB" });
bs.ResetBindings(false);
}
I have a list of objects on my main form - GUI - and while I can access that list using a foreach loop for example foreach (Employee emp in employees)
this allows me to access the employees within the list.
On the other form I have some code that requires it to access the list of employees.
So far I have tried to copy the private List<Employee> employees;
but it gives me a null reference exception obviously meaning that there's nothing in the list that has been copied over.
I'll provide a view of my code so you can have something to base your solution off:
Code
Main form
private List<Employee> employees;
public Form1()
{
InitializeComponent();
employees = new List<Employee>();
}
Employee e1 = new Employee(MemberJob.Employee, "Name", MemberSkills.CPlus);
Added this bit of code in case I need to send some variables into the form
private void btnAddJob_Click(object sender, EventArgs e)
{
CreateAJob newForm2 = new CreateAJob();
newForm2.ShowDialog();
}
**Additional Form code **
private string _jobName = "";
private string _jobDifficulty = "";
private string _skillRequired = "";
private int _shiftsLeft = 0;
private List<Employee> employees; // tried to copy this over but there's nothing in it
public CreateAJob()
{
InitializeComponent();
}
public CreateAJob(string _jobName, string _skillRequired, int _shiftsLeft)
{
this._jobName = JobName;
this._skillRequired = SkillRequired;
this._shiftsLeft = ShiftsLeft;
}
private void Distribute(string _jobName, int _shiftsLeft, string _skillsRequired)
{
foreach (Employee emp in employees)
{
while (emp.Busy == true)
{
if (emp.Busy == false && emp.Skills.ToString() == _skillRequired)
{
emp.EmployeeWorkload = _jobName;
emp.ShiftsLeft = _shiftsLeft;
}
... additional code to finish method
Create another constructor and pass the Employee list like this
CreateAJob form:
internal CreateAJob(List<Employee> employees)
: this() // Make sure the normal constructor is executed
{
this.employees = employees;
}
Main form:
private void btnAddJob_Click(object sender, EventArgs e)
{
CreateAJob newForm2 = new CreateAJob(employees);
newForm2.ShowDialog();
}
Ive been trying to change the background color of a row in the Compact Framework DataGrid and have found little success since the DataGrid on .NET CF is limited compared to its Windows Forms counterpart. My only bit of success in achieving my goal is I have now been able to change the background color of a single cell depending on its values. I couldnt manipulate the code I got from Googling since I am not that good in C#. However, this is the code that I have:
namespace GridColor
{
public delegate void CheckCellEventHandler(object sender, DataGridEnableEventArgs e);
public class DataGridEnableEventArgs : EventArgs
{
private int _column;
private int _row;
private bool _meetsCriteria;
public DataGridEnableEventArgs(int row, int col, bool val)
{
_row = row;
_column = col;
_meetsCriteria = val;
}
public int Column
{
get { return _column; }
set { _column = value; }
}
public int Row
{
get { return _row; }
set { _row = value; }
}
public bool MeetsCriteria
{
get { return _meetsCriteria; }
set { _meetsCriteria = value; }
}
}
public partial class ColumnStyle : DataGridTextBoxColumn
{
//public event CheckCellEventHandler CheckCellEquals;
public event CheckCellEventHandler CheckCellContains;
private int _col;
public ColumnStyle(int column)
{
_col = column;
}
protected override void Paint(Graphics g, Rectangle Bounds, CurrencyManager Source, int RowNum, Brush BackBrush, Brush ForeBrush, bool AlignToRight)
{
bool enabled = true;
if (CheckCellContains != null)
{
DataGridEnableEventArgs e = new DataGridEnableEventArgs(RowNum, _col, enabled);
CheckCellContains(this, e);
if (e.MeetsCriteria)
//g.DrawRectangle(new Pen(Color.Red, 2), Bounds.Y + 1, Bounds.Width - 2, Bounds.Height - 2);
BackBrush = new SolidBrush(Color.PaleGreen);
}
base.Paint(g, Bounds, Source, RowNum, BackBrush, ForeBrush, AlignToRight);
}
}
}
Now for my form, I have this:
namespace GridColor
{
public partial class Form1 : Form
{
DataSet ds;
SqlDataAdapter da;
private List<string> compareValues = new List<string>();
public Form1()
{
InitializeComponent();
try
{
addGridStyle(ref dataGrid1);
compareValues.Add("OK");
compareValues.Add("Filling");
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.ToString());
}
}
private void addGridStyle(ref DataGrid dg)
{
DataGridTableStyle dtStyle = new DataGridTableStyle();
dtStyle.MappingName = "Test";
string connString = "Data Source=192.168.2.16,1433;Initial Catalog=TestDB;User ID=sa;Password=ABC12abc;";
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT * FROM Test";
ds = new DataSet();
da = new SqlDataAdapter(cmd);
da.Fill(ds, "Test");
for (int i = 0; i < ds.Tables["Test"].Columns.Count; i++)
{
ColumnStyle myStyle = new ColumnStyle(i);
myStyle.MappingName = ds.Tables["Test"].Columns[i].ToString();
if (i == 1)
{
if (ds.Tables["Test"].Columns[i].DataType == System.Type.GetType("System.String"))
myStyle.CheckCellContains += new CheckCellEventHandler(myStyle_CheckCellContains);
}
dtStyle.GridColumnStyles.Add(myStyle);
}
dg.TableStyles.Add(dtStyle);
}
public void myStyle_CheckCellContains(object sender, DataGridEnableEventArgs e)
{
try
{
if (compareValues.Contains((string)dataGrid1[e.Row, e.Column]))
e.MeetsCriteria = true;
else
e.MeetsCriteria = false;
}
catch (Exception ex)
{
e.MeetsCriteria = false;
}
}
private void Form1_Load(object sender, EventArgs e)
{
dataGrid1.DataSource = ds.Tables["Test"];
}
}
}
In what part of my code should I change so that If a cell meets the criteria, its whole row will be colored instead of only it's own cell?
Alright I went back and found my code from years ago where I did this on the desktop, before the more advanced DataGridView came out, etc.
First of all there is this tutorial from Microsoft Customizing the Windows Forms DataGrid, which explains how to highlight an entire row.
I looked at my code and I had to add a custom column style for each column, fire an event to the main form which I handled, and then determined the proper color for that record. Then, I set the args.Color property and the DataGridColumn would draw the correct color. So yes, you have to actually have each column be your custom formatable class, then your application logic can handle the event, get the record data and determine the color
** Update: here's a simple example **
public partial class Form1 : Form
{
FormattableTextBoxColumn firstNameColumn = new FormattableTextBoxColumn();
FormattableTextBoxColumn lastNameColumn = new FormattableTextBoxColumn();
public Form1()
{
InitializeComponent();
// add first name col
firstNameColumn.MappingName = "FirstName";
dataGridTableStyle1.GridColumnStyles.Add(firstNameColumn);
firstNameColumn.SetCellFormat += new FormatCellEventHandler(ColumnSetCellFormat);
// add last name col
lastNameColumn.MappingName = "LastName";
lastNameColumn.SetCellFormat += new FormatCellEventHandler(ColumnSetCellFormat);
dataGridTableStyle1.GridColumnStyles.Add(lastNameColumn);
// This just sets up a dummy data source, since I don't have a database in this example
List<PersonTest> peopleList = new List<PersonTest>();
peopleList.Add(new PersonTest
{
FirstName = "Alan",
LastName = "QQQQQ",
HighlightPerson = true
});
peopleList.Add(new PersonTest
{
FirstName = "John",
LastName = "Smith",
HighlightPerson = false
});
BindingSource peopleDataSource = new BindingSource();
peopleDataSource.DataSource = peopleList;
dataGridTableStyle1.MappingName = peopleDataSource.GetListName(null);
dataGrid1.DataSource = peopleDataSource;
}
// I'll cache this brush in the form, just make sure to dispose it (see designer.cs disposing)
SolidBrush highlightBrush = new SolidBrush(Color.Yellow);
// here is the event you can handle to determine the color of your row!
private void ColumnSetCellFormat(object sender, DataGridFormatCellEventArgs e)
{
if ((e.Source.List[e.Row] as PersonTest).HighlightPerson)
e.BackBrush = highlightBrush;
}
// example test class
public class PersonTest
{
public String FirstName { get; set; }
public String LastName { get; set; }
public bool HighlightPerson { get; set; }
}
}
And the custom data grid column
public class FormattableTextBoxColumn : DataGridTextBoxColumn
{
public event FormatCellEventHandler SetCellFormat;
protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight)
{
DataGridFormatCellEventArgs e = new DataGridFormatCellEventArgs(rowNum, source);
e.ForeBrush = foreBrush;
e.BackBrush = backBrush;
OnSetCellFormat(e);
base.Paint(g, bounds, source, rowNum, e.BackBrush, e.ForeBrush, alignToRight);
}
private void OnSetCellFormat(DataGridFormatCellEventArgs e)
{
FormatCellEventHandler handler = SetCellFormat;
if (handler != null)
handler(this, e);
}
}
You'll also need this DataGridCellEventArgs.cs
public delegate void FormatCellEventHandler(object sender, DataGridFormatCellEventArgs e);
public class DataGridFormatCellEventArgs : EventArgs
{
public int Row;
public CurrencyManager Source;
public Brush BackBrush;
public Brush ForeBrush;
public DataGridFormatCellEventArgs(int row, CurrencyManager manager)
{
this.Row = row;
this.Source = manager;
}
}
Here's an example project for you:
DataGridTest.zip
I haven't worked with CF, but I thought I would throw this out there... If you can access the cell, wouldn't the row be it's NamingContainer? If so you could drill up to the row and apply a style or add an attribute with a CSS class.
I want to implement stack in my program by Genericx. I have a textbox and button to add elements in stack, a dropdownlist box and a button to bind total stack in dropdownlist box.
I have generic class and the code is below:
[Serializable]
public class myGenClass<T>
{
private T[] _elements;
private int _pointer;
public myGenClass(int size)
{
_elements = new T[size];
_pointer = 0;
}
public void Push(T item)
{
if (_pointer > _elements.Length - 1)
{
throw new Exception("Stack is full");
}
_elements[_pointer] = item;
_pointer++;
}
public T Pop()
{
_pointer--;
if (_pointer < 0)
{
throw new Exception("Stack is empty");
}
return _elements[_pointer];
}
public T[] myBind()
{
T[] showall = new T[_pointer];
Array.Copy(_elements,showall, _pointer);
T[] newarray = showall;
Array.Reverse(showall);
return showall;
}
}
and my .cs page is below:
public partial class _Default : System.Web.UI.Page
{
myGenClass<int> mystack = new myGenClass<int>(25);
protected void Button1_Click(object sender, EventArgs e)
{
mystack.Push(Int32.Parse(TextBox1.Text));
//DropDownList1.Items.Add(mystack.Pop().ToString());
TextBox1.Text = string.Empty;
TextBox1.Focus();
}
protected void Button2_Click(object sender, EventArgs e)
{
//string[] db;
//db = Array.ConvertAll<int, string>(mystack.myBind(), Convert.ToString);
DropDownList1.DataSource = mystack.myBind();
DropDownList1.DataBind();
}
}
but when I bind the datasource property of dropdownlist box to generic type return array (i.e. myBind()), it shows empty... Please help..
I haven't looked at the myGenClass type, but are you persisting the instance of myGenClass between requests? Each button click would post to a new instance of the _default class which initializes with an empty stack. If the stack isn't persisted between requests, it will contain its default values.
I'm using C#, Windows Forms, .NET 3.5 SP1
I have a DataGridView with a lot of columns that I don't know about until run-time (i.e. I don't know I need a Foo column until run-time). To get data into and out of the cells, I'm thinking about the following architecture.
Am I on the right track, or am I missing something easier?
public interface ICustomColumn
{
object Format (DataGridView dgv, DataGridViewCellFormattingEventArgs e);
void Validate (DataGridView dgv, DataGridViewCellValidatingEventArgs e);
}
public class CustomDataGridView : DataGridView
{
protected override void OnCellFormatting (DataGridViewCellFormattingEventArgs e)
{
ICustomColumn col = Columns [e.ColumnIndex].Tag as ICustomColumn;
if ( col != null )
e.Value = col.Format (this, e);
base.OnCellFormatting (e);
}
protected override void OnCellValidating (DataGridViewCellValidatingEventArgs e)
{
ICustomColumn col = Columns [e.ColumnIndex].Tag as ICustomColumn;
if ( col != null )
col.Validate (this, e);
base.OnCellValidating (e);
}
}
class FooColumn : ICustomColumn
{
public FooColumn (Dictionary <RowData, Foo> fooDictionary)
{ this.FooDictionary = fooDictionary; }
// Foo has a meaningful conversion to the column type (e.g. ToString () for a text column
protected object Format (DGV dgv, DGVCFEA e)
{ return FooDictionary [(RowData) dgv.Rows[e.RowIndex].DataBoundItem]; }
// Foo has a meaningful way to interpret e.FormattedValue
void Validate (DGV dgv, DGVCVEA e)
{ FooDictionary [(RowData) dgv.Rows[e.RowIndex].DataBoundItem].Validate (e.FormattedValue); }
}
void CreateFooColumn (DataGridView dgv)
{
dgv.Columns.Add (new DataGridViewTextBoxColumn () { Tag = new FooColumn (fooDictionary) });
}
Another approach would be to use reflection.
To set up the DataGridView:
private void SetUpDataGridView()
{
// Create the columns based on the data in the album info - get by reflection
var ai = new AlbumInfo();
Type t = ai.GetType();
dataTable.TableName = t.Name;
foreach (PropertyInfo p in t.GetProperties())
{
var columnSpec = new DataColumn();
// If nullable get the underlying type
Type propertyType = p.PropertyType;
if (IsNullableType(propertyType))
{
var nc = new NullableConverter(propertyType);
propertyType = nc.UnderlyingType;
}
columnSpec.DataType = propertyType;
columnSpec.ColumnName = p.Name;
dataTable.Columns.Add(columnSpec);
}
}
The helper method:
private bool IsNullableType(Type theType)
{
return (theType.IsGenericType &&
theType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
}
To populate the DataGridView:
private void AddToGrid(AlbumInfo info)
{
// Add album info to table - add by reflection
Type t = info.GetType();
var row = new object[t.GetProperties().Length];
int index = 0;
foreach (PropertyInfo p in t.GetProperties())
{
row[index++] = p.GetValue(info, null);
}
dataTable.Rows.Add(row);
dataGridView.ClearSelection();
}