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);
}
}
Related
WHAT I'M TRYING TO DO
Hello everyone. I'm trying to make a dropdown inside a property grid. This dropdown for some objects will just place its text inside the property, like a StringConverter. But for other objects, it behaves like a button and will open up other forms or an OpenFileDialog. These forms and OpenFileDialog return a text, which is then put inside the property. I feel like I'm doing this very incorrectly so if there is a better way of doing this. But hopefully there is a simple solution.
WHAT I'VE TRIED
I'm very close. I can get the text to show up on a MessageBox. But I can't seem to put it in the property grid, which is in another form and is protected so I can't shove the value in. The way it works is on the other form, the property grid selected an object, which has an attribute called text.
WHAT I'M DOING NOW
When I click on the dropdown for my text attribute in my property grid. The context menu strip is created, all the click events are on, and then, it returns a value right away. Which is not what I want. I want it to wait for me to click on one of the items inside the context menu, the click events sets the string text, and then, the value returns.
public class text-editor: UITypeEditor
{
string text = "";
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; }
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
ContextMenuStrip cms = new ContextMenuStrip();
cms = MakeCms(cms); // adds some ToolStripEditor, & their click events which updates value
ToolStripMenuItem add = new TooStripMenuItem("Add");
add.Click += new EventHandler(CmsClick); // updates value
cms.Items.Add(add);
ToolStripMenuItem remove = new TooStripMenuItem("Remove");
remove.Click += new EventHandler(CmsClick); // updates value
cms.Items.Add(remove);
cms.Show(Control.MousePosition);
if (text != "")
{
value = text;
}
return value;
}
The ContextMenuStrip.Show method does not pause the caller and cannot be used here. Create a custom ContextMenuStrip integrated with IWindowsFormsEditorService.
public class TextEditor : UITypeEditor {
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; }
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
IWindowsFormsEditorService editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if(editorService != null) {
MyContextMenuStrip cms = new MyContextMenuStrip(editorService, "Add", "Remove");
editorService.DropDownControl(cms);
if(cms.SelectedItem == "Add")
return "Value When Add";
else if(cms.SelectedItem == "Remove")
return "Value When Remove";
}
return value;
}
}
class MyContextMenuStrip : UserControl {
readonly IWindowsFormsEditorService editorService;
readonly ListBox listBox;
public MyContextMenuStrip(IWindowsFormsEditorService editorService, params string[] items) {
this.editorService = editorService;
listBox = new ListBox();
listBox.Items.AddRange(items);
listBox.Dock = DockStyle.Fill;
listBox.MouseClick += ListBox_MouseClick;
listBox.Parent = this;
}
public string SelectedItem => (string)listBox.SelectedItem;
private void ListBox_MouseClick(object sender, MouseEventArgs e) {
ListBox listBox = (ListBox)sender;
if(listBox.SelectedIndex >= 0) {
Rectangle selectedItemRect = listBox.GetItemRectangle(listBox.SelectedIndex);
if(selectedItemRect.Contains(e.Location))
editorService.CloseDropDown();
}
}
}
I am creating a custom control in my C# application in order to add a new property (MyProperty below). It is inheriting from Label. One thing I would like it to do, is display at a particular size when I drag it on to my form (200x132). I'd also like it to display no text. However, no matter how I try to do this, it doesn't seem to work. I am able to set BackColor and BorderStyle with no problem, however. I'm fairly new to C#, so maybe I'm missing something obvious.
Here is my code:
using System.Drawing;
using System.Windows.Forms;
namespace MyProgram
{
public enum MyEnum
{
Value1, Value2, Value3
}
public partial class MyControl : Label
{
public MyControl()
{
BackColor = Color.LightCoral;
BorderStyle = BorderStyle.FixedSingle;
AutoSize = false;
Size = new Size(200, 132);
Text = "";
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
private MyEnum myProperty;
public MyEnum MyProperty
{
get { return myProperty; }
set { myPropery = value; }
}
}
}
The answer provided via Dispersia's link has a bug, in my opinion. The text reset should happen once and then whatever a user does after that shouldn't matter. In Dispersia's link you can't actually set the text back to the control name because it will keep blanking it out.
The answer provided by cramopy doesn't technically answer your question, it is a way to do it by using the defaults on a UserControl though. You'll also need to bind the Text property of the UserControl to the label's.
The following should work while inheriting from a Label and will only reset the Text property once.
public partial class MyControl : Label
{
#region fields
private IComponentChangeService _changeService;
private bool canResetText = false;
#endregion
#region properties
protected override Size DefaultSize
{
get { return new Size(200, 132); }
}
[Browsable(false)]
public override bool AutoSize
{
get { return false; }
set { base.AutoSize = false; }
}
public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
if (!base.DesignMode)
return;
this._changeService = (IComponentChangeService)base.GetService(typeof(IComponentChangeService));
if (this._changeService != null)
this._changeService.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
#region constructors
public MyControl()
{
base.BackColor = Color.LightCoral;
base.BorderStyle = BorderStyle.FixedSingle;
}
#endregion
#region methods
protected override void InitLayout()
{
base.InitLayout();
this.canResetText = true;
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
{
if (ce.Component != null &&
ce.Component == this &&
ce.Member.Name == "Text" &&
base.DesignMode &&
this.canResetText)
{
((MyControl)ce.Component).Text = string.Empty;
this.canResetText = false;
if (this._changeService != null)
this._changeService.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
}
#Dispersia reply only answers the myControl1 thing. (deleted meanwhile)
Here comes a full guide for solving your problem:
Add a new UserControl named MyLabel
Change the following within Designer Mode:
BorderStyle:= FixedSingle
Size:= 200; 132
Now Drag&Drop a new Label onto the control
Edit those Label values (also within Designer Mode):
AutoSize:= false
BackColor:= LightCoral
Dock:= Fill
Text:= clear/empty this box!! (don't write this inside the box, you really have to clear it!)
TextAlign:= MiddleCenter
Just recompile your project && add a MyLabel control from the Toolbar.
Now it show up as you wanted!!
Here I make a user control and there is a textbox in that control. Now, I want the value of that textbox will be displayed in label or form textbox code, below is user control side.
public string TextBox1Value
{
get
{
return tbEndDate.Text;
}
set { tbEndDate.Text = TextBox1Value; }
}
Below code is form side trying to fetch the textbox value of usercontrol in form label
protected void btnsubmit_Click(object sender, EventArgs e)
{
Label1.Text = calander1.TextBox1Value.ToString();
}
The problem is that whenever I click on button the label becomes blank!
You need to use value keyword in set{} routine
public string TextBox1Value
{
get
{
return tbEndDate.Text;
}
set
{
// tbEndDate.Text = TextBox1Value; //<-- you need to use "value" keyword here
tbEndDate.Text = value;
}
}
I want to perform Trim() method on each TexBox control on my page, before value is returned. I dont' want to hard-code the same code for each TexBox control, I want to do it in more elegant way.
I've found made the following class
namespace System.Web.UI.WebControls
{
public partial class TrimmedTextBuox : TextBox
{
private string text;
public override string Text
{
get { return string.IsNullOrEmpty(text) ? text : text.Trim(); }
set { text = value; }
}
}
}
but it fails, while debuggind the compiler doesn't get inside get{} and set{}.
After that, I created a UserControl item, but it must be deriverd from System.Web.UI.UserControl, not System.Web.UI.WebControls.TextBox to get it work (there's an exception which points to that)
So, how can I do that ?
First you have to register your control in your .aspx page like that:
<%# Register TagPrefix="customControls" Namespace="WebApplication.Custom.Controls" Assembly="WebApplication"%>
Then you can call it using the markup
<customControls:TrimmedTextBuox ID="txtTrim" runat="server"/>
Plus you don't have to create another "text" property in your custom TextBox. Instead, it can be done like that:
namespace WebApplication.Custom.Controls
{
public class TrimmedTextBuox : TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (!String.IsNullOrEmpty(value))
base.Text = value.Trim();
}
}
}
}
This will trim recursively all text boxes before inserting.
public static void trimRecursive(Control root)
{
foreach (Control control in root.Controls)
{
if (control is TextBox)
{
var textbox = control as TextBox;
textbox.Text = textbox.Text.Trim();
}
else
{
trimRecursive(control);
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
trimRecursive(Page);
}
Simple Solution to your problem is to hide the Text property of your base class by using new keyword. sample code...
public class TrimmedTextBox : TextBox
{
public new string Text
{
get
{
var t = (string) GetValue(TextProperty);
return t != null ? t.Trim() : string.Empty;
}
}
}
For more info about how new keyword with property works refrer to this SO Question
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);
}