Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / March 2008

Tip: Looking for answers? Try searching our database.

referencing ref variables in forms

Thread view: 
Enable EMail Alerts  Start New Thread
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.

Rate this thread:







Free Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.