Information Bar

Explorer can display notification banners under certain conditions. They are displayed in the upper area of the Shell view, For example, in the case of a failing network, they appear as follows:


As you can see on this screenshot, a banner also may contain a menu that an end user can open and use.

CBFS Shell offers exclusive support for this feature.

To display such a banner, you should override methods of the ShellFolder class. This is demonstrated in the Registry Folder sample. If you just want to display a message, override the GetInformationBar method like this:

// this is called by CBFS Shell
protected override InformationBar GetInformationBar()
{
    // in this sample, we check that the user has sufficient rights
    if (Hive != RegistryHive.LocalMachine || DiagnosticsInformation.GetTokenElevationType() == TokenElevationType.Full)
        return null;
 
    var bar = new InformationBar();
 
    // the GUID identifies the banner, it must not be the empty GUID.
    // Use the same guid for one given banner.
    bar.Guid = _adminMessageGuid;
 
    // Set the message that will be displayed to an end user.
    // It must not be null nor empty and also should not contain mere whitespaces
    bar.Message = "You must be running with full privileges to change anything here";
    return bar;
}

The following is displayed when the Shell folder is open:


In this screenshot, we also added a menu (this is optional) as follows:

protected override void CreateInformationBarMenu(InformationBar bar, ShellMenu appendMenu)
{
    var item = new ShellMenuItem(appendMenu, "What is my UAC level?");
    appendMenu.Items.Add(item);
}

And the menu handling is done like this:

protected override async void HandleInformationBarMenu(InformationBar bar, IntPtr hwndOwner, int id)
{
    if (id == 1) // first item has id=1 and so on
    {
        await WindowsUtilities.DoModelessAsync(() =>
        {
            System.Windows.Forms.MessageBox.Show(new Win32Window(hwndOwner), "UAC level is " + DiagnosticsInformation.GetTokenElevationType(), "Registry Folder");
        });
        return;
    }
}

Each menu item added to the banner menu is automatically assigned an ID that matches its insertion index.

The result is shown here:


Note that the MessageBox window here is modeless for your app (the CBFS Shell Shell Folder server) but modal for the Explorer Window.

You can initiate the display of the information bar at any time using the roundtripshellprogramming. It is based on the native IOleCommandTarget interface, for which the native proxy has support.

Once you have a Shell view on a CBFS Shell Namespace Extension, you can call Exec with special input values, as follows:

public override IEnumerable<ShellItem>
EnumItems(SHCONTF options)
{
       foreach (var item in _items)
       {
             if (SomeCondition(...))
             {
                    // clone the context as we'll change threads
                    var ctx = ShellContext.Current.Clone();
                    Task.Run(async () =>
                    {
                           // this runs in another thread
                           var view = await ctx.GetShellBoostViewAsync(this);
                           if (view != null)
                           {
                                  // get the ShellFolder instance that is running in-process with the host (e.g., Explorer)
                                  // note that this is different from using view.Folder
                                  var sf = view.CoreShellFolder;
                                  if (sf != null)
                                  {
                                        // build a dictionary
                                        var dic = new Dictionary<string, object>();
                                        
                                        // ask the Namespace Extension to show the infobar
                                        dic["Command"] = "InfoBarInform";
                                        
                                        // this is the bar identifier, use your own identifier
                                        var guid = new Guid("b5c4a837-5540-4ba0-83b5-f76a4601ad92");
                                        dic["BarId"] = guid;
                                        
                                        // this is the bar message
                                        dic["BarMessage"] = "There was a problem during items gathering!";
 
                                        // call the Namespace Extension
                                        sf.ExecCommand(IntPtr.Zero, Guid.Empty, 0, dic);
                                  }
                           }
                    });
             }
             
             // etc.
             yield return item.Value;
       }
}

The dictionary must contain the "Command" key that only understands the "InfoBarInform" and "InfoBarCancel" commands. Both commands need the "BarId" key that must contain a valid GUID, which represents the bar identifier. The "InfoBarInform" command also needs the "BarMessage" key, which must contain the text that will be displayed by the information bar.

These bars may have context menus, just like other information bars. For example, the following code adds a menu item to the bar, shown in the code above:

protected override void CreateInformationBarMenu(InformationBar bar, ShellMenu appendMenu)
{
       // use the same id as before to be able to filter on the type of bar
       var guid = new Guid("b5c4a837-5540-4ba0-83b5-f76a4601ad92");
       if (bar.Guid == guid)
       {
             var item = new ShellMenuItem(appendMenu, "Click here to report that...");
             appendMenu.Items.Add(item);
       }
}

If you need to cancel an information bar that has been shown, use code similar to the following:

var dic = new Dictionary<string, object>();
 
// ask the Namespace Extension to cancel the infobar
dic["Command"] = "InfoBarCancel";
 
// This is the bar identifier; use the same identifier as the one used to show the bar.
// Of course, there's no need to pass a message this time
var guid = new Guid("b5c4a837-5540-4ba0-83b5-f76a4601ad92");
dic["BarId"] = guid;
 
// call the Namespace Extension
sf.ExecCommand(IntPtr.Zero, Guid.Empty, 0, dic);

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