Follow up

H

Helmut Giese

Hello out there,
this is a follow-up to a thread from a couple of days ago
Can a 'ref' parameter be saved for later use?
Sorry for the delay - I had to handle a little catastrophe - but still
wanted to continue this discussion.

@Scott M.: Thanks for suggesting to use a Dictionary - makes for much
cleaner code.

@ Christoph Basedau
Seems some of these advanced C++-skills tend to obstruct
a clear view on the simple and orderly .NET world ;-)
I didn't see this. :)

To sum up the issue:
In the main form I manage 2 dictionaries. To give the user the
possibility to change some values I open another form. I pass the
appropriate dict and when the user is done editing he closes the form
and the data is passed back to the main form.
Works like it should, but my question concerned 'coupling'.
- In C++ I would pass a pointer to the respective dictionary and the
callee could just update it - no need to know anything about the
caller.
- In the code below, the 'secondary' form
-- receives as one parameter the 'main' form and
-- in order to udate the (possibly modified) data it also has to
know the main form's variable names.

No big deal, just curiosity and wanting to get better aquainted with
C#: How would you implement this scenario?

I could of course post the whole code but the designer generated stuff
is rather large - hopefully the "functional part" below is sufficient
to illustrate the issue.

Thanks for any ideas and best regards
Helmut Giese

--- Code from the 'main' form ---
public partial class MainForm : Form {
// 2 Dictionaries: parameters and signals
public Dictionary<string, string> paramNames, signalNames;

public MainForm() {
InitializeComponent();
// create 2 dictionaries and fill them
paramNames = new Dictionary<string, string>();
signalNames = new Dictionary<string, string>();
for (int i = 1; i <= 4; i++) {
paramNames.Add("Param" + i, "<undefined>");
signalNames.Add("Signal" + i, "<undefined>");
}
}
// 2 event handlers: for parameters and for signals
private void btnParams_Click(object sender, EventArgs e) {
new Assign(paramNames, true, this);
}
private void btnSignals_Click(object sender, EventArgs e) {
new Assign(signalNames, false, this);
}
}

--- Code from the 'secondary' form ---
public partial class Assign : Form {
MainForm mainForm; // 'back link'
bool isParam; // true: handling parameters, else signals

public Assign(Dictionary<string, string>names, bool isParam,
MainForm caller) {
InitializeComponent();
mainForm = caller;
this.isParam = isParam;
// fill the lists
foreach(KeyValuePair<string, string> pair in names) {
listKeys.Items.Add(new ListViewItem(pair.Key));
listValues.Items.Add(new ListViewItem(pair.Value));
}
Text = isParam ? "Define Parameters" : "Define Signals";
this.Show();
}

// User said 'Ok': Pass data back to 'main'
private void btnOk_Click(object sender, EventArgs e) {
Dictionary<string, string> res = new Dictionary<string,
string>();
// Get data from the ListViews
for (int i = 0; i < listKeys.Items.Count; i++)
res.Add(listKeys.Items.Text, listValues.Items.Text);
// ... and update the correct variable in 'main'
if (isParam)
mainForm.paramNames = res;
else
mainForm.signalNames = res;
Close();
}
}
 
P

Peter Duniho

Helmut said:
[...]
- In C++ I would pass a pointer to the respective dictionary and the
callee could just update it - no need to know anything about the
caller.
- In the code below, the 'secondary' form
-- receives as one parameter the 'main' form and
-- in order to udate the (possibly modified) data it also has to
know the main form's variable names.

No big deal, just curiosity and wanting to get better aquainted with
C#: How would you implement this scenario?

As I believe I mentioned in your previous thread (which is really where
you should have continued this discussion), in the general case you may
find that it's more useful for the secondary form to simply return a new
value via a property, which the caller can then assign as it sees fit
after the secondary form is closed.

