Item properties / folder columns

Starting with Windows Vista, the columns of a Shell Folder are also Windows Properties, from the Windows Property System. To manipulate these concepts easily ShellBoost defines the PropertyKey structure in the ShellBoost.Core.WindowsPropertySystem namespace. Its binary layout is 100% compatible with the Windows PROPERTYKEY structure. ShellBoost also defines the PropertyDefinition class in the same namespace that uses a PropertyKey as a unique identifier. Both classes have useful methods that allow to navigate from one to another.

In pure Windows code, the value of a property for a given item (if that item contains that property) is a PROPVARIANT which is a structure that can hold a variety of data. To simplify things, ShellBoost also defines a PropVariant .NET class that can be used to store Property values.

However, most of the time, a developer using ShellBoost will only have to deal with standard .NET values (strings, integers, booleans, arrays, etc.), as ShellBoost components will handle PropVariant conversions back and forth automatically.

Folder columns

The ShellBoost ShellFolder class has several AddColumn methods to add columns to a shell folder view. In general, the set of available columns for a shell folder is fixed and initialized in the ShellFolder constructor, so the RemoveColumn methods are mostly used to remove the default columns that ShellBoost defines.

By default, ShellBoost defines the following columns / property keys for a Shell Folder, in that exact order (shown here using their canonical names):

System.ItemNameDisplay, with the state flag SHCOLSTATE_ONBYDEFAULT

System.ItemType, with the state flag SHCOLSTATE_ONBYDEFAULT

System.Size

System.DateModified

System.PerceivedType

System.Kind

So, when the end-user opens a ShellBoost Shell Folder, this is what will be displayed the first time:

Folder columns - Picture 7

As you see, only two columns are displayed, although six are available, due to the SHCOLSTATE_ONBYDEFAULT flag that was set only on the first two ones. If the end user right-clicks on the folder view’s header, he will be able to see all available columns and choose which one will be shown in the folder view.

Adding columns to a folder

In general, columns are added in the ShellFolder derived class constructor, something like this for example, in the case of a shell folder that would display photos:

