Building Web Application Projects with CCNET
19 June 06 03:53 PM | madsn | 9 comment(s)
One of the things about Visual Studio 2005 I disapprove of the most is the way features related to your project are stuffed into the local devmachine environment under Program Files. Among other things the coming GAT (guidance automation toolkits) work this way, and so does the new bootstrapper packages. There are many drawbacks to this strategy, and they all become apparent when you decide to do distributed development against a common build environment.

So, when you're starting up with your Web Application Project (WAP) and install the MSI plugin for Visual Studio 2005 you pretty much know that this will break on the buildserver, or your fellow developers machine if the same MSI is not installed on that client. Most likely the other client (CCNET or another dev) will get a taste of this error:

error MSB4019: The imported project "C:\Program Files\MSBuild\Microsoft\VisualStudio\v8.0\WebApplications\Microsoft.WebApplication.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.

Of course you could select the dictator strategy and enforce distributed installation of the WAP MSI installer, but I tend to prefer to keep my project completely packaged.

First copy the targets file to your solution directory and add it as a solution item so it is included in sourcecontrol. Then unload your WAP by right clicking in the Solution Explorer and then select "Edit MyWAP.csproj" to edit the MSBuild markup. Change this line:

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v8.0\WebApplications\Microsoft.WebApplication.targets" />

To this:

<Import Project="$(SolutionDir)\Microsoft.WebApplication.targets" />


Now save your csproj file and reload the project by right-clicking the unloaded project node in the solution explorer. This will give you a security warning (beware! you've edited your own build file) that you can safely ignore.

Now, Visual Studio automatically feeds MSBuild with the SolutionDir property so this will load in your IDE, but if you're going to call MSBuild from another tool, like NAnt, make sure you pass in a value for SolutionDir when executing msbuild.exe.

I know the WAP thing is reused across multiple projects and it is a good thing to have the templates in the "Microsoft Visual Studio 8" folder for easy access, but it does create a lot of work for us that want to keep our projects buildable in multimachine environments.

More VSTO Deployment - AppCheck and PIACheck compiled
03 May 06 12:46 PM | madsn | 1 comment(s)
A while ago Microsoft released a walkthrough for VSTO deployment to, as Misha puts it; "not remove the pain but help controlling it". The walkthrough covers all my (less structured) instructions, and improves on security aspects.

However the article does provide you with a fair amount of work. The security part adds strong-name based evidence for assemblies which is absolutely required from a security perspective. However, the samples does not support multi-assembly deployments so you'll need to write this up yourself. I'll look into using Assembly.GetReferencedAssemblies() later on to see if that will solve this problem.

Furthermore you get native checking utils for common prereqs. These are not compiled however and Aerodrome has posted a compiled version. Although these exe's (PIACheck.exe and AppCheck.exe) works nicely if you've got the .NET Framework preinstalled, they will fail if not. Probably because they're compiled in managed C++, and not the way instructed in the MSDN article. We've made a working set available here (courtesy of compilation genious Øystein Garshol).

Finally I have to point out once more the weak design of the prerequisite package structure. The prerequisite definitions are fundamentally a part of any solution and should at least be copied into the solution or setup project filestructure when used to enable inclusion in sourcecontrol and automated builds. I would sure like to know how Microsoft approaches this problem using WiX or other tools.

And to all of you who haven't deleted my blog from your blogroll: I'm back:-)
Filed under:
MSCRM 3.0 and the VSTO for MSCRM 3.0 Toolkit
03 December 05 02:52 PM | madsn | with no comments
Read about the upcoming VSTO Toolkit for MSCRM 3.0 at Kjell-Sverres blog. Beeing Objectwares MSCRM guru KSJ also have started writing about general 3.0 topics. Keep watching if you're in the MSCRM space.
Filed under:
Validating Sharepoint filenames on upload
17 November 05 09:28 AM | madsn | 4 comment(s)

