c# datagridview red cross - c#

I have a datagridview and datatable. I use the datatable as a datasource of the datagridview. I add and update the data using threads as below. And if I am done with the data I remove it. But two times there were big red x in front of the datagridview. I couldn't find out why? Below are my sample.
Note: This does not always occur, I got this error only two times but I need to handle! Thanks in advance.
Thread listData;
DataTable dt = new DataTable();
Form1_load()
{
dataGridview.DataSource = dt;
}
public void ListData()
{
foreach(var item in data)
{
if(item.delete)
{
var row = dt.Rows.Find(item.id);
if(row != null) { row.Delete();}
continue;
}
listData = new Thread(delegate() { InsertOrUpdateData(item.Id); });
listData.Start(); listData.Join();
}
}
public void InserOrUpdateData(int id)
{
// Here I retrieve some data from database
// and insert or update to the datatable
// like dt.Rows.Add(fields) and dt.Rows.Find(id)["fieldName"] = "new Value"
}

You need to use Invoke method
if (gridView.InvokeRequired)
gridView.Invoke(new MethodInvoker(() => gridView.DataSource = YOUR_DATASOURCE));
else
gridView.DataSource = YOUR_DATASOURCE;

Related

How to add data from Firebase to DataGridView using FireSharp

I just want to retrieve data from a Firebase to DataGridView. The code I have is retrieving data already, however, it's retrieving everything to the same row instead of creating a new one. I'm a beginner in coding, so I really need help with that.
I read online that Firebase doesn't "Count" data, so it'd be needed to create a counter, so each time I add or delete data, an update would be needed. I did it and it's working. I created a method to load the data.
private async Task firebaseData()
{
int i = 0;
FirebaseResponse firebaseResponse = await client.GetAsync("Counter/node");
Counter_class counter = firebaseResponse.ResultAs<Counter_class>();
int foodCount = Convert.ToInt32(counter.food_count);
while (true)
{
if (i == foodCount)
{
break;
}
i++;
try
{
FirebaseResponse response2 = await client.GetAsync("Foods/0" + i);
Foods foods = response2.ResultAs<Foods>();
this.dtGProductsList.Rows[0].Cells[0].Value = foods.menuId;
this.dtGProductsList.Rows[0].Cells[1].Value = foods.name;
this.dtGProductsList.Rows[0].Cells[2].Value = foods.image;
this.dtGProductsList.Rows[0].Cells[3].Value = foods.price;
this.dtGProductsList.Rows[0].Cells[4].Value = foods.discount;
this.dtGProductsList.Rows[0].Cells[5].Value = foods.description;
}
catch
{
}
}
MessageBox.Show("Done");
}
OBS: A DataTable exists already(dataTable), there's a DataGridView too which has columns(ID,Name, Image, Price, Discount, Description), which match the number and order given to the .Cells[x]. When the Form loads, dtGProductsList.DataSource = dataTable; I tried replacing [0] for [i].
I expect the data that is beeing retrieved to be set to a new row and not to the same, and to not skip rows. I'm sorry if it's too simple, but I can't see a way out.
I Faced the same problem and here is mu solution :
Counter_class XClass = new Counter_class();
FirebaseResponse firebaseResponse = await client.GetAsync("Counter/node");
string JsTxt = response.Body;
if (JsTxt == "null")
{
return ;
}
dynamic data = JsonConvert.DeserializeObject<dynamic>(JsTxt);
var list = new List<XClass >();
foreach (var itemDynamic in data)
{
list.Add(JsonConvert.DeserializeObject<XClass >
(((JProperty)itemDynamic).Value.ToString()));
}
// Now you have a list you can loop through to put it at any suitable Visual
//control
foreach ( XClass _Xcls in list)
{
Invoke((MethodInvoker)delegate {
DataGridViewRow row(DataGridViewRow)dg.Rows[0].Clone();
row.Cells[0].Value =_Xdcls...
row.Cells[1].Value =Xdcls...
row.Cells[2].Value =Xdcls...
......
dg.Insert(0, row);
}

Why is my added datatable rows disappearing from the table as soon as my foreach loop closes?

I am adding rows dynamically, as well as columns, into a datatable. The rows add as far as I can see, but as soon as I run the code nothing appears. It's like it's refreshing the table once the foreach loop finishes? Rookie question. Sorry.
summaryTable.Columns.Add("StationName", typeof(string));
summaryTable.Columns.Add("DepartmentCode", typeof(string));
var row = summaryTable.NewRow();
foreach (var item in issuesByClass)
{
row.SetField("StationName", item.StationName);
row.SetField("DepartmentCode", item.DepartmentCode);
foreach (var itemClass in item.Lookup)
{
if (!itemClassLookup.ContainsKey(itemClass.Key)) continue;
var itemClassValue = itemClassLookup[itemClass.Key];
// Ensure column hasn't been added before
if (!summaryTable.Columns.Contains(itemClassValue))
{
summaryTable.Columns.Add(itemClassValue, typeof(decimal));
}
row.SetField(itemClassValue, itemClass.Sum());
}
if (!summaryTable.Columns.Contains("GrandTotal"))
{
summaryTable.Columns.Add("GrandTotal", typeof(decimal));
}
row.SetField("GrandTotal", item.GrandTotal);
}
return summaryTable;
Small windows form app example:
public Form1()
{
this.InitializeComponent();
this.Test();
}
public void Test()
{
var dt = new DataTable();
var subs = new[] { "Sub 1", "Sub 2", "Sub 3" };
var row = dt.NewRow();
foreach (var sub in subs)
{
if (!dt.Columns.Contains("Subs"))
{
dt.Columns.Add("Subs");
}
row.SetField("Subs", sub);
}
dt.Columns.Add("Test");
row.SetField("Test", 1M);
this.dataGridView1.DataSource = dt;
}
NewRow() doesn't add the row. You need to do that manually at the bottom of the loop via .Rows.Add(row). You should also probably create the NewRow() per iteration.
foreach (var item in issuesByClass)
{
var row = summaryTable.NewRow();
// ...
summaryTable.Rows.Add(row);
}

Host custom controls on DataGridView which populate data from DataTable

I have a DataGridView, which I populate data for automatically, as per the following:
MySqlDataAdapter adapter = new MySqlDataAdapter(query, connString);
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
adapter.Fill(table);
e.Result = table;
So no explicit dataGridView1.Columns.Add() calls at all.
I want to make columns of a specific type host a determined control, for example, a column of DateTime type must host a DateTimePicker. Imaginary code example:
private void dataGridView1_ColumnCreating(object sender, DataGridViewColumnEventArgs e)
{
if(e.Column.ValueType is DateTime)
{
e.Column = new MyCalendarColumn();
}
}
Or create class that inherit from DataGridView and add my own column type support.
How can I do that? I have searched for an event fired at column's creation time or how to add my own column type by inheriting from DataGridView or so. But I could find only examples where the columns are add manually and not filling date using a DataTable.
If you are looking for a native approach as suggested by MSDN here, first of all you should set the AutoGenerateColumns property of your DataGridView to false:
dataGridView1.AutoGenerateColumns = false;
Then you need to add columns that you want to appear in the DataGridView like this. (Please note that Id and Name and BirthDate are the fictional names of the columns in your table, so change them to your actual columns names):
SMySqlDataAdapter adapter = new MySqlDataAdapter(query, connString);
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
adapter.Fill(table);
e.Result = table;
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = table;
DataGridViewColumn(table.Columns[0].ColumnName, new DataGridViewTextBoxColumn());
DataGridViewColumn(table.Columns[1].ColumnName, new DataGridViewTextBoxColumn());
DataGridViewColumn(table.Columns[2].ColumnName, new DataGridViewTextBoxColumn());
DataGridViewColumn(table.Columns[3].ColumnName, new CalendarColumn());//Create the CalendarColumn class as MSDN suggested
private void DataGridViewColumn(string colName, DataGridViewColumn colType)
{
colType.DataPropertyName = colName;
colType.HeaderText = colName;
colType.Name = colName;
dataGridView1.Columns.Add(colType);
}
Please note that CalendarColumn is a class that is in the MSDN link.
As I understand the question, you have a DataGridView with AutoGenerateColumns=true, just set the DataSource property to a DataTable and want to be able to customize the auto generated columns.
Let me start with the fact that this is not possible. DataGridView does not provide any way of customizing the auto generated columns - no event, virtual method, attribute etc. It's all or nothing. And by default it creates only 3 types of columns - check box, image and text, with something like this:
private static DataGridViewColumn DefaultColumnFactory(Type type)
{
if (type == typeof(bool)) return new DataGridViewCheckBoxColumn(false);
if (type == typeof(CheckState)) return new DataGridViewCheckBoxColumn(true);
if (typeof(Image).IsAssignableFrom(type)) return new DataGridViewImageColumn();
var imageConverter = TypeDescriptor.GetConverter(typeof(Image));
if (imageConverter.CanConvertFrom(type)) return new DataGridViewImageColumn();
if (!typeof(System.Collections.IList).IsAssignableFrom(type)) return new DataGridViewTextBoxColumn();
return null;
}
So far so good. Fortunately it's not hard to build that functionality ourselves. First, we will add another "default" column type creation based on the property type inside the above method. And second, we will allow the caller to "override" the default behavior for each data source property, thus enable creaing combo boxes and other type of columns that need additional initialization.
In order to that, we will encapsulate it in a custom extension method with the following signature:
public static void Bind(
this DataGridView view,
object dataSource,
string dataMember = "",
Func<PropertyDescriptor, DataGridViewColumn> columnFactory = null)
The dataSource and dataMember represent the corresponding DataGridView properties, while the columnFactory delegate is the extension point.
Here is the full implementation:
public static class DataGridViewExtensions
{
public static void Bind(this DataGridView view, object dataSource, string dataMember = "", Func<PropertyDescriptor, DataGridViewColumn> columnFactory = null)
{
var columns = new List<DataGridViewColumn>();
var properties = ListBindingHelper.GetListItemProperties(dataSource, dataMember, null);
for (int i = 0; i < properties.Count; i++)
{
var property = properties[i];
if (!property.IsBrowsable) continue;
var column = (columnFactory != null ? columnFactory(property) : null) ?? DefaultColumnFactory(property.PropertyType);
if (column == null) continue;
column.DataPropertyName = property.Name;
column.Name = property.Name;
column.HeaderText = !string.IsNullOrEmpty(property.DisplayName) ? property.DisplayName : property.Name;
column.ValueType = property.PropertyType;
column.ReadOnly = property.IsReadOnly;
columns.Add(column);
}
view.DataSource = null;
view.Columns.Clear();
view.AutoGenerateColumns = false;
view.Columns.AddRange(columns.ToArray());
view.DataMember = dataMember;
view.DataSource = dataSource;
}
private static DataGridViewColumn DefaultColumnFactory(Type type)
{
if (type == typeof(bool)) return new DataGridViewCheckBoxColumn(false);
if (type == typeof(CheckState)) return new DataGridViewCheckBoxColumn(true);
if (typeof(Image).IsAssignableFrom(type)) return new DataGridViewImageColumn();
var imageConverter = TypeDescriptor.GetConverter(typeof(Image));
if (imageConverter.CanConvertFrom(type)) return new DataGridViewImageColumn();
// Begin custom default factory
if (type == typeof(DateTime)) return new CalendarColumn();
// End custom default factory
if (!typeof(System.Collections.IList).IsAssignableFrom(type)) return new DataGridViewTextBoxColumn();
return null;
}
}
Note: The CalendarColumn used is from How to: Host Controls in Windows Forms DataGridView Cells MSDN example.
The above method works with DataTable, DataSet as well as every data source type (IList, IBindingList, IListSource etc.) supported by the DataGridView control.
The usage with DataTable is simply as that:
dataGridView.Bind(dataTable);
Demo::
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
var dg = new DataGridView { Dock = DockStyle.Fill, Parent = form };
dg.Bind(GetData());
Application.Run(form);
}
static DataTable GetData()
{
var dt = new DataTable();
dt.Columns.AddRange(new[]
{
new DataColumn("Id", typeof(int)),
new DataColumn("Name"),
new DataColumn("Description"),
new DataColumn("StartDate", typeof(DateTime)),
new DataColumn("EndDate", typeof(DateTime)),
});
dt.Rows.Add(1, "Foo", "Bar", DateTime.Today, DateTime.Today.AddDays(7));
return dt;
}
}
Try the ColumnAdded event, have a look at the definition following:
public event DataGridViewColumnEventHandler ColumnAdded
This event occurs when a column is added to the DataGridView control.
Here is an example:
private void DataGridView1_ColumnAdded(Object sender, DataGridViewColumnEventArgs e) {
if(e.Column.ValueType is DateTime) {
e.Column = new MyCalendarColumn();
}
}
Let me know if this helps.

