Hello I am designing a custom .net web control that inherits from the aspx dropdownlist.
The idea is to have a dropdownlist that will display year values up until the current year. I want to be able to set a "StartYear" property that the control can start from or else use a default date. I am able to create this control but it is always using the defult date. It seems I am unable to use the property setting in the aspx code in the code behind. My front end code is ....
<customControls:YearDropDownList StartYear="2000" ID="ddlYear" runat="server"/>
and the code behind is
using System;
using System.ComponentModel;
using System.Globalization;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace customControls {
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
[DefaultProperty("StartYear")]
public class YearDropDownList : DropDownList
{
public YearDropDownList() {
for (int i = Int32.Parse(StartYear); i <= DateTime.Now.Year; i++)
{
this.Items.Add(new ListItem(i.ToString(), i.ToString()));
}
}
public string StartYear {
get{
String s = (String)ViewState["StartYear"];
return ((s == null) ? "2009":s);
}
set{
ViewState["StartYear"] = value;
}
}
}
}
To my way of thinking, it doesn't make sense to regenerate the list in the constructor. At the time the control is instantiated, ViewState probably won't be populated yet (though I am not sure of this) and therefore you'll always get your default value being used.
Here's what I'd do:
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
[DefaultProperty("StartYear")]
public class YearDropDownList : DropDownList
{
public string StartYear
{
get
{
String s = (String)ViewState["StartYear"];
return ((s == null) ? "2009" : s);
}
set
{
ViewState["StartYear"] = value;
RegenerateList();
}
}
// Defer regenerating the list until as late as possible
protected void OnPreRender(EventArgs e)
{
RegenerateList();
base.OnPreRender(e);
}
public void RegenerateList()
{
// Remove any existing items.
this.Items.Clear();
for (int i = Int32.Parse(StartYear); i <= DateTime.Now.Year; i++)
{
this.Items.Add(new ListItem(i.ToString(), i.ToString()));
}
}
}
you need to regenerate the list when the property is set like so;
[ToolboxData("<{0}:YearDropDownList runat=\"server\" StartYear=\"[StartYear]\"></{0}:YearDropDownList>")]
[DefaultProperty("StartYear")]
public class YearDropDownList : DropDownList
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
RegenerateList();
}
public string StartYear
{
get
{
String s = (String)this.ViewState["StartYear"];
return s ?? "2009";
}
set
{
ViewState["StartYear"] = value;
RegenerateList();
}
}
public void RegenerateList()
{
Items.Clear();
for (int i = Int32.Parse(this.StartYear); i <= DateTime.Now.Year; i++)
{
this.Items.Add(new ListItem(i.ToString(), i.ToString()));
}
}
}
I have tested and verified the code above and it most definitely works. An interesting thing that I did notice was that I was able to reproduce your issue for a while in that the property setter was not being hit. I resolved this by right-clicking the solution and clicking Clean. After the solution was cleaned, I right-clicked on the solution again but this time selected Rebuild. That seemed to solve the property setter issue.
Related
I've already created my custom textbox control inheriting from it and all is fine and dandy. However, I'm having an issue trying the same thing with a DropDownList. I've searched for about 2 hours in Google and all the results I get are either crappy links or some (uncomplete) suggestions on creating a composite control and adding the DropDownList inside, but that also means that I have to expose all the events and properties that I use, which I find pretty overkill for what I need to do, which is to add a validator of any kind next to my DropDownList control.
To illustrate, this is what I'm attempting to do:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
namespace Blah
{
public class ExtendedDropDownList : DropDownList
{
private DropDownList _self;
private CustomValidator _cv;
public bool Required { get; set; }
public String FieldName { get; set; }
protected override void OnInit(EventArgs e)
{
_cv = new CustomValidator()
{
ControlToValidate = ID,
EnableClientScript = false,
ValidationGroup = ValidationGroup,
ValidateEmptyText = true
};
_cv.ServerValidate += new ServerValidateEventHandler(_cv_ServerValidate);
Controls.Add(_cv);
}
private void _cv_ServerValidate(object source, ServerValidateEventArgs args)
{
if (Required && String.IsNullOrWhiteSpace(args.Value))
{
args.IsValid = false;
_cv.ErrorMessage = "The field <strong>" + FieldName + "</strong> is required.";
return;
}
}
}
}
And it throws an exception that the DropDownList cannot have secondary controls. How come? if the TextBox allows it?
Is there any way to do this same thing without creating the composite control and rewriting the wheel? (yes, pun intended :P). I assume I could get away with creating the control and then writing it in the rendering phase AFTER the DropDownList is rendered, but I can't find out how to do it and if it's even possible (though a hack, I'm short on time on a form generator I need to finish and this is taking too long and making me feel really tired :(... you guys know I come to SO when I've used up all the resources available).
Thanks in advance! :D
Okay, I managed to do it the way I wanted, but with a bit of help from the container's page:
I had to add this to my aspx page:
<asp:PlaceHolder runat="server" ID="phValidators" />
And then here's the finished control, which adds the validator to the Page's validators placeholder I created, not the control itself:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
namespace Blah
{
public class ExtendedDropDownList : DropDownList
{
private CustomValidator _cv;
public bool Required { get; set; }
public String FieldName { get; set; }
public String ValidatorPlaceHolder { get; set; }
protected override void OnInit(EventArgs e)
{
_cv = new CustomValidator()
{
ControlToValidate = ID,
EnableClientScript = false,
ValidationGroup = ValidationGroup,
Display = ValidatorDisplay.None,
ValidateEmptyText = true
};
_cv.ServerValidate += new ServerValidateEventHandler(_cv_ServerValidate);
if (Parent.FindControl(ValidatorPlaceHolder) != null)
Parent.FindControl(ValidatorPlaceHolder).Controls.Add(_cv);
else
throw new Exception("Cannot find asp:PlaceHolder inside parent with ID '" + ValidatorPlaceHolder + "'");
base.OnInit(e);
}
private void _cv_ServerValidate(object source, ServerValidateEventArgs args)
{
if (Required && String.IsNullOrWhiteSpace(args.Value))
{
args.IsValid = false;
_cv.ErrorMessage = "The field <strong>" + FieldName + "</strong> is required.";
return;
}
}
}
}
I hope this helps someone out :)
I currently have a gridview that is populated via database through a c# method.
I wanted to know if there was a way to select the row when clicked anywhere on the row and not use the select button altogether. And then have the information from that row be sent back and populate another area on the webpage.
Is there a grid better than gridview for this? Should I outsource to jQuery? Or is gridview all I should need?
what you need to do is develop a row-clickable GridView. Best bet is to follow the instructions in the link. If your okay with VB, you can go along that route. A user has also converted it into C#, its in the comments section. Ill include it incase you dont see it.
Heres a link I have saved: http://aspadvice.com/blogs/joteke/archive/2006/01/07/14576.aspx
using System;
using System.ComponentModel;
using System.Configuration;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CustomGridView
{
/// <summary>
/// Summary description for ClickableGridView
/// </summary>
public class ClickableGridView : GridView
{
public string RowCssClass
{
get
{
string rowClass = (string)ViewState["rowClass"];
if (!string.IsNullOrEmpty(rowClass))
return rowClass;
else
return string.Empty;
}
set
{
ViewState["rowClass"] = value;
}
}
public string HoverRowCssClass
{
get
{
string hoverRowClass = (string)ViewState["hoverRowClass"];
if (!string.IsNullOrEmpty(hoverRowClass))
return hoverRowClass;
else
return string.Empty;
}
set
{
ViewState["hoverRowClass"] = value;
}
}
private static readonly object RowClickedEventKey = new object();
public event GridViewRowClicked RowClicked;
protected virtual void OnRowClicked(GridViewRowClickedEventArgs e)
{
if (RowClicked != null)
RowClicked(this, e);
}
protected override void RaisePostBackEvent(string eventArgument)
{
if (eventArgument.StartsWith("rc"))
{
int index = Int32.Parse(eventArgument.Substring(2));
GridViewRowClickedEventArgs args = new GridViewRowClickedEventArgs(Rows[index]);
OnRowClicked(args);
}
else
base.RaisePostBackEvent(eventArgument);
}
protected override void PrepareControlHierarchy()
{
base.PrepareControlHierarchy();
for (int i = 0; i < Rows.Count; i++)
{
string argsData = "rc" + Rows[i].RowIndex.ToString();
Rows[i].Attributes.Add("onclick", Page.ClientScript.GetPostBackEventReference(this, argsData));
if (RowCssClass != string.Empty)
Rows[i].Attributes.Add("onmouseout", "this.className='" + RowCssClass + "';");
if (HoverRowCssClass != string.Empty)
Rows[i].Attributes.Add("onmouseover", "this.className='" + HoverRowCssClass + "';");
}
}
}
public class GridViewRowClickedEventArgs : EventArgs
{
private GridViewRow _row;
public GridViewRowClickedEventArgs(GridViewRow aRow)
: base()
{
_row = aRow;
}
public GridViewRow Row
{
get
{ return _row; }
}
}
public delegate void GridViewRowClicked(object sender, GridViewRowClickedEventArgs args);
}
I'm finding myself coding C3 for the first time, and using Visual Studio for the first time in a looong time.
I'm creating a user control that allows for picking a file/folder etc, for making that kindof control easier to implement in the future. However whenever I drag the control unto any form, Visual Studio crashes instantly. I have tried rebuilding the entire solution several times.
The error seems to only happen when creating public variables in the control...
Does anyone know how to get around this?
Code is work in progress.... ;)
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;
namespace BackupReport.tools
{
public partial class pathchooser : UserControl
{
#region "Datatypes"
public enum DLG { Folder, FileSave, FileOpen };
#endregion
#region "public properties"
public DLG Dtype
{
get
{
return this.Dtype;
}
set
{
this.Dtype = value;
}
}
public string labelText
{
get
{
return this.labelText;
}
set
{
this.labelText = value;
label1.Text = this.labelText;
}
}
#endregion
#region "Constructor"
public pathchooser()
{
InitializeComponent();
this.Dtype = DLG.Folder;
this.labelText = "Source:";
label1.Text = this.labelText;
}
#endregion
private void browse_button_Click(object sender, EventArgs e)
{
switch (this.Dtype)
{
case DLG.Folder:
if (fbd.ShowDialog() == DialogResult.OK)
{
path_textbox.Text = fbd.SelectedPath;
}
break;
case DLG.FileSave:
break;
case DLG.FileOpen:
break;
default:
break;
}
}
}
}
Any help would be appreciated.
also I'm not sure it matters, but I'm using VS11 beta.
//Martin
public DLG Dtype
{
get
{
return this.Dtype;
}
set
{
this.Dtype = value;
}
}
You have a property referring to itself, and thus calling its own getter and setter inside (respectively) the getter and setter. Something more appropriate would either be to either have empty accessors:
public DLG DType{get; set;}
or to have accessors referring to private variables:
private DLG dtype;
public DLG Dtype
{
get
{
return this.dtype;
}
set
{
this.dtype = value;
}
}
I think your properties are causing a StackOverflowException because the getters and setters invoke themselves in an endless loop (Dtype -> Dtype -> Dtype ...).
Try this code instead:
private string labelText;
public DLG Dtype { get; set; }
public string LabelText
{
get { return this.labelText; }
set
{
this.labelText = value;
label1.Text = value;
}
}
I once asked for a way to let a linkbutton pass more than one value in the commandArgument and then I reached the approach where I pass a string of multiple values separated by any character and split it into it's original parts...that didn't work out I don't know what was wrong with the splitting!
Now I tried the only solution I got, which is created a user control of the LinkButton and add properties to accept any values nedeed!...could you please tell me what's wrong with my 2 approaches and which is better ?
The first question can be found here : link text
and this is the code for the user control approach >>
MultivaluedLinkButton.ascx :
<asp:LinkButton ID="LnkBtnSort" runat="server" Text="Sort" OnClick="LnkBtnSort_Clicked"/>
MultivaluedLinkButton.ascx.cs :
public partial class MultivaluedLinkButton : System.Web.UI.UserControl
{
public event EventHandler Click;
private int _sortingType;
private string _sortingFactor;
private string _text;
public int SortingType
{
set { _sortingType = value; }
get { return _sortingType; }
}
public string SortingFactor
{
set { _sortingFactor = value; }
get { return _sortingFactor.ToString(); }
}
//public string Text
//{
// set { _text = value; }
// get { return _text.ToString(); }
//}
protected void LnkBtnSort_Clicked(object sender, EventArgs e)
{
if( Click != null )
{
this.Click(this, EventArgs.Empty);
}
}
}
Finally, Here's the implementation of my control inside an aspx page:
protected void MultivaluedLinkButton1_Clicked(object sender, EventArgs e)
{
MultivaluedLinkButton ctrl = (MultivaluedLinkButton)sender;
using (SqlConnection cn1 = new SqlConnection(ConfigurationManager.ConnectionStrings["testConnectionString"].ConnectionString))
{
using (SqlCommand cm1 = new SqlCommand(commandString2, cn1))
{
cm1.Parameters.Add("#arrange_by_id", System.Data.SqlDbType.Int);
cm1.Parameters["#arrange_by_id"].Value = ctrl.SortingType;
cn1.Open();
using (SqlDataReader dr1 = cm1.ExecuteReader())
{
SortBy_rpt.DataSource = dr1;
SortBy_rpt.DataBind();
}
}
}
}
The item template of the repeater in the implementation page :
<ItemTemplate>
<uc1:MultivaluedLinkButton ID="MultivaluedLinkButton1" runat="server" OnClick="MultivaluedLinkButton1_Clicked" SortingType='<%#Eval("arrange_by_id")%>' />
</ItemTemplate>
The problem i see is, you have an eventHandler in your usercontrol which you never really use.
Not 100% sure but, on the Page_Load of your parent page, you need to add MultivaluedLinkButton1_Clicked event to your handler.
MultivaluedLinkButton1.EventHandler_Click = new EventHandler(this.MultivaluedLinkButton1_Clicked);
MultivaluedLinkButton1.LnkBtnSort.Click = MultivaluedLinkButton1.EventHandler_Click;
Basically you are telling that when a user clicks on your linkbutton, MultivaluedLinkButton1_Clicked() on the parent page should be called.
You can remove OnClick="MultivaluedLinkButton1_Clicked" from your UserControl properties on your parent page.
Lets say we have 0 displayed in value field of the control and I want that if the value is 0 - display string.Empty (I know that the type of value is decimal and there can be no string inserted instead of decimals in it, but still... Maybe there is some formatting possible there?).
Note: This is dependent on the current implementation of NumericUpDown.
What you need to do is create a new control that inherits from NumericUpDown such that:
public partial class SpecialNumericUpDown : NumericUpDown
{
public SpecialNumericUpDown()
{
InitializeComponent();
}
protected override void UpdateEditText()
{
if (this.Value != 0)
{
base.UpdateEditText();
}
else
{
base.Controls[1].Text = "";
}
}
}
public partial class MyNumericUpDown : NumericUpDown
{
public override string Text
{
get
{
if (base.Text.Length == 0)
{
return "0";
}
else
{
return base.Text;
}
}
set
{
if (value.Equals("0"))
{
base.Text = "";
}
else
{
base.Text = value;
}
}
}
}
It seems that there is only very limited support for changing the formatting.
I have not tried this myself. But you could create a subclass and override the UpdateEditText method to support your custom format. Something like this:
protected override void UpdateEditText()
{
this.Text = Value.ToString(); // Insert your formatting here
}
An easier solution is calling the ResetText() method. You can restore the text changing the Value property.
Example code to hide text when NumericUpDown control is disabled, and restore it on enabled
private void NumericUpDown_EnabledChanged(object sender, EventArgs e)
{
if (numericUpDown.Enabled)
{
if (numericUpDown.Tag != null)
{
// Restore last value
numericUpDown.Value = (decimal)numericUpDown.Tag;
}
}
else
{
// Save last value
numericUpDown.Tag = numericUpDown.Value;
// Just to force value change
numericUpDown.Value = (numericUpDown.Value > numericUpDown.Minimum ? numericUpDown.Minimum : numericUpDown.Maximum);
// Clear text
numericUpDown.ResetText();
}
}
If you only want to hide the value from the user, you can make ForeColor the same as BackColor so the value inside NumericUpDown will be invisible to the user.