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

Wednesday, September 28, 2011

I was shocked

.. and then I laughed

Mosaic?

.. wasn't that the first browser?

I must admit that back in the old days when cows still walked barefoot in the meadows I was browsing the Internet using the Mosaic browser. That was a very, very log time ago.

But this blogpost has nothing to do wth that.

Blogger views

Blogger (which hosts this blog) added a dynamic view mode that allows the user to view a blog in a number of different ways. One them is "mosaic" that I think can provide in a snapshot what a blog is all about.

Blogger is just one example of how Google breathe new life into a product. They have done this by "rotating" it to a new team and it's great to see that they keep innovating on a product that's been around for so long.

Here's the link to see this blog in the mosaic view: http://roho2003.blogspot.com/view/mosaic and you can change it to one of the other dynamic views with the menu in the upper left.

Enjoy!

Friday, July 01, 2011

A multimedia super machine

.. what would that be today?

Today the multimedia business is rapidly (r)evolving with ever more possibilities and more machines are coming together. Telephone, Internet and TV are converging with more and more functionalities coming in as well.
I find it difficult to keep up to date, but still I try. Every month, it seems, some new gadget comes onto the market that has some completely new amazing function or a better way to do something an other device already did. It's hard to keep up with it.
My multimedia center is not yet complete
Although my multimedia machinery is relatively complete I still miss some functionality. I can not playback all the movies I hold on my NAS (DLNA enabled) and I can not record TV shows. Or time shifting. I can do some browsing on my TV and on my Wii but neither is great.
So there are somethings that I would want to add to my setup. But I only want to add a single machine that holds all possible options I can think of. At least that I can think off today.
If only I could think of all the options available today.
And that's where you come in
Since it is impossible for me to compile the list of maybe all the options that are out there at this moment I have decided to crowdsource this task. I have shared a Google Docs document that you can freely edit.
Please bear in mind that I don't want the machine to make coffee, do the laundry and walk the dog. So please only realistic requirements for the machine.
So if you want to help me find the requirements for my next Multimedia Super Machine just click the link and add to the document.
Many thanks in advance!

The results so far
Have a lookie!

Scroll about :-) or just click the link and have better view and maybe even add or correct.
Enhanced by Zemanta

Thursday, June 16, 2011

Using simple jQuery to detect changes

.. a quick solution

Updated
Today I had a small task assigned to me to prevent a user from navigating away from a web form when there are unsaved changes. As it was a rather extensive form (and old code) I had the feeling that it could be quite an extensive task.
Yes, I know this is more of a beginner's level blogpost, but maybe someone can use it. Otherwise burn me down to the ground in the comments.
General idea
A simple solution came to mind:
  1. do this on the client
  2. in comes JavaScript and yep, here comes jQuery!
  3. have a page wide boolean isDirty that starts as 'false'
  4. when an input control is changed, make isDirty 'true'
  5. when user tries to leave the page, check the value of isDirty
  6. when isDirty is 'true' show confirmation dialog
  7. if the user cancels stay on the page
  8. if the user ignores then proceed onwards
All rather simple.
Let's implement this!
Steps 1 and 2 are easy to take. We will just include the jQuery library in the page and start a block of JavaScript.
Also simple is step 3.

var isDirty = false;

Step 6,7 and 8 can be wrapped in a simple function:
function IsPageDirty() {
   if (isDirty) {
      var ignoreChanges = confirm("Unsaved changes. Proceed anyway?");
         return (ignoreChanges);
   }
   return true;
}

That was easy, as some would say.
Now the final leaps
Then I decided that it would be simple a matter of using a selector in jQuery and add a function to the change function that sets the isDirty boolean to 'true'.
I opted for a class 'trackChanges'.
This is the little snippet that I produced.
$(".trackchange").change(function() {
   isDirty = true;
});

Similarly I chose a class of 'moveaway' for those links that can lead the user away from the current page. These links should get a onclick attribute returning the the result from the IsPageDirty() function described above.
$(".moveaway").attr("onclick", "return IsPageDirty();");
And then there was a lot of work ...
Since I then had to go through the entire form. It was an aspx page and there User Controls (ascx) being loaded and I had to go through thse as well. Not very pleasant.
I got myself another cup of coffee and had a good thought. Then I decided to just select all input controls and not just certain marked with a class.
Changed the code to the following.
$("input, select").change(function() {
   isDirty = true;
});
Same was true for all the links. I opted for selecting all anchors.

