diff --git a/PInvoke/NtDll/Winternl.UnicodeString.cs b/PInvoke/NTDll/Winternl.UnicodeString.cs
similarity index 100%
rename from PInvoke/NtDll/Winternl.UnicodeString.cs
rename to PInvoke/NTDll/Winternl.UnicodeString.cs
diff --git a/Windows.Shell/ShellObjects/ShellBrowser.cs b/Windows.Shell/ShellObjects/ShellBrowser.cs
index d67bc2a3..3cc0231b 100644
--- a/Windows.Shell/ShellObjects/ShellBrowser.cs
+++ b/Windows.Shell/ShellObjects/ShellBrowser.cs
@@ -1,205 +1,1177 @@
-using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.IO;
using System.Linq;
-using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
+using System.Runtime.InteropServices;
+using System.Text;
using System.Windows.Forms;
-using Vanara.Extensions;
-using Vanara.InteropServices;
+using System;
+
using Vanara.PInvoke;
-using static Vanara.PInvoke.Ole32;
using static Vanara.PInvoke.Shell32;
namespace Vanara.Windows.Shell
{
- /// A basic implementation of IShellBrowser, IOleCommandTarget and ICommDlgBrowser.
- ///
- /// This implementation used a to implement:
- ///
- /// - BrowseObject
- /// - GetWindow
- /// - OnDefaultCommand
- /// - OnStateChange
- ///
- ///
- ///
- ///
- ///
- ///
- [ComVisible(true), ClassInterface(ClassInterfaceType.None)]
- internal class ShellBrowser : IShellBrowser, IOleCommandTarget, Shell32.IServiceProvider, ICommDlgBrowser
- {
- /// The instance from initialization.
- protected readonly ShellView shellView;
+ /// The direction argument for NavigateFromHistory()
+ public enum NavigationLogDirection
+ {
+ /// Navigates forward through the navigation log
+ Forward,
- /// Initializes a new instance of the class with a instance.
- /// The instance.
- /// view
- public ShellBrowser(ShellView view) => shellView = view ?? throw new ArgumentNullException(nameof(view));
+ /// Navigates backward through the travel log
+ Backward
+ }
- /// Gets or sets the progress bar associated with the view.
- /// The progress bar.
- public ProgressBar ProgressBar { get; set; }
+ /// Indicates the viewing mode of the ShellBrowser
+ public enum ShellBrowserViewMode
+ {
+ /// Choose the best view mode for the folder
+ Auto = Shell32.FOLDERVIEWMODE.FVM_AUTO,
-#if NETFRAMEWORK || NETCOREAPP3_0
- /// Gets or sets the status bar associated with the view.
- /// The status bar.
- public StatusBar StatusBar { get; set; }
+ /// (New for Windows7)
+ Content = Shell32.FOLDERVIEWMODE.FVM_CONTENT,
- /// Gets or sets the tool bar associated with the view.
- /// The tool bar.
- public ToolBar ToolBar { get; set; }
-#endif
+ /// Object names and other selected information, such as the size or date last updated, are shown.
+ Details = Shell32.FOLDERVIEWMODE.FVM_DETAILS,
- /// Gets or sets the TreeView associated with the view.
- /// The TreeView.
- public TreeView TreeView { get; set; }
+ /// The view should display medium-size icons.
+ Icon = Shell32.FOLDERVIEWMODE.FVM_ICON,
- ///
- public virtual HRESULT BrowseObject(IntPtr pidl, SBSP wFlags)
- {
- switch (wFlags)
- {
- case var f when f.IsFlagSet(SBSP.SBSP_NAVIGATEBACK):
- shellView.NavigateBack();
- break;
- case var f when f.IsFlagSet(SBSP.SBSP_NAVIGATEFORWARD):
- shellView.NavigateForward();
- break;
- case var f when f.IsFlagSet(SBSP.SBSP_PARENT):
- shellView.NavigateParent();
- break;
- case var f when f.IsFlagSet(SBSP.SBSP_RELATIVE):
- if (ShellItem.Open(shellView.CurrentFolder.IShellFolder, pidl) is ShellFolder sf)
- shellView.Navigate(sf);
- break;
- default:
- shellView.Navigate(new ShellFolder(pidl));
- break;
- }
- return HRESULT.S_OK;
- }
+ /// Object names are displayed in a list view.
+ List = Shell32.FOLDERVIEWMODE.FVM_LIST,
- ///
- public virtual HRESULT ContextSensitiveHelp(bool fEnterMode) => HRESULT.E_NOTIMPL;
+ /// The view should display small icons.
+ SmallIcon = Shell32.FOLDERVIEWMODE.FVM_SMALLICON,
- ///
- public virtual HRESULT EnableModelessSB(bool fEnable) => HRESULT.E_NOTIMPL;
+ /// The view should display thumbnail icons.
+ Thumbnail = Shell32.FOLDERVIEWMODE.FVM_THUMBNAIL,
- ///
- public virtual HRESULT Exec([In, Optional] GuidPtr pguidCmdGroup, uint nCmdID, uint nCmdexecopt, [In, Optional] /* VARIANT* */ IntPtr pvaIn, [In, Out, Optional] /* VARIANT* */ IntPtr pvaOut) => OLECMDERR_E_NOTSUPPORTED;
+ /// The view should display icons in a filmstrip format.
+ ThumbStrip = Shell32.FOLDERVIEWMODE.FVM_THUMBSTRIP,
- ///
- public virtual HRESULT GetControlWindow(FCW id, out HWND phwnd)
- {
- phwnd = id switch
- {
- FCW.FCW_PROGRESS => CheckAndLoad(ProgressBar),
-#if NETFRAMEWORK || NETCOREAPP3_0
- FCW.FCW_STATUS => CheckAndLoad(StatusBar),
- FCW.FCW_TOOLBAR => CheckAndLoad(ToolBar),
-#endif
- FCW.FCW_TREE => CheckAndLoad(TreeView),
- _ => HWND.NULL,
- };
- return phwnd.IsNull ? HRESULT.E_NOTIMPL : HRESULT.S_OK;
+ /// The view should display large icons.
+ Tile = Shell32.FOLDERVIEWMODE.FVM_TILE
+ }
- static HWND CheckAndLoad(Control c) => c != null && c.IsHandleCreated ? c.Handle : HWND.NULL;
- }
+ /// Event argument for The Navigated event.
+ public class ShellBrowserNavigatedEventArgs : EventArgs
+ {
+ /// The new location of the ShellBrowser
+ public ShellFolder CurrentFolder { get; }
- ///
- public virtual HRESULT GetViewStateStream(STGM grfMode, out IStream ppStrm)
- {
- ppStrm = null;
- return HRESULT.E_NOTIMPL;
- }
+ /// Initializes a new instance of the class.
+ public ShellBrowserNavigatedEventArgs(ShellFolder currentFolder)
+ {
+ this.CurrentFolder = currentFolder ?? throw new ArgumentNullException(nameof(currentFolder));
+ }
+ }
- ///
- public virtual HRESULT GetWindow(out HWND phwnd)
- {
- phwnd = shellView.shellViewWindow;
- return HRESULT.S_OK;
- }
+ ///
+ /// Encapsulates a -Implementation within an .
+ ///
+ /// Implements the following Interfaces:
+ /// -
+ /// -
+ /// -
+ ///
+ /// For more Information on used techniques see:
+ /// -
+ ///
+ ///
+ /// Known Issues:
+ /// - Using windows 10, the virtual Quick-Access folder doesn't get displayed properly. It has to be grouped by "Group"
+ /// (as shown in Windows Explorer UI), but I couldn't find the OLE-Property for this.
+ /// Also, if using Groups, the Frequent Files List doesn't have its Icons. Maybe we have to bind to another version
+ /// of ComCtrls to get this rendered properly - That's just an idea though, cause the Collapse-/Expand-Icons of the
+ /// Groups have the Windows Vista / Windows 7-Theme, not the Windows 10 Theme as I can see.
+ /// - DONE: Keyboard input doesn't work so far.
+ /// - DONE: Only Details-Mode should have column headers: (Using Shell32.FOLDERFLAGS.FWF_NOHEADERINALLVIEWS)
+ /// https://stackoverflow.com/questions/11776266/ishellview-columnheaders-not-hidden-if-autoview-does-not-choose-details
+ /// - TODO: CustomDraw, when currently no shellView available
+ /// - DONE: Network folder: E_FAIL => DONE: Returning HRESULT.E_NOTIMPL from MessageSFVCB fixes this
+ /// - DONE: Disk Drive (empty): E_CANCELLED_BY_USER
+ /// - DONE: Disable header in Details view when grouping is enabled
+ /// - DONE: Creating ViewWindow using '.CreateViewWindow()' fails on Zip-Folders; => Fixed again by returning HRESULT.E_NOTIMPL from MessageSFVCB
+ /// - TODO: internal static readonly bool IsMinVista = Environment.OSVersion.Version.Major >= 6; // TODO: We use one interface, afaik, that only works in vista and above: IFolderView2
+ /// - TODO: Windows 10' Quick Access folder has a special type of grouping, can't find out how this works yet.
+ /// As soon as we would be able to get all the available properties for an particular item, we would be able found out how this grouping works.
+ /// However, it seems to be a special group, since folders are Tiles, whereas files are shown in Details mode.
+ /// - NOTE: The grouping is done by 'Group'. Activate it using "Group by->More->Group", and then do the grouping.
+ /// However, the Icons for 'Recent Files'-Group get lost.
+ /// - TODO: ViewMode-Property, Thumbnailsize => Set ThumbnailSize for Large, ExtraLarge, etc.
+ /// - DONE: Keyboard-Handling
+ /// - DONE: BrowseObject ->Parent -> Relative
+ /// - TODO: Properties in design editor!!!
+ /// - TODO: Write History correctly!
+ /// - TODO: Check getting / losing Focus! again
+ /// - TODO: Context-Menu -> "Open File Location" doesn't work on folder "Quick Access"
+ /// - TODO: When columns get reordered in details mode, then navigate to another folder, then back => column content gets messed
+ ///
+ /// NOTE: https://stackoverflow.com/questions/7698602/how-to-get-embedded-explorer-ishellview-to-be-browsable-i-e-trigger-browseobje
+ /// NOTE: https://stackoverflow.com/questions/54390268/getting-the-current-ishellview-user-is-interacting-with
+ /// NOTE: https://www.codeproject.com/Articles/35197/Undocumented-List-View-Features // IMPORTANT!
+ /// NOTE: https://answers.microsoft.com/en-us/windows/forum/windows_10-files-winpc/windows-10-quick-access-folders-grouped-separately/ecd4be4a-1847-4327-8c44-5aa96e0120b8
+ ///
+ [ComVisible(true)]
+ [ClassInterface(ClassInterfaceType.None)]
+ [Description("A Shell object that displays a list of Shell Items.")]
+ [Guid("B8B0F852-9527-4CA8-AB1D-648AE95B618E")]
+ public class ShellBrowser
+ : UserControl
+ , IWin32Window
+ , IShellBrowser
+ , Shell32.IServiceProvider
+ {
+ private FOLDERSETTINGS folderSettings = new(
+ FOLDERVIEWMODE.FVM_AUTO,
+ FOLDERFLAGS.FWF_NOHEADERINALLVIEWS |
+ FOLDERFLAGS.FWF_NOWEBVIEW |
+ FOLDERFLAGS.FWF_USESEARCHFOLDER);
- ///
- public virtual HRESULT IncludeObject(IShellView ppshv, IntPtr pidl) => shellView.IncludeItem(pidl) ? HRESULT.S_OK : HRESULT.S_FALSE;
+ internal const int defaultThumbnailSize = 32;
- ///
- public virtual HRESULT InsertMenusSB(HMENU hmenuShared, ref Ole32.OLEMENUGROUPWIDTHS lpMenuWidths) => HRESULT.E_NOTIMPL;
+ private readonly StringBuilder processCmdKeyClassName = new(processCmdKeyClassNameMaxLength + 1);
+ private const int processCmdKeyClassNameMaxLength = 31;
+ private const string processCmdKeyClassNameEdit = "Edit";
- ///
- public virtual HRESULT OnDefaultCommand(IShellView ppshv)
- {
- var selected = shellView.SelectedItems;
+ private IStream viewStateStream;
+ private string viewStateStreamIdentifier;
- if (selected.Length > 0 && selected[0].IsFolder)
- {
- try { shellView.Navigate(selected[0] is ShellFolder f ? f : selected[0].Parent); }
- catch { }
- }
- else
- {
- shellView.OnDoubleClick(EventArgs.Empty);
- }
+ #region Properties ====================================================================================================
- return HRESULT.S_OK;
- }
+ ///
+ ///
+ ///
+ /// Note: I've tried using ComCtl32.ListViewMessage.LVM_SETBKIMAGE, but this doesn't work properly.
+ /// That's why this property has been hidden.
+ ///
+ [Bindable(false)]
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage { get => base.BackgroundImage; }
- ///
- public virtual HRESULT OnStateChange(IShellView ppshv, CDBOSC uChange)
- {
- if (uChange == CDBOSC.CDBOSC_SELCHANGE)
- shellView.OnSelectionChanged();
- return HRESULT.S_OK;
- }
+ ///
+ protected override Size DefaultSize => new(200, 150);
- ///
- public virtual HRESULT OnViewWindowActive(IShellView ppshv) => HRESULT.E_NOTIMPL;
+ private string emptyFolderText = "This folder is empty.";
- ///
- public virtual HRESULT QueryActiveShellView(out IShellView ppshv)
- {
- ppshv = null;
- return HRESULT.E_NOTIMPL;
- }
+ /// The default text that is displayed when an empty folder is shown
+ [Category("Appearance"), DefaultValue("This folder is empty."), Description("The default text that is displayed when an empty folder is shown.")]
+ public string EmptyFolderText
+ {
+ get => this.emptyFolderText;
+ set
+ {
+ this.emptyFolderText = value;
- ///
- public virtual HRESULT QueryService(in Guid guidService, in Guid riid, out IntPtr ppvObject)
- {
- var lriid = riid;
- var i = GetType().GetInterfaces().FirstOrDefault(i => i.IsCOMObject && i.GUID == lriid);
- if (i is null)
- {
- ppvObject = IntPtr.Zero;
- return HRESULT.E_NOINTERFACE;
- }
+ if (this.ViewHandler.IsValid)
+ this.ViewHandler.Text = value;
+ }
+ }
- ppvObject = Marshal.GetComInterfaceForObject(this, i);
- return HRESULT.S_OK;
- }
+ /// Contains the navigation history of the ShellBrowser
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public ShellNavigationHistory History { get; private set; }
- ///
- public virtual HRESULT QueryStatus([In, Optional] GuidPtr pguidCmdGroup, uint cCmds, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] OLECMD[] prgCmds, [In, Out, Optional] IntPtr /* OLECMDTEXT* */ pCmdText) => HRESULT.E_FAIL;
+ /// The set of ShellItems in the ShellBrowser
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public IReadOnlyList Items { get; }
- ///
- public virtual HRESULT RemoveMenusSB(HMENU hmenuShared) => HRESULT.E_NOTIMPL;
+ /// The set of selected ShellItems in the ShellBrowser
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public IReadOnlyList SelectedItems { get; }
- ///
- public virtual HRESULT SendControlMsg(FCW id, uint uMsg, IntPtr wParam, IntPtr lParam, out IntPtr pret)
- {
- pret = default;
- return HRESULT.E_NOTIMPL;
- }
+ /// The size of the thumbnails in pixels.
+ [Category("Appearance"), DefaultValue(defaultThumbnailSize), Description("The size of the thumbnails in pixels.")]
+ public int ThumbnailSize
+ {
+ get => (this.ViewHandler.IsValid) ? this.ViewHandler.ThumbnailSize : defaultThumbnailSize;
+ set
+ {
+ if (this.ViewHandler.IsValid)
+ this.ViewHandler.ThumbnailSize = value;
+ }
+ }
- ///
- public virtual HRESULT SetMenuSB(HMENU hmenuShared, IntPtr holemenuRes, HWND hwndActiveObject) => HRESULT.E_NOTIMPL;
+ /// The that is currently in use.
+ protected ShellBrowserViewHandler ViewHandler { get; private set; }
- ///
- public virtual HRESULT SetStatusTextSB(string pszStatusText) => HRESULT.E_NOTIMPL;
+ /// The viewing mode of the ShellBrowser
+ /// Internally, this uses LVM_SETVIEW and LVM_GETVIEW messages on the ListView control
+ [Category("Appearance"), DefaultValue(typeof(ShellBrowserViewMode), "Auto"), Description("The viewing mode of the ShellBrowser.")]
+ public ShellBrowserViewMode ViewMode
+ {
+ get => (ShellBrowserViewMode)this.folderSettings.ViewMode;
+ set
+ {
+ // TODO: Set ThumbnailSize accordingly?
+ this.folderSettings.ViewMode = (FOLDERVIEWMODE)value;
- ///
- public virtual HRESULT SetToolbarItems(ComCtl32.TBBUTTON[] lpButtons, uint nButtons, FCT uFlags) => HRESULT.E_NOTIMPL;
+ if (this.ViewHandler.IsValid)
+ this.ViewHandler.ViewMode = this.folderSettings.ViewMode;
+ }
+ }
- ///
- public virtual HRESULT TranslateAcceleratorSB(ref MSG pmsg, ushort wID) => HRESULT.E_NOTIMPL;
- }
-}
\ No newline at end of file
+ /// The Registry Key where Browser ViewStates get serialized
+ /// Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams\\
+ [Category("Behavior"), Description("The Registry Key where Browser ViewStates get serialized.")]
+ public string ViewStateRegistryKey { get; set; } =
+ $"Software\\{ Application.CompanyName }\\{ Application.ProductName }\\ShellBrowser\\ViewStateStreams";
+
+ #endregion ============================================================================================================
+
+ #region Published Events ==============================================================================================
+
+ /// Fires when the Items collection changes.
+ [Category("Action"), Description("Items changed.")]
+ public event EventHandler ItemsChanged;
+
+ /// Fires when ShellBrowser has navigated to a new folder.
+ [Category("Action"), Description("ShellBowser has navigated to a new folder.")]
+ public event EventHandler Navigated;
+
+ /// Fires when the SelectedItems collection changes.
+ [Category("Behavior"), Description("Selection changed.")]
+ public event EventHandler SelectionChanged;
+
+ #endregion ============================================================================================================
+
+ /// Initializes a new instance of the class.
+ public ShellBrowser()
+ : base()
+ {
+ this.InitializeComponent();
+
+ this.History = new ShellNavigationHistory();
+ this.Items = new ShellItemCollection(this, SVGIO.SVGIO_ALLVIEW);
+ this.SelectedItems = new ShellItemCollection(this, SVGIO.SVGIO_SELECTION);
+
+ this.Resize += this.ShellBrowser_Resize;
+ this.HandleDestroyed += this.ShellBrowser_HandleDestroyed;
+ }
+
+ /// Process known command keys of the ShellBrowser.
+ /// Windows Message
+ /// Key codes and modifiers
+ /// true if character was processed by the control; otherwise, false
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ // We have to take special care when the ShellView is currently renaming an item:
+ // If message's sender equals class 'Edit', and its parent window is our ViewWindow,
+ // the ShellView is currently showing an Edit-field to let the User edit an item's name.
+ // Thus, we have to pass all Key Strokes directly to the ShellView.
+
+ if (this.ViewHandler.Validated() != null)
+ {
+ // Note: I tried using the LVM_GETEDITCONTROL message for finding the edit control without luck
+ if (User32.GetClassName(msg.HWnd,
+ this.processCmdKeyClassName,
+ ShellBrowser.processCmdKeyClassNameMaxLength) > 0)
+ {
+ if (processCmdKeyClassName.ToString().Equals(ShellBrowser.processCmdKeyClassNameEdit))
+ {
+ // Try to get Edit field's parent 'SysListView32' handle
+ HWND hSysListView32 = User32.GetParent(msg.HWnd);
+
+ if (hSysListView32 != null)
+ {
+ // Try to get SysListView32's parent 'SHELLDLL_DefView' handle
+ HWND hShellDllDefViewWindow = User32.GetParent(hSysListView32);
+
+ if ((hShellDllDefViewWindow != null) && (hShellDllDefViewWindow == this.ViewHandler.ViewWindow))
+ {
+ this.ViewHandler.ShellView.TranslateAccelerator(
+ new MSG(msg.HWnd, (uint)msg.Msg, msg.WParam, msg.LParam));
+
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Process tab key for control focus cycle:
+ // Tab => Focus next control
+ // Tab + Shift => Focus previous control
+ //
+ if ((keyData & Keys.KeyCode) == Keys.Tab)
+ {
+ bool forward = (keyData & Keys.Shift) != Keys.Shift;
+
+ this.Parent.SelectNextControl(ActiveControl,
+ forward: forward,
+ tabStopOnly: true,
+ nested: true,
+ wrap: true);
+
+ return true;
+ }
+
+ //
+ // Process folder navigation shortcuts:
+ // Alt + Left OR BrowserBack => Navigate back in history
+ // Alt + Right OR BrowserForward => Navigate forward in history
+ // Backspace => Navigate to parent folder
+ //
+ switch (keyData)
+ {
+ case Keys.BrowserBack:
+ case Keys.Alt | Keys.Left:
+ this.NavigateBack();
+
+ return true;
+
+ case Keys.BrowserForward:
+ case Keys.Alt | Keys.Right:
+ this.NavigateForward();
+
+ return true;
+
+ case Keys.Back:
+ this.NavigateParent();
+
+ return true;
+ }
+
+ //
+ // Let the ShellView process all other keystrokes
+ //
+ if (this.ViewHandler.Validated() != null)
+ {
+ this.ViewHandler.ShellView.TranslateAccelerator(
+ new MSG(msg.HWnd, (uint)msg.Msg, msg.WParam, msg.LParam));
+
+ return true;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ ///
+ /// 's event handler for event:
+ /// Save ViewState when ShellBrowser gets closed.
+ ///
+ protected internal virtual void ShellBrowser_HandleDestroyed(object sender, EventArgs e)
+ {
+ this.ViewHandler.Validated()?.ShellView.SaveViewState();
+ }
+
+ ///
+ /// 's event handler for event:
+ /// Resize ViewWindow when ShellBrowser gets resized.
+ ///
+ protected internal virtual void ShellBrowser_Resize(object sender, EventArgs e)
+ {
+ this.ViewHandler?.MoveWindow(0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, false);
+ }
+
+ /// Gets the items in the ShellBrowser as an IShellItemArray
+ /// An instance or if not available.
+ internal IShellItemArray GetItemsArray(SVGIO opt)
+ {
+ try
+ {
+ var viewHandler = this.ViewHandler.Validated();
+
+ if (viewHandler != null)
+ return viewHandler.FolderView2.Items(opt);
+ else
+ return null;
+ }
+ catch { return null; }
+ }
+
+ /// Selects all items in the current view.
+ public void SelectAll()
+ {
+ var viewHandler = this.ViewHandler.Validated();
+
+ if (viewHandler != null)
+ {
+ // NOTE: The for-loop is rather slow, so send (Ctrl+A)-KeyDown-Message instead and let the ShellView do the work
+ // for (var i = 0; i < viewHandler.FolderView2.ItemCount(SVGIO.SVGIO_ALLVIEW); i++)
+ // viewHandler.FolderView2.SelectItem(i, SVSIF.SVSI_SELECT);
+ //
+ // TODO: Another way would be to use this Windows Message-Pattern (Workaround #2):
+ // https://stackoverflow.com/questions/9039989/how-to-selectall-in-a-winforms-virtual-listview
+
+ var msg = new Message()
+ {
+ HWnd = (IntPtr)this.ViewHandler.ViewWindow,
+ Msg = (int)User32.WindowMessage.WM_KEYDOWN,
+ };
+
+ this.ProcessCmdKey(ref msg, Keys.Control | Keys.A);
+ }
+ }
+
+ /// Unselects all items in the current view.
+ public void UnselectAll()
+ {
+ var viewHandler = this.ViewHandler.Validated();
+
+ if (viewHandler != null)
+ viewHandler.FolderView2.SelectItem(-1, SVSIF.SVSI_DESELECTOTHERS);
+ }
+
+ /// Raises the event.
+ protected internal virtual void OnItemsChanged() => this.ItemsChanged?.Invoke(this, EventArgs.Empty);
+
+ /// Raises the event.
+ protected internal virtual void OnNavigated(ShellFolder shellFolder)
+ {
+ if (this.Navigated != null)
+ {
+ ShellBrowserNavigatedEventArgs eventArgs = new(shellFolder);
+
+ this.Navigated.Invoke(this, eventArgs);
+ }
+ }
+
+ /// Raises the event.
+ protected internal virtual void OnSelectionChanged() => this.SelectionChanged?.Invoke(this, EventArgs.Empty);
+
+ ///
+ /// 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 NavigateBack()
+ {
+ return this.BrowseObject(IntPtr.Zero, Shell32.SBSP.SBSP_NAVIGATEBACK).Succeeded;
+ }
+
+ ///
+ /// 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 NavigateForward()
+ {
+ return this.BrowseObject(IntPtr.Zero, Shell32.SBSP.SBSP_NAVIGATEFORWARD).Succeeded;
+ }
+
+ ///
+ /// Navigate within the navigation log in a specific direciton. This does not change the set of locations in the navigation log.
+ ///
+ /// The direction to navigate within the navigation logs collection.
+ /// True if the navigation succeeded, false if it failed for any reason.
+ public bool NavigateFromHistory(NavigationLogDirection direction)
+ {
+ return direction switch
+ {
+ NavigationLogDirection.Backward => this.NavigateBack(),
+ NavigationLogDirection.Forward => this.NavigateForward(),
+ _ => false,
+ };
+ }
+
+ /// 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)
+ {
+ using (ShellItem shellFolder = this.History.Seek(historyIndex, SeekOrigin.Current))
+ {
+ if (shellFolder != null)
+ return this.BrowseObject((IntPtr)shellFolder.PIDL,
+ SBSP.SBSP_ABSOLUTE | SBSP.SBSP_WRITENOHISTORY).Succeeded;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Navigates to the parent folder.
+ ///
+ /// True if the navigation succeeded, false if it failed for any reason.
+ public bool NavigateParent()
+ {
+ return this.BrowseObject(IntPtr.Zero, Shell32.SBSP.SBSP_PARENT).Succeeded;
+ }
+
+ #region IShellBrowser interface =======================================================================================
+
+ ///
+ public HRESULT GetWindow(out HWND phwnd)
+ {
+ phwnd = this.Handle;
+ return HRESULT.S_OK;
+ }
+
+ ///
+ public HRESULT ContextSensitiveHelp(bool fEnterMode) => HRESULT.E_NOTIMPL;
+
+ ///
+ public HRESULT InsertMenusSB(HMENU hmenuShared, ref Ole32.OLEMENUGROUPWIDTHS lpMenuWidths) => HRESULT.E_NOTIMPL;
+
+ ///
+ public HRESULT SetMenuSB(HMENU hmenuShared, IntPtr holemenuRes, HWND hwndActiveObject) => HRESULT.E_NOTIMPL;
+
+ ///
+ public HRESULT RemoveMenusSB(HMENU hmenuShared) => HRESULT.E_NOTIMPL;
+
+ ///
+ public HRESULT SetStatusTextSB(string pszStatusText) => HRESULT.E_NOTIMPL;
+
+ ///
+ public HRESULT EnableModelessSB(bool fEnable) => HRESULT.E_NOTIMPL;
+
+ ///
+ public HRESULT TranslateAcceleratorSB(ref MSG pmsg, ushort wID) => HRESULT.E_NOTIMPL;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// HRESULT.STG_E_PATHNOTFOUND if path n found
+ public HRESULT BrowseObject(IntPtr pidl, Shell32.SBSP wFlags)
+ {
+ ShellItem shellObject = null;
+
+ //
+ // The given PIDL equals Desktop, so ignore the other flags
+ //
+ if (ShellFolder.Desktop.PIDL.Equals(pidl))
+ {
+ shellObject = new ShellItem(ShellFolder.Desktop.PIDL);
+ }
+
+ //
+ // SBSP_NAVIGATEBACK stands for the last item in the navigation history list (and ignores the pidl)
+ //
+ else if (wFlags.HasFlag(Shell32.SBSP.SBSP_NAVIGATEBACK))
+ {
+ if (this.History.CanSeekBackward)
+ shellObject = this.History.SeekBackward();
+ else
+ return HRESULT.STG_E_PATHNOTFOUND;
+ }
+
+ //
+ // SBSP_NAVIGATEFORWARD stands for the next item in the navigation history list (and ignores the pidl)
+ //
+ else if (wFlags.HasFlag(Shell32.SBSP.SBSP_NAVIGATEFORWARD))
+ {
+ if (this.History.CanSeekForward)
+ shellObject = this.History.SeekForward();
+ else
+ return HRESULT.STG_E_PATHNOTFOUND;
+ }
+
+ //
+ // SBSP_RELATIVE stands for a pidl relative to the current folder
+ //
+ else if (wFlags.HasFlag(Shell32.SBSP.SBSP_RELATIVE))
+ {
+ var currentObject = this.History.Current;
+
+ var targetObject = PIDLUtil.ILCombine((IntPtr)currentObject.PIDL, pidl);
+
+ shellObject = new ShellItem(targetObject);
+ }
+
+ //
+ // SBSP_PARENT stands for the parent folder (and ignores the pidl)
+ //
+ else if (wFlags.HasFlag(Shell32.SBSP.SBSP_PARENT))
+ {
+ var currentObject = this.History.Current;
+ var parentObject = currentObject.Parent;
+
+ if ((parentObject != null) && (parentObject.PIDL.IsParentOf(currentObject.PIDL)))
+ shellObject = parentObject;
+ else
+ return HRESULT.STG_E_PATHNOTFOUND;
+ }
+
+ //
+ // SBSP_ABSOLUTE as the remaining option stands for an absolute pidl that is given
+ //
+ else
+ {
+ // Remember we are not the owner of this pidl, so clone it to have our own copy on the heap.
+ shellObject = new ShellItem(new PIDL(pidl, true));
+ }
+
+ if (this.InvokeRequired)
+ this.BeginInvoke((Action)(() => BrowseShellItemInternal(shellObject)));
+ else
+ BrowseShellItemInternal(shellObject);
+
+ return HRESULT.S_OK;
+
+ #region BrowseShellItemInternal
+
+ void BrowseShellItemInternal(ShellItem shellItem)
+ {
+ // Save ViewState of current folder
+ this.ViewHandler.Validated()?.ShellView.SaveViewState();
+
+ if (this.viewStateStream != null)
+ Marshal.ReleaseComObject(this.viewStateStream);
+
+ this.viewStateStreamIdentifier = shellItem.ParsingName;
+
+ var viewHandler = new ShellBrowserViewHandler(this,
+ new ShellFolder(shellItem),
+ ref this.folderSettings,
+ ref this.emptyFolderText);
+
+ // Clone the PIDL, to have our own object copy on the heap!
+ if (!wFlags.HasFlag(SBSP.SBSP_WRITENOHISTORY))
+ this.History.Add(new PIDL(viewHandler.ShellFolder.PIDL));
+
+ var oldViewHandler = this.ViewHandler;
+ this.ViewHandler = viewHandler;
+ oldViewHandler?.UIDeactivate();
+ viewHandler.UIActivate();
+ oldViewHandler?.DestroyView();
+
+ this.OnNavigated(viewHandler.ShellFolder);
+ this.OnSelectionChanged();
+ }
+
+ #endregion
+ }
+
+ ///
+ public HRESULT GetViewStateStream(STGM grfMode, out IStream stream)
+ {
+ if (this.viewStateStream != null)
+ Marshal.ReleaseComObject(this.viewStateStream);
+
+ stream = this.viewStateStream = ShlwApi.SHOpenRegStream2(
+ hkey: HKEY.HKEY_CURRENT_USER,
+ pszSubkey: this.ViewStateRegistryKey,
+ pszValue: this.viewStateStreamIdentifier,
+ grfMode: grfMode);
+
+ return stream == null ? HRESULT.E_FAIL : HRESULT.S_OK;
+ }
+
+ ///
+ public HRESULT GetControlWindow(Shell32.FCW id, out HWND hwnd)
+ {
+ hwnd = HWND.NULL;
+ return HRESULT.E_NOTIMPL;
+ }
+
+ ///
+ public HRESULT SendControlMsg(Shell32.FCW id, uint uMsg, IntPtr wParam, IntPtr lParam, out IntPtr pret)
+ {
+ pret = IntPtr.Zero;
+ return HRESULT.E_NOTIMPL;
+ }
+
+ ///
+ public HRESULT QueryActiveShellView(out Shell32.IShellView shellView)
+ {
+ if (this.ViewHandler.Validated() != null)
+ {
+ Marshal.AddRef(Marshal.GetIUnknownForObject(this.ViewHandler.ShellView));
+ shellView = this.ViewHandler.ShellView;
+
+ return HRESULT.S_OK;
+ }
+
+ shellView = null;
+ return HRESULT.E_PENDING;
+ }
+
+ ///
+ public HRESULT OnViewWindowActive(Shell32.IShellView ppshv) => HRESULT.E_NOTIMPL;
+
+ ///
+ public HRESULT SetToolbarItems(ComCtl32.TBBUTTON[] lpButtons, uint nButtons, Shell32.FCT uFlags) => HRESULT.E_NOTIMPL;
+
+ #endregion ============================================================================================================
+
+ #region IServiceProvider interface ====================================================================================
+
+ ///
+ /// -Interface Implementation for .
+ ///
+ /// Responds to the following Interfaces:
+ /// -
+ /// -
+ ///
+ /// The service's unique identifier (SID).
+ /// The IID of the desired service interface.
+ /// When this method returns, contains the interface pointer requested riid. If successful,
+ /// the calling application is responsible for calling IUnknown::Release using this value when the service is no
+ /// longer needed. In the case of failure, this value is NULL.
+ ///
+ /// or
+ ///
+ ///
+ HRESULT Shell32.IServiceProvider.QueryService(in Guid guidService, in Guid riid, out IntPtr ppvObject)
+ {
+ // IShellBrowser: Guid("000214E2-0000-0000-C000-000000000046")
+ if (riid.Equals(typeof(Shell32.IShellBrowser).GUID))
+ {
+ ppvObject = Marshal.GetComInterfaceForObject(this, typeof(Shell32.IShellBrowser));
+
+ return HRESULT.S_OK;
+ }
+
+ // IShellFolderViewCB: Guid("2047E320-F2A9-11CE-AE65-08002B2E1262")
+ if (riid.Equals(typeof(Shell32.IShellFolderViewCB).GUID))
+ {
+ ShellBrowserViewHandler shvwHandler = this.ViewHandler.Validated();
+
+ if (!(shvwHandler is null))
+ {
+ ppvObject = Marshal.GetComInterfaceForObject(shvwHandler, typeof(Shell32.IShellFolderViewCB));
+
+ return HRESULT.S_OK;
+ }
+ }
+
+ ppvObject = IntPtr.Zero;
+ return HRESULT.E_NOINTERFACE;
+ }
+
+ #endregion ============================================================================================================
+
+
+
+ #region ShellItemCollection ===========================================================================================
+
+ ///
+ /// Represents a collection of attached to an .
+ ///
+ private class ShellItemCollection : IReadOnlyList
+ {
+ private readonly ShellBrowser shellBrowser;
+ private readonly SVGIO option;
+
+ internal ShellItemCollection(ShellBrowser shellBrowser, SVGIO opt)
+ {
+ this.shellBrowser = shellBrowser;
+ this.option = opt;
+ }
+
+ /// Gets the number of elements in the collection.
+ /// Returns a value.
+ public int Count
+ {
+ get
+ {
+ var viewHandler = this.shellBrowser.ViewHandler.Validated();
+
+ if (viewHandler != null)
+ return viewHandler.FolderView2.ItemCount(this.option);
+
+ return 0;
+ }
+ }
+
+ private IShellItemArray Array => this.shellBrowser.GetItemsArray(this.option);
+
+ private IEnumerable Items
+ {
+ get
+ {
+ var array = this.Array;
+
+ if (array is null)
+ yield break;
+ try
+ {
+ for (uint i = 0; i < array.GetCount(); i++)
+ yield return array.GetItemAt(i);
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(array);
+ }
+ }
+ }
+
+ /// Gets the at the specified index.
+ /// The .
+ /// The zero-based index of the element to get.
+ public ShellItem this[int index]
+ {
+ get
+ {
+ var array = Array;
+ try
+ {
+ return array is null ? null : ShellItem.Open(array.GetItemAt((uint)index));
+ }
+ catch
+ {
+ return null;
+ }
+ finally
+ {
+ if (array != null)
+ Marshal.ReleaseComObject(array);
+ }
+ }
+ }
+
+ /// 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();
+ }
+
+
+ #endregion ============================================================================================================
+
+
+
+
+ #region Component Designer Support ====================================================================================
+
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ }
+
+ #endregion ============================================================================================================
+ }
+
+ #region SFVMUD: ShellFolderView undocumented Messages =====================================================================
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+ ///
+ /// Undocumented Flags used by Callback Handler.
+ ///
+ public enum SFVMUD
+ {
+ SFVM_SELECTIONCHANGED = 8,
+ SFVM_DRAWMENUITEM = 9,
+ SFVM_MEASUREMENUITEM = 10,
+ SFVM_EXITMENULOOP = 11,
+ SFVM_VIEWRELEASE = 12,
+ SFVM_GETNAMELENGTH = 13,
+
+ SFVM_WINDOWCLOSING = 16,
+ SFVM_LISTREFRESHED = 17,
+ SFVM_WINDOWFOCUSED = 18,
+ SFVM_REGISTERCOPYHOOK = 20,
+ SFVM_COPYHOOKCALLBACK = 21,
+
+ SFVM_ADDINGOBJECT = 29,
+ SFVM_REMOVINGOBJECT = 30,
+
+ SFVM_GETCOMMANDDIR = 33,
+ SFVM_GETCOLUMNSTREAM = 34,
+ SFVM_CANSELECTALL = 35,
+
+ SFVM_ISSTRICTREFRESH = 37,
+ SFVM_ISCHILDOBJECT = 38,
+
+ SFVM_GETEXTVIEWS = 40,
+
+ SFVM_GET_CUSTOMVIEWINFO = 77,
+ SFVM_ENUMERATEDITEMS = 79, // It seems this msg never gets sent, using Win 10 at least.
+ SFVM_GET_VIEW_DATA = 80,
+ SFVM_GET_WEBVIEW_LAYOUT = 82,
+ SFVM_GET_WEBVIEW_CONTENT = 83,
+ SFVM_GET_WEBVIEW_TASKS = 84,
+ SFVM_GET_WEBVIEW_THEME = 86,
+ SFVM_GETDEFERREDVIEWSETTINGS = 92,
+ }
+
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
+
+ #endregion ================================================================================================================
+
+ #region ShellBrowserViewHandler ===========================================================================================
+
+ ///
+ /// Encapsulates an IShellFolderViewCB-Implementation within an
+ /// -Object. Beside that it's implemented as a Wrapper-Object that is responsible for
+ /// creating and disposing the following objects aka Interface-Instances:
+ /// -
+ /// -
+ /// -
+ ///
+ /// While doing that, it also handles some common error cases:
+ /// - When there's no disk in a disk drive
+ ///
+ /// Implements the following Interfaces:
+ /// -
+ ///
+ /// This class make use of some undocumented Messages in its
+ /// Callback Handler.
+ ///
+ /// For more Information on these see:
+ /// - Google Drive Shell Extension:
+ /// ShellFolderViewCBHandler.cpp
+ ///
+ /// - ReactOS:
+ /// IShellFolderViewCB.cpp File Reference
+ /// ,
+ /// IShellFolderViewCB.cpp
+ ///
+ ///
+ public class ShellBrowserViewHandler
+ : Shell32.IShellFolderViewCB
+ {
+ private string text;
+ private int thumbnailSize = ShellBrowser.defaultThumbnailSize;
+
+ ///
+ /// {"The operation was canceled by the user. (Exception from HRESULT: 0x800704C7)"}
is the result of a
+ /// call to on a Shell Item that targets a removable Disk Drive
+ /// when currently no Media is present. Let's catch these to use our own error handling for this.
+ ///
+ internal static readonly HRESULT HRESULT_CANCELLED = new(0x800704C7);
+
+ #region Properties ====================================================================================================
+
+ /// The owner of this instance of , i.e. the .
+ public ShellBrowser Owner { get; }
+ /// The .
+ public ShellFolder ShellFolder { get; private set; }
+ /// The .
+ public Shell32.IShellView ShellView { get; private set; }
+ /// The .
+ public Shell32.IFolderView2 FolderView2 { get; private set; }
+ /// The ViewWindow.
+ public HWND ViewWindow { get; private set; }
+ /// Indicates that no error occured while creating this instance, i.e. the View is fully functional.
+ public bool IsValid { get; private set; }
+ /// Indicates that an "No Disk In Drive"-error occured while creating this instance.
+ public bool NoDiskInDriveError { get; }
+ /// The that occured, if creation of the instance failed.
+ public COMException ValidationError { get; private set; }
+
+ /// The default text to be used when there are no items in the view.
+ public string Text
+ {
+ get => this.text;
+
+ set
+ {
+ this.text = value;
+
+ if (this.IsValid)
+ this.FolderView2.SetText(Shell32.FVTEXTTYPE.FVST_EMPTYTEXT, value);
+ }
+ }
+
+ /// The size of the thumbnails in pixels.
+ public int ThumbnailSize
+ {
+ get
+ {
+ if (this.IsValid)
+ this.FolderView2.GetViewModeAndIconSize(out _, out this.thumbnailSize);
+
+ return this.thumbnailSize;
+ }
+ set
+ {
+ if (this.IsValid)
+ {
+ this.FolderView2.GetViewModeAndIconSize(out var fvm, out _);
+ this.FolderView2.SetViewModeAndIconSize(fvm, this.thumbnailSize = value);
+ }
+ }
+ }
+
+ /// The viewing mode of the ShellBrowser.
+ public Shell32.FOLDERVIEWMODE ViewMode
+ {
+ get
+ {
+ if (this.IsValid)
+ {
+ return this.FolderView2.GetCurrentViewMode();
+ // TODO: Check ThumbNailSize for new ViewModes with larger sized icons
+ }
+
+ return Shell32.FOLDERVIEWMODE.FVM_AUTO; // TODO!
+ }
+ set
+ {
+ if (this.IsValid)
+ this.FolderView2.SetCurrentViewMode(value);
+ }
+ }
+
+ #endregion ============================================================================================================
+
+ ///
+ /// Create an instance of to handle Callback messages for the given ShellFolder.
+ ///
+ /// The that is owner of this instance.
+ /// The ShellFolder for the view.
+ /// The folder settings for the view.
+ /// Text to display if the folder is empty.
+ public ShellBrowserViewHandler(
+ ShellBrowser owner,
+ ShellFolder shellFolder,
+ ref Shell32.FOLDERSETTINGS folderSettings,
+ ref string emptyFolderText)
+ {
+ this.Owner = owner ?? throw new ArgumentNullException(nameof(owner));
+ this.ShellFolder = shellFolder ?? throw new ArgumentNullException(nameof(shellFolder));
+
+ // Create ShellView and FolderView2 objects, then its ViewWindow
+ try
+ {
+ var sfvCreate = new Shell32.SFV_CREATE()
+ {
+ cbSize = (uint)Marshal.SizeOf(typeof(SFV_CREATE)),
+ pshf = shellFolder.IShellFolder,
+ psvOuter = null,
+ psfvcb = this,
+ };
+
+ Shell32.SHCreateShellFolderView(ref sfvCreate, out Shell32.IShellView shellView).ThrowIfFailed();
+
+ this.ShellView = shellView ??
+ throw new InvalidComObjectException(nameof(this.ShellView));
+
+ this.FolderView2 = (Shell32.IFolderView2)this.ShellView ??
+ throw new InvalidComObjectException(nameof(this.FolderView2));
+
+ // Try to create ViewWindow and take special care of Exception
+ // {"The operation was canceled by the user. (Exception from HRESULT: 0x800704C7)"}
+ // cause this happens when there's no disk in a drive.
+ try
+ {
+ this.ViewWindow = this.ShellView.CreateViewWindow(null, folderSettings, owner, owner.ClientRectangle);
+
+ this.IsValid = true;
+
+ this.Text = emptyFolderText;
+ }
+ catch (COMException ex)
+ {
+ // TODO: Check if the target folder IS actually a drive with removable disks in it!
+ if (HRESULT_CANCELLED.Equals(ex.ErrorCode))
+ this.NoDiskInDriveError = true;
+ throw;
+ }
+ }
+ catch (COMException ex)
+ {
+ // TODO: e.g. C:\Windows\CSC => Permission denied!
+ // 0x8007 0005 E_ACCESSDENIED
+ this.ValidationError = ex;
+ }
+ }
+
+ /// Destroy the view.
+ public void DestroyView()
+ {
+ this.IsValid = false;
+
+ // TODO: Remove MessageSFVCB here!
+
+ // Destroy ShellView's ViewWindow
+ this.ViewWindow = HWND.NULL;
+ this.ShellView.DestroyViewWindow();
+
+ this.FolderView2 = null;
+ this.ShellView = null;
+ //this.ShellFolder = null; // NOTE: I >>think<< this one causes RPC-Errors
+ }
+
+ ///
+ /// Allows communication between the system folder view object and a system folder view callback object.
+ ///
+ /// One of the SFVM_* notifications.
+ /// Additional information. See the individual notification pages for specific requirements.
+ /// Additional information. See the individual notification pages for specific requirements.
+ /// TODO: @dahall: Where does this come from?
+ /// S_OK if the notification has been handled. E_NOTIMPL otherwise.
+ HRESULT Shell32.IShellFolderViewCB.MessageSFVCB(Shell32.SFVM uMsg, IntPtr wParam, IntPtr lParam, ref IntPtr plResult)
+ {
+ switch ((SFVMUD)uMsg)
+ {
+ case SFVMUD.SFVM_SELECTIONCHANGED:
+ this.Owner.OnSelectionChanged();
+ return HRESULT.S_OK;
+
+ case SFVMUD.SFVM_LISTREFRESHED:
+ this.Owner.OnItemsChanged();
+ return HRESULT.S_OK;
+
+ default:
+ //
+ // TODO: What happens when the ViewMode gets changed via Context-Menu? => Msg #33, #18
+ //
+ return HRESULT.E_NOTIMPL;
+ }
+ }
+
+ /// Changes the position and dimensions of this .
+ /// Left
+ /// Top
+ /// Width
+ /// Height
+ /// Force redraw
+ /// If the function succeeds, the return value is nonzero.
+ public bool MoveWindow(int X, int Y, int nWidth, int nHeight, bool bRepaint) =>
+ this.ViewWindow != HWND.NULL && User32.MoveWindow(this.ViewWindow, X, Y, nWidth, nHeight, bRepaint);
+
+ /// Activate the ShellView of this ShellBrowser.
+ /// The to be set
+ public void UIActivate(Shell32.SVUIA uState = Shell32.SVUIA.SVUIA_ACTIVATE_NOFOCUS) => this.ShellView?.UIActivate(uState);
+
+ /// Deactivate the ShellView of this ShellBrowser.
+ public void UIDeactivate() => this.UIActivate(Shell32.SVUIA.SVUIA_DEACTIVATE);
+ }
+
+ ///
+ /// Extension methods for .
+ ///
+ public static class ShellBrowserViewHandlerExtension
+ {
+ ///
+ /// Returns the reference to the given if it is not null and valid, null otherwise
+ ///
+ /// A (possible) reference.
+ ///
+ public static ShellBrowserViewHandler Validated(this ShellBrowserViewHandler shellBrowserViewHandler) =>
+ ((!(shellBrowserViewHandler is null)) && shellBrowserViewHandler.IsValid) ? shellBrowserViewHandler : null;
+ }
+
+
+ #endregion ================================================================================================================
+
+}
diff --git a/Windows.Shell/ShellObjects/ShellView.cs b/Windows.Shell/ShellObjects/ShellView.cs
index c8f882ed..09b7788c 100644
--- a/Windows.Shell/ShellObjects/ShellView.cs
+++ b/Windows.Shell/ShellObjects/ShellView.cs
@@ -191,7 +191,7 @@ namespace Vanara.Windows.Shell
/// Gets the default size of the control.
protected override Size DefaultSize => new Size(250, 200);
- private IShellBrowser Browser => iBrowser ??= new ShellBrowser(this);
+ private IShellBrowser Browser => iBrowser ??= new ShellBrowser(/* this */);
/// Implements the operator !=.
/// The left.