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?
Related
Background
I have a datagridview that is bound to a dataset. That dataset stores data from a sql db that is obtained using a SqlCommandBuilder. When the dataset is updated the db is updated too.
This works fine.
However I would like one of the columns to contain a combobox.
To do this I have added a DataGridViewComboBoxColumn and hidden the original column. I've then then tried to add extra items to the combobox and then bound the dataset.
My Code
dataGridView1.DataSource = JoblistDataSet.Tables["Joblist"]; //bind ds to dgv
dataGridView1.Columns["Status"].Visible = false; //hide original column
DataGridViewComboBoxColumn Status = new DataGridViewComboBoxColumn();
//setup combobox
Status.Items.Insert(0, "Select Status");//add extra items
Status.DataSource = JoblistDataSet.Tables["Joblist"];//add original item from ds
Status.HeaderText = #"Status ";
Status.DisplayMember = "Status";
Status.DropDownWidth = 78;
Status.DataPropertyName = "Status";
Status.DisplayIndex = 7;
//add combobox
dataGridView1.Columns.Add(Status);
dataGridView1.Refresh();
What i'm Trying to Achieve
I would like a combobox on one of the datagridview columns. In that combobox, the first item should contain (and be displayed) the row's actual value, the other items should contain the other available items.
Question
However, this doesn't seem to work. All I'm getting is the actual rows actual value. Am i going the wrong way about it. Is there a better way to do this?
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.
I have table in my database stored in SQL Server 2012 and through this table I am iterating and adding new object in my binding list. This list is then set as datasource for my DataGridView.
As I understand, the DataGridView should create columns and fill the rows with data, but when I run the build, I only see blank rows. Their count is matching the count of rows in table and I also debugged with breakpoints so I have determined that I really have my datasource filled with data, but I cannot figure those blank rows out.
This is method I use for creating dataset and filling the binding list
public void selectCars()
{
string connString = #"Data Source=POHJOLA\SQLEXPRESS;Initial Catalog=BlueCars;Integrated Security=True";
using (SqlConnection connection = new SqlConnection(connString))
{
connection.Open();
string query = "SELECT * FROM Car ORDER BY CarID ASC";
SqlCommand command = new SqlCommand(query, connection);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
using (DataSet result = new DataSet())
{
adapter.Fill(result);
foreach (DataRow row in result.Tables[0].Rows)
{
carsList.Add(new Car(Convert.ToInt32(row[0]), row[1].ToString(), row[2].ToString(), row[3].ToString(), Convert.ToDecimal(row[4]),Convert.ToInt32(row[5]),row[6].ToString(),row[7].ToString() ));
}
}
}
}
This is my initialization
public managerCarForm()
{
InitializeComponent();
selectCars();
carsGrid.DataSource = carsList;
}
Also I should probably add, that I created columns manually in designer and set datanameproperty to parameters of the car class
I am not getting any exception or error here..
Thanks very much in advance!
I came across the exact same problem in VB.
I Found out that the solution was this:
(I´ll just write my code in VB you can translate it).
Before setting the DataSource of the grid, you should Clear the grid out.
carsGrid.DataSource = Nothing
carsGrid.Rows.Clear()
carsGrid.Columns.Clear()
Then set your grid DataSource as usual. In My case:
carsGrid.DataSource = GetEmptyObject._Get()
Hope it Helps.
foreach (DataRow row in result.Tables[0].Rows)
{
carsList.Add(new Car(Convert.ToInt32(row[0]), row[1].ToString(), row[2].ToString(), row[3].ToString(), Convert.ToDecimal(row[4]),Convert.ToInt32(row[5]),row[6].ToString(),row[7].ToString() ));
}
Please check your carList by applying a breakpoint after foreach loop to verify it contains at least a single data row. And also check your query.
If your application is an ASP.NET
try to modify your code as below..
public managerCarForm()
{
InitializeComponent();
selectCars();
carsGrid.DataSource = carsList;
carsGrid.Databind();
}
Normally this happens when you have manually added the columns in design time.
Or you have AutoGenerateColumns = false;
If you use AutoGenerateColumns = true; the columns will be/should be auto generated.
To solve this:
Right click on the grid -> Edit Columns.
Go to property: DataPropertyName
Set that to the variable name that you bind to (the table column name in your case).
(You say you have done that, but the value here should exactly match what you have in your list. I have made a DTO class and via a loop I have populated a List of my own and set the names to match the properties of that DTO. This should solve it for you.)
I am assigning a datasource to the datagridview and it works fine but only for the first time. When I assign the datasource a second time it doesn't show the data. My code is:
gridProjectEdit.DataSource = null;
gridProjectEdit.Columns.Clear();
gridProjectEdit.Rows.Clear();
gridProjectEdit.Refresh();
if(dt!=null)
dt.Clear();
dt=methodCaller.GetProjectData(); //get the data
gridProjectEdit.DataSource = dt; //copying datatable
copyOfProjectDataTable = dt.Copy(); //this datatable used to check sno
I also rename the column headers ahead like this, if it matters:
//renaming column header
gridProjectEdit.Columns[0].HeaderText = "S.NO.";
gridProjectEdit.Columns[1].HeaderText = "PROJECTNAME";
When I debugged this code the second time it showed the datatable having 6 rows but I don't know why it doesn't show the data, it shows only column headers.
It seems to me like you shouldn't assign your datasource several times. Set the DataSource once in the Constructor or the OnInitialize Method and instead of doing
dt=methodCaller.GetProjectData();
do
dt.AddRange(methodCaller.GetProjectData());
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.