|
|
Tuesday, May 19, 2009
When we were adding WebForm routing to ASP.NET 4 Beta 1, we didn’t have a chance to add in a couple of methods which make working with Routes and Web Forms a lot easier! Note: the code below is ‘similar’ to that which we’re adding ASP.NET 4 Beta 2, *that* code will be of much higher quality however, this is a sample only! 1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Web;
5: using System.Web.Routing;
6:
7: public static class RouteExtensions
8: {
9: public static string GetUrlForRoute(this System.Web.UI.Page page, string routeName, RouteValueDictionary parameters)
10: {
11: VirtualPathData vpd= RouteTable.Routes.GetVirtualPath(null, routeName, parameters);
12: return vpd.VirtualPath;
13: }
14: public static string GetUrlForRoute(this System.Web.UI.Page page, RouteValueDictionary parameters)
15: {
16: VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(null, parameters);
17: return vpd.VirtualPath;
18: }
19:
20: public static void IgnoreRoute(this RouteCollection routes, string url)
21: {
22: routes.IgnoreRoute(url, null);
23: }
24:
25: public static void IgnoreRoute(this RouteCollection routes, string url, object constraints)
26: {
27: if (routes == null)
28: {
29: throw new ArgumentNullException("routes");
30: }
31: if (url == null)
32: {
33: throw new ArgumentNullException("url");
34: }
35: IgnoreRouteInternal internal3 = new IgnoreRouteInternal(url);
36: internal3.Constraints = new RouteValueDictionary(constraints);
37: IgnoreRouteInternal item = internal3;
38: routes.Add(item);
39: }
40:
41: public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile)
42: {
43: return routes.MapRoute(name, url, physicalFile, null, null);
44: }
45:
46: public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, object defaults)
47: {
48: return routes.MapRoute(name, url, physicalFile, defaults, null);
49: }
50:
51: public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, string[] namespaces)
52: {
53: return routes.MapRoute(name, url, null, null, namespaces);
54: }
55:
56: public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, object defaults, object constraints)
57: {
58: return routes.MapRoute(name, url, physicalFile, defaults, constraints, null);
59: }
60:
61: public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, object defaults, string[] namespaces)
62: {
63: return routes.MapRoute(name, url, physicalFile, defaults, null, namespaces);
64: }
65:
66: public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, object defaults, object constraints, string[] namespaces)
67: {
68: if (routes == null)
69: {
70: throw new ArgumentNullException("routes");
71: }
72: if (url == null)
73: {
74: throw new ArgumentNullException("url");
75: }
76: if (physicalFile == null)
77: {
78: throw new ArgumentNullException("physicalfile");
79: }
80: Route route2 = new Route(url, new PageRouteHandler(physicalFile));
81: route2.Defaults = new RouteValueDictionary(defaults);
82: route2.Constraints = new RouteValueDictionary(constraints);
83: Route item = route2;
84: if ((namespaces != null) && (namespaces.Length > 0))
85: {
86: item.DataTokens = new RouteValueDictionary();
87: item.DataTokens["Namespaces"] = namespaces;
88: }
89: routes.Add(name, item);
90: return item;
91: }
92:
93: // Nested Types
94: private sealed class IgnoreRouteInternal : Route
95: {
96: // Methods
97: public IgnoreRouteInternal(string url)
98: : base(url, new StopRoutingHandler())
99: {
100: }
101:
102: public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues)
103: {
104: return null;
105: }
106: }
107: }
108:
So, how do you use these methods?
These extensions add a few simple methods:
MapRoute
Stolen shamelessly from MVC. These methods provide a shortcut to defining Routes for WebForms.
RouteCollection.MapRoute(string name, string url, string physicalFile);
For example to map a simple route you can now do:
RouteTable.Routes.MapRoute(“ProductDetailsRoute”, “products/{productid}”, “~/ProductDetails.aspx”);
Also has a couple of variants allowing for defining defaults etc…
RouteCollection.MapRoute(string name, string url, string physicalFile, object defaults);
RouteCollection.MapRoute(string name, string url, string physicalFile, object defaults, string[] namespaces);
GetRouteForUrl
Page.GetUrlForRoute(string routeName, RouteValueDictionary parameters);
Using a specifically named route and RouteValueDictionary containing a number of parameters will give you back a string containing the Url to use. REALLY useful in databound apps!
e.g.,
<asp:hyperlink ID="HyperLink1" runat="server" NavigateUrl='<%# Page.GetUrlForRoute("ProductDetailsRoute",new RouteValueDictionary( new {productid= Eval("ProductID")}))%>' Text="Details"></asp:hyperlink>
Page.GetUrlForRoute(RouteValueDictionary parameters);
Same thing, but auto-matches the RouteValueDictionary parameters against a route…
IgnoreRoute
Allows you to define urls which should not be tried for matches against routes (can be useful for upgrading WebForms apps)
RouteCollection.IgnoreRoute(string url);
RouteCollection.IgnoreRoute(string url, object constraints);
Friday, May 08, 2009
UPDATE: I’ve put the entire CSS Manager source including this control on my Codeplex site, you can get it here. This has been a bit of an obsession for the past couple of days…I wrote previously about a little CSS combining control which I’m working on. As part of this I wanted to experiment with ways of optimizing our current controls. Well, I thought I’d throw this up (in both meanings of the term) here for some quick reference. Again, not fully implemented, just an experiment. You’ll also quickly notice that there’s some other gubbins missing; like my CSSManager base page, I’ll update this post with their location once I get them tidied up a bit.
Well, what do I do to avoid inline styles, simple…cheat :). Firstly I went spelunking in the GridView control using Reflector and saw where the styles are actually set. A method called ‘PrepareControlHierarchy’, this basically sets the style properties on all of GridView’s child controls. The ‘other’ way you can do this is through the use of Control Adapters (e.g., the CSS Friendly Adapters), but they’re a bit more involved than I wanted for this. In this method I basically call ‘Reset()’ on the styles and set ‘CSSClass’ for each of the styles instead. For the actual styles, I pull them into a string and throw them over to my little Page control for addition to my CSS Manager thingy…(again, be online soon). This then renders the style out to either page or an external CSS file. Anyway, as I’ve said this is really just a hack but it was a fun little thing to play with…and who knows, someone might find it useful! 1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Web.UI.WebControls;
6: using System.Web.UI;
7: using System.Drawing;
8: using System.IO;
9:
10: namespace CSSManager
11: {
12: public class CSSGridView : GridView
13: {
14:
15: public new CSSManagerPage Page
16: {
17: get
18: {
19: return (CSSManagerPage)base.Page;
20: }
21: }
22:
23: protected override void OnPreRender(EventArgs e)
24: {
25:
26: if (!DesignMode)
27: {
28: StringBuilder sb = new StringBuilder();
29: if (this.Controls.Count > 0)
30: {
31: bool controlStyleCreated = base.ControlStyleCreated;
32: Table table = this.Controls[0] as Table;
33:
34:
35:
36: if (table == null)
37: throw new Exception("The first control must be a Table");
38:
39:
40:
41: Style baseStyle = base.ControlStyle;
42: string baseStyleName = this.ClientID + "BaseStyle";
43: sb.AppendFormat(".{0}{{{1}}}", baseStyleName, baseStyle.GetStyleAttributes(this).Value);
44:
45: table.ControlStyle.Reset();
46: table.ControlStyle.CssClass = baseStyleName;
47:
48: Style alternatingRowStyle = AlternatingRowStyle;
49: alternatingRowStyle.MergeWith(RowStyle);
50: sb.AppendFormat(".{0}{{{1}}}", this.ClientID + "AlternatingRow", alternatingRowStyle.GetStyleAttributes(this).Value);
51: sb.AppendFormat(".{0}{{{1}}}", this.ClientID + "Row", RowStyle.GetStyleAttributes(this).Value);
52:
53: foreach (GridViewRow row in this.Rows)
54: {
55:
56: switch (row.RowType)
57: {
58: case DataControlRowType.Header:
59: if (this.ShowHeader && (this.HeaderStyle != null))
60: {
61: string className = this.ClientID + "HeadStyle";
62:
63: sb.AppendFormat(".{0}{{{1}}}", className, HeaderStyle.GetStyleAttributes(this).Value);
64: row.ControlStyle.Reset();
65: row.CssClass = className;
66: }
67: break;
68:
69:
70: case DataControlRowType.Footer:
71: if (this.ShowFooter && (this.FooterStyle != null))
72: {
73: string className = this.ClientID + "FootStyle";
74: sb.AppendFormat(".{0}{{{1}}}", className, FooterStyle.GetStyleAttributes(this).Value);
75: row.ControlStyle.Reset();
76: row.CssClass = className;
77: }
78: break;
79:
80:
81: case DataControlRowType.DataRow:
82: string rowClassName = string.Empty;
83: if ((row.RowIndex % 2) == 0)
84: {
85: rowClassName = this.ClientID + "AlternatingRow";
86:
87: }
88: else
89: {
90: rowClassName = this.ClientID + "Row";
91: }
92: row.ControlStyle.Reset();
93: row.CssClass = rowClassName;
94: break;
95:
96: }
97:
98: }
99: }
100: this.Page.RegisterCSSStyle(sb.ToString());
101: }
102: base.OnPreRender(e);
103: }
104:
105:
106:
107: protected override void PrepareControlHierarchy()
108: {
109: if (DesignMode)
110: base.PrepareControlHierarchy();
111: }
112:
113: }
114: }
What does this actually generate? Well, if you’ve seen the output you normally get from a GroidView you’ll be familiar with all of the inline styles (so, style=”font-family…” etc…which are duplicated for every single row…which kind of sucks as they’re almost all exactly the same. This little control instead generates a little bit of CSS.
1: <style type="text/css">
2: .GridView1BaseStyle{background-color:White;border-color:#CCCCCC;border-width:1px;border-style:None;font-family:Verdana;}.GridView1AlternatingRow{color:#000066;}.GridView1Row{color:#000066;}
3: </style>
Then hooks that up to the GridView:
1: <table class="GridView1BaseStyle" border="0" id="GridView1">
2: <tr>
3: <th scope="col">Discontinued</th><th scope="col">ProductID</th><th scope="col">ProductName</th><th scope="col">QuantityPerUnit</th><th scope="col">ReorderLevel</th><th scope="col">UnitPrice</th><th scope="col">UnitsInStock</th><th scope="col">UnitsOnOrder</th><th scope="col">Categories.CategoryID</th><th scope="col">Suppliers.SupplierID</th>
4: </tr><tr class="GridView1AlternatingRow">
5: <td><span disabled="disabled"><input id="GridView1_ctl02_ctl00" type="checkbox" name="GridView1$ctl02$ctl00" disabled="disabled" /></span></td><td>1</td><td>Chai</td><td>10 boxes x 20 bags</td><td>10</td><td>18.0000</td><td>39</td><td>0</td><td>1</td><td>1</td>
6: </tr>…
So you see that we (mostly) got rid of all of the inline styles; apart from the annoying “border=0” which is a major pain in the ass to clear out! In ASP.NET 4 you’ll be able to turn this off as well though!
Oh, one important thing. This also works with Auto-Formatting…and the designer :)
Monday, April 27, 2009
AdapterGroups are in Beta 1 but will (likely) not be in Beta 2 of ASP.NET 4.0 This week I have the job of removing a feature which I originally designed for ASP.NET 4.0. This feature is ‘AdapterGroups’, so what is it? As you may be aware ASP.NET has a capability which allows switching how a control is rendered based on the capabilities of the browser the user makes the request for an ASPX page using. One of the ways this using a feature called Control Adapters. Even though these were originally designed to support mobile browsers, one of the most common uses for them right now is to ‘fix up’ the markup which many of our controls render using the CSS Control Adapters, these in essence modify the code which specific controls (those with adapters) use to render their output. See Scott Guthrie’s post as well as the original whitepaper on how these work; oh, and of course Fritz Onion’s great article on the topic. So, by now you should see that Control Adapters can be pretty handy when you need to replace the default rendering which our controls provide to be more CSS compliant (or, whatever else you have an adapter for…). One of our big pushes in ASP.NET 4.0 is to improve how controls by default work with CSS; and we’re making a number of changes to the controls themselves to make this happen. Originally the plan was to use the CSS Control Adapters for this, but they had some problems: 1. They’re potentially hard to configure. You have to understand how to use browser files (remember, the original use was for switching markup for mobile devices) For example, to change a single adapter on a specific control type, you’d have to create an App_Browsers directory in your app then create a new *.browser file and enter it’s contents, pointing to the adapter you have created (or dropped the dll ofg into your bin directory). This sample is taken from Fritz Onion’s MSDN article 1: <browsers>
2: <browser refID="Default">
3: <controlAdapters>
4: <adapter
5: controlType="System.Web.UI.WebControls.BulletedList"
6: adapterType="MsdnMagazine.BulletedListControlAdapter"
7: />
8: </controlAdapters>
9: </browser>
10: </browsers>
2. you can only configure these by control for a whole site at a time (want to change just the one you have problems with, well…tough!)
Number two is where AdapterGroup came in…the idea being that if you wanted to use one of the CSS Control Adapters for just one specific page / one specific control with which you were having problems, well you could just do this:
1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" AdapterGroup="CSSAdapterGroup" %>
Notice in the above snippet that I’ve just set the AdapterGroup property for this page. This corresponds to a group defined in a *.browser file as follows:
1: <browsers>
2: <browser refID="Default">
3: <controlAdapters>
4: <adapterGroup name="CSSAdapterGroup" >
5: <adapter
6: controlType="System.Web.UI.WebControls.BulletedList"
7: adapterType="MsdnMagazine.BulletedListControlAdapter"
8: />
9: </controlAdapters>
10: </adapterGroup>
11: </browser>
12: </browsers>
Notice, that we’ve identified a specific set (well, one in this case!) of Control Adapters as belonging to this specific adapter group. It’s also possible to set this at site (by specifying the defaultAdapterGroup attribute in the Page config element), as well as just for individual controls, e.g.,
1: <asp:Menu ID="Menu1" runat="server" adapterGroup="CSSAdapterGroup">
2: ...
3: </asp:Menu>
Again, the idea being that if you wanted just to fix up a page / control at a time to use specific adapters then this became possible.
However, as the owner of the feature I was never able to sell the rest of the team on this concept. So, my task in the next few days is to have this feature removed before Beta 2 ships…well, I liked it :-)
Thus ends the story of the disappearing feature…
Friday, April 24, 2009
CAUTION: When I say hack I mean it…this is in NO WAY production ready…it’s just a sample at this stage. If it causes your dog to explode, don’t blame me! I thought I’d throw a work in progress online for people to have a play with. This is one of my little pet projects (I’m not allowed to write code at work…not a dev as I’[m always reminded!). So, what does it do? Well, one of the major performance issues for current websites is the number of requests they make back to the server, not the actual server-side processing time. When I first wrote about this after reading Steve Sounders excellent ‘ High Performance Websites’ it was frankly a surprise to me! Well, anyway recently I’ve been playing with a number of ASP.NET server control which aim at making optimizing the number of server calls an ASP.NET app has to make much fewer. The first of these is the (as yet) unpublished CSS Sprite Control. Below is the beginning of the second; this was written in a couple of hours last night as I couldn’t sleep for thinking about how it might work. The code is VERY rough but it demonstrates the concept pretty well. In essence I wanted the minimum effort possible to combine and compress multiple <link rel=”stylesheet”> CSS file entries together, minimize them using YUI Compressor ( for .NET…on Codeplex) and output a time dependent (in this case, Date Modified for the file) hash for the file name (in a fixed location at the moment, “~/OutputCSS/”), then output a tag in the page for this new file. So, how does it work? Well, imagine you had a page with some stylesheet definitions like this: 1: <link type="text/css" rel="stylesheet" href="../../../build/logger/assets/logger.css"/>
2: <link type="text/css" rel="stylesheet" href="../../../build/yuitest/assets/testlogger.css"/>
3: <style type="text/css">
4: #container, #container2 {
5: width: 400px;
6: }
7:
8: .yui-carousel-element {
9: margin: 0;
10: padding: 0;
11: }
12:
13: .yui-carousel-element li {
14: border: none;
15: margin: 0;
16: padding: 0;
17: width: 100px;
18: }
19: </style>
As you can see we have two CSS file definitions and a style tag. Wouldn’t it be nice if we could combine and minify these…well…here’s how my little hacky control lets you do it:
1: <asp:CSSManager runat="server">
2: <link type="text/css" rel="stylesheet" href="~/build/logger/assets/logger.css"/>
3: <link type="text/css" rel="stylesheet" href="../../../build/yuitest/assets/testlogger.css"/>
4: <style type="text/css">
5: #container, #container2 {
6: width: 400px;
7: }
8:
9: .yui-carousel-element {
10: margin: 0;
11: padding: 0;
12: }
13:
14: .yui-carousel-element li {
15: border: none;
16: margin: 0;
17: padding: 0;
18: width: 100px;
19: }
20: </style>
21: </asp:CSSManager>
You can see in the snippet above that we just wrapped the CSS definitions in <asp:CSSManager>…</asp:CSSManager> tags…that’s all there is to it! One other thing, the observant will have noticed that I can now use a ~/ in the href…it’s a nice side effect of this method…lets you avoid the crazy path traversal stuff (../../../)
Obviously this is still a bit limited, it doesn’t work in Partial Trust, doesn’t handle multiple scopes; say, Site level CSS, Page Level CSS and Masterpages where you’d want multiple sheets with different contents. Plan is to keep working on this for a little while…so I will almost certainly update this soon.
Source follows…
1: using System;
2: using System.Collections.Generic;
3: using System.ComponentModel;
4: using System.Text;
5: using System.Web.UI;
6: using System.Web.UI.HtmlControls;
7: using System.IO;
8: using System.Security.Cryptography;
9: using Yahoo.Yui.Compressor;
10:
11: namespace CSSManager
12: {
13: [DefaultProperty("Text")]
14: [ToolboxData("<{0}:CSSManager runat=server></{0}:CSSManager>")]
15:
16: [ParseChildren(true, "cssFiles")]
17: public class CSSManager : Control, INamingContainer
18: {
19:
20: public const string FILE_PATH = "~/OutputCSS/{0}.css";
21:
22: public CSSManager() { }
23:
24: public List<HtmlGenericControl> CSSFiles
25: {
26: get;
27: set;
28: }
29:
30:
31: private StringBuilder combinedSheets = new StringBuilder();
32:
33: public string GetStyleSheet(string virtualPath)
34: {
35: string filePath = Context.Server.MapPath(virtualPath);
36: if (!string.IsNullOrEmpty(filePath))
37: {
38: return File.ReadAllText(filePath);
39: }
40: return string.Empty;
41:
42: }
43:
44: public string CalculateFileHash(string filePaths)
45: {
46: MD5CryptoServiceProvider csp = new MD5CryptoServiceProvider();
47: byte[] pathBytes = csp.ComputeHash(System.Text.UTF8Encoding.UTF8.GetBytes(filePaths));
48: return BitConverter.ToUInt64(pathBytes, 0).ToString();
49:
50: }
51:
52: public void WriteFile(string textToWrite, string fileHash)
53: {
54: File.WriteAllText(Context.Server.MapPath(string.Format(FILE_PATH, fileHash)), textToWrite);
55: }
56:
57: public string CreateDatedFilePath(string filePath)
58: {
59: FileInfo fi = new FileInfo(Context.Server.MapPath(filePath));
60: if (fi.Exists)
61: {
62: return fi.LastWriteTimeUtc.Ticks.ToString() + "#" + filePath;
63: }
64: return string.Empty;
65:
66:
67: }
68:
69: protected override void CreateChildControls()
70: {
71:
72: if (this.DesignMode == true)
73: {
74: foreach (var file in CSSFiles)
75: {
76: string rel = file.Attributes["rel"];
77:
78: string href = file.Attributes["href"];
79:
80: if (rel.ToLowerInvariant() == "stylesheet")
81: {
82: file.Attributes["href"] = this.ResolveClientUrl(Context.Server.MapPath(href));
83: this.Controls.Add(file);
84: }
85:
86: }
87: }
88: StringBuilder filePaths = new StringBuilder();
89: int i = 0;
90: foreach (var file in CSSFiles)
91: {
92: string tagName = file.TagName.ToLowerInvariant();
93: if (tagName == "link")
94: {
95: string rel = file.Attributes["rel"];
96: string href = file.Attributes["href"];
97:
98: if (rel.ToLowerInvariant() == "stylesheet")
99: {
100: string styleSheet = GetStyleSheet(href);
101: if (!string.IsNullOrEmpty(styleSheet))
102: combinedSheets.Append(styleSheet);
103: filePaths.AppendFormat("{0}#", CreateDatedFilePath(href));
104: }
105: }
106: else if (tagName == "style")
107: {
108: string inner = file.InnerHtml;
109: combinedSheets.Append(inner);
110: filePaths.Append("CSSSTYLE_" + i);
111: i++;
112: }
113:
114: }
115: string minimizedStyles = CssCompressor.Compress(combinedSheets.ToString(), 0, CssCompressionType.Hybrid);
116: string fileHash = CalculateFileHash(filePaths.ToString());
117: WriteFile(minimizedStyles, fileHash);
118:
119: HtmlGenericControl gen = new HtmlGenericControl();
120: gen.TagName = "link";
121: gen.Attributes.Add("href", this.ResolveClientUrl(string.Format(FILE_PATH, fileHash)));
122: gen.Attributes.Add("rel", "stylesheet");
123: this.Controls.Add(gen);
124: }
125:
126: }
127: }
Sunday, April 05, 2009
This is basically a brain dump…watching / reading too much Sci-Fi :-) Was thinking about the Technological Singularity and how it could happen…which lead me to a different slant on the topic…Be glad I usually spare you these strange mental spasms! I kinda liked this one tho’ Starter phase: transparent electronic retinal implant Uses nano electrodes to stimulate the retina given specific wavelengths of infrared light. A minor modification would allow the use of this device as an optical overlay. A very small camera may be implanted within the vitreous fluid of the eye, allowing the instantaneous interpretation of the current visual field.Such information can also be 3dimensionally processed through the use of synchronized implantable camera electrode modules in both eyes. A very small, directable and morphable lens in front of the camera module would also permit focus / zoom functions for the user. Direct nerve / optic center stimulation using variable length nano scale electrodes. Covers a precisely shaped 3 dimensional area of the brain, permitting direct creation of eye equivalent images. Even further, the creation of interface layers within the brain, permitting replication of communication patterns when passing through the brain. permits the replacement of any brain function by electronic simulation. There should be no realistic boundary to this technology, with any structure able to be simulated.a fully deployable, nanoscale mesh running throughout the brain would enable complete neural interface capabilities. This deep interaction would allow the exact replication and expansion of neural function in an external ultra computer. These simulations are well suited to quantuum computing devices. The vast increase in speed of processing and virtually limitless computational and memory enhancements provided by these computer allows virtually limitless enhancements of the human mind. Instantaneous with the creation of the first of these devices is the arrival of true ai. Once humans experience true computer interaction they choose to forever abandon physical form. Able to live infinite lifetimes in a few milliseconds, the physical form quickly becomes abandoned. The last hold outs quickly find themselves alone on a planet of abandoned bodies. The vast worldwide network now comprised on trans humans is every individuals prefect heaven. We are become gods...time begins.
Friday, March 27, 2009
Well, I’ve been back in the US for 5 days now, still really jet lagged but getting there; also managed to pick up a stomach bug at some point…Yes, I’m a hypochondriac! The three weeks I spend travelling round Europe were really fun, I got to head home and see family for a couple of days as well as spend time in Helsinki, Finland, Antwerp, Belgium and Vasteras, Sweden. The picture below is Peli de Halleux and myself standing on the frozen Baltic Sea…which a few minutes before we’d been swimming in! The three TechDays conferences (twitter feed) were really fun, and my first chance to present to the people who use our products. I presented sessions on MVC as well as covering a lot of the stuff that’s in ASP.NET 4.0. By the end of the 6 talks (2 at each conference) I was pretty confident (always a challenger for me) and enjoying the experience. Meeting our users is always fun, discovering how people are using our products and the problems they’re having (the #aspnetpain hashtag is partly the result of this). I also got a chance to spend some ‘quality time’ with our 4.0 features as well as MVC. Oddly, I don’t get to spend as much time using our stuff as I’d like (my work day is packed with toommuch other stuff). Using our new WebForm Routing feature was a lot of fun; and gave me a bunch of ideas for making things easier (for example, currently it’s not great on DataBound controls; when Beta 1 rolls around I’ll post some extension methods here to make life easier). I also got to thinking a lot more about how people will advance their apps, maybe not to full MVC but a stepping stone to aid transition; using Routing, adopting an MVP pattern etc… Of course I also got tom play with the MVC framework; which is a really fun way to build apps (like many others I rolled my own MVC / MVP frameworks in the past). The new scaffolding functionality Phil and the team added in RC1 really moves this framework to a whole different level for beginners. Frankly if I were building an app nowadays, I’d use MVC… Anyway, now I’m back and catching up on work, always a lot to do… NOTE: The PPTs at the top of the post presentations from TechDays, if you want to use them for anything, feel free! (The speaker notes in the first one are kinda rough, please don’t take them as ‘the official word!’
Tuesday, March 03, 2009
I’m writing this from bed in my parent’s house in Scotland.The next 3 weeks has me doing one of my least favorite activities in the world; presenting, 6 times in 3 countries. Specifically I’m presenting at an MS conference called TechDays in Finland (this week), Belgium (next week) and Sweden (the week after). I really don’t like presenting and have forced myself to do these three conferences; presenting on ASP.NET 4.0 (including a look at VS10’s web dev features) and ASP.NET MVC (the very latest RC2). I am as usual extremely nervous…so we’ll see how it goes! As I write this I leave for the airport in 12 hours to fly to Helsinki, Finland…just discovered that I forgot some small items in the snowstorm at work last week; visible items which make me look bad to the team. This will keep me awake all night and play on my mind for weeks (welcome to the mind of the depressive!). I completed hundreds of individual tasks last week but these 4 bugs will impact my career more than any other…sucks! Anyway, going to try and get more sleep, next time I blog, I’ll be in Helsinki!
Saturday, February 07, 2009
Ah, the early nineties! Zeros and Ones, along with the video below it defined the early nineties for me (sorry aggregators you have to come to the site to see these :)). I’m 36 years old tomorrow (8th Feb) and the world has changed so much in the last 19 years. The time of these songs was right at the start of the internet, the early nineties. At the time I was barely even aware of the changes happening all around us. I remember using gopher and seeing the very first browser NCSA Mosaic at University .Though I had my first email account in 1990 I was never part of the BBS scene. Suddenly everything in the world was changing, the subject of the second song really reflected the times. The Berlin Wall was coming down, there was a new optimism starting to spread and it was the start of the amazing ride which was the nineties. Personally I was just moving out of my parent’s house, discovering life outside a tiny little village in the South of Scotland for the first time (and girls, drink and other umm…substances; I was a late starter). I started down the road of becoming a physicist, a path which ultimately I left. I was overwhelmed by the rate of change I encountered and just couldn’t keep up, this was also the year we discovered that my mother had Hodgkin’s disease, something which changed my live to a really drastic degree (and came close to destroying me and my family). So, I dropped out of University and my life almost came to a shuddering stop; I worked as a trolley boy in a local supermarket for the next few months… Eventually, thanks to the UK funding system at the time which gave you a 1 year ‘false start’ for University grants and the most supportive parents who I could ever have had, I went to another University (The University of Stirling). This was a remarkable place which let me figure out who and what I wanted to be. Part of this was the isolation, it’s a campus University completely separate from anything else and singularly remarkable for it. I started out studying Japanese, Psychology and Computing (not exactly focused!), I had great friends (very unusual for me, I’m a loner), even lived abroad for a year . I studied in Holland, a country to which I owe a lot of how I came to think of life, and even convinced me that much later on I could live in the states. I had some American friends at the student house of the University of Groningen where I studied Clinical Psychology (really, more by default than anything else…). My friends who I haven’t seen in years were amazing, intelligent people who’d experienced so much of life (particular shout out to Bryce Bedford, one of the smartest people I’ve *still* ever met). Anyway, that was the most remarkable 5 years of my life, from 1990-1995, and one which shaped me from a 17 year old country bumpkin to someone who loved learning and yearned to experience everything life had to offer. Now, I’m closer to 40 than 30 and realizing that I dropped out of life once again a few years ago. To borrow a line or two from an old pop song: “Right here, right now, there is no other place I want to be Right here, right now, watching the world wake up from history” We’re in the middle of an historic economic depression, the world is changing radically once more and oddly, I think it’s an exciting time! The last 19 years were an amazing time when the rate of change around the world accelerated, the internet came into being and the world became connected in a way it never has before. Right now, it feels like the world is waking up from the last couple of decades and deciding where we should all go next. We’re all being forced to think about what the next 20 years will be like, and, in part realizing that we can’t really know. I believe in the Technological Singularity, it’s as close to religion for me as I’m ever likely to come and I’ve come to realize that this belief will shape my life for the next few years. I think we’re already seeing the rise of the next phase of civilization. With the dawn social networking, the rise of ‘viral’ thinking in entities like Twitter, massive knowledge repositories like Wikipedia and the remarkable interconnectedness mapped by Google, I really believe that we’re on a road to something amazing. This ‘reset’ gives us the chance to decide where we want the world to go, our children are the smartest they have ever been and we’re finally veering away from the nightmare prophesied by Idiocracy. It’s an exciting time, and at the same time scary…but then revolution always is.
Thursday, January 29, 2009
One of the smaller additions we’ve made to ASP.NET 4.0 WebForms is the addition of two properties on the Page class, Page.MetaKeywords and Page.MetaDescription. What these two properties do is provide access to / create meta tags within your page as follows: <head runat="server"> <title>Untitled Page</title> <meta name="keywords" content="These, are, my, keywords' /> <meta name="description" content="This is the description of my page" /> </head> Simple, right? In fact these two properties work the same way as Page.Title works: 1. If there are no meta tags in the head element matching the property names e.g., name=”keywords” for Page.MetaKeywords and name=”description” for Page.MetaDescription then we create these tags and fill in the string you entered as the content. 2. If there are already meta tags with these names we provide get / set access to the existing tags… Of course you can set these in code, so the content could come from a DB to describe what you particular page is for etc… Oh, and you can also set these in the Page tag at the top of your WebForm as follows: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Metaeywords="Hello" MetaDescription="This is my description" %> This will override the meta tag contents (if any) already declared in the page. Note, that the description in these tags is only really used for improving your snippets on Google, Google and Live don’t use the Keywords for anything but other search engines may. A simple feature but it is pretty useful (and saves you messing about with adding these manually / writing your own code to create these.
Wednesday, January 28, 2009
ViewState is a pain…there, I’ve said it! What causes the most pain with ViewState is the fact that people misuse it a lot, leaving it enabled for every control in a page when in fact you only really need it for that one control which really needs to persist it’s contents to the next page. Dave Reed a developer on the ASP.NET team has written a truly epic post on ‘Truly Understanding ViewState’, and before you read what comes next, you really should head off and read that first… Back? Ok, well now you understand that you should only use ViewState for those controls that actually need it. But there’s a problem…what you’d really like to do is turn off ViewState for an entire page then just turn it on again for the controls that need it, sorry but I have some bad news, you can’t do this in any version of ASP.NET up to and including ASP.NET 3.5 SP1. I also have some good news…you will be able to do this when ASP.NET 4.0 comes out! The magic little property on all ASP.NET Controls which lets you do this is called ViewStateMode. (Incidentally, this was Dave’s idea and the samples below are a shameless rip-off from him :-)) Put very simply, ViewStateMode has 3 possible values, Enabled, Disabled and Inherit. Which are pretty obvious in their function…Enabled turns saving ViewState on for that control (or any child controls which are set as ‘Inherit’ or have nothing set), Disabled turns it off again, and Inherit says ‘use whatever my parent is set to’. That’s really all there is to it! Here’s a very simple sample of how this actually works: <form id="form1" runat="server"> <script runat=”server”> protected override void OnLoad(EventArgs e) { if (!IsPostBack) { label1.Text = label2.Text = "[DynamicValue]"; } base.OnLoad(e); } </script> <asp:PlaceHolder ID="PlaceHolder1" runat="server" ViewStateMode="Disabled"> Disabled: <asp:Label ID="label1" runat="server" Text="[DeclaredValue]" /><br /> <asp:PlaceHolder ID="PlaceHolder2" runat="server" ViewStateMode="Enabled"> Enabled: <asp:Label ID="label2" runat="server" Text="[DeclaredValue]" /> </asp:PlaceHolder> </asp:PlaceHolder> <hr /> <asp:button ID="Button1" runat="server" Text="Postback" /> As you can see in this sample we’re Disabling ViewState for the PlaceHolder1 control, the child label1 Inherits this property (as Inherit is default) and therefore saves no ViewState…In PlaceHolder2 we set ViewStateMode to ‘Enabled’, which leads to label2 inheriting this property and therefore saves ViewState…On the first page load we set both labels to the output the text [DynamicValue]… The effect of all this is that, when the Page first loads you see the labels rendered as follows: Disabled: [DynamicValue] Enabled: [DynamicValue] But, do a PostBack and you see: Disabled: [DeclaredValue] Enabled: [DynamicValue] As you’d now expect, the first label hasn’t preserved the value we set into ViewState…the second label has! What’s really cool is the fact that you can also do this: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" ViewStateMode="Disabled" %> Remember, in WebForms Page is just another control…it acts as the parent control for every other control in the page. This means that no control will save ViewState unless you set ViewStateMode to Enabled for that control or a control further up it’s control hierarchy (so, a great use would be for ContentPlaceHolders in MasterPages…). Told you it was simple!
|