accessing controls created by CreateChildControls() - c#

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;
}

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;
}
}

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);
}

Related

C# WinForms - Cannot access a control in a handler method

I have a form containing two flow layout panels (FLP), which dynamically have buttons added to them. These buttons are actually a class called tagButton which inherits from Button and I have added a handler in the constructor for the click() method. On click, I want to remove the button from the FLP it is currently in then add it to the other FLP.
Below is a trimmed down version of my code for the tagButton class. Note that the tagButton class is defined inside the of the form class both FLPs are in:
class tagButton : Button
{
public string tag = "";
public bool useTag = false; //tells you which FLP the button is in
public tagButton(String tag, Boolean useTag)
{
this.tag = tag;
this.Text = tag;
this.useTag = useTag;
this.Click += TagButton_Click;
}
private void TagButton_Click(object sender, EventArgs e)
{
tagButton tagButton = (tagButton)sender;
tagButton.useTag = !tagButton.useTag;
if (tagButton.useTag)
{
flowLayoutPanel.Controls.Remove(tagButton);
}
}
}
I'm having problems with the last line:
flowLayoutPanel.Controls.Remove(tagButton);
I can switch it to the following and it works, however there is no way for me to add it to the other FLP. Or at least, not without doing Parent.Parent.Parent.Controls[1]... etc which is clearly a bad idea.
tagButton.Parent.Controls.Remove(tagButton);
I've tried switching different classes and methods to static but nothing I tried worked, the this keyword doesn't seem to work either.
I would recommend having a separate class overriding a parent control that's aware of both FlowLayoutPanels. Then, when your button wants to switch, it can find that custom control in its parents and invoke a custom "switch" function that would move the invoking button from the list it's in to the list it wasn't in.
One of many ways to achieve this outcome is to have MainForm expose a static array of the FlowLayoutPanel candidates as Panels property:
public partial class MainForm : Form
{
public static Control[] Panels { get; private set; }
char _id = (char)64;
public MainForm()
{
InitializeComponent();
Panels = new Control[]{ flowLayoutPanelLeft, flowLayoutPanelRight, };
buttonAddLeft.Click += (sender, e) =>
{
flowLayoutPanelLeft.Controls.Add(new tagButton
{
Height= 50, Width=150,
Name = $"tagButton{++_id}",
Text = $"Button {_id}",
});
};
buttonAddRight.Click += (sender, e) =>
{
flowLayoutPanelRight.Controls.Add(new tagButton
{
Height= 50, Width=150,
Name = $"tagButton{++_id}",
Text = $"Button {_id}",
});
};
}
}
Then, suppose you want to swap between panels when a tagButton gets a right-click (for example).
class tagButton : Button
{
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (MouseButtons.Equals(MouseButtons.Right))
{
Control dest;
if(Parent.Name.Contains("Left"))
{
dest = MainForm.Panels.First(_=>_.Name.Contains("Right"));
}
else
{
dest = MainForm.Panels.First(_ => _.Name.Contains("Left"));
}
Parent.Controls.Remove(this);
dest.Controls.Add(this);
}
}
}

Changing form control from user control

I'm trying to changing a user control informations (labels, pictures etc.) from auto added user control. But i cant do it.
Here's my code;
private void KitapButton_Click(object sender, EventArgs e)
{
BıtıkForm BForm = new BıtıkForm();
BForm.kitapGoruntuleme.Visible = true;
}
public partial class BıtıkForm : Form
{
//create controls public instance
public Label label;
public BıtıkForm()
{
InitializeComponent();
//initialize the control
label = new Label();
}
}
Now you can access it from other place like;
BıtıkForm BForm = new BıtıkForm();
BForm.label.Visible = true;
/////// But my Suggestion do not do it like that instead do it like below ///////
BıtıkForm BForm = new BıtıkForm(controlVisible);//Pass the bool value as parameter to the constructor of form
BForm.Show();
And then in form
public partial class BıtıkForm : Form
{
public BıtıkForm(bool controlVisible)
{
InitializeComponent();
//Set Control Visibility
someControl.Visible = controlVisible;
}
}
I didn't use C# too much but It's eventually object oriented. The mistake I made is; I was creating a new instance of 'BıtıkForm' everytime event fired. It could be solved by adding new property where event belongs, and property will carry 'BıtıkForm' object. So It can be managed trough all over the program.

Custom Ext.Net component dynamically initialization