Bind list to gridview C#

I'm trying to bind list to gridview. Situation is like this: I take data from .txt file, later I put it inside first list List<Mycolumns>. I have data in list (with 3 separated columns) that I created. I am taking data from one of the columns called System_Description. Now I would like to show this data in gridview, but only thing that I get is length of each row. How should I fix it? Here is my code.
private void button7_Click(object sender, EventArgs e)
{
List<MyColumns> list = new List<MyColumns>();
OpenFileDialog openFile1 = new OpenFileDialog();
openFile1.Multiselect = true;
if (openFile1.ShowDialog() != DialogResult.Cancel)
{
foreach (string filename in openFile1.FileNames)
{
using (StreamReader sr = new StreamReader(filename))
{
string line;
while ((line = sr.ReadLine()) != null)
{
string[] _columns = line.Split(",".ToCharArray());
MyColumns mc = new MyColumns();
mc.Time = _columns[0];
mc.System_Description = _columns[1];
mc.User_Description = _columns[2];
list.Add(mc);
}
}
}
DataTable ListAsDataTable = BuildDataTable<MyColumns>(list);
DataView ListAsDataView = ListAsDataTable.DefaultView;
this.dataGridView1.DataSource = view = ListAsDataView;
this.dataGridView1.AllowUserToAddRows = false;
dataGridView1.ClearSelection();
}
List<string> description = list.Select(x => x.System_Description).ToList<string>();
this.dataGridView2.DataSource = description;
}
class MyColumns
{
public string Time { get; set; }
public string System_Description { get; set; }
public string User_Description { get; set; }
}
EDIT:
I've read that DataBind() works for Web form, my app is desktop app. What should I do now?
I managed to solve this problem. I did it this way, maybe it will help someone. You can use DataTable and then bind DT to gridview.
DataTable dt = new DataTable();
dt.Columns.Add("values");
foreach(string items in description)
{
DataRow row = dt.NewRow();
dt.Rows.Add(items);
}
this.dataGridView2.DataSource = dt;
this.dataGridView1.DataSource = new BindingSource(list);
You are only selecting the System_Description field using the following statement:
List<string> description = list.Select(x => x.System_Description).ToList<string>();
Then you are binding this list to the gridview:
this.dataGridView2.DataSource = description;
If you want to bind the whole data to the gridview; just bind the list as a datasource.
this.dataGridView2.DataSource = list;
this.dataGridView2.DataBind();
Use this
this.dataGridView1.DataSource = description;
and add Bound field in gridview on aspx.
<asp:BoundField DataField="System_Description" HeaderText="System_Description"></asp:BoundField>

