From c251318fc1f6f8250f854f5eb026164b5a5ae7e5 Mon Sep 17 00:00:00 2001 From: dahall Date: Wed, 10 Jun 2020 20:38:05 -0600 Subject: [PATCH] Added WallpaperManager to Vanara.Windows.Shell to manage wallpapers. --- Windows.Shell/WallpaperManager.cs | 254 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 Windows.Shell/WallpaperManager.cs diff --git a/Windows.Shell/WallpaperManager.cs b/Windows.Shell/WallpaperManager.cs new file mode 100644 index 00000000..79bdc11c --- /dev/null +++ b/Windows.Shell/WallpaperManager.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using Vanara.Extensions; +using Vanara.InteropServices; +using Vanara.PInvoke; +using static Vanara.PInvoke.Shell32; + +namespace Vanara.Windows.Shell +{ + /// Specifies how the desktop wallpaper should be displayed. + public enum WallpaperFit + { + /// Center the image; do not stretch. + Center, + + /// Tile the image across all monitors. + Tile, + + /// Stretch the image to exactly fit on the monitor. + Stretch, + + /// + /// Stretch the image to exactly the height or width of the monitor without changing its aspect ratio or cropping the image. This + /// can result in colored letterbox bars on either side or on above and below of the image. + /// + Fit, + + /// Stretch the image to fill the screen, cropping the image as necessary to avoid letterbox bars. + Fill, + + /// Spans a single image across all monitors attached to the system. + Span, + } + + /// Provides methods for managing the desktop wallpaper. + public static class WallpaperManager + { + [ThreadStatic] + private static ComReleaser pWallpaper; + + /// + /// Sets the color that is visible on the desktop when no image is displayed or when the desktop background has been disabled. This + /// color is also used as a border when the desktop wallpaper does not fill the entire screen. + /// + /// A value that specifies the background RGB color value. + public static Color BackgroundColor + { + get => Wallpaper.GetBackgroundColor(); + set => Wallpaper.SetBackgroundColor(value); + } + + /// Enables or disables the desktop background. + /// to enable the desktop background, to disable it. + public static bool Enabled + { + get => Status.IsFlagSet(DESKTOP_SLIDESHOW_STATE.DSS_ENABLED); + set => Wallpaper.Enable(value); + } + + /// The monitors that are associated with the system. + /// A list of system monitors. + public static IReadOnlyList Monitors { get; } = new WallpaperMonitors(); + + /// Gets an object that controls the wallpaper slideshow options. + /// The slideshow options object. + public static WallpaperSlideshow Slideshow { get; } = new WallpaperSlideshow(); + + /// + /// Gets or sets the display option for the desktop wallpaper image, determining whether the image should be centered, tiled, or stretched. + /// + /// An enumeration value that specify how the image will be displayed on the system's monitors. + public static WallpaperFit WallpaperFit + { + get => (WallpaperFit)Wallpaper.GetPosition(); + set => Wallpaper.SetPosition((DESKTOP_WALLPAPER_POSITION)value); + } + + internal static DESKTOP_SLIDESHOW_STATE Status => Wallpaper.GetStatus(); + + private static IDesktopWallpaper Wallpaper => pWallpaper?.Item ?? (pWallpaper = ComReleaserFactory.Create(new IDesktopWallpaper())).Item; + + /// Sets the wallpaper to a single picture. + /// The full path to the image file. This file must exist and be a picture format (jpg, gif, etc.). + /// The display option for the desktop wallpaper image. + /// + /// The index of the monitor on which to set the wallpaper. Any value less than 0 will cause the picture to be set for all monitors. + /// + /// + public static void SetPicture(string imagePath, WallpaperFit fit, int monitorIndex = -1) + { + if (!File.Exists(imagePath)) throw new FileNotFoundException(); + Enabled = true; + Slideshow.Images = null; + Wallpaper.SetWallpaper(monitorIndex < 0 ? null : Monitors[monitorIndex].Id, imagePath); + WallpaperFit = fit; + } + + /// Sets the wallpaper to display a slideshow of all images within the specified directory. + /// The full path to the folder that contains the images to use in the slideshow. + /// The display option for the displayed image. + /// + /// If specified, the amount of time between image transitions. Specifying will ignore setting this value and + /// any previous setting will stay in place. + /// + /// + /// If specified, determined if the items in will be shuffled. Specifying will + /// ignore setting this value and any previous setting will stay in place. + /// + /// + public static void SetSlideshow(string folderPath, WallpaperFit fit, TimeSpan? interval = null, bool? shuffle = false) + { + if (!Directory.Exists(folderPath)) throw new DirectoryNotFoundException(); + Enabled = true; + Slideshow.Images = (IReadOnlyList)new List() { new ShellItem(folderPath) }; + WallpaperFit = fit; + if (interval.HasValue) Slideshow.Interval = interval.Value; + if (shuffle.HasValue) Slideshow.Shuffle = shuffle.Value; + } + + /// Sets the background to a solid color with no displayed image. + /// + /// If specified, the color of the background. Specifying will ignore setting this value and any previous + /// setting will stay in place. + /// + public static void SetSolidBackground(Color? color = null) + { + Enabled = false; + Wallpaper.SetWallpaper(null, ""); + Slideshow.Images = null; + WallpaperFit = WallpaperFit.Fill; + if (color.HasValue) BackgroundColor = color.Value; + } + + /// Represents the wallpaper settings on a single monitor. + public class WallpaperMonitor + { + /// Retrieves the display rectangle of the monitor. + /// The display rectangle. + public Rectangle DisplayRectangle => Wallpaper.GetMonitorRECT(Id, out var r) == HRESULT.S_OK ? (Rectangle)r : Rectangle.Empty; + + /// Gets the identifier of the monitor. + /// The monitor identifier. + public string Id { get; internal set; } + + /// + /// The path to the wallpaper image file. Note that this image could be currently displayed on all of the system's monitors, not + /// just this monitor. + /// + /// The image path. + public string ImagePath + { + get => Wallpaper.GetWallpaper(Id, out var img).Succeeded ? img : null; + set => Wallpaper.SetWallpaper(Id, value ?? string.Empty); + } + + /// + public override string ToString() => $"{Id}: Rect={DisplayRectangle}, Path={ImagePath}"; + } + + /// Represents the settings for a wallpaper slideshow. + public class WallpaperSlideshow + { + /// Gets or sets the images that are being displayed in the desktop wallpaper slideshow. + public IReadOnlyList Images + { + get + { + try + { + var shArray = Wallpaper.GetSlideshow(); + return new ShellItemArray(shArray); + } + catch + { + return (IReadOnlyList)new List(); + } + } + + set + { + var shArray = value as ShellItemArray ?? (value is null || value.Count == 0 ? new ShellItemArray() : new ShellItemArray(value)); + switch (shArray.Count) + { + case 0: + break; + + case 1: + if (!shArray[0].IsFolder) + throw new ArgumentException("In a list with only one item, that item must be a container.", nameof(Images)); + break; + + default: + var parent = shArray[0].Parent; + if (shArray.Any(shi => shi.IsFolder || shi.Parent != parent)) + throw new ArgumentException("In a list with more than one item, all items must be image paths in the same container.", nameof(Images)); + break; + } + Wallpaper.SetSlideshow(shArray.IShellItemArray); + } + } + + /// Gets or sets the amount of time between image transitions. + /// The interval between transitions. + public TimeSpan Interval + { + get { Wallpaper.GetSlideshowOptions(out _, out var tick); return TimeSpan.FromMilliseconds(tick); } + set => Wallpaper.SetSlideshowOptions(Shuffle ? DESKTOP_SLIDESHOW_OPTIONS.DSO_SHUFFLEIMAGES : 0, (uint)value.TotalMilliseconds); + } + + /// Gets a value indicating whether the slideshow is enabled. + /// if the slideshow is enabled; otherwise, . + public bool IsEnabled => Status.IsFlagSet(DESKTOP_SLIDESHOW_STATE.DSS_SLIDESHOW); + + /// Gets or sets a value indicating whether the images in the slideshow are shuffled. + /// if images are shuffled; otherwise, . + public bool Shuffle + { + get { Wallpaper.GetSlideshowOptions(out var opt, out _); return opt == DESKTOP_SLIDESHOW_OPTIONS.DSO_SHUFFLEIMAGES; } + set => Wallpaper.SetSlideshowOptions(value ? DESKTOP_SLIDESHOW_OPTIONS.DSO_SHUFFLEIMAGES : 0, (uint)Interval.TotalMilliseconds); + } + + /// Switches the wallpaper on a specified monitor to the next image in the slideshow. + /// + /// If set to , advances the slideshow forward. If , advances backwards. + /// + /// + /// The index of the monitor on which to set the wallpaper. Any value less than 0 will cause the picture to be set for all monitors. + /// + public void Advance(bool forward = true, int monitorIndex = -1) => + Wallpaper.AdvanceSlideshow(monitorIndex < 0 ? null : Monitors[monitorIndex].Id, forward ? DESKTOP_SLIDESHOW_DIRECTION.DSD_FORWARD : DESKTOP_SLIDESHOW_DIRECTION.DSD_BACKWARD); + } + + private class WallpaperMonitors : IReadOnlyList + { + public int Count => (int)Wallpaper.GetMonitorDevicePathCount(); + + public WallpaperMonitor this[string id] => new WallpaperMonitor { Id = id }; + + public WallpaperMonitor this[int index] => this[Wallpaper.GetMonitorDevicePathAt((uint)index, out var id).Succeeded ? id : throw new ArgumentOutOfRangeException(nameof(index))]; + + public IEnumerator GetEnumerator() + { + for (var i = 0; i < Count; i++) + yield return this[i]; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + } +} \ No newline at end of file