I’ve a WPF project composed of multiple Views, especially targeted to use multi monitor; the user should be able to position each view around multiple monitors. One of the key requirements is that the software should be able to keep track of the position of all the views to automatically position them in the very same position on opening.

Thanks to MVVM architecture obtaining this result is really simple because we can handle and test the whole logic inside a base viewmodel. I started with the creation of an object that could contain all values to reposition a windows

   1: [Serializable]

   2: public class WindowsPosition : UiObjectBase

   3: {

   4:   /// <summary>

   5:   /// 

   6:   /// </summary>

   7:   /// <value></value>

   8:   public Double Left

   9:   {

  10:       get { return _left; }

  11:       set { this.Set(p => p.Left, value, ref _left); }

  12:   }

  13:  

  14:   private Double _left;

  15:  

  16:   /// <summary>

  17:   /// 

  18:   /// </summary>

  19:   /// <value></value>

  20:   public Double Top

  21:   {

  22:       get { return _top; }

  23:       set { this.Set(p => p.Top, value, ref _top); }

  24:   }

  25:  

  26:   private Double _top;

  27:  

  28:   /// <summary>

  29:   /// 

  30:   /// </summary>

  31:   /// <value></value>

  32:   public Double Height

  33:   {

  34:       get { return _height; }

  35:       set { this.Set(p => p.Height, value, ref _height); }

  36:   }

  37:  

  38:   private Double _height;

  39:  

  40:   /// <summary>

  41:   /// 

  42:   /// </summary>

  43:   /// <value></value>

  44:   public Double Width

  45:   {

  46:       get { return _width; }

  47:       set { this.Set(p => p.Width, value, ref _width); }

  48:   }

  49:  

  50:   private Double _width;

  51: }

Then I insert some standard logic inside the base ViewModel, I simply want to store, for each ViewModel, the relative position of the corresponding view, and I can store everything in a simple dictionary using the name of the ViewModel as the key.

   1: private static Dictionary<string, WindowsPosition> _positions;

   2:   internal static Dictionary<string, WindowsPosition> Positions

   3:   {

   4:       get { return _positions ?? (_positions = GetPositions()); }

   5:   }

   6:  

   7:   private static Dictionary<string, WindowsPosition> GetPositions()

   8:   {

   9:       try

  10:       {

  11:           return Serializer.Base64Deserialize<Dictionary<String, WindowsPosition>>(

  12:               Properties.Settings.Default.LastClosePositions);

  13:       }

  14:       catch (Exception)

  15:       {

  16:           Properties.Settings.Default.LastClosePositions = "";

  17:       }

  18:       return new Dictionary<string, WindowsPosition>();

  19:   }

I use an Helper Serializer class that does binary serialization and convert the result in Base64 to store data in a simple String setting of the application to simplify save and load of the new values. Positions readonly property is static and contains all the logic to deserialize data from settings; if any exception occurs we discard everything and create a new empty dictionary for positions.

Now I add a property and some logic in the Dispose to save position of current View.

   1: public override void Dispose(bool isDisposing)

   2: {

   3:     Positions[GetType().Name] = Position;

   4:     

   5:     base.Dispose(isDisposing);

   6: }

   7:  

   8: /// <summary>

   9: /// 

  10: /// </summary>

  11: /// <value></value>

  12: public WindowsPosition Position

  13: {

  14:     get { return _position; }

  15:     set { this.Set(p => p.Position, value, ref _position); }

  16: }

  17:  

  18: private WindowsPosition _position;

Really really simple, now at application exit I serialize the static Positions property of BaseViewModel into the corresponding application user settings and the game is done. To use this feature, we should simply bind windows position to the corresponding properties.

   1: <Window

   2:  

   3: ...

   4:  

   5: Left="{Binding Position.Left, Mode=TwoWay}"

   6: Top="{Binding Position.Top, Mode=TwoWay}"

   7: Width="{Binding Position.Width, Mode=TwoWay}"

   8: Height="{Binding Position.Height, Mode=TwoWay}">

And the view gets repositioned in the same position he was when the application closed itself.

alk.

Tags: ,