WPF: WindowHelpers - how to retrieve a window-reference by the window's name and others

by Olaf Rabbachin 16. February 2010 15:56

Today I have a quickie that I thought would be worth sharing. Note that, even though I only present C# code in this article, the sample solution (see the bottom for the download) again contains both a project for C# and VB.

 

The problem

In the past 6 months, I've spent quite a while or two in the WPF forums, crawling over other peoples' questions and learning by answering them. A very common approach to answering is to simply post a sample window that i.e. demonstrates the solution to the OP's question. In the very beginning, I tended to have a sample app with a Window1 which got loaded upon startup, replacing the XAML and/or code-behind everytime I created a sample window. That meant that, everytime I tried something new, the earlier version was either lost or had to be copied into another window. Alternatively, I sometimes named windows and then changed App.xaml to show the window I wanted.

Any of the aforementioned approaches includes either a) the loss of previous work, or b) additional work everytime you add something or need to show a different (older) window. I hence thought it should be fairly easy to simply make a main window, provide a list of windows found throughout the application and just open them on a click (or double-click).

 

Reflection to the rescue

I must say I just love Reflection - it provides a convenient (well, most of all times) approach to obtain information that just wasn't possible (or only with many hacks) in pre-.Net-times!

Here goes.

 

Getting a list of all windows

Here's all you need to do in order to get all windows that are part of the executing assemlby (aka the exe):

 

public static IEnumerable<string> WindowNames
{
   get
   {
      IEnumerable<string> ieWindowNames = null;
      Assembly asm = Assembly.GetExecutingAssembly();

      ieWindowNames =
         from types in asm.GetTypes()
         where types.BaseType.Name == "Window"
         orderby types.Name
         select types.Name;

      return ieWindowNames;
   }
}

 

The above returns a list (IEnumerable) that would allow for binding and, thanks to Linq, sorted alphabetically.

 

Displaying windows using their name

How about providing a convenient way of displaying a window when all you have is its name (i.e., taken from the previously introduced list)?

Again, this can be done with just a few lines:

 

public static bool? ShowWindowByName(string strWindowName, bool fShowDialog)
{
   if (string.IsNullOrEmpty(strWindowName)) return null;

   Assembly asm = Assembly.GetExecutingAssembly();
   string strFullyQualifiedName = asm.GetName().Name + "." + strWindowName;
   object obj = asm.CreateInstance(strFullyQualifiedName);

   Window win = obj as Window;
   if (win == null) return null;

   if (fShowDialog)
   {
      win.ShowDialog();
      return win.DialogResult;
   }
   else
   {
      win.Show();
      return null;
   }
}

 

The above method also allows you to show the window modally in which case the method will return the DialogResult after the window was closed.

 

WindowByName

The ShowWindowByName() method might actually do more than desired. If you only need a reference to a window by its name, here's another little helper method:

 

public static Window WindowByName(string strWindowName)
{
   if (string.IsNullOrEmpty(strWindowName)) return null;

   Assembly asm = Assembly.GetExecutingAssembly();
   string strFullyQualifiedName = asm.GetName().Name + "." + strWindowName;
   object obj = asm.CreateInstance(strFullyQualifiedName);

   if (obj != null)
      return obj as Window;
   else
      return null;
}

 

Closing all windows

If you're using VB, one way to close all open windows when your application exits is to specify this in the project's settings:

However, C# doesn't have a respective counterpart. And, anyway, I tend to rather have my own method for dealing with this, so here's another simple helper:

public static void CloseAllWindows()
{
   Application app = Application.Current;
   for (int intCounter = app.Windows.Count - 1; intCounter >= 0; intCounter--)
      app.Windows[intCounter].Close();
}
Due to the simplicity, it's not really all too worth sharing (must say, I feeling a bit ashamed to post something like this), but I thought the method simply belongs to the class. Embarassed

 

The sample solution

I've placed all of the above (basic documentation included - skipped in the article) into a simple class that you'll find in the solution (see the bottom for the download link). However, since I don't want to provide a bunch of windows just to allow to test things out, the projects' sample window only contains - besides the main window - a dummy second window to have at least two of them. Here's a screenshot:

The code-behind for the above is pretty short:

using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;

namespace CS
{
   public partial class Main : Window
   {
      public Main()
      {
         InitializeComponent();
         this.DataContext = this;
         this.Unloaded += new RoutedEventHandler(MainWindow_Unloaded);
      }

      //Allows to bind the ListBox to the window names in this assembly
      public IEnumerable<string> WindowNames { get { return Utils.WindowHelpers.WindowNames; } }

      //Show a window after an item in the ListBox has been double-clicked.
      private void lbWindows_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
      {
         if (lbWindows.SelectedItem != null)
         {
            Utils.WindowHelpers.ShowWindowByName(
                  lbWindows.SelectedItem.ToString(),
                  (bool)chkShowDialog.IsChecked
               );
            e.Handled = true;
         }
      }

      void MainWindow_Unloaded(object sender, RoutedEventArgs e)
      {
         Utils.WindowHelpers.CloseAllWindows();
      }
   }
}

But wait!

In the code above, you may (or may not) have noted that the selected window is shown when double-clicking a window rather than after a single click has been performed. I have no idea as to why there isn't any DoubleClick-event for the ListBox control, but luckily, it's not all too hard to provide a dummy one. I can't take credit for that as this is something I stumbled over in this StackOverflow thread, so thanks go out to Bob King. All you'll have to remember when going that way though is to set e.Handled = true to avoid the message from bubbling up.

 

