Thursday, August 21, 2008

Reset functionality for InfoPath

Something I really miss in InfoPath 2007 is the possibility to have a simple "reset" button, one that clears all your fields and lets you start all over. I didn't find any out-of-the-box functionality for this, so I wrote some code to make this work. So if you're looking for something that clears all your datasource's fields, try this piece of code:

private void ClearAllFields()
{
   //Create a navigator and select the first group in your datasource
   XPathNavigator nav = this.MainDataSource.CreateNavigator();
   XPathNodeIterator nodes = nav.SelectSingleNode("/my:myFields/my:group1", NamespaceManager).SelectChildren(XPathNodeType.Element);

   ClearLevel(nodes);
}

private void ClearLevel(XPathNodeIterator nodes)
{
   while (nodes.MoveNext())
   {
      if (nodes.Current.HasChildren)
      {
         ClearLevel(nodes.Current.SelectChildren(XPathNodeType.All));
      }

      if (nodes.Current.NodeType == XPathNodeType.Text)
      {
         //Reset the value to an empty string
         nodes.Current.SetValue("");
      }
   }
}

Just put the ClearAllFields() method in your button.clicked event handler and it should all work just fine! Make sure you select the right node to start with. Replace the "/my:myFields/my:group1" with the path to your top level group.

Friday, June 27, 2008

The specified name is already in use.

If the above error looks familiar and you can't seem to get rid of it, then read on, I might have a solution for you then!

When trying to activate a feature that would create a list, I received the following error:

The specified name is already in use.

A list, survey, discussion board, or document library cannot have the same name as another list, survey, discussion board, or document library in this Web site.
Use your browser's Back button, and type a new name.


The biggest problem was that the list really didn't exist. When I went to "All Site Content", nothing was there with the name of the list I was creating. I also looped over all my lists with three lines of code I quickly wrote, but nothing there either... After some searching on our good friend Google, I learned that apparently, if you open the site with SharePoint Designer, there is a folder there with that name. Just delete the folder and everything works fine again...

But what if you don't have SharePoint Designer installed and you can't intall it immediately, like me? Well, then these few lines of code might help you out:

SPSite site = null;
SPWeb web = null;

using (site = new SPSite(siteURL))
{
    using (web = site.OpenWeb())
    {
        web.Folders[nameOfListToCreate].Delete();
    }
}

Thursday, April 3, 2008

The file manifest.xml does not exist in the solution package

It's been a while since my last post (I'd really need to write that post on how to integrate Dynamics AX into MOSS ) and this one will also just be a quickie...

When trying to add my solution to the solution store, I got the error The file manifest.xml does not exist in the solution package. This was weird, because I didn't change anything to the deployment parts of my solution and it had always worked before... Also, the manifest.xml file seemed to be inside the wsp file just fine. Thanks to this blog, I was able to solve it!

Apparently, you get this error the moment your package gets too big. Mark Beij mentions 1440kb, but with me it already occured when my package got over 1400kb. The solution is to add the following lines to the top of your cab.ddf file:

.Set CabinetFileCountThreshold=0
.Set FolderFileCountThreshold=0
.Set FolderSizeThreshold=0
.Set MaxCabinetSize=0
.Set MaxDiskFileCount=0
.Set MaxDiskSize=0


Now that all the size limitations are off, just make your wsp file again and you should be able to add your solution again.

Friday, March 7, 2008

Filtering on multiple values with SPGridView

If you want to show list data in a Web Part, one of the options to consider is using the SPGridView control. There is a nice article on Bob's SharePoint Bonanza on how to set this up. This really is a great article, but it won't allow you to filter on multiple columns. My customer wanted to be able to do this though and he also wanted the Web Part to remember the filtering and sorting options when leaving the page.

I was able to make this work using custom Web Part properties. Here's how to do it:

- First, set up your SPGridvVew and it's datasource as described in Bob's article. When you're done, also add a private string to your Web Part. This string will remember the previous filter.

private string prevFilter = "";

Next, find the following line in the SetupObjectDataSource method:

dataSource.FilterExpression = (string)ViewState["FilterExpression"];

Below this line, add the following line:

prevFilter = dataSource.FilterExpression;

- Your next step is to create two custom properties: one for the filter settings and one for sorting.

private string userSettingsFilter;
private string userSettingsSort;

///


///Hidden property to remember filtering options set by the user
///

