3

I need to access the controls created by CreateChildControls() from another class, so that when I choose the file I have the path on a string to refer to.

I have tried the solutions in Accessing controls created dynamically (c#) and Problem in accessing dynamically created controls But with no joy thanks

    publicTextBox txtUrl; 

    protected override void CreateChildControls()
    {
        Label lblUrl = new Label();
        lblUrl.ID = "lblUrl";
        lblUrl.Text = "Url: ";
        Controls.Add(lblUrl);

        TextBox txtUrl = new TextBox();
        txtUrl.ID = "txtUrl";
        Controls.Add(txtUrl);

        AssetUrlSelector picker = new AssetUrlSelector();
        picker.ID = "ausUrl";

        picker.DefaultOpenLocationUrl =  OpenUrl;
        picker.AssetUrlClientID = txtUrl.ClientID;
        picker.AssetUrlTextBoxVisible = false;
        Controls.Add(picker);

        Control control = Page.LoadControl(_ascxPath);
        Controls.Add(control);

    }

From another class I should be able to access the textbox

   protected void Button1_Click(object sender, EventArgs e)
    {
        AssetPicker asspi = new AssetPicker();

        string aaa = asspi.txtUrl.Text;



    }
Community
  • 1
  • 1
user1211929
  • 1,190
  • 1
  • 12
  • 32

2 Answers2

5

I had to make the controls public to be accessible from another class. but it retuns null reference error. I have updated the initial post

If you expose your child controls publicly, you need to call EnsureChildControls in the getter for each publicly-exposed child control. This will force CreateChildControls to be executed, and hence your control tree to be built, ensuring the caller does not get a null reference.

E.g.:

public Button MyChildButton
{
    get
    {
        EnsureChildControls();
        return _myChildButton;
    }
}
private Button _myChildButton;

...

protected override void CreateChildControls()  
{
    ...
    _myChildButton = new Button();
    ... 
}

Note that in order to do this, you need to expose your child controls as properties, not fields. I.e. in your sample code, you need to replace:

public TextBox txtUrl;  

by:

public TextBox TxtUrl
{
    get
    {
        EnsureChildControls();
        return txtUrl;
    }
}
private TextBox txtUrl;

You should also inherit from CompositeControl, which does something similar for the Controls property:

public override ControlCollection Controls
{
    get
    {
        EnsureChildControls();
        return base.Controls;
    }
}

If for some reason you are not inheriting from CompositeControl, then you'll need to add this Controls override to your class.

Incidentally, exposing child controls might be giving too much information to your callers, who probably shouldn't be concerned with such implementation details. Instead you could expose only the relevant properties of your child controls. For example, instead of exposing a child TextBox TxtUrl, you could expose a string property Url thus:

public string Url
{
    get
    {
        EnsureChildControls();
        return txtUrl.Text;            
    }
    set
    {
        EnsureChildControls();
        txtUrl.Text = value;
    }
}
Joe
  • 122,218
  • 32
  • 205
  • 338
  • 1
    Hi thanks for your enser I tried the second bit of code, it now returns a Warning: txtUrl is never assigned to and will always have its default value null – user1211929 May 27 '12 at 19:33
  • "is never assigned to and will always have its default value null " - in that case you need to assign it, which you should do in the CreateChildControls method - "txtUrl = new TextBox();" etc... rather than "TextBox txtUrl = new TextBox();" – Joe May 28 '12 at 15:53
0

At the end, what .NET does when you add a static control to a page, it will hold a reference as of the control as a field (they usually go to the .designer file). So, just put the controls as fields in the same fashion:

private Label lblUrl;
private TextBox txtUrl;
private AssetUrlSelector picker;
private Control control;

protected override void CreateChildControls()
{
    lblUrl = new Label();
    lblUrl.ID = "lblUrl";
    lblUrl.Text = "Url: ";
    Controls.Add(lblUrl);

    txtUrl = new TextBox();
    txtUrl.ID = "txtUrl";
    Controls.Add(txtUrl);

    picker = new AssetUrlSelector();
    picker.ID = "ausUrl";

    picker.DefaultOpenLocationUrl =  OpenUrl;
    picker.AssetUrlClientID = txtUrl.ClientID;
    picker.AssetUrlTextBoxVisible = false;
    Controls.Add(picker);

    control = Page.LoadControl(_ascxPath);
    Controls.Add(control);
}
Ivo
  • 8,172
  • 5
  • 27
  • 42
  • Also, if you need to interact with your "control" in a generic way, you can make all the controls that could be there inherit the same base class that will provide the needed interface to interact with. – Ivo May 25 '12 at 15:55
  • 1
    Thanks I have tried it, altho I had to make the controls public to be accessible from another class. but it retuns null reference error. I have updated the initial post – user1211929 May 25 '12 at 16:01
  • The problem is you are accessing the controls before CreateChildControls is being called. What do you need to do with the Controls? Maybe you have to wait to another event of the control or page before accessing them. Also, I would suggest not to expose the controls but just what you have to use (Don't talk to strangers pattern) – Ivo May 25 '12 at 16:05
  • 1
    The controls select a path in sharepoint and return it to the textbox which I can see that is filled with the path. then I need to access that path in order to use that string and do other stuff. – user1211929 May 25 '12 at 16:09
  • And in which event are you accessing that information? it has to be an event that fires after CreateChildControls. I'd suggest `Load` or Even the `Textbox.TextChanged` to be really sure. – Ivo May 25 '12 at 16:11
  • Maybe you can provide the code of the page/control that uses this one. What you need is totally possible, I just need to see what you are doing. You are really closer. – Ivo May 25 '12 at 16:13
  • I've added it it is a button that is pressed only after the I choose the path with the other button created in the Childcontrol – user1211929 May 25 '12 at 16:16
  • But you are creating a new instance of the control! Is that control added to the page dynamically too or it is in the aspx? Why not use the same instance you have created? – Ivo May 25 '12 at 16:27
  • @ivowiblo - "it has to be an event that fires after CreateChildControls" - this is the wrong approach. In a CompositeControl, CreateChildControls is generally called "as late as possible" - i.e. when the `Controls` property is first accessed, or `EnsureChildControls` is called for some other reason. The correct approach is given in my answer: call `EnsureChildControls` before exposing anything that depends on a child control publicly. – Joe May 25 '12 at 23:44
  • Anyway, the main problem is that he is trying to get the value from a totally new instance instead the one in the page. – Ivo May 26 '12 at 05:57