Multiple DataGridView from single DataSource - c#

I have a Winform application which is almost working, but is becoming increasingly complex around the way users update and insert data via DataGridViews, and how this is fed back into the List in the background. Currently both DataGridView's are populated from a single List, and when a user updates a cell, the List is then updated from the Grid. I'd prefer to use the List as a DataSource.
Very simplified dataset (Server, Function, Proces) - P.S. I can't change my data set:
Server1, KeepAlive, SQLService
Server1, KeepAlive, AnotherProcess
Server1, Kill, RogueProcess
Server2, KeepAlive, SQLService
Server3, KeepAlive, SQLService
DataGridView1 has 1 column containing Distinct server names and DataGridView2 has 2 columns containing all functions and processes for the selected server in DataGridView1. A user can edit any cell and currently I keep track of each change and reflect it back into the List, then refresh the Grids. I'd prefer to handle this using the DataGridView DataSource object.
My question's are: What is the most suitable data source setup (I.e. a List of objects)? How do I filter differently from the data source into the Grid's?
So far I've tried the following. Create a Config class:
class Config
{
[DisplayName("Server")]
public string server { get; set; }
[DisplayName("Function")]
public string function { get; set; }
[DisplayName("Process")]
public string checkType { get; set; }
}
Create a List which contains these Config objects:
List<Config> configurations = new List<Config>();
Assign the List as a Data Source:
dataGridView1.DataSource = configurations;
This displays all three columns of data as expected. How can I a) show only the Server column and b) show only a distinct list?
I'm guessing with DataGridView2 I can use RowFilter to display only the selected server:
(dataGridView2.DataSource as DataTable).DefaultView.RowFilter = ?
Thanks in advance for any help!
Edit
I have tried using LINQ:
dataGridView1.DataSource = configs.Select(o => new { Server = o.server }).ToList();
This worked, but my grid was read-only, so I used a custom view model too:
dataGridView1.DataSource = configs.Select(o => new ServerView() { Server = o.server }).ToList();
This is displaying exactly how I want, but when I edit a cell, the change is not reflected in the List. Can LINQ be used with DataSource like this?
Edit 2
Using stefankmitph's examples I can filter via a SortableBindingList:
SortableBindingList<Config> sortableBindingList = new SortableBindingList<Config>(configs.Where(o => o.server == "Server1").ToList());
BindingSource bindingSource = new BindingSource(sortableBindingList, null);
dataGridView1.DataSource = bindingSource;
This resolves the issue for DataGridView2 which needs to be filtered based on what is selected in #1. However, I still can't work out how to only display certain columns in the grids. #1 should have only the servers and #2 should have the remaining two columns. A select in the LINQ query wouldn't work because I'm dealing with a Config object ... right?

Over the years I've struggled with a lot of problems with the DataGridView. Here's just what for me is 'best practice':
1a) I most often attach my data (List) to a SortableBindingList (there's a lot of examples out there here, here and here. Take what fits you.)
Assuming there's a List configurations;
dataGridView.DataSource = new SortableBindingList<Config>(configurations);
now your DataGridView is sortable.
1b) When it comes to filter the DataSource there's a bunch of options. As far as I'm concerned the filtering only applies to the BindingSource with a DataTable attached as DataSource
Assuming there's a DataTable dataTableConfigurations:
BindingSource bindingSource = new BindingSource(dataTableConfigurations, null);
dataGridView.DataSource = bindingSource;
bindingSource.Filter = "Server = 'Server3'";
but I'm quite sure that this won't work with a List of objects as DataSource.
What you can do:
SortableBindingList<Config> sortableBindingList = new SortableBindingList<Config>(configurations);
BindingSource bindingSource = new BindingSource(sortableBindingList, null);
dataGridView.DataSource = bindingSource;
This way it's very easy to track changes in your data. (f.e. BindingSource.Current returns the current item of your DataGridView)
BindingSource bindingSource = dataGridView.DataSource as BindingSource;
Config currentConfig = bindingSource.Current as Config;
If I would have to filter the data now, I'd do the following:
BindingSource bindingSource = dataGridView.DataSource as BindingSource;
List<Config> list = bindingSource.DataSource as List<Config>;
bindingSource.DataSource = list.Where(item => item.Server = 'banana').ToList();
If you have any bindings to the BindingSource (TextBoxes, ComboBoxes, etc.) keep in mind that attaching/detaching DataSource can cause unwanted behaviour. To avoid this I do suspending and resuming of the binding:
bindingSource.SuspendBinding();
// do the filtering
bindingSource.ResumeBinding();
This keeps all the DataBindings alive.
UPDATE: displaying only certain columns in your DataGridView
That's quite simple. (column names correlate with the names in your DataSource object)
var columnFunction = dataGridView.Columns["function"];
if(columnFunction != null)
columnFunction.Visible = false;
var columnCheckType = dataGridView.Columns["checkType"];
if(columnCheckType != null)
columnCheckType.Visible = false;
So only your server column will be displayed.

