Archives

Archives / 2012 / September
  • SharePoint 2010 Field Expression Builder

    OK, back to two of my favorite topics, expression builders and SharePoint. This time I wanted to be able to retrieve a field value from the current page declaratively on the markup so that I can assign it to some control’s property, without the need for writing code. Of course, the most straight way to do it is through an expression builder.

    Here’s the code:

       1: [ExpressionPrefix("SPField")]
       2: public class SPFieldExpressionBuilder : ExpressionBuilder
       3: {
       4:     #region Public static methods
       5:     public static Object GetFieldValue(String fieldName, PropertyInfo propertyInfo)
       6:     {
       7:         Object fieldValue = SPContext.Current.ListItem[fieldName];
       8:  
       9:         if (fieldValue != null)
      10:         {
      11:             if ((fieldValue is IConvertible) && (typeof(IConvertible).IsAssignableFrom(propertyInfo.PropertyType) == true))
      12:             {
      13:                 if (propertyInfo.PropertyType.IsAssignableFrom(fieldValue.GetType()) != true)
      14:                 {
      15:                     fieldValue = Convert.ChangeType(fieldValue, propertyInfo.PropertyType);
      16:                 }
      17:             }
      18:         }
      19:  
      20:         return (fieldValue);
      21:     }
      22:  
      23:     #endregion
      24:  
      25:     #region Public override methods
      26:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      27:     {
      28:         return (GetFieldValue(entry.Expression, entry.PropertyInfo));
      29:     }
      30:  
      31:     public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
      32:     {
      33:         if (String.IsNullOrEmpty(entry.Expression) == true)
      34:         {
      35:             return (new CodePrimitiveExpression(String.Empty));
      36:         }
      37:         else
      38:         {
      39:             return (new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(this.GetType()), "GetFieldValue"), new CodePrimitiveExpression(entry.Expression), new CodePropertyReferenceExpression(new CodeArgumentReferenceExpression("entry"), "PropertyInfo")));
      40:         }
      41:     }
      42:  
      43:     #endregion
      44:  
      45:     #region Public override properties
      46:     public override Boolean SupportsEvaluate
      47:     {
      48:         get
      49:         {
      50:             return (true);
      51:         }
      52:     }
      53:     #endregion
      54: }

    You will notice that it will even try to convert the field value to the target property’s type, through the use of the IConvertible interface and the Convert.ChangeType method.

    It must be placed on the Global Assembly Cache or you will get a security-related exception. The other alternative is to change the trust level of your web application to full trust.

    Here’s how to register it on Web.config:

       1: <expressionBuilders>
       2:     <!-- ... -->
       3:     <add expressionPrefix="SPField" type="MyNamespace.SPFieldExpressionBuilder, MyAssembly, Culture=neutral, Version=1.0.0.0, PublicKeyToken=29186a6b9e7b779f" />
       4: </expressionBuilders>

    And finally, here’s how to use it on an ASPX or ASCX file inside a publishing page:

       1: <asp:Label runat="server" Text="<%$ SPField:Title %>"/>

    Read more...

  • Fixing SharePoint 2010 Permission Problems on Windows 7

    I had a tough time trying to have SharePoint working perfectly on a Windows 7 development machine that was occasionally disconnected from the Active Directory (when I am home I must connect through a VPN). I mostly had problems with service applications such as User Profile, Managed Metadata, Business Connectivity Services and the like, and all I knew were cryptical messages such as “access denied” or “the service or application pool is not started”. I was sure that both the services and application pools were running under a domain account that had proper permissions on the SQL Server instance, and basically it was a fresh installation. Lots of people are having the same problem, apparently. After banging my head against the wall for several days, I remembered about farm (what I had) versus stand-alone (which I had never tried) installations. Bingo!

    Here’s what I did: I dropped all SharePoint databases and logins and reinstalled SP from scratch, only this time not in farm mode, but as stand-alone. After the SharePoint Configuration Wizard started, I cancelled it and started the Management Shell. I created the configuration database manually by using the New-SPConfigurationDatabase cmdlet where I specified a local account – something that the Configuration Wizard wouldn’t allow me to do. Then I restarted the Configuration Wizard and everything began working perfectly! Yes, I got some pre-configured service applications and also some content which I didn’t need, but I realized it was possible to drop and recreate everything the way I wanted to. All services and application pools are now running under local accounts, which is fine for my development needs. Really, Microsoft… Sad smile

    I hope this will bring light to someone facing the same problems!

    Read more...

  • ASP.NET Web Forms Extensibility: Control Adapters

    All ASP.NET controls from version 2.0 can be associated with a control adapter. A control adapter is a class that inherits from ControlAdapter and it has the chance to interact with the control(s) it is targeting so as to change some of its properties or alter its output. I talked about control adapters before and they really a cool feature.

    The ControlAdapter class exposes virtual methods for some well known lifecycle events, OnInit, OnLoad, OnPreRender and OnUnload that closely match their Control counterparts, but are fired before them. Because the control adapter has a reference to its target Control, it can cast it to its concrete class and do something with it before its lifecycle events are actually fired. The adapter is also notified before the control is rendered (BeginRender), after their children are renderes (RenderChildren) and after itself is rendered (Render): this way the adapter can modify the control’s output.

    Control adapters may be specified for any class inheriting from Control, including abstract classes, web server controls and even pages. You can, for example, specify a control adapter for the WebControl and UserControl classes, but, curiously, not for Control itself. When specifying a control adapter for a page, it must inherit from PageAdapter instead of ControlAdapter. The adapter for a control, if specified, can be found on the protected Adapter property, and for a page, on the PageAdapter property.

    The first use of control adapters that came to my attention was for changing the output of standard ASP.NET web controls so that they were more based on CSS and less on HTML tables: it was the CSS Friendly Control Adapters project, now available at http://code.google.com/p/aspnetcontroladapters/. They are interesting because you specify them in one location and they apply anywhere a control of the target type is created. Mind you, it applies to controls declared on markup as well as controls created by code with the new operator.

    So, how do you use control adapters? The most usual way is through a browser definition file. In it, you specify a set of control adapters and their target controls, for a given browser. This browser definition file is a XML file with extension .Browser, and can either be global (%WINDIR%\Microsoft.NET\Framework64\vXXXX\Config\Browsers) or local to the web application, in which case, it must be placed inside the App_Browsers folder at the root of the web site. It looks like this:

       1: <browsers>
       2:     <browser refID="Default">
       3:         <controlAdapters>
       4:             <adapter controlType="System.Web.UI.WebControls.TextBox" adapterType="MyNamespace.TextBoxAdapter, MyAssembly" />
       5:         </controlAdapters>
       6:     </browser>
       7: </browsers>

    A browser definition file targets a specific browser, so you can have different definitions for Chrome, IE, Firefox, Opera, as well as for specific version of each of those (like IE8, Firefox3). Alternatively, if you set the target to Default, it will apply to all. The reason to pick a specific browser and version might be, for example, in order to circumvent some limitation present in that specific version, so that on markup you don’t need to be concerned with that.

    Another option is through the the current Browser object of the request:

       1: this.Context.Request.Browser.Adapters.Add(typeof(TextBox).FullName, typeof(TextBoxAdapter).FullName);

    This must go very early on the page lifecycle, for example, on the OnPreInit event, or even on Application_Start. You have to specify the full class name for both the target control and the adapter. Of course, you have to do this for every request, because it won’t be persisted.

    As an example, you may know that the classic TextBox control renders an HTML input tag if its TextMode is set to SingleLine and a textarea if set to MultiLine. Because the textarea has no notion of maximum length, unlike the input, something must be done in order to enforce this. Here’s a simple suggestion:

       1: public class TextBoxControlAdapter : ControlAdapter
       2: {
       3:     protected TextBox Target
       4:     {
       5:         get
       6:         {
       7:             return (this.Control as TextBox);
       8:         }
       9:     }
      10:  
      11:     protected override void OnLoad(EventArgs e)
      12:     {
      13:         if ((this.Target.MaxLength > 0) && (this.Target.TextMode == TextBoxMode.MultiLine))
      14:         {
      15:             if (this.Target.Page.ClientScript.IsClientScriptBlockRegistered("TextBox_KeyUp") == false)
      16:             {
      17:                 if (this.Target.Page.ClientScript.IsClientScriptBlockRegistered(this.Target.Page.GetType(), "TextBox_KeyUp") == false)
      18:                 {
      19:                     String script = String.Concat("function TextBox_KeyUp(sender) { if (sender.value.length > sender.getAttribute('MaxLength')) { sender.value = sender.value.substr(0, sender.getAttribute('MaxLength')); } }\n");
      20:  
      21:                     this.Target.Page.ClientScript.RegisterClientScriptBlock(this.Target.Page.GetType(), "TextBox_KeyUp", script, true);
      22:                 }
      23:  
      24:                 this.Target.Attributes["onkeyup"] = "TextBox_KeyUp(this)";this.Target.Attributes["MaxLength"] = this.Target.MaxLength.ToString();
      25:             }
      26:         }
      27:  
      28:         base.OnLoad(e);
      29:     }
      30: }

    What it does is, for every TextBox control, if it is set for multi line and has a defined maximum length, it injects some JavaScript that will filter out any content that exceeds this maximum length. This will occur for any TextBox that you may have on your site, or any class that inherits from it. You can use any of the previous options to register this adapter.

    Stay tuned for more ASP.NET Web Forms extensibility tips! Winking smile

    Read more...