using System;
using System.Collections;
using System.Collections.Generic;
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 COLORREF 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(COLORREF? 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 RECT DisplayRectangle => Wallpaper.GetMonitorRECT(Id, out var r) == HRESULT.S_OK ? r : RECT.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() { 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();
}
}
}