[Browsable(false), Category("Advanced"),
DefaultValue(""),
Personalizable(PersonalizationScope.User),
FriendlyName("User Settings Filter"), Description("Hidden user settings for filtering.")]
public string UserSettingsFilter
{
get
{
return userSettingsFilter;
}
set
{
userSettingsFilter = value;
}
}

///
///Hidden property to remember sorting options set by the user
///

[Browsable(false), Category("Advanced"),
DefaultValue(""),
Personalizable(PersonalizationScope.User),
FriendlyName("User Settings Sort"), Description("Hidden user settings for sorting.")]
public string UserSettingsSort
{
get
{
return userSettingsSort;
}
set
{
userSettingsSort = value;
}
}


- In the OnPreRender method, you first check if there is a sort expression set. If this is the case, then save it. Next, check the filter that has been saved to the ViewState. Depending on the column title (is there already a filter on that column or not), save the filter setting to the custom filter property by appending it with either " and " or " or ".

protected override void OnPreRender(EventArgs e)
{
//Check if there is a sort expression set. If this is the case: save it.
if (gridView.SortExpression != "")
{
userSettingsSort = gridView.SortExpression + "&" + gridView.SortDirection;
}

//Get the filter expression into the viewstate. If a filter is set, add it to the saved filter expression.
ViewState["FilterExpression"] = dataSource.FilterExpression;
if (prevFilter != string.Empty && ViewState["FilterExpression"].ToString() == string.Empty)
{
userSettingsFilter = "";
}
if (ViewState["FilterExpression"].ToString() != "")
{
if (userSettingsFilter != null)
{
if (userSettingsFilter != "")
{
//If there already is a filter on a certain column and the user adds another value of this column,
//add it with an "or" expression
if (userSettingsFilter.Contains(ViewState["FilterExpression"].ToString().Substring(0, ViewState["FilterExpression"].ToString().IndexOf('=') + 1)))
{
int tempIndex = userSettingsFilter.IndexOf(ViewState["FilterExpression"].ToString().Substring(0, ViewState["FilterExpression"].ToString().IndexOf('=') + 1));
userSettingsFilter = userSettingsFilter.Insert(tempIndex, "(");
string tempSettings = userSettingsFilter.Substring(tempIndex);
if (tempSettings.Contains(" and "))
{
userSettingsFilter = userSettingsFilter.Insert(tempIndex + tempSettings.IndexOf(" and "), " or " + ViewState["FilterExpression"] + ") ");
}
else
{
userSettingsFilter = userSettingsFilter.Insert(tempIndex + tempSettings.Length, " or " + ViewState["FilterExpression"] + ") ");
}
}
else
{
userSettingsFilter = userSettingsFilter + " and " + ViewState["FilterExpression"].ToString();
}
}
else
{
userSettingsFilter = ViewState["FilterExpression"].ToString();
}
}
else
{
userSettingsFilter = ViewState["FilterExpression"].ToString();
}
}

//Save the changed properties
this.SetPersonalizationDirty();
base.OnPreRender(e);
}


- Next, in the Render method, use the saved filter settings on the datasource's FilterExpression. If a sort setting has been saved, sort the gridview either ascending or descending (depending on the text in the saved setting) on the saved column.

protected override void Render(HtmlTextWriter writer)
{
dataSource.FilterExpression = userSettingsFilter;

if (userSettingsSort != null)
{
if (userSettingsSort != "")
{
if (userSettingsSort.ToLower().Contains("ascending"))
{
gridView.Sort(userSettingsSort.Substring(0, userSettingsSort.IndexOf('&')), SortDirection.Ascending);
}
else
{
gridView.Sort(userSettingsSort.Substring(0, userSettingsSort.IndexOf('&')), SortDirection.Descending);
}
}
}

gridView.DataBind();
base.Render(writer);
}


There are a few disadvantages when using this method though. Your list won't show icons showing which columns are being filtered on and you will only get the option to remove the filter you've last set. Due to this, removing that last filter value, will automatically remove all filters. Also, if you go away from the page and then return, you won't have the option to undo the filter and you will have no indication that there is a filter enabled (except for the fact that not all fields are shown). To remove this filter, you need to set an extra filter and then remove it again, so all filter options are cleared. Another disadvantage is when you set your filter options in a certain way that no data is shown, your heading will disappear and you won't be able to clear the filter anymore, so you'll be stuck with an empty SPGridView forever. To solve this, you can either make the custom filter property visible, so you can edit your Web Part and manually remove the filter, or you can add a button to the Web Part that clears the filter for you.

