ctodx

This Blog

News

Favorite Posts

Archives

December 2009 - Posts

  • DevExpress Newsletter 17: Message from the CTO

    Dual-posting Julian's Message from the CTO from DX Press, the DevExpress Newsletter, issue 17.

    Standards

    This particular rental car I’m driving this trip is … annoying. It’s nippy enough and can hold me and my luggage with room to spare, but every time I indicate to overtake or to change direction, I turn on the windshield wipers. You see, my own car has the indicators on the left stalk and this car has them on the right. Heaven knows what the other drivers must think as my wipers frantically swish across the windshield as I turn into a side street.

    It’s all a matter of standards. Manufacturers have agreed on the placement of the pedals in a car, but the rest is a mishmash. We have standards too in software: not only in the user interface, but also in “hidden” areas like XML, communication protocols, file formats, and the like. When we write software, it’s as if half of our design decisions have been made for us already. And note I am not knocking this situation, far from it. I still remember that Esc brought up the menu in Word for DOS.

    But sometimes, we implement something that is brand new. It behooves us, in that case, to try and make decisions that could frame some new standards so that other people can follow them (and thereby ensuring their “standardness”). At DevExpress, we encounter this type of scenario relatively often: every new control design that hasn’t been implemented elsewhere will need us to decide on mouse usage, keyboard shortcuts, text placement, and so forth, although we can leverage other standards for things like icons and similar.

    So, embrace standards, for without them, your work would be that much harder to design and complete.

  • Summit and Roadmap news

    RoadmapWe've reached lunchtime on the last day of our DevExpress Summit. The Summit is the occasion where, every year, around the New Year, the management, team leads, and evangelists all meet together, listen and watch each other's presentations about where we are, and where we'd like to be, and discuss the proposed features and enhancements for the upcoming year. Of course, this time around, it's all about the Roadmap for 2010.

    When we published the 2009 Roadmap, we decided to experiment and release it as a series of blog posts as and when we decided on a particular platform or product suite. Although this meant that customers got the news hot off the presses before the ink had even dried, it did have the consequence that a couple of months into the year, finding the "Roadmap" turned into a longwinded exercise in searching the Community site. We received a lot of feedback throughout 2009 that this was, shall we say, not the most stellar decision we ever made. Well, you've got to experiment to improve things and you've got to move on if the experiment fails.

    This year then, we are reverting to the "old" style of publishing a roadmap: posting a single web page on our main site. That's why we've been mostly silent through the whole week — at least compared with last January.

    My job Monday is to amalgamate all my notes and all the Powerpoint slidedecks into a coherent narrative that describes the whole DevExpress Roadmap for 2010. I'll be circulating it to everyone here so that they can check their sections (heaven forbid I add something on the sly Wink), and so it should be published Wednesday or Thursday. Of course, I'll announce it here, and it will be prominently displayed on the home page.

  • Naming anonymous types

    I've been futzing around recently trying to get a grip on LINQ to XML for an internal project. Yeah, I know, everyone else has moved on having solved that particular problem a couple of years ago, but for some reason, although I understood the concepts and the infrastructure behind LINQ, I'd never really coded anything. Until this afternoon, that is.

    Not one to take little nibbles, I decided to attack iTunes Music Library.xml.

    If you've never taken a peek at this file, here's the header plus the top two tracks from mine:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
      <dict>
        <key>Major Version</key><integer>1</integer>
        <key>Minor Version</key><integer>1</integer>
        <key>Application Version</key><string>9.0.2</string>
        <key>Features</key><integer>5</integer>
        <key>Show Content Ratings</key><true/>
        <key>Music Folder</key><string>file://localhost/O:/My%20Music/iTunes/iTunes%20Music/</string>
        <key>Library Persistent ID</key><string>277AA4870A436D01</string>
        <key>Tracks</key>
        <dict>
          <key>1656</key>
          <dict>
            <key>Track ID</key><integer>1656</integer>
            <key>Name</key><string>Voices</string>
            <key>Artist</key><string>Vangelis</string>
            <key>Album Artist</key><string>Vangelis</string>
            <key>Composer</key><string>Vangelis</string>key>Album</key><string>Voices</string> 
            <key>Genre</key><string>Electronica</string>
            <key>Kind</key><string>MPEG audio file</string>
            <key>Size</key><integer>5064895</integer>
            <key>Total Time</key><integer>421381</integer>
            <key>Track Number</key><integer>1</integer>
            <key>Year</key><integer>1995</integer>
            <key>Date Modified</key><date>2008-02-09T21:05:38Z</date>
            <key>Date Added</key><date>2005-05-06T23:21:31Z</date>
            <key>Bit Rate</key><integer>96</integer>
            <key>Sample Rate</key><integer>44100</integer>
            <key>Play Count</key><integer>10</integer> 
            <key>Play Date</key><integer>3327338137</integer>
            <key>Play Date UTC</key><date>2009-06-09T03:35:37Z</date>
            <key>Artwork Count</key><integer>1</integer>
            <key>Persistent ID</key><string>277AA4870A436D0E</string>
            <key>Track Type</key><string>File</string>
            <key>Location</key><string>file://localhost/M:/My%20Music/Vangelis/Voices/01%20-%20Voices.mp3</string>
            <key>File Folder Count</key><integer>-1</integer>
            <key>Library Folder Count</key><integer>-1</integer>
          </dict>
          <key>1658</key>
          <dict>
            <key>Track ID</key><integer>1658</integer>
            <key>Name</key><string>Echoes</string>
            <key>Artist</key><string>Vangelis</string>
            <key>Album Artist</key><string>Vangelis</string>
            <key>Composer</key><string>Vangelis</string>
            <key>Album</key><string>Voices</string>
            <key>Genre</key><string>Electronica</string>
            <key>Kind</key><string>MPEG audio file</string>
            <key>Size</key><integer>6067997</integer>
            <key>Total Time</key><integer>504973</integer>
            <key>Track Number</key><integer>2</integer>
            <key>Year</key><integer>1995</integer>
            <key>Date Modified</key><date>2008-02-09T21:05:38Z</date>
            <key>Date Added</key><date>2005-05-06T23:21:31Z</date>
            <key>Bit Rate</key><integer>96</integer>
            <key>Sample Rate</key><integer>44100</integer>
            <key>Play Count</key><integer>9</integer>
            <key>Play Date</key><integer>3330797581</integer>
            <key>Play Date UTC</key><date>2009-07-19T04:33:01Z</date>
            <key>Artwork Count</key><integer>1</integer>
            <key>Persistent ID</key><string>277AA4870A436D0F</string>
            <key>Track Type</key><string>File</string>
            <key>Location</key><string>file://localhost/M:/My%20Music/Vangelis/Voices/02%20-%20Echoes.mp3</string>
            <key>File Folder Count</key><integer>-1</integer>
            <key>Library Folder Count</key><integer>-1</integer>
          </dict>
    

    Yep, it's a mess. The design is not brilliant: I was expecting to see major elements like "song", and inner elements called "Album" and "Artist" and the like. But no: it's a dictionary with every entry in the dictionary defined as a key-value pair or as another dictionary entry. Bleugh.

    I almost tossed the idea, but then decided to do a quick search to see if anyone had figured out what to do. I came across this article by Joshua Allen in his Better Living Through Software blog. In it he described a method using LINQ that transformed the iTunes file into what I might call more normal-looking XML. So I took that idea and ran with it. Essentially I wanted a method that would return a LINQ result set containing cleaned up data, with POCOs (plain old C# objects) and no XML. (In essence, I was going to feed this directly into one of our grids through its DataSource property.)

    First I loaded the XML file into an XDocument:

    XDocument iTunes = XDocument.Load(@"O:\My Music\iTunes\iTunes Music Library.xml");

    Then I applied Joshua's LINQ expression to convert the iTunes XML into something more palatable:

    var rawtracks = from track in iTunes.Descendants("plist").Descendants("dict").Descendants("dict").Descendants("dict")
                    select new XElement("track",
                        from key in track.Descendants("key")
                        select new XElement(
                           ((string)key).Replace(" ", ""),
                           (string)(XElement)key.NextNode)
                        );

    Remember, until I enumerate the data from the result set, the only thing that happens with this code is that an expression tree is built. No data has been harmed yet! The result set from this is a list of XElements each of whose names is the contents of the <key> element, and whose content is the node that follows the <key> node.

    Next, I want to select the actual tracks from this result set (by "actual" I mean that there is an MP3 file behind the track: for some reason the original XML file has a lot of empty entries) and in doing so I want to create an enumerable list of POCOs. First a couple of helper methods:

        private static string XElementToString(XElement e, string defaultValue) {
          if (e == null) 
            return defaultValue;
          return e.Value;
        }
    
        private static TimeSpan XElementToTimeSpan(XElement e, TimeSpan defaultValue) {
          if (e == null)
            return defaultValue;
          return new TimeSpan(Int64.Parse(e.Value) * 10000);
        }

    And then the LINQ statement:

    var tracks = from track in rawtracks
                 where track.Element("Location") != null
                 select new {
                   Artist = XElementToString(track.Element("Artist"), string.Empty),
                   Album = XElementToString(track.Element("Album"), string.Empty),
                   Name = XElementToString(track.Element("Name"), string.Empty),
                   Time = XElementToTimeSpan(track.Element("TotalTime"), defaultTime)
                 };

    OK, a quick explanation is in order. For each track in the result set I'm creating a new anonymous object with four properties: Artist, Album, Name, and Time. The tracks result set is what I need to attach to the DataSource property of my grid. (Again, executing this last statement does not do anything with the data: that only happens when the grid enumerates the result set.)

    Now, this works very well for my original requirement, but there is a big problem for any other code. If I wrap this up in a method to return the tracks object, what type does the method return? It's an IEnumerable<T>, but what is T? The only thing it can return is object, which is not exactly informative at code time.

    Enter the Name Anonymous Type refactoring from Refactor! Pro. Place the caret on the new keyword (it's easier to hit than the opening brace, which is another activation site), press the refactor key, and select Name Anonymous Type. After renaming the default name, you will get this for the LINQ statement:

    var tracks = from track in rawtracks
                 where track.Element("Location") != null
                 select new Track(
                   XElementToString(track.Element("Artist"), string.Empty), 
                   XElementToString(track.Element("Album"), string.Empty), 
                   XElementToString(track.Element("Name"), string.Empty), 
                   XElementToTimeSpan(track.Element("TotalTime"), defaultTime));

    It's created a new type called Track and news up another object of this type for every track found in the result set. And what does Track look like?

        [DebuggerDisplay("\\{ Artist = {Artist}, Album = {Album}, Name = {Name}, Time = {Time} \\}")]
        private sealed class Track : IEquatable<Track> {
          private readonly string artist;
          private readonly string album;
          private readonly string name;
          private readonly TimeSpan time;
    
          public Track(string artist, string album, string name, TimeSpan time) {
            this.artist = artist;
            this.album = album;
            this.name = name;
            this.time = time;
          }
    
          public override bool Equals(object obj) {
            if (obj is Track)
              return Equals((Track)obj);
            return false;
          }
    
          public bool Equals(Track obj) {
            if (obj == null)
              return false;
            if (!EqualityComparer<string>.Default.Equals(artist, obj.artist))
              return false;
            if (!EqualityComparer<string>.Default.Equals(album, obj.album))
              return false;
            if (!EqualityComparer<string>.Default.Equals(name, obj.name))
              return false;
            if (!EqualityComparer<TimeSpan>.Default.Equals(time, obj.time))
              return false;
            return true;
          }
    
          public override int GetHashCode() {
            int hash = 0;
            hash ^= EqualityComparer<string>.Default.GetHashCode(artist);
            hash ^= EqualityComparer<string>.Default.GetHashCode(album);
            hash ^= EqualityComparer<string>.Default.GetHashCode(name);
            hash ^= EqualityComparer<TimeSpan>.Default.GetHashCode(time);
            return hash;
          }
    
          public override string ToString() {
            return String.Format("{{ Artist = {0}, Album = {1}, Name = {2}, Time = {3} }}", artist, album, name, time);
          }
    
          public string Artist {
            get { return artist; }
          }
          public string Album {
            get { return album; }
          }
          public string Name {
            get { return name; }
          }
          public TimeSpan Time {
            get { return time; }
          }
        }

    This class is not just a bag o' properties either. Just look at what the refactoring decorates it with:

    • A DebuggerDisplay attribute for use when displaying objects of this type with the debugger. Nice.
    • The IEquatable<T> interface. Objects of this class can be compared equal with the semantics and meaning you expect.
    • Readonly fields. The class is readonly from the start.
    • The Equals() methods to fulfill the IEquatable<T> contract
    • A meaningful GetHashCode() implementation that uses all the class' properties.
    • A good ToString() method that shows all the property values.

    (I'll note that this declaration is pretty much identical to what the C# compiler builds for you when it compiles an anonymous type.)

    Of course, now the code for the class has been created, I can alter it in any way I see fit. For example, I can change properties to be writable, I can reduce the influence of the GetHashCode() method by removing some of the field values, and so on.

    But most of all I can change the return type of my putative method to IEnumerable<Track> and move on. Just brilliant.

  • DevExpress Newsletter 16: Message from the CTO

    Reprinting my Message from the CTO from the sixteenth newsletter so that you may comment on my thoughts.

    Virtualizing your experiments

    At PDC this year, I wanted to show off some of the new things we have in v2009.3, but at the time I was setting up my laptop there was only a beta available. Rush-released that very morning, in fact, so I wasn't hopeful it would last beyond the day. No matter, no problem, I just installed it in a virtual machine.

    Virtual machines have changed the lives of developers everywhere. Need a pristine PC? Just clone the pristine virtual machine you have ready for that purpose (mine has Windows 7 plus Visual Studio 2008 freshly installed, all updated) and boot it. Need to test some alpha build and you're worried about it affecting your usual environment? Boot up another clone. Once you're done with your experiments, you can just throw the virtual machine away.

    Back in the day, I used to have a spare physical PC and a copy of Norton Ghost. I'd create a pristine OS install, Ghost it, and then play merry havoc on the PC with dodgy software. Once I'd done, I'd reimage the pristine install from the Ghost image. But, compared with a VM, it took a long time to get to the "good" state.

    Since there are several hypervisors around, you have no excuse. I use VMWare Workstation, but there's also Virtual PC and VirtualBox, both free, and on the Mac there's Parallels and VMWare Fusion. Once you have a hypervisor, all that's required is plenty of disk space to hold the virtual disks (these will be 20GB or so in size).

    So protect yourself with a VM and experiment with abandon.

    Actually, virtualization is slowly being accepted by the non-programming masses too. For example, with the top-of-the-range Windows 7 package, you can download a specialized virtualization app that only works with Windows XP. I wonder how soon before all we do is run software in virtual machines, and our host OS is just a hypervisor.

  • Sneak Peek: Refreshing UI in XtraReports v2009.3

    A few weeks ago I published a blog post about the new report designer in our reporting product. This is due to be released very soon and, to be honest, for such a great feature, my post left a little to be desired in terms of details. Alex, who writes for the XtraCharts and XtraReports teams, thought it would be a good idea for me to interview members of the development team and get them to reveal more about the new XtraReports.

    KonstantinK For this third and final article, he hooked me up with Konstantin Kosukhin, a.k.a. Konstantin K in the forums. Konstantin rocked my world with the new look-and-feel for the designer: he and I are both fans of Mark Miller's series of posts and talks on The Science of Great UI. This time I'll show lots of before and after pictures: he's done a cracking good job in refreshing the UI. (Yes, I am most definitely punning in the title to this post.) Since this is the last article in this small series, I'd also just like to say thanks to Alex (who likes to be known as Alan in the forums) for setting all this up and the work he did in making sure the interviews came off well.

    (Note: Although all screenshots in this article are from the End-User Report Designer, the Visual Studio report designer has all the same enhancements. The only exception is the toolbox and the property grid, which are native VS elements, only available at VS design time).

    JMB: What gave you the idea to revamp the designer UI?

    Konstantin: Easy. Just look at the designer in v2009.2 and earlier:

    Designer in 9.2 

    It's functional in that you can design reports with it, but just look at that visual noise. It looks heavy. It's hard to tell what visual objects are part of the report and what are part of the designer. It violates a lot of the guidance that Mark Miller laid down in his Science of Great UI blog posts.

    So, we decided to make the designer's appearance more lightweight and not beat you over the head with it. So we redid some design work and produced this:

    Designer in 9.3

    I'm sure everyone will agree it looks more restful, and it has not lost any of the functionality.

    JMB: Very nice, indeed. I like the attention you paid to reducing contrast and your better use of color. The visual noise is way down. So, take us through the changes to get to this new version.

    Konstantin: First up for the chop were the grid lines. Grid lines are shown on the report designer surface to make it easier to move and resize report elements. Previously, we used dotted lines but there was no difference between the major and minor lines. All grid lines were the same. Also, another problem was that the grid line increment was measured in pixels, which was most inconvenient for designing reports that were measured in inches or centimeters.

    GridLines 9.2

    We threw away the dotted lines and replaces them with thin low-contrast silver grid lines, with the major subdivision lines being slightly darker than the minor ones. The grid line increment is now measured in the units for the current report.

    GridLines 9.3

    Of course, the new snap lines feature blends in with this new grid very nicely.

    JMB: Totally agree. The dotted grid was very 90s and this new version is much better. More modern. What else?

    Konstantin: Next on the list was what a selected control looked like. Previously, when a report control was selected, it was drawn with a rather noisy selection rectangle.

    Selection_92

    We wiped that design to replace it with a very lightweight selection box, with judicious use of some slight bluish color.

    Selection 9.3

    Already you can see that the high-contrast color of the control contents stands out from the infrastructure of the designer. Look back at the previous image: the dots were merging with the text to produce a very jagged look.

    JMB: I'm liking this new designer more and more. Looking back at the overall picture I see you also altered the report band strips.

    Konstantin: Exactly. In the previous version, the strips that indicated the different report bands were brightly colored and used a saturated color as well. They really dragged your focus away from the report controls, arguably the most important elements on the designer surface, to something that was in essence a set of dividing lines.

    BandStrips 9.2

    I mean talk about visual noise: all you can see on the designer surface is band strips. And just look at the jaggies because of the dotted grid lines. For v2009.3 we turned the contrast and color of the strips way down. Just enough to indicate a division, and the name of that division:

    BandStrips 9.3

    JMB: I think you just saved me from a headache.

    Konstantin: The final change we made to the designer surface was how we treated the margins of the report. We were way too literal about margins in v2009.2 and earlier:

    ReportMargins_92

    I mean, after all, the designer surface is supposed to represent a sheet of paper. To emphasize that similarity, we decided to draw indents from the left and top rulers, show the left report margin on the screen, along with making the top and bottom margins always visible on the report designer surface (previously, only the right report margin was visible). This makes it way easier for a user to change the report margins when designing a report without having to resort to using the Property Grid. Just drag and drop.

    ReportMargins 9.3

    JMB: I like it. I completely agree that being able to drag the margins to resize them is a much better user experience than having to search for and then select the right property in the Property Grid. I also notice you changed the toolbox.

    Konstantin: Yes, this was the final major change we made. We decided that the dock panel we had been using as the toolbox was too heavy for regular use, and felt a simpler bar would be better.

    On the fourth day of DevExpress Christmas, Julian gave me slick new 3D charts for XtraCharts First, it allows us to provide more space for the designer surface, that is, the user's report. Second, it means that someone who writes reports often is not distracted by the icon plus text of the old dock panel but can still locate controls very quickly. For the user who is just starting out with the designer, the names of the toolbox items are displayed in tooltips, and in the previous version (v2009.2) we'd already made the toolbox icons larger. These larger icons are already more recognizable and it becomes easier and quicker to learn which items correspond to which icons.

    Toolbox

    If someone prefers the older dock panel, you can create a custom end-user designer form with the help of the XRDesignDockManager component.

    JMB: I concur. As an example, I use Adobe Illustrator infrequently but regularly, and I certainly don't need the text to go along with the toolbox icons. I've learned enough about the environment that I'm familiar with the 20% I use and I know I can always hover to get the tooltips to learn about the other 80%.

    Anyway, Konstantin, thank you for taking the time to explain about the new refreshing look of the XtraReports report designer. I think we'll have to migrate some of your changes over to our other control designers...

    Konstantin: No problem, I'll help out for sure. And it was fun to explain what we've done for v2009.3.

LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, ASP.NET, WinForms, HTML5 or Windows 10, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2017 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners