2021-04-02 12:12:22 -04:00
using System ;
2021-04-02 11:04:25 -04:00
using System.Collections ;
2021-04-02 12:12:22 -04:00
using System.Collections.Generic ;
2021-04-02 11:04:25 -04:00
using System.ComponentModel ;
using System.Drawing ;
using System.IO ;
2020-10-20 18:26:06 -04:00
using System.Linq ;
2021-04-02 11:04:25 -04:00
using System.Runtime.InteropServices ;
2021-04-02 12:12:22 -04:00
using System.Runtime.InteropServices.ComTypes ;
2021-04-02 11:04:25 -04:00
using System.Text ;
2020-10-20 18:26:06 -04:00
using System.Windows.Forms ;
2020-09-25 19:21:20 -04:00
using Vanara.PInvoke ;
using static Vanara . PInvoke . Shell32 ;
namespace Vanara.Windows.Shell
{
2021-04-02 12:12:22 -04:00
/// <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
{
2021-04-02 11:04:25 -04:00
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
2021-04-02 12:12:22 -04:00
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 ,
2021-04-02 11:04:25 -04:00
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
2021-04-02 12:12:22 -04:00
}
/// <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>
2021-04-06 14:44:49 -04:00
public static ShellBrowserViewHandler GetValidInstance ( this ShellBrowserViewHandler shellBrowserViewHandler ) = >
2021-04-02 12:12:22 -04:00
( ( ! ( shellBrowserViewHandler is null ) ) & & shellBrowserViewHandler . IsValid ) ? shellBrowserViewHandler : null ;
}
/// <summary>
2021-07-21 19:13:01 -04:00
/// Encapsulates a <see cref="IShellBrowser"/>-Implementation within an <see cref="UserControl"/>. <br/><br/> Implements the following
/// Interfaces: <br/>
2021-04-02 12:12:22 -04:00
/// - <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,
2021-07-21 19:13:01 -04:00
/// afaik, that only works in vista and above: IFolderView2
2021-04-02 12:12:22 -04:00
/// - 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." ;
2021-04-06 14:44:49 -04:00
private FOLDERSETTINGS folderSettings = new ( FOLDERVIEWMODE . FVM_AUTO , FOLDERFLAGS . FWF_NOHEADERINALLVIEWS | FOLDERFLAGS . FWF_NOWEBVIEW | FOLDERFLAGS . FWF_USESEARCHFOLDER ) ;
2021-04-02 12:12:22 -04:00
private IStream viewStateStream ;
private string viewStateStreamIdentifier ;
/// <summary>Initializes a new instance of the <see cref="ShellBrowser"/> class.</summary>
public ShellBrowser ( )
: base ( )
{
2021-04-06 14:44:49 -04:00
InitializeComponent ( ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
History = new ShellNavigationHistory ( ) ;
Items = new ShellItemCollection ( this , SVGIO . SVGIO_ALLVIEW ) ;
SelectedItems = new ShellItemCollection ( this , SVGIO . SVGIO_SELECTION ) ;
2021-04-02 12:12:22 -04:00
}
/// <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>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
{
2021-04-06 14:44:49 -04:00
get = > emptyFolderText ;
2021-04-02 12:12:22 -04:00
set
{
2021-04-06 14:44:49 -04:00
emptyFolderText = value ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
if ( ViewHandler . IsValid )
ViewHandler . Text = value ;
2021-04-02 12:12:22 -04:00
}
}
/// <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
{
2021-04-06 14:44:49 -04:00
get = > ViewHandler . IsValid ? ViewHandler . ThumbnailSize : defaultThumbnailSize ;
2021-04-02 12:12:22 -04:00
set
{
2021-04-06 14:44:49 -04:00
if ( ViewHandler . IsValid )
ViewHandler . ThumbnailSize = value ;
2021-04-02 12:12:22 -04:00
}
}
/// <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
{
2021-04-06 14:44:49 -04:00
get = > ( ShellBrowserViewMode ) folderSettings . ViewMode ;
2021-04-02 12:12:22 -04:00
set
{
// TODO: Set ThumbnailSize accordingly?
2021-04-06 14:44:49 -04:00
folderSettings . ViewMode = ( FOLDERVIEWMODE ) value ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
if ( ViewHandler . IsValid )
ViewHandler . ViewMode = folderSettings . ViewMode ;
2021-04-02 12:12:22 -04:00
}
}
/// <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" ;
2021-07-21 19:13:01 -04:00
/// <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 ;
2021-04-02 12:12:22 -04:00
/// <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 ) )
{
2021-04-06 14:44:49 -04:00
if ( History . CanSeekBackward )
shellObject = History . SeekBackward ( ) ;
2021-04-02 12:12:22 -04:00
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 ) )
{
2021-04-06 14:44:49 -04:00
if ( History . CanSeekForward )
shellObject = History . SeekForward ( ) ;
2021-04-02 12:12:22 -04:00
else
return HRESULT . STG_E_PATHNOTFOUND ;
}
// SBSP_RELATIVE stands for a pidl relative to the current folder
else if ( wFlags . HasFlag ( SBSP . SBSP_RELATIVE ) )
{
2021-04-06 14:44:49 -04:00
ShellItem currentObject = History . Current ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
PIDL targetObject = PIDLUtil . ILCombine ( ( IntPtr ) currentObject . PIDL , pidl ) ;
2021-04-02 12:12:22 -04:00
shellObject = new ShellItem ( targetObject ) ;
}
// SBSP_PARENT stands for the parent folder (and ignores the pidl)
else if ( wFlags . HasFlag ( SBSP . SBSP_PARENT ) )
{
2021-04-06 14:44:49 -04:00
ShellItem currentObject = History . Current ;
ShellFolder parentObject = currentObject . Parent ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
if ( ( parentObject is not null ) & & parentObject . PIDL . IsParentOf ( currentObject . PIDL ) )
2021-04-02 12:12:22 -04:00
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 ) ) ;
}
2021-04-06 14:44:49 -04:00
if ( InvokeRequired )
BeginInvoke ( ( Action ) ( ( ) = > BrowseShellItemInternal ( shellObject ) ) ) ;
2021-04-02 12:12:22 -04:00
else
BrowseShellItemInternal ( shellObject ) ;
return HRESULT . S_OK ;
#region BrowseShellItemInternal
void BrowseShellItemInternal ( ShellItem shellItem )
{
// Save ViewState of current folder
2021-04-06 14:44:49 -04:00
ViewHandler . GetValidInstance ( ) ? . ShellView . SaveViewState ( ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
if ( viewStateStream is not null )
Marshal . ReleaseComObject ( viewStateStream ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
viewStateStreamIdentifier = shellItem . ParsingName ;
2021-04-02 12:12:22 -04:00
var viewHandler = new ShellBrowserViewHandler ( this ,
new ShellFolder ( shellItem ) ,
2021-04-06 14:44:49 -04:00
ref folderSettings ,
ref emptyFolderText ) ;
2021-04-02 12:12:22 -04:00
// Clone the PIDL, to have our own object copy on the heap!
if ( ! wFlags . HasFlag ( SBSP . SBSP_WRITENOHISTORY ) )
2022-01-11 20:13:44 -05:00
History . Add ( new ShellItem ( new PIDL ( viewHandler . ShellFolder . PIDL ) ) ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
ShellBrowserViewHandler oldViewHandler = ViewHandler ;
ViewHandler = viewHandler ;
2021-04-02 12:12:22 -04:00
oldViewHandler ? . UIDeactivate ( ) ;
viewHandler . UIActivate ( ) ;
oldViewHandler ? . DestroyView ( ) ;
2021-04-06 14:44:49 -04:00
OnNavigated ( viewHandler . ShellFolder ) ;
OnSelectionChanged ( ) ;
2021-04-02 12:12:22 -04:00
}
#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 )
{
2021-04-06 14:44:49 -04:00
if ( viewStateStream is not null )
Marshal . ReleaseComObject ( viewStateStream ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
stream = viewStateStream = ShlwApi . SHOpenRegStream2 ( hkey : HKEY . HKEY_CURRENT_USER , pszSubkey : ViewStateRegistryKey , pszValue : viewStateStreamIdentifier ,
2021-04-02 12:12:22 -04:00
grfMode : grfMode ) ;
2021-04-06 14:44:49 -04:00
return stream is null ? HRESULT . E_FAIL : HRESULT . S_OK ;
2021-04-02 12:12:22 -04:00
}
/// <inheritdoc/>
public HRESULT GetWindow ( out HWND phwnd )
{
2021-04-06 14:44:49 -04:00
phwnd = Handle ;
2021-04-02 12:12:22 -04:00
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>
2021-04-06 14:44:49 -04:00
public bool NavigateBack ( ) = > BrowseObject ( IntPtr . Zero , SBSP . SBSP_NAVIGATEBACK ) . Succeeded ;
2021-04-02 12:12:22 -04:00
/// <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>
2021-04-06 14:44:49 -04:00
public bool NavigateForward ( ) = > BrowseObject ( IntPtr . Zero , SBSP . SBSP_NAVIGATEFORWARD ) . Succeeded ;
2021-04-02 12:12:22 -04:00
/// <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>
2021-04-06 14:44:49 -04:00
public bool NavigateFromHistory ( NavigationLogDirection direction ) = > direction switch
2021-04-02 12:12:22 -04:00
{
2021-04-06 14:44:49 -04:00
NavigationLogDirection . Backward = > NavigateBack ( ) ,
NavigationLogDirection . Forward = > NavigateForward ( ) ,
_ = > false ,
} ;
2021-04-02 12:12:22 -04:00
/// <summary>Navigates to the parent folder.</summary>
/// <returns>True if the navigation succeeded, false if it failed for any reason.</returns>
2021-04-06 14:44:49 -04:00
public bool NavigateParent ( ) = > BrowseObject ( IntPtr . Zero , SBSP . SBSP_PARENT ) . Succeeded ;
2021-04-02 12:12:22 -04:00
/// <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 )
{
2021-04-06 14:44:49 -04:00
using ShellItem shellFolder = History . Seek ( historyIndex , SeekOrigin . Current ) ;
return shellFolder is not null & & BrowseObject ( ( IntPtr ) shellFolder . PIDL , SBSP . SBSP_ABSOLUTE | SBSP . SBSP_WRITENOHISTORY ) . Succeeded ;
2021-04-02 12:12:22 -04:00
}
/// <inheritdoc/>
public HRESULT OnViewWindowActive ( IShellView ppshv ) = > HRESULT . E_NOTIMPL ;
/// <inheritdoc/>
public HRESULT QueryActiveShellView ( out IShellView shellView )
{
2021-04-06 14:44:49 -04:00
if ( ViewHandler . GetValidInstance ( ) is not null )
2021-04-02 12:12:22 -04:00
{
2021-04-06 14:44:49 -04:00
Marshal . AddRef ( Marshal . GetIUnknownForObject ( ViewHandler . ShellView ) ) ;
shellView = ViewHandler . ShellView ;
2021-04-02 12:12:22 -04:00
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 ( )
{
2021-04-06 14:44:49 -04:00
ShellBrowserViewHandler viewHandler = ViewHandler . GetValidInstance ( ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
if ( viewHandler is not null )
2021-04-02 12:12:22 -04:00
{
// 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 ( )
{
2021-04-06 14:44:49 -04:00
HWnd = ( IntPtr ) ViewHandler . ViewWindow ,
2021-04-02 12:12:22 -04:00
Msg = ( int ) User32 . WindowMessage . WM_KEYDOWN ,
} ;
2021-04-06 14:44:49 -04:00
ProcessCmdKey ( ref msg , Keys . Control | Keys . A ) ;
2021-04-02 12:12:22 -04:00
}
}
/// <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 ( )
{
2021-04-06 14:44:49 -04:00
ShellBrowserViewHandler viewHandler = ViewHandler . GetValidInstance ( ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
if ( viewHandler is not null )
2021-04-02 12:12:22 -04:00
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 ) )
{
2021-04-06 14:44:49 -04:00
ShellBrowserViewHandler shvwHandler = ViewHandler . GetValidInstance ( ) ;
2021-04-02 12:12:22 -04:00
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
{
2021-04-06 14:44:49 -04:00
ShellBrowserViewHandler viewHandler = ViewHandler . GetValidInstance ( ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
return viewHandler is not null ? viewHandler . FolderView2 . Items < IShellItemArray > ( opt ) : null ;
2021-04-02 12:12:22 -04:00
}
catch { return null ; }
}
/// <summary>Raises the <see cref="ItemsChanged"/> event.</summary>
2021-04-06 14:44:49 -04:00
protected internal virtual void OnItemsChanged ( ) = > ItemsChanged ? . Invoke ( this , EventArgs . Empty ) ;
2021-04-02 12:12:22 -04:00
/// <summary>Raises the <see cref="Navigated"/> event.</summary>
protected internal virtual void OnNavigated ( ShellFolder shellFolder )
{
2021-04-06 14:44:49 -04:00
if ( Navigated is not null )
2021-04-02 12:12:22 -04:00
{
ShellBrowserNavigatedEventArgs eventArgs = new ( shellFolder ) ;
2021-04-06 14:44:49 -04:00
Navigated . Invoke ( this , eventArgs ) ;
2021-04-02 12:12:22 -04:00
}
}
/// <summary>Raises the <see cref="SelectionChanged"/> event.</summary>
2021-04-06 14:44:49 -04:00
protected internal virtual void OnSelectionChanged ( ) = > SelectionChanged ? . Invoke ( this , EventArgs . Empty ) ;
2021-04-02 12:12:22 -04:00
/// <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 )
{
2021-04-06 14:44:49 -04:00
if ( disposing & & ( components is not null ) )
2021-04-02 12:12:22 -04:00
{
components . Dispose ( ) ;
}
base . Dispose ( disposing ) ;
}
2021-07-21 19:13:01 -04:00
/// <summary>Raises the <see cref="E:HandleDestroyed"/> event. Saves ViewState when ShellBrowser gets closed.</summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected override void OnHandleDestroyed ( EventArgs e )
{
ViewHandler . GetValidInstance ( ) ? . ShellView . SaveViewState ( ) ;
base . OnHandleDestroyed ( e ) ;
}
/// <summary>Raises the <see cref="E:Resize"/> event. Resize ViewWindow when ShellBrowser gets resized.</summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected override void OnResize ( EventArgs e )
{
ViewHandler ? . MoveWindow ( 0 , 0 , ClientRectangle . Width , ClientRectangle . Height , false ) ;
base . OnResize ( e ) ;
}
2021-04-02 12:12:22 -04:00
/// <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.
2021-04-06 14:44:49 -04:00
if ( ViewHandler . GetValidInstance ( ) is not null )
2021-04-02 12:12:22 -04:00
{
// Note: I tried using the LVM_GETEDITCONTROL message for finding the edit control without luck
if ( User32 . GetClassName ( msg . HWnd ,
2021-04-06 14:44:49 -04:00
processCmdKeyClassName ,
2021-04-02 12:12:22 -04:00
processCmdKeyClassNameMaxLength ) > 0 )
{
if ( processCmdKeyClassName . ToString ( ) . Equals ( processCmdKeyClassNameEdit ) )
{
// Try to get Edit field's parent 'SysListView32' handle
HWND hSysListView32 = User32 . GetParent ( msg . HWnd ) ;
2021-04-06 14:44:49 -04:00
if ( ! hSysListView32 . IsNull )
2021-04-02 12:12:22 -04:00
{
// Try to get SysListView32's parent 'SHELLDLL_DefView' handle
HWND hShellDllDefViewWindow = User32 . GetParent ( hSysListView32 ) ;
2021-04-06 14:44:49 -04:00
if ( ! hShellDllDefViewWindow . IsNull & & ( hShellDllDefViewWindow = = ViewHandler . ViewWindow ) )
2021-04-02 12:12:22 -04:00
{
2021-04-06 14:44:49 -04:00
ViewHandler . ShellView . TranslateAccelerator (
2021-04-02 12:12:22 -04:00
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 )
{
2021-04-06 14:44:49 -04:00
var forward = ( keyData & Keys . Shift ) ! = Keys . Shift ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
Parent . SelectNextControl ( ActiveControl , forward : forward , tabStopOnly : true , nested : true , wrap : true ) ;
2021-04-02 12:12:22 -04:00
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 :
2021-04-06 14:44:49 -04:00
NavigateBack ( ) ;
2021-04-02 12:12:22 -04:00
return true ;
case Keys . BrowserForward :
case Keys . Alt | Keys . Right :
2021-04-06 14:44:49 -04:00
NavigateForward ( ) ;
2021-04-02 12:12:22 -04:00
return true ;
case Keys . Back :
2021-04-06 14:44:49 -04:00
NavigateParent ( ) ;
2021-04-02 12:12:22 -04:00
return true ;
}
// Let the ShellView process all other keystrokes
2021-04-06 14:44:49 -04:00
if ( ViewHandler . GetValidInstance ( ) is not null )
2021-04-02 12:12:22 -04:00
{
2021-04-06 14:44:49 -04:00
ViewHandler . ShellView . TranslateAccelerator ( new MSG ( msg . HWnd , ( uint ) msg . Msg , msg . WParam , msg . LParam ) ) ;
2021-04-02 12:12:22 -04:00
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 ( ) ;
2021-04-06 14:44:49 -04:00
AutoScaleMode = AutoScaleMode . Font ;
2021-04-02 12:12:22 -04:00
}
/// <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 ;
2021-04-06 14:44:49 -04:00
option = opt ;
2021-04-02 12:12:22 -04:00
}
/// <summary>Gets the number of elements in the collection.</summary>
/// <value>Returns a <see cref="int"/> value.</value>
public int Count
{
get
{
2021-04-06 14:44:49 -04:00
ShellBrowserViewHandler viewHandler = shellBrowser . ViewHandler . GetValidInstance ( ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
return viewHandler is not null ? viewHandler . FolderView2 . ItemCount ( option ) : 0 ;
2021-04-02 12:12:22 -04:00
}
}
2021-04-06 14:44:49 -04:00
private IShellItemArray Array = > shellBrowser . GetItemsArray ( option ) ;
2021-04-02 12:12:22 -04:00
private IEnumerable < IShellItem > Items
{
get
{
2021-04-06 14:44:49 -04:00
IShellItemArray array = Array ;
2021-04-02 12:12:22 -04:00
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
{
2021-04-06 14:44:49 -04:00
IShellItemArray array = Array ;
2021-04-02 12:12:22 -04:00
try
{
return array is null ? null : ShellItem . Open ( array . GetItemAt ( ( uint ) index ) ) ;
}
catch
{
return null ;
}
finally
{
2021-04-06 14:44:49 -04:00
if ( array is not null )
2021-04-02 12:12:22 -04:00
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>
2021-04-06 14:44:49 -04:00
public ShellBrowserNavigatedEventArgs ( ShellFolder currentFolder ) = > CurrentFolder = currentFolder ? ? throw new ArgumentNullException ( nameof ( currentFolder ) ) ;
2021-04-02 12:12:22 -04:00
/// <summary>The new location of the ShellBrowser</summary>
public ShellFolder CurrentFolder { get ; }
}
/// <summary>
2021-07-21 19:13:01 -04:00
/// 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/>
2021-04-02 12:12:22 -04:00
/// - <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/>
2021-07-21 19:13:01 -04:00
/// - <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>
2021-04-02 12:12:22 -04:00
/// </summary>
public class ShellBrowserViewHandler : IShellFolderViewCB
{
/// <summary>
/// <code>{"The operation was canceled by the user. (Exception from HRESULT: 0x800704C7)"}</code>
2021-07-21 19:13:01 -04:00
/// 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.
2021-04-02 12:12:22 -04:00
/// </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 )
{
2021-04-06 14:44:49 -04:00
Owner = owner ? ? throw new ArgumentNullException ( nameof ( owner ) ) ;
ShellFolder = shellFolder ? ? throw new ArgumentNullException ( nameof ( shellFolder ) ) ;
2021-04-02 12:12:22 -04:00
// 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 ,
} ;
2021-09-21 14:16:18 -04:00
SHCreateShellFolderView ( sfvCreate , out IShellView shellView ) . ThrowIfFailed ( ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
ShellView = shellView ? ?
throw new InvalidComObjectException ( nameof ( ShellView ) ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
FolderView2 = ( IFolderView2 ) ShellView ? ?
throw new InvalidComObjectException ( nameof ( FolderView2 ) ) ;
2021-04-02 12:12:22 -04:00
// 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
{
2021-04-06 14:44:49 -04:00
ViewWindow = ShellView . CreateViewWindow ( null , folderSettings , owner , owner . ClientRectangle ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
IsValid = true ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
Text = emptyFolderText ;
2021-04-02 12:12:22 -04:00
}
catch ( COMException ex )
{
// TODO: Check if the target folder IS actually a drive with removable disks in it!
if ( HRESULT_CANCELLED . Equals ( ex . ErrorCode ) )
2021-04-06 14:44:49 -04:00
NoDiskInDriveError = true ;
2021-04-02 12:12:22 -04:00
throw ;
}
}
catch ( COMException ex )
{
// TODO: e.g. C:\Windows\CSC => Permission denied! 0x8007 0005 E_ACCESSDENIED
2021-04-06 14:44:49 -04:00
ValidationError = ex ;
2021-04-02 12:12:22 -04:00
}
}
/// <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
{
2021-04-06 14:44:49 -04:00
get = > text ;
2021-04-02 12:12:22 -04:00
set
{
2021-04-06 14:44:49 -04:00
text = value ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
if ( IsValid )
FolderView2 . SetText ( FVTEXTTYPE . FVST_EMPTYTEXT , value ) ;
2021-04-02 12:12:22 -04:00
}
}
/// <summary>The size of the thumbnails in pixels.</summary>
public int ThumbnailSize
{
get
{
2021-04-06 14:44:49 -04:00
if ( IsValid )
FolderView2 . GetViewModeAndIconSize ( out _ , out thumbnailSize ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
return thumbnailSize ;
2021-04-02 12:12:22 -04:00
}
set
{
2021-04-06 14:44:49 -04:00
if ( IsValid )
2021-04-02 12:12:22 -04:00
{
2021-04-06 14:44:49 -04:00
FolderView2 . GetViewModeAndIconSize ( out FOLDERVIEWMODE fvm , out _ ) ;
FolderView2 . SetViewModeAndIconSize ( fvm , thumbnailSize = value ) ;
2021-04-02 12:12:22 -04:00
}
}
}
/// <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
{
2021-04-06 14:44:49 -04:00
if ( IsValid )
2021-04-02 12:12:22 -04:00
{
2021-04-06 14:44:49 -04:00
return FolderView2 . GetCurrentViewMode ( ) ;
2021-04-02 12:12:22 -04:00
// TODO: Check ThumbNailSize for new ViewModes with larger sized icons
}
return FOLDERVIEWMODE . FVM_AUTO ; // TODO!
}
set
{
2021-04-06 14:44:49 -04:00
if ( IsValid )
FolderView2 . SetCurrentViewMode ( value ) ;
2021-04-02 12:12:22 -04:00
}
}
/// <summary>The ViewWindow.</summary>
public HWND ViewWindow { get ; private set ; }
/// <summary>Destroy the view.</summary>
public void DestroyView ( )
{
2021-04-06 14:44:49 -04:00
IsValid = false ;
2021-04-02 12:12:22 -04:00
// TODO: Remove MessageSFVCB here!
// Destroy ShellView's ViewWindow
2021-04-06 14:44:49 -04:00
ViewWindow = HWND . NULL ;
ShellView . DestroyViewWindow ( ) ;
2021-04-02 12:12:22 -04:00
2021-04-06 14:44:49 -04:00
FolderView2 = null ;
ShellView = null ;
2021-04-02 12:12:22 -04:00
//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 ) = >
2021-04-06 14:44:49 -04:00
ViewWindow ! = HWND . NULL & & User32 . MoveWindow ( ViewWindow , X , Y , nWidth , nHeight , bRepaint ) ;
2021-04-02 12:12:22 -04:00
/// <summary>Activate the ShellView of this ShellBrowser.</summary>
/// <param name="uState">The <seealso cref="SVUIA"/> to be set</param>
2021-04-06 14:44:49 -04:00
public void UIActivate ( SVUIA uState = SVUIA . SVUIA_ACTIVATE_NOFOCUS ) = > ShellView ? . UIActivate ( uState ) ;
2021-04-02 12:12:22 -04:00
/// <summary>Deactivate the ShellView of this ShellBrowser.</summary>
2021-04-06 14:44:49 -04:00
public void UIDeactivate ( ) = > UIActivate ( SVUIA . SVUIA_DEACTIVATE ) ;
2021-04-02 12:12:22 -04:00
/// <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 :
2021-04-06 14:44:49 -04:00
Owner . OnSelectionChanged ( ) ;
2021-04-02 12:12:22 -04:00
return HRESULT . S_OK ;
case SFVMUD . SFVM_LISTREFRESHED :
2021-04-06 14:44:49 -04:00
Owner . OnItemsChanged ( ) ;
2021-04-02 12:12:22 -04:00
return HRESULT . S_OK ;
default :
// TODO: What happens when the ViewMode gets changed via Context-Menu? => Msg #33, #18
return HRESULT . E_NOTIMPL ;
}
}
}
}