referencing ref variables in forms

D

doofy

I've got a form associated with a custom control I'm doing.

I want to include a ref variable in a constructor for the form, so I can
feed info back out to the calling control.

When I try to reference that variable in the Ok button code, it can't
see it. I tried prepending it with 'this', tried with 'frmConnect',
still can't access it.

Here's the code:

using numerous.namespaces;

namespace FormConnect
{
public partial class frmConnect : Form
{
public frmConnect()
{
InitializeComponent();
}

public frmConnect(ref string selList)
{
InitializeComponent();
selList = ""; // this compiles
}

private void btnOK_Click(object sender, EventArgs e)
{
foreach(int itm in lstTables.SelectedIndices)
{
selList = selList + lstTables.Items[itm].ToString() + ":";
//this doesn't compile.
}
}
}
}


The selList variable here at the bottom is not accessing the variable
passed through the constructor.

I haven't debugged all of this yet because it keeps getting hung on the
selList problem.

Any ideas?
 
M

Misbah Arefin

the variable selList in the constructor is a local variable
accessible/visible only to the constructor... you need to save this ref in a
class variable/field so it can be used at a later time e.g. in your button
click method
change your contructor to as follows

//add a definition for m_selList in your class

public frmConnect(ref string selList)
{
InitializeComponent();
selList = ""; // this compiles
m_selList = selList;
}

//and your btnOk_Click to

private void btnOK_Click(object sender, EventArgs e)
{
foreach(int itm in lstTables.SelectedIndices)
{
m_selList = m_selList + lstTables.Items[itm].ToString() + ":";
}
}
 
P

Peter Duniho

I've got a form associated with a custom control I'm doing.

I want to include a ref variable in a constructor for the form, so I can
feed info back out to the calling control.

When I try to reference that variable in the Ok button code, it can't
see it. I tried prepending it with 'this', tried with 'frmConnect',
still can't access it.

As I already wrote in my reply to your previous thread, passing a variable
by reference isn't going to do you any good. The "by reference" aspect is
valid only within the method being called.

As far as Misbah's reply goes, his changes will allow the code to compile,
but it won't actually do what it appears you want to do. Whatever
variable is passed into the constructor, changing the "m_selList" variable
in the class isn't going to affect the value of the passed-in variable.

There are a variety of ways that you _can_ cause information to flow from
one class to another. But passing by reference is not generally going to
be among those ways. As I wrote before, it only works for the immediate
call...once the method that had the by-reference parameter has returned,
there's no longer any indirect reference to whatever was passed in for
that parameter.

If you'd like to be more specific, with code examples, about how exactly
you'd like this to actually work, we may be able to offer advice as to a
way that you can do this. Among the various ways, which one is best
depends a lot on just how you want the information to flow and in which
class you want to expose the connection that will allow it to flow.
Without knowing what you're really trying to do, it's hard to make a
specific suggestion as to what technique you'd likely prefer.

Pete
 
M

Misbah Arefin

