Archive

Posts Tagged ‘MoXAMLPowerToys’

Adding to MoXAML.

September 13, 2011 1 comment

In my last post, I talked about the enhancements and new architecture I have put into place for the new version of MoXAML Power Toys. I also mentioned that I’d talk about adding a new command. Well, in this post I’m going to cover how I coded the Scrubber command that’s available in the new version.

One of the first things I did was break the Scrubber options from the actual Scrubber command. By doing this, the user no longer needs to set the options every time they need to run Scrubber. So, I created a traditional model for the options which could be used by the Scrubber commands and the Scrubber options dialog. This model looks like this:

using System.ComponentModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;

namespace MoXAML.Scrubber.Model
{
    public class ScrubberOptionsModel : INotifyPropertyChanged
    {
        private const string FILENAME = "MoXAMLSettings.dat";

        private int _attributeCountTolerance = 3;
        private bool _reorderAttributes = true;
        private bool _reducePrecision = true;
        private int _precision = 3;
        private bool _removeCommonDefaults = true;
        private bool _forceLineMinimum = true;
        private int _spaceCount = 2;
        private bool _convertTabsToSpaces = true;

        public ScrubberOptionsModel()
        {
            LoadModel();
        }

        public bool ConvertTabsToSpaces
        {
            get
            {
                return _convertTabsToSpaces;
            }
            set
            {
                if (_convertTabsToSpaces == value) return;
                _convertTabsToSpaces = value;
                OnChanged("ConvertTabsToSpaces");
            }
        }

        public int SpaceCount
        {
            get
            {
                return _spaceCount;
            }
            set
            {
                if (_spaceCount == value) return;
                _spaceCount = value;
                OnChanged("SpaceCount");
            }
        }

        public bool ForceLineMinimum
        {
            get
            {
                return _forceLineMinimum;
            }
            set
            {
                if (_forceLineMinimum == value) return;
                _forceLineMinimum = value;
                OnChanged("ForceLineMinimum");
            }
        }

        public bool RemoveCommonDefaults
        {
            get
            {
                return _removeCommonDefaults;
            }
            set
            {
                if (_removeCommonDefaults == value) return;
                _removeCommonDefaults = value;
                OnChanged("RemoveCommonDefaults");
            }
        }

        public int Precision
        {
            get
            {
                return _precision;
            }
            set
            {
                if (_precision == value) return;
                _precision = value;
                OnChanged("Precision");
            }
        }

        public bool ReducePrecision
        {
            get
            {
                return _reducePrecision;
            }
            set
            {
                if (_reducePrecision == value) return;
                _reducePrecision = value;
                OnChanged("ReducePrecision");
            }
        }

        public bool ReorderAttributes
        {
            get
            {
                return _reorderAttributes;
            }
            set
            {
                if (_reorderAttributes == value) return;
                _reorderAttributes = value;
                OnChanged("ReorderAttributes");
            }
        }

        public int AttributeCountTolerance
        {
            get { return _attributeCountTolerance; }
            set
            {
                if (_attributeCountTolerance == value) return;
                _attributeCountTolerance = value;
                OnChanged("AttributeCountTolerance");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler == null) return;
            handler(this, new PropertyChangedEventArgs(propertyName));
        }

        private void LoadModel()
        {
            using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForAssembly())
            {
                if (file.GetFileNames(FILENAME).Count() == 0) return;

                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(FILENAME, System.IO.FileMode.Open, file))
                {
                    using (StreamReader sr = new StreamReader(stream))
                    {
                        _attributeCountTolerance = ConvertInt(sr.ReadLine());
                        _reorderAttributes = ConvertBool(sr.ReadLine());
                        _reducePrecision = ConvertBool(sr.ReadLine());
                        _precision = ConvertInt(sr.ReadLine());
                        _removeCommonDefaults = ConvertBool(sr.ReadLine());
                        _forceLineMinimum = ConvertBool(sr.ReadLine());
                        _spaceCount = ConvertInt(sr.ReadLine());
                        _convertTabsToSpaces = ConvertBool(sr.ReadLine());
                    }
                }
            }
        }

        public void SaveModel()
        {
            using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForAssembly())
            {
                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(FILENAME, System.IO.FileMode.Create, file))
                {
                    using (StreamWriter sr = new StreamWriter(stream))
                    {
                        sr.WriteLine(_attributeCountTolerance);
                        sr.WriteLine(_reorderAttributes);
                        sr.WriteLine(_reducePrecision);
                        sr.WriteLine(_precision);
                        sr.WriteLine(_removeCommonDefaults);
                        sr.WriteLine(_forceLineMinimum);
                        sr.WriteLine(_spaceCount);
                        sr.WriteLine(_convertTabsToSpaces);
                    }
                }
            }
        }

        private int ConvertInt(string value)
        {
            int retVal = 0;
            if (int.TryParse(value, out retVal))
            {
                return retVal;
            }
            return 0;
        }

        private bool ConvertBool(string value)
        {
            bool retVal = false;
            if (bool.TryParse(value, out retVal))
            {
                return retVal;
            }
            return false;
        }
    }
}

