Creating A Scrollable Control Surface In WPF

Have you ever had a requirement that called for the user to be able to scroll around a large object, such as a diagram. Well I have, and I have just started working on a hobby project where I need just such a feature. We probably all know that WPF has a ScrollViewer control which allows the users to scroll using the scrollbars, which is fine, but it just looks ugly. What I want is for the user to not really ever realise that there is a scroll area, I want them to just use the mouse to pan around the large area.

To this end I set about looking around, and I have pieced together a little demo project to illustrate this. Its not very elaborate, but it does the job well.

In the end you still use the native WPF ScrollViewer but you hide its ScrollBars, and just respond to mouse events. I have now responded to people requests to add some friction (well my old team leader did it, as its his area) so we have 2 versions, the XAML is the same for both

 

Lets see some code shall we.

   1:  <Window x:Class="ScrollableArea.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Title="Window1" Height="300" Width="300">
   5:      <Window.Resources>
   6:   
   7:          <!-- scroll viewer Style -->
   8:          <Style x:Key="ScrollViewerStyle" 
   9:                      TargetType="{x:Type ScrollViewer}">
  10:              <Setter Property="HorizontalScrollBarVisibility" 
  11:                      Value="Hidden" />
  12:              <Setter Property="VerticalScrollBarVisibility" 
  13:                      Value="Hidden" />
  14:          </Style>
  15:   
  16:      </Window.Resources>
  17:   
  18:      <ScrollViewer x:Name="ScrollViewer" 
  19:                    Style="{StaticResource ScrollViewerStyle}">
  20:          <ItemsControl x:Name="itemsControl" 
  21:                    VerticalAlignment="Center"/>
  22:      </ScrollViewer>
  23:   
  24:  </Window>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

It can be seen that there is a single ScrollViewer which contains an ItemsControl, but the ItemsControl could be replaced with a Diagram control or something else, you choose. The only important part here is that the ScrollViewer has its HorizontalScrollBarVisibility/VerticalScrollBarVisibility set to be Hidden, so that they are not visible to the user.

 

FRICTIONLESS VERSION

Next we need to respond to the Mouse events. This is done as follows:

   1:  protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
   2:  {
   3:      if (ScrollViewer.IsMouseOver)
   4:      {
   5:          // Save starting point, used later when determining 
   6:          //how much to scroll.
   7:          scrollStartPoint = e.GetPosition(this);
   8:          scrollStartOffset.X = ScrollViewer.HorizontalOffset;
   9:          scrollStartOffset.Y = ScrollViewer.VerticalOffset;
  10:   
  11:          // Update the cursor if can scroll or not.
  12:          this.Cursor = (ScrollViewer.ExtentWidth > 
  13:              ScrollViewer.ViewportWidth) ||
  14:              (ScrollViewer.ExtentHeight > 
  15:              ScrollViewer.ViewportHeight) ?
  16:              Cursors.ScrollAll : Cursors.Arrow;
  17:   
  18:          this.CaptureMouse();
  19:      }
  20:   
  21:      base.OnPreviewMouseDown(e);
  22:  }
  23:   
  24:   
  25:  protected override void OnPreviewMouseMove(MouseEventArgs e)
  26:  {
  27:      if (this.IsMouseCaptured)
  28:      {
  29:          // Get the new scroll position.
  30:          Point point = e.GetPosition(this);
  31:   
  32:          // Determine the new amount to scroll.
  33:          Point delta = new Point(
  34:              (point.X > this.scrollStartPoint.X) ?
  35:                  -(point.X - this.scrollStartPoint.X) :
  36:                  (this.scrollStartPoint.X - point.X),
  37:   
  38:              (point.Y > this.scrollStartPoint.Y) ?
  39:                  -(point.Y - this.scrollStartPoint.Y) :
  40:                  (this.scrollStartPoint.Y - point.Y));
  41:   
  42:          // Scroll to the new position.
  43:          ScrollViewer.ScrollToHorizontalOffset(
  44:              this.scrollStartOffset.X + delta.X);
  45:          ScrollViewer.ScrollToVerticalOffset(
  46:              this.scrollStartOffset.Y + delta.Y);
  47:      }
  48:   
  49:      base.OnPreviewMouseMove(e);
  50:  }
  51:   
  52:   
  53:   
  54:  protected override void OnPreviewMouseUp(
  55:      MouseButtonEventArgs e)
  56:  {
  57:      if (this.IsMouseCaptured)
  58:      {
  59:          this.Cursor = Cursors.Arrow;
  60:          this.ReleaseMouseCapture();
  61:      }
  62:   
  63:      base.OnPreviewMouseUp(e);
  64:  }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

