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(); } } }