As you can see, there’s nothing remarkable about it. It’s a straightforward model implementation, and there’s nothing special about using this inside MoXAML (the point I’m making here is that you can mix and match your MoXAML implementation with standard .NET code).

OK Pete, let’s have a look at actually hooking this into MoXAML. Well, as I mentioned in the MoXAML page, I wanted to have two versions of Scrubber, one for the current file and one for the project files. In order to do this, it made sense to pull the core Scrubber functionality into a base class which the actual commands would hook into:

using MoXAML.Infrastructure;
using MoXAML.Scrubber.Model;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using System;
namespace MoXAML.Scrubber
{
    public partial class ScrubberCommandBase : CommandBase
    {
        private ScrubberOptionsModel _model;
        public ScrubberCommandBase() : base()
        {
            _model = new ScrubberOptionsModel();
        }

        protected void ParseFile(string file)
        {
            string text = File.ReadAllText(file);
            text = Perform(text);
            File.WriteAllText(file, text);
        }

        private string Perform(string text)
        {
            text = Indent(text);
            return ReducePrecision(text);
        }

        private string IndentString
        {
            get
            {
                if (_model.ConvertTabsToSpaces)
                {
                    string spaces = string.Empty;
                    spaces = spaces.PadRight(_model.SpaceCount, ' ');

                    return spaces;
                }
                else
                {
                    return "\t";
                }
            }
        }

        private string ReducePrecision(string s)
        {
            string old = s;

            if (_model.ReducePrecision)
            {
                int begin = 0;
                int end = 0;

                while (true)
                {
                    begin = old.IndexOf('.', begin);
                    if (begin == -1) break;

                    // get past the period
                    begin++;

                    for (int i = 0; i < _model.Precision; i++)
                    {
                        if (old[begin] >= '0' && old[begin] <= '9') begin++;
                    }

                    end = begin;

                    while (end < old.Length && old[end] >= '0' && old[end] <= '9') end++;

                    old = old.Substring(0, begin) + old.Substring(end, old.Length - end);

                    begin++;
                }
            }

            return old;
        }

