Home | Blog | Screencasts | Projects
# Saturday, June 13, 2009

By default SharePoint will use impersonation, so the web.config file will have the setting:

<identity impersonate="true" />

 

This means that if you try and connect to a SQL server database from say a custom web part, the connection will appear to SQL server as the user that requested the page. This is very useful if you care about the actual user, so for example if the database has permissions set based on this assumption.

However it is often useful to have just the application pool account connect to the SQL Server database or you may wish to give the application pool account permissions to connect to active directory but not every user.

It this case you will need to impersonate the application pool account. There is a bit of code floating around the web that uses P/Invoke to call ReverToSelf() from the advapi32.dll. It turns out it’s simpler than that:

 

using (HostingEnvironment.Impersonate()) 
{
 
    // access external resource as app pool account
 
}
 

 

The HostingEnvironment.Impersonate() will do the same thing that as a call to RevertToSelf().

 

If you like me and organise your data access calls into nice methods that perform a discrete action such as:

public static void DeleteUser(int userID)
{
 
//do some database access
 
}

 

You might be really loathed to wrap all these methods with the same code like:

public static void DeleteUser(int userID)
{
 
   using(HostingEnvironment.Impersonate())
   {
      //do some database access
   }
}

 

Instead you might like to take a look at what a project such as PostSharp can offer:

 

"You can make your own custom attributes that will really add new behaviors to your code! This is sometimes called aspect-oriented programming (AOP) or policy injection."

 

This means that you can define your own attribute that will have code that will run on any invocation of your code:

public class ImpersonateAttribute : OnMethodInvocationAspect
{
    public override void OnInvocation(MethodInvocationEventArgs eventArgs)
    {
        using(HostingEnvironment.Impersonate())
        {
            eventArgs.Proceed();
        }
    }
}

 

So our code then looks like this:

[ImpersonateAttribute]
public static void DeleteUser(int userID)
{
   //do some database access    
}

 

Much easier to maintain. Now every bit of code with this attribute will run as the application pool account.

Saturday, June 13, 2009 11:16:09 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | Sharepoint

I came across an interesting problem recently deploying an ASP.NET application to the ISV directory in CRM 4, the ASP.NET application was working fine using the Cassini web server inside of Visual Studio, but when it was deployed to the ISV directory on the CRM server it failed to run.

Basically any postback caused the web forms to loose all it’s current data, so things like dropdown lists would end up blank.

It was pretty obvious that this was caused by ViewState being turned off.

The default web.config file of CRM looks like:

 

<pages buffer="true" enableSessionState="false" enableViewState="false" validateRequest="false"/>

 

Since the ISV directory inherits from this web.config file, all the applications in the ISV directory will also have viewstate disabled.

 

So just add the following line to your web.config file in the ISV directory:

 

<pages enableViewState="true"/>

 

Personally I don’t really see the need to enable viewstate, in most cases it isn’t needed if the programmer understands it’s purpose, but sometimes your just trying to deploy code that someone else wrote.

Saturday, June 13, 2009 10:46:28 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
CRM | setup
# Saturday, June 06, 2009

A lot of functionality can be added to CRM with JavaScript, if your like me and do the bulk of your normal web development JavaScript with a toolkit like JQuery, you will quickly miss the power and ease of doing things without it. The good news is that it is pretty easy to load JQuery into the page:

 

image

 

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/ISV/jquery.js';
script.onreadystatechange = function()
{
if(this.readyState == "complete" || this.readyState == "loaded")
   setupPage();
};
 
var head = document.getElementsByTagName('head')[0];
 
head.appendChild(script);

 

Now you can use the setupPage() function with all your familiar JQuery goodness.

Saturday, June 06, 2009 9:12:47 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | CRM | JQuery
# Sunday, April 26, 2009

We needed the basic ability to redirect specific users to a different page. I first took a look at Adam Buenz excellent Redirection web part but we thought that it might be a little bit complex for our content authors to use, so I put together a really simple web part that gives the user the ability to select the users or groups via the standard SharePoint people picker interface.

 

image

 

The idea is that you can select a number of groups or users to apply the redirection to, this could be as broad as all authenticated users or refined to a single user. Of course you need a way edit the web part, so another people picker dialog allows the user to select users or groups that this redirect does not apply to. If they match the exclusion, the web part will present them with the text ‘This account was excluded from redirection to <url>’. This exclusion group is normally the content authors.

 

