schuirink.net
main destinations: home | the web & the world | out of here
Google

news headlines

News headlines collected from 498 newsfeeds.

weblogs.asp.net main feed

url: http://weblogs.asp.net

SnowCovered.com finally gets an upgrade


When we (DNNCorp) originally took ownership of SnowCovered.com from Brice Snow it was running on DotNetNuke 2.1.2 (Release date was June 14, 2004), v1.0 of the .NET Framework and on a low-end hosting provider. Looking back, I recall that we were lucky to get 80% to 90% uptime in any given month. It was on its last legs. We knew, the community knew, we HAD to take action.

Fast forward a little while, and just over a year ago we launched an internal ?Upgrade? project for SnowCovered.com. Here is how that story played out?

Our first initiative was to upgrade from the low-end hosting to our own infrastructure which brought up our stability immediately to 99.9% uptime. This was a huge gain for us and the community as a whole. It was nice to be able to rely on solid infrastructure. All of the negativity around the amount of downtime simply fizzled.

Our first significant win!

The next step was to tackle the DNN 2.1.2 issue. For those of you that don?t recall the history of DNN, 2.1.2 was release before the ASP.NET Team release the (then) new Personalization and Membership pieces. The team here spent hours ensuring that every single user in the SnowCovered membership database was upgraded. Countless tests and dry runs were performed, and in the end we prevailed. All users can and will be upgraded fully.

Another win for the team.

