Wednesday, August 26, 2009

WSPBuilder – Adding multiple web parts to a single feature

One small limitation I found with WSPBuilder is that there is not a default way of adding new web parts to existing features. Fortunately, there is an easy way to do this.

1. Create a WSPBuilder project in Visual Studio.

2. Add a new WSPBuilder Web Part Feature to the project. (Ctrl+Shift+A)

image 

3. Now add another Web Part Feature. At this point, your project should look something like this:

image

4. Drag the web part you want combined into the parent folder of the other feature and then delete the feature folder from where you dragged it.

image

5. Open the elements.xml file and add another File node with the information from the second web part.

<?xml version="1.0" encoding="utf-8" ?>
<
Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<
Module Name="WebPartPopulation" Url="_catalogs/wp" RootWebOnly="TRUE">
<
File Url="WebPartFeature1.webpart" Type="GhostableInLibrary">
<
Property Name="Group" Value="MyGroup"></Property>
<
Property Name="QuickAddGroups" Value="MyGroup" />
</
File>
<
File Url="WebPartFeature2.webpart" Type="GhostableInLibrary">
<
Property Name="Group" Value="MyGroup"></Property
>
<
Property Name="QuickAddGroups" Value="MyGroup"
/>
</
File
>
</
Module>
</
Elements>







6. Open the feature.xml file and add an ElementFile entry for the second web part.



<?xml version="1.0" encoding="utf-8"?>
<
Feature Id="a942cf13-5e3b-48e6-9862-6f98dc931375"
Title="WebPartFeature1"
Description="Description for WebPartFeature1"
Version="12.0.0.0"
Hidden="FALSE"
Scope="Web"
DefaultResourceFile="core"
xmlns="http://schemas.microsoft.com/sharepoint/">
<
ElementManifests>
<
ElementManifest Location="elements.xml"/>
<
ElementFile Location="WebPartFeature1.webpart" />
<
ElementFile Location="WebPartFeature2.webpart" />
</
ElementManifests>
</
Feature>

7. You can now build the project (Right-click Project –> Build), build the WSP (Right-click Project –> WSPBuilder –> Build WSP) and deploy the feature (Right-click Project –> WSPBuilder –> Deploy). If you look in hive under TEMPLATE\Features\YourFeature, you will see that both web parts are there.

I’m digging WSPBuilder for SharePoint project deployment!

After playing around with this tool for a couple days, I’m think I’m sold on it. I’m going to start refactoring all of my SharePoint projects this way. Once you get it figured out, it makes deployment and migration way easier. The nice thing is that it uses standard SharePoint Feature deployment concepts. I also see a lot of recent builds on CodePlex and blogposts, so I think it is pretty well supported unlike other tools I found like STSDEV or SPDeploy.

I included a sample project I made, but you will need to install the WSP Builder Extensions download. Of course, it will need to be on a machine that has SharePoint and Visual Studio. Of course, hopefully this will all be a moot point with SharePoint 2010.

Download

http://www.codeplex.com/wspbuilder

Documentation

http://keutmann.blogspot.com/2009/04/wspbuilder-documentation.html

DEV Screenshot

clip_image001

clip_image002

Once you are done, you can easily create a setup application! It makes remote deployment much easier as you just have a setup.exe.

image

image

image

image

You do have to perform this one step, but maybe there’s some way to automate this. Go to http://[portalname]/_layouts/newdwp.aspx

image

That’s it. Web parts are ready to be added.

image

Notes:

Changing to a Web Application

The first thing I did was change it from a class library to a web application project. That way I could debug it using user controls in VS before deploying. The link will provide further instructions. Basically the ascx page needs to inherit from the assembly, not the code behind.

1.)           Install WSPBuilder.

2.)           Open Visual Studio and create a WSPBuilder Project.

3.)           Right-click on the project and click “Unload Project”.

4.)           Using notepad, open the csproj file for the project, and replace the following:

<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

5.)           Save the file.

6.)           Right-click the project from within Visual Studio and click “Reload Project”.

There are a few gotchas that I found when doing this. One is that debugging doesn’t work right away and you must create a new entry in the Configuration Manager for it to work. You can can access this from the Build menu in Visual Studio. I don’t really know why this is the case, but it works and you can even change your configuration back to Debug or Release when you are done. Another is that if you want to debug and have installed it on that server you are going to want to uninstall (right-click project –> WSPBuilder –> Uninstall). Then rebuild it before your hit F5. You also will need to toggle between the following control directives. I’m sure there’s a better way to do this.

Using User Controls (ascx) for Development

Loading a User Control from your web part code

The key here is to update your web part class CreateChildControls method to look something like this:

base.CreateChildControls();
UserControl uc = (UserControl)Page.LoadControl(@"~/_controltemplates/WspUserControl1.ascx");
this.Controls.Add(uc);


Control Directives for debugging and deployment



During Debugging



<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="WSPBuilderProject1.WspUserControl1" %>



Before Deployed



<%@ Control Language="C#" AutoEventWireup="true" Inherits="WSPBuilderProject1.WspUserControl1, WSPBuilderProject1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fa545836959293eb" %>



WSPBuilder Links



Wrap a User Control inside a Web Part using WSPBuilder



http://oidatsmyleg.wordpress.com/2009/07/23/wrap-a-user-control-inside-a-web-part-using-wspbuilder/



Wrap a User Control inside a Web Part using WSPBuilder



http://oidatsmyleg.wordpress.com/2009/07/23/wrap-a-user-control-inside-a-web-part-using-wspbuilder/



WSS: Development – Quick Start with WSPBuilder



http://rasor.wordpress.com/2008/10/12/wss-dev-quickstart/



WSS Dev: HowTo debug a WebPart



http://rasor.wordpress.com/2008/10/24/wss-howto-debug-a-webpart/