public PhotosFolder(ShellFolder parent, ShellItemId id)
    : base(parent, id)
{
    CanPaste = true;
    CanLink = true;
 
    // luckily, all these media/photo properties are already defined in Windows
    // SysProps is an alias for ShellBoost.Core.WindowsPropertySystem.System
    AddColumn(SysProps.Photo.CameraManufacturer, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Photo.CameraModel, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Photo.ExposureTime, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Photo.Flash, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Photo.FocalLength, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Photo.ISOSpeed, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Photo.FNumber, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Photo.DateTaken, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Photo.SubjectDistance, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Media.UniqueFileIdentifier, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Image.VerticalSize, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Image.HorizontalSize, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
    AddColumn(SysProps.Image.ImageID, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
}

Then, each shell item in this folder would set the value (standard .NET values, strings, integers, booleans, etc.) for all these properties accordingly, for example (here we use a Photo class, defined elsewhere, that has all information for a photo):

public PhotoItem(BaseFolder parent, Photo photo)
    : base(parent, new StringKeyShellItemId(photo.Id))
{
    SetPropertyValue(SysProps.Photo.CameraManufacturer, photo.ExifMake);
    SetPropertyValue(SysProps.Photo.CameraModel, photo.ExifModel);
    SetPropertyValue(SysProps.Photo.ExposureTime, photo.ExifExposure);
    SetPropertyValue(SysProps.Photo.Flash, (photo.ExifFlash ? PHOTO_FLASH_FLASH : PHOTO_FLASH_NONE));
    SetPropertyValue(SysProps.Photo.FocalLength, photo.ExifFocalLength);
    SetPropertyValue(SysProps.Photo.ISOSpeed, photo.ExifIso);
    SetPropertyValue(SysProps.Photo.FNumber, photo.ExifFStop);
    SetPropertyValue(SysProps.Photo.DateTaken, photo.ExifTime);
    SetPropertyValue(SysProps.Photo.SubjectDistance, photo.ExifDistance);
    SetPropertyValue(SysProps.Media.UniqueFileIdentifier, photo.ExifImageUniqueId);
    SetPropertyValue(SysProps.Image.HorizontalSize, photo.Width);
    SetPropertyValue(SysProps.Image.VerticalSize, photo.Height);
    SetPropertyValue(SysProps.Image.ImageID, photo.Id);
}

This is how such a folder could like:

Adding columns to a folder - Picture 13

A shell item (or folder) can set a property value using the SetPropertyValue() method, but if the value is dynamic, it’s better to override the TryGetPropertyValue() method, like this:

public override bool TryGetPropertyValue(PropertyKey key, out object value)
{
    // we use the current date time as the Display Name
    if (key == Props.System.ItemNameDisplay)
    {
        value = DateTime.Now.ToString();
        return true;
    }
    return base.TryGetPropertyValue(key, out value);
} 

As you see, the way they are rendered, including header names, is consistent with the type of data they contain because we only used Windows well-known properties that have a lot of pre-defined characteristics. The benefit of using properties – built-in or not - doesn’t stop at the data type and the name, but also how they are rendered, how they are grouped (using a right-click on column headers), how they are sorted, how they are searched, etc. More info is available on MSDN site in Understanding the Property Description Schema

Windows properties

The latest version of Windows 10 defines around 1600 properties of all sort and types. There are all defined as PropertyKey instances in the ShellBoost.Core .NET assembly, namespace ShellBoost.Core.WindowsPropertySystem. Note all properties are not officially documented.

Property Lists

The property lists are semi-colon delimited strings that have the following form.

prop:[flags]PropertyCanonicalName;[flags]PropertyCanonicalName;...;[flags]PropertyCanonicalName;

They are used as values for properties that expect property lists, for example, System.PropList.InfoTip, which contains the list of properties to show in the infotip / tooltip for a shell item.

By conventions, these properties are defined in the System.PropList namespace. Flags are technically defined on MSDN site in the PROPDESC_VIEW_FLAGS enumeration. To each value in the enumeration corresponds a special character. The correspondence is available here on MSDN: IPropertySystem::GetPropertyDescriptionListFromString method

Instead of building these strings manually, you can use the ShellBoost utility class named PropertyDescriptionList in the ShellBoost.Core.WindowsPropertySystem namespace. The class also supports flags to make things a lot easier. For example, this code:

using Props = ShellBoost.Core.WindowsPropertySystem;
var list = new PropertyDescriptionList(Props.System.ItemNameDisplay, Props.System.ItemTypeText).ToString();

Will build the “prop:System.ItemNameDisplay;System.ItemTypeText;” string. The utility class also handles various flags that the Windows Shell support.

Custom properties

The Windows Property System supports custom properties. As a developer using ShellBoost, you can create your own properties and use them to create custom columns. The process is documented here on MSDN: Creating Custom Properties. Custom property definitions must be registered using a custom .propdesc XML file, on the machine before the Shell can use it. ShellBoost provides a helper method in the PropertySystem class in the ShellBoost.Core.WindowsPropertySystem namespace:

// register a property schema from a .propdesc file path
PropertySystem.RegisterPropertySchema(location);
...
// unregister a property schema from a .propdesc file path
PropertySystem.UnregisterPropertySchema(location);

The Registry Folder Sample demonstrates that with two custom columns defined in a custom .propdesc file.

Note: since registration (and unregistration) requires write access to some part of the HKEY_LOCAL_MACHINE registry, the program that will run that code will need sufficient permissions. This is one inconvenient to defining custom properties. Whenever possible, we recommend using built-in Windows properties.

Item icon properties

Shell Item can have properties / columns whose value will be displayed as an icon. This is not to be confused with the icon or the thumbnail that represents the item in the various folder icon views. Icon properties are displayed in a folder’s details view.

The Local Folder Sample demonstrates that feature with a custom column named “Icon”. This is how the end-user could enable that column (right-click on the folder’s view header):

Item icon properties - Picture 9

And here is the result:

Item icon properties - Picture 10

As you see, the “Icon” column is rendered with an image instead of a text. ShellBoost has support for this kind of properties, but the procedure is a bit more complex that for other properties. First, you must define two custom properties, for each icon-rendered column (this is done the standard way, using a .propdesc file and register it to Windows):

One UInt32 property with an “enumerated” display type. It will define all the possible values and associated icons for this icon property.

One property of “blob” (the binary representation of the icon) type that will be the property set to the Shell Item.

Then, in the code, the “blob” type property must be added as a column to the custom Shell Folder, something like this:

// this code uses the PropertySystem utility class to get the PropertyDescription from its canonical name.
// it also checks the property is registered.
public static readonly PropertyDescription IconUIProperty = PropertySystem.GetPropertyDescription("ShellBoost.Samples.LocalFolder.IconUI", true);
 
...
 
public LocalShellFolder(ShellFolder parent, DirectoryInfo info)
    : base(parent, info)
{
    ...
    // we only add the blob type property as a column to the folder
    AddColumn(IconUIProperty);
    ...
}

In the shell item code, you must add the property value, like this:

public LocalShellItem(ShellFolder parent, FileInfo info)
    : base(parent, info)
{
    ...
 
    // create a memory property store, and add the following values to it
    var ms = new MemoryPropertyStore();
    ms.SetValue(Props.System.PropList.StatusIcons, "prop:" + LocalShellFolder.IconProperty.CanonicalName);
    ms.SetValue(Props.System.PropList.StatusIconsDisplayFlag, (uint)2); // this is mandatory
 
    // define the uint32 property value using your own logic
    // this is specific to your code, you can use an enum value, ShellBoost will convert it to an integer automatically
    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);
    }
 
    // use the memory property store as the icon blob property value (the one added in the folder)
    SetPropertyValue(LocalShellFolder.IconUIProperty, ms);
    ...
}
 