$("a").attr("onclick", "return IsPageDirty();");

Then I had a huge problem, because the saving was also done using a link. Before the saving would start the user gets a warning that there are unsaved changes. A bit confusing to say the least.
I didn't want to go back to putting a class on all the other links just to get around this. I wanted filter out some of the anchors by putting a class on these.
Luckily, jQuery has a function .not() that takes a selector and filters out these from the selection it is applied to. So I put a class 'always' on the anchors that should not have the IsPageDirty() check and changed the line to:

$("a").not(".always").attr("onclick", "return IsPageDirty();");
And that was it! A very simple piece of JavaScript code and hardly any changes to the page itself. I only had to add a class to some of the links.
Bringing it all together
Even if it is a very simple scenario and the code is probably not as compact as it can be, I think it shows once again how versatile JavaScript is in combination with library like jQuery.
So to get all together

<script type="text/javascript">
   var isDirty = false;

   $(document).ready(function() {
      isDirty = false;
      $("input, select").change(function() {
         isDirty = true;
      });
      $("a").not(".always").attr("onclick", "return IsPageDirty();");
      // a with class "always" is not checked
   });

   function IsPageDirty() {
      if (isDirty) {
         var ignoreChanges = confirm("Unsaved changes. Proceed anyway?");
            return (ignoreChanges);
      }
      return true;
   }
</script>

Not earth shocking
I know, I know, but still I think it could be of some help for others somewhere on the web. And maybe to help me remember this for future reference.

Small update
The above solution worked on almost all of the pages, but one of the pages had some ASP.NET AJAX stuff on it, where dropdownlist were filled after the initial pageload. This caused some nasty problems, because these made the
change()
function go off and set the
isDirty
variable to true.
Some scratching of the head and then some Googling made find the
live()
function to be my saviour in this.

<script type="text/javascript">
   var isDirty = false;

   $(document).ready(function() {
      isDirty = false;
      $("input, select").not(".ignore").live("change", (function() {
         // control with class "ignore" does not fire the isDirty flag
         isDirty = true;
      });
      $("a").not(".always").attr("onclick", "return IsPageDirty();");
      // a with class "always" is not checked
   });

   function IsPageDirty() {
      if (isDirty) {
         var ignoreChanges = confirm("Unsaved changes. Proceed anyway?");
            return (ignoreChanges);
      }
      return true;
   }
</script>

A reader with a good eye also notices the
.not(".ignore")
function I added to exclude some controls from firing the isDirty flag. I used this class on some input controls used for search.

That's it for now!
Enhanced by Zemanta

Wednesday, June 08, 2011

Connecting Visual Studio 2008 to TFS 2010

.. a note to self

And amidst all new versions of Visual Studio and Team Foundation Server I find myself at a client where there is a mixed codebase. Some older stuff created in VS2008 and even VS2005 and now working in VS2010. Luckily, they have their code on TFS 2010. For each version of Visual Studio code there is a collection aptly named VS2005 et cetera.
But then I struck the small challenge of actually getting to older code. I could connect to the collections using VS2010 and get latest from there and then I could open it using the correct version, but I completely lost the ability do any real source control.
I could not simply connect the older Visual Studio versions to TFS2010.

Now what?

Luckily this is something the people at Microsoft have anticipated. There are Forward Compatiblity Updates for older versions of Visual Studio:

Almost there

When connecting to TFS2010 from older Visual Studio versions wen need to give a little more data than just the server name. The complete path to the collection is needed. You will get a TF31002 error when you only provide the server name.
In the Add Team Foundation Server dialog you will need to enter the following format in the Team Foundation Server name textbox:

  •  http://{tfsserver}:{port}/{virtualdir}/{projectCollection}

In my situation I came to the following result.

And another trick learned
I hope this helps someone else as well. At least it is a note to self.
Enhanced by Zemanta

Thursday, April 28, 2011

Barefoot running

barefoot running(for this my feet are too sensitive)
Image via Wikipedia

.. it is something completely different

For some of you this will seem like a completely misplaced post. It is not about developing or the Internet or Google but about running.
In my sparse sparetime I like to do some running. I have done that since my days in University on and off with more or less intensisty. Still haven't run the obligatory marathon yet, but two Half Marathon should count as one, I guess.
Traditional running
I have always tried to run on a good pair of running shoes and replace these every 300++ Km to make sure I have sufficient damping. Not doing so always made my muscles hurt and led to small but painful injuries to my knees.
Ever since working for Nike I run on Nike shoes as these suit me very well. It's been a couple of years since I left that company but still the various Nike Air dampened shoes do their job wonderfully.

Time and shoes are changing
Lately new ideas about running shoes are getting more and more mainstream. The fully comfortable running shoe now has a version which more close to nature. The idea being that the foot loses its natural stability and muscle power since it is comfortably supported and strapped in inside a sturdy and soft and easy dampened shoe.
Natural running is closer to the way many of the African athletes train: on their bare feet.
Natural running supposedly makes your feet muscles stronger and also changes your way of running and ideally improving it.
When I was in the Nike Headquarters in Beaverton, Oregon in 2003 I was shown one of the prototypes of the Nike Free shoe. The Nike Free shoes have a very flexible outer sole and the top of the shoe gives less rigid support. You can role them up easily.
But still they provide quite a bit of damping.
The Vibram Fivefingers are a whole step further. They are completely minimalistic. No damping and the upper part of the shoes is as light as possible. To provide even more freedom through the use of five separate toe compartments.
The outer sole is, as can be expected from Vibram, a good wear and tear resistant rubber giving good grip but also minimal damping. With that the Vibram Fivefingers come much closer to true barefoot running than the Nike Free concept.
Being a bit of a geek
Yep, as the geek I am, I was interested in both the Nike Free concept and later on also the true "barefoot running" idea. I wanted to try them sometime. But it never really happened.
Until I saw a tweet come by on my Twitter timeline. It was one of those "retweet this and you have chance to win ...". This time you could win a pair of Vibram Fivefingers. Not really expecting to win anything I did retweet and went on with my work.
Want to win FiveFingers yourself?

Easy!  Go over to this page and hope for the best!

A few hours later I found myself exchanging my shoesize and address to receive a pair of Vibram Bakilas!

 First impressions
Now I have them for a couple of weeks and I have been running them on ever longer stretches. I must say that I am completely amazed by them.
It takes some getting used to and I am still trying to find a new perfect style of running. But I think that is something positive. On my normal luxury running shoes I never really think about the way I run, I just put one foot in front of the other. I do pay some attention to my arms and upper body, but that's it. Most of the running is done without thinking about it. In this respect barefoot running makes me thinking about the way I walk.
The other part that really amazes me that I do not feel anything special in my muscles. I had expected that without the shock absorbing sole I would feel my muscles and knees. Whenever the absorbing qualities of my normal shoes would drop these are the first things I notice.
But even with close to no shock absorbing there are repercussions. This amazes me still as I gradually build of the length and intensity of my runs.
I still run on my Nike shoes, but I try to do every other run on my Vibrams.

Conclusion
I must say that this barefoot running on the Vibram Bakilas have more than amazed me. I am truly enthousiastic. As I have no Nike Free shoes to compare them to (hint hint) I can't comment on that.
At the very least the Vibrams have made think about my running and my posture and also they have once again breathed new life into my running adventures.
I love my Bakilas!
Finally
Some other things you can do with Vibram Fivefingers ;-)





Enhanced by Zemanta

QR codes

.. every blog should have one

Since it seems to be part of a hype I can not be left behind.

Enhanced by Zemanta

Tuesday, January 18, 2011

The module was expected to contain an assembly manifest

.. Could not load file or assembly 'Microsoft.ReportViewer.Common..'

In the project I am currently involved in we had created some nice reports and had these render directly as PDF file to the browser. Worked great on my machine and on my colleague's machine. And then deploying it to our test environment that sits on a shared hosting server it failed.
Could not load file or assembly 'Microsoft.ReportViewer.Common, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The module was expected to contain an assembly manifest.
Clearly a security issue that will easily go away when you put the necessary assemblies in the GAC. But with shared hosting that is probably not an option. The hosting party will allow that nor facilitate.
Monday Monday ~~~~~~~~~~~Image by Trish Hamme ≧◡≦ via Flickr
Trying to figure out a solution
It took some Googling about with not much result but then I found this webpage that explains a simple solution to the problem. And hey presto! It works! Thanks, Desirée Harris from OrcsWeb. You just made our day!
Enhanced by Zemanta