Powershell equivalent of xargs

I wanted to be able to build extmap.xml for all the files that were in my packages\ directory. I had a Powershell script called ToExtMap which automated most of my tasks, and a little experimentation with Powershell gave me this one liner:


PS C:\Users\Chui\Projects\SAM-GET\packages>
Get-ChildItem -Filter *.dll -Recurse -name |
%{ ToExtMap $_ | Out-File ($_ -replace "dll", "extmap.xml") -encoding ASCII}

A bit of explanation might be in order:

  1. Get-ChildItem -Recurse finds all the files I require.
  2. The %{} is the shortcut for the foreach operator.
  3. Powershell is object oriented. $_ -replace ... executes a string substitution

Over at Forbes

It started with making one presentation slide for each idea. Each slide would have a heading and several bullet points defining the scope of the feature and metrics describing what success would look like and how it would impact the business. That’s all people got to make their case. And given the speed of the whole process, there was little time for precision.

Business Forms in Silverlight Part 2

(this is a continuation of Part 1)

From the previous post:

Basic DataForm experience

Basic DataForm experience

The first problem is the OrderDate is unintialized and renders as 0/00/0000.

The first thought is to change the OrderDate from DateTime to Nullable<DateTime>. This will require either the database schema be changed, or a second viewmodel created. Changing the database schema just to support a user-interface tweak is obviously out of the question. However, writing a viewmodel and copying all the properties over breaks a rule of “DRY” (Don’t Repeat Yourself).

Luckily, XAML binding supports the notion of binding converters. This lets us transforms inputs from one type to another.

public class NullValueConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is DateTime && targetType == typeof(Nullable<DateTime>))
        {
            if ((DateTime)value == DateTime.MinValue)
            {
                return null;
            }
        }
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }
}

Applying the converter to the date picker, we get reasonably good results. For example, after entering a date and then clearing it, we get a validation error showing (see picture below). Furthermore, hitting cancel restores the control back to default value.

dataform2

This done, we are ready to tackle our next problem.

Aside

Here’s a sample of how converters are applied.

<sdk:DataForm>
  <sdk:DataForm.Resources>
    <local:NullValueConverter x:Key="NullValueConverter" />
  </sdk:DataForm.Resources>
  <sdk:DataField Label="Order Date">
    <sdk:DatePicker SelectedDate="{Binding OrderDate, Converter={StaticResource NullValueConverter}}" />
  </sdk:DataField>
</sdk:DataForm>

Fool-Proof Business Forms in Silverlight Part I

Silverlight DataForms makes it quick and easy to get started with database applications. However, it has some downsides. The default isn’t particularly customizable, and to do anything more takes a great deal more effort.

Take the following example:

        internal sealed class OrderMetadata
        {

            // Metadata classes are not meant to be instantiated.
            private OrderMetadata()
            {
            }

            [Display(Name = "Order date", GroupName = "Order", Order = 1)]
            public DateTime OrderDate { get; set; }

            [Range(0, 100.0, ErrorMessage = "Please enter more than $0.00")]
            [Display(Name = "Total Amount", GroupName = "Order", Order = 2)]
            public decimal Amount { get; set; }

            [Display(Name = "Purchase Order No", GroupName = "Order", Order = 3)]
            public string PurchaseOrder { get; set; }

            [Display(Name = "Customer Name", GroupName = "Customer", Order = 1)]
            public Customer Customer { get; set; }

            [Display(Name = "Is Delivered", GroupName = "Customer", Order = 2)]
            public byte[] IsDelivered { get; set; }

            [ScaffoldColumn(false)]
            public int CustomerId { get; set; }

            [ScaffoldColumn(false)]
            public int OrderId { get; set; }
            
            public float Weight { get; set; }

            [UIHint("Image", "SL", "ScannedFilename")]
            public Byte[] ScannedImage { get; set; }

            [ScaffoldColumn(false)]
            public String ScannedFilename { get; set; }
        }

This generates a data form that gets us pretty close. See the picture below.

dataform1

However, there are several issues:

  1. The Order date is very ugly. It should have default to a null value
  2. The Customer field should have been bound to a combo box or something more custom
  3. The Total Amount validation message could not be customized. It reports “Input is not in the correct format”. Ideally, the message should be customizable and localizable.
  4. CustomerId and OrderId should be hidden
  5. Scanned file name and Scanned image should be combined into a single image control, showing the image, the file name, and provide a method to replace the image.