Some characters are legal for FAT or NTFS files but illegal for files in Sharepoint documentlibraries. Using the regular upload UI for Sharepoint you'll encounter a rather unpleasant validation error when uploading a file with funky characters. Using the object model will give you a good ole' exception.

I've seen a lot of approaches to solving this problem, and implemented a couple myself. Last night I noticed a method on the SPEncode static class that enabled a clean approach to the problem (updated for double punctuation mark problem):

private string CleanForUrlAndFileNameUse(string dirtyFileName)

{

    char[] dirtyChars = dirtyFileName.ToCharArray();

    foreach (char c in dirtyChars)

    {

        if (!SPEncode.IsLegalCharInUrl(c))

        {

            dirtyFileName = dirtyFileName.Replace(c.ToString(), "");

        }

    }

   

    // Spaces are not appreciated

    dirtyFileName = dirtyFileName.Replace(" ", "");

   

    // double punctuation marks causes validation error

    while(dirtyFileName.IndexOf("..") != -1)

        dirtyFileName = dirtyFileName.Replace("..", ".");

 

    return dirtyFileName; // now clean:-)

}

Filed under:
Refresh page after edit in InfoPath
01 November 05 10:26 AM | madsn | with no comments

When using InfoPath for editing data in you webapplications you run into a couple of tricky situations when beeing used to plain webdevelopment. One of these scenarios is when you want your webpage to refresh after having edited a record in some datasource with InfoPath. Typically you've implemented a view webpage and edit with InfoPath.

This sounds pretty uncomplicated, and I guess it is, but still it took me quite a while to figure out.

My first attempt was to use standard web techniques like opening a browser window pointing to the infopath form modally with showModalDialog and refreshing the hostpage on return. No luck. I was thinking about creating a custom hostpage utilizing iframes to open the document much like a lot of people do to handle pdfs, but I really don't like adhoc host'ish pages in my solution.

I also considered going all the way and serve my Infopath documents dynamically, but that would have been to big of a change to my current solution.

I went back and studied how the Sharepoint SDK suggests to solve this problem. Sharepoint makes use of a clientside component (INLAUNCH.DLL) to open InfoPath documents. The control is called OpenXMLDocuments. From the documentation I adopted the RefreshOnFocus approach from the Create sample and tried to modify the Edit sample with it to get the refresh. Problem was that InfoPath slightly lost focus during load of the XML document which forced a too early refresh of the hostpage when using the EditDocument2 method directly.

Then I read Westins blog and got the idea to se exactly what Sharepoint does on the "Edit in Microsoft Office Infopath" button. Westin also describes how to launch an InfoPath form from a Sharepoint page but this only includes create which seems to work slightly different from edit.

Adopting the call from the "Edit in Microsoft Office Infopath" button and combining it with the RefreshOnFocus approach lead me to the following working solution for refreshing a webpage after editing in InfoPath.

<script language="javascript">

function RefreshOnFocus()

{

   window.location.href = window.location;

}

 

function showInfopathAndRefreshWhenFinished(url)

{

      window.onfocus = RefreshOnFocus;

      editDocumentWithProgID2(url,'InfoPath.Document','SharePoint.OpenXMLDocuments');

}

</script>

Be aware that the clientside control must be present for this to work, but I assume it is installed with InfoPath (anyone care to assert this assumption regarding INLAUNCH.DLL?). The editDocumentWithProgID2 method is defined in the standard Sharepoint script includes.

Complete set of Custom Prerequisites available
26 October 05 07:18 PM | madsn | 1 comment(s)

Seems like VSTO Outlook deployment just got easier. I now have Custom Prerequisites for Office 2003 (SP2), Outlook 2003 (SP2), Office 2003 PIA and the Visual Studio Tools for Office 2005 Runtime ready.

I got some feedback from the community on my previous post and I've updated my article with my findings and also a download for the prerequisite definitions for Visual Studio 2005.

VSTO Outlook: the Complete Setup Solution

Please provide feedback on this if you find bugs or points of improvement.

VSTO Outlook: The complete deplyment solution
14 October 05 06:30 PM | madsn | 1 comment(s)