// values here must match Icon property's enumerated values in the .propdesc XML file
// defining a corresponding C# enum is not mandatory but makes things easier
public enum IconValue
{
    None = 0,
    Ok = 1,
    Error = 2,
    Warning = 3
}

Here is how the .propdesc schema file of the sample is defined:

<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windows/2006/propertydescription" schemaVersion="1.0" >
       <propertyDescriptionList publisher="Aelyo" product="ShellBoost.Samples.RegistryFolder">
             <propertyDescription name="ShellBoost.Samples.LocalFolder.IconUI" formatID="{d9f69df5-01ef-4838-acb7-055012a678ca}" propID="3">
                    <searchInfo reIndexPatterns="" processReIndexPatternsImmediately="true" inInvertedIndex="false" isColumn="false">
                    </searchInfo>
                    <typeInfo type="Blob" isInnate="true" isViewable="true">
                    </typeInfo>
                    <labelInfo label="Icon">
                    </labelInfo>
                    <displayInfo defaultColumnWidth="10">
                           <drawControl control="IconList">
                           </drawControl>
                    </displayInfo>
             </propertyDescription>
             <propertyDescription name="ShellBoost.Samples.LocalFolder.Icon" formatID="{d9f69df5-01ef-4838-acb7-055012a678ca}" propID="4">
                    <searchInfo reIndexPatterns="" processReIndexPatternsImmediately="true" inInvertedIndex="false" isColumn="false">
                    </searchInfo>
                    <typeInfo type="UInt32" isInnate="true" groupingRange="Enumerated" isViewable="true">
                    </typeInfo>
                    <displayInfo displayType="Enumerated">
                           <enumeratedList>
                                  <enum name="None" value="0" text="">
                                  </enum>
                                  <enum name="Ok" value="1" text="Ok">
                                        <image res="%systemroot%\system32\imageres.dll,-1405">
                                        </image>
                                  </enum>
                                  <enum name="Error" value="2" text="Error">
                                        <image res="%systemroot%\system32\imageres.dll,-1402">
                                        </image>
                                  </enum>
                                  <enum name="Warning" value="3" text="Warning">
                                        <image res="%systemroot%\system32\imageres.dll,-1403">
                                        </image>
                                  </enum>
                           </enumeratedList>
                    </displayInfo>
                    <labelInfo label="Icon UI">
                    </labelInfo>
             </propertyDescription>
       </propertyDescriptionList>
</schema>

It defines two custom properties:

ShellBoost.Samples.LocalFolder.IconUI: which is a property of Blob type, displayed as an IconList.

ShellBoost.Samples.LocalFolder.Icon: which is a property of UInt32 type. Each value can be associated with an image that will represent the value. In the sample, we’ve reused a standard Windows dll (imageres.dll), but you can use your own. The image must be located in a binary file as a Win32 Resource. The syntax is the standard Win32 resource syntax: <path>,-<resource index>

