Dotneteers.net
All for .net, .net for all!

LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI

 

In the last part I complained you about the nine hours of jet lag that “inspired” me to start with a deep dive on PowerCommands. Now I am back at Hungary (to be honest I finished Part #19 here at home). However, jet lag effect is over and I do not wake up at 4 o’clock in the morning, but I am still inspired to go on with the work I started.J

In Part #19 I analyzed the basic architecture of PowerCommands focusing on how commands are represented and executed. In that article we saw there is a CommandManagerService in the package responsible for registering commands. Each command is encapsulated into its own class inherited from the DynamicCommand class.

In this article I dive into details about PowerCommands UI. There are two different groups of UI in the package:

—  PowerCommands option pages that integrate with the Tools|Options dialog

—  User interface related to commands

In this article I show you details for both.

Using option pages

Visual Studio extensions—packages and Add-ins—can add their own option pages to Visual Studio integrated into the Tools|Options dialog. The pattern we use to add option pages is very similar to the pattern as we add a tool window, but it has some additional elements:

—  There is a user interface for the option page created as a standard WinForms user control

—  A class representing a wrapper for the user interface of the option page is responsible to embed the UI into the Options dialog.

—  Options have to be persisted between Visual Studio sessions.

—  Decoration attributes are added to the package class: just like tool windows, menus and services, option pages also have to be registered to support the on-demand package loading mechanism.

In the previous part we saw how the option pages are registered with PowerCommands:

// --- Some attributes have been omitted

[ProvideProfileAttribute(typeof(CommandsPage), "PowerCommands", "Commands", 15600,

  1912, true, DescriptionResourceID = 197)]

[ProvideOptionPageAttribute(typeof(CommandsPage), "PowerCommands", "Commands",

  15600, 1912, true)]

[ProvideProfileAttribute(typeof(GeneralPage), "PowerCommands", "General", 15600,

  4606, true, DescriptionResourceID = 2891)]

[ProvideOptionPageAttribute(typeof(GeneralPage), "PowerCommands", "General", 

  15600, 4606, true)]

public sealed class PowerCommandsPackage : Package, IVsInstalledProduct

{

  // --- Package body

}

In Part #19 I have treated attributes in details; here I just summarized the information there: ProvideOptionPage attribute declares the information required for accessing option pages. The ProvideProfile attribute declares information required for persisting information with the standard VS setting management mechanism. As a result of using ProvideProfile here the information set in option pages is persisted by Visual Studio.

Now, let’s have a look at the Options dialog after PowerCommands has been installed:

In the figure we see the PowerCommands category with two option pages named General and Commands as the selected page. Both pages have separate classes responsible to hold option values.

“General” option page

To introduce the concept of option pages implementation of the General page is a good candidate due to its simplicity. The implementation of this option page contains the GeneralControl class that is a WinForms user control and GeneralPage class that is inherited from the DialogPage class of the Microsoft.VisualStudio.Shell namespace. The UI of the page contains only two checkboxes:

The code behind this user control is simple:

using System;

using System.Windows.Forms;

 

namespace Microsoft.PowerCommands.OptionPages

{

  public partial class GeneralControl : UserControl

  {

    private GeneralPage optionPage;

 

    public GeneralPage OptionPage

    {

      get { return optionPage; }

      set { optionPage = value; }

    }

 

    public GeneralControl()

    {

      InitializeComponent();

    }

 

    private void chkFormatOnSave_CheckedChanged(object sender, EventArgs e)

    {

      OptionPage.FormatOnSave = chkFormatOnSave.Checked;

    }

 

    private void RemoveAndSortUsingsOnSave_CheckedChanged(object sender,

      EventArgs e)

    {

      OptionPage.RemoveAndSortUsingsOnSave = chkRemoveAndSortUsingsOnSave.Checked;

    }

 

    private void GeneralControl_Load(object sender, EventArgs e)

    {

      chkFormatOnSave.Checked = OptionPage.FormatOnSave;

      chkRemoveAndSortUsingsOnSave.Checked = OptionPage.RemoveAndSortUsingsOnSave;

    }

  }

}

The control can interact with the option page behind it through its OptionPage property that is assumed to set before the user control is first displayed. When it’s time to display the page, the GeneralControl_Load event updates the controls to be displayed from the information stored behind OptionPage. When the user changes the information displayed, changes are propagated back to the store—to OptionPage.

As you see, there is a clear separation between the UI of the page and the information it holds. The other key class in this pattern is the GeneralPage type:

using System;

using System.ComponentModel;

using System.Drawing;

using System.Runtime.InteropServices;

using System.Windows.Forms;

using Microsoft.VisualStudio.Shell;

 

namespace Microsoft.PowerCommands.OptionPages

{

  [ComVisible(true)]

  [ClassInterface(ClassInterfaceType.AutoDual)]

  [Guid("DF0D89F1-C9A3-47BF-B277-42E0C178F1A0")]

  public class GeneralPage : DialogPage

  {

    GeneralControl control;

    private bool formatOnSave;

    public bool FormatOnSave

    {

      get { return formatOnSave; }

      set { formatOnSave = value; }

    }

 

    private bool removeAndSortUsingsOnSave;

    public bool RemoveAndSortUsingsOnSave

    {

      get { return removeAndSortUsingsOnSave; }

      set { removeAndSortUsingsOnSave = value; }

    }

 

    [Browsable(false),

      DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

    protected override IWin32Window Window

    {

      get

      {

        control = new GeneralControl();

        control.Location = new Point(0, 0);

        control.OptionPage = this;

        return control;

      }

    }

  }

}

The DialogPage base class implements the mechanism to integrate the page’s UI into the Options dialog and also the persistence of settings on the page. The FormatOnSave and RemoveAndSortUsingsOnSave Boolean properties are the ones that store the option information. When information is about to read from the VS setting store or to write, the mechanism behind DialogPage uses properties. It reads or writes them and persists (or recalls) them through serialization.

The Window property provides the binding between the Options dialog and the page UI. It works exactly on the same way as in case of tool windows (exactly the same UI binding mechanism is behind). The method returns the window handle of the GeneralControl instance created here. Although this code works well it would be better to initialize control in the class constructor and simply returning the instance here.

Since the Window property is not to be written into the VS setting store, it is marked with the Browsable(false) and DesignerSerializationVisibility(Hidden) attributes.

“Commands” option page

The structure of the “Commands” option page uses the same pattern as the General page, so from this aspect I should not tell too much about it. However, the implementation of both the user controls representing the page and the DialogPage derived class behind it has a few great techniques that really worth to look into. Let’s start with the code of the user control:

public partial class CommandsControl : UserControl

{

  ICommandManagerService commandManagerService;

  IList<RowItem> items;

 

  private CommandsPage optionPage;

  public CommandsPage OptionPage

  {

    get { return optionPage; }

    set { optionPage = value; }

  }

 

  public CommandsControl()

  {

    InitializeComponent();

  }

 

  private void CommandsControl_Load(object sender, EventArgs e)

  {

    commandManagerService = optionPage.Site.

      GetService<SCommandManagerService, ICommandManagerService>();

 

    items = commandManagerService.GetRegisteredCommands()

            .OrderBy(command => command.GetType().Name)

            .Select(

                command =>

                new RowItem()

                {

                  Command = command.CommandID,

                  CommandText = GetDisplayName(command.GetType()),

                  Enabled = OptionPage.DisabledCommands.SingleOrDefault(

                              cmd => cmd.Guid.Equals(command.CommandID.Guid) &&

                              cmd.ID.Equals(command.CommandID.ID)) == null

                }).ToList();

 

    gridVisibility.DataSource = items;

    gridVisibility.Columns[0].Width = 200;

    gridVisibility.Columns[0].ReadOnly = true;

  }

 

  private void gridVisibility_CellValueChanged(object sender,

    DataGridViewCellEventArgs e)

  {

    if (e.ColumnIndex == 1)

    {

      RowItem item = gridVisibility.CurrentRow.DataBoundItem as RowItem;

      optionPage.DisabledCommands.Remove(item.Command);

 

      if (!item.Enabled)

      {

        optionPage.DisabledCommands.Add(item.Command);

      }

    }

  }

 

  private void gridVisibility_MouseLeave(object sender, EventArgs e)

  {

    gridVisibility.EndEdit();

  }

 

  private string GetDisplayName(Type command)

  {

    string displayName = string.Empty;

 

    DisplayNameAttribute att =

      TypeDescriptor.GetAttributes(command)

        .OfType<DisplayNameAttribute>()

        .FirstOrDefault();

 

    if (att != null)

    {

      displayName = att.DisplayName;

    }

    return displayName;

  }

}

The control inside keeps a list of RowItem instances describing the status (enabled or disabled) of the command to be displayed in a grid. The CommandsControl_Load event uses a LINQ query to combine information about the commands registered and the status of commands stored in the OptionPage instance. In PowerCommands the ICommandManagerService contract defines the operations to handle command registrations.

Note, that we request GetService operation from the site of the user control. It works, since implements the IServiceProvider interface and accesses the same chain of service providers as the package can access.

Another nice feature is the way display names of commands are collected. Each class representing commands is decorated with the DisplayName attribute like this:

  [Guid("7F95D8FB-4996-4763-AF41-A2154A831F77")]

  [DisplayName("Copy Path")]

  internal class CopyPathCommand : DynamicCommand

  { ... }

The GetDisplayName private method extracts the value of the attribute with a LINQ query.

Not only the user control but also the CommandsPage class has a few pearls to show.

[ComVisible(true)]

[ClassInterface(ClassInterfaceType.AutoDual)]

[Guid("7A9E9816-5ADD-4CBD-9C46-1901A492640D")]

public class CommandsPage : DialogPage

{

  CommandsControl control;

  private IList<CommandID> disabledCommands = new List<CommandID>();

 

  [TypeConverter(typeof(DisabledCommandsDictionaryConverter))]

  public IList<CommandID> DisabledCommands

  {

    get { return disabledCommands; }

    set { disabledCommands = value; }

  }

 

  [Browsable(false),

    DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

  protected override IWin32Window Window

  {

    get

    {

      control = new CommandsControl();

      control.Location = new Point(0, 0);

      control.OptionPage = this;

      return control;

    }

  }

}

The option page keeps track of disabled commands. When information is persisted, the DisabledCommands property uses a custom type converter (the DisabledCommandsDictionaryConverter) to manage the serialization. This converter converts the value of that property to a string and vice versa:

public class DisabledCommandsDictionaryConverter : StringConverter

{

  IList<CommandID> disabledCommands;

 

  public override bool CanConvertFrom(ITypeDescriptorContext context,

    Type sourceType)

  {

    return true;

  }

 

  public override bool CanConvertTo(ITypeDescriptorContext context,

    Type destinationType)

  {

    return true;

  }

 

  public override object ConvertTo(ITypeDescriptorContext context,

    CultureInfo culture, object value, Type destinationType)

  {

    disabledCommands = value as IList<CommandID>;

    StringBuilder builder = new StringBuilder();

    disabledCommands.ForEach(

        cmdId =>

        {

          builder.Append(string.Format("{0},{1};", cmdId.Guid, cmdId.ID));

        });

    return builder.ToString();

  }

 

  public override object ConvertFrom(ITypeDescriptorContext context,

    CultureInfo culture, object value)

  {

    Guid cmdGuid;

    int cmdId;

    disabledCommands = new List<CommandID>();

 

    try

    {

      if (!string.IsNullOrEmpty(value.ToString()))

      {

        value.ToString().Split(';').ForEach(

            item =>

            {

              string[] subItems = item.Split(',');

              if (subItems.Count() == 2)

              {

                cmdGuid = new Guid(subItems[0]);

                int.TryParse(subItems[1], out cmdId);

                disabledCommands.Add(new CommandID(cmdGuid, cmdId));

              }

            });

      }

    }

    catch

    {

    }

    return disabledCommands;

  }

}

This class handles a simple string representation of disabled commands: commands are separated by semicolons; within a command GUID and ID are divided by a comma. The implementation of ConvertFrom and ConvertTo uses LINQ extension methods and lambda expressions presenting a cool solution.

Command user interfaces

There are a few commands having their own user interfaces, like Extract to Constant or Clear Recent File List. Event the user interfaces are simple, Pablo Galiano made a clear and great implementation to follow. He used WPF forms with styles in XAML and used the MVP (Model-View-Presenter) pattern for binding commands with their UI. In this part of this paper I go into implementation details.

The Model-View-Presenter pattern

Giving even a brief overview about MVP—do not mix Model-View-Presenter with Most Valuable Professional J—is beyond the scope of this deep dive, you can find a good article on this topic by Jean-Paul Boodhoo here. The Mvp folder of PowerCommands source contains a lightweight implementation of this design pattern.

MVP pattern separates the responsibility of the UI in a clear way and provides an implementation pattern that help TDD style development. It is considered a derivative of MVC (Model-View-Controller) pattern. I tried to make a good description of the pattern, but when I read a good summary on Wikipedia I decided to cite the pattern description from the first to the last word:

The View is defined as an interface that the Presenter will use for getting and setting data to and from the Model. The View implementation will instantiate the Presenter object and provide a reference to itself (the formal constructor parameter is the View interface while the actual parameter is a concrete View class). When the event methods of the View are triggered, they will do nothing else than invoking a method of the Presenter which have no parameters and no return value. The Presenter will then get data from the View, through the View interface variable that the Presenter stored when the constructor was called. Then the Presenter invokes methods of the Model, and then it sets data from the Model into the View through the View interface.

Pablo’s implementation in PowerCommands extracts some stereotype behavior for this pattern. For example, the behavior for a modal dialog can be described as in the IModalView.cs file:

public interface IModalView

{

  void OK();

  void Cancel();

}

There is also a definition of the Presenter behavior:

// --- IPresenter.cs

using System.Collections;

 

namespace Microsoft.PowerCommands.Mvp

{

  public interface IPresenter

  {

    ICollection CommandBindings { get; }

  }

}

 

// --- Presenter.cs

using System.Collections;

using System.Collections.Generic;

using System.Windows.Input;

 

namespace Microsoft.PowerCommands.Mvp

{

  public class Presenter<TModel, IView> : IPresenter

  {

    private List<CommandBinding> bindings = new List<CommandBinding>();

 

    public Presenter(TModel model, IView view)

    {

      this.Model = model;

      this.View = view;

    }

 

    protected IView View { get; private set; }

    protected TModel Model { get; private set; }

 

    protected void AddCommandBinding(CommandBinding binding)

    {

      bindings.Add(binding);

    }

 

    ICollection IPresenter.CommandBindings

    {

      get { return bindings; }

    }

  }

}

The MVP pattern defines the responsibilities and the cooperation among the roles. This cooperation is captured by the MvpFactory class:

using System;

using System.Windows;

 

namespace Microsoft.PowerCommands.Mvp

{

  public static class MvpFactory

  {

    public static void Create<TModel, IView, TView, TPresenter>(out TModel model,

      out TView view, out TPresenter presenter)

      where TModel : new()

      where IView : class

      where TView : FrameworkElement, IView, new()

      where TPresenter : Presenter<TModel, IView>

    {

      model = new TModel();

      view = new TView();

      presenter =

        (TPresenter)Activator.CreateInstance(typeof(TPresenter), model, view);

      AddCommandBindings(view, presenter);

      view.DataContext = model;

    }

 

    private static void AddCommandBindings(FrameworkElement view,

      IPresenter presenter)

    {

      view.CommandBindings.AddRange(presenter.CommandBindings);

    }

  }

}

If you go back to the definition cited, you will recognize that MvpFactory puts together the pieces. Looking at the generic constraints, you can see that TView is declared as a FrameworkElement derived type, so it is strongly bound to WPF. If you know WPF, you may guess its architects designed it with MVP in mind, at that is not far away from the reality. Look at the elegant view.DataContext = model assignment: here we connect the view with its model in a “native” way.

Extract to Constant Command UI

To demonstrate how to use the MVP pattern the best solution is to dive into the details of the Extract to Constant command. Here I show you how the parts of this command are put together to form the whole function, but right now I will not explain how actually the constant extraction is carried out.

The command implemented in the OnExecute method of the ExtractToConstantCommand class:

private static void OnExecute(object sender, EventArgs e)

{

  // --- Some code omitted, we focus on the UI code

  // --- Initializing the UI with MVP pattern

  ExtractToConstantModel model;

  ExtractToConstantView view;

  ExtractToConstantPresenter presenter;

 

  MvpFactory.Create<ExtractToConstantModel, IModalView, ExtractToConstantView,

    ExtractToConstantPresenter>(out model, out view, out presenter);

  try

  {

    // --- Some preparation code omitted

    if ((bool)view.ShowDialog())

    {

      // --- “OK” button is pressed in the dialog

    }

  }

  catch (COMException)

  {

  }

}

The method prepares the UI by creating the cooperating elements with the Create method of the MvpFactory static class. The ExctractToConstantModel and ExtractToConstantPresenter represent the corresponding Model and Presenter roles in the MVP pattern. The view is stereotyped by IModalView and is implemented by ExtractToConstantView. The physical view is a WPF form defined in XAML:

  

The view behaves so that it allows selecting a visibility specifier from the combobox. The Identifier textbox indicates with a red border if the given identifier is invalid and disables the OK button. The code behind the view is really simple:

// --- Using clauses omitted for clarity

namespace Microsoft.PowerCommands.Commands.UI

{

  public partial class ExtractToConstantView : Window, IModalView

  {

    public ExtractToConstantView()

    {

      InitializeComponent();

    }

 

    void IModalView.OK()

    {

      this.DialogResult = true;

      this.Close();

    }

 

    void IModalView.Cancel()

    {

      this.DialogResult = false;

      this.Close();

    }

  }

}

This simplicity comes from the fact that the view itself does not deal with the model through code, but via XAML template bindings. The view uses XAML resources, styles and a few other mechanisms of WPF that is beyond the scope of this paper. The presenter binds the view with the model:

using System.Windows.Input;

using Microsoft.PowerCommands.Mvp;

 

namespace Microsoft.PowerCommands.Commands.UI

{

  public class ExtractToConstantPresenter :

    Presenter<ExtractToConstantModel, IModalView>

  {

    static public RoutedCommand DoOk =

      new RoutedCommand("DoOk", typeof(ExtractToConstantPresenter));

 

    static public RoutedCommand DoCancel =

      new RoutedCommand("DoCancel", typeof(ExtractToConstantPresenter));

 

    public ExtractToConstantPresenter(ExtractToConstantModel model,

      IModalView view) : base(model, view)

    {

      AddCommandBinding(new CommandBinding(DoOk, OnDoOk, OnCanDoOk));

      AddCommandBinding(new CommandBinding(DoCancel, OnDoCancel));

    }

 

    private void OnDoOk(object sender, ExecutedRoutedEventArgs e)

    {

      View.OK();

    }

 

    private void OnCanDoOk(object sender, CanExecuteRoutedEventArgs e)

    {

      e.CanExecute = (Model.Error == null);

    }

 

    private void OnDoCancel(object sender, ExecutedRoutedEventArgs e)

    {

      View.Cancel();

    }

  }

}

The key elements of the binding mechanism are CommandBinding and RoutedCommands types that are core parts of WPF. The bindings above simply call the OK() and Cancel() methods of the view. OK is allowed only if the model is correct (its Error property is null). Although the view and the presenter are simple, the model is more complex due to the fact that the source code to extract the constant from is actually also a part of the model for the operation. It also contains validation logic. Here is the model; I omitted concrete validation logic parts:

using System.Collections.Generic;

using System.ComponentModel;

using System.Linq;

using EnvDTE;

using System.CodeDom.Compiler;

using Microsoft.PowerCommands.Linq;

 

namespace Microsoft.PowerCommands.Commands.UI

{

  public class ExtractToConstantModel : IDataErrorInfo

  {

    public ExtractToConstantModel()

    {

      this.Visibility = new List<string>()

        { "Private", "Public", "Protected", "Internal" };

    }

 

    // --- Simple model properties

    public CodeClass CodeClass { get; set; }

    public CodeDomProvider CodeDomProvider { get; set; }

    public string Identifier { get; set; }

    public string SelectedVisibility { get; set; }

 

    private IEnumerable<string> visibility;

    public IEnumerable<string> Visibility

    {

      get { return this.visibility.OrderBy(visibility => visibility); }

      private set { visibility = value; }

    }

 

    #region IDataErrorInfo Members

 

    public string Error { get; private set; }

    public string this[string columnName]

    {

      get

      {

        // --- Code omitted, this code checks, if the identifier id valid or not.

      }

    }

 

    #endregion

  }

}

The Clear List UI

Two commands, Clear Recent File List and Clear Recent Project List use another UI to list commands or projects to delete. This UI does not follow directly the MVP pattern; it merges the View and Presenter roles into t View class that displays the following WPF dialog:

 

The View is implemented so that it directly instantiates the Model:

using System.Windows.Controls;

using System.Windows.Media;

using System.Windows.Input;

using System.Linq;

 

namespace Microsoft.PowerCommands.Commands.UI

{

  public partial class ClearListView : Window

  {

    public ClearListView()

    {

      InitializeComponent();

      this.DataContext = new ClearListModel();

      this.CommandBindings.Add(new CommandBinding(DoClearAndRestart,

        OnDoClearAndRestart, OnCanDoClearAndRestart));

      this.CommandBindings.Add(new CommandBinding(DoCancel, OnDoCancel));

    }

    public ClearListModel Model

    {

      get { return (ClearListModel)this.DataContext; }

    }

 

    public static readonly RoutedCommand DoClearAndRestart =

      new RoutedCommand("DoClearAndRestart", typeof(ClearListView));

    public static readonly RoutedCommand DoCancel =

      new RoutedCommand("DoCancel", typeof(ClearListView));

 

    private void OnDoClearAndRestart(object sender, ExecutedRoutedEventArgs e)

    {

      Model.SelectedListEntries.Clear();

      Model.SelectedListEntries.AddRange(

        lstEntries.SelectedItems.Cast<KeyValue>());

      this.DialogResult = true;

      this.Close();

    }

 

    private void OnCanDoClearAndRestart(object sender,

      CanExecuteRoutedEventArgs e)

    {

      e.CanExecute = lstEntries.SelectedItems.Count > 0;

    }

 

    private void OnDoCancel(object sender, ExecutedRoutedEventArgs e)

    {

      this.DialogResult = false;

      this.Close();

    }

 

    // --- Code for other user interactions omitted

  }

}

The Model behind the View represents all list entries and collects the selected ones:

using System.Collections.Generic;

 

namespace Microsoft.PowerCommands.Commands.UI

{

  public class ClearListModel

  {

    public ClearListModel()

    {

      ListEntries = new List<KeyValue>();

      SelectedListEntries = new List<KeyValue>();

    }

 

    public List<KeyValue> ListEntries { get; set; }

    public List<KeyValue> SelectedListEntries { get; set; }

  }

}

From this point, it is straightforward, how the OnExcecute method uses the UI for the ClearRecentFileList command:

private static void OnExecute(object sender, EventArgs e)

{

  ClearListView view = new ClearListView();

 

  using (RegistryKey key = GetRecentRootKey())

  {

    key.GetValueNames().ForEach(

      valueName => view.Model.ListEntries.Add(new KeyValue(valueName,

      key.GetValue(valueName).ToString())));

    if ((bool)view.ShowDialog())

    {

      DynamicCommand.Dte.ExecuteCommand("File.SaveAll", string.Empty);

      DeleteRecentFileList(view.Model.SelectedListEntries);

      ReEnumerateFileList();

      DTEHelper.RestartVS(DynamicCommand.Dte);

    }

  }

}

At the beginning of the method the view is created then the model is filled up with the list of recent files (read out from the registry). After the dialog is displayed the list of entries to be deleted is read from the model. Here I do not treat how the recent list is deleted, but in a future article I definitely will deal with this topic.

Where we are?

In this part we looked behind the UI elements of PowerCommands. This deep dive allowed us getting familiar with the option pages; we saw how to declare them with the DialogPage class and how to integrate them with the Options dialog. The information behind the option pages can be stored with the setting persistence mechanism of Visual Studio.

A few commands use their own UI. The UI in PowerCommands has two set of values developers can learn from: they use the Model-View-Presenter pattern and leverage on capabilities and architecture of WPF.

In the next articles we go through each command and look behind the mechanisms making them work.


Posted Apr 20 2008, 08:42 PM by inovak
Filed under:

Comments

pearl jewellery wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Mon, Feb 11 2013 8:56

Looking forward to reading more. Great post. Will read on...

phoenix roofing company wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Thu, Feb 14 2013 20:56

Wow, great article.Thanks Again. Great.

http://www.springbridge.co.uk/categories/Topsoil-/ wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Feb 16 2013 13:10

I am so grateful for your blog.Really thank you! Really Great.

buy clomid no prescription wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Mon, Feb 25 2013 9:45

JbDWRD Thanks so much for the blog.Really thank you! Great.

key words get more twitter followers wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Tue, Feb 26 2013 18:53

I am so grateful for your blog post.Thanks Again. Really Cool.

make your hair grow faster wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Tue, Feb 26 2013 20:30

Great article.Really looking forward to read more. Fantastic.

stay at home parents wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Tue, Feb 26 2013 22:14

I loved your article.Really looking forward to read more. Want more.

What is a Domain Name wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Tue, Feb 26 2013 22:35

Very good blog. Much obliged.

Security Cameras wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Tue, Feb 26 2013 23:51

I am so grateful for your blog article. Fantastic.

empregos rj wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 1:25

I loved your blog article.Much thanks again. Great.

how to get a lot of followers on instagram wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 1:37

Thanks-a-mundo for the blog.Really thank you! Really Cool.

Toronto Businesses for sale wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 3:01

Thanks so much for the blog. Will read on...

Kardashian Superstar wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 4:13

Very good article.Much thanks again. Much obliged.

the trick photography and special effects wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 4:38

Thanks for sharing, this is a fantastic article post.Really looking forward to read more. Really Great.

real instagram followers free wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 5:22

Really appreciate you sharing this article post. Will read on...

meet single woman wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 6:08

Appreciate you sharing, great blog article.Much thanks again. Keep writing.

direct mail copywriter wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 6:19

Really informative article. Really Great.

webstagram wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 7:17

Major thanks for the blog.Really thank you!

nikon d3200 price in chennai wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 7:57

wow, awesome blog.Really looking forward to read more. Keep writing.

wood working wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 8:03

Great post can make continuous improvement, thanks reveal, the actual build up associated with understanding would be to maintain understanding, interest is actually the start of prosperity.

what is gyro meat wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 9:37

Im thankful for the article post.Really looking forward to read more. Will read on...

free instagram followers wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 10:02

Enjoyed every bit of your blog post.Really looking forward to read more. Keep writing.

cheap facebook likes wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 11:16

This is one awesome post. Awesome.

Cell phone plans wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 11:57

I really like this website , and hope you will write more ,thanks a lot for your information.

instant cash wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 13:06

I really enjoy the post.Much thanks again. Really Cool.

increase chances of pregnancy during ovulation wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 15:47

Muchos Gracias for your post.Thanks Again. Really Cool.

buy clomid no prescription wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 17:31

wWvPjm Great blog post.Thanks Again. Really Great.

pianists nyc wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Wed, Feb 27 2013 21:41

I really enjoy the article.Really thank you! Cool.

fileice downloader wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Thu, Feb 28 2013 2:51

Thanks for sharing, this is a fantastic post.Much thanks again.

Locksmith in Bellevue wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Thu, Feb 28 2013 4:52

Im obliged for the blog article.Really looking forward to read more. Will read on...

speaker wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Thu, Feb 28 2013 8:54

Really enjoyed this blog.Really thank you! Will read on...

Vpn wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Thu, Feb 28 2013 19:52

Thanks for the blog post. Really Great.

ourmeds wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Thu, Feb 28 2013 21:29

Really enjoyed this article post.Thanks Again. Awesome.

thy ucak bileti|thy ucak bileti wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 11:29

Thanks for sharing, this is a fantastic post.Really thank you! Great.

GeForce GTX 690 wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 11:55

Really enjoyed this blog article.Really looking forward to read more. Great.

schweizer amateur sex wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 12:19

Wow, great blog article.Really looking forward to read more. Much obliged.

escort agency wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 13:30

Really enjoyed this blog.Thanks Again. Great.

adjustable dumbbell review wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 15:05

I cannot thank you enough for the article post. Awesome.

private label rights wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 16:30

Really enjoyed this article post.Really thank you! Fantastic.

deals and steals wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 16:41

Hey, thanks for the post.Really looking forward to read more. Really Great.

mode femme ronde wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 16:45

Say, you got a nice blog.Really thank you! Will read on...

maillot femme ronde wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 17:53

Awesome blog post.Really looking forward to read more. Awesome.

dublin pass discount wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 18:15

Thanks again for the blog article.Really looking forward to read more. Really Great.

affiliate marketing|make 100 dollars a day wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 18:16

Very informative blog.Really thank you! Awesome.

cardiology blog wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 20:01

Thank you for your blog.Thanks Again. Much obliged.

Libert&#224; finanziaria wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 20:37

Im obliged for the article post.Really looking forward to read more.

youtube wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 21:18

Hey, thanks for the blog post. Cool.

Ferienwohnung cuxhaven wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 21:44

Thank you for your blog. Cool.

halovar wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Fri, Mar 1 2013 23:27

This is one awesome blog.Thanks Again. Great.

oxyelite usplabs wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 1:10

Thank you for your blog.Much thanks again. Really Cool.

seo link wheelers wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 1:28

Muchos Gracias for your blog article.Much thanks again. Keep writing.

Japanese Adult Movies New Japanese videos avidol avidols avidolz wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 2:54

Say, you got a nice blog post.Much thanks again. Awesome.

lipo 6 black wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 2:54

I value the blog article.Really thank you! Awesome.

compound pharmacy wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 3:32

Appreciate you sharing, great blog.Thanks Again.

create a blog wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 4:38

Great, thanks for sharing this article.Really looking forward to read more. Fantastic.

kitchens wimlsow wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 5:36

I truly appreciate this blog.Really looking forward to read more. Much obliged.

cheap car insurance wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 6:22

Fantastic post.Thanks Again.

seo tools wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 7:58

Hey, you used to write excellent, but the last few posts have been kinda boring¡K I miss your great writings. Past few posts are just a bit out of track! come on! seo tools http://seotools.overblog.com/

90 Day Payday Loans wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 8:06

Appreciate you sharing, great blog.Really looking forward to read more. Fantastic.

personal injury attorney houston wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 9:08

Hey, thanks for the blog article.Really looking forward to read more. Will read on...

die cut machine wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 9:45

I really liked your blog article.Thanks Again. Great.

seo services adelaide wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 9:51

Hey, thanks for the article post.Much thanks again.

gold buyers wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 11:39

I cannot thank you enough for the blog article.Thanks Again. Cool.

Get Twitter followers wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 12:25

Thanks again for the blog post.Thanks Again. Great.

green coffee bean weight loss wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 13:25

Thank you for your blog post.Really looking forward to read more. Keep writing.

Business Domain Names wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 14:02

I loved your post.Really looking forward to read more. Much obliged.

guitar online wrote re: LearnVSXNow! #20 - PowerCommands Deep Dive — Commands and UI
on Sat, Mar 2 2013 15:40

Major thankies for the blog.Really thank you! Cool.