I am trying to create a custom component. The component should by dynamically initialized in code behind. The component presents a custom Window containing other components, like datefields, dropdown fields etc. I derived my class from Ext.Net.Window and added simple DateField. The date should than be used by a button click on the server (Date should not be passed over DirectMethod parameter). When I add this component to mark-up it works perfectly. But when I add the window in code behind, the value of the datefield is not set after the server call.
I am creating the window in the life cycle in OnInit event by "Controls.Add(mywindow)". It would be great if anybody could give me a hint. Here my window code (onExecuteButtonClick just calls the direct method and hides the window):
public sealed class WindowFilterComponent:Window
{
private const string Script = "MyProject.JavaScript.src.WindowFilterComponent.js";
public override string InstanceOf
{
get
{
return "MyProject.Filter.WindowFilterComponent";
}
}
public override string XType
{
get
{
return "windowfiltercomponent";
}
}
private Button _btnExecute;
private Button _btnCancel;
private DateField _dateField;
protected override void OnInit(EventArgs e)
{
AutoHeight = true;
_btnExecute = new Button("Execute Export");
_btnExecute.Listeners.Click.Handler = string.Format("#{{{0}}}.onExecuteButtonClick()", ID);
_btnCancel = new Button("Cancel");
_btnCancel.Listeners.Click.Handler = string.Format("#{{{0}}}.onCancelButtonClick()", ID);
Buttons.Add(_btnExecute);
Buttons.Add(_btnCancel);
_dateField = new DateField();
Items.Add(_dateField);
base.OnInit(e);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (ExtNet.IsAjaxRequest || Page.IsCallback) return;
ResourceManager.GetInstance().AddDirectMethodControl(this);
}
[DirectMethod(ShowMask = true)]
public void ExecuteClick()
{
var date = _dateField.SelectedValue;
}
}
Now the useage in my page in the OnInit event:
protected override void OnInit(EventArgs e)
{
var myWindow = new WindowFilterComponent()
{
Hidden = false,
Width = 500
};
myWindow.ID = myWindow.ID + "MyComponent";
Controls.Add(myWindow);
base.OnInit(e);
}
I think the Window is rendered outside of the Form.
Please replace
Controls.Add(myWindow);
with
Form.Controls.Add(myWindow);
Also I would recommend to set up explicit IDs for the submittable fields (the DateField in your case) to ensure that the id key from POST data will match the control's ID on the server.

Changing the properties of the active control automatically

Please consider that im a newcomer to c#. After scanning about 700 posts i decided to post one more question:
On my windows form (c#) I have some controls including textboxes, checkboxes and so on.
I want to change the backcolor whenever the controls become active.
I know i could raise 'enter' and 'leave' events for each control to change the corresponding properties but there should be another way.
Simply hook Enter and Leave events - toggling the color in each. Save the last color saved in OnEnter to use in OnLeave
public Form1()
{
InitializeComponent();
var lastColorSaved = Color.Empty;
foreach(Control child in this.Controls)
{
child.Enter += (s, e) =>
{
var control = (Control)s;
lastColorSaved = control.BackColor;
control.BackColor = Color.Red;
};
child.Leave += (s, e) =>
{
((Control)s).BackColor = lastColorSaved;
};
}
}
You customize control classes just like you customize any class, you derive your own class and override the virtual methods. Arbitrarily:
using System;
using System.Drawing;
using System.Windows.Forms;
class MyTextBox : TextBox {
protected override void OnEnter(EventArgs e) {
prevColor = this.BackColor;
this.BackColor = Color.Cornsilk;
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e) {
this.BackColor = prevColor;
base.OnLeave(e);
}
private Color prevColor;
}
Now any MyTextBox you drop on the form will have this behavior without having to implement events. Although there's certainly nothing wrong with using events.
Create a class (eg. ControlColorizer) and in its constructor pass:
1) The backcolor for the 'active control' and save to a internal Color variable
2) a variable length Control array
In the contructor add the same event handler for OnEnter and OnLeave on each control
In the OnEnter event set the backcolor
In the OnLeave event set the standard background color
The advantage is all in the use of the class:
1) Declare a global instance in your form class
2) Initialize in the form contructor after the InitializeComponent.
3) Forget everything else. No other code required
So let me explain everything with code:
This will go in a file called ControlColorizer.cs
public class ControlColorizer
{
private Color _setBColor = SystemColors.Window;
public ControlColor(Color bkg, params Control[] ctls)
{
_setBColor = bkg;
foreach (Control o in ctls)
{
o.Enter += new EventHandler(o_Enter);
o.Leave += new EventHandler(o_Leave);
}
}
private void o_Enter(object sender, EventArgs e)
{
if (sender is Control)
{
Control c = (Control)sender;
c.BackColor = _setBColor;
}
}
private void o_Leave(object sender, EventArgs e)
{
Control c = sender as Control;
c.BackColor = SystemColors.Window;
}
Now, in every form contructor where you need the functionality you have this
ControlColirizer _ccz;
public Form1()
{
InitializeComponent();
// Create an instance of ControlColorizer, pass the background color
// the list of Controls and that's all
_ccz = new ControlColorizer(Color.LightYellow, this.TextBox1,
this.TextBox2, this.TextBox3, this.TextBox4);
}

ASP.NET Custom control and Page.Request.Form[]

I have a control that inherits from System.Web.UI.Control and contains generic HTML controls rather than asp.net server side controls. I use Page.Request.Form[Control_id] to get the value of one of the controls. This basically works accept if I have a gridview that contaiins a column of these custom controls and I add a new row [row6] and then delete a row from above tht row [row3], the control in the new row [row6 becoming row5] assumes the value of the row immediately above it [row5 becomming row4].
I beleive this is because I use Page.Request.Form[] to get the value for each control but my control doesnt know that those values belonged to controls that had previously occupied the same row. How do I fix this? I'd aprreciate any suggestions!!
You don't need to mess with the Page.Request.Form collection. What you need is a proper composite control. Here's a simple example:
public class InputTextWithLabelControl : CompositeControl {
HtmlGenericControl _label;
HtmlInputText _text;
public string Label {
get {
EnsureChildControls();
return _label.InnerText;
}
set {
EnsureChildControls();
_label.InnerText = value;
}
}
public string Text {
get {
EnsureChildControls();
return _text.Value;
}
set {
EnsureChildControls();
_text.Value = value;
}
}
protected override void CreateChildControls() {
Controls.Clear();
_label = new HtmlGenericControl("span");
_label.ID = "label";
_text = new HtmlInputText();
_text.ID = "text";
Controls.Add(_label);
Controls.Add(_text);
}
}

Categories