Deverill
Deverill

Reputation: 971

Nested class can't access "parent" variable

Please excuse my lack of training/understanding in this issue.

I have a complex asp.net / c# package that is a highly customized ecommerce store. I'll try to simplify my situation for ease of understanding.

I have a custom control and in that control is defined like this:

public partial class ConLib_Custom_ProductDialog : System.Web.UI.UserControl

and within that class is this:

private class QtyBoxTemplate : System.Web.UI.ITemplate

and within that class I am addressing the value of a bool _EventSelected with:

  if (_EventSelected)

which is defined in the outer class as:

public static bool _EventSelected;

I don't want static because it can cause "collisions" between website users since static variables are global to the Application. We've had this problem before and I could just take off the static keyword and it worked.

In this case, if I remove the static I get an error which I kinda understand:

Cannot access a non-static member of outer type ConLib_Custom_ProductDialog via nested type ConLib_Custom_ProductDialog.QtyBoxTemplate

It seems since QtyBoxTemplate is nested it can't access the bool variable in the parent class.

I found another similar question here that says as much and that there are three solutions:

1. Make the (method) variable static.
2. Use inheritance instead of nested classes.
3. Create an instance of the outer class.

The first is a problem since it's a variable instead of a method as in the original post, but it's how I've been working so far and didn't realize it.

As for the second, I don't think inheritance is an option since QtyBoxTemplate is already inheriting the System ITemplate.

Lastly it seems weird to create an instance of the outer class just to access one variable and in my inexperience I don't even know how that could work - When do I create the instance, do I have to pass it around, etc. Since it's a custom control I don't even know if that can be done since isn't it already instantiated by the parent page on which it lives?

Again, apologies for asking a question that is probably basic to most of you but this is a part I just don't get. I realize the code is somewhat complex and hope I captured the essence of it well enough to make sense. Help is greatly appreciated.

Update: Trying to add more info. I have a data grid where each row is a product. Inside that grid row I have a drop down list for quantity they want to purchase, from 1 to 10. However, if they have picked seats on a tour and have already selected a date/time to go they can't change the quantity or it will mess up the reservation system so I check _EventSelected to disable the quantity DDL if that is the case.

We are using a DDL for quantity because it's a mobile store and they are easier than the up/down arrows in the original store. This is all very complex - a commercial store package and 2 different contractors have worked on it to tie it into another reservation system and I have to understand it all and make it work. Wish I knew more .NET/C#! Thanks for trudging through this explanation to try to help.

Here's my QtyBoxTemplate class:

 private class QtyBoxTemplate : System.Web.UI.ITemplate
    {            
        private DataControlRowType templateType;
        private string columnName;

        public QtyBoxTemplate(DataControlRowType type, string colname)
        {
            templateType = type;
            columnName = colname;
        }

        public void InstantiateIn(System.Web.UI.Control container)
        {
            Literal lc = new Literal();

            switch (templateType)
            {
                case DataControlRowType.Header:
                    // build the header for this column
                    //lc.Text = "<b>" + BreakCamelCase(columnName) + "</b>";
                    lc.Text = "<b>" + columnName + "</b>";
                    container.Controls.Add(lc);
                    break;
                case DataControlRowType.DataRow:
                    //--plugables: Now we need a drop down list instead of a quantity box
                    DropDownList ddList = new DropDownList();
                    ddList.Items.Add(new ListItem("0", "0"));
                    ddList.Items.Add(new ListItem("1", "1"));
                    ddList.Items.Add(new ListItem("2", "2"));
                    ddList.Items.Add(new ListItem("3", "3"));
                    ddList.Items.Add(new ListItem("4", "4"));
                    ddList.Items.Add(new ListItem("5", "5"));
                    ddList.Items.Add(new ListItem("6", "6"));
                    ddList.Items.Add(new ListItem("7", "7"));
                    ddList.Items.Add(new ListItem("8", "8"));
                    ddList.Items.Add(new ListItem("9", "9"));
                    ddList.Items.Add(new ListItem("10", "10"));
                    ddList.Items.Add(new ListItem("20", "20"));
                    ddList.Items.Add(new ListItem("40", "40"));
                    ddList.ID = "Quantity";
                    ddList.Font.Size = FontUnit.Parse("18px");
                    if (_EventSelected)
                        ddList.Enabled = false;
                    container.Controls.Add(ddList);
                    break;
                default:
                    break;
            }
        }
    }

And it lives inside a class with dozens of methods, classes, etc... way too much to post here (2500+ lines) but it starts like:

public partial class ConLib_Custom_BuyTourProductDialogGalaxy : System.Web.UI.UserControl
{ 
    #region class variables

    int _ProductId = 0;
    Product _Product = null;
    List<int> _SelectedKitProducts = null;
    ProductVariantManager _VariantManager;
    PersistentCollection<ProductVariant> _AvailableVariants;
    DataTable _DataTable;
    ProductOptionCollection _ProdOptions;

    private bool _ShowAvailableSeats = true;
    public DateTime _SelectedDate;
    public int _EventTypeId;
    public bool _CollectRosterData;
    public Envelope _Inventory;
    private string _DTFormat = "yyyy-MM-dd HH:mm:ss";

    public bool _EventSelected;

Upvotes: 1

Views: 2899

Answers (2)

Magnus
Magnus

Reputation: 46909

Since QtyBoxTemplate is private it can only be created inside the ConLib_Custom_ProductDialog class.
I would suggest you add ConLib_Custom_ProductDialog as a parameter to the QtyBoxTemplate constructor and set it as a local variable. You can then access _EventSelected using that reference.

public partial class ConLib_Custom_ProductDialog : System.Web.UI.UserControl
{
    public bool _EventSelected;

    public void SomeFunctionThatCreatesQtyBoxTemplate()
    {
        var q = new QtyBoxTemplate(this); //Include reference to itself
        q.SetEventSelected();
    }

    private class QtyBoxTemplate : System.Web.UI.ITemplate
    {
        private ConLib_Custom_ProductDialog _ccp;

        public QtyBoxTemplate(ConLib_Custom_ProductDialog ccp)
        {
            _ccp = ccp;
        }

        public void SetEventSelected()
        {
            _ccp._EventSelected = true; //Access to "parent" EventSelected 
        }
    }
}

Upvotes: 1

NathanAldenSr
NathanAldenSr

Reputation: 7961

You can't access instance members of a class from within a nested class. It makes no sense because the nested class does not have a reference to a specific instance of the outer class, unless you pass it one. You could pass an instance of the outer class when constructing instances of the nested class, but without more code, it's difficult for me to know whether this solution would work for you.

Said a different way: Try and rely on dependency inversion principle whenever you can. Avoid referencing things outside of the class you're working on.

Upvotes: 1

Related Questions