I have inherited an asp.net web app , that passes information to a gridview. Each row has different buttons and links on it and they are enabled/disabled based on the information received. There is a very long chain of logic that each row goes through to set up the buttons, which makes it very difficult to read. Is there a way to set up a class of buttons to make this easier to read?
Not sure what you mean by a "class of buttons", but you can create a custom server controls as a wrapper to the button control, and provide some extra properties or methods to simplify the GridView logic.
Here's a quick and dirty example of a RadioButton wrapper that we built, which extends the properties of the standard Radio Button so it can hold extra information. You can try doing something similar for your GridView buttons:
[DefaultProperty("Text")]
[ToolboxData("<{0}:RadioButton runat=server></{0}:RadioButton>")]
public class RadioButton : System.Web.UI.WebControls.RadioButton
{
[Bindable(true)]
[DefaultValue("")]
[Localizable(true)]
public string Value
{
get
{
string RadioValue = (string)ViewState["Value"];
return (RadioValue == null) ? String.Empty : RadioValue;
}
set
{
ViewState["Value"] = value;
}
}
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(Text);
}
}
One thing that you could do quickly would be to convert the DataSource to a report class. You could then move the logic for enabling/disabling into that class (i.e., individual property per button), and update the grid to simply check the appropriate property per link/button.
This at least moves the logic out of the aspx file to somewhere it can be maintained/perused more easily.
For example:
grid.DataSource = MyReportClass.GetReport();
Where
public MyReportClass
{
public string Name { get; set; }
public string EnableLink1 { get { //logic here } }
public string EnableButton3 { get { ///logic here } }
public static List<MyReportClass> GetReport()
{
// get the data
}
}
then the aspx becomes
<gridview id="grid" runat="server" ... >
...
<asp:templatefield headertext="Link1" ><itemtemplate>
<asp:linkbutton id="l1" runat="server" ...
visible='<%# !(bool)DataBinder.Eval(Container.DataItem, "EnableLink1") %>'
/>
</itemTemplate></asp:templatefield>
...
</gridview>
Related
I can see from other people's controls that it is possible to set sub-object properites in markup. For example, if I'm using Telerik's RadComboBox I can write ...
<telerik:RadComboBox runat="server" ID="RadComboBox2">
<CollapseAnimation Duration="100" />
</telerik:RadComboBox>
or, alternatively I can write...
<telerik:RadComboBox runat="server" ID="RadComboBox2" CollapseAnimation-Duration="100">
</telerik:RadComboBox>
What technique do I have to emply to allow me to do this with controls I write? I thought that I might have to explicitly create properties in my parent control for each of the of properties my sub-object I expose. However, I don't seem to allowed to create a property with a '-' in the name.
Try this:
1 - Property Class Definition
public class Option
{
public string First { get; set; }
public string Last { get; set; }
}
2 - UserControl Definition
public partial class CustomUC : System.Web.UI.UserControl
{
//Enables the Option properties to be filled inside the control's tag
[PersistenceMode(PersistenceMode.InnerProperty)]
//Enables the Option properties to be filled on the control's tag
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Option Options
{
get;set;
}
protected void Page_Load(object sender, EventArgs e) { }
}
3 - Markup:
<own:CustomUC ID="uc" runat="server" Options-First="First" Options-Last="Last" />
or
<own:CustomUC ID="uc" runat="server" >
<Options-First="First" Options-Last="Last" />
</own:CustomUC>
Note: You have to reference the usercontrol assembly first with your own tagPrefix.
I'm creating a custom server control in ASP.NET WebForms and want to have a hyphen in my property name the same way the ASP.NET server controls do in the markup. For example, a Label control has a "Font-Size" property in the markup like so:
<asp:Label ID="Label1" Font-Size="small" Text="hi" runat="server" />
How do I accomplish this?
Just use complex properties on your control:
public class MyControl: WebControl
{
public Test()
{
// Make sure to initialize the complex property or you will get a NRE
// when you try to set the Complex-Bar property in the webpage
Complex = new Complex();
}
public Complex Complex { get; set; }
}
public class Complex
{
public string Bar { get; set; }
}
and then:
<asp:MyControl runat="server" ID="myControl" Complex-Bar="foo bar" />
I added the following to get intellisense working with the complex property:
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[PersistenceMode(PersistenceMode.InnerProperty)]
The Error I am getting is:
The name 'strTitle'(name of propert) does not exist in the current context
To make it very clear I will show the code:
Silverlight:
var myData =from xAudioinfo in xResponse.Descendants(ns + "DIDL-Lite").Elements(ns + "item")
select new RMSMedia
{
strTitle =((string)xFolderInfo.Element(dc + "title")).Trim(),
objectID = (int)xFolderInfo.Attribute("id")
};
Now I am able to bind this in my XAML like below:
<TextBlock Text ="{Binding strTitle}" />
But this is not working in ASP.NET.
ASP.NET
var myData =from xAudioinfo in xResponse.Descendants(ns + "DIDL-Lite").Elements(ns + "item")
select new RMSMedia
{
strTitle =((string)xFolderInfo.Element(dc + "title")).Trim(),
objectID = (int)xFolderInfo.Attribute("id")
};
Trying to bind with HTML Control:
<asp:Label ID="lbWindCondition" runat="server" Text=<%#strTitle %>></asp:Label>
Edit:#Prisoner ZERO
My code structure is as below:
public class currentWeatherCondition
{
public string condition { get; set; }
public string temp { get; set; }
public string imageURL { get; set; }
public string humidity { get; set; }
public string windCondition { get; set; }
}
public partial class _Default : System.Web.UI.Page
{
protected void btnGetDetails_Click(object sender, EventArgs e)
{
try
{
var weatherXML = XDocument.Load(weatherURL);
var weatherResult = from weatherDetail in weatherXML.Descendants("current_conditions")
select new currentWeatherCondition
{
condition = ((string)weatherDetail.Element("condition").Attribute("data")).Trim(),
temp = ((string)weatherDetail.Element("temp_c").Attribute("data")).Trim(),
imageURL = ((string)weatherDetail.Element("icon").Attribute("data")).Trim(),
humidity = ((string)weatherDetail.Element("humidity").Attribute("data")).Trim(),
windCondition = ((string)weatherDetail.Element("wind_condition").Attribute("data")).Trim(),
};
}
catch(..)
{
......
}
}
So do we mean I will directly create the prpoerty in _Default class or create object of currentWeatherCondition in default class and then bind/render it to the control.
I would have to see the rest of your code, but I would guess the value "strTitle" exists solely within the scope of the RMSMedia block. You must (then) expose that value as a public property in one of four ways:
From a public property of the page.
From a public property of the RMSMedia object.
Write a databinding function for the container.
Set the value directly
Just in case you don't know what that means...
(1) From a public property of the page.
public String Title { get; set; }
Then set strTitle to Title and <%=Title %>.
(2) From a public property of the RMSMedia object.
Then set myData.strTitle thusly <%=myData.strTitle %>.
(3) Write a databinding function for the container.
myContainer.DataBinding += new EventHandler(myContainer_DataBinding);
protected void myContainer_DataBinding(object sender, EventArgs e)
{
myContainer.DataSource = myData.strTitle;
}
To call this last one you would use the following: myContainer.DataBind();
(4) Set the value directly within your function
lbWindCondition.Text = myData.strTitle;
UPDATE:
There are a bunch of options, so really, you just choose one. Even though it has nothing to do with the control in question, I added the 3rd one above just as an "extra" to show other ways to bind in ASP.NET. The 4th option is the most obvious answer though because you set the Text value directly (meaning, without indirection).
So do we mean I will directly create the property in _Default class?
Yes...if you like that option.
<asp:Label ID="lbWindCondition" runat="server" Text=<%=Title %>></asp:Label>
Or do we create object of currentWeatherCondition in default class and then it to the control?
Yes...if you like that option.
<asp:Label ID="lbWindCondition" runat="server" Text=<%#currentWeatherCondition.YourProperty %>></asp:Label>
You can also use the DataBinder object, but I usually reserve that for collections of objects (plus folks tell me to avoid it because it is slow...my guess is that it is slow because it must use reflection alongside late-binding to expose the property.). However, you certainly can use it.
Lastly, none of the answers above really discuss which option is the RIGHT choice. In all honesty, anytime you use the indirection operators (<% %>) you utilize late-binding (which is bad). Now folks will tell you that ASP.NET is (now) optimized for late-binding so it's okay...but obviously it's still late binding...so if you "can" do so, set the value directly or use the data-binding option.
I hope this helps!
The Label ASP.NET Control does not have DataSource Property so that it is not possible to directly databind it against anonymous object. What you need to do is
1) Create a page container property which would hold the myData collection
2) Bind the Label against that property. =>
Text = '<%# DataBinder.Eval(ContainerObject, "strTitle"); %>'
I hope that this helps :)
How can I pass user control properties to the page AND make these properties available to all methods on the page (and not just to one method that is fired on a control action, e.g. onControlClick)
I have a set up of essentially 3 pages:
user control (ascx/cs)
class (cs) - that contains user control properties
host page (aspx/cs) - references the user control
The user control consists of 3 interrelated dropdowns. I'm having success passing these dropdown values through a class onto the page via an event that is fired when a user clicks on the dropdown menu. So this way the host page is continously aware of the values in the user control. However, I want the page to use the control's properties (stored in a class) on all of its methods - how do I make this user control class available to all?
Also I'm using ASP.NET and C# by the way.
Here's the Code (not sharing the full code here - just the snippets of a similar code block)
On the ASPX for Menu Host Page:
<linked:LinkMenu2 id="Menu1" runat="server" OnLinkClicked="LinkClicked" />
Host Page (cs):
protected void dropdownclicked(object sender, ddtestEventArgs e)
{
if (e.Url == "Menu2Host.aspx?product=Furniture")
{
lblClick.Text = "This link is not allowed.";
e.Cancel = true;
}
else
{
// Allow the redirect, and don't make any changes to the URL.
}
}
Host Page (aspx)
<asp:dropdowncustom ID="dddone" runat="server" OnddAppClicked="dropdownclicked" />
Control (cs)
public partial class usercontrol_tests_dropdown1 : System.Web.UI.UserControl
{
public event ddtestEventHandler ddAppClicked;
}
public void selectapp_SelectedIndexChanged(object sender, EventArgs e)
{
ddtestEventArgs args = new ddtestEventArgs(selectlink.SelectedValue);
ddAppClicked(this, args);
}
Class:
public class ddtestEventArgs : EventArgs
{
// Link
private string link;
public string Link
{
get { return link; }
set { link = value; }
}
public ddtestEventArgs(string link)
{
Link = link;
}
}
public delegate void ddtestEventHandler(object sender, ddtestEventArgs e);
Hopefully this is what you're after. The best way to do it is to expose your controls as public properties from your user control. So, in your user control, for each drop down list add a property:
public DropDownList DropDown1
{
get { return dropDownList1; }
}
public DropDownList DropDown2
{
get { return dropDownList2; }
}
You can do the same for any other properties you want to access on the host page:
public string DropDown1SelectedValue
{
get { return dropDownList1.SelectedValue; }
set { dropDownList1.SelectedValue = value; }
}
Then, from your host page you can access the properties through the user control:
string value = UserControl1.DropDown1SelectedValue;
or
string value = UserControl1.DropDownList1.SelectedValue;
Here's a couple of other answered questions that you might find useful as I think (if I've understood correctly) this is what you're doing:
Getting data from child controls loaded programmatically
How to change the value of a control in a MasterPage.
I'm just getting started with Custom User Controls in C# and I'm wondering if there are any examples out there of how to write one which accepts nested tags?
For example, when you create an asp:repeater you can add a nested tag for itemtemplate.
I wrote a blog post about this some time ago. In brief, if you had a control with the following markup:
<Abc:CustomControlUno runat="server" ID="Control1">
<Children>
<Abc:Control1Child IntegerProperty="1" />
</Children>
</Abc:CustomControlUno>
You'd need the code in the control to be along the lines of:
[ParseChildren(true)]
[PersistChildren(true)]
[ToolboxData("<{0}:CustomControlUno runat=server></{0}:CustomControlUno>")]
public class CustomControlUno : WebControl, INamingContainer
{
private Control1ChildrenCollection _children;
[PersistenceMode(PersistenceMode.InnerProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Control1ChildrenCollection Children
{
get
{
if (_children == null)
{
_children = new Control1ChildrenCollection();
}
return _children;
}
}
}
public class Control1ChildrenCollection : List<Control1Child>
{
}
public class Control1Child
{
public int IntegerProperty { get; set; }
}
I followed Rob's blog post, and made a slightly different control. The control is a conditional one, really just like an if-clause:
<wc:PriceInfo runat="server" ID="PriceInfo">
<IfDiscount>
You don't have a discount.
</IfDiscount>
<IfNotDiscount>
Lucky you, <b>you have a discount!</b>
</IfNotDiscount>
</wc:PriceInfo>
In the code I then set the HasDiscount property of the control to a boolean, which decides which clause is rendered.
The big difference from Rob's solution, is that the clauses within the control really can hold arbitrary HTML/ASPX code.
And here is the code for the control:
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebUtilities
{
[ToolboxData("<{0}:PriceInfo runat=server></{0}:PriceInfo>")]
public class PriceInfo : WebControl, INamingContainer
{
private readonly Control ifDiscountControl = new Control();
private readonly Control ifNotDiscountControl = new Control();
public bool HasDiscount { get; set; }
[PersistenceMode(PersistenceMode.InnerProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Control IfDiscount
{
get { return ifDiscountControl; }
}
[PersistenceMode(PersistenceMode.InnerProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Control IfNotDiscount
{
get { return ifNotDiscountControl; }
}
public override void RenderControl(HtmlTextWriter writer)
{
if (HasDiscount)
ifDiscountControl.RenderControl(writer);
else
ifNotDiscountControl.RenderControl(writer);
}
}
}
I ended up with something very similar to the answer by Rob (in wayback archive) #gudmundur-h, but I used ITemplate to get rid of that annoying "You can't place content between X tags" in the usage. I'm not entirely sure what is actually required or not, so it's all here just in case.
The partial/user control markup: mycontrol.ascx
Note the important bits: plcChild1 and plcChild2.
<!-- markup, controls, etc -->
<div class="shell">
<!-- etc -->
<!-- optional content with default, will map to `ChildContentOne` -->
<asp:PlaceHolder ID="plcChild1" runat="server">
Some default content in the first child.
Will show this unless overwritten.
Include HTML, controls, whatever.
</asp:PlaceHolder>
<!-- etc -->
<!-- optional content, no default, will map to `ChildContentTwo` -->
<asp:PlaceHolder ID="plcChild2" runat="server"></asp:PlaceHolder>
</div>
The partial/user control codebehind: mycontrol.ascx.cs
[ParseChildren(true), PersistChildren(true)]
[ToolboxData(false /* don't care about drag-n-drop */)]
public partial class MyControlWithNestedContent: System.Web.UI.UserControl, INamingContainer {
// expose properties as attributes, etc
/// <summary>
/// "attach" template to child controls
/// </summary>
/// <param name="template">the exposed markup "property"</param>
/// <param name="control">the actual rendered control</param>
protected virtual void attachContent(ITemplate template, Control control) {
if(null != template) template.InstantiateIn(control);
}
[PersistenceMode(PersistenceMode.InnerProperty),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public virtual ITemplate ChildContentOne { get; set; }
[PersistenceMode(PersistenceMode.InnerProperty), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public virtual ITemplate ChildContentTwo { get; set; }
protected override void CreateChildControls() {
// clear stuff, other setup, etc
// needed?
base.CreateChildControls();
this.EnsureChildControls(); // cuz...we want them?
// using the templates, set up the appropriate child controls
attachContent(this.ChildContentOne, this.plcChild1);
attachContent(this.ChildContentTwo, this.plcChild2);
}
}
Important bits (?):
ParseChildren -- so stuff shows up?
PersistChildren -- so dynamically created stuff doesn't get reset?
PersistenceMode(PersistenceMode.InnerProperty) -- so controls are parsed correctly
DesignerSerializationVisibility(DesignerSerializationVisibility.Content) -- ditto?
The control usage
<%# Register Src="~/App_Controls/MyStuff/mycontrol.ascx" TagPrefix="me" TagName="MyNestedControl" %>
<me:MyNestedControl SomeProperty="foo" SomethingElse="bar" runat="server" ID="meWhatever">
<%-- omit `ChildContentOne` to use default --%>
<ChildContentTwo>Stuff at the bottom! (not empty anymore)</ChildContentTwo>
</me:MyNestedControl>
My guess is you're looking for something like this? http://msdn.microsoft.com/en-us/library/aa478964.aspx
Your tags were removed or are invisible, so can't really help you there.