Monthly Archives: September 2014

Synchronizing the columns in hierarchical in a XamDataGrid

Before I start this post, let me just say I’m using an old version of the Infragistics XamDataGrid for this post, version 10.3. Hence this may have been changed in subsequent releases, but as I have a legacy application to support, that’s the version they’re using.

If you followed my previous post on using hierarchical data in a XamDataGrid, you’ll have noticed that the final image was hardly impressive, in that columns were not all in sync and if you resize a column, not all columns resize.

For now, let’s remove the second FieldLayout’s hidden field, so our XAML looks like the following (I’ve commented out the offending line)

<igDP:XamDataGrid GroupByAreaLocation="None" DataSource="{Binding}" Name="Grid">
   <igDP:XamDataGrid.FieldLayoutSettings>
      <igDP:FieldLayoutSettings ExpansionIndicatorDisplayMode="CheckOnDisplay"
                                          AutoGenerateFields="False"/>
   </igDP:XamDataGrid.FieldLayoutSettings>
   <igDP:XamDataGrid.FieldLayouts>
      <igDP:FieldLayout>
         <igDP:FieldLayout.Fields>
            <igDP:Field Name="Name" />
            <igDP:Field Name="Manages" Visibility="Hidden" />
         </igDP:FieldLayout.Fields>
      </igDP:FieldLayout>

      <igDP:FieldLayout>
         <igDP:FieldLayout.Settings>
            <igDP:FieldLayoutSettings LabelLocation="Hidden" />
         </igDP:FieldLayout.Settings>
         <igDP:FieldLayout.Fields>
            <igDP:Field Name="Name" />
            <!-- remove this line for now
            <igDP:Field Name="DepartmentManages" Visibility="Hidden" />
            -->
         </igDP:FieldLayout.Fields>
      </igDP:FieldLayout>

   </igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>

This will produce the following

Columns aligned in parent child relationship

However, this is an illusion that. All is still not quite as we want – if we resize the parent then you’ll see that child columns do not remain in sync with the parent. To handle this, let’s go back to the XAML and add the following to the XamDataGrid

Note: I gave the XamDataGrid the name “Grid” as we’re going to need to write some code for the following changes in functionality.

<igDP:XamDataGrid.Resources>
   <Style TargetType="{x:Type igDP:LabelPresenter}">
      <EventSetter Event="SizeChanged" Handler="EventSetter_OnHandler"/>
   </Style>
</igDP:XamDataGrid.Resources>

The EventSetter_OnHandler code looks like this

private void EventSetter_OnHandler(object sender, SizeChangedEventArgs e)
{
   var lp = sender as LabelPresenter;
   if (lp != null)
   {
      if (Grid.FieldLayouts[0].Fields.Contains(lp.Field))
      {
         var f = Grid.FieldLayouts[1].Fields[lp.Field.Index];
         f.Width = new FieldLength(lp.Field.CellWidthResolved);
      }

      if (Grid.FieldLayouts[1].Fields.Contains(lp.Field))
      {
         var f = Grid.FieldLayouts[0].Fields[lp.Field.Index];
         f.Width = new FieldLength(lp.Field.CellWidthResolved);
      }
   }
}

Now when you resize the columns all will remain in sync, however as you can see from the code this is all a little nasty in that it’s very much dependent upon the number of FieldLayouts you have etc.

Let’s not worry too much about that at the moment. As there’s something a little more pressing, if you happend to expose multiple columns, for example, suppose I added an age column to the parent and child view models and added the fields to the XAML. When moving a column around you’ll notice the child will not reorganise it’s columns, so you’d end up with, for example – the parent columns Age followed by Name whereas the child would be Name followed by Age.

To fix this add the following FieldPositionChanged event intot he XamDataGrid

<igDP:XamDataGrid GroupByAreaLocation="None" 
    DataSource="{Binding}" 
    Name="Grid" 
    FieldPositionChanged="Grid_OnFieldPositionChanged"> <!-- our new addition -->

In the source code we’d now need to add the following

