System.Dynamic ExpandoControl is it possible? - asp.net

I'm trying to figure out how to create a web server control which is basically an ExpandoObject.
The desire is to automatically create a property on the control when it is created in the aspx markup.
For example:
<x:ExpandoControl someProperty="a value"></x:ExpandoControl>
Where the someProperty attribute does not yet exist as a property on the control.
I should also mention that I don't strictly need any functionality of Control or WebControl. I just need to be able to declare it in markup with runat="server" (which in and of itself may require it to be a control, at least that's what I'm thinking).
Is it possible? If so how can I get started?
Many thanks.

I think your first bet would be to implement IAttributeAccessor:
public interface IAttributeAccessor
{
string GetAttribute(string key);
void SetAttribute(string key, string value);
}
The ASP.NET page parser calls IAttributeAccessor.SetAttribute for each attribute it cannot map to a public property.
So perhaps you can start with
public class ExpandoControl : Control, IAttributeAccessor
{
IDictionary<string, object> _expando = new ExpandoObject();
public dynamic Expando
{
{
return _expando;
}
}
void IAttributeAccessor.SetValue(string key, string value)
{
_expando[key] = value;
}
string IAttributeAccessor.GetValue(string key)
{
object value;
if (_expando.TryGetValue(key, out value) && value != null)
return value.ToString();
else
return null;
}
}

Related

ASP.NET. How to modify returned JSON (actionfilter)

We have an ASP.NET application. We cannot edit source code of controllers. But we can implement ActionFilter.
One of our controller action methods returns JSON. Is it possible to modify it in ActionFilter? We need to add one more property to a returned object.
Maybe, some other way to achieve it?
Found this interesting and as #Chris mentioned, though conceptually I knew this would work, I never tried this and hence thought of giving it a shot. I'm not sure whether this is an elegant/correct way of doing it, but this worked for me. (I'm trying to add Age property dynamically using ActionResult)
[PropertyInjector("Age", 12)]
public ActionResult Index()
{
return Json(new { Name = "Hello World" }, JsonRequestBehavior.AllowGet);
}
And the filter:
public class PropertyInjector : ActionFilterAttribute
{
string key;
object value;
public PropertyInjector(string key, object value)
{
this.key = key;
this.value = value;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var jsonData = ((JsonResult)filterContext.Result).Data;
JObject data = JObject.FromObject(jsonData);
data.Add(this.key,JToken.FromObject(this.value));
filterContext.Result = new ContentResult { Content = data.ToString(), ContentType = "application/json" };
base.OnActionExecuted(filterContext);
}
}
Update
If it's not dynamic data which is to be injected, then remove filter constructor and hard code key & value directly and then the filter could be registered globally without editing the controller
GlobalFilters.Filters.Add(new PropertyInjector());

C# ASP.NET - Controlling/updating a textbox.text value through a class

