.NET, C#, TextTemplate, visual studio

Run Custom Tool on Build for Visual Studio 2015

Background

The project that I’m working on supports multiple platform and the approach we’re doing is that we’re sharing codes across solutions. It means that some project/solution adds the source files as link from a common location. Resource (*.resx) and TextTemplate (*.tt) files are being shared across multiple projects and possibly in different platform such as .NET 2.0, .NET 4.5 and UWP.

Problem

Every time we change some part of the code in one project it might affect the output of the custom tool generated files and the custom tool (ResxFileCodeGenerator) for resource files doesn’t give exactly the same output in every platform. Now we have to run the custom tool on every file whenever we switch from one solution to another.

RunCustomToolOnBuild.vsix

RunCustomtoolOnBuild is a visual studio extension that tries to run the custom tools associated to project files on build. You can download it from visual studio gallery or get the source in GitHub.

How to use:
1. Install the RunCustomToolOnBuild.vsix
2. Open Visual Studio 2015
3. Right click on the resource or text template file then click properties.
4. In the properties set the RunCustomToolOnBuild to True.
5. Build the solution. Notice that the resx or TT file regenerates the output files.

RunCustomToolOnBuild

 

Credits to Thomas Levesque for his AutoRunCustomTool in Github.

 

Advertisements
.NET, C#, MVVM, UWP, WPF, XAML

Displaying Details Property Grid in XAML

Most data driven application usually have some header and detail screens. For the header screen, we usually put them in a ListView or an ItemsControl, set the ItemsSource and ItemTemplate, then boom, you have your neat looking list view. For this post, I’ll be talking about the details screen.

The most obvious way to create a details screen is to create a grid with two column, one for the label and one for the value.

<Grid>
  <Grid.RowDefinitions>
    <RowDefinitions />
    <RowDefinitions />
    <RowDefinitions />
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinitions />
    <ColumnDefinitions />
  </Grid.ColumnDefinitions>
  <TextBlock Text="Name" Grid.Row="0" Grid.Column="0"/>
  <TextBlock Text="{Binding Name}" Grid.Row="0" Grid.Column="1"/>
  <TextBlock Text="Address" Grid.Row="1" Grid.Column="0"/>
  <TextBlock Text="{Binding Address}" Grid.Row="1" Grid.Column="1"/>
  <TextBlock Text="Phone" Grid.Row="2" Grid.Column="0"/>
  <TextBlock Text="{Binding Phone}" Grid.Row="2" Grid.Column="1"/>
  <!-- and so on.. -->
</Grid>	

This approach works fine, however it’s really hard to maintain this kind of xaml. If you need to add a new row in the middle of the long list of rows, you have to adjust all the rows that will be affected. And of course, you’re going to have a very large xaml file and you’ll have a hard time reading it.

Whenever I deal with any problems in xaml, I always try to look in to the four techniques that usually solves my problem. These are converters, behavior, attached properties and custom controls. For this problem, I think we can best solve it using a behavior. I won’t elaborate on this, I’ll probably create a new post that will discuss these techniques.

Preparation
First thing we have to do is to prepare the class that we want to use as details. We want to display some or all of the properties of this class in the details page. How do we identify those properties that we want to display? We’ll be using a custom attribute PropertyGridItemAttribute.

 

  [AttributeUsage(AttributeTargets.Property)]
  public class PropertyGridItemAttribute : Attribute
  {
    public PropertyGridItemAttribute(string group = null)
    {
      this.Group = group ?? "default";
    }

    public PropertyGridItemAttribute(string header = null, int order = 0) : this(null)
    {
      this.Header = header;
      this.Order = order;
    }

    public PropertyGridItemAttribute(string group, string header, int order = 0) : this(group)
    {
      this.Header = header;
      this.Order = order;
    }

    public string Group { get; set; }
    public string Header { get; set; }
    public int? Order { get; set; }
  }

We’ll be using this attribute to decorate our properties so that we’ll know how to identify each properties that we display in the property grid. At the top of the class, there’s an AttributeUsage.Property. It only means that the attribute should be used in a property.

To identify the properties that should appear in our details class we’ll use the