The largest task was the actual code base itself. There was a significant amount of rework and simply replacing much of the codebase (VB.NET to C#) as we transitioned it all to the 6.x family of the core framework. At that time we had just hired Nathan Rover and put him on this challenge. He gladly accepted and took the lead. For those of you want have not yet met Nathan, he is a marathon runner ? just the man suited for this marathon upgrade and release schedule.

Over the year they took inventory of the feature points, upgraded code, reviewed artwork & designs, cried/yelled/screamed, held design meetings, drank a few gallons of redbull/monster/beaver buzz, QA, upgrade meetings, daily scrums, Unit Tests, more QA, frequent vendor reviews & demos. Project plans were scraped, created and scraped again. We have had churn in almost every aspect of the project.

The team kept running.

There were a few points over the past year were we almost gave up. The stress was high; the light at the end of the tunnel was just barely visible. Nathan and the team pushed on. We persisted.

The team kept running.

Finally the light was blinding. We invited many (~100) of the vendors in for a private Beta; received well over 40 issues, all of which were addressed with great care and attention, and finally accepted the final list of bugs in for the initial RTM release. The team worked day and night nailing these issues down in order to meet our aggressive release schedule, without sacrificing the level of quality we demanded in this release.

As of 3:00AM PST February 9th, 2012 we crossed the finish line!

We are proud to announce that we have released initial public release of "DotNetNuke, The Store"

Congrats goes out to the entire team!

Here is a before screenshot, as of July 19, 2012:

 

image

 

and now?

 

image

 

 

..we are just getting started?



CSS3 Tips, Tricks, and Resources


Do you need to take your CSS3 skills to the next level? The following resources are monitored and kept current. Bookmark these links!



jQuery Tip #2 - Manipulating the DOM in a Loop


One of jQuery's greatest strengths is its ability to manipulate the DOM with a minimal amount of code. It's so easy to change things that we often don't think about what's happening under the covers. For example, consider the following code that appends nodes to an object named parentDiv.


var parentDiv = $('#emailList');
for (var i = 0; i < 100; i++) {
    parentDiv.append('
' + i + '
'
); }

This is one of the more common ways to append nodes into a given parent. Although it works fine, it's not optimal because jQuery has to constantly perform DOM operations in the loop. You may not notice a problem when looping through 100 items, but as that number increases to a larger number the performance can start to degrade. A more efficient (yet simple) approach from a performance standpoint is shown next:


var parentDiv = $('#emailList');
var divs = '';
for (var i = 0; i < 100; i++) {
  divs += '
' + i + '
'
; } parentDiv.html(divs);

The previous code only touches the DOM once after the loop has completed resulting in better overall performance. While a lot of string concatenation is occurring, it turns out that going that route is much more efficient than calling append() multiple times.

Keep in mind that you can always manipulate the DOM directly as well and increase performance even more in some cases by using things like document.createDocumentFragment() and other techniques. While I prefer to use jQuery functions whenever possible, sometimes it's necessary to step outside of jQuery?s API for a given task and use the native DOM API instead.

A test page available at http://jsperf.com/strings-vs-array-join-vs-doc-fragment/8 shows different techniques that can be used for updating the DOM while using a loop as well as their individual performance results. An example of the test results and the different techniques tested are shown next. You?ll notice that calling jQuery?s append() function in a loop doesn?t fare too well compared to some of the other more ?native? DOM options.

Test Ops/sec
Strings
var str = '';
var c = '
  • ' + d.first + ' ' + d.second + '
  • '

    for (var i = 0; i < 100; i++) {
      str += c;
    }
    document.getElementById('list').innerHTML = str;
    1,226±0.84%61% slower
    Array (using push)
    var arr = [];
    var c = '
  • ' + d.first + ' ' + d.second + '
  • '

    for (var i = 0; i < 100; i++) {
      arr.push(c);
    }
    document.getElementById('list').innerHTML = arr.join('');
    1,234±0.35%61% slower
    Doc Fragment
    var doc = document.createDocumentFragment();
    var c = d.first + ' ' + d.second;

    for (var i = 0; i < 100; i++) {
      var e = document.createElement("li");
      e.textContent = c;
      doc.appendChild(e);
    }
    document.getElementById('list').appendChild(doc);
    3,020±3.24%fastest
    Array (using index)
    var arr = [];
    var c = '
  • ' + d.first + ' ' + d.second + '
  • '

    for (var i = 0; i < 100; i++) {
      arr[i] = c;
    }
    document.getElementById('list').innerHTML = arr.join('');
    1,223±0.46%61% slower
    Array join then string concat
    var arr = [];
    var c = d.first + ' ' + d.second;

    for (var i = 0; i < 100; i++) {
      arr[i] = c;
    }
    document.getElementById('list').innerHTML = '
  • ' + arr.join('
  • ') + '
  • '
    ;
    1,252±0.37%60% slower
    DOM List
    var list = document.getElementById('list');
    var c = d.first + ' ' + d.second;

    for (var i = 0; i < 100; i++) {
      var e = document.createElement("li");
      e.textContent = c;
      list.appendChild(e);
    }
    3,144±0.00%fastest
    jquery
    var $list = $("#list");
    var c = '
  • ' + d.first + ' ' + d.second + '
  • '
    ;
    for (var i = 0; i < 100; i++) {
      $list.append(c);
    }
    428±9.06%86% slower
    doc fragment 2
    var doc = document.createDocumentFragment();
    var c = d.first + ' ' + d.second;
    var e = document.createElement("li");
    e.textContent = c;
    for (var i = 0; i < 100; i++) {
      doc.appendChild(e.cloneNode(true));
    }
    document.getElementById('list').appendChild(doc);
    3,241±11.16%fastest


    Conclusion

    As Peter Parker?s Uncle said in Spider-Man, ?With great power comes great responsibility?. The same can be said about the power jQuery provides to developers. Take time to research sections of code that you know are critical for the performance of your application. Just because you can use a particular function doesn?t mean you should. Check out http://james.padolsey.com/jquery/ if you?d like an easy way to see what jQuery functions are doing behind the scenes.

     

     

     

     

     

    If you?re interested in learning more about jQuery or JavaScript check out my jQuery Fundamentals or Structuring JavaScript Code courses from Pluralsight. We also offer onsite/online training options as well at http://www.thewahlingroup.com.



    jQuery Tip #1 ? Defining a Context When Using Selectors


    I really enjoy working with jQuery and have had the opportunity to use it a lot over the years on various projects.  It?s an essential part of my ?technology tool belt?. My company has also started providing new training classes on jQuery to various companies and I?ve had a lot of great questions come up about best practices, tips and tricks, when certain functions should be used over other functions, and more.

    I decided to put together a series of posts that highlight simple tips and tricks that I?ve used in jQuery applications over the years to provide some guidance to developers new to jQuery and provide answers to different questions I?ve been asked. In this first post I?ll cover an oldie but goodie tip ? defining a context when using selectors. It?s something I struggled with when I first started using jQuery but once I found the secret (which is quite easy) it changed how I used selectors and how I used CSS classes in my HTML.

    Defining a Context when using Selectors

    Selectors are an essential part of jQuery that provide a great way to locate nodes in the DOM quickly and easily. For example, the following selector can be used to find all div elements with a class of panel on them in a page:

    var panelDivs = $('div.panel'); 


    Selectors are well documented at http://api.jquery.com/category/selectors so I won't rehash what they are or how to use them here. However, I do want to mention one simple yet powerful tip that many people new to jQuery will find useful. It's one of the tips that I've ended up using over and over as I build applications. To demonstrate the technique let?s walk through a simple example.

    Selectors can be used in the following way to find all elements with a class of panel on them:


    var panels = $('.panel');


    However, what if you want to grab all elements with a panel class that are children of a given parent container element? To better understand the scenario, consider the following HTML:

    <div id="emailContainer">
        <div class="panel">
            ?
    div> <div class="panel"> ?
    div> div> <div id="ordersContainer"> <div class="panel"> ?
    div> <div class="panel"> ?
    div> div>


    Assume that you already have a reference to the emailContainer div stored in a variable named emailDiv:

    var emailDiv = $('#emailContainer');


    How do you find all div elements with a panel class on them within the emailDiv element as opposed to finding every one within the page? One technique is to use the jQuery find() function:

    var panels = emailDiv.find('div.panel');


    You can alternatively supply a context object to the selector as shown next. This is the technique I normally use mainly because it?s something I?ve used for quite awhile (and old habits are hard to break).

    var panels = $('div.panel', emailDiv);


    Which way is the best? It depends on who you talk to although you can check out some live performance tests at http://jsperf.com/jquery-find-vs-context-sel that infer that find() has a slight performance advantage. Regardless, knowing how to supply a context when using selectors is a great technique to understand and useful in many different scenarios.

    My development team has been using this technique a lot in an application that has several panels with a consistent set of CSS classes defined on elements within each panel. We?re using a plugin that we need to dynamically re-size as the panel changes size and created a function called refreshPanel() that accepts the panel to refresh. The object passed in to the function is used as the context for several selectors (it?s a jQuery object) to identify a starting point for searching elements with specific CSS classes. Here?s a section of a JavaScript file that defines refreshPanel().

     

    ...
    refreshPanel = function (panel) { var padding = 5; //Handle height if (panelHeight == null) { toolbarH = $('.toolBar', panel).height(); tableHeadH = $('.clientDataTable', panel).height(); maxHeight = $(window).height(); panelHeight = (maxHeight / panels.length) - toolbarH - (tableHeadH * 2); } //Grab panel's current height var h = panel.height() - toolbarH - (tableHeadH * 2); //If panel's height is greater than the original panelHeight assigned in "if" above then they're maximizing var newHeight = (h > panelHeight) ? maxHeight - toolbarH - (tableHeadH * 2) : panelHeight; var w = panel.width(); $('div.dataTables_scrollBody', panel).height(newHeight - padding).width(w); $('.dataTables_scrollHeadInner, table.clientDataTable', panel).width(w); }
    ...


    Notice that the panel object is used throughout as the selector context which allows the function to be re-used over and over for multiple panels. We use this technique in several places which allows us to consistently use the same CSS classes over and over within a given panel.


    Conclusion

    This is the first tip in a series I?ll be writing about jQuery. Although this tip is simple (and hopefully something you already know about if you?re an experienced jQuery developer), it?s very useful in many real-world situations.

     

     
    If you?re interested in learning more about jQuery or JavaScript check out my jQuery Fundamentals or Structuring JavaScript Code courses from Pluralsight. We also offer onsite/online training options as well at http://www.thewahlingroup.com.


    Wrapping up Configuration Manager


    Many developers use the ConfigurationManager class to retrieve settings from the .Config file of your application. This class allows you to retrieve settings from the element. With just a single line of code as shown in the following line:

    file = ConfigurationManager.AppSettings["CustomerFile"];

    The above code assumes you have the following setting declared in your App.Config or Web.Config file for your application:


     
                 value="D:\Samples\Customers.xml" />
     

    This works great except in the case where you misspell the key name in your line of code, or you forget to add the element in the element in your .Config file. In either case, the value you get back will be a null value. Sometimes these null values can wreak havoc on your code because you forgot to check the value for null and the attempt to perform some operation on that value such as opening the file name. For example, the following code would cause a ArgumentNullException to be raised because the key value is misspelled.

    string value = string.Empty;
    
    value = ConfigurationManager.AppSettings["CustomerFil"];
    
    System.IO.File.OpenText(value);
    

    To fix the above code you need to test the ?value? variable to see it is null prior to attempting to open the file as shown in the following code:

    string value = string.Empty;
    
    value = ConfigurationManager.AppSettings["TheValue"];
    
    if (value == null)
      MessageBox.Show("Can't open the file");
    else
      System.IO.File.OpenText(value);
    
    MessageBox.Show(value.ToString());
    

    Another issue with the above code is if you wish to change the location of where you are storing your application settings, you have to modify every line of code throughout your whole application where you used ConfigurationManager.AppSettings. For example, sometime in the future you might want to store your settings in an XML file located on a central server, or maybe store them in a database. This would be a lot of extra work due to you probably have a lot of places where you are retrieving configuration settings. Instead let?s wrap up the calls to the ConfigurationManager.AppSettings method in your own class.

    Wrap Up Configuration Manager

    A best practice that I have employed for over twenty years has been to wrap up any method calls that could potentially change in the future. Configuration settings, database access calls, registry settings, WCF Services, and similar types of classes are all candidates for wrapping up. Creating your own class allows you to change the methods for getting data from another source without you having to change the code in your application.

    For the configuration settings shown previously, creating a class to wrap up the call to ConfigurationManager.AppSettings is a very simple task. Below is the complete class that will allow you to get a string and an integer value from a configuration file.

    public class AppConfig
    {
      public string XmlPath { get; set; }
      public int DefaultType { get; set; }
    
      // Wrapper around ConfigurationManager.AppSettings call
      protected string GetSetting(string key)
      {
        return ConfigurationManager.AppSettings[key];
      }
    
      public string GetSetting(string key, string defaultValue)
      {
        string value;
    
        value = GetSetting(key);
        if (value == null)
          value = defaultValue;
    
        return value;
      }
    
      public int GetSetting(string key, int defaultValue)
      {
        int ret;
        string value;
    
        value = GetSetting(key);
        if (value == null)
          ret = defaultValue;
        else
          ret = Convert.ToInt32(value);
    
        return ret;
      }
    }
    

    Notice how each of the public GetSetting methods call a protected method named GetSetting where the call to ConfigurationManager.AppSettings is made. Now, if you want to store your configuration settings in a database table, you only need to change the protected GetSetting method. All of the rest of the methods in this class and the calls you make in your application to the GetSetting methods in this class do not need to change at all.

    In addition to wrapping up the call to the ConfigurationManager.AppSettings method call, you can add on additional functionality as well. For instance you can allow the programmer to pass in a default value to return if the value is not found in your settings storage location.

    You call the GetSetting methods in AppConfig class using the following code:

    AppConfig config = new AppConfig();
    
    config.XmlPath = config.GetSetting("XmlPath", @"C:\");
    
    MessageBox.Show(config.XmlPath);
    

    In the above code if the ?XmlPath? key was not found, then C:\ would be returned. The same call can be used for the overloaded version of GetSetting that returns an integer value. You pass in an integer value that you wish to have returned if the key ?DefaultType? is not found.

    AppConfig config = new AppConfig();
    
    config.DefaultType = config.GetSetting("DefaultType", 1);
    
    MessageBox.Show(config.DefaultType.ToString());
    

    Summary

    Wrapping up calls to .NET classes and their methods can give you more flexibility in the future if you wish to change the implementation of a method. In addition it allows you to add on functionality that is not present in the original calls. Wrapping up methods like this also protects you from changes that Microsoft might introduce in future versions of the .NET Framework. If they obsolete a class or method, you now only need to change your code in just place. The rest of your application does not need to change. In other words, you are helping to future-proof your code.

    NOTE: You can download this article and the sample code that goes with this blog entry at my website. http://www.pdsa.com/downloads. Select ?Tips and Tricks?, then ?Wrapping up Configuration Manager? from the drop down list.

    Good Luck with your Coding,
    Paul Sheriff

    ** SPECIAL OFFER FOR MY BLOG READERS **
    We frequently offer a FREE gift for readers of my blog. Visit http://www.pdsa.com/Event/Blog for your FREE gift!



    AgileSight, my new venture


    Another important milestone in my career started three years ago when I joined Tellago. I convinced my friend Jesus to hire me, and I would eventually move to the United States with my family to work in the company.  That never occurred for some personal things, but I fortunately had a chance to create an excellent team of very talented people in Argentina. I started myself working remotely from Argentina, and the things went so well for the company that we end up hiring more than 15 great architects down here in Argentina.  Creating this team was a very interesting and completely new challenge in my career.

    I also got involved in a lot of interesting projects, and what is more important, I had a chance to work and met great people, which is what it really worth it.

    However, a month ago, I decided it was about time to start a personal project with a good friend of mine, and that?s how AgileSight was born. Although the initial conception of the company was to create software products, we will also offering software consulting and development services. I am definitely very excited to be part of this new venture and face all new kind of challenges ahead.

    My first assignment in AgileSight couldn?t be better as I will be working in the next Web Mobile Guidance project (Liike project) with the Microsoft Patterns & Practices team.



    Upcoming ASP.NET, HTML5 and Windows 8 Talks and Workshops at DevConnections 2012


    It?s that time of the year again! DevConnections 2012 is right around the corner and we have a lot of great new workshops and sessions planned for the ASP.NET and Client-Dev (HTML5, Windows 8 Metro, JavaScript, jQuery, etc.) tracks.

    The keynote at DevConnections will be given by the one and only Scott Guthrie who?s always up to something cool and is a lot of fun to listen to.

    image

    Several other great speakers will be at the conference as well. Here?s a quick sampling:

    image

    And of course there are many more! Sessions are only part of a conference experience. Yeah there are the parties too ? but the real value is in the hallway conversations and the people you meet and continue to talk to beyond the conference event. DevConnections is a great place to strike up these conversations as its one of the largest gatherings of super experts in our industry ? and they are all very approachable. There will also be a panel discussions with many of us, which is a great chance to put us on the spot.

    ?Thanks to John for letting me hijack that last paragraph along with the pictures he put on his blog. I thought he nailed what DevConnections is all about in just a few sentences.

    I?ll be co-presenting a full day jQuery workshop with John on Monday, March 26th. We really enjoy co-presenting and have a lot of great jQuery topics to discuss. You do have to register for the workshops so make sure to do that when you register for the conference.

    image

    Workshop

    APR02: Building Ajax-Enabled Applications with jQuery (9:00AM - 4:00PM)
    Add'l Fee $399.00
    John Papa & Dan Wahlin

    Building cross-browser AJAX applications can be a fun yet challenging proposition. In this workshop, you?ll learn how to put the joy back into AJAX development using the jQuery script library. Learn how jQuery selectors can reduce code and simplify the process of finding DOM elements, how chaining can be used to accomplish multiple tasks with a single line of code and how cross-browser AJAX calls can be made using built-in jQuery functionality. Other topics covered include using client-side templates as well as built-in plugins. If you?ve wanted to learn jQuery but haven?t made the time, this is the workshop for you since we?ll take you from the ground floor all the way to the top.

     

    Sessions

    I?m also presenting or co-presenting 4 other sessions. If you?ll be at the conference please stop by and introduce yourself.

    image

    CWI202: Fundamentals of Windows 8 HTML/JavaScript Metro Style Apps
    Dan Wahlin

    Heard the buzz about Windows 8 Metro style applications but don?t know where to start? In this session, you?ll learn how your existing HTML, JavaScript, and CSS skills can be used to build Windows 8 Metro applications. Learn about different Visual Studio 11 project templates, built-in scripts, and Windows Runtime (WinRT) calls that you can make to build robust applications that will be capable of targeting millions of Windows users on the desktop and on tablets in the near future.

    CHT201: Developing an HTML5/jQuery Application - End to End
    Dan Wahlin

    HTML5 is all the rage these days but where do you look to find robust examples of using it along with jQuery, jQuery templates, Ajax calls, data access technologies, and more? In this session, Dan Wahlin will walk through an application called Account at a Glance that demonstrates how key HTML5 technologies can be integrated and used to present data to users in different ways. Topics covered include exposing data to the client using Entity Framework Code First and RESTful services, using jQuery templates to render data, JavaScript techniques for structuring code, the role of HTML5 semantic tags, as well as how technologies such as the canvas, SVG, and video can be used. If you want to learn HTML5 techniques and strategies from a real app, then this session is for you.

    CJS301: Techniques, Strategies, and Patterns for Structuring JavaScript Code
    John Papa & Dan Wahlin

    Are you writing a lot of JavaScript code and trying to apply good practices to make it more maintainable and flexible? In this session, learn about common practices to make avoid problems such as polluting the global namespace and code that executes inconsistently, by using patterns such as separation and the Revealing Module Pattern. You will leave this session with several tips on how to write clean, more maintainable, and consistent code.



    If you can?t make the conference make sure to check out my new Structuring JavaScript Code course at Pluralsight.com. If you?re still writing ?function spaghetti code? I?ll show you several techniques/patterns that will really improve the re-use and maintainability of your code.Good stuff to know if you?re building Web applications.


    CJS202: Templating and Data Binding for HTML5 Applications with JsRender and JsViews
    John Papa & Dan Wahlin

    Interested in using the latest JavaScript libraries for templating and data binding your HTML5 app? In this session you will learn how to simplify JavaScript development using JsRender and JsViews (the replacements for jQuery templates). Learn how JsRender brings a new templating library to HTML5 development that has a code-less tag syntax, high performance, no dependency on jQuery nor a DOM, supports creating custom functions, and uses pure string-based rendering. You will also see how JsViews compliments JsRender by providing data binding features through observability of changes that work with plain old JavaScript objects (POJO).



    Developing custom field type for SharePoint 2010


    The ECM features of SharePoint 2010 are excellent. The best thing is the extendibility of the platform using SharePoint designer and Visual Studio. By default SharePoint includes field types such as Single line text, multi-line text etc. The following is the snapshot of the fields available in SharePoint 2010

    clip_image001

    The out of the box field controls are useful for most situations, but still you may need additional field types when building business applications. Fortunately you can build custom field controls using Visual Studio 2010 and deploy to SharePoint. Custom field control can provide a unique editing and display experience. Custom field controls can be used to include custom client-side validation and a unique editing experience.

    In this article I am going to demonstrate how you can build custom field control for SharePoint 2010. For the purpose of this article, I am going to build a custom field control that inherits the single line of text. When displaying the control, it will show the entered text in

    tags. Also the field will convert the text to upper case while displaying. The custom field will throw error if it contains more than 10 characters. The examples looks simple, my intention is to show you how easily you can build custom controls. In real time you can add your business logic to build your own field controls.

    Open Visual Studio, from the file menu select new project. In the new project dialog, select empty SharePoint project as the template. I have named the project as ?MyCustomFieldProject?

    clip_image003

    In the SharePoint customization wizard, make sure you select ?deploy as farm solution?. Also you can enter the local site URL where you can deploy the custom field.

    clip_image004

    Click the Finish button. Visual Studio will create an empty project. In Visual Studio solution explorer, the project looks as follows.

    clip_image005

    Now you need to add a class to the solution that represents the custom field. Now right click the project and select Add -> Class

    clip_image007

    I have named the class as ?MyHeaderField?

    clip_image009

    The class just created have the following code.

    clip_image010

    Now you need to set the class as public. You need to include the using statement to include Microsoft.SharePoint to get access to the Sharepoint built in types. As I mentioned initially MyHeaderField needs to inherit the SharePoint Text field which is ?SPFieldText?. Now you need to override the 2 default construtors. After making the mentioned changes, the code file looks as follows.

    clip_image012

    Now I am going to override 2 methods as follows.

    • GetValidatedString ? This will be used to validate the data before saving to database.
    • GetFieldValueAsHtml ? this is how the value will be displayed in the page.

    The implementation of the methods are as follows.

    clip_image013

    Each field type you define should have a corresponding entry in the form of XML file in the XML folder under 14\Template folder. The XML file will have the following naming convention

    • It should start with fldtypes_
    • After _ you can define the field name. It is a good practice to specify the class name as the field name, though it is not a must
    • In the XML file, you need to define the fields such as TypeName, parent type name, type display name, corresponding class and assembly.

    Now in the Visual Studio project, you need to add a XML file and while deploying it should be deployed to the folder 14\Template\XML. To do this, you can add a mapped folder to your project. Right click the project in solution explorer, select Add -> SharePoint mapped folder

    clip_image015

    In the Add mapped folder dialog, expand Template and select XML

    clip_image016

    Now you can see the XML folder under your project. Any files created here will be deployed to the 14\Template\XML folder in the SharePoint farm where the solution is deployed. Now you need to add the XML file for your field.

    Right click the project, select Add -> New Item. Now in the template selection, select Data as the category and then select XML as the file type. I have named fldtypes_MyHeaderField.xml

    clip_image018

    I have updated the content of the XML file as follows. Be noted that $SharePoint.Project.AssemblyFullName$ will be replaced automatically by the correct assembly name wile deploying. All the other fields are self-explanatory.

    clip_image020

    Now we are done with all the requirements. From the solution explorer, right click the project and click deploy.

    clip_image021

    Once deployed, you can add new columns to SharePoint lists and libraries with the type ?MyHeaderField?. Add the new column to SharePoint types will show the new type you added.

    clip_image022

    I have added a new column named ?MyHeader? to my publishing page content type. Now when I try to add text to the column, it will validate it. See a simple validation

    clip_image023

    SharePoint 2010 is extensible as you can build and deploy custom fields that address your custom business requirements.



    Changing a Control?s Output Dynamically


    A question that pops up occasionally in ASP.NET forums or sites like Stack Overflow is how to change a control?s output dynamically. Well, since the CSS Friendly Control Adapters came out, I knew how to do it statically, through static registration on a .browser file (it can also be done dynamically, for all controls of a certain type, by adding a fully qualified type name to HttpContext.Current.Request.Browsers.Adapters collection), but I decided to go and try to do it dynamically, without registration. More specifically, I wanted to achieve two things:

    • Apply a XSL transformation to the generated output, provided it is XML-compliant;
    • Change the output through code in an event handler-like fashion.

    So, here?s what I came up with:

       1: <%@ Register Assembly="WebApplication1" Namespace="WebApplication1" TagPrefix="web" %>
       2: ...
       3: <web:OutputAdapterControl runat="server" TargetControlID="table" OnOutput="OnOutput" XslPath="~/table.xsl" />
       4:  
       5: <asp:Table runat="server" ID="table"/>

    I have two control declarations, one is for a well known asp:Table, and the other is for my OutputAdapterControl. On its declaration, I have a property that references the asp:Table through its id, one property for an external XSL transformation file and the final one for registering an event handler.

    The code for the OutputAdapterControl is:

       1: namespace WebApplication1
       2: {
       3:     [NonVisualControl]
       4:     public class OutputAdapterControl : Control
       5:     {
       6:         private static readonly FieldInfo occasionalFieldsField = typeof(Control).GetField("_occasionalFields", BindingFlags.NonPublic | BindingFlags.Instance);
       7:         private static readonly FieldInfo flagsField = typeof(Control).GetField("flags", BindingFlags.NonPublic | BindingFlags.Instance);
       8:  
       9:         public OutputAdapterControl()
      10:         {
      11:             this.Enabled = true;
      12:         }
      13:  
      14:         public String XslPath
      15:         {
      16:             get;
      17:             set;
      18:         }
      19:  
      20:         public String TargetControlID
      21:         {
      22:             get;
      23:             set;
      24:         }
      25:  
      26:         public Boolean Enabled
      27:         {
      28:             get;
      29:             set;
      30:         }
      31:  
      32:         public event EventHandler Output;
      33:  
      34:         private ControlAdapter getControlAdapter(Control control)
      35:         {
      36:             Object flags = flagsField.GetValue(control);
      37:             MethodInfo setMethod = flags.GetType().GetMethod("Set", BindingFlags.NonPublic | BindingFlags.Instance);
      38:             setMethod.Invoke(flags, new Object[] { 0x8000 });
      39:  
      40:             Object occasionalFields = occasionalFieldsField.GetValue(control);
      41:             FieldInfo rareFieldsField = occasionalFields.GetType().GetField("RareFields");
      42:             Object rareFields = rareFieldsField.GetValue(occasionalFields);
      43:  
      44:             if (rareFields == null)
      45:             {
      46:                 rareFields = FormatterServices.GetUninitializedObject(rareFieldsField.FieldType);
      47:                 rareFieldsField.SetValue(occasionalFields, rareFields);
      48:             }
      49:  
      50:             FieldInfo adapterField = rareFields.GetType().GetField("Adapter");
      51:             ControlAdapter adapter = adapterField.GetValue(rareFields) as ControlAdapter;
      52:  
      53:             return (adapter);
      54:         }
      55:  
      56:         private void setControlAdapter(Control control, ControlAdapter controlAdapter)
      57:         {
      58:             Object occasionalFields = occasionalFieldsField.GetValue(control);
      59:             FieldInfo rareFieldsField = occasionalFields.GetType().GetField("RareFields");
      60:             Object rareFields = rareFieldsField.GetValue(occasionalFields);
      61:             FieldInfo adapterField = rareFields.GetType().GetField("Adapter");
      62:             adapterField.SetValue(rareFields, controlAdapter);
      63:         }
      64:  
      65:         internal void RaiseOutputEvent(OutputEventArgs e)
      66:         {
      67:             if (this.Output != null)
      68:             {
      69:                 this.Output(this, e);
      70:             }
      71:         }
      72:  
      73:         protected override void OnPreRender(EventArgs e)
      74:         {
      75:             if ((this.Enabled == true) && (String.IsNullOrWhiteSpace(this.TargetControlID) == false))
      76:             {
      77:                 Control control = this.FindControl(this.TargetControlID);
      78:                 ControlAdapter controlAdapter = this.getControlAdapter(control);
      79:                 OutputAdapterControlAdapter newAdapter = new OutputAdapterControlAdapter(this, controlAdapter, control, this.XslPath);
      80:  
      81:                 this.setControlAdapter(control, newAdapter);
      82:             }
      83:  
      84:             base.OnPreRender(e);
      85:         }
      86:     }
      87: }

    And the code for the control adapter is:

       1: namespace WebApplication1
       2: {
       3:     public class OutputAdapterControlAdapter : ControlAdapter
       4:     {
       5:         private static readonly FieldInfo controlField = typeof(ControlAdapter).GetField("_control", BindingFlags.NonPublic | BindingFlags.Instance);
       6:         private static readonly MethodInfo controlRenderMethod = typeof(Control).GetMethod("Render", BindingFlags.NonPublic | BindingFlags.Instance);
       7:         private static readonly MethodInfo controlAdapterRenderMethod = typeof(ControlAdapter).GetMethod("Render", BindingFlags.NonPublic | BindingFlags.Instance);
       8:  
       9:         public OutputAdapterControlAdapter(OutputAdapterControl outputControl, ControlAdapter original, Control control, String xslPath)
      10:         {
      11:             this.OutputControl = outputControl;
      12:             this.Original = original;
      13:             this.XslPath = xslPath;
      14:             controlField.SetValue(this, control);
      15:         }
      16:  
      17:         protected OutputAdapterControl OutputControl
      18:         {
      19:             get;
      20:             private set;
      21:         }
      22:  
      23:         protected String XslPath
      24:         {
      25:             get;
      26:             private set;
      27:         }
      28:  
      29:         protected ControlAdapter Original
      30:         {
      31:             get;
      32:             private set;
      33:         }
      34:  
      35:         protected override void Render(HtmlTextWriter writer)
      36:         {
      37:             StringBuilder builder = new StringBuilder();
      38:             HtmlTextWriter tempWriter = new HtmlTextWriter(new StringWriter(builder));
      39:  
      40:             if (this.Original != null)
      41:             {
      42:                 controlAdapterRenderMethod.Invoke(this.Original, new Object[] { tempWriter });
      43:             }
      44:             else
      45:             {
      46:                 controlRenderMethod.Invoke(this.Control, new Object[] { tempWriter });
      47:             }
      48:  
      49:             if (String.IsNullOrWhiteSpace(this.XslPath) == false)
      50:             {
      51:                 String path = HttpContext.Current.Server.MapPath(this.XslPath);
      52:  
      53:                 XmlDocument xml = new XmlDocument();
      54:                 xml.LoadXml(builder.ToString());
      55:  
      56:                 builder.Clear();
      57:  
      58:                 XslCompiledTransform xsl = new XslCompiledTransform();
      59:                 xsl.Load(path);
      60:                 xsl.Transform(xml, null, tempWriter);
      61:             }
      62:  
      63:             OutputEventArgs e = new OutputEventArgs() { Html = builder.ToString() };
      64:  
      65:             this.OutputControl.RaiseOutputEvent(e);
      66:  
      67:             if (e.Html != builder.ToString())
      68:             {
      69:                 builder.Clear();
      70:                 builder.Append(e.Html);
      71:             }
      72:  
      73:             writer.Write(builder.ToString());
      74:         }
      75:     }
      76: }

    Finally there?s the event argument:

       1: namespace WebApplication1
       2: {
       3:     [Serializable]
       4:     public class OutputEventArgs : EventArgs
       5:     {
       6:         public String Html
       7:         {
       8:             get;
       9:             set;
      10:         }
      11:     }
      12: }

    And a sample event handler:

       1: protected void OnOutput(object sender, OutputEventArgs e)
       2: {
       3:     e.Html = e.Html.ToUpper();
       4: }

    Please note that this relies heavily on reflection, and, for that matter, will only work with ASP.NET 4.0. It can be made to work with ASP.NET 2/3.5, but you will have to change the code to get to the stored control adapter in the Control class. Thanks to Joćo Angelo for reminding me of that!

    If you look at it carefully, you will see that, if an XSL transformation file is specified, the control adapter will use it to transform the output of the target control. Either way, if an event handler is registered, it will raise the event, passing in the event argument the HTML generated by the target control, possibly modified by the XSL transformation. In the event handler, all you have to do is change the Html property of the event argument.

    Let me know if this helps in some way, and have fun!



    Scaling ASP.NET websites from thousands to millions?LIDNUG


    Here?s the recent presentation made on LIDNUG on scaling ASP.NET websites from thousands to millions of users.

    Scaling ASP.NET websites from thousands to millions of users by Omar AL Zabir

    Here?re the slides.