|
|
Data Binding a DataGridView
Last post 02-05-2007, 1:21 AM by DarkStar. 12 replies.
-
02-01-2007, 7:38 AM |
-
DarkStar
-
-
-
Joined on 02-01-2007
-
Poland
-
Posts 21
-
-
|
Data Binding a DataGridView
Hello! Am I the first one to whine? ;) Anyway, this has been already touched at your Premium Forums. I am evaluating EntitySpaces Trial for the WinForms 2.0 project I started a few days ago. I would like to make a purchase, but I have to make things work first. This is really odd, because in order to test ES I created just a simple one form application. I have generated all classes with MyGeneration, in accordance to your guides, then I create a WinForms app with just one simple form - there is one DataGridView, and two buttons - nothing fancy. My whole code for this app fits on one screen: Code:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using BusinessObjects;
namespace IntuitiveSpaces
{
public partial class IndustriesForm : Form
{
public IndustriesForm()
{
InitializeComponent();
industriesCollection1.LoadAll();
dgvIndustries.Refresh();
}
private void btnSave_Click(object sender, EventArgs e)
{
industriesCollection1.Save(EntitySpaces.Interfaces.esSqlAccessType.DynamicSQL);
Close();
}
private void btnLoad_Click(object sender, EventArgs e)
{
}
private void IndustriesForm_Load(object sender, EventArgs e)
{
}
}
} I dropped an IndustriesCollection from my Toolbox and binded the dgvIndustries to it in design time. Industries table looks like this:
Code:Create table [Industries]
(
[IndustryId] Integer Identity(0,1) NOT NULL,
[Name] Nvarchar(50) NOT NULL,
[RowVersion] Timestamp NOT NULL,
Constraint [pk_Industries] Primary Key ([IndustryId])
)
go
As you can see, it's very simple and straightforward. RowView is there, because at first I was going to use dOOdads - do we still need those timestamp columns for EntitySpaces? Anyway, I made both IndustryId and RowView columns invisible in the DataGridView, because we are not going to edit them. Now, the strangeness begins...
If we place LoadAll() and Refresh() in the form constructor (as seen above) - it all works, the form is created and the dgvIndustries filled with all records, we are able to edit it and save our changes by clicking btnSave. So far so good. But sometimes I would like to load the grid when a user clicks a button for example. Now, that doesn't work. As you can see, I have created a button called btnLoad and there is an event when you click it. As far as I know, I should be able to just cut & paste the two lines responsible for loading data into a DataGridView and all should work, so I could have this now: Code: public IndustriesForm()
{
InitializeComponent();
}
private void btnSave_Click(object sender, EventArgs e)
{
industriesCollection1.Save(EntitySpaces.Interfaces.esSqlAccessType.DynamicSQL);
Close();
}
private void btnLoad_Click(object sender, EventArgs e)
{
industriesCollection1.LoadAll();
dgvIndustries.Refresh();
}
The form should initialize with an empty grid, but after you click the Load button it should fetch all rows from the Industries table and fill in the grid. Somehow it doesn't. It will just show the first record and nothing more. The same goes when I put those two lines in Form's Load event. The only place it really works is when I put LoadAll() and Refresh() in the form constructor. Am I doing something wrong here? It's really the simplest app I could imagine and it doesn't work - now I'm afraid to use ES for a bigger project if I run into problems with something that small. :(
|
|
-
02-01-2007, 8:15 AM |
|
|
Re: Data Binding a DataGridView
I don't see you instantiating your design time collection anywhere?
Code:
1 /// <summary>
2 /// Binds the divisions.
3 /// </summary>
4 private void BindDivisions()
5 {
6 try
7 {
8 this.divisionsCollection1 = new DivisionsCollection();
9 this.divisionsCollection1.Query.OrderBy(this.divisionsCollection1.Query.DisplayName.Ascending);
10
11 if (this.divisionsCollection1.Query.Load())
12 {
13 this.dgvDivisions.DataSource = null;
14 this.dgvDivisions.DataSource = this.divisionsCollection1;
15 this.dgvDivisions.Refresh();
16 }
17 }
18
19 catch (Exception exc)
20 {
21 frmErrorDialog FriendlyError = new frmErrorDialog(exc);
22 FriendlyError.Show();
23 }
24 }
Regards, Scott Schecter EntitySpaces | My Site
|
|
-
02-01-2007, 11:39 AM |
-
DarkStar
-
-
-
Joined on 02-01-2007
-
Poland
-
Posts 21
-
-
|
Re: Data Binding a DataGridView
Scott.Schecter:I don't see you instantiating your design time collection anywhere?
No, if I do this the way you showed here (I did it before) it works all right. Correct me if I am wrong, but I thought design time means I don't have to write additional code to load the data, that's not how Microsoft's components work and I was expecting the same from EntitySpaces. :) Looks like I have too high demands. You know what I mean? If you design a DataGridView in VisualStudio and bind it in design time, it has a data preview and automatically loads all rows after you run the solution. But it's still unclear to me why it would work correctly only in the form constructor, even though I didn't initialize anything myself, and at the same time return just a single row if I use the same set of methods somewhere else... It confuses me. The simple question is - why does it work correctly in the form's constructor and nowhere else if it's the same piece of code?
|
|
-
02-01-2007, 3:37 PM |
-
Mike.Griffin
-
-
-
Joined on 01-14-2007
-
Indianapolis
-
Posts 2,887
-
-
|
Re: Data Binding a DataGridView
DarkStar, I've alwasy called LoadAll() on the EntitySpaces collection to load the data. I just drag my collection and grid out to the form, set the EntitySpaces collection to the datasource of the grid and pick which columns and so on and then call LoadAll(). When you say that's not how Microsoft does it are you referring to the generation of strongly typed datasets? Our Roadmap is all about DataBinding, you can see it under our "Product" menu. However, never I have I heard or seen it taught that design time databinding = "writing no code" ? We have pretty good DataBinding support in Windows forms and it's only going to get better.
EntitySpaces | Twitter | BLOG | Please honor our Software License
|
|
-
02-01-2007, 11:56 PM |
-
DarkStar
-
-
-
Joined on 02-01-2007
-
Poland
-
Posts 21
-
-
|
Re: Data Binding a DataGridView
Mike.Griffin:DarkStar, I've alwasy called LoadAll() on the EntitySpaces collection to load the data. I just drag my collection and grid out to the form, set the EntitySpaces collection to the datasource of the grid and pick which columns and so on and then call LoadAll().
That's what I do, as you can see from the code above, but I'm confused why it works correctly only in the form constructor, while if I put the same code (those two lines - LoadAll() and grid's Refresh()) anywhere else, it doesn't return a whole recordset. Additionally - if I click the column's header a few times (which means DataGridView sorting) EntitySpaces will throw an exception. I guess I will have to record a movie...
Mike.Griffin: When you say that's not how Microsoft does it are you referring to the generation of strongly typed datasets?
Yes, I think I am. :) But Microsoft's controls just put code in Form.Designer.cs and then automatically add YourTableAdapter.Fill to form's OnLoad event, so you don't have to type. Anyway, I can do this myself in some method, just like Scott showed. But I really would like to know about what I wrote above several times... Why this loads all records properly:
Code:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using BusinessObjects;
namespace IntuitiveSpaces
{
public partial class IndustriesForm : Form
{
public IndustriesForm()
{
InitializeComponent();
industriesCollection1.LoadAll();
dgvIndustries.Refresh();
}
private void btnSave_Click(object sender, EventArgs e)
{
this.industriesCollection1.Save(EntitySpaces.Interfaces.esSqlAccessType.DynamicSQL);
Close();
}
private void btnLoad_Click(object sender, EventArgs e)
{
}
private void IndustriesForm_Load(object sender, EventArgs e)
{
}
}
} And this returns just one row:
Code:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using BusinessObjects;
namespace IntuitiveSpaces
{
public partial class IndustriesForm : Form
{
public IndustriesForm()
{
InitializeComponent();
}
private void btnSave_Click(object sender, EventArgs e)
{
this.industriesCollection1.Save(EntitySpaces.Interfaces.esSqlAccessType.DynamicSQL);
Close();
}
private void btnLoad_Click(object sender, EventArgs e)
{
industriesCollection1.LoadAll();
dgvIndustries.Refresh();
}
private void IndustriesForm_Load(object sender, EventArgs e)
{
}
}
}As you can see, those are exactly the same calls, just made from a different place.
|
|
-
02-02-2007, 4:23 AM |
-
Mike.Griffin
-
-
-
Joined on 01-14-2007
-
Indianapolis
-
Posts 2,887
-
-
|
Re: Data Binding a DataGridView
I will test this over the weekend. We are re-packaging everything for our new Feb 6th pricing structure, if there is a fix I can sneak in I will. By the way, I really like the way your posts look, these Community Server forums with the source formatting really help. Hopefully I can figure out what is going on, thanx for the samples above.
EntitySpaces | Twitter | BLOG | Please honor our Software License
|
|
-
02-03-2007, 5:38 PM |
|
|
Re: Data Binding a DataGridView
Here is an alternate approach to design time data binding using an EntitySpaces OrdersCollection generated from the Northwind Orders table. You will need to build your EntitySpaces BusinessObjects before they will show up in Design View:
Code:In Design View:
1) Click the Data|Show Data Sources menu item.
2) Click Add New Data Source.
3) Click Object and Next.
4) Navigate down and select OrdersCollection and Finished.
If you want a DataGridView, drag the entire collecton to the Form.
If you want a detail Form with only specific fields,
then drag the fields on to the Form.
Before dragging, the collection and each field has a DropDown
that lets you select alternate field types, i.e., a DateTimePicker
for DateTime fields instead of the default TextBox.
Individual fields, also get an aligned label added.
A BindingNavigator, BindingNavigator ToolStrip, and BindingSource
are added automatically.
The Save Toolbar Button will need to be Enabled, either at design
time, or in code.
You can see in the code below that it is very similar to what you already have, but there are some significant differences.
1) To address Scott's point, and some of your confusion as to why things work in one place and not another, is that there is a lot of stuff generated for you by VS that is buried in IndustriesForm.Designer.cs. My guess is that InitializeComponent has instantiated your industriesCollection1 for you and set the DataGridView's DataSource to it. But, if you are going to add your own Load Button, you will have to handle things in code.
Code:private void btnLoad_Click(object sender, EventArgs e)
{
industriesCollection1 = new IndustriesCollection();
industriesCollection1.LoadAll();
dgvIndustries.DataSource = industriesCollection1;
dgvIndustries.Refresh();
}
2) With EntitySpaces, you definitely need to load your data and not rely on VS to generate something that does that. This has significant advantages in that you can LoadAll(), LoadByPrimaryKey(), or come up with your own Query.Load() that uses the EntitySpaces DynamicQuery mechanism to limit the number of rows retrieved to a specific sub-set.
3) Most importantly, my best understanding of MS recommended practices, is to use a BindingSource for anything other than a simple read-only DataGridView. This is especially important for editing, sorting, or filtering a DataGridView. In my experience, the column sort exception you are getting will mysteriously disappear once you start using a BindingSource.
3) Before calling Save, you should always call EndEdit(). Depending on what approach you have taken, that may be on the DataGridView, a BindingSource, or directly on an EntitySpaces entity. Validate() is optional. Again, it depends on what you are trying to accomplish. You might even nest the rest of that method inside if(this.Validate()). You may want to follow the call to Save() with a call to ordersBindingSource.ResetBindings(false). For instance, that is sometimes necessary to display an identity key that was retrieved by EntitySpaces for a newly added record.
Code:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using EntitySpaces.Interfaces;
using BusinessObjects;
namespace ForumHelper
{
public partial class FormNonStringBinding : Form
{
private OrdersCollection collection = new OrdersCollection();
public FormNonStringBinding()
{
InitializeComponent();
}
private void FormNonStringBinding_Load(object sender, EventArgs e)
{
collection = new OrdersCollection();
collection.LoadAll();
this.ordersBindingSource.DataSource = collection;
}
private void ordersBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
this.Validate();
ordersBindingSource.EndEdit();
collection.Save();
}
}
}
David Neal Parsons www.entityspaces.net
|
|
-
02-03-2007, 6:44 PM |
|
|
Re: Data Binding a DataGridView
Almost forgot... EntitySpaces takes advantage of the SQL Server timestamp column just like dOOdads. It will throw a concurrency exception if a violation is detected by the server.
David Neal Parsons www.entityspaces.net
|
|
-
02-04-2007, 2:29 AM |
-
DarkStar
-
-
-
Joined on 02-01-2007
-
Poland
-
Posts 21
-
-
|
Re: Data Binding a DataGridView
David.Parsons:Almost forgot... EntitySpaces takes advantage of the SQL Server timestamp column just like dOOdads. It will throw a concurrency exception if a violation is detected by the server.
Thank you very much for the comprehensive answer in your previous post. :) In regards to concurrency - does EntitySpaces use RowVersion columns "automagically", ie. I don't have to think about it nor code anything - just place a timestamp column in every table? :)
|
|
-
02-04-2007, 5:21 AM |
-
Mike.Griffin
-
-
-
Joined on 01-14-2007
-
Indianapolis
-
Posts 2,887
-
-
|
Re: Data Binding a DataGridView
Yes, if you have a timestamp (or Row-version, but I use timestamp always) and you regenerate it's all done for you. You can quickly verify this, just read the same record twice into two separate objects, then change the data in both, save the first, then save the second and the second one will throw an exception.
EntitySpaces | Twitter | BLOG | Please honor our Software License
|
|
-
02-04-2007, 6:34 AM |
|
|
Re: Data Binding a DataGridView
DarkStar: ... I don't have to think about it nor code anything...
hehe... While that statement is true, you might consider something like this:
Code:try
{
entity.Save();
}
catch (esConcurrencyException concurrencyEx)
{
// You can trap concurrency violations
// and give them special handling.
MessageBox.Show("Oops. Someone beat you to it.");
}
catch (Exception ex)
{
// All other exceptions are handled here
MessageBox.Show("Call me.");
}
David Neal Parsons www.entityspaces.net
|
|
-
02-04-2007, 12:11 PM |
-
DarkStar
-
-
-
Joined on 02-01-2007
-
Poland
-
Posts 21
-
-
|
Re: Data Binding a DataGridView
Ah, yeah, besides error handling, of course. ;) Thanks for all the valuable input from all of you guys. I am going to use EntitySpaces for a CRM project I just began working on, cross your fingers.
|
|
-
02-05-2007, 1:21 AM |
-
DarkStar
-
-
-
Joined on 02-01-2007
-
Poland
-
Posts 21
-
-
|
Re: Data Binding a DataGridView
Well, I bought EntitySpaces license today. I was mainly convinced to it by the great support I experienced here. That's just a "thank you" post. :)
|
|
|
|
|