I have a custom DropDownList control:
<cc1:CountriesControl ID="DdlCountry" TabIndex="69" runat="server" DefaultCountry="USA" OnSelectedIndexChanged="DdlCountryIndexChanged"
CssClass="DefaultDropdown" AutoPostBack="true" />
The DropDownList has a custom property called DefaultCountry. As you can see, the default value is set to "USA". However in my subclass, DefaultCountry is always null.
How do I get it to set the DefaultCountry to what is in the ASP.NET markup?
[DefaultProperty("Text"), ToolboxData("<{0}:CountriesControl runat=server></{0}:CountriesControl>")]
public class CountriesControl : DropDownList
{
[Bindable(true), Category("Appearance"), DefaultValue("")]
private String defaultCountry;
[
Category("Behavior"),
DefaultValue(""),
Description("Sets the default country"),
NotifyParentProperty(true)
]
public String DefaultCountry
{
get
{
return defaultCountry;
}
set
{
defaultCountry = value;
}
}
public CountriesControl()
{
this.DataSource = CountriesDataSource();
this.DataTextField = "CountryName";
this.DataValueField = "Country";
this.SelectedIndex = 0;
this.DataBind();
// DefaultCountry is always null?
this.Items.Insert(0, new ListItem(this.DefaultCountry, "--"));
}
// more code here
}
you need to use Selected property to true for the item which you want to set it as default. look here for examplesDropdownlist
// this is the normal syntax. to get the default value for dropdownlist
<asp:DropDownList ID="DropDownList1" runat="server" width="145px">
<asp:ListItem Text="SomeText" Value="SomeValue" Selected="true"></asp:ListItem>
</asp:DropDownList>
but in your case. you may try like this, i m not sure, but a guess.
this.Selected=true
The solution was to not bind the data in the constructor, but call it from the code behind on my page. It appears that the value of the attributes (e.g. #DefaultCountry) is not set until the RenderControl method is called on the control.
Related
how do you properly call methods inside custom binding expressions? Are there complications because the dropdownlist is inside a detailsview?
asp.net code:
<asp:DropDownList ID="ddlExceptionEditStatus" runat="server"
DataSourceID="odsExceptionsStatus"
DataTextField="Name"
DataValueField="StatusID"
SelectedValue='<%# Bind("StatusID") %>'
BackColor="<%# SetBackColorProp(Container.DataItem) %>">
</asp:DropDownList>
code behind:
protected System.Drawing.Color SetBackColorProp(object o)
{
System.Drawing.Color statusColor = System.Drawing.Color.White;
string statusName = o as string;
if (statusName != null)
{
statusColor = System.Drawing.ColorTranslator.FromHtml(FISBLL.StatusColors.GetColor(statusName));
return statusColor;
}
else
{
return statusColor;
}
}
Doesn't change the backcolor. but doesn't throw an exception.
So, I had two mistakes:
1) I needed to cast the Container.DataItem to the class object i was using for the ObjectDataSource. After casting, the BackColor for each item in the dropdownlist matched the StatusID of the casted Container.DataItem.
2) Unfortunately this gave all the items the same color, where as I wanted each item's color to reflect the their own value attached to the dropdownlist. This is because the dropdownlist has an objectdatasource outside the DetailsView that it's inside of. Therefor the selectedValue item of the dropdownlist dictated the colors for all the other items.
I decided to go with Tim's suggestion and tie the BackColor setting for each item in the databound event:
protected string GetColor(string name)
{
return FISBLL.StatusColors.GetColor(name);
}
protected void ddlExceptionEditStatus_DataBound(object sender, EventArgs e)
{
foreach (ListItem item in ((DropDownList)sender).Items)
{
item.Attributes.Add("style", "background-color:" + GetColor(item.Text));
}
}
And the correct behavior is shown:
I have an ASP.NET TextBox with a CustomValidation control that invokes client side validation script.
<asp:TextBox ID="txtSubsContrRbtAmt" runat="server"
CssClass="textEntry NumericInput" Width="150px"
Text="" onKeyUp="SumValues();" MaxLength="16"></asp:TextBox>
<asp:CustomValidator ID="cvalSubsContrRbtAmt" runat="server" ClientValidationFunction="ValidatetxtSubsContrRbtAmt"
ControlToValidate="txtSubsContrRbtAmt" CssClass="errlable" ErrorMessage="Max Decimals = 7"
SetFocusOnError="True" ValidationGroup="CarbsAdd"></asp:CustomValidator>
Here's the Client script:
function ValidatetxtSubsContrRbtAmt(source, args) {
var txtSubsContrRbtAmt = document.getElementById("<%=txtSubsContrRbtAmt.ClientID%>");
var amount = txtSubsContrRbtAmt.value;
args.IsValid = ValidAmount(amount);
if (!args.IsValid)
txtSubsContrRbtAmt.focus();
}
function ValidAmount(amount) {
if (isNumber(amount)) {
return (RoundToXDecimalPlaces(amount, 7) == amount);
}
else {
return true;
}
In the ValidatetxtSubsContrRbtAmt function, the "source" parameter is the CustomValidator. That control has a property "ControlToValidate." If I can get to it, I can programmatically retrieve the value from that control and not have to have a separate function to validate each textbox.
jQuery is too much for me at this point, I'm looking for a plain old Javascript approach, please.
You don't have to get the text box. You can get the value from args.Value. The focus should be set automatically if you set SetFocusOnError="true".
function ValidatetxtSubsContrRbtAmt(source, args) {
var amount = args.Value;
args.IsValid = ValidAmount(amount);
}
You should be able to get to the control from the source object.
function ValidatetxtSubsContrRbtAmt(source, args) {
var controlToFocusOn = source.ControlToValidate;
you can switch that out with "document.getElementByID()" to get the ID or whatever attribute you need
var controlId = document.getElementById(source.ControlToValidate).id;
}
now you can focus or do what you need with the control. I had to access the the actual ControlToValidate earlier today from a CustomValidator.
I have this code
<asp:RadioButtonList ID="rblSplitWeek" runat="server">
<asp:ListItem selected="true">No Choice</asp:ListItem>
<asp:ListItem Text = "First" Value = "Session('s_price_1')"></asp:ListItem>
<asp:ListItem Text = "Second"></asp:ListItem>
</asp:RadioButtonList>
But keep getting an error when trying to put the session variable in
Many thanks
Jamie
Unfortunately, data binding syntax (<%# %>) is not supported in this context, and literal binding syntax (<%= %> or <%: %>) do not produce the desired results when assigning values to server controls.
Here are a few alternate approaches:
1. Bind to a data source:
If you created a collection of objects containing text and value, you could easily set the DataSource, DataTextField, and DataValueField properties of the radio button list. Because the data source would be populated in code-behind, access to session variables is trivial.
For example, in markup:
<asp:RadioButtonList ID="rblSplitWeek" runat="server"
DataTextField="Text"
DataValueField="Value" />
And in code-behind:
public class RadioValue
{
public string Text { get; set; }
public string Value { get; set; }
}
// ...
var values = new RadioValue[]
{
new RadioValue { Text = "No Choice" },
new RadioValue { Text = "First", Value = Session["s_price_1"].ToString() },
new RadioValue { Text = "Second" }
}
rblSplitWeek.DataSource = values;
rblSplitWeek.DataBind();
2. Assign the value from code-behind
If you declare the list item with text but without value, you can set the value from script.
For example, in markup:
<asp:RadioButtonList ID="rblSplitWeek" runat="server">
<asp:ListItem selected="true">No Choice</asp:ListItem>
<asp:ListItem Text = "First" />
<asp:ListItem Text = "Second" />
</asp:RadioButtonList>
And in code-behind:
rblSplitWeek.Items.FindByText("First").Value = Session["s_price_1"].ToString();
I know this is way late, but another alternative is to load the RadioButtonList's SelectedIndex property in the PageLoad() event. Then you don't have to have a small RadioValue class - which is fine of course. Here is how I did it the last time... and yes I have used the RadioValue class way too.... but here it is using your radiobuttonlist without the RadioValue class:
protected void Page_Load(object sender, EventArgs e)
{
//Reload the last radio button selected.
if (Session["rblSplitWeekIndex"] != null)
{
rblSplitWeek.SelectedIndex = ((int)Session["rblSplitWeekIndex"]);
}
...
I save my radio button list selected index in a MasterPage which has a search scope radio button list and a search text box. So since a MasterPage killed my radio button list (and also the text which is not shown here), I had to load it when needed. Hope this helps!
I have a property int? MyProperty as a member in my datasource (ObjectDataSource). Can I bind this to a TextBox, like
<asp:TextBox ID="MyTextBox" runat="server" Text='<%# Bind("MyProperty") %>' />
Basically I want to get a null value displayed as blank "" in the TextBox, and a number as a number. If the TextBox is blank MyProperty shall be set to null. If the TextBox has a number in it, MyProperty should be set to this number.
If I try it I get an exception: "Blank is not a valid Int32".
But how can I do that? How to work with nullable properties and Bind?
Thanks in advance!
Well i've found a solution, which includes a FormView however and you don't specify if that fits your scenario.
Anyhow, in my case the DataBound-ed entity is a dto of my own (not that it should matter) and the trick is that when you update the formview you have to essentially attach on the pre-databound event and re-write the empty string as a null value so that the framework can property inject the value to the constructed object:
protected void myFormView_Updating(object sender, FormViewUpdateEventArgs e)
{
if (string.Empty.Equals(e.NewValues["MyProperty"]))
e.NewValues["MyProperty"] = null;
}
and similarly on insert
protected void myFormView_Inserting(object sender, FormViewInsertEventArgs e)
{
if (string.Empty.Equals(e.Values["MyProperty"]))
e.Values["MyProperty"] = null;
}
what makes this really fun is the fact that the error message ( is not a valid Int32) is actually wrong and it should write ( is not a valid Nullable) but then nullables would have been first class citizens wouldn't they?
I'm starting to believe it's not possible to bind a nullable value property. Til now I only can see the workaround to add an additional helper property to bind a nullable type:
public int? MyProperty { get; set; }
public string MyBindableProperty
{
get
{
if (MyProperty.HasValue)
return string.Format("{0}", MyProperty);
else
return string.Empty;
}
set
{
if (string.IsNullOrEmpty(value))
MyProperty = null;
else
MyProperty = int.Parse(value);
// value should be validated before to be an int
}
}
and then bind the helper property to the TextBox instead of the original:
<asp:TextBox ID="MyTextBox" runat="server"
Text='<%# Bind("MyBindableProperty") %>' />
I'd be happy to see another solution.
<asp:TextBox ID="MyTextBox" runat="server"
Text='<%# Bind("MyProperty").HasValue ? Bind("MyProperty") : "" %>' />
You could use HasValue to determine if the nullable type is null, and then set the Text property.
I am trying to add a template to a simplified composite control containing a Label and a TextBox. I want my mark up to look something like this:
<test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
<TestTemplate>
<i>
<%# Container.Title) %></i>
<br />
<%# Container.Text %>
</TestTemplate>
</test:FormItem>
I have a templateContainer class that has properties for the TextBox and Label.
public class TemplateContainer : WebControl, INamingContainer
{
public TextBox Text { get { return m_item.Text; } }
public Label Title { get { return m_item.Title; } }
private FormItem m_item;
public TemplateContainer(FormItem item)
{
m_item = item;
}
}
In the main FormItem class I have a CreateControlHierarchy() method that is being called from CreateChildControls():
protected virtual void CreateControlHierarchy()
{
m_itemTemplateContainer = new TemplateContainer(this);
TestTemplate.InstantiateIn(m_itemTemplateContainer);
Controls.Add(m_itemTemplateContainer);
}
What I WANT is for the Template to render the actual control. Instead, it's calling ToString() on the control and displaying System.Web.UI.WebControls.Label and System.Web.UI.WebControls.TextBox. Is there a way to make the template add the controls to it's collection instead of just calling ToString() on them?
Note: I've also tried adding the textbox and label to the controls collection of the container which does the same thing.
Ok. So I tried a few things and I came up with an OK solution.
First, I tried to use methods in the data binding expression and then keep track of where in the container's Control collection the textbox or label would go. However, the CompiledTemplateBuilder (which is what .Net internally builds for ITemplates specified in mark up) put all of the markup before and after both binding expressions into one DataBoundLiteral control and the Control collection was already built when the method was called.
What did work was to create a new WebControl which serves as a place holder for the controls within the composite control. It has one property Control and when set, it add the control to it's Controls Collection.
public class FormItemPlaceHolder : WebControl, INamingContainer
{
public WebControl Control
{
get
{
if(Controls.Count == 0)
return null;
return Controls[0] as WebControl;
}
set
{
if (Controls.Count != 0)
Controls.Clear();
Controls.Add(value);
}
}
}
Then in the mark up, I create a control of this type and bind it's Control property to the correct property in the container.
<test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
<TestTemplate>
<i>
<test:FormItemPlaceHolder ID="ph" runat="server"
Control='<%# Container.Title %>' />
</i>
<br />
<test:FormItemPlaceHolder ID="ph2" runat="server"
Control='<%# Container.Text %>' />
</TestTemplate>
</test:FormItem>
Does anyone have a better solution?
The container should not define the controls, just the data.
It is in the markup that you should define the actual controls of the data, and assign them the values in from the container.
E.g.
public class TemplateContainer : UserControl
{
public string Text { get { return m_text; } }
public string Title { get { return m_title; } }
private string m_text;
private string m_title;
private FormItem m_item;
public TemplateContainer(FormItem item)
{
m_item = item;
}
}
And in the markup:
<test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
<TestTemplate>
<i><asp:Label runat="server" Text='<%# Container.Title) %>' /></i>
<br />
<asp:TextBox runat="server" Text='<%# Container.Text %>' />
</TestTemplate>
</test:FormItem>
If you are trying to create a composite control that does not require controls to be added in the markup, then why are you using a Template? If it is just for styling then perhaps creating your own Style object may be more effective?