This week has been all about the pain of client deployment of our VSTO solution. I've written a summary which I hope will be the start of the complete solution for idiot-proof installs of Outlook plugins. I'm not quite there yet but I feel I'm pretty close.

Read it here!

Input and feedback is very welcome!

Also check out Peters blog for VSTO content and the MSDN VSTO Forums.

Recycle Bin / Undelete for Document Libraries finally solved
07 October 05 10:03 AM | madsn | 4 comment(s)

Last week I met Todd from Mindsharp in Seattle and he told me that he'd just created a purely client side solution to the whole undelete / recycle bin problem with Sharepoint document libraries. Several solutions have been posted to address this issue in the past, amongst others Max and Erics article in MSDN Mag.

The beauty of Todds solution is it simplicity. Todd has solved everything from the clientside and wrapped it all up in a list template (.stp) file.

In addition to supply undelete features (and even right-shift key integration!!) he also adds the bonus of multitemplate document creation.

Giant kudos to Todd for this excellent piece of Sharepoint/javascript voodoomagic. In my opinion this is the final solution for undelete until WSS v3 arrives.

Get it here!

Filed under:
InfoPath DropDown List doesn't match on items in list
06 October 05 02:30 PM | madsn | 1 comment(s)

When populating InfoPath DropDown lists with values and then binding the dropdown to a field in your main datasource you'll sometimes see the bound value instead of its corresponding text value in the dropdown on load. It seems slightly confusing given that the entry containing your bound value is actually in the list, but it doesn't match.

Most likely this is a string casing problem. I've got a dropdown bound to a Sharepoint (WSS) UserGroup service and bind the value to @LoginName. These values are typically DOMAIN\login.name. My main datasource however, lowercases all reference variables to avoid any confusion. Since I don't control the formatting done by the Sharepoint service my best bet is to lowercase the values of the @LoginName attributes in my secondary datasource.

First off I got excited by this post on the InfoPath Blog which feels a bit like calling C# or javascript code from regular XSLT transformations. The blog describes calling code to evaluate conditions, not setting DOM values, and furthermore there are no events you can respond to in order to call the code that will manipulate your secondary datasource values and setting them toLower.

Since I query my UserGroup service on form load, and this occurs before the Load event in script I was able to perform toLower (or more precicely toLowerCase()) in the load event like this:

var objXMLNodes = XDocument.GetDOM("UserService").selectNodes(//@LoginName);
for(i = 0; i < objXMLNodes.length; i++)
{
      objXMLNodes(i).text = objXMLNodes(i).text.toLowerCase();
}

Filed under:
WebBrowser control and InvokeScript
23 September 05 10:24 AM | madsn | 3 comment(s)

This is a little code nugget I planned to blog about a while ago, but forgot, so my recap and reasoning is not a 100% but hopefully it might be of help to someone struggelig with making the System.Windows.Forms.WebBrowser control execute scripts programatically.

I started out using the WebBrowser.InvokeScript method to force execution of clientside scripts in the page that was loaded in my WebBrowser control:

HtmlWindow win = doc.Window;
doc.InvokeScript(
"Update_UI_From_Values();");

This is where my reasoning is not complete (because i didn't bother to actually reproduce the original problem..), but basically it didn't work as expected. After some Reflector action I figured out that the mshtml.IHTMLWindow2.execScript method wasn't actually directly mapped into the new WebBrowser APIs and I figured I'd give it a go and add a reference to Microsoft.mshtml.dll (COM component Microsoft HTML Object Library):

mshtml.IHTMLWindow2 win = (mshtml.IHTMLWindow2)propertyEditorWebBrowser.Document.Window.DomWindow;
win.execScript(
"Update_UI_From_Values();", "javascript");

This had the desired effect and the script executed. Too bad the managed option didn't do the job.

Filed under:
More Posts Next page »

This Blog

<a name="MyWork">!My work!</a>

Funstuff

Goodies

MSCRM Blogs

Sharepoint

Useful reading

Weblogs

Syndication