private void Grid_OnFieldPositionChanged(object sender, FieldPositionChangedEventArgs e)
{
   if (Grid.FieldLayouts[0].Fields.Contains(e.Field))
   {
      foreach (var field in Grid.FieldLayouts[0].Fields)
      {
         if (field.Index < Grid.FieldLayouts[1].Fields.Count)
         {
            var field2 = Grid.FieldLayouts[1].Fields[field.Index];
            field2.ActualPosition = new FieldPosition(field.ActualPosition.Column, 
                        field.ActualPosition.Row, 
                        field.ActualPosition.ColumnSpan, 
                        field.ActualPosition.RowSpan);
         }
      }
   }

   if (Grid.FieldLayouts[1].Fields.Contains(e.Field))
   {
      foreach (var field in Grid.FieldLayouts[1].Fields)
      {
         var field2 = Grid.FieldLayouts[0].Fields[field.Index];
         field2.ActualPosition = new FieldPosition(field.ActualPosition.Column, 
                     field.ActualPosition.Row, 
                     field.ActualPosition.ColumnSpan, 
                     field.ActualPosition.RowSpan);
      }
   }
}

Notice again we need to have knowledge about the number of field layouts to handle this correctly, but this will now work. Reordering the columns occurs across all rows.

This is not perfect – and obviously we need to look at rewriting our code to handle any number of FieldLayouts, but it’s a start. I’ve more things I need to do with this hierarchical data, so if it’s interesting enough I’ll continue with further posts, but for now, between this and the previous post on the subject, we can now display hierarchical data and keep the columns in sync.

Addendum

After writing this post I found a mad desire to rewrite the code to handle multiple FieldLayouts, this has not been tested fully (so use at your own risk) but here goes

private void EventSetter_OnHandler(object sender, SizeChangedEventArgs e)
{
   var lp = sender as LabelPresenter;
   if (lp != null)
   {
      var found = Grid.FieldLayouts.FirstOrDefault(fl => fl.Fields.Contains(lp.Field));
      if (found != null)
      {
         foreach (var fl in Grid.FieldLayouts)
         {
            if (!fl.Equals(found))
            {
               var f = fl.Fields[lp.Field.Index];
               f.Width = new FieldLength(lp.Field.CellWidthResolved);							
            }
         }
     }
   }
}

private void Grid_OnFieldPositionChanged(object sender, FieldPositionChangedEventArgs e)
{
   var found = Grid.FieldLayouts.FirstOrDefault(fl => fl.Fields.Contains(e.Field));
   if (found != null)
   {
      foreach (var field in found.Fields)
      {
         foreach (var fl in Grid.FieldLayouts)
         {
            if (!fl.Equals(found))
            {
               if (field.Index < fl.Fields.Count)
               {
                  var f = fl.Fields[field.Index];
                  f.ActualPosition = new FieldPosition(
                       field.ActualPosition.Column, 
                       field.ActualPosition.Row, 
                       field.ActualPosition.ColumnSpan, 
                       field.ActualPosition.RowSpan);
               }
            }
         }
      }
   }
}

Update

Revisiting this code to use it on a project I’m working on, I found I’d missed a piece off. When resizing the columns from the parent/header this works fine but XamDataGrid allows you to resize the child rows and we don’t have code to resize the parents and keep in sync.

An easy way to “fix” this is to disable resizing on columns except for on the header which we can achieve easily by adding to our grid XAML

PreviewMouseMove="Grid_OnPreviewMouseMove"

and in our code behind we have

// you'll need this using Infragistics.Windows;
private void Grid_OnPreviewMouseMove(object sender, MouseEventArgs e)
{
   var lp = Utilities.GetAncestorFromType(e.OriginalSource as DependencyObject, 
                typeof(LabelPresenter), true) as LabelPresenter;

   if (lp == null)
   {
      var cvp = Utilities.GetAncestorFromType(e.OriginalSource as DependencyObject, 
                    typeof(CellValuePresenter), true) as CellValuePresenter;
      if (cvp != null)
      {
         cvp.Field.Settings.AllowResize = false;
      }
   }
   else
   {
      lp.Field.Settings.AllowResize = true;
   }
}

Displaying Hierarchical data in Infragistics XamDataGrid

Before I start this post, let me just say I’m using an old version of the Infragistics XamDataGrid for this post, version 10.3. Hence this may have been changed in subsequent releases, but as I have a legacy application to support, that’s the version we’re using.

