Blog


maart 25, 2016  Ewart Nijburg

Testing iisexpress MVC5 from iOS device

Accessing a local website running in IISExpress from another device can be a hassle. If you register the address with netsh, starting the website from Visual Studio fails with a message that it cannot register the address.

The following node.js tool from https://github.com/icflorescu/iisexpress-proxy solves the problem very nicely.

  Ewart Nijburg

Trigger Windows 7 Firewall access dialog if cancelled

Recently I accidentally cancelled a windows firewall dialog causing a node.js app to fail.
After the following statement in a command prompt, the dialog resurfaced and I could accept the change.

netsh advfirewall reset

https://support.microsoft.com/en-us/kb/947709

januari 25, 2013  Ewart Nijburg

Reference paths to the rescue

On a regular basis I have to compile code against assemblies produced by separate scrum teams. These teams share the same solution structure but add new functionality to projects as they go. Within the solutions we abandoned the use of the global assembly cache in favor of easier deployment and most assembly’s referenced lack a hint path to aid in assembly resolving.

All comes down to having to build the complete feature team solution in order to get the right assemblies. Most of the time my development machine resulted in being able to build for a specific team after quite some effort, but everything had to be redone for another team.

In search of a better solution I remembered the use of the reference paths in the project properties.

vs1

Any reference path added here ends up in a file (.user) that is local to the developer and should be ignored by versioning systems. Resolving assemblies during compilation is done by a lookup of the assembly in the first reference path, followed by the next etcetera.

Using this developer local file, I copied the TFS build output to a local directory and referenced that directory as a reference path in the projects. Switching teams meant deleting the contents of the local directory and copying the new assemblies from the build output, something I automated as well.

The reason for this article is twofold; first of all I want to share the use of this little gem called reference paths. Furthermore, there is one inconvenience with this solution if there are many projects and solutions. Adding reference paths to all of them is bit of a bore.

My solution is a small command line application that recursively searches for projects and adds the reference path to the .user file auto-magically, leaving intact any settings already in place or adding a .user file if nonexistent.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace SetLibPathInUserFile
{
    class Program
    {
        /// <summary>
        /// The namespace to which the elements belong in the .user xml file
        /// </summary>
        private static readonly XNamespace MsBuild2003 = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");

        /// <summary>
        /// The main program entry
        /// </summary>
        /// <param name="args">The args.</param>
        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                Console.WriteLine("SetLibPathInUserFile [RootPath] [ReferencePathsToAdd]");
                Console.WriteLine();
                Console.WriteLine("RootPath            : File path from which to search for .csproj and related .user files");
                Console.WriteLine("ReferencePathsToAdd : a ';' separated line with the paths to add to the refences");
                return;
            }
            var rootPathParameter = args[0].Trim('"');
            var referencesParameter = args[1].Trim('"');

            var referencesToAdd = new List<string>(referencesParameter.Split(';'));
            var count = 0;
            var processed = 0;

            foreach (var file in new DirectoryInfo(rootPathParameter).GetFiles("*.csproj", SearchOption.AllDirectories))
            {
                count++;
                var isDirty = false;
                var userfile = file.FullName + ".user";

                XElement xml;

                if (!File.Exists(userfile))
                {
                    xml = new XElement(MsBuild2003 + "Project",
                                           new XAttribute("ToolsVersion", "4.0"),
                                           CreatePropertyGroup(referencesToAdd));

                    isDirty = true;
                }
                else
                {
                    xml = XElement.Load(userfile);

                    var project = xml.Element(MsBuild2003 + "Project");
                    if (project != null)
                    {
                        foreach (var propertyGroupElement in project.Elements(MsBuild2003 + "PropertyGroup"))
                        {
                            var referenceElement = propertyGroupElement.Element(MsBuild2003 + "ReferencePath");
                            if (referenceElement != null)
                            {
                                var references = string.IsNullOrEmpty(referenceElement.Value)
                                    ? new List<string>()
                                    : new List<string>(referenceElement.Value.Split(';'));

                                var joinedReferences = references.Union(referencesToAdd).ToList();
                                if (joinedReferences.Count > references.Count)
                                {
                                    referenceElement.Value = string.Join(";", joinedReferences);
                                    isDirty = true;
                                }

                                break;
                            }
                        }

                        if (!isDirty)
                        {
                            project.Add(CreatePropertyGroup(referencesToAdd));
                            isDirty = true;
                        }
                    }
                }

                if (isDirty)
                {
                    xml.Save(userfile);

                    Console.WriteLine("saved {0}", Path.GetFileName(userfile));

                    processed++;
                }
            }

            Console.WriteLine("C# projects located: {0}", count);
            Console.WriteLine("User files modified: {0}", processed);
        }

        /// <summary>
        /// Creates a property group containing the References element
        /// </summary>
        /// <param name="referencesToAdd">The references to add to the references element</param>
        /// <returns></returns>
        private static XElement CreatePropertyGroup(IEnumerable<string> referencesToAdd)
        {
            return new XElement(MsBuild2003 + "PropertyGroup",
                                new XElement(MsBuild2003 + "ReferencePath", string.Join(";", referencesToAdd)));
        }
    }
}
Tags:
januari 24, 2013  Ewart Nijburg