Related

Datagridview datasource is null after adding values by ui

I built a datagridview with columns on the UI,
I added a button which tries to convert the datasource into datatable.
The datagridview binded to bindingsource, which binded to new datatable.
After adding values on the ui and clicking on the button, I try to get the datatable from the datasource but it keeps null.
BindingSource b = new BindingSource();
b.DataSource = new DataTable();
grdView.DataSource = b;
public void OnButtonClick()
{
BindingSource b = (BindingSource)grdView.DataSource;
DataTable dt = b.DataSource;
// dt keeps be null (but count is the count of the added rows)
}
From MSDN
The DataSource property can be set to a number of data sources,
including types, objects, and lists of types. The resulting data
source will be exposed as a list
The data source is a List, not a DataTable. That's probably why it has a count but dt ends up null.
The reason to use BindingSource is so that you can access the data withouth going through the UI component (the grid). If you want the data, get it from the BindingSource, don't get it from the grid.

how to remove (not hiding) columns from a DevExpress gridView?

I load my data into a GridView using DataSet. At first databinding, it works well. But when I run query that return different columns, the GridView show the new column plus the old one. I've tried the gridView1.Columns.Clear() method, but it doesn't solve the problem.
Here are some of the code:
// loading data into dataset
dataSet dsGrid = new dataSet();
string dtMember = "kpi";
// note: "thequery" is query generated based on user's selection
using (MySqlDataAdapter da = new MySqlDataAdapter(thequery, myCn))
{
da.Fill(dsGrid, dtMember);
}
// set the gridControl's datasource
gc_report.DataSource = null;
// clear the columns
gridView1.Columns.Clear();
// bind the data
gc_report.DataSource = dsGrid;
gc_report.DataMember = dtMember;
When I run my application, it runs well, but if the query return different column, the old column still appear although I've put the Clear() method of the gridView1.Columns collection.
How to set the gridView1 , so the grid always shows the current query?

How to add new row to datagridview?