        public string Indent(string s)
        {
            string result;

            s = s.Replace("&", "¬¬");
            using (MemoryStream ms = new MemoryStream(s.Length))
            {
                using (StreamWriter sw = new StreamWriter(ms))
                {
                    sw.Write(s);
                    sw.Flush();

                    ms.Seek(0, SeekOrigin.Begin);

                    using (StreamReader reader = new StreamReader(ms))
                    {
                        XmlReaderSettings settings = new XmlReaderSettings();
                        settings.CheckCharacters = false;
                        settings.ConformanceLevel = ConformanceLevel.Auto;
                        //XmlReader xmlReader = XmlReader.Create(reader.BaseStream, settings);
                        XmlTextReader xmlReader = new XmlTextReader(reader.BaseStream);
                        xmlReader.Normalization = false;
                        xmlReader.Read();
                        xmlReader.Normalization = false;

                        string str = "";

                        while (!xmlReader.EOF)
                        {
                            string xml;
                            int num;
                            int num6;
                            int num7;
                            int num8;

                            switch (xmlReader.NodeType)
                            {
                                case XmlNodeType.Element:
                                    xml = "";
                                    num = 0;
                                    goto Element;

                                case XmlNodeType.Text:
                                    {
                                        string str4 = xmlReader.Value.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("\"", """);
                                        str = str + str4;
                                        xmlReader.Read();
                                        continue;
                                    }
                                case XmlNodeType.ProcessingInstruction:
                                    xml = "";
                                    num7 = 0;
                                    goto ProcessingInstruction;

                                case XmlNodeType.Comment:
                                    xml = "";
                                    num8 = 0;
                                    goto Comment;

                                case XmlNodeType.Whitespace:
                                    {
                                        xmlReader.Read();
                                        continue;
                                    }
                                case XmlNodeType.EndElement:
                                    xml = "";
                                    num6 = 0;
                                    goto EndElement;

                                default:
                                    goto Other;
                            }

                        Label_00C0:
                            xml = xml + IndentString;
                            num++;

                        Element:
                            if (num < xmlReader.Depth)
                            {
                                goto Label_00C0;
                            }

                            string elementName = xmlReader.Name;

                            string str5 = str;
                            str = str5 + "\r\n" + xml + "<" + xmlReader.Name;
                            bool isEmptyElement = xmlReader.IsEmptyElement;

                            if (xmlReader.HasAttributes)
                            {
                                // construct an array of the attributes that we reorder later on
                                List<AttributeValuePair> attributes = new List<AttributeValuePair>(xmlReader.AttributeCount);

                                for (int k = 0; k < xmlReader.AttributeCount; k++)
                                {
                                    xmlReader.MoveToAttribute(k);

                                    string value = xmlReader.Value;

                                    if (_model.RemoveCommonDefaults)
                                    {
                                        if (!AttributeValuePair.IsCommonDefault(elementName, xmlReader.Name, value))
                                        {
                                            attributes.Add(new AttributeValuePair(elementName, xmlReader.Name, value));
                                        }
                                    }
                                    else
                                    {
                                        attributes.Add(new AttributeValuePair(elementName, xmlReader.Name, value));
                                    }
                                }

                                if (_model.ReorderAttributes)
                                {
                                    attributes.Sort();
                                }

                                xml = "";
                                string str3 = "";
                                int depth = xmlReader.Depth;

                                //str3 = str3 + IndentString;

                                for (int j = 0; j < depth; j++)
                                {
                                    xml = xml + IndentString;
                                }

                                foreach (AttributeValuePair a in attributes)
                                {
                                    string str7 = str;

                                    if (attributes.Count > _model.AttributeCountTolerance && !AttributeValuePair.ForceNoLineBreaks(elementName))
                                    {
                                        // break up attributes into different lines
                                        str = str7 + "\r\n" + xml + str3 + a.Name + "=\"" + a.Value + "\"";
                                    }
                                    else
                                    {
                                        // attributes on one line
                                        str = str7 + " " + a.Name + "=\"" + a.Value + "\"";
                                    }
                                }

                            }
                            if (isEmptyElement)
                            {
                                str = str + "/";
                            }
                            str = str + ">";
                            xmlReader.Read();
                            continue;
                        Label_02F4:
                            xml = xml + IndentString;
                            num6++;
                        EndElement:
                            if (num6 < xmlReader.Depth)
                            {
                                goto Label_02F4;
                            }
                            string str8 = str;
                            str = str8 + "\r\n" + xml + "</" + xmlReader.Name + ">";
                            xmlReader.Read();
                            continue;
                        Label_037A:
                            xml = xml + "    ";
                            num7++;
                        ProcessingInstruction:
                            if (num7 < xmlReader.Depth)
                            {
                                goto Label_037A;
                            }
                            string str9 = str;
                            str = str9 + "\r\n" + xml + "<?Mapping " + xmlReader.Value + " ?>";
                            xmlReader.Read();
                            continue;

                        Comment:

                            if (num8 < xmlReader.Depth)
                            {
                                xml = xml + IndentString;
                                num8++;
                            }
                            str = str + "\r\n" + xml + "<!--" + xmlReader.Value + "-->";

                            xmlReader.Read();
                            continue;

                        Other:
                            xmlReader.Read();
                        }

                        xmlReader.Close();

                        result = str;
                    }
                }
            }
            return result.Replace("¬¬", "&");

        }

        private class AttributeValuePair : IComparable
        {
            public string Name = "";
            public string Value = "";
            public AttributeType AttributeType = AttributeType.Other;

            public AttributeValuePair(string elementname, string name, string value)
            {
                Name = name;
                Value = value;

                // compute the AttributeType
                if (name.StartsWith("xmlns"))
                {
                    AttributeType = AttributeType.Namespace;

                }
                else
                {
                    switch (name)
                    {
                        case "Key":
                        case "x:Key":
                            AttributeType = AttributeType.Key;
                            break;

                        case "Name":
                        case "x:Name":
                            AttributeType = AttributeType.Name;
                            break;

                        case "x:Class":
                            AttributeType = AttributeType.Class;
                            break;

                        case "Canvas.Top":
                        case "Canvas.Left":
                        case "Canvas.Bottom":
                        case "Canvas.Right":
                        case "Grid.Row":
                        case "Grid.RowSpan":
                        case "Grid.Column":
                        case "Grid.ColumnSpan":
                            AttributeType = AttributeType.AttachedLayout;
                            break;

                        case "Width":
                        case "Height":
                        case "MaxWidth":
                        case "MinWidth":
                        case "MinHeight":
                        case "MaxHeight":
                            AttributeType = AttributeType.CoreLayout;
                            break;

                        case "Margin":
                        case "VerticalAlignment":
                        case "HorizontalAlignment":
                        case "Panel.ZIndex":
                            AttributeType = AttributeType.StandardLayout;
                            break;

                        case "mc:Ignorable":
                        case "d:IsDataSource":
                        case "d:LayoutOverrides":
                        case "d:IsStaticText":

                            AttributeType = AttributeType.BlendGoo;
                            break;

                        default:
                            AttributeType = AttributeType.Other;
                            break;
                    }
                }
            }

            #region IComparable Members

            public int CompareTo(object obj)
            {
                AttributeValuePair other = obj as AttributeValuePair;

                if (other != null)
                {
                    if (this.AttributeType == other.AttributeType)
                    {
                        // some common special cases where we want things to be out of the normal order

                        if (this.Name.Equals("StartPoint") && other.Name.Equals("EndPoint")) return -1;
                        if (this.Name.Equals("EndPoint") && other.Name.Equals("StartPoint")) return 1;

                        if (this.Name.Equals("Width") && other.Name.Equals("Height")) return -1;
                        if (this.Name.Equals("Height") && other.Name.Equals("Width")) return 1;

                        if (this.Name.Equals("Offset") && other.Name.Equals("Color")) return -1;
                        if (this.Name.Equals("Color") && other.Name.Equals("Offset")) return 1;

                        if (this.Name.Equals("TargetName") && other.Name.Equals("Property")) return -1;
                        if (this.Name.Equals("Property") && other.Name.Equals("TargetName")) return 1;

                        return Name.CompareTo(other.Name);
                    }
                    else
                    {
                        return this.AttributeType.CompareTo(other.AttributeType);
                    }
                }

                return 0;
            }

            public static bool IsCommonDefault(string elementname, string name, string value)
            {

                if (
                    (name == "HorizontalAlignment" && value == "Stretch") ||
                    (name == "VerticalAlignment" && value == "Stretch") ||
                    (name == "Margin" && value == "0") ||
                    (name == "Margin" && value == "0,0,0,0") ||
                    (name == "Opacity" && value == "1") ||
                    (name == "FontWeight" && value == "{x:Null}") ||
                    (name == "Background" && value == "{x:Null}") ||
                    (name == "Stroke" && value == "{x:Null}") ||
                    (name == "Fill" && value == "{x:Null}") ||
                    (name == "Visibility" && value == "Visible") ||
                    (name == "Grid.RowSpan" && value == "1") ||
                    (name == "Grid.ColumnSpan" && value == "1") ||
                    (name == "BasedOn" && value == "{x:Null}") ||
                    (elementname != "ColumnDefinition" && elementname != "RowDefinition" && name == "Width" && value == "Auto") ||
                    (elementname != "ColumnDefinition" && elementname != "RowDefinition" && name == "Height" && value == "Auto")

                    )
                {
                    return true;
                }

                return false;
            }

            public static bool ForceNoLineBreaks(string elementname)
            {
                if (
                    (elementname == "RadialGradientBrush") ||
                    (elementname == "GradientStop") ||
                    (elementname == "LinearGradientBrush") ||
                    (elementname == "ScaleTransfom") ||
                    (elementname == "SkewTransform") ||
                    (elementname == "RotateTransform") ||
                    (elementname == "TranslateTransform") ||
                    (elementname == "Trigger") ||
                    (elementname == "Setter")
                    )
                {
                    return true;
                }
                else
                {
                    return false;
                }

            }

            #endregion
        }

        // note that these are declared in priority order for easy sorting
        private enum AttributeType
        {
            Key = 10,
            Name = 20,
            Class = 30,
            Namespace = 40,
            CoreLayout = 50,
            AttachedLayout = 60,
            StandardLayout = 70,
            Other = 1000,
            BlendGoo = 2000
        }
    }
}

I appreciate that this is a long listing, but don’t worry about what’s going on inside. To hook into the MoXAML plugin architecture, only one line in there is really important:to be picked up

public partial class ScrubberCommandBase : CommandBase

By inheriting from CommandBase, MoXAML picks the command up because CommandBase implements ICommandBase. The cunning thing about MoXAML is that it uses MEF which allows you to mark interfaces so that any implementations of the interface will automatically be picked up. Well, we have most of the infrastructure in place – all we actually need to do is write the actual command. You’ll be surprised how little that actually takes:

using MoXAML.Infrastructure;

namespace MoXAML.Scrubber
{
    public class ScrubFileCommand : ScrubberCommandBase
    {
        public ScrubFileCommand()
            : base()
        {
            CommandName = "ScrubFile";
            Caption = "Scrubber";
            ParentCommandBar.Add(CommandBarType.XamlContextMenu);
        }

        public override void Execute()
        {
            base.Execute();
            ParseFile(Application.ActiveDocument.FullName);
        }
    }
}

Let’s break it down. In the constructor, we’re giving the command a unique name that MoXAML uses to track whether or not the command was previously installed. The Caption is what will actually appear in the menu, and the line ParentCommandBar.Add is used to add the command to the appropriate menu.

Finally, we actually need our command to do something, and that’s where the Execute method comes in – MoXAML uses this method to execute the command, so it’s the place to hook our command logic to. In this case, we’re hooking into the ParseFile method in the class we inherit from.

MoXAML goes MefXAML

September 6, 2011 1 comment

First of all, I must apologise that it’s taken me so long to release the new version of MoXAML. Unfortunately it’s taken a back burner to other projects that I have going on, so it’s been lower down on my priority list than I would have liked. Saying that, I have been tinkering with a new version and making changes to the internal architecture to make it easier for me to add new functionality to it. MoXAML has now got the MEF mojo underpinning it.

A feature of the original version of MoXAML that I was never happy with was the way I had to register commands so that they will appear in the menus. To be honest, the code was a complete mess and it offended my sensibilities whenever I looked at it. Well, the new version has completely changed that. I’ve added a MoXAML.Infrastructure library that provides a lot of the underpinnings of MoXAML registration; all we need to do is derive from the CommandBase class in this library and with just a couple of commands we are good to go.

First of all, we need to add a unique command name, the menu text and the parent command that we want to show this command in. This just gets added into the constructor likes so:

public MyConstructor()
{
  CommandName = "MyUniqueCommandName";
  Caption = "Menu text";
  ParentCommandBar.Add(CommandBarType.SolutionContextMenu);
}

Now, to add the code to actually do the work, just override the Execute method and add in the functionality that you want to perform.

In a future post, I’ll cover creating an add-in from scratch and talk about the API that’s now been exposed in MoXAML. To get the latest version of MoXAML, please check it out here.

Categories: MoXAML Tags: ,

MoXAML 2.4 Released

February 22, 2009 2 comments

I’ve just uploaded the newest version of MoXAML Power Toys. MoXAML now includes a version of Scrubber that runs inside Visual Studio – Scrubber was introduced to the world by Robby Ingebretsen in his wonderful Kaxaml tool. Go on – beautify all the XAML in your projects with one simple menu option.

MoXAML 2.1 Released – AKA Iona

October 15, 2008 5 comments

I’ve just finished coding up a new version of MoXAML Power Toys. This release adds a new keyword search function and tidies up the commenting functionality based on feedback.

Keyword search

Highlight a word in the source and select Lookup keyword from the menu, and voila – a Google and Live search are performed based on the keyword (using the appropriate language type as well).

Menu in action

Search window in operation

Commenting

The comment feature now places nicely with the existing comment command, so if you have an ordinary XAML comment in place, running the MoXAML commenter on it will handle it just as if the original comment had been done with MoXAML.

Another new feature in the Commenter is the ability to add an empty <!– –> comment so you can add your own comment text in.

Downloads

After downloading, you’ll need to change the extension from .doc to .zip. This is a requirement of WordPress.com

Installer – no source: moxaml21releasezip

Source code: moxamlpowertoys21zip

As always, keep your comments and requests coming in. No idea goes unconsidered.

So why should I use MoXAML.

September 26, 2008 7 comments

It’s occurred to me that you might be wondering why you would want to use MoXAML Power Toys, especially as it seems to add features that you’ve already got. In this post, I’m going to explain the rationale behind the different features, and give you a preview as to what’s coming up.

The Comment command. Well, currently you can comment your XAML code and it works OK if you do it once, but if you then attempt to comment over the block again, you get some problems. The following picture shows what the problem actually is.

It doesn’t look too good does it? The first Power Toy, the Comment command behaves slightly differently:

As you can see, multiple comments and it continues to behave. So, that’s the Comment command – I hope you like it.

The sister command of the Comment command is the Uncomment command, and it works it’s way back up through the comment tree to uncomment the commands.

The Make Notify Property converts automatic properties into full properties, implementing the INotifyPropertyChanged functionality.

So, where are we going from here? Well, one of the things I’ve missed since my MFC days is the AppWizard. I want to be able to automatically create applications with default toolbars, status bars and menus. So, MoXAML is going to allow you to add these to an application by default. Once added, your app will look like this (and yes, the status bar indicators are fully functioning, so the keys change as you press them and the date and time updates every second):

Another item that’s high on my priority list is a major refactoring so that you can add extra plugins to the code without having to touch the base code. You’ll be able to specify which window(s) you want the command to attach to, and which menu item to hook it up to, and the underlying system will do the rest. In this way, it will be easy for you to add new commands as you see fit.

As always, let me know what you’d like to see in future versions and keep your comments coming in.

More MoXAML

September 16, 2008 11 comments

As promised, MoXAMLPowerToys is being enhanced and updated. To this end, I’ve been hard at work tidying up the code and responding to the comments raised in the original post.

The original code has been heavily rejigged to make it easier for me to add new features and in the process I’ve added a new power toy; now it’s simple to convert an automatic property to use INotifyPropertyChanged. All you need to do is pick the property in the code window and select Notify Property (C# Only), and it will add the necessary plumbing.

Cunningly enough, it adds the Changed method if it’s not already present. ;->

Notify Property in action:

After the command:

Download

You can download the latest source (and a setup) here. (Please redownload this package, I’ve updated the setup project to move the application Addin file into the Addins directory, and to point it to the installation folder as a custom action).

Source moved here.

As always, keep your comments coming in and let me know what else you’d like to see – and while you’re at it, don’t forget to download the original XAMLPowerToys.

MoXAMLPowerToys

September 14, 2008 6 comments

Inspired by the truly excellent XAML Power Toys from Karl Shifflett, I’m pleased to introduce MoXAMLPowerToys, which stands for More XAML Power Toys in deference to Karl’s excellent XAMLPowerToys. MoXAMLPowerToys is a Visual Studio addin, which is designed to enhance your productivity in Visual Studio. I have many plans for this utility, but I would love to hear any areas that you think you would like to see adding.

In this initial release, you can comment and uncomment XAML code. To use this addin, simply copy the addin file to C:\Users\xxx\Documents\Visual Studio 2008\Addins\MoXAMLPowerToys – For Testing.AddIn and edit it to point the Assembly at the addin dll.

Requirements

  • Visual Studio 2008 with SP1

Menus

MoXAMLPowerToys menu.

MoXAMLPowerToys menu.

Plans

Future versions of this code will include further productivity enhancements:

  • The ability to automatically assign standard command bindings to tags
  • Convert automatic properties into INotifyPropertyChanged
The Canny Coder

Java 8 Functional Programming with Lambda Expressions

pihole.org

Adventures in theoretical computer science, with your host, chaiguy1337

Confessions of a coder

Confessions of a WPF lover

WordPress.com

WordPress.com is the best place for your personal blog or business site.

Follow

Get every new post delivered to your Inbox.

Join 39 other followers