Thanks for the reply Peter, I had just realized that i had made a mistake in
my reply and was about to send an updated rpely but saw your post
in my reply i forgot to mention that any change to the class variable will
not affect the value of the passedin variable:(
 
D

doofy

Peter said:
As I already wrote in my reply to your previous thread, passing a
variable by reference isn't going to do you any good. The "by
reference" aspect is valid only within the method being called.

As far as Misbah's reply goes, his changes will allow the code to
compile, but it won't actually do what it appears you want to do.
Whatever variable is passed into the constructor, changing the
"m_selList" variable in the class isn't going to affect the value of
the passed-in variable.

There are a variety of ways that you _can_ cause information to flow
from one class to another. But passing by reference is not generally
going to be among those ways. As I wrote before, it only works for the
immediate call...once the method that had the by-reference parameter
has returned, there's no longer any indirect reference to whatever was
passed in for that parameter.

If you'd like to be more specific, with code examples, about how
exactly you'd like this to actually work, we may be able to offer
advice as to a way that you can do this. Among the various ways, which
one is best depends a lot on just how you want the information to flow
and in which class you want to expose the connection that will allow it
to flow. Without knowing what you're really trying to do, it's hard to
make a specific suggestion as to what technique you'd likely prefer.

Pete

THanks Pete. I'm still digesting your whole post from previously. I
thought I had an understanding of it, but I guess not.
 
D

doofy

THanks Pete. I'm still digesting your whole post from previously. I
thought I had an understanding of it, but I guess not.

Ok, now I've got my code on a machine that has internet access. In
general, I'm trying to create a custom control. That custom control
holds a tree view, a button for hiding the tree view, a label, and a
context menu. In the end, the tree view will hold table and field
information from a database.

I want to right click on the tree view, bring up the context menu, then
click on the Connect To DB menu choice.

At that point, it will pull up a form that is part of the custom control
project. That form includes a combo box for selecting a server, a
combo box for selecting a database (neither of these work right now and
I'm just using a hard coded connection string), and a list box for
containing table names from the designated database. It is a multiple
select list box. I want to send the selected table name items back to
the tree view which will then populate itself with the details of those
selected tables.

Hopefully you don't need the designer code too.

here's the code for the main custom control:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Microsoft.SqlServer.Server;
using System.Data.SqlClient;



namespace dbTree
{

public partial class DBTreeContainer : UserControl
{
public DBTreeContainer()
{
InitializeComponent();

}
public DBTreeContainer(string connStr)
{
InitializeComponent();

}
private string connStr;
private string dbName;
private string server;
private int width;
private int height;
private int top;
private bool expanded = true;
private int numTables;
private string tblList;
private string selList;




private void connectToDBToolStripMenuItem_Click(object sender,
EventArgs e)
{
// build connection string
//SqlConnection cn = new SqlConnection();



// fill tree
FormConnect.frmConnect frmConn = new
FormConnect.frmConnect(ref selList);


frmConn.Show();



}

private void btnExpand_Click(object sender, EventArgs e)
{
if (expanded == false)
{
// expand tree
dbTreeView.Height = 360;

// set flag true
expanded = true;

}
else
{
// collapse tree
dbTreeView.Height = 0;

// set flag false
expanded = false;
}
}

}

}




Here is the code for the form that gets called:



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.ProviderBase;
using System.Data.SqlClient;

namespace FormConnect
{
public partial class frmConnect : Form
{
public frmConnect()
{
InitializeComponent();
}

public frmConnect(ref string selList)
{
InitializeComponent();
selList = "";
}


private string cnString;

public string cnStringProp
{
get
{
return cnString;
}
set
{
cnString = value;
}
}


private void frmConnect_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the
'comEdBisDataSet.COM_TAP_ACCOUNT_CONTRACT' table. You can move, or
remove it, as needed.

this.cnString = @"Data Source=TOSHIBA-USER\SQLEXPRESS;" +
"Integrated Security=SSPI;Initial
Catalog=ComEdBis;" +
"Connect Timeout=30";

string queryString = "select table_catalog, table_name
from information_schema.tables";
using (SqlConnection connection = new SqlConnection(
cnString))
{
SqlCommand command = new SqlCommand(
queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
this.lstTables.Items.Add(reader[1]);
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
}

private void cboServer_SelectedIndexChanged(object sender,
EventArgs e)
{

}

private void btnOK_Click(object sender, EventArgs e)
{

foreach (int itm in lstTables.SelectedIndices)
{
selList = selList + lstTables.Items[itm].ToString() +
";";
}
}

}
}
 
P

Peter Duniho

[...] I want to send the selected table name items back to the tree view
which will then populate itself with the details of those selected
tables.

Hopefully you don't need the designer code too.

I'm not going to use the code you posted. It's not concise enough, and I
don't feel like simplifying it myself.

As far as what you want to do goes, it sounds to me as though one obvious
approach would be to declare an event on your input form, to which the
custom control can subscribe. The event will be raised when the data on
the input form changes, and the custom control's handler would then take
the information in the event and do something with it.

See my comments after this code example (I'm leaving out all but the stuff
directly related to the event...obviously these would not compile or work
as-is):

class InputChangedEventArgs : EventArgs
{
private string _strInput;

public InputChangedEventArgs(string strInput)
{
_strInput = strInput;
}

public string Input
{
get { return _strInput; }
}
}

delegate void InputChangedEventHandler(object sender,
InputChangedEventArgs e);

class frmConnect : Form
{
public frmConnect()
{
InitializeComponent();
}

public event InputChangedEventHandler InputChanged;

protected void OnInputChanged(object sender, InputChangedEventArgs
e)
{
InputChangedEventHandler handler = InputChanged;

if (handler != null)
{
handler(sender, e);
}
}

private void btnOK_Click(object sender, EventArgs e)
{
foreach (int itm in lstTables.SelectedIndices)
{
selList = selList + lstTables.Items[itm].ToString() +";";
}

OnInputChanged(this, new InputChangedEventArgs(selList));
}
}

class DBTreeContainer : UserControl
{
private void connectToDBToolStripMenuItem_Click(object sender,
EventArgs e)
{
// build connection string
//SqlConnection cn = new SqlConnection();

// fill tree
FormConnect.frmConnect frmConn = new FormConnect.frmConnect();

frmConn.InputChanged += MyInputChangedHandler;

frmConn.Show();
}

private void MyInputChangedHandler(object sender,
InputChangedEventHandler e)
{
// This method will be called when the "selList" variable would
// have changed. The "e" paramter has an "Input" property that
// you can get that contains the value of the string that the
// "selList" variable would have had (and did in fact have in
// the scope in which that variable does exist).

// So, do whatever you want here using the value "e.Input" as
the
// input string from your form. For example, populating the
tree
// view control with the information from the tables in the
string.
}
}

My apologies in advance for any compilation errors. I didn't actually
compile the code above, and might have some typos. But it should be close.

Also, while the above matches as closely as possible the original design
you present, that doesn't mean it's actually the best design. In
particular, you appear to be concatenating the list into a single
';'-separated string. You didn't post code showing how you'd use that
string, but I'd guess you're just going to split that string up again upon
receipt.

If so, then you're probably better off just passing an array of strings in
the event in the first place. One obvious way to do that would be to
declare the data as "string[]" instead of "string", creating the array in
the "btnOK_Click()" method by appending each list box item value into a
local List<string> variable and then using the List.ToArray() method to
pass the resulting list as an array to the constructor of
InputChangedEventArgs. For example:

class InputChangedEventArgs : EventArgs
{
private string[] _rgstrInput;

public InputChangedEventArgs(string[] rgstrInput)
{
_rgstrInput = rgstrInput;
}

public string[] Input
{
get { return _rgstrInput; }
}
}

and then...

private void btnOK_Click(object sender, EventArgs e)
{
List<string> lstr = new List<string>();

foreach (int itm in lstTables.SelectedIndices)
{
lstr.Add(lstTables.Items[itm].ToString());
}

OnInputChanged(this, new
InputChangedEventArgs(lstr.ToArray()));
}

With appropriate changes elsewhere to deal with the
InputChangedEventArgs.Input property correctly, of course.

If for some reason you wanted to be super-careful and prevent that array
from being mutable, you could even return a ReadOnlyCollection<string>
instead of a string[]. For example:

class InputChangedEventArgs : EventArgs
{
private ReadOnlyCollection<string> _rgstrInput;

public InputChangedEventArgs(IList<string> lstrInput)
{
_rgstrInput = new ReadOnlyCollection<string>(lstrInput);
}

public ReadOnlyCollection<string> Input
{
get { return _rgstrInput; }
}
}

These are only examples. You can construct a ReadOnlyCollection<string>
from anything that implements IList<string>, so you could just pass the
List<string> to the constructor of InputChangedEventArgs instead of
converting it to an array, or any other collection class that might be
more appropriate than a string[].

Another option for making the results read-only would be to implement an
indexer on the InputChangedEventArgs class, hiding the actual collection
instance, but since that class isn't really a collection itself per se,
I'm not a big fan of doing it that way.

Hope that helps.

Pete
 
M

Mufaka

doofy wrote:
I want to right click on the tree view, bring up the context menu, then
click on the Connect To DB menu choice.

At that point, it will pull up a form that is part of the custom control
project. That form includes a combo box for selecting a server, a
combo box for selecting a database (neither of these work right now and
I'm just using a hard coded connection string), and a list box for
containing table names from the designated database. It is a multiple
select list box. I want to send the selected table name items back to
the tree view which will then populate itself with the details of those
selected tables.
<snip>
I may be missing something here, but why can't this be a Dialog that can
expose a property that contains the list of selected tables?

From the context menu click event you can do something like this:

frmConn dlg = new frmConn();
DialogResult result = dlg.ShowDialog(); // blocks until dialog is closed

if (result == DialogResult.OK)
{
foreach (string tableName in dlg.SelectedTables)
{
... add nodes for selected tables ...
}
}

Your frmConn would just need to return a list of selected items from
your multi-select list through a property.

public List<string> SelectedTables
{
get
{
List<string> selected = new List<string>();

// pseudo
foreach selected item in your list view, add to 'selected' list.

return selected;
}
}
 
D

doofy

Mufaka said:
doofy wrote:

<snip>
I may be missing something here, but why can't this be a Dialog that can
expose a property that contains the list of selected tables?

From the context menu click event you can do something like this:

frmConn dlg = new frmConn();
DialogResult result = dlg.ShowDialog(); // blocks until dialog is closed

if (result == DialogResult.OK)
{
foreach (string tableName in dlg.SelectedTables)
{
... add nodes for selected tables ...
}
}

Your frmConn would just need to return a list of selected items from
your multi-select list through a property.

public List<string> SelectedTables
{
get
{
List<string> selected = new List<string>();

// pseudo
foreach selected item in your list view, add to 'selected' list.

return selected;
}
}

Thanks. That answers another question I had about the timing of the
call to the form, and what do I do after the call, and how to sequence
the calling code with form activity.
 
P

Peter Duniho

I may be missing something here, but why can't this be a Dialog that can
expose a property that contains the list of selected tables?

That's fine if it's okay to not get the information until the dialog is
dismissed. "Doofy"'s question seemed to imply that he wanted immediate
updating of the data as it changed, but yes...if all he wants is the
information once the dialog's dismissed, it can be exposed as a property
without the event I suggested.

Pete
 
D

doofy

Peter said:
That's fine if it's okay to not get the information until the dialog is
dismissed. "Doofy"'s question seemed to imply that he wanted immediate
updating of the data as it changed, but yes...if all he wants is the
information once the dialog's dismissed, it can be exposed as a
property without the event I suggested.

Pete

yes, that's all I need is to take action after the dialog is closed.
 
D

doofy

doofy said:
yes, that's all I need is to take action after the dialog is closed.

The advice yall gave me is working. I did not think about leaving the
form open after I hid it, and pulling over the data before disposing of it.

I'm having to learn this thing in a "free-range" manner because the
class I signed up for got cancelled. Seems like the colleges around
here are having a hard time making IT classes go, given the number of
people leaving the industry.
 
P

Peter Duniho

The advice yall gave me is working. I did not think about leaving the
form open after I hid it, and pulling over the data before disposing of
it.

Actually, you don't need to worry about closing the form. When a form is
shown using ShowDialog(), closing the form simply hides it even when you
call Close() directly or set one or more buttons to return a dialog result
(which closes the form implicitly), for the very reason that doing it that
way allows access to the control members to extract data.

In some respects it's sort of an arbitrary design choice on the part of
..NET, but it does mean that when you're showing a form modally (as in, as
a dialog), closing the form doesn't discard all of the data that was in
the form. This matches the way that a form shown modally would be used
for the vast majority of the time.

Do remember to dispose the form when you're done with it, of course. But
you shouldn't have to do anything special in the form itself with respect
to hiding instead of closing it. That happens automatically on your
behalf, without any effort on your part.

Pete
 
D

doofy

Peter said:
Actually, you don't need to worry about closing the form. When a form
is shown using ShowDialog(), closing the form simply hides it even when
you call Close() directly or set one or more buttons to return a dialog
result (which closes the form implicitly), for the very reason that
doing it that way allows access to the control members to extract data.

In some respects it's sort of an arbitrary design choice on the part of
.NET, but it does mean that when you're showing a form modally (as in,
as a dialog), closing the form doesn't discard all of the data that was
in the form. This matches the way that a form shown modally would be
used for the vast majority of the time.

Do remember to dispose the form when you're done with it, of course.
But you shouldn't have to do anything special in the form itself with
respect to hiding instead of closing it. That happens automatically on
your behalf, without any effort on your part.

I do dispose it after getting the data out.

Again, thanks for the help and patience. I'll get it sooner or later.
I just have to learn a new way of conceptualizing.
 

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