I have a custom control that I created and it was working when everything was single thread. It does update with the first message before I send it off to the interface.
I have a datatable:
DataTable dtNew = new DataTable(szTableName); // to add into a dataset later
dtNew.Columns.Add("EventDate",typeof(string));
dtNew.Columns.Add("Function",typeof(string));
dtNew.Columns.Add("IsError",typeof(bool));
dtNew.Columns.Add("LongMessage",typeof(string));
dtNew.Columns.Add("Message",typeof(string));
dtNew.Columns.Add("Process",typeof(string));
dtNew.Columns.Add("RecID",typeof(string));
dtNew.Columns.Add("Thread",typeof(string));
dtNew.Columns.Add("UserName", typeof(string));
ProcessStatus NewPanel = new ProcessStatus(); // create custom control
NewPanel.SetDataSource(ref dtNew); // sets the datasource to the datatable
dsLogs.Tables.Add(dtNew); // add datatable into dataset
Custom control receives the datasource so it should be bound:
public partial class ProcessStatus : UserControl
{
public ProcessStatus()
{
InitializeComponent();
}
public void SetProcess(string szProcName)
{
lblProcess.Text = "Process: " + szProcName;
}
public void SetThread(string szThreadName)
{
lblThreadNum.Text = "Thread: " + szThreadName;
}
public void SetDataSource(ref DataTable dtTable)
{
dgvProcess.DataSource = dtTable;
dgvProcess.Columns["EventDate"].Visible = false;
//dgvProcess.Columns["Function"].Visible = false;
dgvProcess.Columns["IsError"].Visible = false;
dgvProcess.Columns["LongMessage"].Visible = false;
//dgvProcess.Columns["Message"].Visible = false;
dgvProcess.Columns["Process"].Visible = false;
dgvProcess.Columns["RecID"].Visible = false;
dgvProcess.Columns["Thread"].Visible = false;
dgvProcess.Columns["UserName"].Visible = false;
dgvProcess.Columns["Function"].Width = 80;
dgvProcess.Columns["Message"].Width = 200;
}
// only show first line
private void dgvProcess_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
if (dgvProcess.RowCount > 0)
dgvProcess.FirstDisplayedScrollingRowIndex = dgvProcess.RowCount - 1;
}
}
When the datatable is updated with a new message, that message is not updated in the custom control (in the datagrid). Again, this did work on single thread so I'm probably not doing something right to allow this to update crossthreads or something like that...
Related
private void btnLoad_click(object sender, EventArgs e)
{
string filename = txtfile.Text;
progressBar.Visible = true;
progressBar.Style = ProgressBarStyle.Marquee;
DataTable dt= GetDt(filename);
datagridview1.DataSource = dt;
datagridview1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
progressBar.Visible = false;
}
}
On button click I am trying to display a progress bar, do the load (takes about 10 seconds), AutoSizeRowsMode that takes about 5 seconds, and then hide the progress bar. However, while the load is happening, I cannot see the progress bar - it looks like the app has frozen. Is this because everything runs on single thread?
To counter this possible problem, I tried to put the grid load into separate thread using Task.Run:
await Task.Run(() =>
{
DataTable dt= GetDt(filename);
datagridview1.DataSource = dt;
datagridview1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
});
Above code gives error because the datagridview is on UI thread where as the Task.Run is on separate thread. What is the simples solution here?
Not sure if this is wpf or winforms but you can use the Progress class for this it synchronizes with the base context where it is initialized. So it will have no issues communicating with the Main/UI thread.
Progress can be initialized with an Action and this action can be really helpful in updating your UI components.
basic click event example tested on WPF forms:
private void btn_Click(object sender, RoutedEventArgs e)
{
IProgress<int> p = new Progress<int>((x)=>
{
pg1.Value = x;
if (x == 100)
{
//update data grid here
MessageBox.Show("Completed!");
}
});
Task.Run(() =>
{
//Your heavy tasks can go here
var x = 0;
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(500);
x += 10;
p.Report(x); //report back progress
}
});
}
Here pg1 (Progress bar component of WPF) is updated on button click event which triggers the heavy process you need in the new task run.
Updated code where the grid data loading can be done.
See if the following will fulfill your basic requirements.
The Progressbar is on a panel which once done loading is hidden, alternate is to show a child form with a progressbar and close once data is loaded.
Data is read from a SQL-Server database
Using a separate class
Code responsible to load data
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
namespace LoadDataGridViewProgressBar.Classes
{
public class Operations
{
private static string _connectionString =
"Data Source=.\\sqlexpress;Initial Catalog=NorthWind2020;Integrated Security=True";
public static async Task<(Exception exception, DataTable dataTable)> LoadCustomerData()
{
var customersDataTable = new DataTable();
using (var cn = new SqlConnection() { ConnectionString = _connectionString })
{
using (var cmd = new SqlCommand() { Connection = cn })
{
try
{
cmd.CommandText =
"SELECT C.CustomerIdentifier, C.CompanyName, C.ContactId, CT.ContactTitle, C.City, CO.[Name] " +
"FROM Customers AS C INNER JOIN ContactType AS CT ON C.ContactTypeIdentifier = CT.ContactTypeIdentifier " +
"INNER JOIN Countries AS CO ON C.CountryIdentifier = CO.CountryIdentifier ";
await cn.OpenAsync();
customersDataTable.Load(await cmd.ExecuteReaderAsync());
customersDataTable.Columns["Name"].AllowDBNull = false;
customersDataTable.Columns["CustomerIdentifier"].ColumnMapping = MappingType.Hidden;
customersDataTable.Columns["ContactId"].ColumnMapping = MappingType.Hidden;
}
catch (Exception exception)
{
return (exception, customersDataTable);
}
}
}
return (null, customersDataTable);
}
}
}
In the form I use a BindingSource for working with data which is used as the DataSource for the DataGridView.
using System;
using System.Data;
using System.Windows.Forms;
using LoadDataGridViewProgressBar.Classes;
namespace LoadDataGridViewProgressBar
{
public partial class Form1 : Form
{
public readonly BindingSource _customersBindingSource = new BindingSource();
public Form1()
{
InitializeComponent();
Shown += OnShown;
}
private async void OnShown(object sender, EventArgs e)
{
var (exception, dataTable) = await Operations.LoadCustomerData();
try
{
if (exception == null)
{
_customersBindingSource.DataSource = dataTable;
customerDataGridView.DataSource = _customersBindingSource;
customerDataGridView.ExpandColumns(true);
}
else
{
CurrentButton.Enabled = false;
MessageBox.Show(exception.Message);
}
}
finally
{
progressBar1.Style = ProgressBarStyle.Continuous;
progressBar1.MarqueeAnimationSpeed = 0;
panel1.Hide();
}
}
private void Form1_Load(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.MarqueeAnimationSpeed = 30;
}
private void CurrentButton_Click(object sender, EventArgs e)
{
if (_customersBindingSource.Current == null) return;
// access fields from the DataRow
var data = ((DataRowView) _customersBindingSource.Current).Row;
MessageBox.Show($"{data.Field<int>("CustomerIdentifier")}");
}
}
}
Resize DataGridView columns
using System.Windows.Forms;
namespace LoadDataGridViewProgressBar.Classes
{
public static class DataGridViewExtensions
{
public static void ExpandColumns(this DataGridView sender, bool sizable = false)
{
foreach (DataGridViewColumn col in sender.Columns)
{
// ensure we are not attempting to do this on a Entity
if (col.ValueType.Name != "ICollection`1")
{
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
}
}
if (!sizable) return;
for (int index = 0; index <= sender.Columns.Count - 1; index++)
{
int columnWidth = sender.Columns[index].Width;
sender.Columns[index].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
// Set Width to calculated AutoSize value:
sender.Columns[index].Width = columnWidth;
}
}
}
}
I cannot see the progress bar - it looks like the app has frozen. Is
this because everything runs on single thread?
Correct
Above code gives error because the datagridview is on UI thread where
as the Task.Run is on separate thread.
Also correct
What is the simples solution here?
The simplest solution would be to use MVVM architecture and bind the DataSource property to an ObservableCollection in the view-model, but if we ignore that, the most straight-forward way would be to GetDt on a worker thread (using Task.Run), but to set the DataSource on the UI thread.
So instead of this:
await Task.Run(() =>
{
DataTable dt= GetDt(filename);
datagridview1.DataSource = dt;
datagridview1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
});
Do this:
DataTable dt = await Task.Run(() => GetDt(filename));
datagridview1.DataSource = dt;
datagridview1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
I need some help getting my code to work. DataGridView is proving much more complex that I had anticipated. I need someone to please show me how to fix my code, so that both the DataGridView control can be updated and my DataGridViewComboBoxColumn dropdown and DataGridViewCheckBoxColumn controls all work. I'm okay with any strategy, provided the solution you offer is somewhat elegant and complete. I've already spent far too much time trying to figure this out on my own. Any thoughts or suggestions would be greatly appreciated.
Here is a simplifcation of what my backend looks like:
MySqlDataAdapter dataAdapter = new MySqlDataAdapter(sqlRequestString, this.dbConnection);
MySqlCommandBuilder commandBuilder = new MySqlCommandBuilder(dataAdapter);
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
dataAdapter.Fill(table);
bindingSource.DataSource = table;
If I get rid of the BindingSource, how do I connect the DataTable directly to the DataGridView, so that the ComboBox and Checkbox columns are properly populated?
UPDATE
This should be a complete overview of how I am attempting to initialize my DataGridView:
private void InitializeComponent()
{
/* ... */
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.DataGridViewColumn_Section,
this.DataGridViewColumn_Indent,
this.DataGridViewColumn_Content,
this.DataGridViewColumn_Summary,
this.DataGridViewColumn_Role,
this.DataGridViewColumn_Author,
this.DataGridViewColumn_Updated});
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.DataSourceChanged += new System.EventHandler(this.dataGridView1_DataSourceChanged);
this.dataGridView1.CellEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellEnter);
this.dataGridView1.CellLeave += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellLeave);
this.dataGridView1.ColumnAdded += new System.Windows.Forms.DataGridViewColumnEventHandler(this.dataGridView1_ColumnAdded);
this.dataGridView1.SelectionChanged += new System.EventHandler(this.dataGridView1_SelectionChanged);
this.dataGridView1.Leave += new System.EventHandler(this.dataGridView1_Leave);
//
// DataGridViewColumn_Section
//
this.DataGridViewColumn_Section.HeaderText = "Section";
this.DataGridViewColumn_Section.Name = "DataGridViewColumn_Section";
//
// DataGridViewColumn_Indent
//
this.DataGridViewColumn_Indent.HeaderText = "Indent";
this.DataGridViewColumn_Indent.Name = "DataGridViewColumn_Indent";
//
// DataGridViewColumn_Content
//
this.DataGridViewColumn_Content.HeaderText = "Content";
this.DataGridViewColumn_Content.MinimumWidth = 100;
this.DataGridViewColumn_Content.Name = "DataGridViewColumn_Content";
//
// DataGridViewColumn_Summary
//
this.DataGridViewColumn_Summary.HeaderText = "Summary";
this.DataGridViewColumn_Summary.Name = "DataGridViewColumn_Summary";
//
// DataGridViewColumn_Role
//
this.DataGridViewColumn_Role.HeaderText = "Minimum Signoff";
this.DataGridViewColumn_Role.Name = "DataGridViewColumn_Role";
//
// DataGridViewColumn_Author
//
this.DataGridViewColumn_Author.HeaderText = "Author";
this.DataGridViewColumn_Author.Name = "DataGridViewColumn_Author";
this.DataGridViewColumn_Author.ReadOnly = true;
//
// DataGridViewColumn_Updated
//
this.DataGridViewColumn_Updated.HeaderText = "Updated";
this.DataGridViewColumn_Updated.Name = "DataGridViewColumn_Updated";
this.DataGridViewColumn_Updated.ReadOnly = true;
/* ... */
}
public MyWinform()
{
InitializeComponent();
// Initialize DataGridView DataTable
this.dgvDataTable = new DataTable();
// Initialize DataGridView column indexes
this.idxSection = dataGridView1.Columns["DataGridViewColumn_Section"].Index;
this.idxIndent = dataGridView1.Columns["DataGridViewColumn_Indent"].Index;
this.idxContent = dataGridView1.Columns["DataGridViewColumn_Content"].Index;
this.idxSummary = dataGridView1.Columns["DataGridViewColumn_Summary"].Index;
this.idxRole = dataGridView1.Columns["DataGridViewColumn_Role"].Index;
this.idxAuthor = dataGridView1.Columns["DataGridViewColumn_Author"].Index;
this.idxLastUpdate = dataGridView1.Columns["DataGridViewColumn_Updated"].Index;
}
private void MyWinform_Load(object sender, EventArgs e)
{
DataGridView dgv = this.dataGridView1;
DataGridViewComboBoxColumn comboCol;
// Load System Menu
SystemMenu.Load(this.Handle);
// Insert selection prompt
ProcTemplateRecord selectProcPrompt = new ProcTemplateRecord();
selectProcPrompt.PrimaryKey = 0;
selectProcPrompt.ProcName = "Select from the list...";
this.procList.Insert(0, selectProcPrompt);
// Add new procedure prompt
ProcTemplateRecord newProcPrompt = new ProcTemplateRecord();
newProcPrompt.PrimaryKey = -1;
newProcPrompt.ProcName = "Start a new Safe Job Procedure";
this.procList.Add(newProcPrompt);
// Finish initializing the ComboBox dropdown list
this.comboBox1.DataSource = this.procList;
this.comboBox1.DisplayMember = "ProcName";
this.comboBox1.ValueMember = "PrimaryKey";
// Finish initializing DataGridView and bind to BindingSource
this.dataGridView1.AutoGenerateColumns = false;
/*
// Finish initializing the DataGridView ComboBox columns...
comboCol = (DataGridViewComboBoxColumn)dgv.Columns[this.idxSection];
comboCol.DataSource = Enum.GetValues(typeof(SectionType));
comboCol.ValueType = typeof(SectionType);
comboCol.DropDownWidth = ComboBoxMaxLabelWidth(comboCol);
comboCol = (DataGridViewComboBoxColumn)dgv.Columns[this.idxRole];
comboCol.DataSource = Enum.GetValues(typeof(RoleType));
comboCol.ValueType = typeof(RoleType);
comboCol.DropDownWidth = ComboBoxMaxLabelWidth(comboCol);
this.dataGridView1.DataSource = this.dgvDataTable;
*/
this.RefreshDataGridViewColumnWidths();
// Setup post-initialization DataGridViewEvent handlers
this.dataGridView1.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellValueChanged);
this.dataGridView1.CurrentCellDirtyStateChanged += new System.EventHandler(this.dataGridView1_CurrentCellDirtyStateChanged);
}
private void dataGridView1_DataSourceChanged(object sender, EventArgs e)
{
this.RefreshDataGridViewColumnWidths();
}
private void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
this.RefreshButtons();
if (e.ColumnIndex == this.idxSection)
{
this.label_dgvToolTip.Visible = false;
}
else if (e.ColumnIndex == this.idxIndent)
{
this.label_dgvToolTip.Visible = false;
}
else if (e.ColumnIndex == this.idxContent)
{
this.label_dgvToolTip.Visible = false;
}
else if (e.ColumnIndex == this.idxSummary)
{
this.label_dgvToolTip.Visible = false;
}
else if (e.ColumnIndex == this.idxRole)
{
this.label_dgvToolTip.Visible = false;
}
else if (e.ColumnIndex == this.idxAuthor)
{
this.label_dgvToolTip.Visible = false;
this.label_dgvToolTip.Text = "Author column values are read only.";
this.label_dgvToolTip.Visible = true;
}
else if (e.ColumnIndex == this.idxLastUpdate)
{
this.label_dgvToolTip.Visible = false;
this.label_dgvToolTip.Text = "Updated column values are read only.";
this.label_dgvToolTip.Visible = true;
}
else
{
this.label_dgvToolTip.Visible = false;
}
this.idxActiveColumn = e.ColumnIndex;
this.idxActiveRow = e.RowIndex;
}
private void dataGridView1_CellLeave(object sender, DataGridViewCellEventArgs e)
{
this.label_dgvToolTip.Visible = false;
this.RefreshButtons();
}
private void dataGridView1_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
{
DataGridView dgv = this.dataGridView1;
if ((e.Column.DataPropertyName == "PageSection") &&
(e.Column.CellType != typeof(DataGridViewComboBoxCell)))
{
var cbo = GetComboBoxColumn(e.Column);
cbo.DataSource = Enum.GetValues(typeof(SectionType));
cbo.ValueType = typeof(SectionType);
dgv.Columns.Remove(e.Column);
dgv.Columns.Add(cbo);
}
else if ((e.Column.DataPropertyName == "UserRole") &&
(e.Column.CellType != typeof(DataGridViewComboBoxCell)))
{
var cbo = GetComboBoxColumn(e.Column);
cbo.DataSource = Enum.GetValues(typeof(RoleType));
cbo.ValueType = typeof(RoleType);
dgv.Columns.Remove(e.Column);
dgv.Columns.Add(cbo);
}
}
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
this.RefreshButtons();
}
private void dataGridView1_Leave(object sender, EventArgs e)
{
DataGridView dgv = this.dataGridView1;
if (dgv.SelectedCells.Count == 0)
{
this.idxActiveColumn = -1;
this.idxActiveRow = -1;
}
}
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridViewComboBoxCell sectionCell = (DataGridViewComboBoxCell)dgv.Rows[e.RowIndex].Cells[this.idxSection];
DataGridViewTextBoxCell indentCell = (DataGridViewTextBoxCell)dgv.Rows[e.RowIndex].Cells[this.idxIndent];
DataGridViewComboBoxCell roleCell = (DataGridViewComboBoxCell)dgv.Rows[e.RowIndex].Cells[this.idxRole];
Int32 colIndex = e.ColumnIndex;
try
{
if (colIndex == this.idxIndent)
{
int number;
string cellValue = indentCell.Value.ToString();
bool isNumeric = int.TryParse(cellValue, out number);
if (!isNumeric)
{
cellValue = cellValue.Substring(0, cellValue.Length - 1);
indentCell.Value = cellValue;
}
}
// Column resizing code goes last
this.RefreshDataGridViewColumnWidths();
this.dgvIsDirty = true;
}
catch (Exception ex)
{
throw new Exception("Failed to refresh DataGridView on cell change.", ex);
}
}
private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
DataGridView dgv = (DataGridView)sender;
try
{
if (dgv.IsCurrentCellDirty)
{
// This fires the cell value changed handler below
dgv.CommitEdit(DataGridViewDataErrorContexts.Commit);
this.RefreshDataGridViewColumnWidths();
}
}
catch (Exception ex)
{
throw new Exception("Failed to commit edit of DataGridView cell.", ex);
}
}
This is the code I am using to update my DataGridView contorl:
// Load the data from the database into the DataGridView
this.dbif.GetProcedure(this.procList.ElementAt(selectedIndex).PrimaryKey, ref this.dgvDataTable);
DataRow[] rows = this.dgvDataTable.Select();
//Object dgvDataSource = dgv.DataSource;
//dgv.DataSource = null;
foreach (DataRow dataRow in rows)
{
DataGridViewRow dgvRow = new DataGridViewRow();
dgvRow.CreateCells(dgv);
dgvRow.Cells[idxSection].Value = dataRow.Field<string>(0);
dgvRow.Cells[idxIndent].Value = dataRow.Field<byte>(1);
dgvRow.Cells[idxContent].Value = dataRow.Field<string>(3);
dgvRow.Cells[idxSummary].Value = dataRow.Field<UInt32>(4) != 0;
dgvRow.Cells[idxRole].Value = dataRow.Field<string>(5);
dgvRow.Cells[idxAuthor].Value = dataRow.Field<string>(6) + dataRow.Field<string>(7);
dgvRow.Cells[idxLastUpdate].Value = dataRow.Field<DateTime>(8).ToString();
dgv.Rows.Add(dgvRow);
}
//dgv.DataSource = dgvDataSource;
This is how my enumerations are defined:
public enum SectionType
{
ESJP_SECTION_HEADER = 1, // start with 1 for database compatibility
ESJP_SECTION_FOOTER,
ESJP_SECTION_BODY
}
public enum RoleType
{
ESJP_ROLE_NONE = 1, // start with 1 for database compatibility
ESJP_ROLE_TEST_ENG,
ESJP_ROLE_FEATURE_LEAD,
ESJP_ROLE_TEAM_LEAD
}
There are a number of issues/improvements. There are too many unknowns about the data to fix everything, but some techniques shown here may help.
1. Manually populating the DGV
You ought not have to do that. Just setting the DataTable as the DataSource will work most of the columns.
2. Expressions
You have 2 expressions where you populate the dgv:
dgvRow.Cells[idxSummary].Value = dataRow.Field<UInt32>(4) != 0;
dgvRow.Cells[idxAuthor].Value = dataRow.Field<string>(6) + dataRow.Field<string>(7);
This leads me to believe the dgv is ReadOnly. Otherwise you will have trouble with those. The Summary for instance: if the user Unchecks the column, you can set that value to 0, but what if they check it? How will you know what value to set??
Indent as Byte seems odd too - almost like it is a boolean.
3. Combos
In a previous question, the dgv was populated from a List<Class>. With that as the `DataSource, an Enum works well because the 2 properties were of that Type. With an enum, the following works:
cbo.ValueType = typeof(RoleType);
It's less likely to work with a DataTable as in version 1, 2 or 3 of this question because there is no db/Datatable type of SectionType or RoleType. In other cases where there is a translation - show the user "ESJP_SECTION_HEADER" but store 2 in the DataTable and ultimately the DB - a small NameValue pairs list will work. A different data model means a different approach to the DGV cbo.
It now looks like those are text columns (I've asked 3 times). If so, you really just need to constrain the selection to the enum names. In the IDE, paste in the text for the Items property:
Alternatively, you can do so in code:
private string[] secList = {"ESJP_SECTION_HEADER","ESJP_SECTION_FOOTER",
"ESJP_SECTION_BODY"};
...
((DataGridViewComboBoxColumn)dgv1.Columns["PageSection"]).Items.AddRange(secList);
AutoGenerateColumns
When the DataSource for a DGV is set, by default it will automatically create a DGVcolumn for each DTColumn, and this works well for the most part. Sometimes you'll want some small tweak like hide an Id column, or change a TextColumn to a CBOColumn. Code in the ColumnAddedEvent to make these changes rather than manually laying out columns can work well.
But since there are quite a few such such changes and since you have already layed out columns in the IDE, you want to be sure to set AutoGenerateColumns to false somewhere in code. Otherwise it will add still more columns.
Setup
Things you may or may not have done in the DGV designer:
Add the columns in whatever order you wish, but be sure to assign the DataPropertyName to the name used in the SQL query. Leave it blank for Author.
Expression columns like Author means either changing the query or doing some formatting in the DGV. For this, add First and Last name columns to the DGV as well as the Author column. Make the first two invisible. The code below shows the formatting. (Make sure that the compound column (Author) appears after the parts!).
Normally, I would try to do that in SQL: SELECT (First + Last) AS Author, but if you do not want to mess with that query, you can concat in DGV events.
Be sure to add the names for the Page and Role columns to the Items collection.
Then, the rest is fairly simple:
private DataTable dtF;
...
string SQL = "SELECT PageSection, Indent, Content, SummaryId, "
+ "UserRole, AuthorFirstN, AuthorLastN, LastUpdated FROM FellPage";
using (var dbCon = new MySqlConnection(MySQLConnStr))
using (var cmd = new MySqlCommand(SQL, dbCon))
{
dbCon.Open();
dtF = new DataTable();
dtF.Load(cmd.ExecuteReader());
}
// IMPORTANT!!!
dgv1.AutoGenerateColumns = false;
dgv1.DataSource = dtF;
The formatting event:
private void dgv1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (dgv1.Rows[e.RowIndex].IsNewRow)
return;
if (e.ColumnIndex == 7) // ie "Author"
{
e.Value = dgv1.Rows[e.RowIndex].Cells[6].Value.ToString() + ", " +
dgv1.Rows[e.RowIndex].Cells[5].Value.ToString();
e.FormattingApplied = true;
}
}
Result:
It is very important to turn off AutoGenerateColumns. By default, the dgv will add columns for everything in the source. Since you added them, set that to false in the code (there is no IDE property for it).
By virtue of Summary and Indent being defined as check columns, it translates non zero as true. I have no idea how you will edit any SummaryId value. That should probably be a text column so they can enter a value, if that is allowed (that col could be read only too?).
My query and such arent as complex as yours, and surely there are some details I/we are unaware of which have been omitted. But there is certainly a much less gyrations and code to get it working...whatever this is.
Being bound to a DataTable, when the user edits anything, the changes flow thru to the underlying DataTable (use the various Validating events to check their work). The DataTable in turn tracks the state of each row - new, changed, deleted - so later you can:
var changes = dtF.GetChanges();
This will return all the rows which have been changed since the last AcceptChanges().
So I'm accessing a DB through a stored procedure and am able to pull the data I wanted into a popup box. The problem now is that no matter what row I click on, the SAME data keeps populating my fields. How the heck can I choose a particular row, and have that specific row's data pulled from the DB? Thanks for any help
Form1
public partial class DSC_Mon : Form
{
DSCU_SvcConsumer.MTCaller Caller;
DataSet dsZA;
DataSet dsOut;
public DSC_Mon()
{
InitializeComponent();
Caller = new DSCU_SvcConsumer.MTCaller();
dsZA = new DataSet();
dsOut = new DataSet();
}
private void DSC_Mon_Load(object sender, EventArgs e)
{
timer1.Enabled = true;
}
private void LoadData()
{
if (!dsZA.Tables.Contains("Query"))
{
DataTable dtZA = dsZA.Tables.Add("Query");
dtZA.Columns.Add("str");
dtZA.Rows.Add(string.Format(#"exec MON_GetStatus"));
}
//dtZA.Rows.Add(string.Format(#"select * from am_company"));
dsOut.Clear();
dsOut.Merge(Caller.CallRequest("ZuluAction", dsZA));
if (gridEXMon.DataSource == null)
{
gridEXMon.DataSource = dsOut;
gridEXMon.DataMember = "Table";
}
//gridEXMon.RetrieveStructure();
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
LoadData();
timer1.Enabled = true;
}
private void gridEXMon_DoubleClick(object sender, EventArgs e)
{
ReportInfo report = new ReportInfo();
report.ShowDialog();
}
}
I replaced the private void gridEXMon_DoubleClick with:
private void gridEXMon_RowDoubleClick(object sender, Janus.Windows.GridEX.RowActionEventArgs e)
{
if (e.Row.RowIndex < 0)
return;
int rowIndex = Convert.ToInt32(e.Row.RowIndex);
ReportInfo report = new ReportInfo(rowIndex);
report.ShowDialog();
}
Popup Dialog
public partial class ReportInfo : Form
{
public ReportInfo()
{
InitializeComponent();
DSCU_SvcConsumer.MTCaller caller = new DSCU_SvcConsumer.MTCaller();
DataSet dsZA = new DataSet();
DataSet dsOut = new DataSet();
if (!dsZA.Tables.Contains("Query"))
{
DataTable dtZA = dsZA.Tables.Add("Query");
dtZA.Columns.Add("str");
dtZA.Rows.Add(string.Format(#"MON_ReportInfo"));
}
dsOut.Clear();
dsOut.Merge(caller.CallRequest("ZuluAction", dsZA));
DataTable dt = dsOut.Tables["Table"];
DataRow dr = dt.Rows[0];
if (dt != null)
{
systemNameTextBox.Text = dr["System"].ToString();
contactName1TextBox.Text = dr["Manager"].ToString();
functionNameTextBox.Text = dr["Function"].ToString();
durationMSTextBox.Text = dr["Speed"].ToString();
}
}
I then sent the rowIndex to the popup:
DataTable dt = dsOut.Tables["Table"];
DataRow dr = dt.Rows[rowIndex];
systemNameTextBox.Text = dr["System"].ToString();
contactName1TextBox.Text = dr["Manager"].ToString();
functionNameTextBox.Text = dr["Function"].ToString();
durationMSTextBox.Text = dr["Speed"].ToString();
}
Better to get the data from the grid to save the extra call to the database in your ReportInfo class.
Here is the code:
private void gridEXMon_DoubleClick(object sender, EventArgs e)
{
if (e.Row.RowIndex < 0)
return;
ReportInfo report = new ReportInfo();
String System = Convert.ToInt32(e.Row.Cells["System"].Value);
String Manager = Convert.ToString(e.Row.Cells["Manager"].Value);
String Function = Convert.ToDecimal(e.Row.Cells["Function"].Value);
String Speed = Convert.ToInt32(e.Row.Cells["Speed"].Value);
report.ShowDialog(System, Manager, Function, Speed);
}
Then your ReportInfo class ctor should be:
public partial class ReportInfo : Form
{
public ReportInfo(String System, String Manager, String Function, String Speed)
{
InitializeComponent();
systemNameTextBox.Text = System;
contactName1TextBox.Text = Manager;
functionNameTextBox.Text = Function;
durationMSTextBox.Text = Speed;
}
}
Have you tried with:
gridEXMon.DataSource = dsOut.Tables[0];
// next line is not required if you've already defined proper grid structure
gridEXMon.RetrieveStructure();
Janus has also his own forum available here. You can find there a lot of useful information. For browsing I will use IE. Janus forum works good only with IE...
Edited:
How ReportInfo class can know what record have you selected? Especially that you have fallowing code:
DataTable dt = dsOut.Tables["Table"];
DataRow dr = dt.Rows[0]; // always first record is taken!
Probably you should define ctor with row index:
public ReportInfo(int rowIndex)
{
...
DataTable dt = dsOut.Tables["Table"];
DataRow dr = dt.Rows[rowIndex];
....
}
I'm trying to populate a number of combo boxes in a formLoad method, but only the 1st one populates. in this same method the same stored procedures are called for Data Grids and work fine. Please see attached code:
private void frmMain_Load(object sender, EventArgs e)
{
DataAccessLayer dal = new DataAccessLayer();
pnlEditCall.Visible = false;
pnlEditInspection.Visible = false;
pnlEditEquipment.Visible = false;
#region Populate DataGrids
dgvInspections.DataSource = dal.GetAllInspections();
dgvCalls.DataSource = dal.GetAllCalls();
dgvStaff.DataSource = dal.GetAllStaff();
dgvLabs.DataSource = dal.GetAllLabs();
dgvEquipment.DataSource = dal.GetAllEquipment();
#endregion
#region Populate ComboBoxes
cmbInspectionStaff.DataSource = dal.GetAllStaff();
cmbInspectionStaff.DisplayMember = "Name";
cmbInspectionStaff.ValueMember = "[StaffID]";
cmbCallStaff.DataSource = dal.GetAllStaff();
cmbCallStaff.DisplayMember = "Name";
cmbCallStaff.ValueMember = "[StaffID]";
cmbCurrentLab.DataSource = dal.GetAllLabs();
cmbCurrentLab.ValueMember = "[LabNo]";
cmbCurrentLab.DisplayMember = "[LabNo]";
#endregion
}
I'm taking data entered by the user and trying to add them to a datagridview in another form that I created, however, I'm having an
Object reference not set to an instance of an object. when I try to add my first row,
this is the code below:
monthTrans mt = new monthTrans();
private void completeBtn_Click(object sender, EventArgs e)
{
var todayDate = DateTime.Now;
//MessageBox.Show(todayDate.ToString());
var transType = typeLabel.Text;
var currencySelected = CurrencyList.Text;
var amount = AmountText.Text;
var currencyPrice = currencyPriceText.Text;
var total = totalAmountText.Text;
var monthlyData = mt.myData;
monthlyData.Rows.Add(todayDate, transType, currencySelected, amount, currencyPrice, total);
//open a new window
mt.Parent = Parent;
mt.StartPosition = FormStartPosition.CenterParent;
if (mt.IsDisposed)
mt = new monthTrans();
mt.Show();
}
the error appears on "monthlyData.Rows.Add......."
btw, I added in my datagridview form this code below to access it outside that form:
public DataGridView myData
{
get;
set;
}
thanks for your help
try:
private DataGridView _mydata = new DataGridView();
public DataGridView myData
{
get { return _mydata; }
set { _mydata = value; }
}