I have DataGridView filled with data from datasource (SQL). Now I want to a add new row, but I can't, because new data can't be added to bounded DataGridView...
I was trying to :
dataGridView1.Source = null;
dataGridView1.Rows.Add("1");
but it clears my previous data in table. How to do it, to add new row without deleting previous data?
When you set the DataSource property to null, you are essentially removing all data from the DataGridView (since it doesn't know what to bind to anymore).
You have two options here. The first is to update the underlying data source. Let's assume that it's a DataTable. In this case, you'd do something like:
DataTable dt = dataGridView1.Source as DataTable;
dt.Rows.Add(new object[] { ... });
And then the DataGridView will pick up on the changes (note that if you are not binding to something that doesn't implement the INotifyCollectionChanged interface, you'll have to call the ResetBindings method to get the grid to refresh).
The other option is to let the DataGridView manage the rows. You can do this by manually adding each item using the Add method on the DataGridViewRowCollection returned by the Rows property:
foreach (var item in source)
{
dataGridView1.Rows.Add("1", "2", "3", ...);
}
I wouldn't say the second solution is optimal, but it will work.
Finally, assuming you are binding to a DataTable (or some other materialization of the data from an underlying data source), this doesn't do anything about to updating underlying data source (that would be a separate question).
The short answer is, you don't.
When you set your DataSource to null, you've broken the link between your DataGridView and your data source, so its data won't be persisted. You can't add a row to a bound DataGridView because it's supposed to represent the state of the underlying DataSource; you're effectively asking .net to make your table out of sync with its backing store, defeating the purpose of databinding in the first place.
If you want to add a row to the backing store, you should be adding a row in the DataSource, not in your DataGridView.
maybe you want to do it manually and detailed? Something like this?
DataSet ds = new DataSet();
OleDbDataAdapter adapter = null;
adapter = new OleDbDataAdapter("SELECT * FROM WHERE", conn);
adapter.Fill(ds);
dataGridView1.ColumnCount = 5; //how many columns returns your SQL query? starts with 0
dataGridView1.Columns[0].Name = "COl-1";
dataGridView1.Columns[1].Name = "COl-2";
dataGridView1.Columns[2].Name = "COl-3";
dataGridView1.Columns[3].Name = "COl-4";
dataGridView1.Columns[4].Name = "COl-5";
DataTable dt = ds.Tables[0];
foreach (DataRow dr in dt.Rows)
{
dataGridView1.Rows.Add(
(dr["COL_HEADER_NAME1"].ToString()),
(dr["COL_HEADER_NAME2"].ToString()),
(dr["COL_HEADER_NAME3"].ToString()),
(dr["COL_HEADER_NAME4"].ToString()),
(dr["COL_HEADER_NAME5"].ToString()));
}
You just add rows by using add method of rows collection
me.datagridview1.rows.add("first","second","third");
You can add any amount of items with array collection.

Editing cells in a datagrid winform with var type datasource

I'm trying to edit a column of in the datagrid but it just simply refuses to edit. I've set editmode but it still wouldn't respond. My only conlcusion is the datasource that I'm binding it to. Here's some of my code:
public partial class Example: Form
{
DataTable edtable = new DataTable();
DbHelper db;
public Example()
{
InitializeComponent();
db = new DbHelper("dbname", "dbid", "dbpassword");
var cmd = db.GetCommand(
"Select ED.ID, ED.Name, ED.Code from EXAMPLETABLE ED Join (Select trim(code) as code, count(code) as codecount from exampletable group by trim(code)) X ON X.codecount > 1 and ED.Code like X.Code || '%'"
);
edtable = db.ExeGetDataTable(cmd);
dataGridView1.DataSource = edtable;
}
}
When I run my app, my datagrid is populated with the edtable that contains contents obtained via the query. However, I cannot edit any of the values in them. I click on the cells and its as if the values are read only even though I specifically set editmode to be edit.
Is it because of my datasource? If so, how can I make my values editable?
Thanks!
I think you've about got it in your last comment. I know that when you bind to a data source, if you bind to a read-only property, the cells in the DataGridView will be read-only no matter what the EditMode or ReadOnly or any of its other properties are set to. I haven't bound to one of the DataTable objects you're using, but I know that when you bind to a class object with { get; } properties, those are read-only in the grid, while { get; set; } properties are editable.
Are you expecting that when you edit values in the grid, the values will be updated in the database? If so, I'm sure you need something more than an object populated from a simple SELECT statement. How you attack this will depend on what you expect to happen to the values edited in the grid.

how to update dataGrid in database

In my C# project, i populated the values in DataGrid from DataTable. Now if i make changes in the values in the DataGrid i need to update them in the DataBase. I'm using MS access.
Here is the code snippet of how i populate values in the DataGrid.
while (myReader.Read())
{
frmBind.dr = frmBind.dtResults.NewRow();
frmBind.dr["ClassName"] = myReader.GetString(0);
frmBind.dr["MethodSignature"] = myReader.GetString(1);
frmBind.dr["ParameterValues"] = myReader.GetString(2);
frmBind.dr["ExpectedResults"] = myReader.GetString(3);
frmBind.dtResults.Rows.Add(frmBind.dr);
}
frmBind.dataGrid2.DataSource = frmBind.dtResults;
where,
dtResults is DataTable,
frmBind is a Class Object,
dataGrid2 is the DataGrid,
myReader.Read() is used to get the values from the DataBase.
I suggest you drop the datareader.
Drag an SQLDataSource onto the page and configure it with your query. It will give you reading/updating without writing any code at all.

Categories