Tuesday, September 30, 2008

Generic Binding in WPF through explicit interfaces and DataTemplates

I have faced a problem recently that required all sorts of different data to be displayed in a single list. Now this probably is not the first time this kind of requirement has surfaced. The requirement was to list entities that could have documents linked to them. These entities don’t really have anything in common, and in fact we don’t even know about these entities as the Document code is independent of anything that consumes it.

Ignoring the requirements for a moment lets just show the problem space. The problem is that we need to display a list of entities of differing types with enough information to identify the entity. Identifying entities is more difficult than just a type + Id/Key pair. Many of the entities are transient or the user may need more information to accurately identify the entity. So we have decided that we need a column for Type, ID/Key, Name and Description/Detail. Further to this the UI designer also wanted space to display comprehensive information about the entity.

So from the information I have from the requirements and the UI guy I decide that the best way to do this is an interface that entities can implement and then they can be displayed on the list.

public interface ILinkedEntity
{
    string Type { get; }
    string Key { get; }
    string Name { get; }
    string Detail { get; }
    string Preview { get; }
}

Then I could display the list like the following

DocumentLinkListExample

Then I thought to myself that exposing Detail and Preview as strings is a stupid Idea because then I would be require presentation logic via string.format in the entity. So I changed the interface to look like this:

public interface ILinkedEntity
{
    string Type { get; }
    string Key { get; }
    string Name { get; }
    object Detail { get; }
    object Preview { get; }
}

Now it is up to the implementer of the interface to return an object that has a DataTemplate ready to be applied to it.

An example of an implementation:

public class Deal : ILinkedEntity
{
    public int DealId { get; set; }
    public string PortfolioNumber { get; set; }
    public string PortfolioGroup { get; set; }
    public int RiskGrade { get; set; }
    public DateTime? DueDate { get; set; }

    #region ILinkedEntity Members
    public string Type
    {
        get { return "Deal"; }
    }

    public string Key
    {
        get { return DealId.ToString(); }
    }

    public string Name
    {
        get { return DealId.ToString(); }
    }

    public object Detail
    {
        get { return DealDescriptionTranslator.Translate(this); }
    }

    public object Preview
    {
        get { return this; }
    }
    #endregion
}

So I create the XAML for the GridView and set the bindings to the correct paths on my ObservableCollection<ILinkedEntity> member.

<ListView x:Name="LinkedEntityList"  ItemsSource="{Binding Path=LinkedEntities}">
  <ListView.Resources>
    <Style TargetType="ListViewItem">
      <Setter Property="ToolTip" Value="{Binding Path=Preview}"/>
    </Style>
  </ListView.Resources>
  <ListView.View>
    <GridView>
      <GridViewColumn Header="Type" DisplayMemberBinding="{Binding Path=Type}"/>
      <GridViewColumn Header="Id/Key" DisplayMemberBinding="{Binding Path=Key}"/>
      <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}"/>
      <GridViewColumn Header="Detail" >
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <ContentControl 
                Content="{Binding Path=Detail}" 
                ToolTip="{Binding Path=Preview}"
                HorizontalContentAlignment="Stretch"
                HorizontalAlignment="Stretch"
                />
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>
    </GridView>
  </ListView.View>
</ListView>

This all works well except I haven’t provided any DataTemplates for the Objects I return for Detail or Preview members. So I do this as per the UI guy’s specs and put them in a Resource Dictionary that shares scope with the view (app.xaml because Im lazy).

<DataTemplate DataType="{x:Type entities:Deal}">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Label Grid.Column="0" Grid.Row="0" 
       VerticalAlignment="Center">Deal Id :</Label>
        <TextBlock Grid.Column="1" Grid.Row="0" 
           VerticalAlignment="Center" 
           Text="{Binding Path=DealId}" />

        <Label Grid.Column="0" Grid.Row="1" 
       VerticalAlignment="Center">Portfolio :</Label>
        <TextBlock Grid.Column="1" Grid.Row="1" 
           VerticalAlignment="Center" 
           Text="{Binding Path=PortfolioNumber}" />

        <Label Grid.Column="0" Grid.Row="2" 
       VerticalAlignment="Center">Portfolio Group :</Label>
        <TextBlock Grid.Column="1" Grid.Row="2" 
           VerticalAlignment="Center" 
           Text="{Binding Path=PortfolioGroup}" />

        <Label Grid.Column="2" Grid.Row="0" 
       VerticalAlignment="Center">Risk Grade :</Label>
        <TextBlock Grid.Column="3" Grid.Row="0" 
           VerticalAlignment="Center" 
           Text="{Binding Path=RiskGrade}" />

        <Label Grid.Column="2" Grid.Row="1" 
       VerticalAlignment="Center">Due Date:</Label>
        <TextBlock Grid.Column="3" Grid.Row="1" 
           VerticalAlignment="Center" 
           Text="{Binding Path=DueDate}" />

    </Grid>
</DataTemplate>

<DataTemplate DataType="{x:Type entities:DealDescription}">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Path=PortfolioGroup}"/>
        <TextBlock Text="["/>
        <TextBlock Text="{Binding Path=PortfolioNumber}"/>
        <TextBlock Text="]"/>
    </StackPanel>
</DataTemplate>

Right now I’m happy. I am now presenting data as per the requirements and UI design. The only problem is that I am Implementing the Interface implicitly. This creates a lot of noise on some of my entities that have members that are very similar to the ones defined on the interface. For example I have an “DealId” property that is an integer on the Deal object. For a consumer of the object it would be very confusing for them to see both a DealId and a Key property. So I decide to implement the interface explicitly to clean things up.

SHOCK!

My view is now broken! The view presents nothing but blank rows to the user. After a bit of time on the Google-machine I find post on a forum where Josh Smith offers some help. A quick change to the bindings on my XAML to use the interface explicitly :

<ListView x:Name="LinkedEntityList" ItemsSource="{Binding Path=LinkedEntities}">
  <ListView.Resources>
    <Style TargetType="ListViewItem">
      <Setter Property="ToolTip" Value="{Binding Path=Preview}"/>
    </Style>
  </ListView.Resources>
  <ListView.View>
    <!-- this version allows for Explicit interface implementation-->
    <GridView>
      <GridViewColumn Header="Type" 
                      DisplayMemberBinding="{Binding Path=(local:ILinkedEntity.Type)}"/>
      <GridViewColumn Header="Id/Key" 
                      DisplayMemberBinding="{Binding Path=(local:ILinkedEntity.Key)}"/>
      <GridViewColumn Header="Name" 
                      DisplayMemberBinding="{Binding Path=(local:ILinkedEntity.Name)}"/>
      <GridViewColumn Header="Detail" >
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <ContentControl 
                Content="{Binding Path=(local:ILinkedEntity.Detail)}" 
                ToolTip="{Binding Path=(local:ILinkedEntity.Preview)}"
                HorizontalContentAlignment="Stretch"
                HorizontalAlignment="Stretch"
                />
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>
    </GridView>
  </ListView.View>
</ListView>

Ta-dah! Thanks Josh. All happy now. I have a list that can display things it knows nothing about and hand off any fancy logic to a DataTemplate that should be defined by the UI guy in change of that domain.

Sweet.

Check out full code here

Wednesday, September 10, 2008

Federated identity and Security – Video

Bill Poole recently gave a presentation on Federated Identity at the Perth .Net User Group. His slides are available on his blog post regarding the session. His video available here:

Bill Poole presenting Federated Identity : MP4 Videos (6x~10min.)

At the start of the video the sound is poor due to the distance away from the presenter and the background noise. I move the camera a few minutes in which makes it better.