If you don't want to wait until the secondary form is closed, then
instead just have some event declared in the secondary form that is
raised at the appropriate moment (i.e. whatever triggers the need to
update the main form's data structures), and at that time the main form
(which will have subscribed to that event) can inspect the property and
do the assignment, again as it sees fit.

Now, that said...in this particular example, yet another possible
approach is to simply not change the object instance at all. Just clear
the dictionary and reinitialize it with the new data. After all, in
your code example, the main form is already passing the reference to its
own data structures (however inadvisable that may be), so the secondary
form is using the same instance the main form is.

Finally, I would strongly recommend against the "Assign" form calling
the Show() method from its constructor. There is no functional need to
do so, and it ties the behavior of the form to the implementation,
without giving the client any control over it (e.g. to modify properties
of the secondary form, such as position or size, or even assigning a
property that would reflect the data in the ListView).

Pete
 
H

Helmut Giese

Hi Pete,
Hi Pete,
As I believe I mentioned in your previous thread (which is really where
you should have continued this discussion), in the general case you may
sorry, I thought it would be a good idea to start a new thread.
find that it's more useful for the secondary form to simply return a new
value via a property, which the caller can then assign as it sees fit
after the secondary form is closed.
Yes, I remember, and I didn't understand the 'after the secondary form
is closed' part. Somehow I thought 'closing == destroying' - your
remark about not calling 'Show' in the constructor was an eye opener.
Thank you.

[snip]
Now, that said...in this particular example, yet another possible
approach is to simply not change the object instance at all. Just clear
the dictionary and reinitialize it with the new data. After all, in
your code example, the main form is already passing the reference to its
own data structures (however inadvisable that may be), so the secondary
form is using the same instance the main form is.
Please elaborate on this - I don't get it:
- I receive the reference in the constructor, but
- I need it when the user is done, so
- how do I save it?
It would be nice if you could take the code I posted and insert the
statements needed to accomplish this. (Actually the attempt to achieve
exactly this was the idea behind my original posting - but apparently
I didn't get it across.)
Finally, I would strongly recommend against the "Assign" form calling
the Show() method from its constructor. There is no functional need to
do so, and it ties the behavior of the form to the implementation,
without giving the client any control over it (e.g. to modify properties
of the secondary form, such as position or size, or even assigning a
property that would reflect the data in the ListView).
The eye opener mentioned above.

Thanks for your help and best regards
Helmut Giese
 
P

Peter Duniho

Helmut said:
[...]
Now, that said...in this particular example, yet another possible
approach is to simply not change the object instance at all. Just clear
the dictionary and reinitialize it with the new data. After all, in
your code example, the main form is already passing the reference to its
own data structures (however inadvisable that may be), so the secondary
form is using the same instance the main form is.

Please elaborate on this - I don't get it:
- I receive the reference in the constructor, but
- I need it when the user is done, so
- how do I save it?

None of the above is of concern. That is, you don't need to "save it",
at least not in any other way than your example already does, and
needing it when the user is done is trivial, because the main form still
have is (just because you pass a object reference to some other method,
that doesn't mean the code doing the passing no longer has it...quite
the contrary, actually).
It would be nice if you could take the code I posted and insert the
statements needed to accomplish this. (Actually the attempt to achieve
exactly this was the idea behind my original posting - but apparently
I didn't get it across.)

Okay, try this:

public partial class Assign : Form
{
private Dictionary<string, string> _names;

public Assign(Dictionary<string, string>names, bool isParam)
{
InitializeComponent();
_names = names;

// fill the lists
foreach(KeyValuePair<string, string> pair in _names)
{
listKeys.Items.Add(new ListViewItem(pair.Key));
listValues.Items.Add(new ListViewItem(pair.Value));
}
Text = isParam ? "Define Parameters" : "Define Signals";
}

// User said 'Ok': Pass data back to 'main'
private void btnOk_Click(object sender, EventArgs e)
{
_names.Clear();

// Get data from the ListViews
for (int i = 0; i < listKeys.Items.Count; i++)
{
_names.Add(listKeys.Items.Text, listValues.Items.Text);
}

Close();
}
}

I'm not really a big fan of this particular approach. Having two
different objects share data where both objects are allowed to mutate
that data seems a bit "off" to me, and could lead to maintenance
problems. But, in this case the sharing seems to be done in a
well-defined, limited way. So at least it's not as bad as it could be.

Still, presumably the main form still needs to _do_ something with the
new data once it's been updated. So the main form still needs some form
of notification that the secondary form has been closed. Given that, I
still think it would be much better for the secondary form to simply
provide an API to do the updating of the values. The code's a little
more complicated in the secondary form, but it's also quite a bit more
"modular":

public partial class Assign : Form
{
private Dictionary<string, string> _names = new Dictionary<string,
string>();

public Assign(Dictionary<string, string>names, bool isParam)
{
InitializeComponent();

// fill the lists
foreach(KeyValuePair<string, string> pair in names)
{
listKeys.Items.Add(new ListViewItem(pair.Key));
listValues.Items.Add(new ListViewItem(pair.Value));
}
Text = isParam ? "Define Parameters" : "Define Signals";
}

// User said 'Ok': Pass data back to 'main'
private void btnOk_Click(object sender, EventArgs e)
{
// Get data from the ListViews
for (int i = 0; i < listKeys.Items.Count; i++)
{
_names.Add(listKeys.Items.Text, listValues.Items.Text);
}

base.OnFormClosed(sender, e);
}

// One way is to simply return the new dictionary, so
// the main form can then assign the value to its own
// member field or other variable:
public Dictionary<string, string> NamesDictionary
{
get { return _names; }
}

// Another way would be to allow the main form to call
// a method that copies the values over:
public void CopyNames(Dictionary<string, string> namesDst)
{
namesDst.Clear();

foreach (KeyValuePair<string, string> kvp in _names)
{
namesDst.Add(kvp.key, kvp.Value);
}
}
}

Note that because you're showing the form modelessly (i.e. with Show()),
you _must_ save the data from the form's controls before the form
closes. However, if this were a modal form (i.e. shown with
ShowDialog()), a couple of things change:

-- You can configure the button to automatically close the form,
and override OnFormClosed() instead of having to have a button Click
event handler that calls Close(), and

-- There's no need to create and save an intermediate copy of the
dictionary, because you can simply extract the data from the control at
the time the main form attempts to retrieve the data (a form shown with
ShowDialog() is not disposed when it's closed...in fact, it's never
really "closed" at all, but instead is hidden; and yes, this means you
always have to dispose a modal form explicitly).

Because of the second point above, the method-oriented technique is
probably preferable to the property-oriented technique, because it gives
you the opportunity to operate directly on the caller's own dictionary,
without having to create a new one. But, even the property-oriented one
can be nicer with a modal dialog, if for no other reason than that you
don't have to explicitly handle the closing of the form, and can just
create the dictionary on-demand.

In any case, with either the property-oriented or method-oriented
technique, the main form would simply retrieve the property value or
call the method at the appropriate time, when the secondary form has
been closed or otherwise signals to the main form it's time to update
itself (none of which should require the secondary form to know anything
about the main form...if not with the main form simply handling the
FormClosed event in the secondary form, then with some other event
declared in the secondary form that the main form handles).

Hope that helps.

Pete
 
M

Michael C

Helmut Giese said:
- In C++ I would pass a pointer to the respective dictionary and the
callee could just update it - no need to know anything about the
caller.

And C# is no different. You can pass a dictionary object from one form to
the other and keep a pointer to it. It's basically the same thing we just
don't call them pointers any more.

Michael
 
H

Helmut Giese

Hi Pete,
sorry for the delay - last week's little catastrophe continues to
haunt me.
I cannot look in detail at your answer now (not at this time of the
day) but I just wanted to say that you're really an extraordinary
helpful person. What would we do without you? :)

Many, many thanks for your time and advice and best regards
Helmut Giese
[snip a lot of helpful explanations and examples]
 
P

Peter Duniho

Helmut said:
Hi Pete,
sorry for the delay - last week's little catastrophe continues to
haunt me.
I cannot look in detail at your answer now (not at this time of the
day) but I just wanted to say that you're really an extraordinary
helpful person. What would we do without you? :)

Probably, you'd just get advice from the other helpful people who use
this newsgroup. :)

But thanks very much for the kinds words. I do try my best to be helpful.
Many, many thanks for your time and advice and best regards

You're very welcome.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top