Multi-monitor slideshow

 

This was originally designed to display photos taken by a roaming photographer at a local attraction. The photos are relayed wirelessly to a networked computer. The images are displayed in the order in which they are taken, and are filtered so that only images taken at least 15 minutes ago and no more than 45 minutes ago (these are configurable variables) based on the average amount of time it takes for people to walk from where the photo was taken and where they’re displayed/sold. Kodak, which is the vendor used for the attraction’s photo system, has a solution for public photo display, but it only allows the display of a single photo at a time.

My solution displays two photos at a time. The application automatically runs as full-screen on both monitors, and resizes the images to fill either the length or width of the monitor, maintaining the photo’s aspect ratio. The photos are displayed for a predetermined length of time (default is 3 seconds).

The path to the photos can be either UNC or a local path. Below is the code for the Form class. The program class creates instances of this class and starts the individual threads running, but this is where all the work gets done. There’s also a Taskbar class which calls the user32.dll and disables the taskbar – this is completely different from autohide and it’s very useful for digital signage applications. You can still launch the start menu using your keyboard’s window key, but the task bar will still not be displayed.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Linq;
using System.Threading;
using System.IO;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PhotoDisplay
{
    public partial class Form1 : Form
    {
        private String[] filepaths;
        static string[] imagepaths;
        static String[] adpaths;
       private string mode="ads";
       static string mode1 = "ads";
       static string mode2 = "ads";
      // Cursor cursor;
       public struct IconInfo
       {
           public bool fIcon;
           public int xHotspot;
           public int yHotspot;
           public IntPtr hbmMask;
           public IntPtr hbmColor;
       }
        int screenNum = 0;
        public Form1()
        {

            Taskbar.Hide(); // this disables the taskbar it gets re-enabled when the application is closed
            InitializeComponent();
            Bitmap bitmap = new Bitmap(1, 1);
            Graphics g = Graphics.FromImage(bitmap);
            this.Cursor = CreateCursor(bitmap, 1, 1); // this makes the mouse pointer invisible

            this.FormBorderStyle = FormBorderStyle.None;

            Thread thread0 = new Thread(new ThreadStart(getimages)); // this gets the arrays of images to be displayed
            Thread thread1 = new Thread(new ThreadStart(RotateImages)); // this displays the images in sequence

            thread0.Start();
            Thread.Sleep(5000);
            thread1.Start();

        }
        public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
        {
            IconInfo tmp = new IconInfo();
            GetIconInfo(bmp.GetHicon(), ref tmp);
            tmp.xHotspot = xHotSpot;
            tmp.yHotspot = yHotSpot;
            tmp.fIcon = false;
            return new Cursor(CreateIconIndirect(ref tmp));
        }
        [DllImport("user32.dll")]
        public static extern IntPtr CreateIconIndirect(ref IconInfo icon);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
        public void setScreenNum(int num)
        {
            screenNum = num;
        }
        void getimages()
        {
            while (mode != "exit")
            {
                try
                {
                   // mode = "refreshing";
                    adpaths = Directory.GetFiles(Properties.Settings.Default.AdPath, "*.JPG");
                        DirectoryInfo Dir = new DirectoryInfo(Properties.Settings.Default.ImagePath);
                        FileInfo[] FileList = Dir.GetFiles("*.JPG", SearchOption.TopDirectoryOnly);

                        var query = from FI in FileList
                                    where FI.LastWriteTime.Date == DateTime.Now.Date
                                    where FI.LastWriteTime.TimeOfDay > DateTime.Now.TimeOfDay.Add(System.TimeSpan.FromMinutes(-1 * Properties.Settings.Default.Oldest))
                                    where FI.LastWriteTime.TimeOfDay < DateTime.Now.TimeOfDay.Add(System.TimeSpan.FromMinutes(-1 * Properties.Settings.Default.Newest))
                                    select FI.FullName;
                        imagepaths = query.ToArray();
                      //  MessageBox.Show(filepaths.Length.ToString() + " images found");
                        // if the number of photos found is less than the pre-defined minimum, just rotate through the ad images
                        if (filepaths.Length < Properties.Settings.Default.Minimumimages)
                        {
                            mode = "ads";

                        }
                        else this.mode = "images";

                    adpaths = Directory.GetFiles(Properties.Settings.Default.AdPath, "*.JPG");
                    // MessageBox.Show("finished getting images Mode:" + mode);
                }
                catch (System.IO.IOException io)
                {
                    Console.WriteLine(io.Message);
                    if (Properties.Settings.Default.debug) MessageBox.Show("offline");
                   // status = "offline";
                   // break;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    this.mode = "ads";
                    if (Properties.Settings.Default.debug) MessageBox.Show(e.Message + e.StackTrace, "getimages: " + e.ToString());

                }
                //status = "idle";
                Thread.SpinWait(30);
            }
        }
        void RotateImages()
        {

            while (mode != "exit")
            {
               // Point p;

                try
                {

                    int cycle = 0;

                    if (screenNum == 0)
                        mode1 = this.mode;
                    else mode2 = this.mode;
                    if (this.mode == "images") filepaths = imagepaths;
                    else filepaths = adpaths;

                        while (mode1 != mode2)
                        {
                            Thread.Sleep(100);
                            cycle++;
                            if (cycle > 100) break;
                        }

                    if (!String.IsNullOrEmpty(filepaths[0]))
                    {
                        for (int i = screenNum; i < filepaths.Length; i = i + 2)
                        {

                            //MessageBox.Show(filepaths[i]);
                            Image img = Image.FromFile("aqLogo.jpg");
                            img = Image.FromFile(filepaths[i]);
                            img = resizeImage(img, this.Size);
                            pictureBox1.Invoke(new MethodInvoker(delegate { resizePicturebox(img.Size); }));
                            pictureBox1.Invoke(new MethodInvoker(delegate { pictureBox1.Image = img; }));

                            if (this.mode == "ads") Thread.Sleep(Properties.Settings.Default.AdDelay);
                            else Thread.Sleep(Properties.Settings.Default.ImageDelay);

                        }
                        if (this.mode == "images")
                            this.mode = "ads";
                        else if (this.mode == "ads") this.mode = "images";
                    }
                }
                catch (System.NullReferenceException nr)
                {
                    if (Properties.Settings.Default.debug)
                        MessageBox.Show("Rotate Null Reference: " + nr.Message);
                    mode = "ads";
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    if (Properties.Settings.Default.debug) MessageBox.Show(e.Message + " from: " + e.StackTrace, "rotate: " + e.ToString());
                    //break;
                }
                //MessageBox.Show("rotation complete");

            }

        }
        private static Image resizeImage(Image imgToResize, Size size)
        {
            int sourceWidth = imgToResize.Width;
            int sourceHeight = imgToResize.Height;

            float nPercent = 0;
            float nPercentW = 0;
            float nPercentH = 0;

            nPercentW = ((float)size.Width / (float)sourceWidth);
            nPercentH = ((float)size.Height / (float)sourceHeight);

            if (nPercentH < nPercentW)
                nPercent = nPercentH;
            else
                nPercent = nPercentW;

            int destWidth = (int)(sourceWidth * nPercent);
            int destHeight = (int)(sourceHeight * nPercent);
            Bitmap c = new Bitmap(destWidth, destHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            Graphics g = Graphics.FromImage((Image)c);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
            g.Dispose();
            return (Image)c;
        }

        private void Form1_Resize(object sender, EventArgs e)
        {

            this.pictureBox1.Width = (this.Height / this.pictureBox1.Height * this.pictureBox1.Width);
            this.pictureBox1.Height = this.Height;

        }

        private void pictureBox1_Resize(object sender, EventArgs e)
        {
            Point location = new Point();
            location.Y = pictureBox1.Location.Y;
            location.X = (this.Width - pictureBox1.Width) / 2;
            pictureBox1.Location = location;
        }

        private void resizePicturebox(Size size){
            pictureBox1.Size=size;
            Point location = new Point();
            location.Y = 0;
            location.X = (this.Width - pictureBox1.Width) / 2;
            pictureBox1.Location = location;
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                mode = "exit";
                Taskbar.Show();
                Process[] processlist = Process.GetProcesses();
                foreach (Process p in processlist)
                {

                    if (p.ProcessName.Contains("PhotoDisplay")) p.Kill();
                }

            }
        }

    }
}