image

 

It is possible to put a number of these web parts on the same page, so if for example you wanted to redirect to a number of different sites based on AD group or user accounts, this should work fine.

 

First step, as always is to add the solution to SharePoint, once the solution is deployed, be sure to activate the ‘Redirector web part’ feature at the site level:

 

image

 

Once that is done, it’s just a matter of selecting the ‘Redirect web part’ from the web part gallery:

 

image

 

Hopefully you’ll find some use for it. The source code can be downloaded here and the WSP can be found here.

 

The code for this web part is fairly straight forward, I have a tool part that uses the SharePoint  PeopleEditor control. This control has a property to return the selection of groups or accounts as a comma separated string. With this string I can then do a search to see if the current user is in the group like:

        private bool IsUserIn(string commaSeperatedList)
        {
            bool isMatch = false;

            if (commaSeperatedList != string.Empty)
            {
                foreach (string group in commaSeperatedList.Split(','))
                {
                    if (Page.User.IsInRole(group) || Page.User.Identity.Name == group)
                    {
                        isMatch = true;
                    }
                }
            }
            return isMatch;
        }

 

So the web parts main logic looks like:

 

            bool redirectMatch = false;
            bool exclusionMatch = false;

            redirectMatch = IsUserIn(selectedADVal);
            exclusionMatch = IsUserIn(excludeADVal);
                   

            if (redirectMatch == true && exclusionMatch == false)
            {
                Page.Response.Redirect( redirectUrl);
            }
            else
           
{
                if (redirectMatch)
                {
                    outputCtrl.Text = "This account was excluded from redirection to " + redirectUrl;
                }
            }

Sunday, April 26, 2009 12:23:41 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | MOSS | Sharepoint
# Saturday, April 25, 2009

CRM 4 has lots of excellent customization options, one of which is the auto-numbering of entities:

 

image

 

The Settings – Administration section allows you to setup some options for auto-numbering your entities, you can choose the prefix, and the suffix length:

 

image

 

This is pretty powerful, but how would approach this problem if you needed your entities to have a more complex numbering scheme? Being a developer, everything looks like a development task to me. So I would start with a plugin that implements the IPlugin interface:

 

       public class AutoNumber : IPlugin

 

Then we need to implement the Execute method such as:

 

        public void Execute(IPluginExecutionContext context)
        {
           
            if (context.InputParameters.Properties.Contains("Target"))
            {
                DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties["Target"];

                if (entity != null)
                {
                    if (!entity.Properties.Contains("AttributeName"))
                    {
                        entity.Properties.Add(new StringProperty("AttributeName", GetAutoNumber()));

                    }
                }
            }
        }

 

So the above code will check to make sure our input properties contains the ‘Target’ parameter, this will be the entity that is affected by the plugin. From here we can cast it to a DynamicEntity  which will make it easier to work with. Once we have an instance of DynamicEntity the whole task of updating and attributes becomes trivial, the above code just creates a new property and calls some method that will generate the complex AutoNumber.

 

Once the plugin has been developed, the next important step is to register the plugin. The best tool for the job is probably the CRM Plugin Registration Tool:

 

PlugIn_thumb

 

The first step is to register the plugin assembly:

 

image

 

This will prompt you with a form that allows you to select the assembly and the location it will be stored i.e. Database, file system or GAC.

 

Once the assembly has been registered we can register a ‘new step’, this is done from the same menu as the assembly registration.

You will be prompted to fill out the following form:

 

image

 

For our plugin we will choose the following options:

Message: Create  (since we are doing an auto number, it makes sense to only run the plugin at the create stage)

Primary Entity:  Your choice

Plugin: Your assembly that was loaded at the previous step

Post Stage: we want to run it after the entity has been created, otherwise the built in CRM auto numbering will overwrite our value.

Synchronous: lets fire the event synchronously, no reason not to, the end user will be presented with a consistent interface this way.

Step Deployment: your choice

Triggering Pipeline: Parent

 

As you can see its all fairly simple and straight forward, now you can have an auto number that be what ever you need, it might be simple like some combination of a financial year or complex where a lookup to some external system needs to take place, but at the end of the day at least there is a nice mechanism in place to help you out.

Saturday, April 25, 2009 6:50:35 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
code | CRM

By default when you edit the tool part settings of a SharePoint web part and you click on the ‘…’ if the property is a string, you will get the default property builder:

 

image

 

Clicking the above ‘…’ will launch the default string property builder:

Standard SharePoint Property Text Builder

 

This popup is actually the ‘zoombldr.aspx’ page located in the _layouts virtual directory.

 

There are a few problems with this builder, well it’s not so much the builder, the string property’s of the web part are serialised in a manner that strips out carriage returns. So if you were to spend time formatting the contents of this dialog, it will get wiped out when you save the contents. This is most apparent when you edit the XSLT of the core search results web part.

 

How can you override this property?

 

The key is to make use of the HtmlDesigner attribute on your web part property. There are two ways to make use of the attribute, the first way is to explicitly define the page which you want to load (i.e. change ‘url to page’):

 

        [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
        System.Web.UI.WebControls.WebParts.WebDisplayName("Template"),
        System.Web.UI.WebControls.WebParts.WebDescription(""),
        System.Web.UI.WebControls.WebParts.Personalizable(
        System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
        System.ComponentModel.Category("Settings"),
        System.ComponentModel.DefaultValue("")
        ]
        [HtmlDesignerAttribute("url to page", DialogFeatures = "dialogHeight:500px;dialogWidth:650px;help:no;status:no;resizable:yes")]
        public string CustomProp
        {
            get { return customProp; }
            set { customProp = value; }
        }

 

Notice that you could also modify the parameters that get passed into the JavaScript popup window creation script, in the case above I have made the popup window larger than the default size. The problem with the above approach is that the URL that gets passed as the first parameter to the HtmlDesignerAttribute must be a constant value, since it’s used in the attribute declaration. However Microsoft have provided us with a nice way to change this behaviour.

 

        [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
        System.Web.UI.WebControls.WebParts.WebDisplayName("Template"),
        System.Web.UI.WebControls.WebParts.WebDescription(""),
        System.Web.UI.WebControls.WebParts.Personalizable(
        System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
        System.ComponentModel.Category("Settings"),
        System.ComponentModel.DefaultValue("")
        ]
        [HtmlDesignerAttribute(BrowserBuilderType.Dynamic, DialogFeatures = dialogHeight:500px;dialogWidth:650px;help:no;status:no;resizable:yes")]
        public string CustomProp
        {
            get { return customProp; }
            set { customProp = value; }
        }

 

I’ve now added the BrowserBuilderType.Dynamic option. To get this to work, we now need to override the GetCustomBuilder method of your web part:

 

         protected override string GetCustomBuilder(stringpropertyName)
        {
            if(propertyName == "CustomProp")
            {
                return"url.aspx?"+ "some custom params";
            }
            return base.GetCustomBuilder(propertyName);
        }

 

Now every property that has BrowserBuilderType.Dynamic passed into the HtmlDesignerAttribute will get passed into the GetCustomBuilder method, this gives you the chance to create a URL that passes parameters to your custom builder page.

 

Ok, so now you got the web part covered, what about the JavaScript that needs to run on your custom property builder page?

 

The best thing to do is to look at the current zoomdldr.aspx page, when the page loads you can get the current arguments from:

 

window.dialogArguments

 

Then to save the arguments, save the string value back to:

 

window.returnValue

 

 

Now that you’ve got a fair idea about creating custom property builders, you can create builders that are specific to you needs, below is a screenshot of a property builder form that lets the user enter some c# code that gets compiled an injected into the web part, the need was to provide a nice interface that the end user (i.e. a programmer) could use. The property builder uses the EditArea control to format the code nicely. The user can then click the compile button which will do a compilation of the code and report any errors. I’ve also added some JavaScript code to get around the removal of the carriage return characters, which was a major pain point for us.

 

Custom Property Builder

 

 

This approach will give you the power to create property builders that more closely suit your needs and goals.

Saturday, April 25, 2009 12:44:51 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | Sharepoint | Tip
# Tuesday, April 21, 2009

There is always lots of criticism about SharePoint from developers, things like bloated pages etc. But there is lots of good stuff in SharePoint, so I thought it might be fun to come up with a list of things that a typical asp.net application might be able to look at SharePoint for guidance.

 

1. Search - Have a good search story

We now have a whole generation that have grown up using a search engine, they expect a search box that they can type free form data, so by far the biggest feature that ASP.NET applications could learn a lot is the search capability of SharePoint. The good news is that you could use a product like search server express to crawl parts of your application and then use the search server web services like I did with my ASP.NET MVC search engine example.

 

2. Extensibility – Make your system extensible where it makes sense

SharePoint has a number of extensibility points like Features, Site Definitions, Web Parts which all work in different ways to allow fine grade control over the system. The model that SharePoint provides works well for the tasks that it is trying to achieve, your ASP.NET application may benefit from a plugin model that a framework like MEF provides, where crazy things like plugins for plugins are supported. Maybe you want to expose a RESTful service via ADO.NET Data Services, in any case extensibility has proved for many web applications (twitter, facebook) to be an important consideration.

 

3. Workflow - Hosting done right

Workflow has become a hot topic in recent years, lots of tasks are well suited to be modelled with a workflow, I’m sure your ASP.NET application has some actions that need some auditing or approval before action can be taken. SharePoint does a particularly good job at hosting Window Workflow Foundation, even if we exclude the building of custom workflow’s on the fly in SharePoint Designer, a great deal can be taken away from the way SharePoint has implemented WorkFlow, it’s made it simple for most users to understand.

 

4. Well designed API  

Well not everyone would agree with the ‘well designed’ bit, but for the most part you can achieve almost all the administrative activities from the API. Although a well built ASP.NET application should in theory have loosely coupled components that should facilitate an API, in practice most ASP.NET applications have code thrown all over the place, with event handlers performing data access etc. Ideally the well designed API should fit with the Extensibility point from number two, it’s no good having an extensible system if the API is unusable.

 

5. Background Processes – How do you handle them?

One thing that SharePoint does particularly well is the background processing of tasks like Active Directory imports, site cleanup processes and scheduled emails. The API from the above point extends to the scheduling plugin’s as well, the main point here is that SharePoint runs these tasks as a windows service, this is in contrast to applications like DotNetNuke which do background and scheduled processing on the application OnStart event. Both are valid approaches given certain circumstances, my point is that the difference between a professional product like SharePoint and a custom ASP.NET application is the little house keeping items, SharePoint tries to take care of it all, so should your ASP.NET application.

 

6. Integration with the Microsoft Software Ecosystem

SharePoint being a Microsoft product plays nice with other Microsoft systems, your ASP.NET application can do the same, things like using Exchange for email events to support incoming email. Making use of active directory groups for authentication and authorisation or even for organisation charts and manager notifications, this integration is what really differentiates Microsoft’s products and makes people feel more comfortable. Does your ASP.NET application provide custom WMI performance counters for it’s operations?

Tuesday, April 21, 2009 10:48:44 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
Misc
# Sunday, April 19, 2009

A little while ago, I was working with a customer who wanted to have the ability to write some annotations on a graph that was presented in a SharePoint site. The idea was that the graph was quite complex and it would help the end user understand it more if it was annotated by a domain expert.

So I put together a little proof of concept web part that makes use of all that JQuery goodness. I figured that it didn’t matter if you were annotating complex graphs, or tagging someone’s face like the way flickr and facebook does, the concept was the same:

 

image

 

image

 

image

 

So like I said the code is proof of concept, not production ready, but I thought I’d post it here anyway, if only to inspire ideas.

I’ve stored the data in a list with the schema of

 

Title:  default setting (single line of text)

JSON: single line of text

 

Or:

image

 

The data will look like this once some annotations have been added:

 

image

 

So I’ve just stored the JSON serialised object as is. The web part will iterate over each item on that list and pump that JSON into some JavaScript that is written to the page, so when the page load’s it will insatiate a JavaScript array of annotations and then render them. Which looks something like:

 

notes = [{ "x1": "11", "y1": "28", "height": "91", "width": "73", "note": "This is bill", "imgID": "0"}, { "x1": "86", "y1": "54", "height": "81", "width": "95", "note": "This is Kate", "imgID": "0"}];

 

You might ask why I have the imgID property, this is used if more than one image on the page is annotated.

 

To have a play around with the web part, you can download the WSP here, you just need to deploy the solution in the normal manner (stsadm –o addsolution –filename <path to httpcode.imageannotate.wsp>

 

Next step is to activate the Image Annotation web part feature on the site:

 

image

 

Add the web part as normal from the web part menu:

 

image

 

Once the web part is on the page, you need to set List setting to the SharePoint list that will store the JSON data (this list must conform to the schema above):

 

image

 

 

The client ID is the most technical part, this is a selector that gets used directly by JQuery, so any valid JQuery selector will work. A couple of examples:

This will find the default image on the homepage of a standard collabration portal:

img[src*='/PublishingImages/newsarticleimage.jpg']

 

All the images produced by a Dundas chart:

img[src*='SharePointChartAxd.axd']

 

Hopefully you get the idea.

 

I haven’t added any way to delete the annotation from the image itself, rather you need to go to the custom list and delete the item from there. The web part is smart enough to not allow users who can’t add items to the list. Credit should also go to odyniec.net for the imgAreaSelect JQuery plugin which I’ve made use of.

The web part injects lots of JavaScript into the page, so the code might look a little messy because of this, but the basic premise is pretty straight forward.

 

The source code can be found here. It includes a HttpHandler (ImageAnnotate.ashx) for handling all the ajax calls from the client side script. Remember this is just some sample code, use it at your own risk.

Sunday, April 19, 2009 6:49:54 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Sharepoint
# Tuesday, March 24, 2009

I came across an interesting problem the other day, I needed to do some string manipulation with SQL Server, normally dealing with this sort of stuff sucks, I wanted to split a string like  ‘item1, item1, item3’ .. so splitting on a given character. The first thing that popped to my mind was a Table Valued function, something like this:

 

-- Splits a column based on a specified delimiter
CREATE FUNCTION dbo.Split
(
      
@List nvarchar(2000),
      
@SplitOn nvarchar(5)
)
RETURNS @RtnValue table
(

      
Id int identity(1,1),
      
Value nvarchar(2000)
)
AS
BEGIN

       While
(Charindex(@SplitOn,@List)>0)
      
Begin
               Insert Into
@RtnValue (value)
              
Select  Value = ltrim(rtrim(Substring(@List,1,Charindex(@SplitOn,@List)-1)))
              
Set @List = Substring(@List,Charindex(@SplitOn,@List)+len(@SplitOn),len(@List))
      
End

       Insert Into
@RtnValue (Value)
              
Select Value = ltrim(rtrim(@List))

              
Return
END

 

There are many examples of this type of function on web, my point isn’t the actual implementation, rather the way you can use it.

I can now run the query:

 

select Value from dbo.Split('item1, item2, item3',',')

 

image

 

But my problem was a little different from the trivial example above, I wanted to pass data into this function that was the result of my query, rather than just passing arguments in statically like above and using the function as a table, I really wanted to apply the function to the data from my table, on a row by row level.

After a little searching I found the CROSS APPLY SQL syntax, with this I can now do exactly what I want:

 

select SF.Value from TestTable TT
cross apply dbo.Split(TT.Value, ',') SF

 

The above query simple selects the data from the TestTable, then cross apply’s the Split function passing it the TestTable’s value column (which contains the data ‘Item1, Item2, Item3, Item4’, also note that I’m selecting the column from the Split function.

 

This Produces the desired results:

image

 

Given a table like:

 

image

 

And data like:

 

image

 

I thought this was pretty cool. SQL can be fun.

Tuesday, March 24, 2009 10:51:29 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | SQL Server
# Thursday, March 19, 2009

If you are using the BDC in your MOSS application, you may have noticed that the AUDEvent table in the SSP database can grow quite large and quickly. You might think that the stsadm command –o trimauditlog would do the trick in reducing this table, but in fact it does not work for the SSP database only content databases.

 

You can specify the audit setting in the Application Definition File. Simply add the Property ‘Audit’ to your Entity’s Properties section:

 

<Entities>
    <
Entity EstimatedInstanceCount="0" Name="dbo.Sales">
      <
Properties>
        <
Property Name="Audit" Type="System.Boolean">false</Property>
      </
Properties>

The setting of false will prevent any audit entries being written to the AUDEvent table.

Thursday, March 19, 2009 9:48:23 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
BDC | MOSS | Sharepoint | Tip

Yesterday I presented at the Brisbane SharePoint User Group, my topic was Using the Business Data Catalog with User Profiles. It was loosely based on this screencast I did last year.

The entire presentation was recorded using Live Meeting, it can be viewed by going to: https://www112.livemeeting.com/cc/microsoft/viewRecordings

and use the Recording Code: 7B8B6D

I’ve put the PowerPoint presentation for my talk here.

Thursday, March 19, 2009 9:32:01 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
BDC | setup | Sharepoint

A while ago I wrote about a simple method to give web parts an easy to configure design experience. The method I came up with made use of the ASP.NET Page method ParseControl.

In my original example, the developer had to do some manual work like finding the templated control once it was added to the page, this was a tedious and ugly design. At the time it was more of an idea rather than something that I had build seriously, but recently I’ve put a bit more thought into the idea of web parts with a configurable UI template.

I’ve built on the original example, but with the goal of abstracting the ParseControl and ControlBinding implementation.

This example simply writes a title and description:

 

image

 

We want to give end users the ability to change the way this title and description look without any web part code changes.

This is done via a template, which is a property of the web part:

 

image

 

Notice this is ASP.NET syntax

 

The template can be easily changed:

 

image

 

To produce a different rendering, without the need for any developer assistance:

 

image

 

From a developers point of view, we really want to just declare some variables and be able to use them as if they were part of the web part, without thinking about FindControl or any implementation detail.

 

I put together a base web part that looks for any private variables with a custom attribute called ControlBinding and via reflection sets this variable to the control instance specified in the attribute, so for the above example the code for the web part would look like:

 

public class TemplateTest : WebPartTemplateBase
{
        [ControlBinding("titleLabel")]
        Label titleLabel = null;

        [ControlBinding("descriptionLabel")]
        Label descriptionLabel = null;

        protected override void OnPreRender(EventArgs e)
        {

            if (titleLabel != null)
            {
                titleLabel.Text = "Some text set from code";
            }

            if (descriptionLabel != null)
            {
                descriptionLabel.Text = "some text description that was set in code";
            }
           
            base.OnPreRender(e);
        }
}

 

So now all the developer needs to do is add an attribute to private variables with the name of the templated control they want to obtain an instance to. This fits very much with the code behind / partial class philosophy that web developers are used to working with.

 

If we look at some more complex scenario’s we start finding a few little issues, for example lets say that we want to use a GridView to do some data binding, in a normal aspx page we would use code nuggets to do the data binding:

 

<asp:GridView ID="GridView1" runat="server">
        <
Columns>
            <
asp:TemplateField>
                <
ItemTemplate>
                 <
asp:label id="dataItemLabel" runat="server"
                                   
Text='<%DataBinder.Eval(Container, "SomeValue"%>'></asp:label>
                </
ItemTemplate>
            </
asp:TemplateField>
        </
Columns>
</
asp:GridView>

 

If we used this in our template we would find that it doesn’t work. There is however a work around:

We can subscribe to the RowDataBound event and we can find the controls we wish to data bind to:

 

 

        [ControlBinding("GridView1")]
        GridView gridView = null;

        protected override voidOnPreRender(EventArgs e)

         {

            if(gridView != null)
            {
                gridView.RowDataBound += newGridViewRowEventHandler(gridView_RowDataBound);
                gridView.DataSource = GetData();
                gridView.DataBind();
            }
           
            base.OnPreRender(e);
        }

        void gridView_RowDataBound(objectsender, GridViewRowEventArgs e)
        {
            Label itemLabel = e.Row.FindControl("dataItemLabel") as Label;

            if(itemLabel != null)
            {
                itemLabel.Text = e.Row.DataItem.ToString(); //cast and use the data item
          
}
        }

 

In which case the template would look like:

 

<asp:GridView ID="GridView1" runat="server">
        <
Columns>
            <
asp:TemplateField>
                <
ItemTemplate>
                 <
asp:label id="dataItemLabel" runat="server"></asp:label>
                </
ItemTemplate>
            </
asp:TemplateField>
        </
Columns>
</
asp:GridView>

 

I’m still in the early stages of experimenting with this pattern, so it could be the case that it’s totally unsuitable for some scenario’s.

If your interested in exploring this pattern a little more, you can download the code to the base class here.

Thursday, March 19, 2009 9:07:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
code | Sharepoint | Tip
Statistics
Total Posts: 191
This Year: 4
This Month: 0
This Week: 0
Comments: 41