// MMMMMMMMMMMMMMMN0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // MMMMMMMMMMMMMWOc::dNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // MMMMMMMMMMMMWk;cko,lXMMMMMMMMMMMMWNXXXXXXNNWMMMMMMNXXXXXXXNWMMMMMMMMMMMWXK000KXWMMMMMMWNXXNMMMMMNXKXWMMNXXXXXXXXXNWMMWXXXNWMMMMMMNXXNMMMM // MMMMMMMMMMMNd,l0K0d;cKMMMMMMMMMMM0;.......,:xXMMMXl.......';lONMMMMNXOl,... ...,lOWMMMKc..cXMMWk;..:KMXc.........cKMNd....cKMMMMXc..dWMMM // MMMMMMMMMMNo,oOdclxx;:KMMMMMMMMMMx. 'lol, :XMMK, .:ol:. .oNMM0:.. 'cool:. .oXMM0' ,KMXo. ;0WM0' .:lllllkNMN: ,0WMMK, cNMMM // MMMMMMMMMXl,d0d. ;Ok::0WMMMMMMMMx. lWMMK, '0MMK, '0MMWk. ,KMK; .oNMMMMW0; .oWM0' ,00; .dNMMM0' ,KMMMMMMMMX: .. 'OWMK, cNMMM // MMMMMMMMK:;x0Kx. :0KO:;kWMMMMMMMx. ;xkx:. .dNMMK, .xKKO: oNWd. cNMMMMMMMx. ;KM0, .:. :0WMMMM0' .lxxxxONMMX: ;d' .kWK, cNMMM // MMMMMMW0::kKKKk, c0KKOc,xWMMMMMMx. .. ,dXMMK, ... .;kNMWl oWMMMMMMMO. '0M0, . .xWMMMMM0' ....:KMMX: :XO' .k0, cNMMM // MMMMMWk;cOKKKKO; .oKKKK0l,dNMMMMMx. :O0Ox:. :KMK, .co:. :0WMWo cNMMMMMMMk. ;XM0, 'o, .oNMMMM0' 'x0000XWMMN: ;XW0, .l, cNMMM // MMMMWx,lOKKKKK0d;ckKKKKK0o,oNMMMMx. lWMMM0, .OMK, '0MWx. ,0MMO. .kWMMMMWK; .dWM0, ,KKc :KWMM0' ,KMMMMMMMMN: ;XMM0; cNMMM // MMMNo,o0KKKKKKk:..l0KKKKK0d,lXMMMx. ,dxxo; cXMK, '0MMWd. ;KMNd. .:dxxdc. .oNMM0, ,KMNo. 'kWM0' .lddddxONMN: ;XMMMK: cNMMM // MMXl,d0KKKKKKKOc''o0KKKKKKKx;cKMMO' ......'ckNMMX: ;KMMMNo. .oNMW0xl'. .,l0WMMMK; :XMMWk,. 'kMK:. ......;KMNl. cXMMMMXl. .dWMMM // MMx':dxxxxxxxxxdoddxxxxxxxxd:'dWMWK000000KXNMMMMMWX0OKWMMMMNKOOKWMMMMMNKOkxxkOKNMMMMMMWX00XWMMMMX0O0XMWX000000000KWMMX00XWMMMMMWK00KNMMMM // MMXxoooooooooooooooooooooooooxXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // Heavily leverages the work done by Microsoft on the control by the same name in WindowsVistaApiPack. Work was done to improve the designer // experience, remove nested properties, add missing capabilities, simplify COM calls, align names to those in other mainstream controls, and // use the Vanara libraries. using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; using Vanara.Extensions; using Vanara.PInvoke; using Vanara.Windows.Shell; using static Vanara.PInvoke.Shell32; using static Vanara.PInvoke.ShlwApi; using static Vanara.PInvoke.User32_Gdi; using IServiceProvider = Vanara.PInvoke.Shell32.IServiceProvider; namespace Vanara.Windows.Forms { /// /// Indicates the content options of the explorer browser. Typically use one, or a bitwise combination of these flags to specify how /// content should appear in the explorer browser control /// [Flags] public enum ExplorerBrowserContentSectionOptions : uint { /// No options. None = FOLDERFLAGS.FWF_NONE, /// The view should be left-aligned. AlignLeft = FOLDERFLAGS.FWF_ALIGNLEFT, /// Automatically arrange the elements in the view. AutoArrange = FOLDERFLAGS.FWF_AUTOARRANGE, /// Turns on check mode for the view CheckSelect = FOLDERFLAGS.FWF_CHECKSELECT, /// When the view is set to "Tile" the layout of a single item should be extended to the width of the view. ExtendedTiles = FOLDERFLAGS.FWF_EXTENDEDTILES, /// When an item is selected, the item and all its sub-items are highlighted. FullRowSelect = FOLDERFLAGS.FWF_FULLROWSELECT, /// The view should not display file names HideFileNames = FOLDERFLAGS.FWF_HIDEFILENAMES, /// The view should not save view state in the browser. NoBrowserViewState = FOLDERFLAGS.FWF_NOBROWSERVIEWSTATE, /// Do not display a column header in the view in any view mode. NoColumnHeader = FOLDERFLAGS.FWF_NOCOLUMNHEADER, /// Only show the column header in details view mode. NoHeaderInAllViews = FOLDERFLAGS.FWF_NOHEADERINALLVIEWS, /// The view should not display icons. NoIcons = FOLDERFLAGS.FWF_NOICONS, /// Do not show subfolders. NoSubfolders = FOLDERFLAGS.FWF_NOSUBFOLDERS, /// Navigate with a single click SingleClickActivate = FOLDERFLAGS.FWF_SINGLECLICKACTIVATE, /// Do not allow more than a single item to be selected. SingleSelection = FOLDERFLAGS.FWF_SINGLESEL, /// /// Make the folder behave like the desktop. This value applies only to the desktop and is not used for typical Shell folders. /// Desktop = FOLDERFLAGS.FWF_DESKTOP, /// Draw transparently. This is used only for the desktop. Transparent = FOLDERFLAGS.FWF_TRANSPARENT, /// Do not add scroll bars. This is used only for the desktop. NoScrollBars = FOLDERFLAGS.FWF_NOSCROLL, /// The view should not be shown as a web view. NoWebView = FOLDERFLAGS.FWF_NOWEBVIEW, /// /// Windows Vista and later. Do not re-enumerate the view (or drop the current contents of the view) when the view is refreshed. /// NoEnumOnRefresh = FOLDERFLAGS.FWF_NOENUMREFRESH, /// Windows Vista and later. Do not allow grouping in the view NoGrouping = FOLDERFLAGS.FWF_NOGROUPING, /// Windows Vista and later. Do not display filters in the view. NoFilters = FOLDERFLAGS.FWF_NOFILTERS, /// Windows Vista and later. Items can be selected using check-boxes. AutoCheckSelect = FOLDERFLAGS.FWF_AUTOCHECKSELECT, /// Windows Vista and later. The view should list the number of items displayed in each group. To be used with IFolderView2::SetGroupSubsetCount. SubsetGroup = FOLDERFLAGS.FWF_SUBSETGROUPS, /// Windows Vista and later. Use the search folder for stacking and searching. UseSearchFolder = FOLDERFLAGS.FWF_USESEARCHFOLDER, /// /// Windows Vista and later. Ensure right-to-left reading layout in a right-to-left system. Without this flag, the view displays /// strings from left-to-right both on systems set to left-to-right and right-to-left reading layout, which ensures that file names /// display correctly. /// AllowRtlReading = FOLDERFLAGS.FWF_ALLOWRTLREADING, } /// These flags are used with . [Flags] public enum ExplorerBrowserLoadFlags { /// No flags. None = EXPLORER_BROWSER_FILL_FLAGS.EBF_NONE, /// /// Causes to first populate the results folder with the contents of the parent folders /// of the items in the data object, and then select only the items that are in the data object. /// SelectFromDataObject = EXPLORER_BROWSER_FILL_FLAGS.EBF_SELECTFROMDATAOBJECT, /// /// Do not allow dropping on the folder. In other words, do not register a drop target for the view. Applications can then register /// their own drop targets. /// NoDropTarget = EXPLORER_BROWSER_FILL_FLAGS.EBF_NODROPTARGET, } /// /// Specifies the options that control subsequent navigation. Typically use one, or a bitwise combination of these flags to specify how /// the explorer browser navigates. /// [Flags] public enum ExplorerBrowserNavigateOptions { /// No options. None = EXPLORER_BROWSER_OPTIONS.EBO_NONE, /// Always navigate, even if you are attempting to navigate to the current folder. AlwaysNavigate = EXPLORER_BROWSER_OPTIONS.EBO_ALWAYSNAVIGATE, /// Do not navigate further than the initial navigation. NavigateOnce = EXPLORER_BROWSER_OPTIONS.EBO_NAVIGATEONCE, /// /// Use the following standard panes: Commands Module pane, Navigation pane, Details pane, and Preview pane. An implementer of /// IExplorerPaneVisibility can modify the components of the Commands Module that are shown. For more information see, /// IExplorerPaneVisibility::GetPaneState. If EBO_SHOWFRAMES is not set, Explorer browser uses a single view object. /// ShowFrames = EXPLORER_BROWSER_OPTIONS.EBO_SHOWFRAMES, /// Do not update the travel log. NoTravelLog = EXPLORER_BROWSER_OPTIONS.EBO_NOTRAVELLOG, /// Do not use a wrapper window. This flag is used with legacy clients that need the browser parented directly on themselves. NoWrapperWindow = EXPLORER_BROWSER_OPTIONS.EBO_NOWRAPPERWINDOW, /// Show WebView for SharePoint sites. HtmlSharePointView = EXPLORER_BROWSER_OPTIONS.EBO_HTMLSHAREPOINTVIEW, /// Introduced in Windows Vista. Do not draw a border around the browser window. NoBorder = EXPLORER_BROWSER_OPTIONS.EBO_NOBORDER, /// Introduced in Windows Vista. Do not persist the view state. NoPersistViewState = EXPLORER_BROWSER_OPTIONS.EBO_NOPERSISTVIEWSTATE, } /// Flags specifying the folder to be browsed. [Flags] public enum ExplorerBrowserNavigationItemCategory : uint { /// An absolute PIDL, relative to the desktop. Absolute = SBSP.SBSP_ABSOLUTE, /// Windows Vista and later. Navigate without the default behavior of setting focus into the new view. ActivateNoFocus = SBSP.SBSP_ACTIVATE_NOFOCUS, /// Enable auto-navigation. AllowAutoNavigate = SBSP.SBSP_ALLOW_AUTONAVIGATE, /// /// Microsoft Internet Explorer 6 Service Pack 2 (SP2) and later. The navigation was possibly initiated by a web page with scripting /// code already present on the local system. /// CallerUntrusted = SBSP.SBSP_CALLERUNTRUSTED, /// /// Windows 7 and later. Do not add a new entry to the travel log. When the user enters a search term in the search box and /// subsequently refines the query, the browser navigates forward but does not add an additional travel log entry. /// CreateNoHistory = SBSP.SBSP_CREATENOHISTORY, /// /// Use default behavior, which respects the view option (the user setting to create new windows or to browse in place). In most /// cases, calling applications should use this flag. /// Default = SBSP.SBSP_DEFBROWSER, /// Use the current window. UseCurrentWindow = SBSP.SBSP_DEFMODE, /// /// Specifies a folder tree for the new browse window. If the current browser does not match the SBSP.SBSP_EXPLOREMODE of the browse /// object call, a new window is opened. /// ExploreMode = SBSP.SBSP_EXPLOREMODE, /// /// Windows Internet Explorer 7 and later. If allowed by current registry settings, give the browser a destination to navigate to. /// FeedNavigation = SBSP.SBSP_FEEDNAVIGATION, /// Windows Vista and later. Navigate without clearing the search entry field. KeepSearchText = SBSP.SBSP_KEEPWORDWHEELTEXT, /// Navigate back, ignore the PIDL. NavigateBack = SBSP.SBSP_NAVIGATEBACK, /// Navigate forward, ignore the PIDL. NavigateForward = SBSP.SBSP_NAVIGATEFORWARD, /// Creates another window for the specified folder. NewWindow = SBSP.SBSP_NEWBROWSER, /// Suppress selection in the history pane. NoHistorySelect = SBSP.SBSP_NOAUTOSELECT, /// Do not transfer the browsing history to the new window. NoTransferHistory = SBSP.SBSP_NOTRANSFERHIST, /// /// Specifies no folder tree for the new browse window. If the current browser does not match the SBSP.SBSP_OPENMODE of the browse /// object call, a new window is opened. /// NoFolderTree = SBSP.SBSP_OPENMODE, /// Browse the parent folder, ignore the PIDL. ParentFolder = SBSP.SBSP_PARENT, /// Windows 7 and later. Do not make the navigation complete sound for each keystroke in the search box. PlayNoSound = SBSP.SBSP_PLAYNOSOUND, /// Enables redirection to another URL. Redirect = SBSP.SBSP_REDIRECT, /// A relative PIDL, relative to the current folder. Relative = SBSP.SBSP_RELATIVE, /// Browse to another folder with the same Windows Explorer window. SameWindow = SBSP.SBSP_SAMEBROWSER, /// Microsoft Internet Explorer 6 Service Pack 2 (SP2) and later. The navigate should allow ActiveX prompts. TrustedForActiveX = SBSP.SBSP_TRUSTEDFORACTIVEX, /// /// Microsoft Internet Explorer 6 Service Pack 2 (SP2) and later. The new window is the result of a user initiated action. Trust the /// new window if it immediately attempts to download content. /// TrustFirstDownload = SBSP.SBSP_TRUSTFIRSTDOWNLOAD, /// /// Microsoft Internet Explorer 6 Service Pack 2 (SP2) and later. The window is navigating to an untrusted, non-HTML file. If the /// user attempts to download the file, do not allow the download. /// UntrustedForDownload = SBSP.SBSP_UNTRUSTEDFORDOWNLOAD, /// Write no history of this navigation in the history Shell folder. WriteNoHistory = SBSP.SBSP_WRITENOHISTORY } /// Indicates the viewing mode of the explorer browser public enum ExplorerBrowserViewMode { /// Choose the best view mode for the folder Auto = FOLDERVIEWMODE.FVM_AUTO, /// (New for Windows7) Content = FOLDERVIEWMODE.FVM_CONTENT, /// Object names and other selected information, such as the size or date last updated, are shown. Details = FOLDERVIEWMODE.FVM_DETAILS, /// The view should display medium-size icons. Icon = FOLDERVIEWMODE.FVM_ICON, /// Object names are displayed in a list view. List = FOLDERVIEWMODE.FVM_LIST, /// The view should display small icons. SmallIcon = FOLDERVIEWMODE.FVM_SMALLICON, /// The view should display thumbnail icons. Thumbnail = FOLDERVIEWMODE.FVM_THUMBNAIL, /// The view should display icons in a filmstrip format. ThumbStrip = FOLDERVIEWMODE.FVM_THUMBSTRIP, /// The view should display large icons. Tile = FOLDERVIEWMODE.FVM_TILE } /// Indicates the visibility state of an ExplorerBrowser pane. public enum PaneVisibilityState { /// Allow the explorer browser to determine if this pane is displayed. Default = EXPLORERPANESTATE.EPS_DONTCARE, /// Hide the pane Hide = EXPLORERPANESTATE.EPS_DEFAULT_OFF | EXPLORERPANESTATE.EPS_FORCE, /// Show the pane Show = EXPLORERPANESTATE.EPS_DEFAULT_ON | EXPLORERPANESTATE.EPS_FORCE } /// The direction argument for Navigate internal enum NavigationLogDirection { /// Navigates forward through the navigation log Forward, /// Navigates backward through the travel log Backward } /// /// ExplorerBrowser is a browser object that can be either navigated or that can host a view of a data object. As a /// full-featured browser object, it also supports an automatic travel log. /// /// /// /// /// /// /// [Designer(typeof(Design.ExplorerBrowserDesigner)), DefaultProperty(nameof(Name)), DefaultEvent(nameof(SelectionChanged))] [ToolboxItem(true), ToolboxBitmap(typeof(ExplorerBrowser), "ExplorerBrowser.bmp")] [Description("A Shell browser object that can be either navigated or that can host a view of a data object.")] public class ExplorerBrowser : UserControl, IServiceProvider, IExplorerPaneVisibility, IExplorerBrowserEvents, ICommDlgBrowser3, IMessageFilter { internal uint eventsCookie; internal IExplorerBrowser explorerBrowserControl; internal FOLDERSETTINGS folderSettings = new FOLDERSETTINGS(FOLDERVIEWMODE.FVM_AUTO, defaultFolderFlags); private const FOLDERFLAGS defaultFolderFlags = FOLDERFLAGS.FWF_USESEARCHFOLDER | FOLDERFLAGS.FWF_NOWEBVIEW; private const int defaultThumbnailSize = 32; private const int HRESULT_CANCELLED = unchecked((int)0x800704C7); private const int HRESULT_RESOURCE_IN_USE = unchecked((int)0x800700AA); private static readonly string defaultPropBagName = typeof(ExplorerBrowser).FullName; private static readonly Guid IID_ICommDlgBrowser = new Guid("000214F1-0000-0000-C000-000000000046"); private Tuple antecreationNavigationTarget; private EXPLORER_BROWSER_OPTIONS options = EXPLORER_BROWSER_OPTIONS.EBO_SHOWFRAMES; private string propertyBagName = defaultPropBagName; private int thumbnailSize = defaultThumbnailSize; private ExplorerBrowserViewEvents viewEvents; /// Initializes a new instance of the class. public ExplorerBrowser() { History = new ExplorerBrowserNavigationLog(this); Items = new ShellItemCollection(this, SVGIO.SVGIO_ALLVIEW); SelectedItems = new ShellItemCollection(this, SVGIO.SVGIO_SELECTION); } /// Fires when the Items collection changes. [Category("Action"), Description("Items changed.")] public event EventHandler ItemsChanged; /// Fires when the ExplorerBorwser view has finished enumerating files. [Category("Behavior"), Description("View is done enumerating files.")] public event EventHandler ItemsEnumerated; /// /// Fires when a navigation has been 'completed': no Navigating listener has canceled, and the ExplorerBorwser has created a new /// view. The view will be populated with new items asynchronously, and ItemsChanged will be fired to reflect this some time later. /// [Category("Action"), Description("Navigation complete.")] public event EventHandler Navigated; /// Fires when a navigation has been initiated, but is not yet complete. [Category("Action"), Description("Navigation initiated, but not complete.")] public event EventHandler Navigating; /// /// Fires when either a Navigating listener cancels the navigation, or if the operating system determines that navigation is not possible. /// [Category("Action"), Description("Navigation failed.")] public event EventHandler NavigationFailed; /// Fires when the item selected in the view has changed (i.e., a rename ). This is not the same as SelectionChanged. [Category("Action"), Description("Selected item has changed.")] public event EventHandler SelectedItemModified; /// Fires when the SelectedItems collection changes. [Category("Behavior"), Description("Selection changed.")] public event EventHandler SelectionChanged; /// The view should be left-aligned. [Browsable(false), DefaultValue(false), Category("Appearance"), Description("The view should be left-aligned.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool AlignLeft { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.AlignLeft); set => SetContentFlag(ExplorerBrowserContentSectionOptions.AlignLeft, value); } /// /// Ensure right-to-left reading layout in a right-to-left system. Without this flag, the view displays strings from left-to-right /// both on systems set to left-to-right and right-to-left reading layout, which ensures that file names display correctly. /// [Browsable(false), DefaultValue(false), Category("Appearance"), Description("Ensure right-to-left reading layout in a right-to-left system.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool AllowRtlReading { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.AllowRtlReading); set => SetContentFlag(ExplorerBrowserContentSectionOptions.AllowRtlReading, value); } /// Always navigate, even if you are attempting to navigate to the current folder. [DefaultValue(false), Category("Behavior"), Description("Always navigate, even if you are attempting to navigate to the current folder.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool AlwaysNavigate { get => IsNavFlagSet(ExplorerBrowserNavigateOptions.AlwaysNavigate); set => SetNavFlag(ExplorerBrowserNavigateOptions.AlwaysNavigate, value); } /// Automatically arrange the elements in the view. [DefaultValue(false), Category("Behavior"), Description("Automatically arrange the elements in the view.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool AutoArrange { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.AutoArrange); set => SetContentFlag(ExplorerBrowserContentSectionOptions.AutoArrange, value); } /// Items can be selected using check-boxes. [DefaultValue(false), Category("Behavior"), Description("Items can be selected using check-boxes.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool AutoCheckSelect { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.AutoCheckSelect); set => SetContentFlag(ExplorerBrowserContentSectionOptions.AutoCheckSelect, value); } /// Turns on check mode for the view [DefaultValue(false), Category("Behavior"), Description("Turns on check mode for the view")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool CheckSelect { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.CheckSelect); set => SetContentFlag(ExplorerBrowserContentSectionOptions.CheckSelect, value); } /// The binary representation of the ExplorerBrowser content flags [Browsable(false), DefaultValue(0), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public ExplorerBrowserContentSectionOptions ContentFlags { get => (ExplorerBrowserContentSectionOptions)folderSettings.fFlags; set { folderSettings.fFlags = (FOLDERFLAGS)value | defaultFolderFlags; explorerBrowserControl?.SetFolderSettings(folderSettings); } } /// Make the folder behave like the desktop. This applies only to the desktop and is not used for typical Shell folders. [Browsable(false), DefaultValue(false), Category("Appearance"), Description("Make the folder behave like the desktop.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool Desktop { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.Desktop); set => SetContentFlag(ExplorerBrowserContentSectionOptions.Desktop, value); } /// When the view is in "tile view mode" the layout of a single item should be extended to the width of the view. [DefaultValue(false), Category("Appearance"), Description("The layout of a single item should be extended to the width of the view.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ExtendedTiles { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.ExtendedTiles); set => SetContentFlag(ExplorerBrowserContentSectionOptions.ExtendedTiles, value); } /// When an item is selected, the item and all its sub-items are highlighted. [DefaultValue(false), Category("Behavior"), Description("When an item is selected, the item and all its sub-items are highlighted.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool FullRowSelect { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.FullRowSelect); set => SetContentFlag(ExplorerBrowserContentSectionOptions.FullRowSelect, value); } /// The view should not display file names [DefaultValue(false), Category("Appearance"), Description("The view should not display file names")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool HideFileNames { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.HideFileNames); set => SetContentFlag(ExplorerBrowserContentSectionOptions.HideFileNames, value); } /// Contains the navigation history of the ExplorerBrowser [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ExplorerBrowserNavigationLog History { get; } /// The set of ShellItems in the Explorer Browser [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IReadOnlyList Items { get; } /// Do not navigate further than the initial navigation. [DefaultValue(false), Category("Behavior"), Description("Do not navigate further than the initial navigation.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NavigateOnce { get => IsNavFlagSet(ExplorerBrowserNavigateOptions.NavigateOnce); set => SetNavFlag(ExplorerBrowserNavigateOptions.NavigateOnce, value); } /// The binary flags that are passed to the explorer browser control's GetOptions/SetOptions methods [Browsable(false), DefaultValue(0), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public ExplorerBrowserNavigateOptions NavigationFlags { get => (ExplorerBrowserNavigateOptions)(explorerBrowserControl?.GetOptions() ?? options); set { // Always forcing SHOWFRAMES because we handle IExplorerPaneVisibility options = (EXPLORER_BROWSER_OPTIONS)value | EXPLORER_BROWSER_OPTIONS.EBO_SHOWFRAMES; explorerBrowserControl?.SetOptions(options); } } /// The view should not save view state in the browser. [DefaultValue(false), Category("Behavior"), Description("The view should not save view state in the browser.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoBrowserViewState { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoBrowserViewState); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoBrowserViewState, value); } /// Do not display a column header in the view in any view mode. [DefaultValue(false), Category("Appearance"), Description("Do not display a column header in the view in any view mode.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoColumnHeader { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoColumnHeader); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoColumnHeader, value); } /// Do not re-enumerate the view (or drop the current contents of the view) when the view is refreshed. [DefaultValue(false), Category("Behavior"), Description("Do not re-enumerate the view (or drop the current contents of the view) when the view is refreshed.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoEnumOnRefresh { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoEnumOnRefresh); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoEnumOnRefresh, value); } /// Do not display filters in the view. [DefaultValue(false), Category("Appearance"), Description("Do not display filters in the view.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoFilters { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoFilters); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoFilters, value); } /// Do not allow grouping in the view. [DefaultValue(false), Category("Appearance"), Description("Do not allow grouping in the view.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoGrouping { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoGrouping); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoGrouping, value); } /// Only show the column header in details view mode. [DefaultValue(false), Category("Appearance"), Description("Only show the column header in details view mode.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoHeaderInAllViews { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoHeaderInAllViews); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoHeaderInAllViews, value); } /// The view should not display icons. [DefaultValue(false), Category("Appearance"), Description("The view should not display icons.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoIcons { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoIcons); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoIcons, value); } /// Introduced in Windows Vista. Do not persist the view state. [DefaultValue(false), Category("Behavior"), Description("Do not persist the view state.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoPersistViewState { get => IsNavFlagSet(ExplorerBrowserNavigateOptions.NoPersistViewState); set => SetNavFlag(ExplorerBrowserNavigateOptions.NoPersistViewState, value); } /// Do not add scroll bars. This is used only for the desktop. [Browsable(false), DefaultValue(false), Category("Appearance"), Description("Do not add scroll bars.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoScrollBars { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoScrollBars); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoScrollBars, value); } /// Do not show subfolders. [DefaultValue(false), Category("Appearance"), Description("Do not show subfolders.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoSubfolders { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.NoSubfolders); set => SetContentFlag(ExplorerBrowserContentSectionOptions.NoSubfolders, value); } /// Do not update the travel log. [DefaultValue(false), Category("Behavior"), Description("Do not update the travel log.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoTravelLog { get => IsNavFlagSet(ExplorerBrowserNavigateOptions.NoTravelLog); set => SetNavFlag(ExplorerBrowserNavigateOptions.NoTravelLog, value); } /// Do not use a wrapper window. This flag is used with legacy clients that need the browser parented directly on themselves. [Browsable(false), DefaultValue(false), Category("Behavior"), Description("Do not use a wrapper window.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool NoWrapperWindow { get => IsNavFlagSet(ExplorerBrowserNavigateOptions.NoWrapperWindow); set => SetNavFlag(ExplorerBrowserNavigateOptions.NoWrapperWindow, value); } /// Controls the visibility of the various ExplorerBrowser panes on subsequent navigation [Category("Appearance"), Description("Set visibility of child panes.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public ExplorerBrowserPaneVisibility PaneVisibility { get; } = new ExplorerBrowserPaneVisibility(); /// The name of the property bag used to persist changes to the ExplorerBrowser's view state. [Browsable(false), Category("Data"), Description("Name of the property bag used to persist changes to the ExplorerBrowser's view state")] public string PropertyBagName { get => propertyBagName; set { propertyBagName = value; explorerBrowserControl?.SetPropertyBag(propertyBagName); } } /// The set of selected ShellItems in the Explorer Browser [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IReadOnlyList SelectedItems { get; } /// Navigate with a single click. [DefaultValue(false), Category("Behavior"), Description("Navigate with a single click.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool SingleClickActivate { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.SingleClickActivate); set => SetContentFlag(ExplorerBrowserContentSectionOptions.SingleClickActivate, value); } /// Do not allow more than a single item to be selected. [DefaultValue(false), Category("Behavior"), Description("Do not allow more than a single item to be selected.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool SingleSelection { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.SingleSelection); set => SetContentFlag(ExplorerBrowserContentSectionOptions.SingleSelection, value); } /// The view should list the number of items displayed in each group. [DefaultValue(false), Category("Appearance"), Description("The view should list the number of items displayed in each group.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool SubsetGroup { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.SubsetGroup); set => SetContentFlag(ExplorerBrowserContentSectionOptions.SubsetGroup, value); } /// The size of the thumbnails in pixels. [Category("Appearance"), DefaultValue(defaultThumbnailSize), Description("The size of the thumbnails in pixels.")] public int ThumbnailSize { get { var iFV2 = GetFolderView2(); if (iFV2 is null) return thumbnailSize; try { iFV2.GetViewModeAndIconSize(out var fvm, out var iconSize); return thumbnailSize = iconSize; } finally { iFV2 = null; } } set { var iFV2 = GetFolderView2(); if (iFV2 is null) return; try { iFV2.GetViewModeAndIconSize(out var fvm, out var iconSize); iFV2.SetViewModeAndIconSize(fvm, thumbnailSize = value); } finally { iFV2 = null; } } } /// Draw transparently. This is used only for the desktop. [Browsable(false), DefaultValue(false), Category("Appearance"), Description("Draw transparently.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool Transparent { get => IsContentFlagSet(ExplorerBrowserContentSectionOptions.Transparent); set => SetContentFlag(ExplorerBrowserContentSectionOptions.Transparent, value); } /// Show WebView for SharePoint sites. [Browsable(false), DefaultValue(false), Category("Behavior"), Description("Show WebView for SharePoint sites.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool UseHtmlSharePointView { get => IsNavFlagSet(ExplorerBrowserNavigateOptions.HtmlSharePointView); set => SetNavFlag(ExplorerBrowserNavigateOptions.HtmlSharePointView, value); } /// The viewing mode of the Explorer Browser [DefaultValue(typeof(ExplorerBrowserViewMode), "Auto"), Category("Appearance"), Description("The viewing mode of the Explorer Browser.")] public ExplorerBrowserViewMode ViewMode { get => (ExplorerBrowserViewMode)folderSettings.ViewMode; set { folderSettings.ViewMode = (FOLDERVIEWMODE)value; explorerBrowserControl?.SetFolderSettings(folderSettings); } } /// protected override Size DefaultSize => new Size(200, 150); /// Removes all items from the results folder. public void ClearCustomItems() => explorerBrowserControl?.RemoveAll(); /// Navigates to the last item in the navigation history list. This does not change the set of locations in the navigation log. /// True if the navigation succeeded, false if it failed for any reason. public bool GoBack() => History.NavigateLog(NavigationLogDirection.Backward); /// Navigates to the next item in the navigation history list. This does not change the set of locations in the navigation log. /// True if the navigation succeeded, false if it failed for any reason. public bool GoForward() => History.NavigateLog(NavigationLogDirection.Forward); /// Creates a custom folder and fills it with items. /// /// An interface pointer on the source object that will fill the control. This can be an or any object that /// can be used with . /// /// One of the values. public void LoadCustomItems(object obj, ExplorerBrowserLoadFlags flags = ExplorerBrowserLoadFlags.None) => explorerBrowserControl?.FillFromObject(obj, (EXPLORER_BROWSER_FILL_FLAGS)flags); /// /// Clears the Explorer Browser of existing content, fills it with content from the specified container, and adds a new point to the /// Travel Log. /// /// The shell container to navigate to. /// The category of the . public void Navigate(ShellItem shellItem, ExplorerBrowserNavigationItemCategory category = ExplorerBrowserNavigationItemCategory.Absolute) { if (shellItem == null) throw new ArgumentNullException(nameof(shellItem)); if (explorerBrowserControl == null) { antecreationNavigationTarget = new Tuple(shellItem, category); } else { try { explorerBrowserControl.BrowseToObject(shellItem.IShellItem, (SBSP)category); } catch (COMException e) { if (e.ErrorCode == HRESULT_RESOURCE_IN_USE || e.ErrorCode == HRESULT_CANCELLED) { OnNavigationFailed(new NavigationFailedEventArgs { FailedLocation = shellItem }); } else { throw new ArgumentException("Unable to browse to this shell item.", nameof(shellItem), e); } } catch (Exception e) { throw new ArgumentException("Unable to browse to this shell item.", nameof(shellItem), e); } } } /// Navigate within the navigation log. This does not change the set of locations in the navigation log. /// An index into the navigation logs Locations collection. /// True if the navigation succeeded, false if it failed for any reason. public bool NavigateToHistoryIndex(int historyIndex) => History.NavigateLog(historyIndex); HRESULT ICommDlgBrowser3.GetCurrentFilter(StringBuilder pszFileSpec, int cchFileSpec) => HRESULT.S_OK; HRESULT ICommDlgBrowser3.GetDefaultMenuText(IShellView ppshv, StringBuilder pszText, int cchMax) => HRESULT.S_FALSE; HRESULT IExplorerPaneVisibility.GetPaneState(in Guid ep, out EXPLORERPANESTATE peps) { switch (ep) { case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_AdvQueryPane): peps = (EXPLORERPANESTATE)PaneVisibility.AdvancedQuery; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_Commands): peps = (EXPLORERPANESTATE)PaneVisibility.Commands; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_Commands_Organize): peps = (EXPLORERPANESTATE)PaneVisibility.CommandsOrganize; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_Commands_View): peps = (EXPLORERPANESTATE)PaneVisibility.CommandsView; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_DetailsPane): peps = (EXPLORERPANESTATE)PaneVisibility.Details; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_NavPane): peps = (EXPLORERPANESTATE)PaneVisibility.Navigation; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_PreviewPane): peps = (EXPLORERPANESTATE)PaneVisibility.Preview; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_QueryPane): peps = (EXPLORERPANESTATE)PaneVisibility.Query; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_Ribbon): peps = (EXPLORERPANESTATE)PaneVisibility.Ribbon; break; case var a when a.Equals(IExplorerPaneVisibilityConstants.EP_StatusBar): peps = (EXPLORERPANESTATE)PaneVisibility.StatusBar; break; default: peps = EXPLORERPANESTATE.EPS_DONTCARE; break; } return HRESULT.S_OK; } HRESULT ICommDlgBrowser3.GetViewFlags(out CDB2GVF pdwFlags) { pdwFlags = CDB2GVF.CDB2GVF_SHOWALLFILES; return HRESULT.S_OK; } HRESULT ICommDlgBrowser3.IncludeObject(IShellView ppshv, IntPtr pidl) { OnItemsChanged(); return HRESULT.S_OK; } //HRESULT ICommDlgBrowser.IncludeObject(IShellView ppshv, PIDL pidl) => ((ICommDlgBrowser3)this).IncludeObject(ppshv, pidl); HRESULT ICommDlgBrowser3.Notify(IShellView ppshv, CDB2N dwNotifyType) => HRESULT.S_OK; HRESULT ICommDlgBrowser3.OnColumnClicked(IShellView ppshv, int iColumn) => HRESULT.S_OK; HRESULT ICommDlgBrowser3.OnDefaultCommand(IShellView ppshv) => HRESULT.S_FALSE; //HRESULT ICommDlgBrowser.OnDefaultCommand(IShellView ppshv) => ((ICommDlgBrowser3)this).OnDefaultCommand(ppshv); HRESULT IExplorerBrowserEvents.OnNavigationComplete(IntPtr pidlFolder) { folderSettings.ViewMode = GetCurrentViewMode(); OnNavigated(new NavigatedEventArgs { NewLocation = new ShellItem(pidlFolder) }); return HRESULT.S_OK; } HRESULT IExplorerBrowserEvents.OnNavigationFailed(IntPtr pidlFolder) { OnNavigationFailed(new NavigationFailedEventArgs { FailedLocation = new ShellItem(pidlFolder) }); return HRESULT.S_OK; } HRESULT IExplorerBrowserEvents.OnNavigationPending(IntPtr pidlFolder) { OnNavigating(new NavigatingEventArgs { PendingLocation = new ShellItem(pidlFolder) }, out var cancelled); return cancelled ? (HRESULT)HRESULT_CANCELLED : HRESULT.S_OK; } HRESULT ICommDlgBrowser3.OnPreViewCreated(IShellView ppshv) => HRESULT.S_OK; HRESULT ICommDlgBrowser3.OnStateChange(IShellView ppshv, CDBOSC uChange) { if (uChange == CDBOSC.CDBOSC_SELCHANGE) OnSelectionChanged(); return HRESULT.S_OK; } //HRESULT ICommDlgBrowser.OnStateChange(IShellView ppshv, CDBOSC uChange) => ((ICommDlgBrowser3)this).OnStateChange(ppshv, uChange); HRESULT IExplorerBrowserEvents.OnViewCreated(IShellView psv) { viewEvents.ConnectToView((IShellView)psv); return HRESULT.S_OK; } bool IMessageFilter.PreFilterMessage(ref Message m) => (explorerBrowserControl as IInputObject)?.TranslateAcceleratorIO(m.ToMSG()).Succeeded ?? false; HRESULT IServiceProvider.QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject) { HRESULT hr = HRESULT.E_NOINTERFACE; ppvObject = default; if (guidService.Equals(typeof(IExplorerPaneVisibility).GUID)) { //hr = InteropExtensions.QueryInterface(this, guidService, out ppvObject); ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IExplorerPaneVisibility)); hr = HRESULT.S_OK; } else if (guidService.Equals(IID_ICommDlgBrowser) && (riid.Equals(IID_ICommDlgBrowser) || riid.Equals(typeof(ICommDlgBrowser3).GUID))) { //hr = InteropExtensions.QueryInterface(this, riid, out ppvObject); ppvObject = Marshal.GetComInterfaceForObject(this, typeof(ICommDlgBrowser3)); hr = HRESULT.S_OK; } return hr; } /// Gets the IFolderView2 interface from the explorer browser. /// An instance. internal IFolderView2 GetFolderView2() => explorerBrowserControl?.GetCurrentView(); /// Gets the items in the ExplorerBrowser as an IShellItemArray /// internal IShellItemArray GetItemsArray(SVGIO opt) { var iFV2 = GetFolderView2(); if (iFV2 is null) return null; try { return iFV2.Items(opt); } finally { iFV2 = null; } } /// Raises the event. protected internal virtual void OnItemsChanged() => ItemsChanged?.Invoke(this, EventArgs.Empty); /// Raises the event. protected internal virtual void OnItemsEnumerated() => ItemsEnumerated?.Invoke(this, EventArgs.Empty); /// Raises the event. protected internal virtual void OnNavigated(NavigatedEventArgs ncevent) { if (ncevent?.NewLocation == null) return; Navigated?.Invoke(this, ncevent); } /// Raises the event. protected internal virtual void OnNavigating(NavigatingEventArgs npevent, out bool cancelled) { cancelled = false; if (Navigating == null || npevent?.PendingLocation == null) return; foreach (var del in Navigating.GetInvocationList()) { del.DynamicInvoke(new object[] { this, npevent }); if (npevent.Cancel) cancelled = true; } } /// Raises the event. protected internal virtual void OnNavigationFailed(NavigationFailedEventArgs nfevent) { if (nfevent?.FailedLocation == null) return; NavigationFailed?.Invoke(this, nfevent); } /// Raises the event. protected internal virtual void OnSelectedItemModified() => SelectedItemModified?.Invoke(this, EventArgs.Empty); /// Raises the event. protected internal virtual void OnSelectionChanged() => SelectionChanged?.Invoke(this, EventArgs.Empty); /// Called when [create control]. protected override void OnCreateControl() { base.OnCreateControl(); if (!DesignMode) { explorerBrowserControl = new IExplorerBrowser(); // hooks up IExplorerPaneVisibility and ICommDlgBrowser event notifications SetSite(this); // hooks up IExplorerBrowserEvents event notification explorerBrowserControl.Advise(this, out eventsCookie); // sets up ExplorerBrowser view connection point events viewEvents = new ExplorerBrowserViewEvents(this); explorerBrowserControl.Initialize(Handle, ClientRectangle, folderSettings); // Force an initial show frames so that IExplorerPaneVisibility works the first time it is set. This also enables the control // panel to be browsed to. If it is not set, then navigating to the control panel succeeds, but no items are visible in the view. explorerBrowserControl.SetOptions(options); // ExplorerBrowserOptions.NoBorder does not work, so we do it manually... RemoveWindowBorder(); explorerBrowserControl.SetPropertyBag(propertyBagName); if (antecreationNavigationTarget != null) { BeginInvoke(new MethodInvoker(delegate { Navigate(antecreationNavigationTarget.Item1, antecreationNavigationTarget.Item2); antecreationNavigationTarget = null; })); } } Application.AddMessageFilter(this); } /// Cleans up the explorer browser events+object when the window is being taken down. /// An EventArgs that contains event data. protected override void OnHandleDestroyed(EventArgs e) { if (explorerBrowserControl != null) { // unhook events viewEvents.DisconnectFromView(); explorerBrowserControl.Unadvise(eventsCookie); SetSite(null); // destroy the explorer browser control explorerBrowserControl.Destroy(); // release com reference to it explorerBrowserControl = null; } base.OnHandleDestroyed(e); } /// Raises the event. /// The instance containing the event data. protected override void OnPaint(PaintEventArgs pe) { if (DesignMode && pe != null) { var cr = ClientRectangle; pe.Graphics.FillRectangle(SystemBrushes.Window, cr); if (VisualStyleRenderer.IsSupported) { var btn = new VisualStyleRenderer(VisualStyleElement.ScrollBar.ArrowButton.UpDisabled); var sz = btn.GetPartSize(pe.Graphics, ThemeSizeType.True); var rsb = new Rectangle(cr.X + cr.Width - sz.Width, cr.Y, sz.Width, cr.Height); new VisualStyleRenderer(VisualStyleElement.ScrollBar.LowerTrackVertical.Disabled).DrawBackground(pe.Graphics, rsb); rsb.Height = sz.Height; btn.DrawBackground(pe.Graphics, rsb); rsb.Offset(0, cr.Height - sz.Height); new VisualStyleRenderer(VisualStyleElement.ScrollBar.ArrowButton.DownDisabled).DrawBackground(pe.Graphics, rsb); } ControlPaint.DrawBorder(pe.Graphics, cr, SystemColors.WindowFrame, ButtonBorderStyle.Solid); using (var font = new Font("Segoe UI", 9)) using (var sf = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Near }) { pe.Graphics.DrawString(nameof(ExplorerBrowser), font, SystemBrushes.GrayText, cr, sf); } } base.OnPaint(pe); } /// Sizes the native control to match the WinForms control wrapper. /// Contains information about the size changed event. protected override void OnSizeChanged(EventArgs e) { explorerBrowserControl?.SetRect(default, ClientRectangle); base.OnSizeChanged(e); } private FOLDERVIEWMODE GetCurrentViewMode() { var ifv2 = GetFolderView2(); if (ifv2 is null) return 0; try { return ifv2.GetCurrentViewMode(); } finally { ifv2 = null; } } private bool IsContentFlagSet(ExplorerBrowserContentSectionOptions flag) => folderSettings.fFlags.IsFlagSet((FOLDERFLAGS)flag); private bool IsNavFlagSet(ExplorerBrowserNavigateOptions flag) => NavigationFlags.IsFlagSet(flag); /// Find the native control handle, remove its border style, then ask for a redraw. private void RemoveWindowBorder() { // There is an option (EBO_NOBORDER) to avoid showing a border on the native ExplorerBrowser control so we wouldn't have to // remove it afterwards, but: // 1. It's not implemented by the Windows API Code Pack // 2. The flag doesn't seem to work anyway (tested on 7 and 8.1) For reference: EXPLORER_BROWSER_OPTIONS https://msdn.microsoft.com/en-us/library/windows/desktop/bb762501(v=vs.85).aspx var hwnd = FindWindowEx(Handle, default, "ExplorerBrowserControl", default); var explorerBrowserStyle = (WindowStyles)GetWindowLongAuto(hwnd, WindowLongFlags.GWL_STYLE).ToInt32(); SetWindowLong(hwnd, WindowLongFlags.GWL_STYLE, (int)explorerBrowserStyle.ClearFlags(WindowStyles.WS_CAPTION | WindowStyles.WS_BORDER)); SetWindowPos(hwnd, default, 0, 0, 0, 0, SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); } private void ResetPropertyBagName() => PropertyBagName = defaultPropBagName; private void SetContentFlag(ExplorerBrowserContentSectionOptions flag, bool value) { folderSettings.fFlags = folderSettings.fFlags.SetFlags((FOLDERFLAGS)flag, value); explorerBrowserControl?.SetFolderSettings(folderSettings); } private void SetNavFlag(ExplorerBrowserNavigateOptions flag, bool value) => NavigationFlags = NavigationFlags.SetFlags(flag, value); private void SetSite(IServiceProvider sp) => (explorerBrowserControl as IObjectWithSite)?.SetSite(sp); private bool ShouldSerializePropertyBagName() => propertyBagName != defaultPropBagName; /// The navigation log is a history of the locations visited by the explorer browser. public class ExplorerBrowserNavigationLog { private ExplorerBrowser parent = null; /// The pending navigation log action. null if the user is not navigating via the navigation log. private PendingNavigation pendingNavigation; internal ExplorerBrowserNavigationLog(ExplorerBrowser parent) { // Hook navigation events from the parent to distinguish between navigation log induced navigation, and other navigations. this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); this.parent.Navigated += OnNavigated; this.parent.NavigationFailed += OnNavigationFailed; } /// Fires when the navigation log changes or the current navigation position changes public event EventHandler NavigationLogChanged; /// Indicates the presence of locations in the log that can be reached by calling Navigate(Backward) public bool CanNavigateBackward => CurrentLocationIndex > 0; /// Indicates the presence of locations in the log that can be reached by calling Navigate(Forward) public bool CanNavigateForward => CurrentLocationIndex < Locations.Count - 1; /// Gets the shell object in the Locations collection pointed to by CurrentLocationIndex. public ShellItem CurrentLocation => CurrentLocationIndex < 0 ? null : Locations[CurrentLocationIndex]; /// /// An index into the Locations collection. The ShellItem pointed to by this index is the current location of the ExplorerBrowser. /// public int CurrentLocationIndex { get; set; } = -1; /// The navigation log public List Locations { get; } = new List(); /// Clears the contents of the navigation log. public void Clear() { if (Locations.Count == 0) return; var oldCanNavigateBackward = CanNavigateBackward; var oldCanNavigateForward = CanNavigateForward; Locations.Clear(); CurrentLocationIndex = -1; var args = new NavigationLogEventArgs { LocationsChanged = true, CanNavigateBackwardChanged = oldCanNavigateBackward != CanNavigateBackward, CanNavigateForwardChanged = oldCanNavigateForward != CanNavigateForward }; NavigationLogChanged?.Invoke(this, args); } internal bool NavigateLog(NavigationLogDirection direction) { // determine proper index to navigate to var locationIndex = 0; if (direction == NavigationLogDirection.Backward && CanNavigateBackward) { locationIndex = CurrentLocationIndex - 1; } else if (direction == NavigationLogDirection.Forward && CanNavigateForward) { locationIndex = CurrentLocationIndex + 1; } else { return false; } // initiate traversal request var location = Locations[locationIndex]; pendingNavigation = new PendingNavigation(location, locationIndex); parent.Navigate(location); return true; } internal bool NavigateLog(int index) { // can't go anywhere if (index >= Locations.Count || index < 0) return false; // no need to re navigate to the same location if (index == CurrentLocationIndex) return false; // initiate traversal request var location = Locations[index]; pendingNavigation = new PendingNavigation(location, index); parent.Navigate(location); return true; } private void OnNavigated(object sender, NavigatedEventArgs args) { var eventArgs = new NavigationLogEventArgs(); var oldCanNavigateBackward = CanNavigateBackward; var oldCanNavigateForward = CanNavigateForward; if (pendingNavigation != null) { // navigation log traversal in progress // determine if new location is the same as the traversal request var shellItemsEqual = pendingNavigation.Location.IShellItem.Compare(args.NewLocation.IShellItem, SICHINTF.SICHINT_ALLFIELDS) == 0; if (shellItemsEqual == false) { // new location is different than traversal request, behave is if it never happened! remove history following // currentLocationIndex, append new item if (CurrentLocationIndex < Locations.Count - 1) { Locations.RemoveRange(CurrentLocationIndex + 1, Locations.Count - (CurrentLocationIndex + 1)); } Locations.Add(args.NewLocation); CurrentLocationIndex = Locations.Count - 1; eventArgs.LocationsChanged = true; } else { // log traversal successful, update index CurrentLocationIndex = pendingNavigation.Index; eventArgs.LocationsChanged = false; } pendingNavigation = null; } else { // remove history following currentLocationIndex, append new item if (CurrentLocationIndex < Locations.Count - 1) { Locations.RemoveRange(CurrentLocationIndex + 1, Locations.Count - (CurrentLocationIndex + 1)); } Locations.Add(args.NewLocation); CurrentLocationIndex = Locations.Count - 1; eventArgs.LocationsChanged = true; } // update event args eventArgs.CanNavigateBackwardChanged = oldCanNavigateBackward != CanNavigateBackward; eventArgs.CanNavigateForwardChanged = oldCanNavigateForward != CanNavigateForward; NavigationLogChanged?.Invoke(this, eventArgs); } private void OnNavigationFailed(object sender, NavigationFailedEventArgs args) => pendingNavigation = null; /// A navigation traversal request private class PendingNavigation { internal PendingNavigation(ShellItem location, int index) { Location = location; Index = index; } internal int Index { get; set; } internal ShellItem Location { get; set; } } } /// Controls the visibility of the various ExplorerBrowser panes on subsequent navigation [TypeConverter(typeof(BetterExpandableObjectConverter)), Serializable] public class ExplorerBrowserPaneVisibility { /// Additional fields and options to aid in a search. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("Additional fields and options to aid in a search.")] public PaneVisibilityState AdvancedQuery { get; set; } = PaneVisibilityState.Default; /// Commands module along the top of the Windows Explorer window. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("Commands module along the top of the Windows Explorer window.")] public PaneVisibilityState Commands { get; set; } = PaneVisibilityState.Default; /// Organize menu within the commands module. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("Organize menu within the commands module.")] public PaneVisibilityState CommandsOrganize { get; set; } = PaneVisibilityState.Default; /// View menu within the commands module. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("View menu within the commands module.")] public PaneVisibilityState CommandsView { get; set; } = PaneVisibilityState.Default; /// Pane showing metadata along the bottom of the Windows Explorer window. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("Pane showing metadata along the bottom of the Windows Explorer window.")] public PaneVisibilityState Details { get; set; } = PaneVisibilityState.Default; /// The pane on the left side of the Windows Explorer window that hosts the folders tree and Favorites. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("The pane on the left side of the Windows Explorer window that hosts the folders tree and Favorites.")] public PaneVisibilityState Navigation { get; set; } = PaneVisibilityState.Default; /// Pane on the right of the Windows Explorer window that shows a large reading preview of the file. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("Pane on the right of the Windows Explorer window that shows a large reading preview of the file.")] public PaneVisibilityState Preview { get; set; } = PaneVisibilityState.Default; /// Quick filter buttons to aid in a search. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("Quick filter buttons to aid in a search.")] public PaneVisibilityState Query { get; set; } = PaneVisibilityState.Default; /// /// Introduced in Windows 8: The ribbon, which is the control that replaced menus and toolbars at the top of many Microsoft applications. /// [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("The ribbon, which is the control that replaced menus and toolbars at the top of many Microsoft applications.")] public PaneVisibilityState Ribbon { get; set; } = PaneVisibilityState.Default; /// Introduced in Windows 8: A status bar that indicates the progress of some process, such as copying or downloading. [DefaultValue(PaneVisibilityState.Default), Category("Appearance"), Description("A status bar that indicates the progress of some process, such as copying or downloading.")] public PaneVisibilityState StatusBar { get; set; } = PaneVisibilityState.Default; } /// This provides a connection point container compatible dispatch interface for hooking into the ExplorerBrowser view. [ComVisible(true)] [ClassInterface(ClassInterfaceType.AutoDual)] private class ExplorerBrowserViewEvents : IDisposable { private const int ContentsChanged = 207; private const int FileListEnumDone = 201; private const int SelectedItemModified = 220; private const int SelectionChanged = 200; private static readonly Guid IID_DShellFolderViewEvents = new Guid("62112AA2-EBE4-11cf-A5FB-0020AFE7292D"); private static readonly Guid IID_IDispatch = new Guid("00020400-0000-0000-C000-000000000046"); private ExplorerBrowser parent; private uint viewConnectionPointCookie; private object viewDispatch; /// Default constructor for ExplorerBrowserViewEvents public ExplorerBrowserViewEvents() : this(null) { } internal ExplorerBrowserViewEvents(ExplorerBrowser parent) => this.parent = parent; /// Finalizes ExplorerBrowserViewEvents ~ExplorerBrowserViewEvents() { Dispose(false); } /// Disconnects and disposes object. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// The contents of the view have changed [DispId(ContentsChanged)] public void ViewContentsChanged() => parent.OnItemsChanged(); /// The enumeration of files in the view is complete [DispId(FileListEnumDone)] public void ViewFileListEnumDone() => parent.OnItemsEnumerated(); /// The selected item in the view has changed (not the same as the selection has changed) [DispId(SelectedItemModified)] public void ViewSelectedItemModified() => parent.OnSelectedItemModified(); /// The view selection has changed [DispId(SelectionChanged)] public void ViewSelectionChanged() => parent.OnSelectionChanged(); internal void ConnectToView(IShellView psv) { DisconnectFromView(); viewDispatch = psv.GetItemObject(SVGIO.SVGIO_BACKGROUND, IID_IDispatch); var hr = ConnectToConnectionPoint(this, IID_DShellFolderViewEvents, true, viewDispatch, ref viewConnectionPointCookie, out var _); if (hr != HRESULT.S_OK) viewDispatch = null; } internal void DisconnectFromView() { if (viewDispatch is null) return; ConnectToConnectionPoint(null, IID_DShellFolderViewEvents, false, viewDispatch, ref viewConnectionPointCookie, out var _); viewDispatch = null; viewConnectionPointCookie = 0; } // These need to be public to be accessible via AutoDual reflection /// Disconnects and disposes object. /// protected virtual void Dispose(bool disposed) { if (disposed) { DisconnectFromView(); } } } /// Represents a collection of attached to an . private class ShellItemCollection : IReadOnlyList { private readonly ExplorerBrowser eb; private readonly SVGIO option; internal ShellItemCollection(ExplorerBrowser eb, SVGIO opt) { this.eb = eb; option = opt; } /// Gets the number of elements in the collection. /// Returns a value. public int Count => (int)Array.GetCount(); private IShellItemArray Array => eb.GetItemsArray(option); private IEnumerable Items { get { var array = Array; for (uint i = 0; i < array.GetCount(); i++) yield return array.GetItemAt(i); } } /// Gets the at the specified index. /// The . /// The zero-based index of the element to get. public ShellItem this[int index] { get { try { return ShellItem.Open(Array.GetItemAt((uint)index)); } catch { return null; } } } /// Returns an enumerator that iterates through the collection. /// An enumerator that can be used to iterate through the collection. public IEnumerator GetEnumerator() => Items.Select(ShellItem.Open).GetEnumerator(); /// Returns an enumerator that iterates through the collection. /// An enumerator that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } /// Event argument for The Navigated event public class NavigatedEventArgs : EventArgs { /// The new location of the explorer browser public ShellItem NewLocation { get; set; } } /// Event argument for The Navigating event public class NavigatingEventArgs : EventArgs { /// Set to 'True' to cancel the navigation. public bool Cancel { get; set; } /// The location being navigated to public ShellItem PendingLocation { get; set; } } /// Event argument for the NavigatinoFailed event public class NavigationFailedEventArgs : EventArgs { /// The location the browser would have navigated to. public ShellItem FailedLocation { get; set; } } /// The event argument for NavigationLogChangedEvent public class NavigationLogEventArgs : EventArgs { /// Indicates CanNavigateBackward has changed public bool CanNavigateBackwardChanged { get; set; } /// Indicates CanNavigateForward has changed public bool CanNavigateForwardChanged { get; set; } /// Indicates the Locations collection has changed public bool LocationsChanged { get; set; } } }