Joel's profileJoel Holder's Salient Si...PhotosBlogListsMore ![]() | Help |
Joel Holder's Salient SignalA Problem/Solution Study of Software Architecture, Design Patterns, and Algorithms
|
|||||||||||||||||||||
|
Things that I intend to blog about.
|
July 25 JQuery Being Born Through The Eyes of Gource (Recipe For Win32)I think that a rendered historical view of a source controlled software project can be a key asset in understanding how a piece of software got to be what it is at any point in time. This seems to me to be a missing first class asset from our general tooling. Gource provides this as a beautifully rendered video timeline of a software project’s birth and evolution over time. Here I’ve provided a small sample showing John Resig’s initial commit activity in JQuery’s Git repository.
Most of the documentation and work with Gource appears to be targeted toward *nix. To the end spreading this love a bit wider into the Windows community, I’ll now share with you my recipe for getting it working in Win32. 1. First you’ll need to get Git working in Windows. For this I recommend downloading and installing the Full Official Version of MsysGit from its Google code page: http://code.google.com/p/msysgit/downloads/list. Make sure you install it with the “Git Bash Here” option (see the install options in the wizard). Also, you should allow the installer to put Git into your PATH environment variable, so that its globally accessible from the command-line. The default location of git.exe in Win32 is “C:\Program Files\Git\bin” 2. Next you’ll need Gource. You can get it from its Google code page: http://code.google.com/p/gource/. Download the binaries for Windows and place them into a directory at c:\gource. Manually add this path to your PATH environment variable so that it too is globally accessible. 3. Now in order to save the video output of Gource, you’ll need a video capture program. For this you can use the free ffmpeg, althrough I was not able to get the ppm output working in Windows to pipe it into ffmpeg. Thus, I opted for the shareware version of Fraps: http://www.fraps.com/download.php. Fraps can be used to create a video of any window, including in our case the Gource output. Also while I have not tried it myself, I suspect that Techsmith’s Camtasia and/or Jing might do the trick nicely also. 4. Next its time to get some source and make a vid. You can get the source of a Git project by cloning its repository to your local system.
Tada!… Pretty nifty tools. Gource works with Git, Mercurial, and SVN to date. I’ve seen a few ppl out there claiming to have it working with TFS also. See what kind of fun you can have with it.. Go forth and be fruitful.. April 19 ColdFusion and onMissingMethod – Tapping The Hidden PowerRecently I had to do some work in ColdFusion. Imagine my surprise and delight to find that CF components support a missing_method construct. It works like this. When you attempt a method call against a .cfc and the method does not exist, CF automatically calls a method called “onMissingMethod”, if it exists. With this, you get a chance to handle the call in onMissingMethod itself, redispatch the call to another method in the .cfc, or dispatch the the call to another component all together, such as a ServiceLocator that could resolve the message to some external responder. This is the tip of the meta programming carrot, and it tastes great. I use a base component from which I extend the other components in my app that takes care of this for me. My base.cfc has an onMissingMethod that looks for the presence of a well known named service locator component. If its there, it passes any method calls that it cannot resolve locally to a “forwardingContext” component. This allows us to externalize the logic for finding the resolver as well as opens up our ability to have it proxy objects that are proxied by other facades. Here’s the implementation of base.cfc: <cfcomponent> <cfset forwardContext=createObject("component", "responderLocator")><!--- MISSING_METHOD: pass the call to a service locator ---> <cffunction name="onMissingMethod" access="public" returnType="any" output="true" description="DELEGATE TO A FORWARDCONTEXT OBJECT"> <cfargument name="missingMethodName" type="string" required="true"> <cfargument name="missingMethodArguments" type="struct" required="true"> <cfset var local = {} /> <cfset local.returnValue = "" /> <cfif IsDefined("forwardContext")> <cfif StructKeyExists(forwardContext, arguments.missingMethodName)> <cfset local.meta = getMetadata(forwardContext[arguments.missingMethodName]) /> <cfset local.i = 1 /> <cfinvoke component="#forwardContext#" method="#arguments.missingMethodName#" returnvariable="local.returnValue"> <cfloop array="#arguments.missingMethodArguments#" index="local.arg"> <cfinvokeargument name="#local.meta.parameters[local.i++].name#" value="#local.arg#"> </cfloop> </cfinvoke> <cfelse> <cfinvoke component="#forwardContext#" method="#Arguments.missingMethodName#" argumentcollection="#Arguments.missingMethodArguments#" returnvariable="local.returnValue" /> </cfif> <cfelse> <cfset arguments.missingMethodArguments.calledMethodName = Arguments.missingMethodName /> <cfdump var="#arguments.missingMethodArguments#" expand="yes" label="MISSING METHOD ARGUMENTS" /> </cfif> <cfreturn local.returnValue /> <cfif NOT StructKeyExists(local,"returnValue")> <cfset local.returnValue = "" /> </cfif> <cfreturn local.returnValue /> </cffunction> </cfcomponent> A simple responderLocator.cfc could look like this (note that in this case I’ve put the method being proxied directly on to it, so it does not have to pass the call elsewhere): <cfcomponent> <cffunction name="onMissingMethod" access="public" returnType="any" output="false"> <cfargument name="missingMethodName" type="string" required="true"> <cfargument name="missingMethodArguments" type="struct" required="true"> <cfset tmpReturn = ""> <cfset functionToCallName = Arguments.missingMethodName> <cfset arguments.missingMethodArguments.calledMethodName = Arguments.missingMethodName> <cfscript> dump(missingMethodArguments, true); </cfscript> <cfreturn tmpReturn> </cffunction> <cffunction name="DontExist" access="public" returnType="any" output="false"> <cfargument name="data" required="true"> <!--- ECHO BACK FOR EXAMPLE ---> <cfreturn "You sent me: " & data> </cffunction> <cffunction name="dump"> <cfargument name="data" required="true"> <cfargument name="bAbort" required="false" default="0"> <cfdump var="#arguments.data#"> <cfif arguments.bAbort eq 1> <cfabort> </cfif> <cfreturn true> </cffunction> </cfcomponent> Given the above implementation of the responderLocator, I can expect to call a method called “DontExist” on base.cfc or one extended from it that does not have the method, it would pass the call to reponderLocator and expect it to respond. Base.cfc would then pass the response back to the caller. Here’s an example: <cfscript> myCfc = createObject("component","base"); <!--- or some component derived from base.cfc ---> myVal = myCfc.dontExist("foo"); </cfscript> In this case, myVal will equal “You sent me: foo”, which demonstrates that the call to DontExist was delegated to responderLocator, invoked, and returned to this calling code. This is a fairly contrived example just to show the pattern off in its simplest form. Beefier implementations might use an Array of responseLocators and loop through them trying to resolve the call. Additionally, note that if all the components participating in the pattern are extended from base.cfc, and each has its own spools of locators, you can see how the call resolution attempts could spread across potentially hundreds of components until a responder was located. Pretty nifty feature.. Enjoy.. March 18 Fat-Free Templating with Barebones JavascriptToday I’m going to demonstrate a straight-forward and very effective technique for markup templating. I’ve borrowed the supplant prototype function from Douglas Crockford’s - And Then There Was Javascript presentation, in order to show you how to inject values from custom data structures directly into into strings. We’ll be doing a minimal implementation here in order to show the great outcomes you can get without requiring buy in to one of the many over engineered frameworks that do this. First we extend the prototype of the built in String type. String.prototype.supplant = function(o) { return this.replace(/{([^{}]*)}/g, function(a, b) { var r = o[b]; return typeof r === 'string' || typeof r === 'number' ? r : a; } ); }; With this minimalist formatter in place we are able to do string manipulation like this: var result = "{a}-{b}-{c}".supplant({"a": "Foo", "b": "Bar", "c": "Baz" }); The resulting string would be: "Foo-Bar-Baz". As you can see, we’ve essentially got a string formatting extension to all objects of type String here. Where this becomes immediately useful to us in our web pages, is for injecting objects directly into markup strings, which can then be programmatically added to the DOM. This technique is shown below in only 4 lines (statements) of Javascript. <div id="container" />
<script type="text/javascript">
var template = '<table border="{border}">' + '<tr><th>Last</th><td>{last}</td></tr>' + '<tr><th>First</th><td>{first}</td></tr>' + '</table>';
var data = { "first": "Carl", "last": "Hollywood", "border": 2 };
var container = document.getElementById("container"); container.innerHTML = template.supplant(data);
</script>
As you can see, we start with a template String and a JSON data Object. Then we get our target DOM node and simply set its .innerHTML property to the output of the template running its own .supplant() function against the data object. Very sparse, no heavy framework.. Its just the bare bones Javascript and a helper function. Here’s an example of using this technique to set the .src property of an <img> DOM node. Assuming I have an image file that I want to be targetable based on some script logic, it looks like this. The code that I need to target this file at runtime looks like this: <img id="logo" src="" /> <script type="text/javascript">
var param = { domain: 'memecannon.com', media: '/Content/ninja' }; var url = "{media}.jpg".supplant(param);
var logo = document.getElementById("logo"); logo.setAttribute("src", url);
</script>
The output looks like this: Using this approach, its quite easy to do targeting of custom skin artifacts and layout structures. Other uses of this approach are to programmatically inject variables into CSS strings and runtime script includes. As you can see, the basics for screen templating are exposed near the surface of DOM scripting with Javascript. That said, I do typically use JQuery plugins for this type of screen composition. I’ve surveyed most of the JQuery plugins in this space. I recommend JSRepeater for ease of use and support for automatic iterating over collections and dealing with heirarchal data structures. For an extremely unobtrusive templating solution, you can’t beat NoTemplate. It provides a level of externalized purity, in that you don’t need to hack replacement ${targets} into your markup at all; rather, it uses selectors to reach down into your markup for mapping data to screen targets. Hopefully, in this article I’ve informed, if not convinced you that you have the power to blend your data and markup in whatever ways you need purely with Javascript and with a minimum of effort. In a successive post, I’ll show you how to interact this approach with Ajax Services to bring your screens to life. Enjoy.. March 14 Implementing missing_method with C# dynamic - Part 2In my previous post, Implementing missing_method with C# dynamic - Part 1, I demonstrated a simple approach to plugging a missing_method call routing seam into a DynamicObject. Here I'll take it a step further to implement a generic missing_method function capable of passing any call to a forwarding context object. Note that I do not have to define the missing_method in the calling code; its now automatically setup for me in DynamicObject itself. [TestMethod] public void Can_Forward_Through_Default_Missing_Method() { dynamic dispatcher1 = new ExpandableDispatcher(); dynamic dispatcher2 = new ExpandableDispatcher(); dynamic dispatcher3 = new ExpandableDispatcher();
//configure forwarding dispatcher1.ForwardContext = dispatcher2; dispatcher2.ForwardContext = dispatcher3;
//set responder on dispatcher3 dispatcher3.Methods["RunMeta"] = new Func<string, string>(param => { return "Meta said " + param; });
//try to execute the responder from dispatcher1 var response = dispatcher1.RunMeta("I am a probe..");
Assert.IsTrue(response == "Meta said I am a probe.."); }
The implementation of ExpandableDispatcher contains a DynamicObject reference called ForwardContext. This handle is for forwarding messages that cannot be responded to by the this in the current execution context. Note that in the ctor, the dispatcher sets up its own, missing_method. public class ExpandableDispatcher : DynamicObject { DynamicObject forwardContext;
public DynamicObject ForwardContext { get { return forwardContext; } set { forwardContext = value; } } IDictionary<string, object> _methods = new Dictionary<string, object>();
public IDictionary<string, object> Methods { get { return _methods; } set { _methods = value; } }
public ExpandableDispatcher() {
//setup default missing_method this._methods["missing_method"] = new Func<DynamicObject, InvokeMemberBinder, object[], object>((contextObject, binder, args) => {
dynamic context = contextObject;
var method = context.Methods.ContainsKey(binder.Name) ? context.Methods[binder.Name] : null; var missing_method = context.Methods.ContainsKey("missing_method") ? context.Methods["missing_method"] : null;
if (method != null) { if (method.ToString().StartsWith("System.Action")) { RunAction(method, args); } else { return RunFunc(method, args); } } else if (missing_method != null) { return missing_method(context.ForwardContext, binder, args); }
return null; }); }
private object RunFunc(dynamic method, object[] args) { switch (args.Length) { case 0: return method(); case 1: return method(args[0]); case 2: return method(args[0], args[1]); case 3: return method(args[0], args[1], args[2]); case 4: return method(args[0], args[1], args[2], args[3]); case 5: return method(args[0], args[1], args[2], args[3], args[4]); default: throw new ArgumentException("No support for more than 5 arguments currently"); } }
private void RunAction(dynamic method, object[] args) { switch (args.Length) { case 0: method(); break; case 1: method(args[0]); break; case 2: method(args[0], args[1]); break; case 3: method(args[0], args[1], args[2]); break; case 4: method(args[0], args[1], args[2], args[3]); break; case 5: method(args[0], args[1], args[2], args[3], args[4]); break; default: throw new ArgumentException("No support for more than 5 arguments currently"); } }
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (_methods.ContainsKey(binder.Name) && _methods[binder.Name] is Delegate) { result = (_methods[binder.Name] as Delegate).DynamicInvoke(args); return true; } else if (_methods.ContainsKey("missing_method") && _methods["missing_method"] is Delegate) { var missing_method = _methods["missing_method"] as Delegate; var ctxParam = missing_method.Method.GetParameters().Where(p => p.Position == 0 && p.ParameterType == typeof(DynamicObject)).FirstOrDefault(); if (ctxParam != null && forwardContext != null) { dynamic context = forwardContext; result = missing_method.DynamicInvoke(context, binder, args); return true; } else { result = missing_method.DynamicInvoke(binder, args); return true; } } else { return base.TryInvokeMember(binder, args, out result); } } }
For further reading on the topics I discussed and demonstrated in this series, this MSDN article shows a clever approach to creating MethodBags by passing lambda Expressions to a DynamicObject, which are then compiled into Delegates and assigned to to the dynamic itself. In successive posts, I'll be exploring the new extensions to the System.Linq.Expressions.Expression API in .NET 4.0. Enjoy.. March 11 Implementing missing_method with C# dynamic - Part 1One of the neat things about Ruby is its missing_method fallback capability. Using the C# 4.0 DynamicObject, we’re also able to control dispatch at runtime. I wanted to see how the missing_method idiom might work with a C# dynamic dispatcher, so I wrote up a small sample. [TestMethod] public void Can_Control_Dynamic_Dispatch() { dynamic dispatcher1 = new ExpandableDispatcher();
dynamic dispatcher2 = new ExpandableDispatcher();
dispatcher1.Methods["missing_method"] = new Func<string, object>(param => { return expandable2.RunMeta(param); });
dispatcher2.Methods["RunMeta"] = new Func<string, string>(param => { return "Meta said " + param; });
var response = dispatcher1.RunMeta("I am a probe..");
Assert.IsTrue(response == "Meta said I am a probe.."); }
Note that an attempt to execute RunMeta is made on dispatcher1, but RunMeta is not defined on dispatcher1; its on dispatcher2. The call gets routed by missing_method to dispatcher2 for execution. In this simple case, the call is passed from a Delegate in dispatcher1 to one in dispatcher2 via a closure reference set in the calling code. A more elaborate message passing implementation, however, will allow missing_method to forward the call to a ServiceLocator or back up the call stack until it finds a method with a matching name and signature.
Here’s the simplest implementation of ExpandableDispatcher. public class ExpandableDispatcher : DynamicObject {
IDictionary<string, object> _methods = new Dictionary<string, object>(); public IDictionary <string, object> Methods { get { return _methods; } set { _methods = value; } }
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (_methods.ContainsKey(binder.Name) && (_methods[binder.Name] is Delegate) { result = (_methods[binder.Name] as Delegate).DynamicInvoke(args); return true; } else if (_methods.ContainsKey("missing_method") && _methods["missing_method"] is Delegate) { result = (_methods["missing_method"] as Delegate).DynamicInvoke(args); return true; } else { return base.TryInvokeMember(binder, args, out result); } } }
As you can see, dynamic dispatch in C# 4.0 gives us the power to define method calling routes with an emphasis on runtime behavioral composition. This enables a new family patterns in C# that leverage dynamic message routing using runtime assignment of Delegates to DynamicObject facades. For more reading on this topic, see the follow up post, Implementing missing_method with C# dynamic - Part 2. Enjoy.. January 19 A Small Javascript To Safely Load Scripts and Styles Into The DOMConsider scenarios where you have pages composed of parts that are combined at runtime. If the many authors of those parts have external script references sprinkeled throughout, there is the chance that author2 might over write author1's reference of JQuery or some other script dependency. The consequences of doing this are usually breakage. I wrote this little script to allow all authors to call their scripts into page parts safely, ensuring that a script or stylesheet reference never overwrites another. This script assumes that URI is sufficient to uniquely identify a script. It does not attempt to deal with the situation where 2 developers might both load the same library from different URIs.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript">
/** * Function object to carry DOM management behaviors */ function bootStrapper() {
var head = document.getElementsByTagName("head")[0];
/** * Loads an external javascript source by creating a <script> tag * and injecting it into the DOM. * @param {string} src Url of the content uri * @param {string} opt_id An optional id for the <script> tag */ this.addScriptReference = function (url, opt_id) { if (!isLoaded(head, 'script', 'src', url)) { var script = document.createElement('script'); if (opt_id) script.id = opt_id; script.type = 'text/javascript'; script.src = url; head.appendChild(script); } };
/** * Loads an external style source by creating a <link> tag * and injecting it into the DOM. * @param {string} src Url of the content uri * @param {string} opt_id An optional id for the <link> tag */ this.addStyleReference = function (url, opt_id) { if (!isLoaded(head, 'link', 'href', url)) { var style = document.createElement('link'); if (opt_id) style.id = opt_id; style.href = url; style.rel = "stylesheet"; style.type = "text/css"; head.appendChild(style); } };
/** * Tests to see if tag with matching key and value properties is * present in container * @param {DOM Node} container dom element to search * @param {string} name of the tag to be searched for * @param {string} name of the attribute of belonging to the tag * @param {string} value of the attribute of belonging to the tag */ function isLoaded(container, tagName, propKey, propValue) { var elems = container.getElementsByTagName(tagName);
var alreadyLoaded = false; for (var i = 0, elem; elem = elems[i]; i++) { if (elem[propKey] == propValue) { alreadyLoaded = true; break; } } return alreadyLoaded; }; }
</script>
<script type="text/javascript">
var boot = new bootStrapper();
//redundant script boot.addScriptReference('http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js', "jQuery1"); boot.addScriptReference('http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js', "jQuery2");
//redundant style boot.addStyleReference('http://www.codeweblog.com/template/andy/css/style.css', "Style1"); boot.addStyleReference('http://www.codeweblog.com/template/andy/css/style.css', "Style2");
</script>
</head> <body>
<p>Example Usage...</p>
</body> </html>
The result of the above usage is that only 1 instance of the Javascript and only 1 instance of the stylesheet is loaded. December 21 Property Copying With Dynamic Objects in .NET 4.0Lately, I've been trying out some of the new .NET 4.0 language features. Specifically, I've been looking into ways to trivially combine late dispatch and late binding in order to build general purpose convenience objects. In this case, I wanted an expando object that I could program against with some of the techniques we use in javascript to programmatically build up a graph's shape with runtime logic. The built in System.Dynamic.ExpandoObject was not sufficient for this purpose, in that it does not provide a mechanism for setting property names at runtime via string, not to mention its sealed. In this post I'll quickly show you how to leverage C# dynamic to copy and replicate the shape and values of a POCO at runtime. By exposing a subclassed DynamicObject's properties publicly via a Dictionary<string, object>, we can do things like this:
[TestMethod] public void Can_Set_Get_Properties() { dynamic expandable = new ExpandableObject();
expandable.Properties.Add("foo", "good stuff");
Assert.AreEqual(expandable.foo, expandable.Properties["foo"]);
expandable.bar = "yummy";
Assert.AreEqual(expandable.bar, expandable.Properties["bar"]); } With a few helper methods this capability becomes more useful. See here:
[TestMethod()] public void Expandable_Object_Can_Copy_Properties_From_To() { dynamic expandable = new ExpandableObject();
var testObj = new TestObject { ID = 1, Description = "ASDFASDF", Name = "GGGG", UnitPrice = 6 };
expandable.CopyPropertiesFrom(testObj, null);
Assert.AreEqual(expandable.Description, testObj.Description);
var testObj2 = new TestObject();
expandable.CopyPropertiesTo(testObj2, null);
Assert.AreEqual(testObj, testObj2);
}
Here is the implementation of ExpandableObject.
public class ExpandableObject : DynamicObject { Dictionary<string, object> _properties = new Dictionary<string, object>();
public Dictionary<string, object> Properties { get { return _properties; } set { _properties = value; } }
public override bool TrySetMember(SetMemberBinder binder, object value) { _properties[binder.Name] = value; return true; }
public override bool TryGetMember(GetMemberBinder binder, out object result) { return _properties.TryGetValue(binder.Name, out result); }
public void CopyPropertiesFrom(object source, List<string> ignoreList) { ignoreList = ignoreList ?? new List<string>();
foreach (var property in source.GetType().GetProperties().Where(p => p.CanRead)) { var key = property.Name; if (!ignoreList.Contains(key)) { var value = property.GetValue(source, null); this.Properties[key] = value; } } }
public void CopyPropertiesTo(object destination, List<string> ignoreList) { ignoreList = ignoreList ?? new List<string>();
var destProps = destination.GetType().GetProperties().ToList(); this.Properties.Keys.ToList().ForEach(key => {
if (!ignoreList.Contains(key)) { var value = this.Properties[key];
var property = destProps.Where(p => p.CanWrite && p.Name == key && p.PropertyType == value.GetType()).FirstOrDefault(); if (property != null) { property.SetValue(destination, value, null); } } }); } }
There is no spoon...
Enjoy... June 14 Using JQuery With WebForms ControlsThe scenario: We have an ASP.NET CheckListBox from which we want to ensure at least one option has been selected before allowing the user to click a button. Sounds simple enough, but we're going to use JQuery to do all of this work on the client.
ASP.NET MARKUP: <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="FileDownloadUserControl.ascx.cs" Inherits="FileDownloadUserControl" %> <asp:CheckBoxList ID="chklstFilesAvailable" runat="server"> <asp:Button style="display: none" ID="btnDownload" runat="server" Text="Download" OnClick="btnDownload_Click" />
Simple and straightforward.. Now lets have a look at the HTML that this code results in. With three items bound to our CheckListBox, we get this:
RENDERED HTML MARKUP: <table id="ctl0_FileDownloadUserControl1_chklstFilesAvailable" border="0"> <tr><td> <input id="ctl0_FileDownloadUserControl1_chklstFilesAvailable_0" type="checkbox" name="ctl0$FileDownloadUserControl1$chklstFilesAvailable$0" /> <label for="ctl0_FileDownloadUserControl1_chklstFilesAvailable_0">BLAH1_20090608.csv</label> </td></tr> <tr><td> <input id="ctl0_FileDownloadUserControl1_chklstFilesAvailable_1" type="checkbox" name="ctl0$FileDownloadUserControl1$chklstFilesAvailable$1" /> <label for="ctl0_FileDownloadUserControl1_chklstFilesAvailable_1">BLAH2_20090608.csv</label> </td></tr> <tr><td> <input id="ctl0_FileDownloadUserControl1_chklstFilesAvailable_2" type="checkbox" name="ctl0$FileDownloadUserControl1$chklstFilesAvailable$2" /> <label for="ctl0_FileDownloadUserControl1_chklstFilesAvailable_2">BLAH1_20090608.csv</label> </td></tr> </table> <input type="submit" name="btnDownload" value="Download" id="btnDownload" />
WebForms has created a table, labels, and checkboxes. Note that the IDs have been changed from the original names we gave them. We have effectively lost control of our ID naming at this point. This makes it difficult to use JavaScript to target these elements after they've been rendered. Here is a workaround to target the CheckListBox and the Button post render. First we get the mangled IDs and use those values in JQuery selectors to create JQuery objects. Then we pass them as parameters to the wireButtonShowToCheckListBox function in order to wire the click event of each checkbox to a nested anonymous function, which checks to see if at least one of the boxes are checked and, if so, shows the button.
JAVASCRIPT: <script type="text/javascript" language="javascript">
$(document).ready(function() { var id = "<%= chklstFilesAvailable.ClientID %>"; var chklist = $("#" + id);
var btnId = "<%= btnDownload.ClientID %>"; var button = $("#" + btnId);
//reusable show/hide button wiring wireButtonShowToCheckListBox(chklist, button); });
function wireButtonShowToCheckListBox(chklist, button) { chklist.find('input:checkbox').each(function() { var cb = $(this); cb.click(function() {
var hit = false; chklist.find('input:checkbox').each(function() { var checked = $(this).attr('checked'); if (checked) hit = true; });
if (hit == true) { button.show(); } else { button.hide(); } }); }); }
</script>
Thats it.. With this approach we can pass any CheckListBox and any Button to this function and achieve a very fast pure client-side solution to control when and if the user can click the button. The same basic approach can be used to target and wire other rendered WebForms controls. Enjoy.. April 14 A Straight Forward ASP.NET AJAX Solution With MS Ajax, Restful WCF Services, and A Dash Of JQuery
One of the main advantages of JSON is that, like XML, it can represent an object graph of any shape. JSON allows for the natural structure of entity Types to be expressed in the form of nested and aggregated object literals. The simplicity and small footprint of this format has made it a popular choice for data interchange with the dynamic languages crowd, where JSON can be parsed into native objects with basic language constructs. This is what makes it most appealing for JavaScript developers. This article will demonstrate a straight forward way of doing Ajax with WCF Rest, MS Ajax, Object Bakery, and JQuery. 1. Create a new WebForms project. 2. Drag in a ScriptManager to Default.aspx's form element. 3. Add JQuery-1.3.2.min.js to the project. 4. Drag JQuery-1.3.2.min.js into the head section. 5. Add an empty <p /> to the form. 6. Switch to design view. 7. Drag in an HTML input button and double click it. Your markup should now look something like this: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="OBWeb._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script src="js/jquery-1.3.2.min.js" type="text/javascript"></script> <script type="text/javascript"> function Button1_onclick() { } </script> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server" /> <input id="Button1" type="button" value="submit" onclick="return Button1_onclick()" /> <p /> </div> </form> </body> </html>
Notice that we have a stubbed out JavaScript function wired to our button. 8. Add a reference to ObjectBakery.dll, available here. 9. Add a new “Ajax-enabled WCF Service” to your project. 10. Name it Service1. 11. Create a sample POCO that we can use for the demo. Here’s mine: public class TestObject { public int ID { get; set; } public string Description { get; set; } }
12. Change the stubbed out DoWork method in Service1 to look like this. [WebGet] [OperationContract] public string DoWork(int id) { //fab some data objects List<TestObject> obs = new List<TestObject>(); obs.Add(new TestObject { ID = id, Description = "ASDFASDF" }); obs.Add(new TestObject { ID = id + 1, Description = "QWER" }); obs.Add(new TestObject { ID = id + 2, Description = "DFFG" }); obs.Add(new TestObject { ID = id + 3, Description = "ZXCV" }); //magic var helper = new ObjectBakery.Helpers.SerializationHelper(); var txtO = helper.JsonSerialize(obs.ToArray()); return txtO; }
13. In the page designer right mouse click your ScriptManager and go to properties. 14. In the property designer click the ellipsis next to Services 15. Add a Service, setting the Path property to the url of Service1, i.e. “/Service1.svc”. 16. Now lets write some JavaScript. <script type="text/javascript"> function Button1_onclick() { var service = new Service1(); service.DoWork(3, onSuccess, null, null); } function onSuccess(result) { var objects = eval("(" + result + ")"); for (var i in objects) { $("p").append(objects[i].Description + "<br />"); } } </script> What we did here is fill out our Button1_onclick with code that news up a Service1 reference and invokes its DoWork. The first argument will be passed as the id parameter to our WebMethod. The second argument is a callback, in our case the onSuccess method. Notice the signature of onSuccess takes a result param. This will be our JSON string returned from DoWork. Note that we can parse the result into a native JavaScript object graph with a single line of code. var objects = eval("(" + result + ")");
This is pretty cool. We trust our own service, so there’s no issue with using eval, instead of parseJSON(). In our for loop, we use JQuery to add new DOM elements holding the Description property from each of our objects. $("p").append(objects[i].Description + "<br />"); That’s the basic story. What we’ve been able to do here is enable the basic async data exchange paradigm between an ASPX page and data enabled Rest services. There are some fun bits here when you couple this approach with ORM frameworks, such as Entity Framework or NHibernate. We now have the capability to move objects between JavaScript and C# seamlessly. Object Bakery has helper methods to move between JSON and CLR graphs with a single line of code. I have found this approach helpful when I need to share objects between client and server, and I don’t want to worry about the details of data interchange. Note that Object Bakery also works with Anonymous Types, which allows us to project out objects containing only the data our front-end needs and thus keeps our graphs and their isomorphic JSON strings as small as possible. Your users will appreciate every bit of responsivity you can give them. You can download the code for this project here. Enjoy.. January 25 Get Control Of Your Enterprise Library Logging Application Block LogsEnterprise Library’s Logger is powerful and convenient. It's default TextFormatter provides a suitable human readable format, and it also allows you to implement custom formatters to suite your needs. I think that being able to work with log data without having to manually parse strings, can really simplify the development of tools for mining logs. To this end, I whipped up a small WinForms app that demonstrates how to take log entries originating from a text spool, convert them into objects, and query them with Linq. The primary goal was to provide simple UI that enables log search, filter, and find, with sorting, grouping, etc. First off, the heavy lifting in this sample is provided by the EntLib Contrib LogParser. Their FilteredLogReader class drives a tokenizer, which creates and populates LogEntry objects out of log text. The tokenizer will use any formatting template, in our case, I’ve used the built in “Text Formatter”. When it executes, it generates a RegularExpression capable of matching and trapping the values involved in populating our collection of LogEntry objects. Ultimately, we are provided with a simple API for driving all of this. Here’s a brief sample: string configFile = "abc.config"; string traceLog = "trace.log";
FilteredLogFileReader reader = new FilteredLogFileReader(traceLog, configFile, new TimeStampParser(CultureInfo.CurrentCulture));
Filter filter = new Filter { StartTime = DateTime.Now.AddHours(-1), StopTime = DateTime.Now };
IEnumerable<LogEntry> entries = reader.ParseLogFile(filter);
The basic idiom here is: 1. Get a FilteredLogFileReader
2. Setup a Filter
3. Feed the filter to the reader’s ParseLogFile
To facilitate a flexible set of filtering concepts at the UI, I decided to use a simple FilterExpression class to represent a set of keys, values, and match operators. Here’s a quick way to get the intersection result set from applying a set of filters with a single pass through the logs.
private IList<LogEntry> FilterLogEntries(List<LogEntry> lst, List<FilterExpression> exprs) { var matchAllExprs = new List<LogEntry>(); List<PropertyInfo> props = typeof(LogEntry).GetProperties().ToList(); lst.ForEach(entry => { int matchCount = 0; exprs.ForEach(expr => { string value = props.Find(prop => prop.Name.Equals(expr.Key)) .GetValue(entry, null).ToString(); switch (expr.Operator.ToLower()) { case "contains": if (value.Contains(expr.Value)) matchCount++; break; case "!contains": if (!value.Contains(expr.Value)) matchCount++; break; case "regex match": if (new Regex(expr.Value).IsMatch(value)) matchCount++; break; case "==": if (expr.Value.Equals(value)) matchCount++; break; case "!=": if (!expr.Value.Equals(value)) matchCount++; break; default: throw new Exception("operator not found"); } }); if (exprs.Count == matchCount) matchAllExprs.Add(entry); }); return matchAllExprs; }
If any of the LogEntries match all of the expressions, then those are returned to the caller. Now we can place the log data onto our screen for users.
this.dataGridView1.DataSource = new BindingList<LogEntry>(FilterLogEntries(lst, lbxFilterExpressions.Items.Cast<FilterExpression>().ToList()));
The sample app that I’ve included, gives users the ability to create, configure, and apply FilterExpressions on the fly. Here’s a screenshot.
You can get the source for this sample here. Enjoy..
|
||||||||||||||||||||
|
There are no categories in use.
Thanks for visiting!
![]() To add a comment, sign in with your Windows Live ID.
|
|||||||||||||||||||||
|
|