Creating new child entities through AutoCompleteBox in LightSwitch

Recently I had a visit from my brother regarding a programming question in LightSwitch. He was stuck on some functionality he wanted to implement, more precisely the ability to add a new record through an AutoCompleteBox if the record was nonexistent.

After playing around with LightSwitch for a while we had to conclude that this was not available out-of-the-box and it had to be implemented manually. I told him I would solve the problem during the Christmas holidays.

Not thinking about his problem I continued working on a LightSwitch application regarding dogs and their breeders, and as you might expect I ran into the same requirement after a few screens.

In my situation I had an entity called Breeder, which is linked to an entity called Person. While adding a new Breeder record, I wanted to assign the Person as well. No problem if it exists, but end of the road if it doesn’t. I fancied a dialog box asking me to add the missing record and presenting a modal popup to fill the required fields, after which the records would be added and used.

After consulting Google, I found a link to Tim Leung’s blog

http://dotnettim.wordpress.com/2011/04/20/lightswitch-add-non-existent-records-using-autocompletebox/

That was pretty much what I needed, but after implementing the solution some issues remained. I was not very happy with the database roundtrip needed to see if the search criteria would result in a valid record, I prefer to use the enter key to submit the criteria instead of tabbing to the next control and triggering a focus change and last but not least I wanted a modal dialog to allow all required fields to be added.

Debugging the solution proposed by Tim revealed that besides the Text property, the System.Windows.Controls.AutoCompleteBox has another property called SearchText. This property always contains the search criteria entered while the Text property contains the text from the located record or is empty in case nothing is found.

That was exactly what I was looking for, so I changed my code to:

partial void BreederDetail_Created()
{
    // Write your code here.
    this.FindControl("Person").ControlAvailable += PersonControlAvailable;
}

void PersonControlAvailable(object sender, ControlAvailableEventArgs e)
{
    var control = ((System.Windows.Controls.Control)e.Control);
    control.LostFocus += PersonLostFocus;
}

void PersonLostFocus(object sender, System.Windows.RoutedEventArgs e)
{
    PopupIfNeeded(sender);
}
private void PopupIfNeeded(object sender)
{
    var autoCompleteBox = ((System.Windows.Controls.AutoCompleteBox)sender);
    var value = autoCompleteBox.SearchText;
    var hasValue = !string.IsNullOrEmpty(autoCompleteBox.Text);

    Details.Dispatcher.BeginInvoke(delegate
    {
        if (string.IsNullOrEmpty(value))
            return;

        if (!hasValue)
        {
            if (
                this.ShowMessageBox(
                    string.Format("Do you want to add the person {0}?", value),
                    "Add Person",
                    MessageBoxOption.YesNo) == System.Windows.MessageBoxResult.Yes)
            {
                var selectedPerson =
                    DataWorkspace.PedigreeData.People.AddNew();
                selectedPerson.Surname = value;
                Breeder.Person = selectedPerson;
            }
        }

    });
}

This solved the problem with the additional roundtrip to the database. Be sure not to access the AutoCompleteBox from within the Dispatcher call, this will result in a thread access error because the control was created by another thread.

The next thing to tackle was creating the record by pressing the enter key, while most of the plumbing was already in place, I simply added a KeyUp handler to intercept the Enter key.

void PersonControlAvailable(object sender, ControlAvailableEventArgs e)
{
    var control = ((System.Windows.Controls.Control)e.Control);
    control.LostFocus += PersonLostFocus;
    control.KeyUp += ControlKeyUp;
}

void ControlKeyUp(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        PopupIfNeeded(sender);
        e.Handled = true;
    }
}

