[...] If the query returns multiple results, a new window is opened
with a grid containing the results. When the user double clicks on
the desired row in the grid, I want the first form to populate with
the correct data. I don't know how to access that other form.
Basically on the double click event, I think I need to call an event
on the other form. How do I do that? [...]
You need a couple of things: some sort of reference back to the form you
want to update, and some sort of mechanism to accomplish that update.
The most direct method is to access an actual reference for the calling
form in the results form, and have that results window directly access the
fields on the original form that you want to update. There are at least
three variations on this, all somewhat "clunky":
1) Access your calling form directly by name. Eg "form1". Nice and
simple. Not very extensible, but it'll get the job done.
2) Access your calling form by type, passing the form instance
reference to the results window constructor or other mechanism (setting a
property after construction for example). Almost as simple as #1, you can
still access controls by field name, but can be extended to any instance
of the calling form (that is, any form with the specific type).
Note that in 1 and 2, the controls in your form need the access modifier
to be changed to public so that the results form can see them.
3) Access your calling form's control by name, using the
ControlCollection.Find() method to get the controls. Icky-pooh!

If
you're going to do something like this, IMHO you might as well just define
an interface that the query form exposes, because it's just as much work
and at least an interface is something that can be verified at compile
time.
I don't know what the VB "WithEvent stuff" does, but assuming it's similar
to C# events, then that's actually a better, more-maintainable solution.
You can declare an event on the results form, and then the form with the
query button on it can subscribe to the event. The event would be
designed to allow passing the interesting data back from the results form
to the query form. When the query form's event handler is executed it
would then update the query form directly.
For example:
class Results : Form
{
public delegate void DoubleClickHandler(ResultsData data);
public event DoubleClickHandler DoubleClick;
// Hooked to the double-click event of the grid
void grid_DoubleClick(object sender, EventArgs e)
{
DoubleClickHandler handler = DoubleClick;
if (handler != null)
{
ResultsData data = /* initialize results data to be passed
to calling form */
handler(data);
}
}
}
class Query : Form
{
void buttonQuery_Click(object sender, EventArgs e)
{
Results results = new Results();
results.DoubleClick = HandleResultsDoubleClick;
results.ShowDialog();
}
void HandleResultsDoubleClick(ResultsData data)
{
// extract information from "data" and fill in form
appropriately
}
}
The above assumes you've declared some "ResultsData" data structure
(struct or class, as appropriate) that can contain the data you want to
pass. You don't really have to do that though...you could just pass each
piece of data as an individual parameter in the event.
Note that the results form in this case needs to know exactly *nothing*
about the calling form. The results form declares a standard event that
any caller can subscribe to, and it's up to the caller to process that
event in some way that makes sense to itself.
The above is just an example. The specifics will vary according to your
needs. The key thing is to define the delegate, event, and event handler,
to hook the event handler up in the query form, and to call it at the
appropriate time in the results form.
Some people may complain that I didn't use the standard "(object sender,
EventArgs e)" parameter list used by the standard control-style events. I
think those are fine for the standardized events, but for a custom eventI
don't see the point, _unless that pattern actually fits well in your
design_. For example, your query form needs to know which form is
signaling the event, then you'd want to include an "object sender"
parameter.
Finally, I'll mention that you can also accomplish this by defining an
interface that the calling form is required to implement. As with the #2
solution in the "direct method" described above, you'd pass a reference to
your calling form to the results form. You can use an interface in a
similar way to the events. For example, defining a single method that
takes a ResultsData instance as a parameter and having the results form
call that method. The constructor or property on the results form that is
used to pass the interface could just use the interface type rather than
the original form type.
For example:
class Results : Form
{
public interface IDoubleClick
{
void HandleDoubleClick(ResultsData data);
}
public IDoubleClick DoubleClick;
// Hooked to the double-click event of the grid
void grid_DoubleClick(object sender, EventArgs e)
{
ResultsData data = /* initialize results data to be passed to
calling form */
DoubleClick.HandleDoubleClick(data);
}
}
class Query : Form, Query.IDoubleClick
{
void buttonQuery_Click(object sender, EventArgs e)
{
Results results = new Results();
results.DoubleClick = this;
results.ShowDialog();
}
void HandleDoubleClick(ResultsData data)
{
// extract information from "data" and fill in form
appropriately
}
}
This allows different forms to support the same interface and in some ways
is no different than providing an event handler. The main difference
being that if you use an event, it is trivial to attach multiple event
handlers to the event. An interface reference provides no such
functionality; if you wanted to support multiple event handlers, that
would have to be done explicitly.
Pete