Local Folder sample

Source repository: https://github.com/aelyo-softworks/ShellBoost-Samples

A Console application shell folder that supports a combination of virtual shell items and physical shell items. It demonstrates custom properties, overlays icons, and icon properties. This is how the project is laid out:

Local Folder sample - Picture 50

The “Data” directory contains a bunch of physical sample files that we’ll use as a built-in hierarchy when we’ll run the project (they’re all configured with a Visual Studio Build Action set to “Copy if newer”).

The “Resources” directory contain two overlay icons, one shell item icon and one custom property .propdesc schema file (they’re all configured with a Visual Studio Build Action set to “Embedded resource”).

Since the project contains custom properties, it must be ran at least once (for a production program, it would be at install time) using admin rights. So if you run the project with admin rights, the first time, you should see this:

Local Folder sample - Picture 55

Press 6 to register the custom properties. You should get an answer like this if everything went fine:

Properties are registered. Schema location is: C:\Users\kilroy\AppData\Local\ShellBoost\cache\fc1e70e78c9b3c9df1a78038bcd69ff4.propdesc
Properties can be successfully retrieved from database.

The second message means the program has used the ShellBoost’s PropertyDescription utility class to test if the properties are correctly registered.

Now you can continue, or exit and restart without admin rights. You can press 1 or 2+3 to register to the Windows Shell as a namespace extension.

Open Windows Explorer and browse to “This PC\Samples.LocalFolder”. You should see this:

Local Folder sample - Picture 56

As you see the folder named “Hidden folder” in the “Data” folder of the Visual Studio project is not shown. This is due to some custom code in LocalShellFolder.cs:

internal static IEnumerable<FileSystemInfo> EnumerateFileSystemItems(DirectoryInfo info, string searchPattern)
{
    // for demonstration purpose, we hide any file or directory that has "hidden" in its name
    foreach (var child in info.EnumerateFileSystemInfos(searchPattern))
    {
        if (child.Name.IndexOf("hidden", StringComparison.OrdinalIgnoreCase) >= 0)
            continue;
 
        yield return child;
    }
}

If you browse the “One Folder” folder, you should see this:

Local Folder sample - Picture 61

If you right click on the columns headers, an “Icon” custom column is available, check it:

Local Folder sample - Picture 58

This demonstrates a custom icon property. The relevant code is in LocalShellFolder.cs, and LocalShellItem.cs:

public class LocalShellFolder : ShellFolder
{
  // Declared in LocalFolder.propdesc schema file. This file must be registered once. Check Program.cs.
  public static readonly PropertyDescription IconUIProperty = PropertySystem.GetPropertyDescription("ShellBoost.Samples.LocalFolder.IconUI", true);
  public static readonly PropertyDescription IconProperty = PropertySystem.GetPropertyDescription("ShellBoost.Samples.LocalFolder.Icon", true);
 
  public LocalShellFolder(ShellFolder parent, DirectoryInfo info)
      : base(parent, info) // there is a specific overload for DirectoryInfo
  {
      ...
      AddColumn(IconUIProperty);
      ...
  }
  ...
}
 
public class LocalShellItem : ShellItem
{
    public LocalShellItem(ShellFolder parent, FileInfo info)
        : base(parent, info)
    {
        ...
        var ms = new MemoryPropertyStore();
        ms.SetValue(Props.System.PropList.StatusIcons, "prop:" + LocalShellFolder.IconProperty.CanonicalName);
        ms.SetValue(Props.System.PropList.StatusIconsDisplayFlag, (uint)2);
 
        if (info.Name.Contains("error"))
        {
            ms.SetValue(LocalShellFolder.IconProperty, IconValue.Error);
        }
        else if (info.Name.Contains("warn"))
        {
            ms.SetValue(LocalShellFolder.IconProperty, IconValue.Warning);
        }
        else
        {
            ms.SetValue(LocalShellFolder.IconProperty, IconValue.Ok);
        }
 
        SetPropertyValue(LocalShellFolder.IconUIProperty, ms);
    }
    ...
}

If you browse the “An Xml File.Xml\root\element” folder, you enter virtual folders. For demonstration purposes, the code handles .XML file in a specific way: it opens the file content and displays Xml Element as virtual shell folders and Xml Attribute as virtual shell item. The content of an attribute shell item is the Xml attribute’s value.

Local Folder sample - Picture 59

This is the code that creates a virtual shell folder when browsing an Xml physical file:

protected override ShellItem CreateFileSystemItem(FileInfo info)
{
    // for demonstration purpose, we handle XML files like they were folder over their elements
    if (string.Compare(info.Extension, ".xml", StringComparison.OrdinalIgnoreCase) == 0)
        return new XmlDocumentShellFolder(this, info);
 
    return new LocalShellItem(this, info);
}

This is the code that create two types of shell items:

public class XmlElementShellFolder : ShellFolder
{
    public XmlElementShellFolder(ShellFolder parent, XmlElement element)
        : base(parent, new StringKeyShellItemId(element.LocalName))
    {
        Element = element;
        DisplayName = element.LocalName;
    }
 
    public XmlElement Element { get; }
 
    public override IEnumerable<ShellItem> EnumItems(SHCONTF options)
    {
        // shell is asking for folders, use Xml elements as folder
        if (options.HasFlag(SHCONTF.SHCONTF_FOLDERS))
        {
            foreach (var child in Element.ChildNodes.OfType<XmlElement>())
            {
                yield return new XmlElementShellFolder(this, child);
            }
        }
 
        // shell is asking for non folders (items), use Xml attributes as items
        if (options.HasFlag(SHCONTF.SHCONTF_NONFOLDERS))
        {
            foreach (var att in Element.Attributes.OfType<XmlAttribute>())
            {
                yield return new XmlAttributeShellItem(this, att);
            }
        }
    }
}

The attributes have a custom icon “’Attribute.ico”, that was embedded in the .NET project.

If you navigate to the “” folder, you will see this:

Local Folder sample - Picture 60

The attribute has an overlay icon, with this code:

public override bool TryGetPropertyValue(PropertyKey key, out object value)
{
    // OverlayIconLocation is not a Windows property, it's a ShellBoost special property
    if (key == PropertyStore.OverlayIconLocation)
    {
        var iconsPath = ((RootFolder)Parent.Root).Server.IconsDllPath;
        
        // note the icon index syntax: the index must be negative when passed to the Windows Shell
        switch (Attribute.Value)
        {
            case "Error":
                value = iconsPath + ",-" + LocalShellFolderServer.ErrorOverlayIconIndex;
                return true;
 
            case "Warning":
                value = iconsPath + ",-" + LocalShellFolderServer.WarningOverlayIconIndex;
                return true;
        }
    }
 
    return base.TryGetPropertyValue(key, out value);
}

Overlay icons are explained in detail in the “Overlay icons support” sub-chapter of the Item icon and thumbnail support chapter.