One problem I now encountered was duplicate calls to PopupIfNeeded because the Enter resulted in a focus change which in turn resulted in a second call to the PopupIfNeeded method. I decided to add a Boolean to see if the popup was already triggered.

private bool _popupActive;

void PersonLostFocus(object sender, System.Windows.RoutedEventArgs e)
{
    if (!_popupActive)
        PopupIfNeeded(sender);
}

private void PopupIfNeeded(object sender)
{
    var autoCompleteBox = ((System.Windows.Controls.AutoCompleteBox)sender);
    var value = autoCompleteBox.SearchText;
    var hasValue = !string.IsNullOrEmpty(autoCompleteBox.Text);

    Details.Dispatcher.BeginInvoke(delegate
    {
        if (_popupActive)
            return;

        _popupActive = true;
        try
        {
            if (string.IsNullOrEmpty(value))
                return;

            if (!hasValue)
            {
                if (
                    this.ShowMessageBox(
                        string.Format("Do you want to add the person {0}?", value),
                        "Add Person",
                        MessageBoxOption.YesNo) == System.Windows.MessageBoxResult.Yes)
                {
                    var selectedPerson =
                        DataWorkspace.PedigreeData.People.AddNew();
                    selectedPerson.Surname = value;
                    Breeder.Person = selectedPerson;
                }
            }
        }
        finally
        {
            _popupActive = false;
        }
    });
}

Last but not least I had to devise a way to show the Person record in a modal window and allow the properties to be set.

As it turns out, you can show and hide modal windows through calls to this.OpenModalWindow and this.CloseModalWindow on the screen instance. So the next thing was how to add a modal window to my existing BreederDetails screen.

A quick Google resulted in the following page:

http://blogs.msdn.com/b/lightswitch/archive/2011/06/23/tips-and-tricks-for-using-the-screen-designer.aspx

Where the “Using a Modal Window” section is of particular interest.

Following the recipe, it gave me a nice modal window called “AddPersonDetails”

In the Command Bar I added two buttons, one to cancel the change and one to approve it. For the OK button I created a new method called “AddPersonDetailsOk”. As the process is a bit tideous, I’ll describe it.

Execute the Add Button command and change the Name in New Method to a name you can easily distinguish in the backend code.

Press OK and go to the properties on the lower right side of Visual Studio

Change the Display Name of the button into what you want the button to show.

Next, go to the left side of visual studio and locate the placeholder for the “AddPersonalDetailsOk” method you just created. Select it.

Again, in the properties on the right, click on the “Edit Execute Code” to go to the event handler for the method.

 

Add the call to CloseModalWindow to close the modal windowl, the entity is already updated and a save of the screen will persist it to the database.

partial void AddPersonDetailsOk_Execute()
{
    // Write your code here.
    this.CloseModalWindow("AddPersonDetails");
}

I repeated the steps for a second button with the name “AddPersonDetailsCancel” and edited the execute code to match the following:

partial void AddPersonDetailsCancel_Execute()
{
    // Write your code here.
    this.CloseModalWindow("AddPersonDetails");
    // Get the newly created instance
    var person = Breeder.Person;
    // Delete the refetence on the breeder
    Breeder.Person = null;
    // Dispose of the newly created item so it is not saved
    person.Delete();
}

The general idea is to delete the newly created instance when a cancel is requested.   To show the Modal Window I changed the Dispatcher.Invoke code to include a call to this.OpenModalWindow:

if (!hasValue)
{
    if (this.ShowMessageBox(
            string.Format("Do you want to add the person {0}?", value),
            "Add Person",
            MessageBoxOption.YesNo) == System.Windows.MessageBoxResult.Yes)
    {
        // …

        this.OpenModalWindow("AddPersonDetails");
    }
}

It is good to see that there is a nice balance between the LightSwitch designer and the code. If you want something special it can be done without too much of a fuss, if you know where to look and Google 😉

Somewhere is the future I will wrap things in a neat reusable component, but for now enjoy.

Tags:
januari 24, 2012  Ewart Nijburg

Resize VMWare virtual machine under Windows 7

  1. Turn off the virtual machine;
  2. Commit/remove all the snapshots or make a Full Clone if you use Link Clones.
  3. Open a Command Prompt and go to:
    C:\Program Files\VMWare\VMWare Server or C:\Program Files\VMware\VMware Workstation
    or for 64-bit
    C:\Program Files (x86)\VMWare\VMWare Server or C:\Program Files (x86)\VMware\VMware WorkstationRun this command to expand the virtual disk:
    vmware-vdiskmanager -x 12GB “My harddisk.vmdk” (in this case, 12 GB will be the new size).
    The file name can contain spaces because of the double quotes.