Newbie here, I need help with a website I'm creating.
I have a class that does some analysis on some text that is input by the user, the class then finds an appropriate answer and sends it back to the textbox. (in theory)
Problem is I don't know how I can control and access the textbox on the default.aspx page from a class, all I get is "object reference is required non static field".
I made the textbox public in the designer file yet still no joy. :(
I've also read this: How can I access the controls on my ASP.NET page from a class within the solution? , which I think is along the lines of what I'm trying to achieve but I need clarification/step by step on how to achieve this.
Hope someone can point me in the right direction.
Many thanks,
Kal
This is the code I have added to the designer.cs file:
public global::System.Web.UI.WebControls.TextBox TextBox3;
public string MyTextBoxText
{
get
{
return TextBox3.Text;
}
set
{
TextBox3.Text = value;
}
}
This is the class method i have created:
public static cleanseMe(string input)
{
string utterance = input;
string cleansedUtt = Regex.Replace(utterance, #"[!]|[.]|[?]|[,]|[']", "");
WebApplication1._Default.TextBox3.text = cleansedUtt;
}
I could just return the cleansedUtt string i know, but is it possible for me to just append this string to the said textbox from this method, within this class?
I also tried it this way, i wrote a class that takes in the name of the textbox and string to append to that textbox. it works BUT only on the default.aspx page and does not recognise the textbox names within the difference classes. The code is as follows:
public class formControl
{
public static void ModifyText(TextBox textBox, string appendthis)
{
textBox.Text += appendthis + "\r\n";
}
I would suggest you that do not access the Page Controls like TextBox in your class. It will be more useful and a good practice that whatever functionality your class does, convert them into function which accept the parameters and returns some value and then on the basis of that value you can set the controls value.
So now you have reusable function that you can use from any of the page you want. You do not need to write it for every textbox.
Here I am giving you a simple example
public class Test
{
public bool IsValid(string value)
{
// Your logic
return true;
}
}
Now you can use it simple on your page like this
Test objTest = new Test();
bool result=objTest.IsValid(TextBox1.Text);
if(result)
{
TextBox1.Text="Everything is correct";
}
else
{
TextBox1.Text="Something went wrong";
}
If you have your class in the same project (Web Project) the following will work:
public class Test
{
public Test()
{
//
// TODO: Add constructor logic here
//
}
public static void ValidateTextBox(System.Web.UI.WebControls.TextBox txt)
{
//validation logic here
if (txt != null)
txt.Text = "Modified from class";
}
}
You can use this from your webform like this:
protected void Page_Load(object sender, EventArgs e)
{
Test.ValidateTextBox(this.txt);
}
If your class is in a different (class project), you would need to add a reference to System.Web to your project.

Required Property in Custom ASP .NET Control

I am making a custom web control for my ASP page that inherits from CompositeDataBoundControl. I have a public property in the definition of my control that is required, if the user does not provide this property in the control definition on an ASP page it will break and we will get an exception. I want the compiler to throw a warning similar to the one when a user forgets to provide the 'runat' property of a Control.
"Validation (ASP.Net): Element 'asp:Button' is missing required attribute 'runat'."
Here is basically what my code looks like:
public class MyControl : CompositeDataBoundControl, IPostBackEventHandler
{
private string _someString;
public string SomeString
{
get { return _someString; }
set { _someString = value; }
}
// Other Control Properties, Functions, Events, etc.
}
I want "SomeString" to be a required property and throw a compiler warning when I build my page.
I have tried putting a Required attribute above the property like so:
[Required]
public string SomeString
{
get { return _someString; }
set { _someString = value; }
}
but this doesnt seem to work.
How can I generate such a compiler message?
Thats quite simple, on page load you can check if the property has some value or not. You can check it for Null or Empty case depending on your property type.
like if i have this
private string _someString;
public string SomeString
{
get { return _someString; }
set { _someString = value; }
}
On page_load event i will check if
if(_someString != null && _someString != "")
{
String message = "Missing someString property";
isAllPropertySet = false; //This is boolean variable that will decide whether any property is not left un-initialised
}
All The Best.
and finally

Whats the point of the ObjectDataSource

Given that every grid, combobox, checkboxlist and in general multi row control supports binding directly to any IEnumerable what is the point of the ObjectDataSource?
Why would one use it as opposed to binding directly to your collection? Particularly if you already have reasonable separation of concerns in your business, presentation and data layers?
I also feel this is an even more relevant question since the introduction of LINQ. I have often found that when binding I would like to perform some further ordering, exclusion and so forth using LINQ and I believe this is not possible when using the ObjectDataSource without creating a specific method for your (potentially single use case)?
So when is it appropriate to use an ObjectDataSource and what are the advantages compared to direct binding to IEnumerable?
First, ObjectDataSource is usually used in ASP.NET WebForms (aspx). ObjectDataSource is located in System.Web.UI.WebControls, as you can see this link on MSDN Library:
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.objectdatasource.aspx
Using ObjectDataSource to bind your data means you bind you'll have datasource as an object, can be in form of DataSet or any other .NET object that implements IEnumerable. Using ObjectDataSource means you have to perform your own Select, Update, Insert and Delete method that usually found in SqlDataSource.
There's this nice walkthrough in MSDN Library: Walkthrough: Data Binding to a Custom Business Object
But binding to a simple IEnumerable without implementing IListSource (like DataTable has) means you won't have nice feature such as data bindings to a complex data control such as GridView. And you'll lose other feature too, because a simple IEnumerable alone can't be bound in two ways to other list control such as ListView and GridView.
To have your data to be bindable two way, your object must also implement INotifyPropertyChanged interface before added into the IListSource as data item.
Samples:
public class Employee : BusinessObjectBase
{
private string _id;
private string _name;
private Decimal parkingId;
public Employee() : this(string.Empty, 0) {}
public Employee(string name) : this(name, 0) {}
public Employee(string name, Decimal parkingId) : base()
{
this._id = System.Guid.NewGuid().ToString();
// Set values
this.Name = name;
this.ParkingID = parkingId;
}
public string ID
{
get { return _id; }
}
const string NAME = "Name";
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
// Raise the PropertyChanged event.
OnPropertyChanged(NAME);
}
}
}
const string PARKING_ID = "Salary";
public Decimal ParkingID
{
get { return parkingId; }
set
{
if (parkingId != value)
{
parkingId = value;
// Raise the PropertyChanged event.
OnPropertyChanged(PARKING_ID);
}
}
}
}
This is the implementation of INotifyPropertyChanged:
public class BusinessObjectBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (null != PropertyChanged)
{
PropertyChanged(this, e);
}
}
#endregion
}
You can bind to an enum with it if you dont want codebehind.

ASP.NET - Avoid hardcoding paths