I want to display hierarchical data within the grid, so let’s start with a sample view model.

public class EmployeeViewModel
{
   public EmployeeViewModel()
   {
      Manages = new ObservableCollection<EmployeeViewModel>();
   }

   public string Name { get; set; }
   public ObservableCollection<EmployeeViewModel> Manages { get; set; }
}

Note: I’ve not bothered with supporting the INotifyPropertyChanged interface etc. this is bare bones just to demonstrate the concepts

So the EmployeeViewModel represents a basic employee hierarchy. Here’s a factory that creates the same data, which should make the hierarchy quite obvious.

public static class EmployeeViewModelFactory
{
   public static ObservableCollection<EmployeeViewModel> Create()
   {
      var employees = new ObservableCollection<EmployeeViewModel>();

      var bob = new EmployeeViewModel {Name = "Bob"};
      var bill = new EmployeeViewModel { Name = "Bill" };
      var fred = new EmployeeViewModel { Name = "Fred" };
      var alfred = new EmployeeViewModel { Name = "Alfred" };
      var jim = new EmployeeViewModel { Name = "Jim" };
      var jeff = new EmployeeViewModel { Name = "Jeff" };
      var craig = new EmployeeViewModel { Name = "Craig" };

      bob.Manages.Add(bill);
      bob.Manages.Add(fred);

      alfred.Manages.Add(jim);

      jim.Manages.Add(jeff);

      employees.Add(bob);
      employees.Add(alfred);
      employees.Add(craig);

      return employees;
   }
}

Now it’s pretty easy to configure the XamDataGrid to handle this data, I’ll manually create the fields, so here’s the XAML

<igDP:XamDataGrid GroupByAreaLocation="None" DataSource="{Binding}">

   <igDP:XamDataGrid.FieldLayoutSettings>
      <igDP:FieldLayoutSettings ExpansionIndicatorDisplayMode="CheckOnDisplay"
                                          AutoGenerateFields="False"/>
   </igDP:XamDataGrid.FieldLayoutSettings>

   <igDP:XamDataGrid.FieldLayouts>
      <igDP:FieldLayout>
         <igDP:FieldLayout.Fields>
            <igDP:Field Name="Name" />
            <igDP:Field Name="Manages" Visibility="Hidden" />
         </igDP:FieldLayout.Fields>
      </igDP:FieldLayout>
   </igDP:XamDataGrid.FieldLayouts>

</igDP:XamDataGrid>

This will produce the following UI

XamDataGrid displaying hierarchial data

This is great, but I don’t really want the subheadings for this data and I’d prefer if the child columns lined up with the parents, more like a treeview and this is where things get a little tricky.

Removing the child item headers

To remove the headers we use

<igDP:FieldLayout.Settings>
   <igDP:FieldLayoutSettings LabelLocation="Hidden" />
</igDP:FieldLayout.Settings>

This code is placed within the FieldLayout section, but ofcourse if you do this in the one and only FieldLayout you’ll also lose the heading of the parent row.

So what you might try is to implement a second FieldLayout section as per

<igDP:XamDataGrid.FieldLayouts>
   <igDP:FieldLayout>
      <igDP:FieldLayout.Fields>
         <igDP:Field Name="Name" />
         <igDP:Field Name="Manages" Visibility="Hidden" />
      </igDP:FieldLayout.Fields>
  </igDP:FieldLayout>

  <igDP:FieldLayout>
     <igDP:FieldLayout.Settings>
        <igDP:FieldLayoutSettings LabelLocation="Hidden" />
     </igDP:FieldLayout.Settings>
     <igDP:FieldLayout.Fields>
        <igDP:Field Name="Name" />
        <igDP:Field Name="Manages" Visibility="Hidden" />
     </igDP:FieldLayout.Fields>
   </igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>

But this will not solve the problem and here’s the gotcha…