Note: Because this only expands the disk and not the partition, you’ll need to resize the partition table as well. This can be done with ‘diskpart.exe’, a built-in tool of Windows. VMWare provides a list of tools on their web site: KB1004071

  1. Power on your virtual machine;
  2. Open a Command Prompt and type: diskpart
  3. Type: list volume
    Remember the volume number (#) of your volume!
  4. Type: select volume (the number from step 3)
  5. Type: extend

Finished, no need for a reboot.

Seen on Leon Meijer’s Weblog

Tags:
juli 5, 2011  Ewart Nijburg

Adding a DataSourceAttribute through CodeDom

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using Microsoft.VisualBasic;

namespace AttributeCodeGenDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //using (CodeDomProvider codeProvider = new VBCodeProvider())
            using (CodeDomProvider codeProvider = new CSharpCodeProvider())
            {
                // Create namespace to hold new class
                CodeNamespace demoCodeNamespace = new CodeNamespace("Troolean.CodeDom.Demo");
                // Create using statements
                demoCodeNamespace.Imports.Add(new CodeNamespaceImport("Microsoft.VisualStudio.TestTools.UnitTesting"));
                demoCodeNamespace.Imports.Add(new CodeNamespaceImport("System"));

                // Create class
                CodeTypeDeclaration fooBarCodeTypeDecl = new CodeTypeDeclaration("FooBar")
                {
                    IsClass = true
                };

                // Add class to namespace
                demoCodeNamespace.Types.Add(fooBarCodeTypeDecl);

                // Decorate class with TestClass attribute
                fooBarCodeTypeDecl.CustomAttributes.Add(new CodeAttributeDeclaration("TestClass"));

                // Create a new method
                CodeMemberMethod executeCodeMember = new CodeMemberMethod()
                {
                    Name = "Execute",
                    Attributes = MemberAttributes.Public | MemberAttributes.Final
                };

                // Add new method to the class
                fooBarCodeTypeDecl.Members.Add(executeCodeMember);

                // Decorate method with TestMethod attribute
                executeCodeMember.CustomAttributes.Add(
                    new CodeAttributeDeclaration("TestMethod"));

                // Create DataSource attribute. Output the constructor that takes 4 parameters of which three are strings and te last one is an enum.
                var dataSourceCodeAttrDecl = new CodeAttributeDeclaration("DataSource",
                    new CodeAttributeArgument(new CodePrimitiveExpression("Microsoft.VisualStudio.TestTools.DataSource.TestCase")),
                    new CodeAttributeArgument(new CodePrimitiveExpression(string.Format("http://tfsserver:8080/tfs/troolean;{0}", "Product1"))),
                    new CodeAttributeArgument(new CodePrimitiveExpression("100")));

                // The last parameter is an enum
                CodeTypeReferenceExpression dataAccessMethodCodeTypeRefExpr = new CodeTypeReferenceExpression("DataAccessMethod");
                dataSourceCodeAttrDecl.Arguments.Add(new CodeAttributeArgument(new CodeFieldReferenceExpression(dataAccessMethodCodeTypeRefExpr, "Sequential")));

                //  Decorate the method with the DataSource attribute
                executeCodeMember.CustomAttributes.Add(dataSourceCodeAttrDecl);

                /*
                namespace Troolean.CodeDom.Demo
                {
                    using Microsoft.VisualStudio.TestTools.UnitTesting;
                    using System;

                    [TestClass()]
                    public partial class FooBar
                    {

                        [TestMethod()]
                        [DataSource("Microsoft.VisualStudio.TestTools.DataSource.TestCase", "http://tfsserver:8080/tfs/troolean;Product1", "100", DataAccessMethod.Sequential)]
                        public void Execute()
                        {
                        }
                    }
                }
                */

                // Generate the code and output to console
                codeProvider.GenerateCodeFromNamespace(
                    demoCodeNamespace,
                    Console.Out,
                    new CodeGeneratorOptions
                    {
                        BlankLinesBetweenMembers = true,
                        BracingStyle = "C"
                    }
                );
            }
        }
    }
}
Tags: ,