FRICTION VERSION

 

Use the Friction property to set a value between 0 and 1, 0 being no friction 1 is full friction meaning the panel won’t "auto-scroll".

 

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Windows;
   6:  using System.Windows.Controls;
   7:  using System.Windows.Data;
   8:  using System.Windows.Documents;
   9:  using System.Windows.Input;
  10:  using System.Windows.Media;
  11:  using System.Windows.Media.Imaging;
  12:  using System.Windows.Navigation;
  13:  using System.Windows.Shapes;
  14:  using System.Windows.Threading;
  15:  using System.Diagnostics;
  16:   
  17:  namespace ScrollableArea
  18:  {
  19:      /// <summary>
  20:      /// Demonstrates how to make a scrollable (via the mouse) area that
  21:      /// would be useful for storing a large object, such as diagram or
  22:      /// something like that
  23:      /// </summary>
  24:      public partial class Window1 : Window
  25:      {
  26:          #region Data
  27:          // Used when manually scrolling.
  28:          private Point scrollTarget;
  29:          private Point scrollStartPoint;
  30:          private Point scrollStartOffset;
  31:          private Point previousPoint;
  32:          private Vector velocity;
  33:          private double friction; 
  34:          private DispatcherTimer animationTimer = new DispatcherTimer();
  35:          #endregion
  36:   
  37:          #region Ctor
  38:   
  39:          public Window1()
  40:          {
  41:              InitializeComponent();
  42:              this.LoadStuff();
  43:   
  44:              friction = 0.95;
  45:      
  46:              animationTimer.Interval = new TimeSpan(0, 0, 0, 0, 20);
  47:              animationTimer.Tick += new EventHandler(HandleWorldTimerTick);
  48:              animationTimer.Start();
  49:          }
  50:          #endregion
  51:   
  52:          #region Load DUMMY Items
  53:          void LoadStuff()
  54:          {
  55:              //this could be any large object, imagine a diagram...
  56:              //though for this example im just using loads
  57:              //of Rectangles
  58:              itemsControl.Items.Add(CreateStackPanel(Brushes.Salmon));
  59:              itemsControl.Items.Add(CreateStackPanel(Brushes.Goldenrod));
  60:              itemsControl.Items.Add(CreateStackPanel(Brushes.Green));
  61:              itemsControl.Items.Add(CreateStackPanel(Brushes.Yellow));
  62:              itemsControl.Items.Add(CreateStackPanel(Brushes.Purple));
  63:              itemsControl.Items.Add(CreateStackPanel(Brushes.SeaShell));
  64:              itemsControl.Items.Add(CreateStackPanel(Brushes.SlateBlue));
  65:              itemsControl.Items.Add(CreateStackPanel(Brushes.Tomato));
  66:              itemsControl.Items.Add(CreateStackPanel(Brushes.Violet));
  67:              itemsControl.Items.Add(CreateStackPanel(Brushes.Plum));
  68:              itemsControl.Items.Add(CreateStackPanel(Brushes.PapayaWhip));
  69:              itemsControl.Items.Add(CreateStackPanel(Brushes.Pink));
  70:              itemsControl.Items.Add(CreateStackPanel(Brushes.Snow));
  71:              itemsControl.Items.Add(CreateStackPanel(Brushes.YellowGreen));
  72:              itemsControl.Items.Add(CreateStackPanel(Brushes.Tan));
  73:   
  74:          }
  75:   
  76:          private StackPanel CreateStackPanel(SolidColorBrush color)
  77:          {
  78:   
  79:              StackPanel sp = new StackPanel();
  80:              sp.Orientation = Orientation.Horizontal;
  81:   
  82:              for (int i = 0; i < 50; i++)
  83:              {
  84:                  Rectangle rect = new Rectangle();
  85:                  rect.Width = 100;
  86:                  rect.Height = 100;
  87:                  rect.Margin = new Thickness(5);
  88:                  rect.Fill = i % 2 == 0 ? Brushes.Black : color;
  89:                  sp.Children.Add(rect);
  90:              }
  91:              return sp;
  92:          }
  93:          #endregion
  94:   
  95:          #region Friction Stuff
  96:          private void HandleWorldTimerTick(object sender, EventArgs e)
  97:          {
  98:              if (IsMouseCaptured)
  99:              {
 100:                  Point currentPoint = Mouse.GetPosition(this);
 101:                  velocity = previousPoint - currentPoint;
 102:                  previousPoint = currentPoint;
 103:              }
 104:              else
 105:              {
 106:                  if (velocity.Length > 1)
 107:                  {
 108:                      ScrollViewer.ScrollToHorizontalOffset(scrollTarget.X);
 109:                      ScrollViewer.ScrollToVerticalOffset(scrollTarget.Y);
 110:                      scrollTarget.X += velocity.X;
 111:                      scrollTarget.Y += velocity.Y;
 112:                      velocity *= friction;
 113:                  }
 114:              }
 115:          }
 116:   
 117:          public double Friction
 118:          {
 119:              get { return 1.0 - friction; }
 120:              set { friction = Math.Min(Math.Max(1.0 - value, 0), 1.0); }
 121:          }
 122:          #endregion
 123:   
 124:          #region Mouse Events
 125:          protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
 126:          {
 127:              if (ScrollViewer.IsMouseOver)
 128:              {
 129:                  // Save starting point, used later when determining how much to scroll.
 130:                  scrollStartPoint = e.GetPosition(this);
 131:                  scrollStartOffset.X = ScrollViewer.HorizontalOffset;
 132:                  scrollStartOffset.Y = ScrollViewer.VerticalOffset;
 133:   
 134:                  // Update the cursor if can scroll or not.
 135:                  this.Cursor = (ScrollViewer.ExtentWidth > ScrollViewer.ViewportWidth) ||
 136:                      (ScrollViewer.ExtentHeight > ScrollViewer.ViewportHeight) ?
 137:                      Cursors.ScrollAll : Cursors.Arrow;
 138:   
 139:                  this.CaptureMouse();
 140:              }
 141:   
 142:              base.OnPreviewMouseDown(e);
 143:          }
 144:   
 145:          
 146:          protected override void OnPreviewMouseMove(MouseEventArgs e)
 147:          {
 148:              if (this.IsMouseCaptured)
 149:              {
 150:                  Point currentPoint = e.GetPosition(this);
 151:   
 152:                  // Determine the new amount to scroll.
 153:                  Point delta = new Point(scrollStartPoint.X - 
 154:                      currentPoint.X, scrollStartPoint.Y - currentPoint.Y);
 155:   
 156:                  scrollTarget.X = scrollStartOffset.X + delta.X;
 157:                  scrollTarget.Y = scrollStartOffset.Y + delta.Y;
 158:   
 159:                  // Scroll to the new position.
 160:                  ScrollViewer.ScrollToHorizontalOffset(scrollTarget.X);
 161:                  ScrollViewer.ScrollToVerticalOffset(scrollTarget.Y);
 162:              }
 163:   
 164:              base.OnPreviewMouseMove(e);
 165:          }
 166:   
 167:          protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
 168:          {
 169:              if (this.IsMouseCaptured)
 170:              {
 171:                  this.Cursor = Cursors.Arrow;
 172:                  this.ReleaseMouseCapture();
 173:              }
 174:   
 175:              base.OnPreviewMouseUp(e);
 176:          }
 177:          #endregion
 178:   
 179:   
 180:   
 181:      }
 182:  }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And that’s it, we now have a nice scrollable design surface.  Here is a screen shot of the demo app, where the user can happily scroll around using the mouse (mouse button must be down)

