.NET Forum / Languages / C# / March 2008
referencing ref variables in forms
|
|
Thread rating:  |
doofy - 04 Mar 2008 05:35 GMT 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?
Misbah Arefin - 04 Mar 2008 06:27 GMT 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() + ":"; } }
 Signature Misbah Arefin https://mcp.support.microsoft.com/profile/MISBAH.AREFIN http://www.linkedin.com/in/misbaharefin
> I've got a form associated with a custom control I'm doing. > [quoted text clipped - 42 lines] > > Any ideas? Peter Duniho - 04 Mar 2008 07:05 GMT > I've got a form associated with a custom control I'm doing. > [quoted text clipped - 4 lines] > 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
Misbah Arefin - 04 Mar 2008 08:32 GMT 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:(
 Signature Misbah Arefin https://mcp.support.microsoft.com/profile/MISBAH.AREFIN http://www.linkedin.com/in/misbaharefin
> > I've got a form associated with a custom control I'm doing. > > [quoted text clipped - 30 lines] > > Pete doofy - 04 Mar 2008 14:47 GMT >> I've got a form associated with a custom control I'm doing. >> [quoted text clipped - 31 lines] > > Pete THanks Pete. I'm still digesting your whole post from previously. I thought I had an understanding of it, but I guess not.
doofy - 04 Mar 2008 16:12 GMT >> 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 [quoted text clipped - 9 lines] > 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() + ";"; } }
} }
Peter Duniho - 04 Mar 2008 19:16 GMT > [...] 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
Mufaka - 04 Mar 2008 20:02 GMT <snip>
> I want to right click on the tree view, bring up the context menu, then > click on the Connect To DB menu choice. [quoted text clipped - 7 lines] > 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; } }
doofy - 04 Mar 2008 20:42 GMT > <snip> > [quoted text clipped - 42 lines] > } > } 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.
Peter Duniho - 04 Mar 2008 21:00 GMT > 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
doofy - 04 Mar 2008 21:52 GMT >> 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? [quoted text clipped - 6 lines] > > Pete yes, that's all I need is to take action after the dialog is closed.
doofy - 05 Mar 2008 06:44 GMT >>> 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? [quoted text clipped - 8 lines] > > 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.
Peter Duniho - 05 Mar 2008 07:17 GMT > 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
doofy - 05 Mar 2008 14:30 GMT >> 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 [quoted text clipped - 16 lines] > 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.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|