The last word

I presently don't really see much "real world" usage scenarios for the class, but my forums-life has gotten a hell of a lot easier. Using the class, I can simply add windows to my test-solution as they come and then access/run them by selecting them from a list of my pre-defined main window. One problem that this might impose on you (it does on me) - the time it takes to build your solution will increase rapidly. My present solution has short of 100 windows ...

 

The sample solution

I’ve created a sample solution that contains everything discussed here.

Download: WindowByName.zip (27.38 kb)

Tags: , , , , ,

Utilities | WPF (.net)

WPF: ColorHelper - how to retrieve the name of a given color or to retrieve a color by its name

by Olaf Rabbachin 5. February 2010 19:47

Today I stumbled over a thread in the WPF forum in which the OP was looking for a way to retrieve the (known) name for a given color.
That is, given a color such as #FFB22222 (or ARGB :: 255, 178, 32, 32), to retrieve "Firebrick". What I thought was darn simple actually isn't, because System.Windows.Media.Colors is a class and thus exposes all (known) colors as properties (as opposed to System.Drawing.KnownColor which is an enum). So there really isn't any other way than to use reflection to obtain a list of all colors. Here's a little helper class that returns the name of a color passed to it:

Retrieving the name of a given Color object

/// <summary>
/// Returns the known name of the color passed (if found), or an empty string.
/// </summary>
/// <param name="clr">The color whose name is to be returned.</param>
/// <returns>
/// The name of the passed color resp. an empty string if 
/// no matching known color could be found.
/// </returns>
public static string GetKnownColorName(Color clr)
{
   Color clrKnownColor;

   //Use reflection to get all known colors
   Type ColorType = typeof(System.Windows.Media.Colors);
   PropertyInfo[] arrPiColors = ColorType.GetProperties(BindingFlags.Public | BindingFlags.Static);

   //Iterate over all known colors, convert each to a <Color> and then compare
   //that color to the passed color.
   foreach (PropertyInfo pi in arrPiColors)
   {
      clrKnownColor = (Color)pi.GetValue(null, null);
      if (clrKnownColor == clr) return pi.Name;
   }

   return string.Empty;
}

If you call the above method with i.e. ...

string strColorName = GetKnownColorName(Color.FromArgb(255, 178, 32, 32));
MessageBox.Show(strColorName == "" ? "(None found)" : strColorName);

... the MessageBox will show "Firebrick".

 

Update to the above (Feb 13 2010)

I just updated the code for the method above after it became obvious that the GetHashCode() method is not returning unique values. I did a bit of searching in order to find out more about that, but couldn't find anything; the docs are no help at all with this respect or even when it comes to the reason for the existance of this method in the first place.

Actually, I don't have the slightest idea as to how the hashes couldn't be unique - colors resp. their ARGB values are uniqe in itself after all. However, the hashes for Colors.White and Colors.Blue are equal. Now if that isn't plain stupid! Lesson learned: never assume anything ...

Thanks to Pedro for catching this (see the comments).

 

Retrieving a list with all known colors (names + Color objects)

If you actually need a list with all color values and their name-counterparts (might make sense if you need this frequently), here's another little helper-method that will return all known colors along with the name of each color:

/// <summary>
/// Returns a list containing all known colors, each as a KeyValuePair with the name
/// as the key and the Color as the value.
/// </summary>
public static List<KeyValuePair<string, Color>> GetKnownColors()
{
   List<KeyValuePair<string, Color>> lst = new List<KeyValuePair<string, Color>>();
   Type ColorType = typeof(System.Windows.Media.Colors);
   PropertyInfo[] arrPiColors = ColorType.GetProperties(BindingFlags.Public | BindingFlags.Static);

   foreach (PropertyInfo pi in arrPiColors)
      lst.Add(new KeyValuePair<string, Color>(pi.Name, (Color)pi.GetValue(null, null)));
   return lst;
}

You could use the above in various ways, here's just one sample:

Color clr = Color.FromArgb(255, 178, 32, 32);
List> lstKnownColors = GetKnownColors();
string strColorName = (
   from c in lstKnownColors
   where
      c.Value.A == clr.A &&
      c.Value.R == clr.R &&
      c.Value.G == clr.G &&
      c.Value.B == clr.B
   select c.Key
   ).FirstOrDefault();

 

Retrieving a color via its name

Last but not least, if you need to go the opposite way, how about another simple helper method:

 

/// <summary>
/// Returns the Color which is represented by the name passed.
/// </summary>
/// <param name="strColorName">The name of the Color to retrieve.</param>
/// <returns>Nullable color - either the found Color or null.</returns>
public static Color? GetColorByName(string strColorName)
{
   Color? clrResult = null;
   try
   {
      object value = ColorConverter.ConvertFromString(strColorName);
      if (null != value) clrResult = (Color)value; 
   }
   catch (Exception) { }

   return clrResult;
}

 

Update to the above (Feb 15 2010)

I've just updated the GetColorByName method after Richard pointed out that it was prone to fail if an invalid name or an empty string were passed (see the comments). I've only resolved for a general purpose handler rather than just to catch FormatExceptions.

 

Wrapping it all up

To simplify things, I dropped the above into a class that you can simply drop into your projects.
Download it here:

C# version: ColorHelpers.cs (3.43 kb)

VB version: ColorHelpers.vb (3.48 kb)

 

Happy coding!

Tags: , , , , , ,

Utilities | WPF (.net)

About

Hi and welcome to my blog!

I'm a developer from Germany, currently focusing on .Net and WPF.

More about me ...