A better way of implementing this is rendering the view as html (see one of my previous posts). This way, the list will have the same look and feel as a default SharePoint list. I think that this last solution is better in almost every way, but for some situations you might need to use the SPGridView, for example when you need to use a certain datasource (which was the situation I was in, it was impossible for me to display the same data that I got from the datasource) and with this implementation, you're also able to filter on multiple values of one column, which is not possible with the default SharePoint list.

If you have any comments, remarks, possible improvements or questions, just shoot!

Monday, February 25, 2008

Custom Web Part properties not showing

Just a quick note: if you're having trouble getting your custom properties to show when writing your own Web Part, check which class you're inheriting from. If your Web Part inherits System.Web.UI.WebControls.WebParts.WebPart, try changing this into Microsoft.SharePoint.WebPartPages.WebPart and then test your properties again, they may just work now... (if you haven't made any other mistakes of course ;-))

Update: apparently, you can also get this to work when inheriting from System.Web.UI.WebControls.WebParts.WebPart. To do this, change the line "WebPartStorage(Storage.Personal)" (above your properties) to "Personalizable(PersonalizationScope.User)". If you were using "this.SaveProperties = true" to save the changes done to your properties, this won't be available when inheriting from the System.Web.UI.WebControls.WebParts.WebPart class. You can use this.SetPersonalizationDirty() instead.

Thursday, February 21, 2008

Rendering a list programmatically

Today, I was trying to display list data in a Web Part programmatically and have it behave the same way as the default SharePoint lists do. I was given a Web Part that was using an SPGridView to do this, but the filtering in it didn't work the way it was supposed to and it also didn't show which column it was filtered on (as default SharePoint lists do).

Thanks to this post, I was able to render the list the same way default lists get rendered. Here's an example on how to do it for the tasks list, including an example Caml query that sorts the view by title and filters it so that it shows only the incomplete tasks assigned to the current user. Beware though: when using this Caml query, sorting and filtering won't work since it will always render the view by the conditions you have provided in your query. Still working on solving this...

private string html;

protected override void OnPreRender(EventArgs e)
{
using (SPSite mySite = SPContext.Current.Site)
{
using (SPWeb myWeb = mySite.OpenWeb())
{

SPList myList = myWeb.Lists["Tasks"];
SPView myView = myList.Views["All Tasks"];

SPQuery myQuery = new SPQuery(myView);

myQuery.Query = "<OrderBy><FieldRef Name=\"Title\" /></OrderBy><Where><And><Eq><FieldRef Name=\"AssignedTo\" /><Value Type=\"User\"><UserID/></Value></Eq><Neq><FieldRef Name=\"Status\" /><Value Type=\"Choice\">Completed</Value></Neq></And></Where>";

html = myList.RenderAsHtml(myQuery);

this.Title = "Tasks";

}
}
}

protected override void Render(HtmlTextWriter writer)
{

writer.Write(html);

}

Tuesday, January 22, 2008

“This site is not found in the SharePoint configuration database.” when registering a site in Dynamics Ax

At the moment, I am working on integrating MOSS and Dynamics Ax, so you can expect some updates on this topic the next few weeks.

If you want to use the out-of-the-box integration, you can either create SharePoint sites using the Dynamics Enterprise Portal site template, or you can register the site in Ax to be able to use the Dynamics Web Parts in your existing SharePoint site. For my situation, I needed this last option, but when testing this, I regularly got the next error:

Error:
This site is not found in the SharePoint configuration database.


The solution for this problem may or may not be very simple... If you're lucky, just remove the slash at the end of the url and you shouldn't get the error anymore. With other sites, this won't work though and I haven't found the solution to this either, so if you were able to solve it, feel free to let me know how you did it!

Update: Also check your casing. This seemed to work in my situation…

Monday, January 14, 2008

Error: “Cannot find the path specified” when adding role assignments

If you ever get the error “The system cannot find the path specified. (Exception from HRESULT: 0x80070003)” while adding role assignments to a library or web in SharePoint, then this might help you solve your problem… I was trying to add role assignments to a library (using library.RoleAssignments.Add(newRole)), which had already worked perfectly before, but when trying it this time, I got the error I’ve mentioned before. The key to solving my issue was something that didn’t include paths at all: I just forgot to break the role inheritance of my library. So if you ever encounter this error, just add the following lines, they will most probably solve your issue:

library.BreakRoleInheritance(true);
library.Update();

Friday, December 28, 2007

MCTS: .NET Framework 2.0 Web Applications

Yesterday, I passed the 70-528 exam, so yes, I'm a Microsoft Certified Technology Specialist in .NET Framework 2.0 Web Applications now! What a mouthfull... I'm glad I got through it in one time, seeing how I had to study it during the holidays...

If you're taking the exam too and you want some general pointers: both custom controls and personalization were really important topics on my exam. However, don't focus on these two topics too much, since every exam can be completely different... But they did form a large part of my exam, so it won't hurt you to reread these topics an extra time.

Sunday, December 9, 2007

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

If you ever get the error "Attempted to read or write protected memory. This is often an indication that other memory is corrupt.", take a look at the rest of this post, it might save you a lot of time... I got it when writing a console application for SharePoint, but I think it might occur with other sorts of applications too.

I had been looking for a solution for many days and had seen lots of possible causes online, but none of them applied to my situation. Today, I noticed for the first time that Visual Studio gave me a warning when building. I hadn't noticed this before because there weren't any errors, so it just said "build succeeded", so I thought everything in my application was fine and it must have been something about the setup of the test server. Well, I clearly was wrong... Here's what the warning said:

Found conflicts between different versions of the same dependent assembly.

When double clicking this warning, Visual Studio asks you if you want it to fix the errors by adding binding redirect records in the app.config file. If you choose "yes", then Visual Studio adds some lines to the app.config file. Here's what it added for me:

<runtime>

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

<dependentAssembly>

<assemblyIdentity name="Microsoft.SharePoint.Library" publicKeyToken="71E9BCE111E9429C" culture="neutral"/>

<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0"/>

</dependentAssembly>

<dependentAssembly>

<assemblyIdentity name="Microsoft.SharePoint" publicKeyToken="71E9BCE111E9429C" culture="neutral"/>

<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0"/>

</dependentAssembly>

<dependentAssembly>

<assemblyIdentity name="Microsoft.SharePoint.Security" publicKeyToken="71E9BCE111E9429C" culture="neutral"/>

<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0"/>

</dependentAssembly>

<dependentAssembly>

<assemblyIdentity name="Microsoft.SharePoint.Dsp" publicKeyToken="71E9BCE111E9429C" culture="neutral"/>

<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0"/>

</dependentAssembly>

</assemblyBinding>

</runtime>


Now just rebuild your project and try running it again... Everything should be working fine now!

Monday, November 26, 2007

Enabling the language switch in SharePoint

Today someone at work pointed out that there was a switch control in SharePoint that allows users to easily switch between the different languages a page is available in. I hadn't heard of this already, so if you haven't either, now you've got no more excuses to say you don't!

Now what exactly does it do? Quite easy... If variations are enabled and there's a version of a certain page available in a different language, a switch will show up to easily switch between the different versions of the page. If your page only exists in English for example, the switch control won't show up.

But most importantly... How do you enable it? Well, that's easy too. All you need to do is follow some simple steps:

- Go to the ...\12\TEMPLATE\CONTROLTEMPLATES folder and open the VariationsLabelMenu.ascx file.

- Look for the line that's commented out. Remove the comment tags to enable the control. Here's the line you should comment out:

<cms:VariationsLabelEcbMenu id ="varlabelmenu1" DataSourceID="LabelMenuDataSource" DisplayText="<%$Resources:cms,VariationLabelMenuTitle%>" IsCallbackMode="true" runat="server" />

- Now go to the Master Page you want to include the control in and make sure that it has the following tag at the top:

<%@ Register TagPrefix="PublishingVariations" TagName="VariationsLabelMenu" src="~/_controltemplates/VariationsLabelMenu.ascx" %>

If this tag is on your Master Page and you've commented out the control, then the switch control should be showing right beside the links in the upper right corner of the Master Page. Now you can use this control and put it where ever you want to. If you want, you can also use it like this:

<PublishingVariations:VariationsLabelMenu id="labelmenu1" runat="server"/>

Easy as that!

Thursday, November 8, 2007

Cannot update. Database or object is read-only.

In Access 2007 you have a great feature that can copy data from or to a SharePoint list, but the error messages could have been better...

While copying a whole bunch of data, I got the following error:

Cannot update. Database or object is read-only.

Very vague... Looking at the record that caused the error (another great feature of the new Access, it shows which records couldn't be copied) I saw nothing strange. When googling for the error message, I even found a page telling that this was a known issue with a Hotfix for it, so I first thought that there was no way for me to solve this error quickly. When taking another look at the record though, I noticed a multiline field I overlooked. At the end of this field, there was a strange sign (a question mark in a little square), which was obviously causing the error...

So, long story short, if you ever run into this error, just check your data (all of it, don't be like me...) and try copying field by field to determine the field with the "illegal" character.

Wednesday, November 7, 2007

Files uploaded programmatically don't work for all users

Files uploaded programmatically don't work for all users

Today I encountered a problem when uploading images in a feature. Everything seemed to work fine, the images (used in my master page) appeared nicely where they should be, but then I logged in as another user and it just showed me the typical red x's. I noticed that the images were still checked out to the Administrator, so I checked them in programmatically and... still nothing. At first I thought they were not visible due to some kind of permissions problem, but the real solution was that you also need to publish a major version of the file. So if you ever upload files through code, do not forget these lines:

//Upload the new file.
SPFile addedFile = destinationFolder.Files.Add(fileName, fileToAdd.Open(FileMode.Open));

//Check in the newly created file and then publish a major version of it.
addedFile.CheckIn("File checked in by activating FodCustomLayout feature.");
addedFile.Publish("File published by activating FodCustomLayout feature.");

Monday, October 15, 2007

Automatically applying a master page upon activating it as a feature

Today, I was facing the problem of making all My Sites use the same master page. I figured this would be possible through feature stapling, by providing the master page to every newly created My Site. The problem here was that it did copy the master page to the master page gallery, but it did not selected it yet. To do this, I found a great solution thanks to Paul Papanek Stork.

If you want the complete explenation, you can read it all through on his blog. The key I needed was the following bit of code which will fire when activating or deactivating the feature:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

namespace CustomMaster
{
public class ChangeMaster:Microsoft.SharePoint.SPFeatureReceiver
{
public override void FeatureInstalled (SPFeatureReceiverProperties properties)
{
}

public override void FeatureUninstalling (SPFeatureReceiverProperties properties)
{
}

public override void FeatureActivated (SPFeatureReceiverProperties properties)
{
SPWeb CurrentWeb = properties.Feature.Parent as SPWeb;
CurrentWeb.MasterUrl = "/_catalogs/masterpage/MyDefault.master";
CurrentWeb.CustomMasterUrl = "/_catalogs/masterpage/custom.master";
CurrentWeb.Update();
}

public override void FeatureDeactivating (SPFeatureReceiverProperties properties)
{
SPWeb CurrentWeb = properties.Feature.Parent as SPWeb;
CurrentWeb.MasterUrl = "/_catalogs/masterpage/default.master";
CurrentWeb.CustomMasterUrl = "/_catalogs/masterpage/default.master";
CurrentWeb.Update();
}
}
}

Just build the feature as Paul describes it, include your own custom master page in the solution and then write a stapling feature that binds this master page feature to the "SPSITE#0" template. Every new My Site will now automatically use your custom master page. Of course, this will also work for every other site template and if you also want the whole portal to automatically use this master page, then use the "GLOBAL" template when stapling your feature.

Tuesday, October 2, 2007

Error: The type or namespace name 'Publishing' does not exist

Here's something useful I've learned today...

I had created a custom master file and included this line:

<img runat="server" src="<% $SPUrl:~SiteCollection/Style Library/Images/~language/LocalLogo.JPG%>" alt="Logo"/>

This line had to provide a localized site logo for a MOSS 2007 site. When I tried using the master page, I got the following error:

An error occurred during the compilation of the requested file, or one of its dependencies. The type or namespace name 'Publishing' does not exist in the namespace 'Microsoft.SharePoint' (are you missing an assembly reference?)

After quite a long search (at least for such a "small" problem), I finally found the solution on Clever Workarounds. Apparently, the error was that I had used the Default.master file to start my custom page from. This masterpage doesn't support Publishing by default though (as opposed to Blueband.master for example), so you have to add these following assembly declarations to your custom master page:

<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="PublishingVariations" TagName="VariationsLabelMenu" src="~/_controltemplates/VariationsLabelMenu.ascx" %>
<%@ Register Tagprefix="PublishingConsole" TagName="Console" src="~/_controltemplates/PublishingConsole.ascx" %>
<%@ Register TagPrefix="PublishingSiteAction" TagName="SiteActionMenu" src="~/_controltemplates/PublishingActionMenu.ascx" %>


That should do the job...