Over the next few posts, I’ll discuss how to overcome these problems using a custom view model and avoid unnecessary code duplication.

Dell Service Drop off Brisbane

The Dell service center in Brisbane has moved to 8 Castlemaine St, Cooparoo (effective 6 Jan 2014). It is in the big courier’s shed, and there’s a small narrow storefront. If you need to drop off your computer for repairs, it is no longer at Mowbray Tce.

Hopefully this post finds you before you get lost.

Piano Teacher in Toowoomba

My kids have been taking piano lessons with Marie Kilpatrick in Toowoomba and I liked her style of communicating musical ideas to the children. For example, in Mozart’s Andante, she likened the left hand to rustling wind, while a diva sings on the right hand, and then she works through with the children the kind of touch necessary. Another way she communicated this is by explaining that the triplets on the left hand outnumber the melody on the right hand, and therefore has to be much much softer.

Syncfusion’s latest datagrid

I’ve been looking forward to Syncfusion’s latest release of their Silverlight components (and it’s out today). In particular, their new SfDataGrid datagrid is reaching maturity so I decided to take it out for a quick spin, and verdict is out. Two thumbs up. Well done. Their API is intuitive and much improved from their original DataGrid. It is different, so don’t expect to be able to port over existing code easily, but there is considerably less code to port.

We’ll start off with a simple model

namespace SyncfusionGridDemo.ViewModels
{
    using System.ComponentModel.DataAnnotations;

    public class Customer
    {
        [Display(Name="Full Name")]
        [Required(ErrorMessage="{0} is required")]
        public string FullName { get; set; }

        [Range(18, 80)]
        public int Age { get; set; }
    }
}

As you can see there is not much to it, and we’ll create a viewmodel and host a collection of Customers.

namespace SyncfusionGridDemo.ViewModels
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;

    public class MainPageViewModel
    {
        private IList<Customer> _addedCustomers = new List<Customer>();
        private IList<Customer> _deletedCustomers = new List<Customer>();

        public MainPageViewModel()
        {
            Customers = new ObservableCollection<Customer>(new[]
            {
                new Customer { FullName = "Marcus Aurelius", Age=30 },
                new Customer { FullName = "Dana Fausbinder", Age=3 },
                new Customer { FullName = "Tomoko Cressida McManus", Age=54 },
            });
            Customers.CollectionChanged += Customers_CollectionChanged;
        }

        void Customers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    // TODO these will need to be saved to the database
                    _deletedCustomers.RemoveRange((IEnumerable<Customer>)e.NewItems);
                    _addedCustomers.AddRange<Customer>((IEnumerable<Customer>)e.NewItems);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    _addedCustomers.RemoveRange((IEnumerable<Customer>)e.NewItems);
                    _deletedCustomers.AddRange<Customer>((IEnumerable<Customer>)e.NewItems);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    // TODO these will need to be saved to the database
                    _deletedCustomers.RemoveRange((IEnumerable<Customer>)e.OldItems);
                    _addedCustomers.RemoveRange((IEnumerable<Customer>)e.OldItems);
                    _addedCustomers.AddRange<Customer>((IEnumerable<Customer>)e.NewItems);
                    break;

            }

        }

        public ObservableCollection<Customer> Customers { get; set; }
    }

    public static class CollectionExtension
    {
        public static void AddRange<T>(this IList<T> coll, IEnumerable<T> items)
        {
            foreach (var item in items)
            {
                if (!coll.Contains(item))
                    coll.Add(item);
            }
        }

        public static void RemoveRange<T>(this IList<T> coll, IEnumerable<T> items)
        {
            foreach (var item in items)
            {
                if (coll.Contains(item))
                    coll.Remove(item);
            }
        }


    }
}

And now let’s bind to Syncfusion SfDataGrid


        <Grid:SfDataGrid 
            AllowEditing="True" 
            Grid.Row="1" 
            ItemsSource="{Binding Customers}" 
            x:Name="SampleGrid" 
            GridValidationMode="InEdit" 
            AddNewRowPosition="Top"/>

and the result is rather good for so little work.

Some minor criticism

The datagrid is not aware of IEditableObject interface. This interface supports BeginEdit(), EndEdit() and CancelEdit(). This allows the user to revert the record’s changes using the escape key.

Overall, the user experience is much improved. Thanks again for listening to my concerns.