PropertyGridItemAttribute on each properties.

  public class Customer : ViewModelBase
  {
    private string _Name = "Lance";
    [PropertyGridItem("Name", 1)]
    public string Name
    {
      get { return _Name; }
      set { Set(ref _Name, value); }
    }

    private string _Address = "3 Richards St., Hackensack, NJ";
    [PropertyGridItem("Delivery Address", 2)]
    public string Address
    {
      get { return _Address; }
      set { Set(ref _Address, value); }
    }

    private bool _IsCashOnDelivery = true;
    [PropertyGridItem("Cash on Delivery", 3)]
    public bool IsCashOnDelivery
    {
      get { return _IsCashOnDelivery; }
      set { Set(ref _IsCashOnDelivery, value); }
    }
  }

PropertyGridBehavior
What we wanna do is to be able to use a single template for all the details that we want to display and we want to put it in a container (clue: we’ll use ItemsContol) and we want it to be as reusable as possible.

So now, I’m going to create a behavior that we can use on every ItemsControl (that includes ListView and ListBox too). This behavior will have two dependency properties. The Source Object and the Group. I’ll start explaining the SourceObject first then the group later. The source object is the details class, in our example it’s the customer class. Whenever the source object property changes we generate a list of grid items and set it to the items source of the ItemsControl. So we won’t be binding anything to the ItemsSource of the ItemsControl in our xaml. Instead, we will bind to the SourceObject of the behavior.

  public object SourceObject
    {
      get { return (object)GetValue(SourceObjectProperty); }
      set { SetValue(SourceObjectProperty, value); }
    }

    public static readonly DependencyProperty SourceObjectProperty =
        DependencyProperty.Register("SourceObject", typeof(object), typeof(PropertyGridBehavior), new PropertyMetadata(null, SourceObjectChanged));

    private static void SourceObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      PropertyGridBehavior behavior = d as PropertyGridBehavior;
      (d as PropertyGridBehavior).AssociatedObject.ItemsSource = new ObservableCollection<PropertyGridItem>(GenerateGridItems(behavior.Group, e.NewValue));
    }

	The grid items is a list iof GridItems class that has a Header and a value. The header should be bound to the label. We'll see that later in the xaml part. 

    class PropertyGridItem
	{
		public string Header { get; set; }
		public object Value { get; set; }
	}

The grid items is a list iof GridItems class that has a Header and a value. The header should be bound to the label. We’ll see that later in the xaml part.

    class PropertyGridItem
	{
		public string Header { get; set; }
		public object Value { get; set; }
	}

To generate the GridItems, we take each properties that has a PropertyGridItemAttribute and wrap it inside a PropertyGridItem.

private static IEnumerable<PropertyGridItem> GenerateGridItems(string groupName, object sourceObject)
    {
      if (groupName == null)
        groupName = string.Empty; 

      var properties = from i in sourceObject.GetType().GetTypeInfo().DeclaredProperties.Where(p => p.CustomAttributes.Count(c => c.AttributeType == typeof(PropertyGridItemAttribute)) > 0)
                       where (groupName != string.Empty && i.GetCustomAttribute<PropertyGridItemAttribute>().Group == groupName) || groupName == string.Empty
                       orderby i.GetCustomAttribute<PropertyGridItemAttribute>().Order
                       select i;

      foreach (var property in properties)
      {
        int ctr = 0;
        string header = property.GetCustomAttribute<PropertyGridItemAttribute>().Header;
        int? order = property.GetCustomAttribute<PropertyGridItemAttribute>().Order;
        if (header == null)
          header = property.Name;
        if (order == null)
          order = ctr;
        ctr++;
        yield return new PropertyGridItem() { Header = header, Value = property.GetValue(sourceObject) };
      }
    }