You can define any number of icon-rendered columns in a folder.

Important Windows Properties

Since Windows defines around 1600 properties, as of today, we list here some of the most interesting and important ones, most of them being handled undercovers by ShellBoost:

Canonical Name

MSDN description

ShellBoost Comment

ShellBoost Shell Item equivalent Property

Item Default Value

Folder Default Value

System.ItemNameDisplay

The display name in "most complete" form. It is the unique representation of the item name most appropriate for end users.

 

DisplayName

Key/ID value

Key/ID value

System.Size

The system-provided file system size of the item, in bytes.

 

Size

File size (physical) or 0 (virtual)

0

System.ItemType

The canonical type of the item.

 

ItemType

<empty>

<empty>

System.DateModified

The date and time of the last modification to the item.

 

DateModified

File Date Modified (physical) or Date.MinValue (virtual)

Folder Date Modified (physical) or Date.MinValue (virtual)

System.DateCreated

The date and time the item was created on the file system where it is currently located.

 

DateCreated

File Date Created (physical) or Date.MinValue (virtual)

Folder Date Created (physical) or Date.MinValue (virtuel)

System.DateAccessed

Indicates the last time the item was accessed.

 

DateAccessed

File Date Accessed (physical) or Date.MinValue (virtual)

Folder Date Accessed (physical) or Date.MinValue (virtual)

System.SFGAOFlags

SFGAO values.

 

Attributes

See the “Attributes detailed“ sub-chapter in the ShellBoost Binaries setup chapter.

See the “Attributes detailed“ sub-chapter in the ShellBoost Binaries setup chapter.

System.PerceivedType

The perceived file type based on its canonical type.

 

Perceived

PERCEIVED_TYPE_DOCUMENT

PERCEIVED_TYPE_FOLDER

System.Kind

Maps extensions to various .Search folders.

 

KindList

KIND_DOCUMENT

KIND_FOLDER

 

 

 

 

 

 

System.FileName

The file name, including its extension. System.FileExtension is derived from this property.

 

FileName

File name (physical) or <empty> (virtual)

Folder name (physical) or <empty> (virtual)

System.FileAttributes

The attributes of the item.

 

 

File attributes (physical) or <empty> (virtual)

Folder attributes (physical) or <empty> (virtual)

System.FileExtension

Identifies the file extension of the file-based item, including the leading period.

 

 

File extension (physical) or <empty> (virtual)

Folder extension (physical) or <empty> (virtual)

System.PropList.InfoTip

The list of properties to show in the infotip. Properties with empty values will not be displayed.

 

 

“~System.ItemNameDisplay”

“~System.ItemNameDisplay”

System.InfoTipText

The text (with formatted property values) to show in the infotip.

 

 

The DisplayName property

The DisplayName property

System.PropList.PreviewTitle

The one or two properties to display in the preview pane title section. The optional second property is displayed as a subtitle.

 

 

“System.Title”

“System.Title”

System.PropList.PreviewDetails

The list of properties to display in the preview pane.

 

 

“System.ItemNameDisplay;System.ItemTypeText”

“System.ItemNameDisplay;System.ItemTypeText”

System.PropList.FullDetails

The list of all the properties to show in the details page. Property groups can be included in this list in order to more easily organize the UI.

This defines what’s shown when clicking the “Properties” menu item. Property Groups are in the System.PropGroup.* namespace. Each group will create a properties category.

 

The list of parent folder columns

The list of parent folder columns

System.PropList.FileOperationPrompt

The list of properties to show in the file operation confirmation dialog. Properties with empty values will not be displayed. If this list is not specified, then the InfoTip property list is used instead.

 

 

“System.ItemNameDisplay”

“System.ItemNameDisplay”

System.LayoutPattern.ContentViewModeForBrowse

Identifies the layout pattern that the content view mode should apply for this item in the context of browsing.

This is explained in detail here : How to Register Custom Properties and Layout for Your File Type and How to Register a Unique Content View Set of Properties and Layout Pattern for the File Type or Item

 

“Delta”

“Delta”

System.PropList.ContentViewModeForBrowse

The list of properties to show in the content view mode of an item in the context of browsing.

 

 

“~System.ItemNameDisplay”

“~System.ItemNameDisplay”