Showing posts with label Web Parts. Show all posts
Showing posts with label Web Parts. Show all posts

Tuesday, 15 February 2011

How to reference a user control deployed to the GAC

Warning: I don't consider myself a control developer in the classic sense (although I frequently write web parts for deployment within a SharePoint environment, the scope of that deployment is fairly small by default).

A lot of the functionality I build these days runs client-side, powered by my all-time best friend jQuery. Because of this, I find I'm most productive when I first establish the HTML structure in a static HTML file before moving the mark-up to a control. In the bad old days of complete ignorance, I would have had refactor this HTML and build it programmatically within a custom control—an approach I despise (HTML belongs in mark-up).

To overcome many of the drawbacks to custom control development, I prefer migrating my prototype to a user control (which has a front-end .ascx file) and then loading that control in my web part code-behind or custom field control. Check out the LoadControl documentation for information about how load a user control programmatically.

This works beautifully when the control's code behind is compiled to an assembly destined for deployment to the private bin directory. The user control can be built in isolation with the ascx file and code behind remaining wired up for easy access to Intellisense, etc. At build time, the .ascx file is copied somewhere and deployed somewhere useful (the CONTROLTEMPLATES virtual directory, if you like).

Things get trickier when that assembly is to be deployed to the global assembly cache (aka the GAC). In my case I wanted to do this for a custom field control; although most of the code for that control was already being deployed to the GAC, it made sense to keep these artefacts together in the same project. For my non-SharePoint readers, any assembly going into the GAC has to be strong named and signed (via the project's properties sheet > Signing tab); to add the assembly to the GAC, call gacutil:

gacutil –i "MyAsemblyName.dll"

With that out of the way, assume we've got two projects: the first (MyControls) is a class library outputting a signed assembly intended for the GAC; the second is a simple web site (Web).

GAC_User_Control_sln

The MyControls project contains our user control (for information about how to set this up, refer to my post How to add a web project item to a class library). The MyControls assembly is deployed to the GAC.

The web site project contains a copy of the .ascx user control file from the MyControls project and a web page with a @ Register directive pointing to the project-local .ascx file. The Web project doesn't reference the MyControls project because we want it to load the assembly it depends on from the GAC. The .ascx copy can be done manually but you'll likely want to automate this as a pre-build task.

While the MyControls project will now compile, the Web site project will fail to compile with the error: Could not load type 'MyControls.MyUserControl'. If you're in a SharePoint environment, you'll likely see get this as a parser error when the page is dynamically compiled at first request.

To fix this, you need to add an @ Assembly directive to the top of the .ascx file to reference the MyControls assembly deployed to the GAC. You'll need the assembly name and public key token to flesh out this directive. The assembly name can be retrieved from the project properties sheet (normally it's the same as the project anyway). Then extract the public key token using the strong name application (it's the short value):

sn –Tp "MyControls.dll"

If your AssemblyInfo.cs specifies a version number of 1.0.0.0, your @ Assembly directive should look something like this:

<%@ Assembly Name="MyControls, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=b3de351d91c5d4d2"%>

If you version numbers are automatically updated by some kind of policy or build event, beware you'll also need to update this directive as well, which may prove cumbersome.

This directive can be added to both copies of the .ascx file without impacting the MyControls build or edit-time experience.

Both projects will now compile and run, successfully loading the user control base type (i.e. the code behind) from the GAC.

You can download this solution here.

If you found this post helpful, please support my advertisers.

Wednesday, 15 December 2010

How to disable web part chrome

In the context of a highly-branded WCM site, the default "chrome" (border and title) SharePoint wraps around your custom web parts can be ugly and inconvenient. At worst, it will make your site look SharePoint-y and require every web part added to a page to have its Chrome settings manually adjusted.

Chrome Type

That's no way for a web part to behave in content management system but what to do?

In the past I've set the chrome type explicitly for each custom web part, either in the .webpart/.dwp file itself or programmatically:

<property name="ChromeType" type="chrometype">None</property>

this.ChromeType = PartChromeType.None;

To avoid doing this per web part (or cluttering up your nice little base web part class), you can set the PartChromeType property on the WebPartZone declared in your page layout:

<WebPartPages:WebPartZone id="wpzLeftColumn" runat="server" title="Left Column" PartChromeType="None">

Any web parts added to that zone with a default Chrome Type (aptly named "Default") will inherit this setting from the web part zone. Of course individual web parts can override this as required.

If you found this post helpful, please support my advertisers.

Enumeration-based property with initialiser fails to load in tool pane

I encountered what is likely an obscure issue today while using an existing enumeration to populate a drop down list in the tool pane of a SharePoint web part.

The web part didn't require a custom toolpart so I'm referring to a web part property defined within the web part class itself:

[SPWebCategoryName ("TWA")]
[FriendlyNameAttribute ("Region Override")]
[WebDescription ("Explicitly configure the region displayed when the mode is set to Region.")]
[Personalizable (PersonalizationScope.Shared, false)]
[WebBrowsable (true)]
public WARegions RegionOverride { get; set; }

Notice how the public RegionOverride property has a type of WARegions. Normally, the end result is an additional custom property displayed in the web part tool pane; in this case, the WARegions type is an enumeration and SharePoint therefore renders this as a drop down list:

Enumeration DDL

I say normally because today I was only getting a disabled DDL containing a single item ("Perth"). SharePoint was also kind enough to present me with this vaguely useful error message:

Some of the properties for this Web Part cannot be displayed properly. For more information, see your site administrator.

The WARegions enumeration is an existing enum I chose to reuse for the sake of convenience. Upon further inspection, I noticed the first element in the enum (Perth) was explicitly assigned a value of 1. Acting on my suspicion, I removed the explicit assignment and the DDL would then display as expected.

As a final test to confirm whether the explicit initialisation was somehow behind the single list item being displayed, I set default values for all elements in the enum but the error returned. Fortunately in my case I can do away with the explicit initialisation (which I'm not keen on—I prefer to initialise enums myself).

If you found this post helpful, please support my advertisers.

Thursday, 19 August 2010

Setting the Visible property on a webpart throws

Using myWebPart.Visible = false and getting this?

The Visible property cannot be set on Web Part 'your_web_part'. It can only be set on a standalone Web Part.

Use myWebPart.Hidden = true instead.

If you found this post helpful, please support my advertisers:

Saturday, 19 June 2010

Don't use XmlRoot with .webpart files

Just noticed using an [XmlRoot("MyRoot")] attribute on my webpart class in conjunction with a .webpart file (as opposed to a .dwp file) gives me this error when attempting to add the webpart to a page:

Incompatible Web Part markup detected. Use *.dwp Web Part XML instead of *.webpart Web Part XML.

Incompatible_web_part_markup_detected

Well I don't want to use *.dwp Web Part XML so I removed the XmlRoot attribute instead. I'm feeling wild, you see.

Beware the old doco: http://msdn.microsoft.com/en-us/library/dd584160(office.11).aspx 

Monday, 19 April 2010

No parameterless constructor defined for this object when creating a new page

With everything working admirably in development I was greeted with the below error message when attempting to create a new page after today’s fresh deploy to UAT (and eventually prod, where I gave up):

Error No parameterless constructor defined for this object

No fun but luckily the problem was easily isolated and a work around arrived at—although I have yet to unearth the root cause.

By way of more detail, I was attempting to create a new page instance in a new sub site using a branded master page and page layout. This isn’t normally a problem and seems to be limited to this master page/layout combo as I can successfully create page instances in the same sub site using another layout known to be good. The problematic page layout contains a single rich text field and webpartzone but, unlike other posts discussing the same error message, the <zonetemplate> element is empty and I’m not attempting to deploy web parts to the page using the AllUsersWebPart crappiness. I just want my basic page!!

Most of the suggestions out there for resolving the above error lead you to the web part editor maintenance screen (see below) where you’ll find one or more ErrorWebParts, which can then be deleted. The problem with this approach is maintaining the page’s web parts requires the page layout to be checked out and that customises the layout, meaning future feature-based deployments of that layout won’t “take”. Of course, my layout isn’t supposed to have any web parts so go figure where the single mysterious ErrorWebPart originates… there seems to be a (related?) issue accessing web part programmatically in which ErrorWebParts objects are returned instead so I wonder if this is a different web part than how it’s represented…

Others suggest redeploying the layout after deleting it from the gallery but that didn’t work in my case.

In the end, I created a page from a different layout using the same content type as my desired page and then changing the layout. All fine from there but not at all usable—luckily this was for a short-lived campaign site.

How to open a page layout in web part maintenance mode

Just as a page instance can have web parts, a page layout can also have web parts. To manage those web parts, check out the layout within the Master Page Gallery, and click View Properties from the context menu. Next, edit the item and click the ‘Open Web Part Page in maintenance view’ link at the bottom of the page.

Alternatively, you can substitute your page layout in this URL:

http://<server>/_catalogs/masterpage/<page layout name>.aspx?contents=1

Friday, 2 April 2010

Use the SPWebCategoryName attribute instead of the System.ComponentModel.Category attribute to group web part settings

For some reason I’ve always used the System.ComponentModel.Category attribute to configure the category or group under which a web part property is displayed when viewing the web part’s settings. This approach always worked fine but it also felt a little dirty—especially because I always knew an alternative existed (but was never certain what that alternative might be ;-)

At long last I’ve determined the alternative to be the SPWebCategoryName, which works identically to Category:

[Personalizable(true),
WebBrowsable(true),
SPWebCategoryName(“My Category”),
WebDisplayName(“My Property”),
WebDescription(“My property description.”)]
public string MyProperty ( get {…} set {…} )

The SPWebCategoryNameAttribute type lives in the Microsoft.SharePoint.WebPartPages namespace so you’ll need to add a using statement or fully-qualify the type name.

Wednesday, 10 February 2010

SharePoint 404: The resource cannot be found

If a page element is missing—either a user control hasn’t been deployed or something else that needs to be in place for the page to compile dynamically is awol—you might be the proud owner of a 404 even though the page itself does exist.

HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly.

After setting the SafeMode element’s CallStack element true in web.config, turning off CustomErrors, and setting the debug=”true” attribute on the compilation element, you’re possibly looking at a beautiful ASP.NET error message with no stack trace and not a lot to go on.

Well here’s a trick: view source on the error page and and scroll down below the closing HTML tag. Behold the stack trace! It should tell you exactly where to look for the problem.

Tuesday, 9 February 2010

westernaustralia.com Relaunch and Other Website Recent Activities

If you’ve been following the progress of the westernaustralia.com website over the years, you’ll already know the site was one of the first in Australia to be launched on the MOSS 2007 platform and has been consistently voted as one of the top fully branded SharePoint sites in the world ever since. Having worked on the site since the get-go and as the current technical lead for the project, I’m the proud owner of the site’s programmatic aspects; today I’m tickled pink to tell you, the SharePoint community, about yesterday’s launch of the new Tourism Western Australia brand, Experience Extraordinary, and, more specifically, the brand’s impact on the westernaustralia.com website.

At the moment we’re working with the Digital Marketing team on a two (or three phase) approach (depending on how you look at it) to implement the new brand. The first phase unfolded in August last year and involved the neutralisation of the preceding brand’s elements (The Real Thing) while the latest creative design agency, Host, was appointed. Phase 1 of the new brand implementation kicked off in the dev team a little over three weeks back and involved making the Photoshop mockups provided by Host into a living website. This work focused largely on the page banners (vibrant imagery, the tab, and extra height), the navigation, the background colour (trust me—everything is more complicated than you might think at first glance with wa.com!), what we call the ePostcards element, and the footer elements.

Here’s a picture of what I think is a visually stunning website and a vast improvement over its predecessor:

wacom homepage 4 Experience Extraordinary Phase 1

Phase 2 holds still more secrets but you can bet we’ll be making all the middle bits fit with the rest of the changes. One of the challenges we’ll be facing is getting the home page weight down from a whopping 1750KB and 100 requests. Stunning, certainly, but our CDN of choice (Akamai) allows us to get away with this sort of monkey business.

In addition to all the glam, we also took the opportunity to overhaul the heading infrastructure. This subsystem is designed to give content editors the ability to upload new banner images and all of the corresponding display data required to render the Flash banner. The Flash banner itself was built by Host from our previous banner and it takes as a parameter an XML file providing all the information it needs to display the banner images, text, links, and colours.

Obviously this data is stored in a list to which the FlashBanner.cs web part communicates at render time. Like I say, nothing in wa.com is as it first appears and one of the more complex requirements was allowing a subsite to override the banners displayed for for it and its children (in effect to support the major subsections of the site such as the destinations). Apart from simply walking up the site hierarchy recursively until a banner list is found, we then needed interrogate the list and emit an XML file acceptable to the banner; the URL to the file is finally cached with a dependency on the file itself.

Interestingly, this was one of the first times we started using event receivers on the banner lists to invalidate the cache when a banner is added, edited, or deleted; this keeps the content editors happy and productive and is one less of those annoying “is my banner cached in the browser cache | Akamai | reverse proxy| application cache | output cache | blob cache?” questions ;)

I’ll also point out we’re using a simple custom list for this instead of a picture library or custom list derived from a picture library. The previous version of this list was in fact a custom picture library but it suffered from broken thumbnails (which we now know how to fix) and proved rather cumbersome to use in practice (no reordering, a difficult view, etc). The move away from the picture library wasn’t, in retrospect, necessarily a great decision but it was probably the best decision at the time and came with considerable deliberation. The one major problem with the current approach is banner images must be uploaded separately to a real picture library and then referenced from the list; this is a bit of a double-up and disconnects the image itself from its metadata. As our understanding of list development and the peculiarities of these other list types increases, we may revisit this approach.

Also at a technical level, we recently move the site from our old IBM BladeCenter kit running Windows Server 2003 x86, MOSS 2007 SP1 (not even the Infrastructure Updates—gasp!), .NET 3.0, and a non-clustered SQL Server 2005 x86 database server. Everything was virtualised on ESX 4.0. We actually had three of these farms: one in prod, one in DR, and one for authoring; as content deployment never worked for the site, this meant a daily backup and restore of the content database from authoring to production and corresponding switch to or from DR (with DR actually being treated as a standby prod environment). That must have really sucked for our admin who had did that essentially manual task every business day since launch in May 2007. What. A. Drag.

From that setup, we moved to what was briefly the bees knees: Windows Server 2008 x64, MOSS 2007 SP2 + June 2009 CU, .NET 3.5 SP1, and a clustered SQL Server 2008 x64 database environment. Everything is still virtualised on ESX 4.0 and the entire setup is mirrored in DR. We’ve finally done away with the authoring farm so content editors are editing content in the production farm (we’re working towards a tiered security design and approval workflows, by the way, but our content editors have been working with the environment for years now and are mature in their understanding of how not to break the site ;). So no more daily content deployments and those of us in the dev team can finally start working with .NET 3.5 and LINQ.

For more on how we run the site, check out my Perth SharePoint User Group presentations and videos (things have changed but not that much):

http://blog.mediawhole.com/2009/02/how-we-do-wcm-at-tourism-wa-im-speaking.html

http://blog.mediawhole.com/2009/03/presenting-at-next-sharepoint-user.html

If you’ve read this far, you deserve the chance to win a prize. Want the opportunity to take an extraordinary taxi ride around the state of Western Australia? If so, check out the “brand activation” microsite: http://www.extraordinarytaxiride.com.au/

Note: I am employed by Tourism WA as a contractor working for Diversus. Mediawhole, mediawhole.com, and this blog are not associated directly with Tourism Western Australia or the westernaustralia.com website. The information provided above  is published independently as a member of the public and does not reflect the views of Tourism Western Australia or Diversus. Please consult a Tourism WA representative for more information about its brand, campaigns, and websites.

Monday, 27 April 2009

Delete web parts from the Web Parts gallery when a feature is deactivated

I don’t normally pay a lot of attention to what SharePoint does when deactivating features, retracting solutions and deleting solutions; I’m usually much more interested in what happens as things are installed: did I get everything right in manifest.xml? Did all the files get deployed correctly? And so on.

Now I’m a bit of a neat freak and to date I’ve always been reasonably impressed with how SharePoint actually deletes the folder wrapping a feature when the solution is removed and generally cleans up after itself. I wasn’t so excited, however, when I realised SharePoint still leaves some junk behind—where it’s visible to users and clients in the Web Parts gallery, no less. If I request a feature be deactivated, I expect it to be deactivate in full, not leave web parts listed in the Web Parts gallery that when added to a page will simply break.

I was also noticing the modified date in the Web Parts gallery wasn’t updating as I redeployed and reactivated my solutions/features. I’m not clear on this but I wonder if that means the web part definition isn’t getting updated in the site collection (it was suggested elsewhere).

A quick search reveals this is normal SharePoint behaviour but luckily two guys have come up with an elegant solution to remove spurious web parts from the gallery when the containing feature is deactivated; the only downside is the feature must be explicitly deactivated—simply retracting the solution and even following up by deleting the solution from the solution store won’t trigger the feature receiver’s FeatureDeactivating method. Of course these web parts can be deleted manually but we all know that sucks.

Anyway, it’s a fine start: Greg Galipeau kicked off a first draft that removes a single web part based on the feature name and Trent Foley applied some nice LINQ wizardry to remove all the web parts listed in the feature’s element definitions. Nice work guys! My contribution? Well apart from saying thanks, I’ve tested out Trent’s implementation and proven it works brilliantly; I’ve also swapped out the var declarations for concrete types to assist with my understanding and because I like being explicit (I won’t bother posting the code unless someone requests it).

Next step: page layouts? ;-)