Changing the DateTimeMode of a few columns in a datatable after it has been populated with data

I need to change the DateTimeMode of some columns in an already populated dataset. (I don't want to change it before it gets populated as it would mean making changes in several methods throughtout the application.)
Here's the stmt I am using (for a single column):
copy.Tables[0].Columns["DateColName"].DateTimeMode = DataSetDateTime.Utc;
However, it throws an error that you can't change the DateTimeMode if the dataset contains data. So the solution I am thinking is creating a clone of the dataset, changing the DateTimeMode of required columns and then re-loading data back.
DataSet copy = dsdata.Clone();
copy.Tables[0].Columns["DateColName"].DateTimeMode = DataSetDateTime.Utc;
copy.Load(dsdata.CreateDataReader(), LoadOption.OverwriteChanges, "TableName");
Is there a better way of doing this??
try this, cheers
private void SetUtcDateTime()
{
var ds = new DataSet { Locale = CultureInfo.InvariantCulture };
foreach (DataTable source in DataSet.Tables)
{
bool containsDate = false;
var target = source.Clone();
foreach (DataColumn col in target.Columns)
{
if (col.DataType == System.Type.GetType("System.DateTime"))
{
col.DateTimeMode = DataSetDateTime.Utc;
containsDate = true;
}
}
if (containsDate)
{
foreach (DataRow row in source.Rows)
target.ImportRow(row);
ds.Tables.Add(target);
}
else
{
ds.Tables.Add(source.Copy());
}
}
DataSet.Tables.Clear();
DataSet = ds;
}
where 'DataSet' is a public property on your object.
I just ran into this issue also. The DateTimeMode cannot be changed once the dataset has been filled, so the only solution I could find is to re-create the column with the correct DateTimeMode.
This code might help, you don't have to clone the entire dataset, just remove the column, modify it and add it back to the table.
private static void SetDateTimeMode(DataTable table, DataColumn col, DataSetDateTime mode)
{
var rowValues = new object[table.Rows.Count];
for (int i = 0; i < rowValues.Length; i++)
{
// ignore deleted rows
if (table.Rows[i].RowState == DataRowState.Deleted) continue;
rowValues[i] = table.Rows[i][col];
}
// we must remove and re-add the row because DateTimeMode cannot be
// changed on a column that has data.
table.Columns.Remove(col);
col.DateTimeMode = mode;
table.Columns.Add(col);
// write back each row value
for (int i = 0; i < rowValues.Length; i++)
{
// ignore deleted rows
if (table.Rows[i].RowState == DataRowState.Deleted) continue;
var rowState = table.Rows[i].RowState;
table.Rows[i][col] = rowValues[i];
// preserve unchanged rowstate
if (rowState == DataRowState.Unchanged)
table.Rows[i].AcceptChanges();
}
}
You just have to be careful, when you copy the row values back to the column to preserve the RowState.

Categories