From my understanding the FieldLayout is based upon the the field names on your objects and hence as the EmployeeViewModel is used throughout only the first FieldLayout is ever applied to the view of the data. For example even if you changed all top level employees in the example to be of type ManagerViewModel (with this being an exact copy of EmployeeViewModel, and obviously fixing the code to handle this in the factory etc., you would still find only the first FieldLayout used. On the other hand if we had something like

public class ManagerViewModel
{
   public ManagerViewModel()
   {
      Manages = new ObservableCollection<EmployeeViewModel>();
   }

   public string Name { get; set; }
   public ObservableCollection<EmployeeViewModel> Manages { get; set; }
}

public class EmployeeViewModel
{
   public EmployeeViewModel()
   {
      DepartmentManages = new ObservableCollection<EmployeeViewModel>();
   }

   public string Name { get; set; }
   public ObservableCollection<EmployeeViewModel> DepartmentManages { get; set; }
}

public static class EmployeeViewModelFactory
{
   public static ObservableCollection<ManagerViewModel> Create()
   {
      var employees = new ObservableCollection<ManagerViewModel>();

      var bob = new ManagerViewModel { Name = "Bob" };
      var bill = new EmployeeViewModel { Name = "Bill" };
      var fred = new EmployeeViewModel { Name = "Fred" };
      var alfred = new ManagerViewModel { Name = "Alfred" };
      var jim = new EmployeeViewModel { Name = "Jim" };
      var jeff = new EmployeeViewModel { Name = "Jeff" };
      var craig = new ManagerViewModel { Name = "Craig" };

      bob.Manages.Add(bill);
      bob.Manages.Add(fred);

      alfred.Manages.Add(jim);

      jim.DepartmentManages.Add(jeff);

      employees.Add(bob);
      employees.Add(alfred);
      employees.Add(craig);

      return employees;
   }
}

and the XAML might look like

<igDP:XamDataGrid GroupByAreaLocation="None" DataSource="{Binding}">
   <igDP:XamDataGrid.FieldLayoutSettings>
      <igDP:FieldLayoutSettings ExpansionIndicatorDisplayMode="CheckOnDisplay"
                 AutoGenerateFields="False"/>
   </igDP:XamDataGrid.FieldLayoutSettings>
   <igDP:XamDataGrid.FieldLayouts>
   <igDP:FieldLayout>
      <igDP:FieldLayout.Fields>
         <igDP:Field Name="Name" />
         <igDP:Field Name="Manages" Visibility="Hidden" />
      </igDP:FieldLayout.Fields>
   </igDP:FieldLayout>

   <igDP:FieldLayout>
      <igDP:FieldLayout.Settings>
         <igDP:FieldLayoutSettings LabelLocation="Hidden" />
      </igDP:FieldLayout.Settings>
      <igDP:FieldLayout.Fields>
         <igDP:Field Name="Name" />
         <igDP:Field Name="DepartmentManages" Visibility="Hidden" />
      </igDP:FieldLayout.Fields>
   </igDP:FieldLayout>
   </igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>

We would now see the following

XamDatdGrid without subheadings

Not ideal, but we now know why EmployeeViewModel referencing itself in the Manages property fails to work. However we now have some more oddities, including the fact that the column lines are out of alignment. I’ll be writing another post on my attempts to resolve this.

References

Can I choose a field layout based on data Type?
FieldLayout Class

Comparing a generic to its default value

This is a very short post on something which, for some reason, I keep forgetting and having to find code I’ve written in the past to refresh my memory – so I thought I’d write a quick post to remind myself about this.

So you have a generic type T. Maybe it’s passed as an argument to a method and you want to check whether it’s set to its default value (in the case of classes this is equivalent to null).

To do this we use the following code (where o is the variable name of some generic type)

if(EqualityComparer<T>.Default.Equals(o, default(T))) 
{
   // do something
}

As previously stated – for class types, this is equivalent to null, for other types it depends upon their default value.

This code will handle structs and classes as well as Nullable types and also avoids boxing (see C# 5.0 in a Nutshell).

Starting out with RavenDB

I’m just starting to try out RavenDB, so thought I’d write a quick post highlighting basic CRUD operations using it.

RavenDB can be run as both a server or locally as an embedded datastore. For these examples we’ll use the server run via the command prompt. At the end of the post I will demonstrate what’s needed to get the Embeddable version up and running.

Let’s get the server and get an instance of RavenDB up and running

  • Download the current release of RavenDB from here
  • I downloaded the zip – so either unzip then go to the RavenDB\Server folder or find this folder where the installation place the RavenDB app.
  • Run Raven.Server.exe from the Server folder. If all went well you should have an instance of the RavenDB server running, to check this, from your preferred browser navigate to http://http://localhost:8080/ and you should see the RavenDB studio
  • From the studio create a new database, for simplicity I named mine Test1

Creating a client

Before we get into the actual CRUD operations, let’s get a project up with the pre-requisites for the code to follow

  • Run Visual Studio and create a new console application
  • Using NuGet install the RavenDB Client package
  • Let’s create a simple Person object to store, here’s mine
    public enum Gender
    {
       Male,
       Female
    }
    
    public class Person
    {
       public string FirstName { get; set; }
       public string LastName { get; set; }
       public DateTime DateOfBirth { get; set; }
       public Gender Gender { get; set; } 
    }
    

Let’s create some data in our new database

Now let’s access the store and save a new Person object, something like this

var documentStore = new DocumentStore 
{ 
   Url = "http://localhost:8080/", 
   DefaultDatabase = "Test1"
};
documentStore.Initialize();

using (var session = documentStore.OpenSession())
{
   Person person = new Person
   {
      FirstName = "Scooby",
      LastName = "Doo",
      DateOfBirth = new DateTime(1969, 9, 13),
      Gender = Gender.Male
   };

   session.Store(person);
   session.SaveChanges();
}

Now if we look at the RavenDB studio and the Test1 database we should see two documents, one system document and a “People” document.

Time to retrieve an object from the database

Now let’s see how we get the object. Raven stored an Id “people/1” with my data, so we’ll use that key to get that document.

var documentStore = new DocumentStore 
{ 
   Url = "http://localhost:8080/", 
   DefaultDatabase = "Test1"
};
documentStore.Initialize();

using (var session = documentStore.OpenSession())
{
   Person person = session.Load<Person>("people/1");
   // do something with the person instance
}

Updating a document

To update a document we simply load the document, make the changes then save if, for example

var documentStore = new DocumentStore 
{ 
   Url = "http://localhost:8080/", 
   DefaultDatabase = "Test1"
};
documentStore.Initialize();

using (var session = documentStore.OpenSession())
{
   Person person = session.Load<Person>("people/1");
   person.DateOfBirth = new DateTime(1996, 9, 13);
   session.SaveChanges();
}

You’ll have noticed that key’s are automatically generated, if our Person object included an Id property, such as

public class Person
{
   public string Id { get; set; }
   /// other properties
}

then Raven will automatically fill in this field when you first save an object, with the id it assigned. Alternatively you can give you own Id for an object and this will become the Id within RavenDB also, i.e. you’re assigning your own id’s at this point.

Deleting a document

So we’ve created a document, retrieved it and updated it, so now it’s time to delete a document.

using (var session = documentStore.OpenSession())
{
   Person person = session.Load<Person>("123");
   session.Delete(person);
   session.SaveChanges();
}

In the above we’re deleting using an instance of an object but it might be we’ve not loaded the object and want to simply delete using the id, in which case can can do the following

using (var session = documentStore.OpenSession())
{
   session.Advanced.DocumentStore.DatabaseCommands.Delete("123", null);
   session.SaveChanges();
}

RavenDB as an embeddable datastore

The only changes we need from the server based code (shown above) is that we no longer use the DocumentStore object but instead use the EmbeddableDocumentStore, so let’s look at this and how we get it up and running in a client.

  • RavenDB Embedded 2.5.2916 shows the package we need to install from NuGet to use the EmbeddableDocumentStore however I was getting the error “Install-Package : Updating ‘System.Spatial 5.2.0’ to ‘System.Spatial 5.0.2’ failed. Unable to find a version of ‘RavenDB.Database’ that is compatible with ‘System.Spatial 5.0.
    2’.”. Instead use Install-Package RavenDB.Embedded -DependencyVersion Highest from the Package Manager Console.

So as mentioned, to use the embeddable version of RavenDB we use the EmbeddableDocumentStore object, thus

var documentStore = new EmbeddableDocumentStore
{
   DataDirectory = "Data"
};
documentStore.Initialize();

// everything else as per the DocumentStore code from earlier

The DataDirectory is the location of your local datastore, this case the folder Data will be created off of the directory you application is run from.