I have a datagridView on Form1. When I double-click on an item in that Datagridview, I go to a detailform where I get to see the details of the line in the datagridview. (I get them based on the id of the record I keep in the datagridview). Now I want to make prev/next buttons on the detailform so that the user doesn't have to go back to form1.
I can access the datagridview (which is public), but I can't see the row info.
In the debugger I can see the row info if I start from
this.Owner.Controls[0].Controls[2].Controls[0]
and then click to open the Rows
properties. When I enter this.Owner.Controls[0].Controls[2].Controls[0].Rows[0]
, I get error CS1061.
How can I fix this?
CodePudding user response:
This is a data type issue. Looking at this expression:
this.Owner.Controls[0].Controls[2].Controls[0].Rows[0]
We can follow the data type of each part of the expression from beginning to end to see the problem.
First, the type of this
is the current Form, which will be something inherited from System.Windows.Forms.Form
. From there, we check that type's .Owner
property, and see it also a (nullable) System.Windows.Forms.Form
.
Now we start looking at the Form
type's .Controls
member. We find the datatype here by understanding that Form
inherits from System.Windows.Forms.Control
, which leads us to the property definition. Now we learn we're looking at a System.Windows.Forms.ControlCollection
. This type has an indexer property that allows you to make the [0]
and [2]
lookups (probably better to do this by name, fwiw). The documentation for this property tells us the result of the indexer expression is back to System.Windows.Forms.Control
.
Now we know enough to understand that when we take this expression:
this.Owner.Controls[0].Controls[2].Controls[0]
the data type chain for the expression looks like this:
this .Owner .Conrols [0] .Controls [2] .Controls [0]
Form > Form > ControlCollection > Control > ControlCollection > Control > ControlCollection > Control
It shows the final result is a System.Windows.Forms.Control
. From there we go look for the .Rows
property and find... nothing. The basic Control
type does not provide this property.
So why does it work in the debugger? At run time, we aren't dealing with base Control
objects. We are dealing with types that inherit from Control
, like Panel, TextBox, or DataGridView. It just so happens the DataGridView
control does provide a Rows
property. The debugger allows you to resolve this to use the run time type, but the compiler doesn't know what's going to happen once the program starts running; it has to be more strict. For example, you might have a button to remove the gridview and put something else there instead, and now the Rows
expression would no longer be valid.
You can smooth over the compiler issue with a cast:
var dgv = (DataGridView)this.Owner.Controls[0].Controls[2].Controls[0];
var row = dgv.Rows[0];
But this is tightly-coupled to the form layout. Move something around in the designer and suddenly you're tracing through a lot of code to find out what went wrong. You will be better off setting a reference directly to the DataGridView in the child form before even showing it on the screen.
var child = new detailForm(this);
child.ParentGrid = this.DataGridView1; // assuming you declare a public property named "ParentGrid" and your control is named DataGridView1
child.ShowDialog();
Finally, you may be wondering whether you're expected to work through a data type expression chain like that for every line of code you use. I'd say you are expected to understand what data types you're working with at every level of every expression. Fortunately, this doesn't mean you need to have it all memorized. It doesn't take much experience to understand how the Windows Forms types fit together generally, so you can understand these data types without having to go chase through the documentation on every single thing. And speaking of "chasing through the documentation. When you really don't know, you are expected to know how to read the docs to find the answer.
CodePudding user response:
Assuming this is a read-only operation, I would pass the DataGridView
to the detail form. You could create a constructor for the detail form that takes a DataGridView
as a parameter, or create some kind of public void Populate(DataGridView dgv) { }
function that you call after the form constructor.
If you intend on changing data in the DataGridView
from the detail page, and expect it to reflect on the main form, then you can either create a event on the detail form that the main form listens for, or after the detail form closes, retrieve the stored DataGridView
and look for changes (or just overwrite).