Here’s the source code of the behavior class:

 public class PropertyGridBehavior : Behavior<ItemsControl>
  {
    public object SourceObject
    {
      get { return (object)GetValue(SourceObjectProperty); }
      set { SetValue(SourceObjectProperty, value); }
    }

    public static readonly DependencyProperty SourceObjectProperty =
        DependencyProperty.Register("SourceObject", typeof(object), typeof(PropertyGridBehavior), new PropertyMetadata(null, SourceObjectChanged));

    public string Group
    {
      get { return (string)GetValue(GroupProperty); }
      set { SetValue(GroupProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Group.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty GroupProperty =
        DependencyProperty.Register("Group", typeof(string), typeof(PropertyGridBehavior), new PropertyMetadata(null));

    private static void SourceObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      PropertyGridBehavior behavior = d as PropertyGridBehavior;
      (d as PropertyGridBehavior).AssociatedObject.ItemsSource = new ObservableCollection<PropertyGridItem>(GenerateGridItems(behavior.Group, e.NewValue));
    }

    private static IEnumerable<PropertyGridItem> GenerateGridItems(string groupName, object sourceObject)
    {
      if (groupName == null)
        groupName = string.Empty;

      var properties = from i in sourceObject.GetType().GetTypeInfo().DeclaredProperties.Where(p => p.CustomAttributes.Count(c => c.AttributeType == typeof(PropertyGridItemAttribute)) > 0)
                       where (groupName != string.Empty && i.GetCustomAttribute<PropertyGridItemAttribute>().Group == groupName) || groupName == string.Empty
                       orderby i.GetCustomAttribute<PropertyGridItemAttribute>().Order
                       select i;

      foreach (var property in properties)
      {
        int ctr = 0;
        string header = property.GetCustomAttribute<PropertyGridItemAttribute>().Header;
        int? order = property.GetCustomAttribute<PropertyGridItemAttribute>().Order;
        if (header == null)
          header = property.Name;
        if (order == null)
          order = ctr;
        ctr++;
        yield return new PropertyGridItem() { Header = header, Value = property.GetValue(sourceObject) };
      }
    }
  }

Now in the XAML part, we’ll add a PropertyGridBehavior in the ItemsControl. This will generate a list of PropertyGridItem from all the properties attributed with PropertyGridAttribute in the Customer class. We also define the DataTemplate in the xaml. Keep in mind that when we use this behavior we’ll be binding to a list of PropertyGridItem. PropertyGridItem has 2 properties, a Header which is the label and a Value.

	  <ItemsControl HorizontalAlignment="Stretch">
        <i:Interaction.Behaviors>
          <CustomBehavior:PropertyGridBehavior SourceObject="{Binding Customer}"/>
        </i:Interaction.Behaviors>
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <Grid HorizontalAlignment="Stretch">
              <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
              </Grid.ColumnDefinitions>
              <TextBlock Grid.Column="0" Text="{Binding Header}" />
              <TextBlock Grid.Column="1" Text="{Binding Value}" />
              <Border BorderBrush="Silver" BorderThickness="0,0,0,1" Grid.ColumnSpan="2"/>
            </Grid>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>

Grouping
If we have a lot of details in the class, we would probably want to display it as groups. That’s when we will use the grouping dependency property of the PropertyGridBehavior. We will also have to specify the group when we put the attribute in the class. Let’s go back to the example. This time we’re going to specify the group name in the first parameter of the PropertyGridItemAttribute. We can also specify the order in the last parameter of the constructor. The order will tell the order of the property within the group when displayed in the grid.


    public class Customer : ViewModelBase
    {
      private string _Name = "Lance";
      [PropertyGridItem("PersonalInfo", "Name", 1)]
      public string Name
      {
        get { return _Name; }
        set { Set(ref _Name, value); }
      }

      private DateTime _BirthDay;
      [PropertyGridItem("PersonalInfo", "Birthday", 2)]
      public DateTime BirthDay
      {
        get { return _BirthDay; }
        set { Set(ref _BirthDay, value); }
      }

      private string _Address = "3 Richards St., Hackensack, NJ";
      [PropertyGridItem("DeliveryInfo", "Delivery Address", 1)]
      public string Address
      {
        get { return _Address; }
        set { Set(ref _Address, value); }
      }

      private bool _IsCashOnDelivery = true;
      [PropertyGridItem("DeliveryInfo", "Cash On Delivery", 2)]
      public bool IsCashOnDelivery
      {
        get { return _IsCashOnDelivery; }
        set { Set(ref _IsCashOnDelivery, value); }
      }
    }

We also need to specify the group in the PropertyGridBehavior in the xaml. This time we’ll have two ItemsControl, one for each group. We can also use a resource for the item template so we don’t have to duplicate our code.

 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.Resources>
      <DataTemplate x:Key="gridItemTemplate">
        <Grid Margin="0,10,0,10" HorizontalAlignment="Stretch">
          <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
          </Grid.ColumnDefinitions>
          <Border Grid.ColumnSpan="2"                   BorderBrush="Gray"                   BorderThickness="0,0,0,1" />
          <TextBlock Grid.Column="0"                      Margin="2,5,2,5"                      FontWeight="Bold"                      Text="{Binding Header}" />
          <TextBlock Grid.Column="1"                      Margin="2,5,2,5"                      HorizontalAlignment="Right"                      Text="{Binding Value}" />
        </Grid>
      </DataTemplate>
    </Grid.Resources>
    <StackPanel>
      <StackPanel Width="500">
        <TextBlock Margin="20"                    FontSize="32"                    FontWeight="Bold"                    Text="Personal Info" />
        <ItemsControl Margin="20"                       HorizontalAlignment="Stretch"                       ItemTemplate="{StaticResource gridItemTemplate}">
          <i:Interaction.Behaviors>
            <behavior:PropertyGridBehavior Group="PersonalInfo" SourceObject="{Binding}" />
          </i:Interaction.Behaviors>
        </ItemsControl>
      </StackPanel>
      <StackPanel Width="500">
        <TextBlock Margin="20"                    FontSize="32"                    FontWeight="Bold"                    Text="Delivery Info" />
        <ItemsControl Margin="20"                       HorizontalAlignment="Stretch"                       ItemTemplate="{StaticResource gridItemTemplate}">
          <i:Interaction.Behaviors>
            <behavior:PropertyGridBehavior Group="DeliveryInfo" SourceObject="{Binding}" />
          </i:Interaction.Behaviors>
        </ItemsControl>
      </StackPanel>

    </StackPanel>
  </Grid>

Here’s a screen shot of the sample application
PropertyGrid.PNG

 

You can download the source code from my github page.

.NET, C#, MVVM, TextTemplate, XAML

Using TextTemplate to Generate Wrapper classes

Wrapping classes is sometimes necessary in development. The MVVM pattern usually wraps the model inside a viewmodel class for it tobe bindable. In brokered component we can’t expose a class from a referenced library unless we wrap it in a sealed class. The problem with wrapping is that we repeat the same code over and we expose properties and function with the same name and we have to retype it and it takes some amount of time.

The good news is that there’s an item in visual studio called TextTemplate. Text template is a scriptable file that can generate code for you and those generated files are usable within the assembly.

Now going back to the problem of wrapping classes. We as developers doesn’t want to spend time typing repetitive codes. Using reflection I managed to create a reusable script that can generate a wrapper class. This script will generate wrapped properties, methods, constructors and nested class. You can download the TextTemplate source code from my Github repo.

Basic Class Wrapping

Classes that will be wrapped within a wrapper:

public class User
{
public string FirstName { get; set; }

public string LastName { get; set; }

public string UserName { get; set; }

public int Role { get; set; }

public int InvalidLoginAttempt { get; set; }

}

public class Driver
{
public Driver(string name) { }

public Driver() { Nested = new SomeNestedClass(); }

public string DriverId { get; private set; }

public string DriverName { get; set; }

public string TruckNo { get; set; }

public SomeNestedClass Nested { get; set; }

public User UserInfo { get; set; }

public void StartShift(string p1, string p2, ref int odometer, params object[] args) { Console.WriteLine(&amp;quot;StartShift&amp;quot;); }

public int EndShift(string p1, string p2, out int odometer) { Console.WriteLine(&amp;quot;EndShift&amp;quot;); odometer = 1; return 0; }

public class SomeNestedClass
{
public string NestedValue { get; set; }

public string NestedName { get; set; }

public void Hello()
{
Console.WriteLine(&amp;quot;Hello Nested World&amp;quot;);
}
}
}

Wrapped Classes:

public sealed class Driver
{
#region Constructors

public DataModels.Driver driverDataModel;
public Driver(DataModels.Driver driver) { this.driverDataModel = driver; }
public Driver(System.String name)
{
driverDataModel = new DataModels.Driver(name);
}
public Driver()
{
driverDataModel = new DataModels.Driver();
}

#endregion Constructors

#region Properties

public System.String DriverId { get { return driverDataModel.DriverId; }  }
public System.String DriverName { get { return driverDataModel.DriverName; } set { driverDataModel.DriverName = value; } }
public System.String TruckNo { get { return driverDataModel.TruckNo; } set { driverDataModel.TruckNo = value; } }
private BasicClassWrapperSample.SomeNestedClass _Nested = null;
public BasicClassWrapperSample.SomeNestedClass Nested { get { if(_Nested == null) _Nested = new BasicClassWrapperSample.SomeNestedClass(driverDataModel.Nested); return _Nested; } set { _Nested = value; } }
private BasicClassWrapperSample.User _UserInfo = null;
public BasicClassWrapperSample.User UserInfo { get { if(_UserInfo == null) _UserInfo = new BasicClassWrapperSample.User(driverDataModel.UserInfo); return _UserInfo; } set { _UserInfo = value; } }

#endregion Properties

#region Methods

public void StartShift (System.String p1, System.String p2, ref System.Int32 odometer, params System.Object[] args) =&amp;gt; driverDataModel.StartShift(p1, p2, ref odometer, args);
public System.Int32 EndShift (System.String p1, System.String p2, out System.Int32 odometer) =&amp;gt; driverDataModel.EndShift(p1, p2, out odometer);

#endregion Methods
}
public sealed class SomeNestedClass
{
#region Constructors

public DataModels.Driver.SomeNestedClass somenestedclassDataModel;
public SomeNestedClass(DataModels.Driver.SomeNestedClass somenestedclass) { this.somenestedclassDataModel = somenestedclass; }
public SomeNestedClass()
{
somenestedclassDataModel = new DataModels.Driver.SomeNestedClass();
}

#endregion Constructors

#region Properties

public System.String NestedValue { get { return somenestedclassDataModel.NestedValue; } set { somenestedclassDataModel.NestedValue = value; } }
public System.String NestedName { get { return somenestedclassDataModel.NestedName; } set { somenestedclassDataModel.NestedName = value; } }

#endregion Properties

#region Methods

public void Hello () =&amp;gt; somenestedclassDataModel.Hello();

#endregion Methods
}

public sealed class User
{
#region Constructors

public DataModels.User userDataModel;
public User(DataModels.User user) { this.userDataModel = user; }
public User()
{
userDataModel = new DataModels.User();
}

#endregion Constructors

#region Properties

public System.String FirstName { get { return userDataModel.FirstName; } set { userDataModel.FirstName = value; } }
public System.String LastName { get { return userDataModel.LastName; } set { userDataModel.LastName = value; } }
public System.String UserName { get { return userDataModel.UserName; } set { userDataModel.UserName = value; } }
public System.Int32 Role { get { return userDataModel.Role; } set { userDataModel.Role = value; } }
public System.Int32 InvalidLoginAttempt { get { return userDataModel.InvalidLoginAttempt; } set { userDataModel.InvalidLoginAttempt = value; } }

#endregion Properties

#region Methods

#endregion Methods
}

Wrapping Model within ViewModel

//Model
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public int Role { get; set; }
public int InvalidLoginAttempt { get; set; }
}

//Wrapped ViewModel
public sealed class UserViewModel : ViewModelBase
{
#region Constructors

public DataModels.User userDataModel;
public UserViewModel(DataModels.User user) { this.userDataModel = user; }
public UserViewModel()
{
userDataModel = new DataModels.User();
}

#endregion Constructors

#region Properties

public System.String FirstName { get { return userDataModel.FirstName; } set { userDataModel.FirstName = value; OnPropertyChanged(); } }
public System.String LastName { get { return userDataModel.LastName; } set { userDataModel.LastName = value; OnPropertyChanged(); } }
public System.String UserName { get { return userDataModel.UserName; } set { userDataModel.UserName = value; OnPropertyChanged(); } }
public System.Int32 Role { get { return userDataModel.Role; } set { userDataModel.Role = value; OnPropertyChanged(); } }
public System.Int32 InvalidLoginAttempt { get { return userDataModel.InvalidLoginAttempt; } set { userDataModel.InvalidLoginAttempt = value; OnPropertyChanged(); } }

#endregion Properties
}