image

And here is a link to the demo app (Frictionless) scrollablearea.zip

And here is a link to the demo app (Friction) scrollablearea_friction.zip

Advertisements

41 thoughts on “Creating A Scrollable Control Surface In WPF

  1. Rudi Grobler says:

    Hi Sacha,

    nice article… something I needed for a project!

    Rudi

    PS. This is the stuff attached properties are made for 🙂

  2. sacha barber says:

    Thanks Rudi. Yeah I think this is handy for sure

  3. Marlon Grech says:

    Great stuff!!! It would be cool if you add just a little bit of physics…. Yet still this is simple brilliant dude !!!

    Thanks for sharing !

  4. sacha says:

    Thanks Marlon. Friction is now there, have a play.

  5. Josh Smith says:

    Very cool work. This would make an excellent starting point for a lot of projects. 🙂

  6. sacha barber says:

    Thanks Josh…you should see where these 2 came from, its way cool. Some of my best yet, its all just working to plan. Excellent. Cant wait to share it.

  7. John Henkel says:

    Oh man. You just cut at least 3 days off my project debugging time. Very nice work!

  8. sacha says:

    Thanks John. I wrapped this into a scrollable ScrollViewer in my codeproject article http://www.codeproject.com/KB/WPF/SortingLists.aspx in case you are interested.

    I like the friction touch its cool.

    Thanks for liking it.

  9. Dave says:

    Very cool. The animation on the friction scroll viewer was a little less performant than I wanted, so I reimplemented it as a spline animation and allowed for specifying the animation’s duration instead of the friction. Now the animation is smoother and it leverages WPF animation goodness. I still use the timer to calculate velocity though – that still seemed the best way.

  10. sacha says:

    Dave

    Sounds great

    Care to send me the code and Ill ammend this blog and credit you where I put a link to your code.

  11. fcchan says:

    i wish i am as good programmer as you are. cool code. thanks man

  12. sacha says:

    Cheers man

  13. fcchan says:

    oh no overrided the mouse down. my do drags now have to interwine with your FrictionScrollViewer.cs

  14. sacha says:

    OK so fo not override them, you could put all this in form code if you prefer.

  15. fcchan says:

    My drag drop are from another genius’ who enable drag and drop operations on UIElement passed in.

    The art now is to amalgam 2 code:
    friction scroll viewer and drag drop helper.

    I modified drag drop helper, when a textblock is dragged and if her parent is a border, both drags. Cute shapes with labels that gets dragged to another panel! Cute!!

    The scrollviewer somehow interefered with MouseEventArgs original source. Having trouble understanding why….

    Have u ever played Dungeons and Dragons 3rd edition? I am like the Rogue, who have a chance of casting a spell in an Archmage scroll but never truly understand the content

  16. fcchan says:

    This Rogue programmer found a workaround in FrictionScrollViewer.cs –>protected override void OnMouseDown

    if (e.OriginalSource is StackPanel)
    {
    CaptureMouse();
    }

    Sorry Sacha, I turn your code into something so not elegant! But if it’s any consolation, I scroll on StackPanel surface now it scrolls with friction. If I push the mouse on those little ‘shapes’, it drags.
    Plus drag drop helper class no need to change!

    Here my XAML code is like this. Just wanna show you and readers here how my little shapes looks like. But with CornerRadius and stuff the possibilities for cute is endless.

  17. fcchan says:

    sorry had to change to little symbols (can’t type here) to _ or here it won’t accept.

    _diagram:FrictionScrollViewer x:Name=”frictionScroller1″ Height=”250″ CanContentScroll=”False” Friction=”0.85″ VerticalScrollBarVisibility=”Hidden”_

    _StackPanel x:Name=”canvas1″ Background=”AliceBlue” Opacity=”1″ Grid.Row=”0″ Grid.RowSpan=”1″ Grid.ColumnSpan=”1″ CanHorizontallyScroll=”True” CanVerticallyScroll=”False”_
    _Border HorizontalAlignment=”Center” Width=”100″ BorderBrush=”#008″ BorderThickness=”2″ CornerRadius=”20″ Padding=”30″ Margin=”1″_
    _Border.Background_
    _RadialGradientBrush RadiusX=”1″ RadiusY=”1″ GradientOrigin=”0.7,0.3″_
    _GradientStop Color=”White” Offset=”0″ /_
    _GradientStop Color=”Black” Offset=”1″ /_
    _/RadialGradientBrush_
    _/Border.Background_
    _TextBlock FontSize=”25″ FontFamily=”Pericles”__/TextBlock_
    _/Border_
    _/StackPanel_

    _/diagram:FrictionScrollViewer_

  18. fcchan says:

    Forgot to say I got the FrictionScrollViewer.cs here:

    A Spider type control tree thingy for WPF
    By Sacha Barber, Fredrik Bornander
    http://www.codeproject.com/KB/WPF/SpiderControl.aspx

  19. sacha says:

    fcchan

    Its all cool, if you modify the code and it works for you its all cool.

  20. fcchan says:

    hmmmm… u look a bit like Jack Sheppard of Lost

  21. […] a post about creating a friction enabled scrolling canvas in WPF (the old post can be found at http://sachabarber.net/?p=225), which I thought was way cool. It turns out that I was not the only one that thought this, and one […]

  22. André Knuth says:

    very nice idea, going to port this to silverlight…
    thanks!

  23. sacha says:

    Andre

    Here is a Silverlight version (Sl3 though)

    http://sachabarber.net/?p=481

  24. André Knuth says:

    Thanks, mate. That will do!

  25. Mark Pearl says:

    Thanks so much. This really helped me with ym WPF project.

  26. sacha says:

    You are welcome

  27. sandra742 says:

    Hi! I was surfing and found your blog post… nice! I love your blog. 🙂 Cheers! Sandra. R.

  28. sandra407 says:

    Hi! I was surfing and found your blog post… nice! I love your blog. 🙂 Cheers! Sandra. R.

  29. sacha says:

    Thanks Sandra

  30. Ambipsbob says:

    where did you read this?

  31. Brian says:

    Hi,

    I’ve currently encountered laggy when i tried to change the code in “scrollablearea.zip”

    for (int i = 0; i < 5000; i++)
    {
    Rectangle rect = new Rectangle();
    rect.Width = 3;
    rect.Height = 3;
    rect.Margin = new Thickness(1);
    rect.Fill = i % 2 == 0 ? Brushes.Black : color;
    sp.Children.Add(rect);
    }

    Do you have any idea why is it has a poor performance if I try to change the size of the rectangle ?

    • sacha says:

      No idea about that actually. Obviously the rectangles were just simulation, I would never create a UI that had 5000 rectangles on it.

    • sacha says:

      cant recall but the problem could be no virtualization, if it is using a standard StackPanel in the XAML try and swap it for a VirtualizingStackPanel and try that.

  32. Brian says:

    oh ~
    I need to have around 5k ~ 10k rectangle…
    is there any better way you can suggest me to improve its performance ?
    i’ve ask in the msdn>wpf forum
    some suggest me to draw it in Viewport3D
    is there any other way that i can do it ?
    your reply would be greatly appreciated.. sorry i have to post in ur comment here..

  33. Arun says:

    Hi,

    Good article.

    Mouse scrolling works well. Any idea for creating apps for touch scrolling using touch devices.

    pl suggest me regarding this.

  34. vbPyrate says:

    Great control, thanks.
    One issue though:

    I have populated the ItemsControl with items that have buttons in them.
    The buttons are bound to a command in my ViewModel.

    When I click on a button the command does not fire. I believe it’s because the OnPreviewMouseDown even has been overridden.
    If I comment out that event then the command fires, also if I hit tab until the button has focus and then hit enter; the command fires.

    Any thoughts or suggestions will be appreciated
    Thanks,
    vbPyrate.

  35. vbPyrate says:

    RE: My previous comment…

    I created a private property ‘MouseCaptured’ and used that instead of actually capturing the mouse.

    The buttons commands fire as expected now.

    • sacha says:

      Ah cool, This is something I fixed but people really like this scrollable thing, and it got turned into attached props, content control, SL version etc etc and I just lost track of what I fixed where. Good you found answer. Great stuff.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: