Monday, December 05, 2011

Creating a master-detail grid with Telerik ASP.NET MVC controls

.. with a twist

Right now I am part of a team that is building a big HR related application. Maybe it's better to call it an HR suite. When finished it can be used to do performance reviews, 360s, development plans and the like with this application. It will be big and the customer plans to conwuer the world wit it. Well, at least Europe.
Architecture
For this applications some choices were made with regards to the archictecture.
First decision: we use ASP.NET MVC3. Why? Well, main reason is that this is becoming some sort of standard way of developing web applications using .NET. Is it the best way? Is the easiest way? Is it the only way? No. No. No. It is one of the possible frameworks to use. And because we expect to get more people on board we pick a framework that more people use over own framework.
The second choice we use Entity Framework and with that the Code-First option. This was a first for us and it takes a bit getting used to not have to write any clever stored procedures.
Database access is separated from the web application and is handled through web services. Making it so much easier to later decide to share the load over more machines.
Finally, a decision that was already made by the client, we should Telerik Components were possible.
Getting back to the subject
Ok, let's get back to what this really about. Implementing a master-detail grid with a Telerik component.
So why a blog post on this? Telerik has an extensive demo site and has a demo on a master detail grid. Yes, they have, but we want to do it slightly different.
To be totally honest, we think that the standard way of ASP.NET MVC3 is in some repect a step back. Done the simple way it means that a lot of the logic is done in the aspx page. It comes close again to classic ASP. Mixing markup with C# code and ending up with not optimal code in both of these areas. We are great believers in Object Oriented approach and we seek to reuse as much code as possible and want to kae our controls as clever as it needs to be.
Beacuse the target market is Europe we have to cater for different languages and we have solved that problem in a base control and derive all controls from that.
In the aspx page, we only want to load a custom user control that we build up completely in code. No, we did not go down the Razor path.
So yes, we have private controls and we override the CreateChildControls() method. We do the binding in the OnPreRender() and finaly in the Render(writer) method we make sure the correct markup is being produced.
Brining in the Telerik controls
The above approach also means that we cannot use the Telerik components as private controls. It is not possible to instantiate the components. So in comes the Telerik.Web.Mvc.UI.Fluent namespace and a variety of Builders. With these builders it is relatively easy to build the component. When we have set all properties we want and bound the data we then use the method ToHtmlString() to get the markup produced by the component and assign that outcome to a LiteralControl. After that we let the rendering go on in the nromal way.
This way we can use our own OO-based control hierarchy and still use the powerful Telerik Components. We do not wish to completely build (and test) a sortable, pageable, editable grid control.
Now for the Master-Detail grid
In one of our Use Cases we needed a Master-Detail grid. No problem the Telerik Grid component supports that as shown in the demo.
The problems was how to get that done in our approach using the Builders. We did not find any real examples of this.
Telerik support team
So then we got into the normal horror of sending a question to the support desk. Normally it takes some days to get a reply and then you need a couple of messages to get them to understand your question and then explain why you want to have it done the way you are doing it. Before finnaly, you get into the struggle of getting an answer that really works.
But no wait!
The Telerik support is different!
They reply within 24 hours as advertised. I even had an answer with ten minutes on one of my questions.
And they quickly understood what I was trying to and gave me enough info to solve my problem with a workday. And being probably in different time zones I think that is a wondeful achievement! Many thanks for that!
As a big thank you I wil explain what the final solution was.
The problem
We want show a grid with all the departments of a company and we want to click open (expand) a department to show a list of it's employees.
Nothing too special.
The solution
Our data model is pretty simple:
Department
Properties
  • Guid DepartmentId
  • string Name
  • List Employees
Employee
Properties
  • Guid EmployeeId
  • string FirstName
  • string FirstName
Our control has a LiteralControl that will be the placeholder for the markup produced by the GridBuilders. Also there is a private field that holds a list of Departments with the corresponding Employees.
Let's start using the GriDBuilder to first create the Master part.
GridBuilder gridBuilder = HtmlHelperExtension.Telerik(viewPage.Html).Grid();
            gridBuilder.Name("MasterDetailGrid")
               .DataBinding(dataBinding => dataBinding
                   .Server())
               .BindTo(departments)
               .Columns(columns =>
               {
                   columns.Bound(d => d.Name).Title("Department");
               });
This is the normal way to use the GridBuilder to create a Telerik Grid.
The Detail part of the Grid is itself also a Grid, so we can use teh same approach.
GridBuilder employeeGridBuilder = HtmlHelperExtension.Telerik(viewPage.Html).Grid()
   .DataBinding(dataBinding => dataBinding
   .Server())
   .Name("Detail")
   .Columns(columns =>
   {
      columns.Bound(d => d.FirstName).Title("FirstName");
      columns.Bound(d => d.LastName).Title("LastName");
});
Note that I have not bound this Deatil Grid to anything yet. That is something we need to do as part of the master Grid. To do so we need the DetailView method of the Grid.
gridBuilder.DetailView(dep => dep.Template(d =>
   {
      employeeGridBuilder.BindTo(d.Employees);
   }));
This makes the Employee bound to the Employees of the Department that is in the row of the Master part of the Grid. Now, spit out the markup and put it in the LiteralControl.
literalControl.Text = gridBuilder.ToHtmlString();
But nothing shows up for the Employees. We ca expand a Department row and there is some white space, but no Employees.
The final step
Something inside my head said that I had to use ToHtmlString() on the detail Grid as well. But I could not find the correct syntax. So back to the Supprt Team at Telerik and an answer came very quick. It was simple as the following:
gridBuilder.DetailView(dep => dep.Template(d =>
   {
      employeeGridBuilder.BindTo(d.Employees);
      return employeeGridBuilder.ToHtmlString();
   }));
This makes the detail Grid appear and now we can move forward again full speed!
Thansk again!
Not only does Telerik produce great components, they can be used in various ways. Even the not so standard way we want to use them. Telerik also has a super Support Team is both fast and correct in their answers.
If you ever run ito a problem with Telerik components they will surely be able to help you out in no time!
Enhanced by Zemanta