In this article, I will show you how to use SharpShell to quickly create Shell Property Sheet extensions. These are extensions that add extra pages to the property sheets shown for shell items such as files, network shares, folders and so on. Here's a screenshot of what we'll create in the tutorial.
Above: A screenshot of a Shell Property Sheet Extension. This sample adds a 'File Times' page that lets the file times for a file be modified.
The SeriesThis article is part of the series '.NET Shell Extensions', which includes:
Technically, a Shell Property Sheet extensions is a COM server that exports an object that implements the interface IShellPropSheetExt. This extension lets us do two things - add property pages to a shell property sheet, or replace them.
Now, typically working with old-fashioned APIs like this can be quite difficult when you're in the managed world. You need to do a lot of interop, you need to import functions from the Win32 APIs to create property pages, create message pumps and so on. We're going to use the SharpShell
library to do all of the plumbing for us, leaving us with the much easier task of simply creating property pages and adding them to our extension class.
Now that we know what a Property Sheet Extension is, we can get started implementing one.
Step 1: Creating the ProjectFirst, create a new C# Class Library project.
Tip: You can use Visual Basic rather than C# - in this article, the source code is C# but the method for creating a Visual Basic Shell Extension is just the same.
In this example, we'll call the project 'FileTimesPropertySheet
'.
Now add the following references:
System.Windows.Forms
System.Drawing
Rename 'Class1.cs' to 'FileTimesPropertySheet.cs'.
Step 2: Referencing SharpShellWe now need to add a reference to the core SharpShell
library. You can do that in a few different ways:
If you have Nuget installed, just do a quick search for SharpShell and install it directly - or get the package details at https://www.nuget.org/packages/SharpShell. This is the best way - you'll always get the latest version of the library and it'll add any dependencies automatically.
Add ReferenceDownload the 'SharpShell Core Library' zip file at the top of the article and add a reference to the downloaded SharpShell.dll file.
Use CodePlexTip: The download on this article is correct at the time of writing - if you need the latest version, use Nuget (as described below) or get the library from sharpshell.codeplex.com.
Rather than getting the library from this page, which may not be the latest version, you can always get the very latest version of the library from CodePlex - on the SharpShell home page which is sharpshell.codeplex.com. Nuget will always have the latest stable version - CodePlex may have betas available, and the Code Project articles will have the version that was available at the time of writing.
Step 3: Derive from SharpPropertySheetWe must have a class that derives from SharpPropertySheet - this will be the main extension class. Let's take the file we named 'FileTimesPropertySheet
' and derive from SharpPropertySheet
now:
public class FileTimesPropertySheet : SharpPropertySheet
Now we're going to have to create implementations of two abstract functions:
CanShowSheetThis function returns a bool
. If the result is true
, then the property sheet pages we create are going to be shown, otherwise we won't show them. Here's our implementation:
protected override bool CanShowSheet() { // We will only show the resources pages if we have ONE file selected return SelectedItemPaths.Count() == 1; }
Because we're derived from SharpPropertySheet
, we have a property called 'SelectedItemPaths
' - this is an enumerable set of string
s that store the paths selected by the user when the sheet was invoked. In our example, we're only going to show the pages if we have just one file selected.
This function returns a set of SharpPropertyPage
objects - these are the actual property pages we're going to add to the shell property sheet.
Here's how our implementations will look:
protected override IEnumerable<SharpPropertyPage> CreatePages() { // Create the property sheet page var page = new FileTimesPropertyPage(); // Return the pages we've created return new[] {page}; }
This isn't going to work until we create the 'FileTimesPropertyPage
' class, we'll do that soon!
We need to define what shell objects this extension is going to be used for. This is done by decorating the extensions with the COMServerAssociation
attribute. Here's how we can apply this extension will all files.
[COMServerAssociation(AssociationType.AllFiles)] public class FileTimesPropertySheet : SharpPropertySheet
There are a number of different types of associations we can make - we can associate with files, folders, files with specific extensions and so on. There's a more complete guide here on the page COM Server Associations.
Step 5: Create the Property Sheet PageYou can follow this step as many times as you need - one property sheet extension can add any number of pages.
First, add a UserControl
to your class library, call it 'FileTimesPropertyPage
'. Now open up the code-behind for the control and change the parent class from 'UserControl
' to 'SharpPropertyPage
'.
Here's the first thing we can do - set the title of the property page:
/// <summary> /// The FileTimesPropertyPage class /// </summary> public partial class FileTimesPropertyPage : SharpPropertyPage { /// <summary> /// Initializes a new instance of the <see cref="FileTimesPropertyPage"/> class /// </summary> public FileTimesPropertyPage() { InitializeComponent(); // Set the page title PageTitle = "File Times"; // Note: You can also set the icon to be used: // PageIcon = Properties.Resources.SomeIcon; }
We set the title of the page by setting the PageTitle
property. We can also optionally set the icon for the page by setting the PageIcon
property.
Now there are a bunch of virtual functions in the SharpPropertyPage
that we can override if we choose to. Here, I'll go through the key ones.
This function is called when the page has been created and is about to be displayed to the user. If a property sheet is shown, but the user doesn't click on this particular page, then this function won't be called. Any initialization should be done here. In this function, you are provided with the parent SharpPropertySheet
object, which contains the selected file paths.
private string filePath; /// <summary> /// Called when the page is initialized /// </summary> /// <param name="parent">The parent property sheet.</param> protected override void OnPropertyPageInitialised(SharpPropertySheet parent) { // Store the file path filePath = parent.SelectedItemPaths.First(); // Load the file times into the dialog LoadFileTimes(); }
In our example, we store the selected file path and then update the UI from the file times (through the LoadFileTimes
function).
What other functions should we implement? Well, almost always, we'll want to handle 'OK' and 'Apply':
OnPropertySheetOK and OnPropertySheetApply/// <summary> /// Called when apply is pressed on the property sheet, or the property /// sheet is dismissed with the OK button /// </summary> protected override void OnPropertySheetApply() { // Save the changes SaveFileTimes(); } /// <summary> /// Called when OK is pressed on the property sheet /// </summary> protected override void OnPropertySheetOK() { // Save the changes SaveFileTimes(); }
Nothing too advanced here - in this class, we're just going to save any changes the user has made.
Property Sheet Pages Quick ReferenceHere's a quick reference for property sheet pages.
Function UsageOnPropertyPageInitialised
Called when the page must be initialized. OnPropertyPageSetActive
Called when the page is about to be presented to the user. This is always called after OnPropertyPageInitialised
. OnPropertyPageKillActive
Called when the page is about to be deselected. OnPropertySheetApply
Called when the Apply button is pressed. OnPropertySheetOK
Called when the OK button is pressed. OnPropertySheetCancel
Called when the Cancel button is pressed. OnPropertySheetClose
Called when the cross at the top right of the dialog is pressed. SetPageDataChanged
Call this function to enable the 'Apply' button. Step 6: Expose to COM
We're going to need to do a few things now to allow our class to be created by the shell.
First, we must add the COMVisible
attribute to our class:
[ComVisible(true)] [COMServerAssociation(AssociationType.AllFiles)] public class FileTimesPropertySheet : SharpPropertySheet
Now we must sign the assembly. To do this, right click on the project and choose 'Properties'. Then go to 'Signing'. Choose 'Sign the Assembly', specify 'New' for the key and choose a key name. You can password project the key if you want to, but it is not required:
Debugging the Shell ExtensionThe Shell Extension can be debugged in the following way:
See the article Shell Context Menus for a guide on how to register SharpShell extensions.
HistoryRetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4