From 64c7389e971d5d9d952e60687ccff185e6d4172e Mon Sep 17 00:00:00 2001 From: dahall Date: Mon, 20 Jan 2020 22:48:24 -0700 Subject: [PATCH] Added ability to put MRU "Clear All" menu item at top (#104), ability to give "Clear All" menu an icon (#103), and provided callback to provide file icons (#102). --- WIndows.Forms/Components/MRUManager.cs | 286 ++++++++++++++++++--------------- 1 file changed, 158 insertions(+), 128 deletions(-) diff --git a/WIndows.Forms/Components/MRUManager.cs b/WIndows.Forms/Components/MRUManager.cs index 7ed66335..22b1316e 100644 --- a/WIndows.Forms/Components/MRUManager.cs +++ b/WIndows.Forms/Components/MRUManager.cs @@ -19,9 +19,12 @@ namespace Vanara.Configuration [DefaultEvent(nameof(RecentFileMenuItemClick))] public class MRUManager : Component { + /// This value should contain the appropriate image for the clear list menu item in derived classes. + protected object clearListMenuItemImage; + private const string defClearListMenuItemText = "Clear List"; private const int defMaxHistoryCount = 10; - + private MenuPlacement clearListMenuItemPlacement; private string clearListMenuItemText = defClearListMenuItemText; private string[] exts; private IFileListStorage storage; @@ -42,10 +45,24 @@ namespace Vanara.Configuration [Category("Behavior"), Description("Occurs when the clear recent files menu item is clicked.")] public event Action ClearListMenuItemClick; + /// Occurs when [get menu image for file]. + [Category("Behavior"), Description("Occurs when a file menu item is about to be drawn and is requesting an image.")] + public event Func GetMenuImageForFile; + /// Occurs when one of the automatically added recent file menu items is clicked. [Category("Behavior"), Description("Occurs when one of the automatically added recent file menu items is clicked.")] public event Action RecentFileMenuItemClick; + /// The placement of a menu item in a list. + public enum MenuPlacement + { + /// The bottom of the list, after all items. + Bottom, + + /// The top of the list, before all items. + Top + } + /// Defines a class that implements storage for an MRU file list. public interface IFileListStorage { @@ -74,9 +91,25 @@ namespace Vanara.Configuration /// Rebuilds the menus. /// The file listing. /// The handler for when one of the automatically added recent file menu items is clicked.. - /// The clear list menu item text. A null value indicates that this menu items should not be shown. + /// + /// The clear list menu item text. A null value indicates that this menu item should not be shown. + /// /// The handler for when the clear recent files menu item is clicked. - void RebuildMenus(IEnumerable files, Action fileMenuItemClick, string clearListMenuItemText = null, Action clearListMenuItemClick = null); + /// + /// The clear list menu item image. A null value indicates that this menu item's image should not be shown. + /// + /// if set to , the clear list menu item precedes the files. + /// The menu image callback delegate. + void RebuildMenus(IEnumerable files, Action fileMenuItemClick, string clearListMenuItemText = null, Action clearListMenuItemClick = null, object clearListMenuItemImage = null, bool clearListMenuItemOnTop = false, Func menuImageCallback = null); + } + + /// Gets or sets the clear list menu item placement relative to the MRU items. + /// The clear list menu item placement. + [Category("Appearance"), DefaultValue(typeof(MenuPlacement), "Bottom"), Description("The clear list menu item text."), Localizable(true)] + public MenuPlacement ClearListMenuItemPlacement + { + get => clearListMenuItemPlacement; + set { clearListMenuItemPlacement = value; RefreshRecentFilesMenu(); } } /// Gets or sets the clear list menu item text. @@ -219,7 +252,7 @@ namespace Vanara.Configuration protected void RefreshRecentFilesMenu() { if (!DesignMode) - MenuBuilderHandler?.RebuildMenus(Files, OnRecentFileMenuItemClick, ClearListMenuItemText, OnClearListMenuItemClick); + MenuBuilderHandler?.RebuildMenus(Files, OnRecentFileMenuItemClick, ClearListMenuItemText, OnClearListMenuItemClick, clearListMenuItemImage, clearListMenuItemPlacement == MenuPlacement.Top, GetMenuImageForFile); } [DllImport("shell32.dll", CharSet = CharSet.Ansi)] @@ -258,7 +291,9 @@ namespace Vanara.Configuration private ApplicationSettingsBase settings; /// Initializes a new instance of the class. - /// The settings object to use. If , the component will search all loaded assemblies for an instance. + /// + /// The settings object to use. If , the component will search all loaded assemblies for an instance. + /// public AppSettingsFileListStorage(ApplicationSettingsBase settings = null) { if (settings != null) Settings = settings; @@ -371,12 +406,10 @@ namespace Vanara.Configuration { try { - using (var rK = GetKey()) - { - var l = new List(GetFiles(rK)); - l.Insert(0, fileNameWithFullPath); - SetFiles(rK, l.Distinct()); - } + using var rK = GetKey(); + var l = new List(GetFiles(rK)); + l.Insert(0, fileNameWithFullPath); + SetFiles(rK, l.Distinct()); } catch (Exception ex) { @@ -410,12 +443,10 @@ namespace Vanara.Configuration { try { - using (var rK = GetKey()) - { - var l = new List(GetFiles(rK)); - l.RemoveAll(s => string.Equals(s, fileNameWithFullPath, StringComparison.InvariantCultureIgnoreCase)); - SetFiles(rK, l.Distinct()); - } + using var rK = GetKey(); + var l = new List(GetFiles(rK)); + l.RemoveAll(s => string.Equals(s, fileNameWithFullPath, StringComparison.InvariantCultureIgnoreCase)); + SetFiles(rK, l.Distinct()); } catch (Exception ex) { @@ -470,17 +501,16 @@ namespace Vanara.Configuration } } -#if !WPF - namespace Vanara.Windows.Forms { - using Vanara.Configuration; using System.Drawing; using System.Windows.Forms; + using Vanara.Configuration; /// - /// A class that manages a Most Recently Used file listing and interacts with a MenuStrip to display a menu list of the files. By default, the application - /// settings are used to store the history. Optionally a constructor can be used to provide an alternate class to handle that work. + /// A class that manages a Most Recently Used file listing and interacts with a MenuStrip to display a menu list of the files. By + /// default, the application settings are used to store the history. Optionally a constructor can be used to provide an alternate class + /// to handle that work. /// [DefaultProperty("RecentFileMenuItem")] public class MenuStripMRUManager : MRUManager @@ -510,6 +540,21 @@ namespace Vanara.Windows.Forms RefreshRecentFilesMenu(); } + /// Gets or sets the clear list menu item image. + /// The clear list menu item image. + [Category("Appearance"), DefaultValue(null), Description("The clear list menu item icon."), Localizable(true)] + public Image ClearListMenuItemImage + { + get => clearListMenuItemImage as Image; + set + { + if (!(value is Image)) + throw new ArgumentException("Must be of type Image."); + clearListMenuItemImage = value; + RefreshRecentFilesMenu(); + } + } + /// Gets or sets the recent file menu item. /// The recent file menu item. [DefaultValue(null), Category("Behavior"), Description("The recent file menu item.")] @@ -549,30 +594,45 @@ namespace Vanara.Windows.Forms } /// Rebuilds the menus. - /// The files. - /// The file menu item click. - /// The clear list menu item text. - /// The clear list menu item click. - public void RebuildMenus(IEnumerable files, Action fileMenuItemClick, string clearListMenuItemText = null, Action clearListMenuItemClick = null) + /// The file listing. + /// The handler for when one of the automatically added recent file menu items is clicked.. + /// + /// The clear list menu item text. A null value indicates that this menu item should not be shown. + /// + /// The handler for when the clear recent files menu item is clicked. + /// + /// The clear list menu item image. A null value indicates that this menu item's image should not be shown. + /// + /// if set to , the clear list menu item precedes the files. + /// The menu image callback delegate. + public void RebuildMenus(IEnumerable files, Action fileMenuItemClick, string clearListMenuItemText = null, Action clearListMenuItemClick = null, object clearListMenuItemImage = null, bool clearListMenuItemOnTop = false, Func menuImageCallback = null) { if (RecentFileMenuItem == null) return; RecentFileMenuItem.DropDownItems.Clear(); - fileMenuItemClickAction = fileMenuItemClick; - foreach (var f in files) - RecentFileMenuItem.DropDownItems.Add(new ToolStripMenuItem(CompactPath(f, RecentFileMenuItem.Font, RecentFileMenuItem.Width), null, OnFileMenuItemClick) { Tag = f }); - - if (RecentFileMenuItem.DropDownItems.Count == 0) + if (files is null || !files.Any()) { RecentFileMenuItem.Enabled = false; return; } - if (!string.IsNullOrEmpty(clearListMenuItemText)) + if (clearListMenuItemOnTop && !string.IsNullOrEmpty(clearListMenuItemText)) + { + RecentFileMenuItem.DropDownItems.Add(clearListMenuItemText, clearListMenuItemImage as Image, (o, e) => clearListMenuItemClick?.Invoke()); + RecentFileMenuItem.DropDownItems.Add("-"); + } + + fileMenuItemClickAction = fileMenuItemClick; + foreach (var f in files) + { + RecentFileMenuItem.DropDownItems.Add(new ToolStripMenuItem(CompactPath(f, RecentFileMenuItem.Font, RecentFileMenuItem.Width), menuImageCallback?.Invoke(f) as Image, OnFileMenuItemClick) { Tag = f }); + } + + if (!clearListMenuItemOnTop && !string.IsNullOrEmpty(clearListMenuItemText)) { RecentFileMenuItem.DropDownItems.Add("-"); - RecentFileMenuItem.DropDownItems.Add(clearListMenuItemText, null, (o, e) => clearListMenuItemClick?.Invoke()); + RecentFileMenuItem.DropDownItems.Add(clearListMenuItemText, clearListMenuItemImage as Image, (o, e) => clearListMenuItemClick?.Invoke()); } RecentFileMenuItem.Enabled = true; } @@ -586,111 +646,81 @@ namespace Vanara.Windows.Forms } } -#else -namespace Vanara.Windows.Controls -{ - using Vanara.Configuration; - using System.Windows; +//namespace Vanara.Windows.Controls +//{ +// using Vanara.Configuration; +// using System.Windows.Controls; - /// - /// A class that manages a Most Recently Used file listing and interacts with a MenuStrip to display a menu list of the files. By default, the application - /// settings are used to store the history. Optionally a constructor can be used to provide an alternate class to handle that work. - /// - [DefaultProperty("RecentFileMenuItem")] - public class MenuMRUManager : MRUManager - { - /// Initializes a new instance of the class. - public MenuMRUManager() : base(new AppSettingsFileListStorage(), new MenuBuilder()) - { - } +// /// +// +// /// A class that manages a Most Recently Used file listing and interacts with a MenuStrip to display a menu list of the files. By +// default, the application /// settings are used to store the history. Optionally a constructor can be used to provide an alternate class +// to handle that work. /// +// +// [DefaultProperty("RecentFileMenuItem")] public class MenuMRUManager : MRUManager { /// +// Initializes a new instance of the class. +// public MenuMRUManager() : base(new AppSettingsFileListStorage(), new MenuBuilder()) { } - /// Initializes a new instance of the class. - /// The extensions of files to find in system MRU list. - /// The parent "Recent Files" menu item. - /// Action to run when The on recent file click. - /// Optional. The on clear recent files click. - /// Optional. The storage handler. - public MenuMRUManager(string extensions, MenuItem parentMenuItem, Action onRecentFileClick, Action onClearRecentFilesClick = null, IFileListStorage storageHandler = null) - : base(storageHandler ?? new AppSettingsFileListStorage(), new MenuBuilder()) - { - FileExtensions = extensions; - ((MenuBuilder)MenuBuilderHandler).RecentFileMenuItem = parentMenuItem; - if (onRecentFileClick != null) - RecentFileMenuItemClick += onRecentFileClick; - if (onClearRecentFilesClick != null) - ClearListMenuItemClick += onClearRecentFilesClick; +// /// Initializes a new instance of the class. /// The extensions +// of files to find in system MRU list. /// The parent "Recent Files" menu item. /// Action to run when The on recent file click. /// Optional. The on +// clear recent files click. /// Optional. The storage handler. public MenuMRUManager(string +// extensions, MenuItem parentMenuItem, Action onRecentFileClick, Action onClearRecentFilesClick = null, +// IFileListStorage storageHandler = null) : base(storageHandler ?? new AppSettingsFileListStorage(), new MenuBuilder()) { FileExtensions = +// extensions; ((MenuBuilder)MenuBuilderHandler).RecentFileMenuItem = parentMenuItem; if (onRecentFileClick != null) RecentFileMenuItemClick +// += onRecentFileClick; if (onClearRecentFilesClick != null) ClearListMenuItemClick += onClearRecentFilesClick; - RefreshRecentFilesMenu(); - } +// RefreshRecentFilesMenu(); } - /// Gets or sets the recent file menu item. - /// The recent file menu item. - [DefaultValue(null), Category("Behavior"), Description("The recent file menu item.")] - public MenuItem RecentFileMenuItem - { - get => ((MenuBuilder)MenuBuilderHandler).RecentFileMenuItem; - set { ((MenuBuilder)MenuBuilderHandler).RecentFileMenuItem = value; RefreshRecentFilesMenu(); } - } +// /// +// Gets or sets the recent file menu item. +// /// +// The recent file menu item. +// [DefaultValue(null), Category("Behavior"), Description("The recent file menu item.")] public MenuItem RecentFileMenuItem { get => +// ((MenuBuilder)MenuBuilderHandler).RecentFileMenuItem; set { ((MenuBuilder)MenuBuilderHandler).RecentFileMenuItem = value; +// RefreshRecentFilesMenu(); } } - /// Builds a menu within a MenuStrip. - private class MenuBuilder : IMenuBuilder - { - private Action fileMenuItemClickAction; +// /// Builds a menu within a MenuStrip. private class MenuBuilder : IMenuBuilder { private Action fileMenuItemClickAction; - /// Gets or sets the recent file menu item. - /// The recent file menu item. - public MenuItem RecentFileMenuItem { get; set; } +// /// +// Gets or sets the recent file menu item. +// /// +// The recent file menu item. +// public MenuItem RecentFileMenuItem { get; set; } - /// Clears the recent files. - public void ClearRecentFiles() - { - if (RecentFileMenuItem == null) return; - RecentFileMenuItem.Items.Clear(); - RecentFileMenuItem.IsEnabled = false; - } +// /// +// Clears the recent files. +// public void ClearRecentFiles() { if (RecentFileMenuItem == null) return; RecentFileMenuItem.Items.Clear(); RecentFileMenuItem.IsEnabled = +// false; } - /// Rebuilds the menus. - /// The files. - /// The file menu item click. - /// The clear list menu item text. - /// The clear list menu item click. - public void RebuildMenus(IEnumerable files, Action fileMenuItemClick, string clearListMenuItemText, Action clearListMenuItemClick) - { - if (RecentFileMenuItem == null) return; +// /// Rebuilds the menus. /// The file listing. /// The +// handler for when one of the automatically added recent file menu items is clicked.. /// The +// clear list menu item text. A null value indicates that this menu item should not be shown. /// The handler for when the clear recent files menu item is clicked. /// The clear list menu item image. A null value indicates that this menu item's image should not be +// shown. /// if set to , the clear list menu item precedes the +// files. /// The menu image callback delegate. public void RebuildMenus(IEnumerable +// files, Action fileMenuItemClick, string clearListMenuItemText = null, Action clearListMenuItemClick = null, object +// clearListMenuItemImage = null, bool clearListMenuItemOnTop = false, Func menuImageCallback = null) { if +// (RecentFileMenuItem == null) return; - RecentFileMenuItem.Items.Clear(); +// RecentFileMenuItem.Items.Clear(); - fileMenuItemClickAction = fileMenuItemClick; - foreach (var f in files) - AddMenuItem(f, OnFileMenuItemClick); +// fileMenuItemClickAction = fileMenuItemClick; foreach (var f in files) AddMenuItem(f, OnFileMenuItemClick); - if (RecentFileMenuItem.Items.Count == 0) - { - RecentFileMenuItem.IsEnabled = false; - return; - } +// if (RecentFileMenuItem.Items.Count == 0) { RecentFileMenuItem.IsEnabled = false; return; } - if (!string.IsNullOrEmpty(clearListMenuItemText)) - { - RecentFileMenuItem.Items.Add(new Separator()); - AddMenuItem(clearListMenuItemText, (o, e) => clearListMenuItemClick()); - } - RecentFileMenuItem.IsEnabled = true; - } +// if (!string.IsNullOrEmpty(clearListMenuItemText)) { RecentFileMenuItem.Items.Add(new Separator()); AddMenuItem(clearListMenuItemText, (o, +// e) => clearListMenuItemClick()); } RecentFileMenuItem.IsEnabled = true; } - private void AddMenuItem(string f, System.Windows.RoutedEventHandler handler) - { - var m = new MenuItem() { Header = f, Tag = f }; - m.Click += handler; - RecentFileMenuItem.Items.Add(m); - } +// private void AddMenuItem(string f, System.Windows.RoutedEventHandler handler) { var m = new MenuItem() { Header = f, Tag = f }; m.Click +// += handler; RecentFileMenuItem.Items.Add(m); } - private void OnFileMenuItemClick(object sender, RoutedEventArgs e) - { - if (sender is MenuItem item) - fileMenuItemClickAction(item.Tag.ToString()); - } - } - } -} -#endif \ No newline at end of file +// private void OnFileMenuItemClick(object sender, RoutedEventArgs e) +// { +// if (sender is MenuItem item) +// fileMenuItemClickAction(item.Tag.ToString()); +// } +// } +// } +//} \ No newline at end of file