Thursday, August 6, 2009

Compare the content of two files

Use the following snippet:

using System.IO;

. . .


private static bool FileCompare(string filePath1, string filePath2)
{
    int file1byte;
    int file2byte;
    FileStream fileStream1;
    FileStream fileStream2;
    if (filePath1 == filePath2)
    {
        return true;
    }
    FileInfo fileInfo = new FileInfo(filePath1);
    fileInfo.IsReadOnly = false;
    fileInfo = new FileInfo(filePath2);
    fileInfo.IsReadOnly = false;
    fileStream1 = new FileStream(filePath1, FileMode.Open);
    fileStream2 = new FileStream(filePath2, FileMode.Open);
    if (fileStream1.Length != fileStream2.Length)
    {
        fileStream1.Close();
        fileStream2.Close();
        return false;
    }
    do
   
{
        file1byte = fileStream1.ReadByte();
        file2byte = fileStream2.ReadByte();
    }
    while ((file1byte == file2byte) && (file1byte != -1));
    fileStream1.Close();
    fileStream2.Close();
    return ((file1byte - file2byte) == 0);
}

 

Kill a Windows Service or Process Thread

Use one of the following code snippets:

using System.Diagnostics;

. . .

private static void KillProcessThread(string processName)
{
    Process[] processes;
    processes = Process.GetProcessesByName(processName);
    foreach(Process proc in processes)
    {
        proc.Kill();
    }
}

 

using System.ServiceProcess;

. . .

private static void KillWindowsService(string serviceName)
{
        ServiceController sc = new ServiceController();
        sc.ServiceName = serviceName;
        sc.Stop();
}

Get a recursive list of files in a given directory

Here is a simple code snippet:

using System.Collections.Generic;
usingSystem.IO;
usingSystem.Text;

. . .

static void Main(string[] args)
{
List<FileSystemInfo> fileInfo = GetDirectoryInfo(@"c:\temp", true);
StringBuilder directoryCsv = new StringBuilder();
directoryCsv.Append("Name,Full Name,Parent Folder,Creation Time,Last Access Time,Last Write Time,Length, Extension,Attributes\r\n");
foreach (FileSystemInfo di in fileInfo)
{
if (di.GetType() == typeof(FileInfo))
{
directoryCsv.Append(string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8}\r\n", di.Name, di.FullName, di.FullName.Replace(di.Name, ""), di.CreationTime, di.LastAccessTime, di.LastWriteTime, ((FileInfo)di).Length.ToString(), di.Extension, di.Attributes.ToString()));
}
else
{
directoryCsv.Append(string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8}\r\n", di.Name, di.FullName, di.FullName.Replace(di.Name, ""), di.CreationTime, di.LastAccessTime, di.LastWriteTime, "", "", di.Attributes.ToString()));
}
}
SaveCsvFile(directoryCsv.ToString(), @"c:\temp\test.csv");
}
public List<FileSystemInfo> GetDirectoryInfo(string directoryPath, bool includeDirectoryInfo)
{
List<FileSystemInfo> directoryList = new List<FileSystemInfo>();
DirectoryInfo di = new DirectoryInfo(directoryPath);
foreach (DirectoryInfo childDi in di.GetDirectories())
{
directoryList.AddRange(GetDirectoryInfo(childDi.FullName, includeDirectoryInfo));
}
if (includeDirectoryInfo)
{
directoryList.Add(di);
}
directoryList.AddRange(GetFileInfo(directoryPath));
return directoryList;
}

public List<FileSystemInfo> GetFileInfo(string filePath)
{
DirectoryInfo di = new DirectoryInfo(filePath);
FileInfo[] rgFiles = di.GetFiles("*.*");
List<FileSystemInfo> fileList = new List<FileSystemInfo>();
foreach (FileInfo fi in rgFiles)
{
fileList.Add(fi);
}
return fileList;
}
private void SaveCsvFile(string fileText, string fileName)
{
try
{
Stream myStream;
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.Filter = "CSV Files (*.csv)|*.csv";
saveFileDialog1.FileName = fileName + ".csv";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
if ((myStream = saveFileDialog1.OpenFile()) != null)
{
StreamWriter wText = new StreamWriter(myStream);
wText.Write(fileText);
wText.Close();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
 

Update:  I found a nice solution using LINQ for filtering on multiple file extensions.

http://msdn.microsoft.com/en-us/library/wz42302f.aspx

using System.Linq;

string folder = @"C:\Projects";

IEnumerable<string> fileNames =  Directory.GetFiles(folder).Where(f => f.EndsWith( ".vb" ) || f.EndsWith( ".xml" ) || f.EndsWith( ".aspx" ));

I have a whopping 693 programs on my XP development machine. How many do you have?

You can simply find out by creating a C# console application, cutting the following, and hitting F5.

using System;
using System.Collections.Generic;
using Microsoft.Win32;


. . .


static void Main(string[] args)
{
List<string> programs = GetInstalledPrograms();
foreach (string program in programs)
{
Console.WriteLine(program);
}
Console.WriteLine("Total - " + programs.Count.ToString());
Console.Read();
}

private static List<string> GetInstalledPrograms()
{
string installKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
List<string> displayNames = new List<string>();
string[] subkeyNames = Registry.LocalMachine.OpenSubKey(installKey).GetSubKeyNames();
foreach (string subkeyName in subkeyNames)
{
string displayName = (string)Registry.LocalMachine.OpenSubKey(installKey + "\\" + subkeyName).GetValue("DisplayName");
if (!string.IsNullOrEmpty(displayName))
{
displayName += "\t" + subkeyName;
}
else
{
displayName = subkeyName;
}
displayNames.Add(displayName);
}
displayNames.Sort();
return displayNames;
}