mirror of https://github.com/dahall/Vanara.git
1002 lines
38 KiB
C#
1002 lines
38 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.InteropServices.ComTypes;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using Vanara.PInvoke;
|
|
using static Vanara.PInvoke.Shell32;
|
|
|
|
namespace Vanara.Windows.Shell
|
|
{
|
|
/// <summary>The direction argument for NavigateFromHistory()</summary>
|
|
public enum NavigationLogDirection
|
|
{
|
|
/// <summary>Navigates forward through the navigation log</summary>
|
|
Forward,
|
|
|
|
/// <summary>Navigates backward through the travel log</summary>
|
|
Backward
|
|
}
|
|
|
|
/// <summary>Undocumented Flags used by <see cref="IShellFolderViewCB.MessageSFVCB"/> Callback Handler.</summary>
|
|
public enum SFVMUD
|
|
{
|
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
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
|
|
}
|
|
|
|
/// <summary>Indicates the viewing mode of the ShellBrowser</summary>
|
|
public enum ShellBrowserViewMode
|
|
{
|
|
/// <summary>Choose the best view mode for the folder</summary>
|
|
Auto = FOLDERVIEWMODE.FVM_AUTO,
|
|
|
|
/// <summary>(New for Windows7)</summary>
|
|
Content = FOLDERVIEWMODE.FVM_CONTENT,
|
|
|
|
/// <summary>Object names and other selected information, such as the size or date last updated, are shown.</summary>
|
|
Details = FOLDERVIEWMODE.FVM_DETAILS,
|
|
|
|
/// <summary>The view should display medium-size icons.</summary>
|
|
Icon = FOLDERVIEWMODE.FVM_ICON,
|
|
|
|
/// <summary>Object names are displayed in a list view.</summary>
|
|
List = FOLDERVIEWMODE.FVM_LIST,
|
|
|
|
/// <summary>The view should display small icons.</summary>
|
|
SmallIcon = FOLDERVIEWMODE.FVM_SMALLICON,
|
|
|
|
/// <summary>The view should display thumbnail icons.</summary>
|
|
Thumbnail = FOLDERVIEWMODE.FVM_THUMBNAIL,
|
|
|
|
/// <summary>The view should display icons in a filmstrip format.</summary>
|
|
ThumbStrip = FOLDERVIEWMODE.FVM_THUMBSTRIP,
|
|
|
|
/// <summary>The view should display large icons.</summary>
|
|
Tile = FOLDERVIEWMODE.FVM_TILE
|
|
}
|
|
|
|
/// <summary>Extension methods for <see cref="ShellBrowserViewHandler"/>.</summary>
|
|
public static class ShellBrowserViewHandlerExtension
|
|
{
|
|
/// <summary>
|
|
/// Returns the reference to the given <see cref="ShellBrowserViewHandler"/> if it is not null <b>and</b> valid, null otherwise
|
|
/// </summary>
|
|
/// <param name="shellBrowserViewHandler">A (possible) <see cref="ShellBrowserViewHandler"/> reference.</param>
|
|
/// <returns></returns>
|
|
public static ShellBrowserViewHandler GetValidInstance(this ShellBrowserViewHandler shellBrowserViewHandler) =>
|
|
((!(shellBrowserViewHandler is null)) && shellBrowserViewHandler.IsValid) ? shellBrowserViewHandler : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encapsulates a <see cref="IShellBrowser"/>-Implementation within an <see cref="UserControl"/>. <br/><br/> Implements the
|
|
/// following Interfaces: <br/>
|
|
/// - <seealso cref="IWin32Window"/><br/>
|
|
/// - <seealso cref="IShellBrowser"/><br/>
|
|
/// - <seealso cref="Shell32.IServiceProvider"/><br/><br/> For more Information on used techniques see: <br/>
|
|
/// - <seealso href="https://www.codeproject.com/Articles/28961/Full-implementation-of-IShellBrowser"/><br/><br/><br/> Known Issues: <br/>
|
|
/// - 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. <br/>
|
|
/// - DONE: Keyboard input doesn't work so far. <br/>
|
|
/// - DONE: Only Details-Mode should have column headers: (Using Shell32.FOLDERFLAGS.FWF_NOHEADERINALLVIEWS) <br/> https://stackoverflow.com/questions/11776266/ishellview-columnheaders-not-hidden-if-autoview-does-not-choose-details
|
|
/// - TODO: CustomDraw, when currently no shellView available <br/>
|
|
/// - DONE: Network folder: E_FAIL => DONE: Returning HRESULT.E_NOTIMPL from MessageSFVCB fixes this <br/>
|
|
/// - DONE: Disk Drive (empty): E_CANCELLED_BY_USER <br/>
|
|
/// - 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
|
|
/// </summary>
|
|
[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
|
|
{
|
|
internal const int defaultThumbnailSize = 32;
|
|
private const string processCmdKeyClassNameEdit = "Edit";
|
|
private const int processCmdKeyClassNameMaxLength = 31;
|
|
|
|
private readonly StringBuilder processCmdKeyClassName = new(processCmdKeyClassNameMaxLength + 1);
|
|
|
|
/// <summary>Required designer variable.</summary>
|
|
private IContainer components;
|
|
|
|
private string emptyFolderText = "This folder is empty.";
|
|
private FOLDERSETTINGS folderSettings = new(FOLDERVIEWMODE.FVM_AUTO, FOLDERFLAGS.FWF_NOHEADERINALLVIEWS | FOLDERFLAGS.FWF_NOWEBVIEW | FOLDERFLAGS.FWF_USESEARCHFOLDER);
|
|
private IStream viewStateStream;
|
|
private string viewStateStreamIdentifier;
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="ShellBrowser"/> class.</summary>
|
|
public ShellBrowser()
|
|
: base()
|
|
{
|
|
InitializeComponent();
|
|
|
|
History = new ShellNavigationHistory();
|
|
Items = new ShellItemCollection(this, SVGIO.SVGIO_ALLVIEW);
|
|
SelectedItems = new ShellItemCollection(this, SVGIO.SVGIO_SELECTION);
|
|
|
|
Resize += ShellBrowser_Resize;
|
|
HandleDestroyed += ShellBrowser_HandleDestroyed;
|
|
}
|
|
|
|
/// <summary>Fires when the Items collection changes.</summary>
|
|
[Category("Action"), Description("Items changed.")]
|
|
public event EventHandler ItemsChanged;
|
|
|
|
/// <summary>Fires when ShellBrowser has navigated to a new folder.</summary>
|
|
[Category("Action"), Description("ShellBowser has navigated to a new folder.")]
|
|
public event EventHandler<ShellBrowserNavigatedEventArgs> Navigated;
|
|
|
|
/// <summary>Fires when the SelectedItems collection changes.</summary>
|
|
[Category("Behavior"), Description("Selection changed.")]
|
|
public event EventHandler SelectionChanged;
|
|
|
|
/// <summary>
|
|
/// <inheritdoc/><br/><br/>
|
|
/// Note: I've tried using ComCtl32.ListViewMessage.LVM_SETBKIMAGE, but this doesn't work properly. That's why this property has
|
|
/// been hidden.
|
|
/// </summary>
|
|
[Bindable(false), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public override Image BackgroundImage => base.BackgroundImage;
|
|
|
|
/// <summary>The default text that is displayed when an empty folder is shown</summary>
|
|
[Category("Appearance"), DefaultValue("This folder is empty."), Description("The default text that is displayed when an empty folder is shown.")]
|
|
public string EmptyFolderText
|
|
{
|
|
get => emptyFolderText;
|
|
set
|
|
{
|
|
emptyFolderText = value;
|
|
|
|
if (ViewHandler.IsValid)
|
|
ViewHandler.Text = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>Contains the navigation history of the ShellBrowser</summary>
|
|
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public ShellNavigationHistory History { get; private set; }
|
|
|
|
/// <summary>The set of ShellItems in the ShellBrowser</summary>
|
|
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public IReadOnlyList<ShellItem> Items { get; }
|
|
|
|
/// <summary>The set of selected ShellItems in the ShellBrowser</summary>
|
|
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public IReadOnlyList<ShellItem> SelectedItems { get; }
|
|
|
|
/// <summary>The size of the thumbnails in pixels.</summary>
|
|
[Category("Appearance"), DefaultValue(defaultThumbnailSize), Description("The size of the thumbnails in pixels.")]
|
|
public int ThumbnailSize
|
|
{
|
|
get => ViewHandler.IsValid ? ViewHandler.ThumbnailSize : defaultThumbnailSize;
|
|
set
|
|
{
|
|
if (ViewHandler.IsValid)
|
|
ViewHandler.ThumbnailSize = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>The viewing mode of the ShellBrowser</summary>
|
|
/// <remarks>Internally, this uses LVM_SETVIEW and LVM_GETVIEW messages on the ListView control</remarks>
|
|
[Category("Appearance"), DefaultValue(typeof(ShellBrowserViewMode), "Auto"), Description("The viewing mode of the ShellBrowser.")]
|
|
public ShellBrowserViewMode ViewMode
|
|
{
|
|
get => (ShellBrowserViewMode)folderSettings.ViewMode;
|
|
set
|
|
{
|
|
// TODO: Set ThumbnailSize accordingly?
|
|
folderSettings.ViewMode = (FOLDERVIEWMODE)value;
|
|
|
|
if (ViewHandler.IsValid)
|
|
ViewHandler.ViewMode = folderSettings.ViewMode;
|
|
}
|
|
}
|
|
|
|
/// <summary>The Registry Key where Browser ViewStates get serialized</summary>
|
|
/// <example>Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Streams\\</example>
|
|
[Category("Behavior"), Description("The Registry Key where Browser ViewStates get serialized.")]
|
|
public string ViewStateRegistryKey { get; set; } =
|
|
$"Software\\{ Application.CompanyName }\\{ Application.ProductName }\\ShellBrowser\\ViewStateStreams";
|
|
|
|
/// <inheritdoc/>
|
|
protected override Size DefaultSize => new(200, 150);
|
|
|
|
/// <summary>The <see cref="ShellBrowserViewHandler"/> that is currently in use.</summary>
|
|
protected ShellBrowserViewHandler ViewHandler { get; private set; }
|
|
|
|
/// <summary></summary>
|
|
/// <param name="pidl"></param>
|
|
/// <param name="wFlags"></param>
|
|
/// <returns>HRESULT.STG_E_PATHNOTFOUND if path n found</returns>
|
|
public HRESULT BrowseObject(IntPtr pidl, 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(SBSP.SBSP_NAVIGATEBACK))
|
|
{
|
|
if (History.CanSeekBackward)
|
|
shellObject = 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(SBSP.SBSP_NAVIGATEFORWARD))
|
|
{
|
|
if (History.CanSeekForward)
|
|
shellObject = History.SeekForward();
|
|
else
|
|
return HRESULT.STG_E_PATHNOTFOUND;
|
|
}
|
|
|
|
// SBSP_RELATIVE stands for a pidl relative to the current folder
|
|
else if (wFlags.HasFlag(SBSP.SBSP_RELATIVE))
|
|
{
|
|
ShellItem currentObject = History.Current;
|
|
|
|
PIDL 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(SBSP.SBSP_PARENT))
|
|
{
|
|
ShellItem currentObject = History.Current;
|
|
ShellFolder parentObject = currentObject.Parent;
|
|
|
|
if ((parentObject is not 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 (InvokeRequired)
|
|
BeginInvoke((Action)(() => BrowseShellItemInternal(shellObject)));
|
|
else
|
|
BrowseShellItemInternal(shellObject);
|
|
|
|
return HRESULT.S_OK;
|
|
|
|
#region BrowseShellItemInternal
|
|
|
|
void BrowseShellItemInternal(ShellItem shellItem)
|
|
{
|
|
// Save ViewState of current folder
|
|
ViewHandler.GetValidInstance()?.ShellView.SaveViewState();
|
|
|
|
if (viewStateStream is not null)
|
|
Marshal.ReleaseComObject(viewStateStream);
|
|
|
|
viewStateStreamIdentifier = shellItem.ParsingName;
|
|
|
|
var viewHandler = new ShellBrowserViewHandler(this,
|
|
new ShellFolder(shellItem),
|
|
ref folderSettings,
|
|
ref emptyFolderText);
|
|
|
|
// Clone the PIDL, to have our own object copy on the heap!
|
|
if (!wFlags.HasFlag(SBSP.SBSP_WRITENOHISTORY))
|
|
History.Add(new PIDL(viewHandler.ShellFolder.PIDL));
|
|
|
|
ShellBrowserViewHandler oldViewHandler = ViewHandler;
|
|
ViewHandler = viewHandler;
|
|
oldViewHandler?.UIDeactivate();
|
|
viewHandler.UIActivate();
|
|
oldViewHandler?.DestroyView();
|
|
|
|
OnNavigated(viewHandler.ShellFolder);
|
|
OnSelectionChanged();
|
|
}
|
|
|
|
#endregion BrowseShellItemInternal
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT ContextSensitiveHelp(bool fEnterMode) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT EnableModelessSB(bool fEnable) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT GetControlWindow(FCW id, out HWND hwnd)
|
|
{
|
|
hwnd = HWND.NULL;
|
|
return HRESULT.E_NOTIMPL;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT GetViewStateStream(STGM grfMode, out IStream stream)
|
|
{
|
|
if (viewStateStream is not null)
|
|
Marshal.ReleaseComObject(viewStateStream);
|
|
|
|
stream = viewStateStream = ShlwApi.SHOpenRegStream2(hkey: HKEY.HKEY_CURRENT_USER, pszSubkey: ViewStateRegistryKey, pszValue: viewStateStreamIdentifier,
|
|
grfMode: grfMode);
|
|
|
|
return stream is null ? HRESULT.E_FAIL : HRESULT.S_OK;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT GetWindow(out HWND phwnd)
|
|
{
|
|
phwnd = Handle;
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT InsertMenusSB(HMENU hmenuShared, ref Ole32.OLEMENUGROUPWIDTHS lpMenuWidths) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <summary>
|
|
/// Navigates to the last item in the navigation history list. This does not change the set of locations in the navigation log.
|
|
/// </summary>
|
|
/// <returns>True if the navigation succeeded, false if it failed for any reason.</returns>
|
|
public bool NavigateBack() => BrowseObject(IntPtr.Zero, SBSP.SBSP_NAVIGATEBACK).Succeeded;
|
|
|
|
/// <summary>
|
|
/// Navigates to the next item in the navigation history list. This does not change the set of locations in the navigation log.
|
|
/// </summary>
|
|
/// <returns>True if the navigation succeeded, false if it failed for any reason.</returns>
|
|
public bool NavigateForward() => BrowseObject(IntPtr.Zero, SBSP.SBSP_NAVIGATEFORWARD).Succeeded;
|
|
|
|
/// <summary>
|
|
/// Navigate within the navigation log in a specific direciton. This does not change the set of locations in the navigation log.
|
|
/// </summary>
|
|
/// <param name="direction">The direction to navigate within the navigation logs collection.</param>
|
|
/// <returns>True if the navigation succeeded, false if it failed for any reason.</returns>
|
|
public bool NavigateFromHistory(NavigationLogDirection direction) => direction switch
|
|
{
|
|
NavigationLogDirection.Backward => NavigateBack(),
|
|
NavigationLogDirection.Forward => NavigateForward(),
|
|
_ => false,
|
|
};
|
|
|
|
/// <summary>Navigates to the parent folder.</summary>
|
|
/// <returns>True if the navigation succeeded, false if it failed for any reason.</returns>
|
|
public bool NavigateParent() => BrowseObject(IntPtr.Zero, SBSP.SBSP_PARENT).Succeeded;
|
|
|
|
/// <summary>Navigate within the navigation log. This does not change the set of locations in the navigation log.</summary>
|
|
/// <param name="historyIndex">An index into the navigation logs Locations collection.</param>
|
|
/// <returns>True if the navigation succeeded, false if it failed for any reason.</returns>
|
|
public bool NavigateToHistoryIndex(int historyIndex)
|
|
{
|
|
using ShellItem shellFolder = History.Seek(historyIndex, SeekOrigin.Current);
|
|
return shellFolder is not null && BrowseObject((IntPtr)shellFolder.PIDL, SBSP.SBSP_ABSOLUTE | SBSP.SBSP_WRITENOHISTORY).Succeeded;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT OnViewWindowActive(IShellView ppshv) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT QueryActiveShellView(out IShellView shellView)
|
|
{
|
|
if (ViewHandler.GetValidInstance() is not null)
|
|
{
|
|
Marshal.AddRef(Marshal.GetIUnknownForObject(ViewHandler.ShellView));
|
|
shellView = ViewHandler.ShellView;
|
|
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
shellView = null;
|
|
return HRESULT.E_PENDING;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT RemoveMenusSB(HMENU hmenuShared) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <summary>Selects all items in the current view.</summary>
|
|
public void SelectAll()
|
|
{
|
|
ShellBrowserViewHandler viewHandler = ViewHandler.GetValidInstance();
|
|
|
|
if (viewHandler is not 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)ViewHandler.ViewWindow,
|
|
Msg = (int)User32.WindowMessage.WM_KEYDOWN,
|
|
};
|
|
|
|
ProcessCmdKey(ref msg, Keys.Control | Keys.A);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT SendControlMsg(FCW id, uint uMsg, IntPtr wParam, IntPtr lParam, out IntPtr pret)
|
|
{
|
|
pret = IntPtr.Zero;
|
|
return HRESULT.E_NOTIMPL;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT SetMenuSB(HMENU hmenuShared, IntPtr holemenuRes, HWND hwndActiveObject) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT SetStatusTextSB(string pszStatusText) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT SetToolbarItems(ComCtl32.TBBUTTON[] lpButtons, uint nButtons, FCT uFlags) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <inheritdoc/>
|
|
public HRESULT TranslateAcceleratorSB(ref MSG pmsg, ushort wID) => HRESULT.E_NOTIMPL;
|
|
|
|
/// <summary>Unselects all items in the current view.</summary>
|
|
public void UnselectAll()
|
|
{
|
|
ShellBrowserViewHandler viewHandler = ViewHandler.GetValidInstance();
|
|
|
|
if (viewHandler is not null)
|
|
viewHandler.FolderView2.SelectItem(-1, SVSIF.SVSI_DESELECTOTHERS);
|
|
}
|
|
|
|
/// <summary>
|
|
/// <see cref="Shell32.IServiceProvider"/>-Interface Implementation for <see cref="ShellBrowser"/>. <br/><br/> Responds to the
|
|
/// following Interfaces: <br/>
|
|
/// - <see cref="IShellBrowser"/><br/>
|
|
/// - <see cref="IShellFolderViewCB"/><br/>
|
|
/// </summary>
|
|
/// <param name="guidService">The service's unique identifier (SID).</param>
|
|
/// <param name="riid">The IID of the desired service interface.</param>
|
|
/// <param name="ppvObject">
|
|
/// 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.
|
|
/// </param>
|
|
/// <returns><see cref="HRESULT.S_OK"/> or <br/><see cref="HRESULT.E_NOINTERFACE"/></returns>
|
|
HRESULT Shell32.IServiceProvider.QueryService(in Guid guidService, in Guid riid, out IntPtr ppvObject)
|
|
{
|
|
// IShellBrowser: Guid("000214E2-0000-0000-C000-000000000046")
|
|
if (riid.Equals(typeof(IShellBrowser).GUID))
|
|
{
|
|
ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IShellBrowser));
|
|
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
// IShellFolderViewCB: Guid("2047E320-F2A9-11CE-AE65-08002B2E1262")
|
|
if (riid.Equals(typeof(IShellFolderViewCB).GUID))
|
|
{
|
|
ShellBrowserViewHandler shvwHandler = ViewHandler.GetValidInstance();
|
|
|
|
if (!(shvwHandler is null))
|
|
{
|
|
ppvObject = Marshal.GetComInterfaceForObject(shvwHandler, typeof(IShellFolderViewCB));
|
|
|
|
return HRESULT.S_OK;
|
|
}
|
|
}
|
|
|
|
ppvObject = IntPtr.Zero;
|
|
return HRESULT.E_NOINTERFACE;
|
|
}
|
|
|
|
/// <summary>Gets the items in the ShellBrowser as an IShellItemArray</summary>
|
|
/// <returns>An <see cref="IShellItemArray"/> instance or <see langword="null"/> if not available.</returns>
|
|
internal IShellItemArray GetItemsArray(SVGIO opt)
|
|
{
|
|
try
|
|
{
|
|
ShellBrowserViewHandler viewHandler = ViewHandler.GetValidInstance();
|
|
|
|
return viewHandler is not null ? viewHandler.FolderView2.Items<IShellItemArray>(opt) : null;
|
|
}
|
|
catch { return null; }
|
|
}
|
|
|
|
/// <summary>Raises the <see cref="ItemsChanged"/> event.</summary>
|
|
protected internal virtual void OnItemsChanged() => ItemsChanged?.Invoke(this, EventArgs.Empty);
|
|
|
|
/// <summary>Raises the <see cref="Navigated"/> event.</summary>
|
|
protected internal virtual void OnNavigated(ShellFolder shellFolder)
|
|
{
|
|
if (Navigated is not null)
|
|
{
|
|
ShellBrowserNavigatedEventArgs eventArgs = new(shellFolder);
|
|
|
|
Navigated.Invoke(this, eventArgs);
|
|
}
|
|
}
|
|
|
|
/// <summary>Raises the <see cref="SelectionChanged"/> event.</summary>
|
|
protected internal virtual void OnSelectionChanged() => SelectionChanged?.Invoke(this, EventArgs.Empty);
|
|
|
|
/// <summary>
|
|
/// <seealso cref="ShellBrowser"/>'s event handler for <seealso cref="Control.HandleDestroyed"/> event: Save ViewState when
|
|
/// ShellBrowser gets closed.
|
|
/// </summary>
|
|
protected internal virtual void ShellBrowser_HandleDestroyed(object sender, EventArgs e) => ViewHandler.GetValidInstance()?.ShellView.SaveViewState();
|
|
|
|
/// <summary>
|
|
/// <seealso cref="ShellBrowser"/>'s event handler for <seealso cref="Control.Resize"/> event: Resize ViewWindow when ShellBrowser
|
|
/// gets resized.
|
|
/// </summary>
|
|
protected internal virtual void ShellBrowser_Resize(object sender, EventArgs e) => ViewHandler?.MoveWindow(0, 0, ClientRectangle.Width, ClientRectangle.Height, false);
|
|
|
|
/// <summary>Clean up any resources being used.</summary>
|
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing && (components is not null))
|
|
{
|
|
components.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
/// <summary>Process known command keys of the ShellBrowser.</summary>
|
|
/// <param name="msg">Windows Message</param>
|
|
/// <param name="keyData">Key codes and modifiers</param>
|
|
/// <returns>true if character was processed by the control; otherwise, false</returns>
|
|
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 (ViewHandler.GetValidInstance() is not null)
|
|
{
|
|
// Note: I tried using the LVM_GETEDITCONTROL message for finding the edit control without luck
|
|
if (User32.GetClassName(msg.HWnd,
|
|
processCmdKeyClassName,
|
|
processCmdKeyClassNameMaxLength) > 0)
|
|
{
|
|
if (processCmdKeyClassName.ToString().Equals(processCmdKeyClassNameEdit))
|
|
{
|
|
// Try to get Edit field's parent 'SysListView32' handle
|
|
HWND hSysListView32 = User32.GetParent(msg.HWnd);
|
|
|
|
if (!hSysListView32.IsNull)
|
|
{
|
|
// Try to get SysListView32's parent 'SHELLDLL_DefView' handle
|
|
HWND hShellDllDefViewWindow = User32.GetParent(hSysListView32);
|
|
|
|
if (!hShellDllDefViewWindow.IsNull && (hShellDllDefViewWindow == ViewHandler.ViewWindow))
|
|
{
|
|
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)
|
|
{
|
|
var forward = (keyData & Keys.Shift) != Keys.Shift;
|
|
|
|
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:
|
|
NavigateBack();
|
|
|
|
return true;
|
|
|
|
case Keys.BrowserForward:
|
|
case Keys.Alt | Keys.Right:
|
|
NavigateForward();
|
|
|
|
return true;
|
|
|
|
case Keys.Back:
|
|
NavigateParent();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Let the ShellView process all other keystrokes
|
|
if (ViewHandler.GetValidInstance() is not null)
|
|
{
|
|
ViewHandler.ShellView.TranslateAccelerator(new MSG(msg.HWnd, (uint)msg.Msg, msg.WParam, msg.LParam));
|
|
|
|
return true;
|
|
}
|
|
|
|
return base.ProcessCmdKey(ref msg, keyData);
|
|
}
|
|
|
|
/// <summary>Required method for Designer support - do not modify the contents of this method with the code editor.</summary>
|
|
private void InitializeComponent()
|
|
{
|
|
components = new Container();
|
|
AutoScaleMode = AutoScaleMode.Font;
|
|
}
|
|
|
|
/// <summary>Represents a collection of <see cref="ShellItem"/> attached to an <see cref="ShellBrowser"/>.</summary>
|
|
private class ShellItemCollection : IReadOnlyList<ShellItem>
|
|
{
|
|
private readonly SVGIO option;
|
|
private readonly ShellBrowser shellBrowser;
|
|
|
|
internal ShellItemCollection(ShellBrowser shellBrowser, SVGIO opt)
|
|
{
|
|
this.shellBrowser = shellBrowser;
|
|
option = opt;
|
|
}
|
|
|
|
/// <summary>Gets the number of elements in the collection.</summary>
|
|
/// <value>Returns a <see cref="int"/> value.</value>
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
ShellBrowserViewHandler viewHandler = shellBrowser.ViewHandler.GetValidInstance();
|
|
|
|
return viewHandler is not null ? viewHandler.FolderView2.ItemCount(option) : 0;
|
|
}
|
|
}
|
|
|
|
private IShellItemArray Array => shellBrowser.GetItemsArray(option);
|
|
|
|
private IEnumerable<IShellItem> Items
|
|
{
|
|
get
|
|
{
|
|
IShellItemArray array = 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets the <see cref="ShellItem"/> at the specified index.</summary>
|
|
/// <value>The <see cref="ShellItem"/>.</value>
|
|
/// <param name="index">The zero-based index of the element to get.</param>
|
|
public ShellItem this[int index]
|
|
{
|
|
get
|
|
{
|
|
IShellItemArray array = Array;
|
|
try
|
|
{
|
|
return array is null ? null : ShellItem.Open(array.GetItemAt((uint)index));
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
finally
|
|
{
|
|
if (array is not null)
|
|
Marshal.ReleaseComObject(array);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Returns an enumerator that iterates through the collection.</summary>
|
|
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
|
|
public IEnumerator<ShellItem> GetEnumerator() => Items.Select(ShellItem.Open).GetEnumerator();
|
|
|
|
/// <summary>Returns an enumerator that iterates through the collection.</summary>
|
|
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
}
|
|
}
|
|
|
|
/// <summary>Event argument for The Navigated event.</summary>
|
|
public class ShellBrowserNavigatedEventArgs : EventArgs
|
|
{
|
|
/// <summary>Initializes a new instance of the <see cref="ShellBrowserNavigatedEventArgs"/> class.</summary>
|
|
public ShellBrowserNavigatedEventArgs(ShellFolder currentFolder) => CurrentFolder = currentFolder ?? throw new ArgumentNullException(nameof(currentFolder));
|
|
|
|
/// <summary>The new location of the ShellBrowser</summary>
|
|
public ShellFolder CurrentFolder { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encapsulates an <see cref="IShellFolderViewCB">IShellFolderViewCB</see>-Implementation within an <see
|
|
/// cref="IDisposable"/>-Object. Beside that it's implemented as a Wrapper-Object that is responsible for creating and disposing the
|
|
/// following objects aka Interface-Instances: <br/>
|
|
/// - <seealso cref="Shell.ShellFolder"/><br/>
|
|
/// - <seealso cref="IShellView"/><br/>
|
|
/// - <seealso cref="IFolderView2"/><br/><br/> While doing that, it also handles some common error cases: <br/>
|
|
/// - When there's no disk in a disk drive <br/><br/> Implements the following Interfaces: <br/>
|
|
/// - <seealso cref="IShellFolderViewCB"/><br/><br/> This class make use of some <see cref="SFVMUD">undocumented Messages</see>
|
|
/// in its <see cref="IShellFolderViewCB.MessageSFVCB"/> Callback Handler. <br/><br/> For more Information on these see: <br/>
|
|
/// - Google Drive Shell Extension: <seealso href="https://github.com/google/google-drive-shell-extension/blob/master/DriveFusion/ShellFolderViewCBHandler.cpp">
|
|
/// ShellFolderViewCBHandler.cpp </seealso><br/>
|
|
/// - ReactOS: <seealso href="https://doxygen.reactos.org/d2/dbb/IShellFolderViewCB_8cpp.html"> IShellFolderViewCB.cpp File Reference
|
|
/// </seealso>, <seealso href="https://doxygen.reactos.org/d2/dbb/IShellFolderViewCB_8cpp_source.html"> IShellFolderViewCB.cpp </seealso>
|
|
/// </summary>
|
|
public class ShellBrowserViewHandler : IShellFolderViewCB
|
|
{
|
|
/// <summary>
|
|
/// <code>{"The operation was canceled by the user. (Exception from HRESULT: 0x800704C7)"}</code>
|
|
/// is the result of a call to <see cref="IShellView.CreateViewWindow"/> 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.
|
|
/// </summary>
|
|
internal static readonly HRESULT HRESULT_CANCELLED = new(0x800704C7);
|
|
|
|
private string text;
|
|
private int thumbnailSize = ShellBrowser.defaultThumbnailSize;
|
|
|
|
/// <summary>Create an instance of <see cref="ShellBrowserViewHandler"/> to handle Callback messages for the given ShellFolder.</summary>
|
|
/// <param name="owner">The <see cref="ShellBrowser"/> that is owner of this instance.</param>
|
|
/// <param name="shellFolder">The ShellFolder for the view.</param>
|
|
/// <param name="folderSettings">The folder settings for the view.</param>
|
|
/// <param name="emptyFolderText">Text to display if the folder is empty.</param>
|
|
public ShellBrowserViewHandler(ShellBrowser owner, ShellFolder shellFolder, ref FOLDERSETTINGS folderSettings, ref string emptyFolderText)
|
|
{
|
|
Owner = owner ?? throw new ArgumentNullException(nameof(owner));
|
|
ShellFolder = shellFolder ?? throw new ArgumentNullException(nameof(shellFolder));
|
|
|
|
// Create ShellView and FolderView2 objects, then its ViewWindow
|
|
try
|
|
{
|
|
var sfvCreate = new SFV_CREATE()
|
|
{
|
|
cbSize = (uint)Marshal.SizeOf(typeof(SFV_CREATE)),
|
|
pshf = shellFolder.IShellFolder,
|
|
psvOuter = null,
|
|
psfvcb = this,
|
|
};
|
|
|
|
SHCreateShellFolderView(ref sfvCreate, out IShellView shellView).ThrowIfFailed();
|
|
|
|
ShellView = shellView ??
|
|
throw new InvalidComObjectException(nameof(ShellView));
|
|
|
|
FolderView2 = (IFolderView2)ShellView ??
|
|
throw new InvalidComObjectException(nameof(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
|
|
{
|
|
ViewWindow = ShellView.CreateViewWindow(null, folderSettings, owner, owner.ClientRectangle);
|
|
|
|
IsValid = true;
|
|
|
|
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))
|
|
NoDiskInDriveError = true;
|
|
throw;
|
|
}
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
// TODO: e.g. C:\Windows\CSC => Permission denied! 0x8007 0005 E_ACCESSDENIED
|
|
ValidationError = ex;
|
|
}
|
|
}
|
|
|
|
/// <summary>The <see cref="IFolderView2"/>.</summary>
|
|
public IFolderView2 FolderView2 { get; private set; }
|
|
|
|
/// <summary>Indicates that no error occured while creating this instance, i.e. the View is fully functional.</summary>
|
|
public bool IsValid { get; private set; }
|
|
|
|
/// <summary>Indicates that an "No Disk In Drive"-error occured while creating this instance.</summary>
|
|
public bool NoDiskInDriveError { get; }
|
|
|
|
/// <summary>The owner of this instance of <see cref="ShellBrowserViewHandler"/>, i.e. the <see cref="ShellBrowser"/>.</summary>
|
|
public ShellBrowser Owner { get; }
|
|
|
|
/// <summary>The <see cref="ShellFolder"/>.</summary>
|
|
public ShellFolder ShellFolder { get; private set; }
|
|
|
|
/// <summary>The <see cref="IShellView"/>.</summary>
|
|
public IShellView ShellView { get; private set; }
|
|
|
|
/// <summary>The default text to be used when there are no items in the view.</summary>
|
|
public string Text
|
|
{
|
|
get => text;
|
|
set
|
|
{
|
|
text = value;
|
|
|
|
if (IsValid)
|
|
FolderView2.SetText(FVTEXTTYPE.FVST_EMPTYTEXT, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>The size of the thumbnails in pixels.</summary>
|
|
public int ThumbnailSize
|
|
{
|
|
get
|
|
{
|
|
if (IsValid)
|
|
FolderView2.GetViewModeAndIconSize(out _, out thumbnailSize);
|
|
|
|
return thumbnailSize;
|
|
}
|
|
set
|
|
{
|
|
if (IsValid)
|
|
{
|
|
FolderView2.GetViewModeAndIconSize(out FOLDERVIEWMODE fvm, out _);
|
|
FolderView2.SetViewModeAndIconSize(fvm, thumbnailSize = value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>The <see cref="COMException"/> that occured, if creation of the instance failed.</summary>
|
|
public COMException ValidationError { get; private set; }
|
|
|
|
/// <summary>The viewing mode of the ShellBrowser.</summary>
|
|
public FOLDERVIEWMODE ViewMode
|
|
{
|
|
get
|
|
{
|
|
if (IsValid)
|
|
{
|
|
return FolderView2.GetCurrentViewMode();
|
|
// TODO: Check ThumbNailSize for new ViewModes with larger sized icons
|
|
}
|
|
|
|
return FOLDERVIEWMODE.FVM_AUTO; // TODO!
|
|
}
|
|
set
|
|
{
|
|
if (IsValid)
|
|
FolderView2.SetCurrentViewMode(value);
|
|
}
|
|
}
|
|
|
|
/// <summary>The ViewWindow.</summary>
|
|
public HWND ViewWindow { get; private set; }
|
|
|
|
/// <summary>Destroy the view.</summary>
|
|
public void DestroyView()
|
|
{
|
|
IsValid = false;
|
|
|
|
// TODO: Remove MessageSFVCB here!
|
|
|
|
// Destroy ShellView's ViewWindow
|
|
ViewWindow = HWND.NULL;
|
|
ShellView.DestroyViewWindow();
|
|
|
|
FolderView2 = null;
|
|
ShellView = null;
|
|
//this.ShellFolder = null; // NOTE: I >>think<< this one causes RPC-Errors
|
|
}
|
|
|
|
/// <summary>Changes the position and dimensions of this <see cref="ViewWindow"/>.</summary>
|
|
/// <param name="X">Left</param>
|
|
/// <param name="Y">Top</param>
|
|
/// <param name="nWidth">Width</param>
|
|
/// <param name="nHeight">Height</param>
|
|
/// <param name="bRepaint">Force redraw</param>
|
|
/// <returns>If the function succeeds, the return value is nonzero.</returns>
|
|
public bool MoveWindow(int X, int Y, int nWidth, int nHeight, bool bRepaint) =>
|
|
ViewWindow != HWND.NULL && User32.MoveWindow(ViewWindow, X, Y, nWidth, nHeight, bRepaint);
|
|
|
|
/// <summary>Activate the ShellView of this ShellBrowser.</summary>
|
|
/// <param name="uState">The <seealso cref="SVUIA"/> to be set</param>
|
|
public void UIActivate(SVUIA uState = SVUIA.SVUIA_ACTIVATE_NOFOCUS) => ShellView?.UIActivate(uState);
|
|
|
|
/// <summary>Deactivate the ShellView of this ShellBrowser.</summary>
|
|
public void UIDeactivate() => UIActivate(SVUIA.SVUIA_DEACTIVATE);
|
|
|
|
/// <summary>Allows communication between the system folder view object and a system folder view callback object.</summary>
|
|
/// <param name="uMsg">One of the SFVM_* notifications.</param>
|
|
/// <param name="wParam">Additional information. See the individual notification pages for specific requirements.</param>
|
|
/// <param name="lParam">Additional information. See the individual notification pages for specific requirements.</param>
|
|
/// <param name="plResult">TODO: @dahall: Where does this come from?</param>
|
|
/// <returns><b>S_OK</b> if the notification has been handled. <b>E_NOTIMPL</b> otherwise.</returns>
|
|
HRESULT IShellFolderViewCB.MessageSFVCB(SFVM uMsg, IntPtr wParam, IntPtr lParam, ref IntPtr plResult)
|
|
{
|
|
switch ((SFVMUD)uMsg)
|
|
{
|
|
case SFVMUD.SFVM_SELECTIONCHANGED:
|
|
Owner.OnSelectionChanged();
|
|
return HRESULT.S_OK;
|
|
|
|
case SFVMUD.SFVM_LISTREFRESHED:
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
} |