Inject dynamically a column with data-aware icons in a lightswitch datagrid

Introduction

There is no out-of-the-box solution in LightSwitch to show an icon in a datagrid based on the current row data (e.g. show an exclamation mark for customers satisfying a certain condition (or predicate). In many cases, developers would create a custom control for this. That’s a good solution but time-consuming and since LightSwitch is all about saving time and focussing and the business part of the application…. we need something better.

What do we have in mind?

this:

 

 

As you can see, an exclamation mark is shown for customers from Belgium.

How do we want to inject such an icon column?

We want that injecting such a column is simple, preferably in 2 lines of code:

 public partial class SearchCustomers
    {
        partial void SearchCustomers_Created()
        {
            Predicate<Customer> predicate = (c) => c.Country.Equals("belgium", StringComparison.InvariantCultureIgnoreCase);
            this.FindControl("grid").InjectColumnWithIcon<Customer, Customer.DetailsClass>(predicate,"important.png");
        }
    }

 

The first line is specifying our predicate or the condition under which we want to show the icon. In our case: customers from Belgium deserve an exclamation mark.

The second line is rendering the grid with the “icon column”.

 

Which is the base infrastructure we need?

Allow me first to tell you that my implementation is heavily inspired by an exquisite technique introduced by Jewel Lambert. See: http://dotnetlore.com/vertical-column-header-text-in-lightswitch-grids/ and here: http://social.msdn.microsoft.com/Forums/en-US/lightswitch/thread/3e7993c0-e51b-40bb-981d-99bcf878eb64

public static class GridExtensions
    {
        public static void InjectColumnWithIcon<TEntity, TDetails>(this IContentItemProxy gridProxy, Predicate<TEntity> predicate, string imagefileName)
            where TEntity : EntityObject<TEntity, TDetails>
            where TDetails : EntityDetails<TEntity, TDetails>, new()
        {
            EventHandler<ControlAvailableEventArgs> gridProxy_ControlAvailable = null;

            gridProxy_ControlAvailable = (s1, e1) =>
            {
                BitmapImage bitmapImage = GetBitmapImage(imagefileName);
                DataGrid dataGrid = e1.Control as DataGrid;
                var col = new DataGridTemplateColumn();
                var xaml =
                    @"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
                      <Image Height =""25"" Width=""25""  />
                </DataTemplate>";

                var dataTemplate = XamlReader.Load(xaml) as DataTemplate;
                col.CellTemplate = dataTemplate;
                col.IsReadOnly = true;
                dataGrid.Columns.Insert(0, col);
                dataGrid.LoadingRow += new EventHandler<DataGridRowEventArgs>((s2, e2) =>
                {
                    TEntity currentEntity = e2.Row.DataContext as TEntity;
                    if (currentEntity !=null && predicate(currentEntity))
                    {
                        DataGridColumn column = dataGrid.Columns[0];
                        Image image = column.GetCellContent(e2.Row) as Image;
                        image.Source = bitmapImage;
                    }
                });
                gridProxy.ControlAvailable -= gridProxy_ControlAvailable;
            };

            gridProxy.ControlAvailable += gridProxy_ControlAvailable;
        }

        private static BitmapImage GetBitmapImage(string fileName)
        {
            byte[] bytes = GetImageByName(fileName);
            using (MemoryStream ms = new MemoryStream(bytes))
            {
                var bi = new BitmapImage();
                bi.SetSource(ms);
                return bi;
            }
        }

        private static byte[] GetImageByName(string fileName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            fileName = Application.Current.Details.Name + ".Resources." + fileName;

            using (Stream stream = assembly.GetManifestResourceStream(fileName))
            {
                if (stream == null) return null;
                byte[] buf = new byte[stream.Length];
                stream.Read(buf, 0, (int)stream.Length);
                return buf;
            }
        }
    }

 

 

How do I have to include my icon in the silverlight project?

 

Include your icon (or image) in the Resource folder of the client project.

Make sure to mark is as embedded resource:

 

Source Code

Can be downloaded here: InjectColumnWithIcons

Conclusion

A simple solution for something that would take hours to implement in a regular line of business framework. In lightSwitch it takes now 1 minute :)

Enjoy!