Local Folder Sample

This is a console application with a Shell folder that supports a combination of virtual Shell items and physical Shell items. It demonstrates custom properties, overlays icons, and icon properties. It also demonstrates dynamic columns and the "roundtrip" feature.

This sample can be found in the GitHub repository with CBFS Shell samples.

The project is laid out as follows:


The "Data" directory contains a bunch of physical sample files that we will use as a built-in hierarchy when we run the project. They are all configured with a Visual Studio Build Action set to "Copy if newer".

The "Resources" directory contains two overlay icons, one Shell item icon, and one custom property .propdesc schema file. They are all configured with a Visual Studio Build Action set to "Embedded resource".

Because the project contains custom properties, it must be run at least once (for a production program, it would be at install time) with administrative rights. If you run the project with administrative rights the first time, you should see the following:


Press 6 to register the custom properties. If everything goes well, you should get the following answer:

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 whether or not the properties are correctly registered.

Now you can continue or quit and restart the sample without administrative rights. You can press 1 or 2+3 to register in the Windows Shell as a Namespace Extension.

Open Explorer and browse to "This PC\Samples.LocalFolder". You should see the following:


As you can see, the folder named "Hidden folder" in the "Data" folder of the Visual Studio project is not shown. This is the result of 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 the following:


If you right click on the column's header, a custom "Icon" column will be available, as follows:


This sample demonstrates a custom icon property. The relevant code is found 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 files in a specific way: it opens the file content and displays the Xml element as virtual Shell folders and the Xml attribute as virtual Shell item. The content of an attribute Shell item is the Xml attribute's value.


The following code 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 are a folder over their elements
    if (string.Compare(info.Extension, ".xml", StringComparison.OrdinalIgnoreCase) == 0)
        return new XmlDocumentShellFolder(this, info);
 
    return new LocalShellItem(this, info);
}

The following code creates 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 the following:


The attribute has an overlay icon, which was added with the following 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 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" subchapter of the Item icon and thumbnail support chapter.

Copyright (c) 2022 Callback Technologies, Inc. - All rights reserved.
CBFS Shell 2022 .NET Edition - Version 22.0 [Build 8172]