I'm looking for a best practice solution that aims to reduce the amount of URLs that are hard-coded in an ASP.NET application.
For example, when viewing a product details screen, performing an edit on these details, and then submitting the changes, the user is redirected back to the product listing screen. Instead of coding the following:
Response.Redirect("~/products/list.aspx?category=books");
I would like to have a solution in place that allows me to do something like this:
Pages.GotoProductList("books");
where Pages is a member of the common base class.
I'm just spit-balling here, and would love to hear any other way in which anyone has managed their application redirects.
EDIT
I ended up creating the following solution: I already had a common base class, to which I added a Pages enum (thanks Mark), with each item having a System.ComponentModel.DescriptionAttribute attribute containing the page's URL:
public enum Pages
{
[Description("~/secure/default.aspx")]
Landing,
[Description("~/secure/modelling/default.aspx")]
ModellingHome,
[Description("~/secure/reports/default.aspx")]
ReportsHome,
[Description("~/error.aspx")]
Error
}
Then I created a few overloaded methods to handle different scenarios. I used reflection to get the URL of the page through it's Description attribute, and I pass query-string parameters as an anonymous type (also using reflection to add each property as a query-string parameter):
private string GetEnumDescription(Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
return attr.Description;
}
}
return null;
}
protected string GetPageUrl(Enums.Pages target, object variables)
{
var sb = new StringBuilder();
sb.Append(UrlHelper.ResolveUrl(Helper.GetEnumDescription(target)));
if (variables != null)
{
sb.Append("?");
var properties = (variables.GetType()).GetProperties();
foreach (var property in properties)
sb.Append(string.Format("{0}={1}&", property.Name, property.GetValue(variables, null)));
}
return sb.ToString();
}
protected void GotoPage(Enums.Pages target, object variables, bool useTransfer)
{
if(useTransfer)
HttpContext.Current.Server.Transfer(GetPageUrl(target, variables));
else
HttpContext.Current.Response.Redirect(GetPageUrl(target, variables));
}
A typical call would then look like so:
GotoPage(Enums.Pages.Landing, new {id = 12, category = "books"});
Comments?
I'd suggest that you derive your own class ("MyPageClass") from the Page class and include this method there:
public class MyPageClass : Page
{
private const string productListPagePath = "~/products/list.aspx?category=";
protected void GotoProductList(string category)
{
Response.Redirect(productListPagePath + category);
}
}
Then, in your codebehind, make sure that your page derives from this class:
public partial class Default : MyPageClass
{
...
}
within that, you can redirect just by using:
GotoProductList("Books");
Now, this is a bit limited as is since you'll undoubtedly have a variety of other pages like the ProductList page. You could give each one of them its own method in your page class but this is kind of grody and not smoothly extensible.
I solve a problem kind of like this by keeping a db table with a page name/file name mapping in it (I'm calling external, dynamically added HTML files, not ASPX files so my needs are a bit different but I think the principles apply). Your call would then use either a string or, better yet, an enum to redirect:
protected void GoToPage(PageTypeEnum pgType, string category)
{
//Get the enum-to-page mapping from a table or a dictionary object stored in the Application space on startup
Response.Redirect(GetPageString(pgType) + category); // *something* like this
}
From your page your call would be: GoToPage(enumProductList, "Books");
The nice thing is that the call is to a function defined in an ancestor class (no need to pass around or create manager objects) and the path is pretty obvious (intellisense will limit your ranges if you use an enum).
Good luck!
You have a wealth of options availible, and they all start with creating a mapping dictionary, whereas you can reference a keyword to a hard URL. Whether you chose to store it in a configuration file or database lookup table, your options are endless.
You have a huge number of options available here. Database table or XML file are probably the most commonly used examples.
// Please note i have not included any error handling code.
public class RoutingHelper
{
private NameValueCollecton routes;
private void LoadRoutes()
{
//Get your routes from db or config file
routes = /* what ever your source is*/
}
public void RedirectToSection(string section)
{
if(routes == null) LoadRoutes();
Response.Redirect(routes[section]);
}
}
This is just sample code, and it can be implemented any way you wish. The main question you need to think about is where you want to store the mappings. A simple xml file could do it:
`<mappings>
<map name="Books" value="/products.aspx/section=books"/>
...
</mappings>`
and then just load that into your routes collection.
public class BasePage : Page
{
public virtual string GetVirtualUrl()
{
throw new NotImplementedException();
}
public void PageRedirect<T>() where T : BasePage, new()
{
T page = new T();
Response.Redirect(page.GetVirtualUrl());
}
}
public partial class SomePage1 : BasePage
{
protected void Page_Load()
{
// Redirect to SomePage2.aspx
PageRedirect<SomePage2>();
}
}
public partial class SomePage2 : BasePage
{
public override string GetVirtualUrl()
{
return "~/Folder/SomePage2.aspx";
}
}

Resources