Click here to Skip to main content
11,642,855 members (71,070 online)
Click here to Skip to main content

WebRefreshR - LiveReload All Your Browsers Easier (WebDevs Unite!)

, 6 Apr 2014 CPOL 5.2K 117 5
Rate this:
Please Sign up or sign in to vote.
WebDev - Make any/all running browsers refresh themselves each time you save your work (css, html, javascript, etc)

Introduction

My application, WebRefreshR solves a serious problem for anyone doing web development.

Here's what it does:

  1. allows you to easily select 1 or more running browsers which will be refreshed each time you save your code target (.css, .js, .htm, etc).
  2. No need to tell it which type of files, instead it will watch a directory and it's subdirs and if any of those files change, it will refresh the running browser(s) you've selected.
  3. Very easy to use. All you have to do is configure it and forget it. Each time you save, you'll see the changes in your browser(s) window.

Background

One of the worst things about building web apps is that the editor / IDE is completely separate from the process you use to view (Web Browser) so each time you make a change you have to switch to the browser window and do the key combo which refreshes the page to see your work.

Watch It In Action -- YouTube Video

You can see a quick (less than 1 minute) video of WebRefreshR in action at youtube.

https://www.youtube.com/watch?v=hrr90RqHHUY

Caveats!

There are some issues. The code is not perfect.

  1. Someone out there is going to say, "SendKeys, how amateur." Well, you might think that until you watch the video and think about how this solves the ugly LiveReload problem where you have to alter your files with the javascript link which tells the browser to reload. I don't like that. Keep in mind, you run this program, select your browser and path and editor, then every time you save your browser updates. Way cool!!!
  2. Internet Explorer shows up a few times -- I know, IE shows up in the browser list a few times because there are multiple IE procs running. Chrome and Opera have multiples running too, but when I initially call the SetForegroundWindow, the command fails -- as it should -- and then I know those are not the associated proc with the window. However, IE succeeds on those calls even though the window never shows. I fix this by allow you to right-click on the item in the browser list and say, Show Window. If the window flashes up, then you know that is the one you want to check so it refreshes.
  3. You do not want to set the Watch Path to a Root Directory ( C:\ ) -- I use the FileSystemWatcher to watch for changes in your directory, when the change event fires, I simply run code to refresh all the browsers and then (if you've chosen an editor) I set focus back to your editor. If you have the Watch Path set to a place where this event fires constantly it is going to make your system go crazy. Pick a directory where your HTML (ASP.NET, JavaScript, etc.) project is located. Also, to get out of this situation I've created a System-Wide HotKey which you can fire to make it all stop.
  4. I've included some deadwood code. This is a work-in-progress, because I want to add some advanced features, which will allow the user to send any command to any process when any path is altered, so I've left some non-running code in there for later.

First let's take a look at using the program as an end-user, then I'll talk more about the code.

Using The Program

Here's a quick walk-thru to get you up and running with the WebRefreshR.

1. Start up the program and click the [Find Running Browsers] button.

2. When you click that button, it finds all the running processes and adds the Browser processes to the bottom list and will look like the following:

3. Now, we can add an editor by finding the process in the drop down list. We want to [Add Editor] so that after the program refreshes the browser(s) it will move focus back to our editor window so we can begin typing again (after saving each time). You can see that I've located Notepad++ and am going to set it as my editor. You can use any running program as your editor even Dev Studio.

Note: When you select any of the processes from the list, the program will attempt to locate its main window and flash it at you so you will know you've selected it.

4. Now that you've selected a process you want to set, just click the [Add Editor] button and you're ready to go.

5. Next, we want to select a path to watch so that when any files in that path are changed, then the program will be notified and the browsers will be refreshed.

6. Finally, we select the browser(s) we want to be refreshed and finally click the [Start/Stop] button.

Once, you click the Start/Stop button, it will afterward say, "Stop" so you know it is running.

Now you can go to your editor, change one of your files in your project and see the browser(s) update automatically.

Make some changes and Save in your editor and you will see the browser auto-refresh immediately.

Tricky Stuff - Internet Explorer & Task Manager

If you were to look in TaskMgr you'd find that many of the browsers actually run multiple processes even though you only started one copy of the application.

I only have one copy of each of the following browsers (Opera, Chrome, IExplorer) running, but they show multiple processes.

With the other browsers I do a check to make sure I have the main window and then I only provide that process in the list, however, Internet Explorer doesn't allow me to do this so you will get multiple in the browser list.

The way to solve this is to check which one of the browsers is the actual window, by right clicking on the Browser Process in the bottom list and then selecting the menu item: Show Window. If the IE window pops up then you have the correct one.

Now that we've seen how you use the application, let's take a look at some of the implementation.

Using The Code

One of the best things about the program is that it is broken up into manageable pieces.

I needed to keep track of the browser processes that I would then find associated windows for so I created a separate class called ProcessItem. It looks like the following:

public class ProcessItem
    {
        private string name;
        private int pid;
        private bool isMarkedToRun;
        private string watchPath;
        private List<string> commandKeys = new List<string>();
        
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        
        public int Pid 
        {
            get { return pid; }
            set { pid = value; }
        }
        

        public bool IsMarkedToRun
        {
            get{return isMarkedToRun;}
            set
            {
                isMarkedToRun = value;
            }
        }
        
        public string WatchPath
        {
            get{return watchPath;}
            set{watchPath = value;}
        }
        
        public List<string>CommandKeys
        {
            get{return commandKeys;}
            set { commandKeys = value;}
        } 

This way I can keep track of the process Name, ID (Pid), if the process is marked to run (refresh) and a list of CommandKeys (commands that will be sent to the window). This is all nicely separated into it's own class so I can use this object in the mainForm.

That is one the basics of good Object-Oriented Programming.

Finding Processes and Handling Browser Processes

I think the next interesting thing about the program is how I find all the processes. C# and .Net have made this quite easy for the basics.

private void DiscoverAllProcesses() 
{ 
    Process [] allProcs = Process.GetProcesses(); 
    foreach (Process p in allProcs) 
    { 
        try { 
            if (CheckForValidBrowserName(p.ProcessName)) 
            { 
                IntPtr h = p.MainWindowHandle; 
                if (ShowWindow(h, SW_SHOWNORMAL)) 
                { 
                    if (SetForegroundWindow(h) != 0) 
                    { 
                        ProcessItem localProcItem = new ProcessItem(p.ProcessName, p.Id); 
                        AddDefaultCommandKeys(localProcItem); AddProcess(localProcItem, true); 
                    } 
                } 

                IntPtr mainFormHandle = Process.GetCurrentProcess().MainWindowHandle; 
                SetForegroundWindow(mainFormHandle); 
            } 
            else 
            { 
                ProcessItem localProcItem = new ProcessItem(p.ProcessName, p.Id); 
                AddProcess(localProcItem, false); 
            } 
        } 
        catch { } 
    }
}

But, it's more than just finding the processes, in this case. I attempt to manage them separately, because the browser processes are going to be of interest to us as I allow the user to select those processes and find the window which is associated with them.

Associate Process With Window : Win API

To associate a window with each process requires some code you'll see at the top of the MainForm class, which are some calls to WinAPI.

These calls to Win API are required simply because the .NET Framework never wrapped them up for easy usage. Luckily, the great web site pinvoke.net provides the function declarations we need to access the API functions.

        [System.Runtime.InteropServices.DllImport ("User32.dll")]
        static extern int SetForegroundWindow(IntPtr point);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        
        [DllImport("user32.dll", SetLastError=true)]
        static extern int GetWindowLong(IntPtr hWnd, int nIndex);
        
        private const int SW_SHOWNORMAL = 1;
        private const int SW_SHOWMINIMIZED = 2;
        private const int SW_SHOWMAXIMIZED = 3;
         private const int SW_RESTORE = 9;
         
         const UInt32 WS_MINIMIZE = 0x20000000;
         const int GWL_STYLE = (-16);

You can see that these declarations have the special attribute on each of them which tell .NET which System DLL it can find the function call in. After that there are some special values we have to provide to the API calls which are defined.

Tracking Processes

Summary of What The Application Must Do

The main things the application does are as follows:

  1. allow user easily see which browser processes are running
  2. allow user to indicate which of those browser processes should be refreshed
  3. allow user to indicate a path which should be watched for changes.
  4. allow user to indicate an editor processes which will be flashed back to after browser windows are refreshed.

Connecting ManForm UI to ProcessItems

You'll notice that I have two lists of processes. One is the dropdown list, which are all "non-browser" processes. How do I determine that it is a "non-browser" process? Simply by name. I have a list of names in the code which I expect to be browser processes. Some of that code looks like the following:

/// <summary>
        /// Set up a list of common browser names we will
        /// search for when finding all running processes.
        /// </summary>
        private void InitializeBrowserNames()
        {
            allBrowserNames.Add("OPERA");
            allBrowserNames.Add("SAFARI");
            allBrowserNames.Add("IEXPLORE");
            allBrowserNames.Add("FIREFOX");
            allBrowserNames.Add("CHROME");
        }
        
        private bool CheckForValidBrowserName(string procName)
        {
            foreach (string name in allBrowserNames)
            {
                if (procName.ToUpper().Contains(name))
                {
                    return true;        
                }
            }
            return false;
        }

You can surely see the problem with this method. It means that any other process which Contains a string which is also part of the name of the Big5 Group of Browsers is also going to be considered a browser.

However, this is a trade-off with simplicity. How would you determine that a process is a browser? It's an interesting challenge and I decided for my app this was the most direct and simplest solution.

I could've found the installation path for each browser, then determined if the Process Path matched that and then if it did I could say it was a browser. However, the impact here is small because if it is a browser I simply add it to the list of browsers which the user can pick to refresh, so there is little harm in doing that.

Allow User To Choose An Editor

The user isn't required to choose an editor. The reason I have her do so is so that after the program iterates through the processes and refreshes each browser, it should return input to the window where the user is typing: most likely an editor, however, it can be any process with an associated window. Visual Studio works great too. You can type in Visual Studio and save and after the selected browsers are refreshed you'll be returned to input more code (html, javascript, etc). It's fast even on slow machines.

FileSystemWatcher: Goodness, Danger and System-Wide HotKey Help

First a word about the danger.

Danger

If you set the WatchPath up at your C:\ drive (main drive) the FileSystemWatcher is going to see changes and attempt to cycle through refreshing the browsers and shifting back to your editor so often your windows are going to flip out.

System-Wide HotKey

I've implemented a HotKey you can fire at any time to make it stop. Ctrl-Shift-Up (arrow).

More about how that is implemented, later, but I wanted you to know how to toggle using the SystemWide HotKey in case you set the WatchPath to far up and things got crazy.

FileSystemWatcher is an easy way to watch the file system for changes and then react. The work is done on a separate thread (different one than the Windows Form) so it can create some challenges.

Setting Up FileSystemWatcher

You basically tell the FileSystemWatcher what function you want to perform when a particular File System event occurs. In our case I only care about the change event so I set it up like the following:

 fsw.Changed += new FileSystemEventHandler(FileChanged);

FileChanged is the function that fires -- the one where we indicate what our program will do -- when the fsw Changed event fires.

Conclusion

That is most of the basics of what happens in the program. You will see some other code which is required to handle the binding of my ProcessItem to the controls where I display them (DropList and CheckedListbox). That code feels very klugey because of some limitations related to how Microsoft handles the binding.

I hope you find this utility as useful as I do and I hope you learn from the code. Take a close look at it because there is a lot in there for such a small utility.

I think the idea is much better than LiveLoad which requires me to put JavaScript in my source files which I am working on. WebFreshR requires no alteration to the source and I think that is fantastic.

Valuable Utility?

I think this utility is extremely valuable, because it means web developers and especially newbie web devs can learn HTML, CSS, and JavaScript far easier now. Instead of having to set up GruntJS and Node (NodeJS) and the LiveReload scripts and all that, the user can simply click a few choices and see their web browser refresh.

If you find this valuable and helpful, please give me feedback and consider checking out my books on Amazon. Thanks. ~Newton

System-Wide HotKey

In my Utility namespace, you'll find a way to Register and UnRegister System-Wide hotkeys. That means, no matter where you are in the windows system the can fire the functionality.

Danger: Clashing of HotKeys

Danger is really what makes software good. If you take no chances then you're probably not doing anything fun. However, since it is system-wide I could trounce another program's hotkey so I've made mine to be Shift-Ctrl-Up (arrow). Hopefully that isn't often used.

To set a HotKey you simply pass in two things:

  1. A Windows Form which contains the method you want to run when the HotKey is pressed
  2. The HotKey value which is a bitwise OR ( | ) of System

You want to set up the HotKey when your initial form loads so that the user can use it at any time.

 void MainFormLoad(object sender, EventArgs e) 
 { 
      Utility.SystemWideHotKey.RegisterHotKey(this,(Keys.Shift | Keys.Control | Keys.Up)); 
 } 

Always remember to remove when your application closes though. Do it in the Form_Closing event.

 void MainFormFormClosing(object sender, FormClosingEventArgs e) 
 { 
     Utility.SystemWideHotKey.UnregisterHotKey(this); 
 }

Points of Interest

What Is FileDirSelector.dll?

It is basically a FileDialog/FolderDialog, a button and a TextBox all wrapped up into one control. I found myself often needing these three together so I rolled them all up into one. I have another article in draft that is going to be more about that control. I used it for the Watch Path selection because it makes things a little simpler. I plan on adding much more to that control. More soon, hopefully.

Dude, Where Did You Get That Sweet Tree Icon?

I'm glad you asked. I used my screen capture utility -- some other day I'll share this code -- to grab it out of the Shell32.dll. Did you know there are lots of cool icons in Shell32.dll?

History

Released version 3.0.0.0 with first version of the article on Saturday April 5, 2014

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

newton.saber
Architect
United States United States
My newest book is Learn Python, Think Python (amazon link opens in new window/tab)

My previous book is Object-Oriented JavaScript (See it at Amazon.com)

My book, Learn JavaScript - amazon.com link is available at Amazon.

My upcoming book, Learn AngularJS - Think AngularJS, will be releasing later in 2014.

You can learn more about me and my other books, at, NewtonSaber.com

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150731.1 | Last Updated 7 Apr 2014
Article Copyright 2014 by newton.saber
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid