Welcome to the Xceed Community | Help
Community Search  
More Search Options

DON'T PANIC!

  • Getting Started with Xceed DataGrid for Silverlight: Part 2, Making it Pretty

    In my first post in this series, Binding to a Data Source, I explained how to connect Xceed DataGrid for Silverlight to an OData data source. This post will explain how to configure the datagrid to get the most out of it and your data.

    As I said in the first post, loading data from a data source is quite easy; however, the initial result is probably not what most people would like since the data is displayed in its "raw" form. For example, if we take the data returned by the Netflix catalog, you would probably want to limit the number of decimals places for double-typed columns and display an actual image rather than its URL. You may also want to limit the number of columns that are displayed to only those that contain the pertinent information. And so on.

    So, first things first, let's reduce the number of columns that are displayed to only those that contain the information we want. There are 2 ways that this can be accomplished: the first being to set the Visible property to false for the columns that are not to be displayed, the second is to set the grid's AutoCreateColumns property to false and manually define the desired columns. The first option would be acceptable if only a couple of columns from a large list are to be hidden; however, the second option is preferrable if more columns are to be hidden than displayed. This is the option that we will use in this sample since we only want to display 6 of the columns.

    <sldg:DataGridControl x:Name="netflixGrid"
                          
    ItemsSource="{Binding Path=NetflixTitles}"
                         
    AutoCreateColumns="False">

       <!-- Because the AutoCreateColumns property was set to false, it is necessary to
            define the columns that will be displayed in the grid. If a custom content
            template is to be used, it is defined through a column's
            CellContentTemplate property. -->
       
    <sldg:DataGridControl.Columns>
         
    <sldg:Column FieldName="BoxArt"
                       
    Title=""
                      
    Width="100"/>
         
    <sldg:Column FieldName="ShortName"
                      
    Title="Title"
                      
    Width="200"/>
         
    <sldg:Column FieldName="Synopsis"
                      
    Width="600"/>
         
    <sldg:Column FieldName="ReleaseYear"
                      
    Title="Released"
                       
    Width="75"/>
          
    <sldg:Column FieldName="Rating"
                      
    Width="75"/>
         
    <sldg:Column FieldName="AverageRating"
                      
    Title="Average Rating"
                      
    Width="100"/>
      
    </sldg:DataGridControl.Columns>
    </sldg:DataGridControl>

    Now that we have limited the number of columns to only those we want, the next step is to decide how the content in those columns is displayed. In the columns that we decided to keep, there are 3 that could use a facelift. These are the BoxArt column, in which we could display an image rather than a URL; the Synopsis column, in which we could format the text to support the various HTML tags such has bold, italic, and hyperlinks (i.e., href); and the AverageRating column, in which we could display a "star-rating" rather than simply displaying a numeric value.

    Up first, the BoxArt column. But before we start, let's make a grocery list of what we will need.

    In order to change how the content of each cell in a column is displayed, you need to provide a DataTemplate to the column's CellContentTemplate property. Easy enough. So what will we need to change a URL into an actual image? For starters, we will need to create a new DataTemplate that uses an Image and whose Source property is bound to the LargeUrl field, which provides the URL. In some cases a title will not have a URL. When this happens, we want to display an image that indicates that no image is available for the title. In order to do this, we will create an IValueConverter that will return the "no image" image when one is not provided for the title, and use this converter in the binding that is applied to the Source property.

    <!-- The BoxArtImageSourceConverter is an IValueConverter that is used to
         display a custom image when one is not provided by the Netflix data. -->
    <local:BoxArtImageSourceConverter x:Key="boxArtImageSourceConverter" />

    <!-- The DataTemplate that will be used to display the box art. -->
    <DataTemplate x:Key="boxArtCellContentTemplate">
      
    <Image Source="{Binding Path=LargeUrl, Converter={StaticResource boxArtImageSourceConverter}}"
             
    Height="100"
             
    VerticalAlignment="Center"
             
    HorizontalAlignment="Center"/>
    </DataTemplate>

    The next DataTemplate we will create is one that will be used by the AverageRating column to display a star-rating rather than a value. Like the BoxArt column, we will need to create an IValueConverter that will take the value and return a star-rating instead. To keep things clean, the various data templates that can be returned by the RatingConverter are defined in the ImageResourceDictionary.xaml resource dictionary.

    <!-- The RatingConverter is an IValueConverter that is used to convert a value 
         into an image. In this case, it will display a numeric value as a star-rating system.

         The DataTemplates that are used are retrieved from the ImageResourceDictionary.xaml
         resource dictionary, which is included as a merged dictionary in the resources
         of the UserControl. -->
    <local:RatingConverter x:Key="ratingConverter"
                          
    FullTemplate="{StaticResource fullStarDataTemplate}"
                          
    HalfTemplate="{StaticResource halfStarDataTemplate}"
                           
    EmptyTemplate="{StaticResource noStarDataTemplate}"/>

    <!-- The DataTemplate that will be used to display the star ratings. -->
    <DataTemplate x:Key="ratingCellContentTemplate">
      
    <ItemsControl ItemsSource="{Binding Converter={StaticResource ratingConverter}}"
                    
    VerticalAlignment="Center"
                    
    HorizontalAlignment="Center"
                    
    ToolTipService.ToolTip="{Binding StringFormat=\{0:f1\}}">
         
    <ItemsControl.ItemsPanel>
            
    <ItemsPanelTemplate>
               
    <StackPanel Orientation="Horizontal" />
            
    </ItemsPanelTemplate>
         
    </ItemsControl.ItemsPanel>
       </ItemsControl>
    </DataTemplate>

    The last DataTemplate will be used by the Synopsis column to properly format the text that it displays according to the various HTML tags found throughout the text. This template required a little more work since some things that were required (*cough* FlowDocument *cough*) are missing from the Silverlight framework and needed to be created. Like the other 2 columns, an IValueConverter was also needed, except this one converts plain text to a Paragraph that contains the appropriate text and inlines (e.g., Hyperlink).

    <!-- The TextToParaConverter is an IValueConverter that is used to create
         a paragraph from plain text. It will also replace tags such as bold, italic,
         and href into the appropriate inline type (e.g., HyperLink)-->
    <local:TextToParaConverter x:Key="textToParaConverter"/>

    <!-- The DataTemplate that will be used to display the synopsis. The background
         has been set to Transparent to allow the default row highlighting to be apparent. -->
    <DataTemplate x:Key="textWrappingContentTemplate">
      
    <local:BindableRichTextBox BindableBlocks="{Binding Converter={StaticResource textToParaConverter}}"
                                 
    BorderThickness="0"
                                 
    Background="Transparent"/>
    </DataTemplate>

    Now that we have all these nifty data templates, simply assign them to CellContentTemplate property of the appropriate columns, and voilà! All data is now displayed in a more user-friendly and appropriate manner. Sprinkle a little sorting and grouping, and you are ready to go!

    <sldg:Column FieldName="BoxArt"
                 
    Title=""
                 
    Width="100"
                
    CellContentTemplate="{StaticResource boxArtCellContentTemplate}"/>

    <sldg:Column FieldName="Synopsis"
                
    Width="600"
                 
    CellContentTemplate="{StaticResource textWrappingContentTemplate}"/>

    <sldg:Column FieldName="AverageRating"
                
    Title="Average Rating"
                
    Width="100"
                 
    CellContentTemplate="{StaticResource ratingCellContentTemplate}"/>

     

    If you would like to download the sample to play around with it and have access to the custom classes that were created for it, you can do so HERE!

  • Getting Started with Xceed DataGrid for Silverlight: Part 1, Binding to a Data Source

    A while back Odi (@kosmatos) tweeted about how easy it was to bind Xceed DataGrid for Silverlight to an OData data source, and he was right. It is easy! So easy in fact that it wouldn't make for much of a blog post. So what I decided to do is show you how to connect to an OData data source as well as demonstrate how to set up the grid to get the most out of it and your data. Of course, you can swap an OData source for another one of your liking.

    Obviously, the first step is to connect to an OData data source to get some data. To do so, add a service reference to your Silverlight application, provide an address to an OData source, such as the Netflix catalog, and give it a more appropriate namespace name.

    Add Service Reference

    Now that the service reference is added to the project, let's add a property to the MainPage that will expose the DataServiceQuery that contains the Netflix titles, including the total count of all entities in the entity set. This is the property that the grid will bind to to get its data. Note that I have also set the DataContext of the page to itself to make my life easier.

    private NetflixCatalog m_catalog = null;
    public DataServiceQuery<Title> NetflixTitles
    {
      
    get
      
    {
         
    if( m_catalog == null )
             m_catalog =
    new NetflixCatalog( new Uri( "http://odata.netflix.com/Catalog", UriKind.Absolute ) );
       
          
    return m_catalog.Titles.IncludeTotalCount();
       }
    }

    So let's get to down to the nitty-gritty and add the datagrid to the page so that we can bind it to the Netflix data. Like most item-containing controls, the grid has an ItemsSource property through which we will bind to the NetflixTitles property. Because I set the data context of the main page to itself and the data context of the grid is the page, a simple "path binding" can be used. Something like this:

    <sldg:DataGridControl x:Name="netflixGrid"
                          
    ItemsSource="{Binding Path=NetflixTitles}"/>

    And that's it! In theory, there is nothing else that needs to be done. All the data will be loaded in the grid and can be grouped, sorted, filtered, and edited, but it won't be as pretty as it could be.

     

    So yeah, only a couple lines of code and XAML to get the grid to load data from an OData source, but there is more to displaying data than simply having it on the screen. You will most likely want your data to be displayed in an attractive and easy-to-understand manner for your end users. Not a problem! But I'll show you in my next post :)

    If you can't wait for the next post, you can always download the full sample and try it out for yourself.

    Stay tuned!

  • Data Virtualization: Load Smarter, Not Harder*

    So after a year and a half of development, Xceed DataGrid for Silverlight was officially released this week! Unlike Xceed DataGrid for WPF, where we were first to market, we are a little late coming into the Silverlight datagrid game; however, it will have been well worth your wait. We did not just want to make another Silverlight grid. We wanted to make the BEST Silverlight grid. So why is it so awesome? Simple. From the first line of code that was written, user experience and data virtualization were our top priorities. So how does it work? I'm glad you asked!

    The data-virtualization engine behind Xceed DataGrid for Silverlight is the invisible maestro that orchestrates all the behind-the-scenes data management. Although it is the cornerstone on which Xceed DataGrid for Silverlight was built, in most cases, you will never have to deal with it and will only know that it is working because everything is working so well. To get a little bit more technical, the data-virtualization engine is basically a layer of enumerators that each manipulate and process the data items that are displayed in the grid, all the while making sure that "extra" items are available, just in case. 

    Take a look at the image below and consider a simple operation, such as a line down (i.e., "Get(1)"):
     

     
    When the grid requires new items (1), the request is processed by the data-virtualization engine, which begins its initial pass by querying the caching enumerator to see if it has the data items that are needed (2). If the caching enumerator has the items, they are immediately returned and displayed in the grid (10). If the caching enumerator does not contain the required items, the request is sent to the async enumerator, which will create and immediately return "dummy" items (3) so that the grid remains responsive even though there is a pending request to retrieve new items. The request will then be queued along with any additional pending requests. The async enumerator is also responsible for optimizing the query that is sent to the subsequent enumerators. For example, if three line down operations are queued, the async enumerator will condense these pending operations into a single "Get(3)" query, thereby only sending one request to the next enumerator rather than three (4). Obviously, if there is only one pending request within a reasonable lapse of time, it will be sent as-is to the next enumerator.

    Once the async enumerator has optimized the pending queries, the resulting query is sent to the grouping enumerator, which takes care of appending any grouping criteria (i.e., "OrderBy") to the query that is being sent to the data source (5). Once the grouping enumerator has added its requirements, the query is passed to the filtering enumerator, which may also append its filtering criteria if it is using a filter expression (6). If a predicate is being used to filter the items, the information will not be appended to the query and the unmodified query will be sent to the buffering enumerator, which is next in line. The buffering enumerator, which typically contains multiple pages of data that has been fetched from the data source, is the last layer before the query is handed over to the data-source provider. Now, the buffering enumerator has an additional role, which is it make sure that it makes a worthwhile roundtrip to the server. If it realizes that the trip is "not worth it," as in this example of only retrieving three items, it will decide to get more items and keep them in its buffer (7). Once the query exits the data-virtualization engine, the data-source provider takes over and queries the data source using the transformed query (8).

    All this time, the grid has remained fully responsive.

    Once the data-source provider receives the items from the data source, they are handed to the buffering enumerator, which passes the items that were requested to the filtering enumerator and buffers the rest for later use. At this point, if a predicate is being used to filter the items, it is possible that the filtering enumerator rejects the items that are received and requests more from the buffering enumerator, which luckily grabbed more than was requested. If only a filter expression was used, then the items are passed directly from the filtering to the grouping enumerator. Like the filtering enumerator, it is possible that the grouping enumerator rejects the items. For example, if the items belong to a collapsed group, they will be rejected and new items will be requested (9). Of course, the grouping enumerator will modify the request to make sure that further requests will "jump" over collapsed groups.

    After the items have successfully passed the lower enumerators, they are finally transferred to the async enumerator that will replace the dummy items with the actual items, which will then be displayed in the grid (10).

    In addition to the enumeration layers, Xceed DataGrid for Silverlight also actively pre-fetches new items and rebalances the caching and buffering enumerators while the data-virtualization engine is idle. What this means is that when there are no pending requests in the async enumerator, the engine will actively retrieve new items and make sure that the currently displayed items are moved to the middle of the buffers. In other words, if the currently displayed items are located at the end of the buffer, they will be moved to the middle, the extra items at the top will be cleared, and new items will be fetched to complete the buffer.

    So there you have it: a glimpse of the inner workings of Xceed DataGrid for Silverlight!
     
    *Title created by and stolen from Mike Strobel (@mstrobel)
  • Why There Won't Be A SelectedItems Property And Why You Will Like It

    One of the first things you will notice when you`ll start using Xceed DataGrid for Silverlight is the fact that there is no SelectedItem(s) property. That's right. No SelectedItems property. 

    The upcoming datagrid for Silverlight is a very different beast than today's current datagrids. It is built from the ground up to load and navigate through remote data sources extremely efficiently. It aims to make those remote data sources feel like they are completely loaded locally in your datagrid, without actually doing so. To achieve this, it takes data virtualization a few steps further than any other datagrid ever has. I talked about some of these advances in my post that introduces Xceed DataGrid for Silverlight. One of them is that everything this datagrid does happens asynchronously. This makes for a highly responsive user experience, but there are a few things developers will have to do differently in order to reap the benefits.

    Although it may seem absurd, the decision to not have a SelectedItems property was not taken lightly, and many long discussions were had with proponents on both sides of the debate. So why is it not there? Simple. Because 95% of the time, it wouldn't work. Yes, 95% is an arbitrary number but it is a fair representation. The only way to keep your application responsive, even when the network isn't, is to use asynchronous data virtualization, which means to read and write data in the background. Having a synchronous SelectedItems property in this situation made no sense since the UI would have to wait for the selected items to be returned from the server. So we decided to go with a fully virtualized approach and created a selection model in which "selection ranges" are created. These ranges abstract the need to store the exact list of selected items and provide instantaneous selection across any number of items. If this process were done locally, the selection range would need to be processed as soon as it is created.

    For example, let's say you have a large source with and the user selects a few items currently on screen. In this case, the SelectedItems property would return immediately since the items are already loaded. The UI wouldn't lock-up. But if the user selects a group of items and many of them aren't on screen, those would have to be immediately fetched from the data source before SelectedItems could return its value. The UI would freeze for an unpredictable amount of time while those items are returned, making your application feel sluggish.

    This is why we exposed the BeginGetSelectedItems and EndGetSelectedItems asynchronous methods rather than a SelectedItem(s) property: we wanted you to be aware that requesting the selected items can be a costly operation and that in most cases, we could not guarantee if and when you would get the result. 

    "But I absolutely MUST have a SelectedItems property!" Ok!

     

    public IEnumerable<object> SelectedItems
    {
       get
       {
          return ( IEnumerable<object> )GetValue( SelectedItemsProperty );
       }
       set
       {
          SetValue( SelectedItemsProperty, value );
       }
    }

    // Using a DependencyProperty as the backing store for SelectedItems. 
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedItemsProperty =
    DependencyProperty.Register( "SelectedItems", typeof( IEnumerable<object> ), typeof( MainPage ), null );

    private void GetSelectedItems()
    {
       IAsyncResult result = this.sldgDataGridControl.BeginGetSelectedItems( new AsyncCallback( this.ProcessSelectedItems ), null );

       if( result.IsCompleted )
          this.SelectedItems = this.sldgDataGridControl.EndGetSelectedItems( result );
    }

    private void ProcessSelectedItems( IAsyncResult result )
    {
       if( result.CompletedSynchronously )
          return;

       this.SelectedItems = this.sldgDataGridControl.EndGetSelectedItems( result );
    }

    private void DataGridControl_SelectionChanged( object sender, EventArgs e )
    {
       this.GetSelectedItems();
    }

     

  • Xceed DataGrid for Silverlight Tech Preview!

    The Silverlight development team has been hard at work creating our Silverlight datagrid, and I thought that I would let you in on our progress and give you a glimpse as to what you can expect when it's released. Last year at PDC 2009, we demoed what was an early-stage version of the grid. Since then, it has come a long way and is nearing feature completion.

     Xceed DataGrid for Silverilght - Grouped

    Xceed DataGrid for Silverlight takes user experience to a whole other level. Perfectly smooth horizontal and vertical scrolling, fully animated column reordering and resizing, customizable animations for group expansion and collapse, as well as transition animations for when rows are added or removed, provides an unprecedented level of UI slickness and responsiveness.


    Asynchronous data virtualization. THE must-have in a Silverlight data grid, it’s the cornerstone on which Xceed DataGrid for Silverlight is built. We’ve packed a lot into our implementation of this, such as major advancements in how virtualized data is handled, automatic discovery, active pre-fetching and caching of data, quick navigation, and instant grouping. You can add to that built-in support for WCF Data Services and WCF RIA Services, as well as "event-driven" and "full-list" data sources (i.e., pass-through data virtualization) and many more to come, which means that where you get your data from no longer matters. Just connect to the source, and we handle the rest. Simple. Trust me: you haven't seen anything like this in any other grid before.

    Sometimes, appearances are everything. Don't like the yellow/orange/i-am-sorry-Catherine-i-do-not-know-the-exact-color border in the PDC-demo theme? Change it! Xceed DataGrid for Silverlight is 100% "blendable" and its elements can be customized to match the look and feel of any application. Or if you prefer, you can use one of the built-in themes that were designed for the grid with user experience in mind.

    Now, I know the first question our current WPF clients will have is this: "I have a project that currently uses Xceed DataGrid for WPF. Can I just switch it out for the Silverlight version?" The answer to that is no, you can't. Why? Well there are a couple of reasons. First, Silverlight is not WPF; there are things you can do in WPF that you can't do in Silverlight. Doing a direct API port would have been 1) nearly impossible and 2) would not have allowed us to take full advantage of the Silverlight platform. Second, since the initial release of Xceed DataGrid for WPF, we have developed new, innovative ways of handling data, but it would be impossible to implement them in that product without doing major breaking changes. With our upcoming Silverlight datagrid, we have been able to implement these new techniques without worrying about backwards compatibility. At any rate, although it is not a direct API port, the API will feel very familiar and you should feel right at home using the Silverlight datagrid. We intend to also provide a WPF-compiled version of the Silverlight grid, so if you want to build for both platforms, you will have the option to do so with the Silverlight datagrid.

    Want a quick point-by-point list of the features/goodies that we are aiming for? Here you go:

    UI

    • Ultra smooth and responsive tabular layout
    • Multi-level grouping
    • Sorting
    • Filtering
    • Sticky group containers
    • Animated vertical and horizontal scrolling
    • Fixed and scrollable headers and footers
    • Easy group navigation (think something similar to the group-navigation control in Xceed DataGrid for WPF)
    • Animated column reordering and resizing
    • Row and cell selection

    Data Virtualization

    • Asynchronous data loading for continuously responsive UI
    • Minimal (if at all) code required
    • No discovery needed on remote data source
    • Pre-fetching and caching of data
    • Instant expanding and collapsing of groups
    • Quick navigation through data source
    • Support for almost any type of data source including WCF Data Services and WCF RIA Services

    Editing

    • Default editors for all the common data types
    • Ability to create custom cell editor controls
    • Validation (e.g., IDataErrorInfo, INotifyDataErrorInfo)

    While you wait for the official launch of Xceed DataGrid for Silverlight, I will (hopefully) be blogging about specific features and how easily they can be used in any Silverlight application. I will also take you on indepth tours of some of the design concepts behind our latest baby Smile

    In order to get feedback from the community, we will be opening a private beta in March to make sure that we have the best possible product when it is officially released. If you are interested, send me an email to datagridbeta@xceed.com letting me know the type of project in which Xceed DataGrid for Silverlight would be used (e.g., new project, moving existing WPF project to Silverlight, replacing an existing grid) and the timeframe  needed to make a decision, and I will sign you up for the beta.

    Questions, comments, feature requests? Now's the time! 

  • Ode to the Hidden and Forgotten

    Ever since I started working in Silverlight, I found some strange ommissions, whether voluntary or not, in the Silverlight API. At times, this was very frustrating since I was expecting something as common as BindingList to be there. So, to end this week on a more "creative" note, I present you with an ode. Enjoy! 

    Ode to the Hidden and Forgotten 

    My head aches as I search for something new,
    Something to replace all the things they do.
    Yet Silverlight keeps thwarting all my plans,
    By hiding classes that I want to use.

    No longer can I batch initialize.
    Oh, where is ISupportInitialize?
    Why has IBindingList been abandoned?
    I don't want ObservableCollection!

    Why do you stop me from selecting styles?
    Is StyleSelector never to return?
    Triggers! Oh, dear Triggers! Where have you gone?
    Without you, I will have to manage states.

    And to all the others that are hidden,
    And to those that were simply forgotten:
    We will remember you and all you do,
    When we must search for something new to use.

  • Sneak Peek at Version 3.6!

    So I didn't get much feedback from my post asking for help in regards to what to write, so I will go with the only suggestion I received and let you in on the upcoming features planned for the next major version of Xceed DataGrid for WPF.

    So let's start with a feature that clients have been asking for since the virtualizing collection view was released: multiple selection! Yes ladies and gentlemen you will finally be able to select more than one row when using a virtualized source! Actually, the whole selection process is being revamped, so expect to see some (long awaited) changes including.... wait for it.... CELL SELECTION!

    Performance! As if the grid was not already fast enough, get ready to see some performance enhancements in the standard table view!

    Finally, after many many requests, the Live Explorer theme, which can be seen in the the Xceed DataGrid for WPF Live Explorer, is now included with Xceed DataGrid for WPF! Want more? How about a beautiful new Windows 7 theme? (I feel like I am on an infomercial!).

    Windows 7 Theme

    Another fantastic new addition is the print preview, which will allow users to take a look at their snazzy documents before sending them to the printer.

    QTP? Did someone say QTP? You heard right! The upcoming edition of Xceed DataGrid for WPF will provide full QTP support!

    Last, but definitely not least since I am most likely forgetting some, is the new group-navigation control, which replaces the previous group-navigation button, and provides quick and easy navigation between groups.

    So which features are part of the Standard Edition and which ones are part of the Professional Edition? Glad you asked! Here's the breakdown:

    Professional Edition Features

    • Windows 7 and Xceed Live Explorer themes
    • Print Preview
    • Multiple selection when using data virtualization
    • Group-navigation control
    • QTP support

    Standard Edition Features

    • Improved performance in table view
    • Cell selection
    • Customizable cursors in most views

    Of course, you can always check out our feature comparaison chart for the full list Smile

    Enjoy!

  • My Search for My Perfect Twitter Client

    Hello, my name is Jenny (@_random_) and I am a Twitter addict. Okay, not really and nothing even close to @kosmatos, but close enough that I check Twitter many times a day at work and on my iPhone to see what is going on. I don't follow many people and not many people follow me, but I like the interactions I have with those people and sometimes good conversations and suggestions come up. Sometimes I just write random stuff because I am bored. But I guess that if there is a point to Twitter, that is it.

    So, ever since I have come back from the PDC, I have been using the Seesmic desktop client for Twitter, and although not perfect, it seemed to "do the job" although something was annoying me about it and yesterday I figured out what it was: the scrolling! When I scroll, I don't want to have to spend a few seconds finding the last tweet I was reading. It's annoying and it breaks the "flow". And so began my search for the perfect Twitter client this morning.

    Now, if you are expecting an indepth review of Twitter clients, you are reading the wrong blog post. I didn't go looking for THE best Twitter client. I went looking for the best one for ME. So what was I looking for? I had no idea, but I figured I would know it when I saw it. So the first client I installed after uninstalling Seesmic was Tweetdeck, which many people at Xceed seem to enjoy using so I figured that it was probably a safe bet. A couple of minutes later, I was up and running with Tweetdeck and a couple of minutes later it was unistalled. Yes, it scrolled better than Seesmic but I found it hard to differentiate between my tweets, those that are replies to my tweets, and all the other noise. I really hate the new "Blend" themes (if that's what they are called). Use color people!! Black, white, and multiple shades of gray are not the best colors to use when you need to differentiate things. If you insist on using them, at least give me the option to change or customize them to something that I will like. Yes, I know this is a matter of opinion, but I told you I was looking for the perfect Twitter client for ME, not for YOU (I'm talking to you @superdupercat! )!

    While on Tweetdeck, @ftdube sent me a tweet telling me to try Digsby, so I did. As soon as Tweedeck was uninstalled, I downloaded and installed Digsby. I would like to say that I actually tried it; however, when I attempted to add my MSN account in addition to my Twitter account (hey, it's also an IM client) it said my password was "too long". Excuse me? My password is too long? Ummm. No. I didn't even try it as a Twitter client. If it can't handle 24+ character passwords, it has no business being client for anything.

    After my failed Digsby attempt, I took a look at the Twitter web interface to see what clients people were sending from. I saw many Tweetdecks and some Seesmic, but other than that it seemed to be all iPhone apps. "iPhone apps!! Maybe Twitterrific is available for Windows" I thought to myself. So off to the Twitterrific website I go only to be disappointed that there is no Windows version available. I love Twitterrific on my iPhone. It just works and it's easy to find/see what I am looking for. But alas, I was to be disappointed one more time.

    At this point I am getting frustrated and thinking about going back to Seesmic since the only thing that actually bugged me was the scrolling. After quick consideration I decided to go back to the Twitter web site to see if they had a list of clients that they recommended. Lo and behold, right beside the listing for Twitterific was Twhirl. I had seen some tweets that originated from Twhirl before so I decided to try it out. Why? Honestly, because it looked pretty in the screenshot. No black/white/gray theme. 45 minutes later, I am still using it.

    So what was I looking for? I think in the end it came out to "easy". I want it to be easy. I want to easily see what I tweeted, who replied, and whatnot without having to really think about it. The fact that it can stay on top of other windows (with or without opacity) and also that fact that it has a built-in spell checker just made it that much better for ME.

    So that's it. My search for my "Perfect Twitter Client" has come to an end. For now Smile

  • Getting Ready for PDC 2009!

    For the second year in a row Xceed will be exhibiting and attending at PDC 2009! In addition to the amazing new features in the upcoming version of Xceed DataGrid for WPF, we will be demoing the latest development build of our future Silverlight grid.  
     
    This year we will be handing out snazy Xceed t-shirts and if you get spotted wearing one, you will be entered into 1 of the 3 draws to win a 16GB Zune HD video and MP3 player!


    So if you want to come see what we've been working on, ask questions, make suggests for future releases, or simply chat with some of our team members make sure you stop by our booth! Also make sure you stop by Party with Palermo! We will be fresh off the plane but ready to party! Wink

    See you in LA!
  • Writer's Block

    When we started writing blogs at Xceed, I think we all thought that we would always have interesting content to post about and that we could never run out of ideas. How wrong we were! For me, what I find the hardest is finding topics that will interest our current and future clients and that interest me as well! So here I am , more than a month since my last blog post and I have no ideas what to write about. 

    Now, this is where you come in! Tell me what you would like me to write about! Do you want to know more about a specific feature? About upcoming features? About developement at Xceed? A short story about kittens and unicorns? Something about me? About one of the developers?

    Help!! Smile
  • Sneak Peek at Version 3.5!

    Yes! Even with version 3.2 just out the door, the developers at Xceed have been hard at work on version 3.3, now officially version 3.5. So, what new features are being introduced in the latest version? I'm glad you asked! Among other features, the long awaited "smooth-scrolling table view" finally makes its debut in version 3.5. Known as the Tableflow view, this new view provides smooth, animated scrolling and column reordering as well as "sticky" group, grid, and detail headers and footers, which remain at the top and bottom of the grid until the next group or detail needs to be displayed. The "group-navigation" button, which is displayed in the group headers, can be clicked to have the first item in the group brought to the top of the viewport. Take a look!

    So what do you need to do to replace the classic Table view you have been using with the Tableflow view? Simple! Search and replace "TableView" with "TableflowView" and you're done!

    In addition to the Tableflow view, support for 2 new filter criteria has been added to the filter row: "starts with" and "ends width", which allow the content of a column to be filtered according to the values that start or end with the specified filter value.

    Another nice little feature that was added in version 3.5 is a notification glyph that is displayed when the grid is bound to a virtualizing collection view. This glyph (actually, it's 3 separate glyphs) are displayed when data is loaded into the grid, when data is being committed, and when there is an error. In the demo below, you can see the default glyph that is displayed when data is being loaded. Now, if you don't like the default connection-state glyphs, you can always replace them with the image of a kitten! Everyone likes kittens Smile

  • Sneak Peek at Version 3.2!

    So version 3.2 is compiled and ready to be released in mid-June! It has been a hectic past couple of weeks trying to get everything done and we are all very happy with the end result. I know that many of you have been anxiously waiting for the 3.2 release so I though I would give you a little preview to whet your appetite. So what exactly is included in version 3.2? Good question! It looks something like this:

    • Custom distinct values (e.g., "less than 10") in the auto-filter control
    • Filter row that allows end users to provide filter criteria at run time.
    • Built-in support for Entity Framework
    • Improvements to the datagrid's data virtualization capabilities including grouping and support for data sources implementing IQueryable (LINQ)
    • Automatic detection of foreign key constraints and enumerations
    • Support for unbound columns and data
    • Enable and disable grouping and sorting on a per-column basis
    • and a couple little extras that you will discover!

    One feature that clients have often inquired about is custom distinct values. Or, in other words, being able to provide "Excel-like" filter criteria such as "less than x" and "greater than x" in the auto-filter drop down. With version 3.2, it is possible to do so and even more. How? Easy! Just use the QueryDistinctValue event exposed by each DataGridItemProperty and return the desired distinct value. For example, in the code below, I provide distinct values that will filter date/time values according to the year (by default, all dates would have been displayed in the auto-filter control) and I also provide value ranges where, by default, all values would have been displayed in the drop down.

    <Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
      <Grid.Resources>
         <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                              Source="{Binding Source={x:Static Application.Current}, Path=Orders}"
                              AutoFilterMode="And"
                              DefaultCalculateDistinctValues="False">
            <xcdg:DataGridCollectionViewSource.ItemProperties>
              <xcdg:DataGridItemProperty Name="OrderDate"
                              QueryDistinctValue="DataGridItemProperty_QueryDistinctValue_Date"
                              CalculateDistinctValues="True"/>
              <xcdg:DataGridItemProperty Name="RequiredDate"
                              QueryDistinctValue="DataGridItemProperty_QueryDistinctValue_Date"
                              CalculateDistinctValues="True" />
              <xcdg:DataGridItemProperty Name="ShippedDate"
                              QueryDistinctValue="DataGridItemProperty_QueryDistinctValue_Date"
                              CalculateDistinctValues="True" />
              <xcdg:DataGridItemProperty Name="Freight"
                              QueryDistinctValue="DataGridItemProperty_QueryDistinctValue_Range"
                              CalculateDistinctValues="True" />
            </xcdg:DataGridCollectionViewSource.ItemProperties>
         </xcdg:DataGridCollectionViewSource>
      
      </Grid.Resources>
      <xcdg:DataGridControl x:Name="OrdersGrid"
                            ItemsSource="{Binding Source={StaticResource cvs_orders}}"/>
    </Grid>

    private void DataGridItemProperty_QueryDistinctValue_Date( object sender, QueryDistinctValueEventArgs e)
    {
     if( e.DataSourceValue is DateTime )
     {
       e.DistinctValue = ( ( DateTime )e.DataSourceValue ).ToString( "MMMM" );
     }
    }

    private void DataGridItemProperty_QueryDistinctValue_Range(object sender,QueryDistinctValueEventArgs e)
    {
     if( e.DataSourceValue is decimal )
     {
       decimal value = ( decimal )e.DataSourceValue;
       if( value <= 100 )
       {
         e.DistinctValue = "0 - 100";
       }
       else if( value > 100 && value <= 500 )
       {
         e.DistinctValue = "101 - 500";
       }
       else
       {
         e.DistinctValue = "500+";
       }
     }
    }

    Beautifully simple Smile

    Version 3.2 also adds the ability for end users to provide filtering criteria at run time through the use of a "filter row", which can be added to the fixed or scrolling header and footer sections of a grid, group, or detail. In addition to supporting relation filters such as "contains", "equal to", and "greater than", among others, the filter row also supports conditional filters such as "AND" and "NOT", which allow a column to be filtered by more than one filter criterion.

     

     

     

     

     

     

    Another sought-after feature is built-in support for Entity Framework. Although it was possible to use Entity Framework with the previous version of the grid, version 3.2 makes this a whole lot simpler by automatically detecting Entity collections (EntityCollection<TEntity>) and displaying their content as details. In the case where the reference to related Entity objects are not loaded, the QueryDetails event, exposed by the EntityDetailDescription class, can be handled to provide details for a data item.  For example, in the code snippet below, a query that will return the appropriate details for a parent item is executed and the Handled property set to true to indicate that the event was handled.

    private void EntityDetailDescription_QueryDetails( object sender, QueryEntityDetailsEventArgs e )
    {
        Customer customer = ( Customer )e.ParentItem;

        // Since EntityFramework doesn't load automatically references to
        // other objects, we build a query that will include those objects.
        // We start from the base query but we could have added restrictions
        // to the query.
        ObjectQuery<Order> query = customer.Orders.CreateSourceQuery();
        customer.Orders.Attach( query.Include( "Employee" ).Include( "Shipper" ) );
        e.Handled = true;
    }

    A feature that was lacking in the 3.1 implementation of data virtualization was grouping support. With version 3.2, this limitation is a thing of the past :) Now, unlike the other features I just mentioned, grouping in a virtualized collection view is not something for the feint of heart! But, if you know what data you are dealing with and with the help of the Data Virtualization sample, which provides a detailed example of how to implement grouping support, you should be up and grouping in no time. If not, contact our support department, that's what they are there for Wink

    For those of you who are already using the virtualized collection view provided with Xceed DataGrid for WPF, you may be happy to learn that version 3.2 also provides built-in support for IQueryable data sources through the DataGridVirtualizingQueryableCollectionView and its XAML-proxy the DataGridVirtualizingQueryableCollectionViewSource, which, by the way, earned its developer the "longest class name" award!

    A feature that has been implemented in version 3.2 whose lack caused a lot of frustration in previous versions, is automatic detection and support for foreign keys and enumerations. In previous versions, in order to display foreign key constraints, you had to write a lot of code, which, quite honestly, was time consuming and annoying to implement. In version 3.2, all that is a thing of the past (although you can still use your code if you want!). By default, foreign key constraints defined by a DataTable or DataView used as a data source, as well as enums, can be automatically detected and displayed and edited, through a ComboBox, as the corresponding value rather than its key. If you are dealing with a different type of source or would like to provide custom key/value mappings, you can do that too. Although it is recommended to always bind the grid to its data by-proxy of a DataGridCollectionView[Source] or any other data-grid collection view, it is not obligatory. When binding a grid directly to a source that contains foreign key constraints or enums and data-grid collection view is not used, it is still possible to display the value rather than the key of the constraints and enums; however, they must be defined manually by providing the appropriate foreign key configurations, and a ForeignKeyConverter must be used in order to convert keys to values and back (see ReportsTo column in Example ).

    <Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
      <Grid.Resources>
         <local:OccupationToStringConverter x:Key="occupationToStringConverter" />
         <local:PersonForeignKeyConverter x:Key="personForeignKeyConverter" />

         <ObjectDataProvider x:Key="occupationValues"
                             MethodName="GetValues"
                             ObjectType="{x:Type sys:Enum}">
            <ObjectDataProvider.MethodParameters>
               <x:Type TypeName="local:Occupation" />
            </ObjectDataProvider.MethodParameters>
         </ObjectDataProvider>

      </Grid.Resources>    
     
      <xcdg:DataGridControl x:Name="PersonsGrid"
                            ItemsSource="{Binding Source={x:Static Application.Current}, Path=Persons}">

        <xcdg:DataGridControl.Columns>
           <xcdg:Column FieldName="Occupation">
              <xcdg:Column.CellContentTemplate>
                 <DataTemplate>
                    <TextBlock Text="{Binding Converter={StaticResource occupationToStringConverter}}" />
                 </DataTemplate>
              </xcdg:Column.CellContentTemplate>
              <xcdg:Column.ForeignKeyConfiguration>
                <xcdg:ForeignKeyConfiguration ItemsSource="{Binding Source={StaticResource occupationValues}}"/>
              </xcdg:Column.ForeignKeyConfiguration>
           </xcdg:Column>
           <xcdg:Column FieldName="ReportsTo">
              <xcdg:Column.CellContentTemplate>
                 <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                       <TextBlock Text="{Binding FirstName}" />
                       <TextBlock Text=" " />
                       <TextBlock Text="{Binding LastName}" />
                    </StackPanel>
                 </DataTemplate>
              </xcdg:Column.CellContentTemplate>
              <xcdg:Column.ForeignKeyConfiguration>
                <xcdg:ForeignKeyConfiguration ItemsSource="{Binding Source={x:Static Application.Current}, Path=Persons}"
                                               ForeignKeyConverter="{StaticResource personForeignKeyConverter}"
                                               ValuePath="PersonID"/>
              </xcdg:Column.ForeignKeyConfiguration>
           </xcdg:Column>
        </xcdg:DataGridControl.Columns>
      </xcdg:DataGridControl>
    </Grid>

    Another feature that was often requested is support for unbound columns and data. Now, you may be wondering why I am referring to unbound columns AND unbound data. Good question. Initially, this feature was known as unbound columns; however, after it was implemented, we realized that  unbound columns and unbound data were two different things. Let me try to explain... "Unbound data" is data that can be "appended" to a data item through the use of unbound item properties, which are represented by the DataGridUnboundItemProperty class. Unlike "unbound columns", which can be used to display non-data related information such as a label or controls that allow some sort of action to be carried out, unbound item properties can be used to provide additional data, such as calculated columns. If you are scratching your head at this point, don't worry, it is a lot simpler than it seems! Let me demonstrate:

    Unbound Data

    <Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
      <Grid.Resources>
         <xcdg:DataGridCollectionViewSource x:Key="cvs_products"
                                            Source="{Binding Source={x:Static Application.Current}, Path=Products}">

            <xcdg:DataGridCollectionViewSource.ItemProperties>

              <xcdg:DataGridUnboundItemProperty Name="TotalUnitsValue"
                                                DataType="{x:Type sys:Double}"
                                                QueryValue="DataGridUnboundItemProperty_QueryValue" />
            </xcdg:DataGridCollectionViewSource.ItemProperties>
         </xcdg:DataGridCollectionViewSource>

         <local:CurrencyConverter x:Key="currencyConverter" />

      </Grid.Resources>
      <xcdg:DataGridControl x:Name="OrdersGrid"
                            ItemsSource="{Binding Source={StaticResource cvs_products}}">
         <xcdg:DataGridControl.Columns>
            <xcdg:Column FieldName="TotalUnitsValue"
                         Title="Total Inventory">
               <xcdg:Column.CellContentTemplate>
                  <DataTemplate>
                     <TextBlock Text="{Binding Converter={StaticResource currencyConverter}}" />
                  </DataTemplate>
               </xcdg:Column.CellContentTemplate>
            </xcdg:Column>

            <xcdg:Column FieldName="Photo"
                         Visible="False" />           
         </xcdg:DataGridControl.Columns>
      </xcdg:DataGridControl>
    </Grid>

    private void DataGridUnboundItemProperty_QueryValue( object sender, DataGridItemPropertyQueryValueEventArgs e )
    {
     System.Data.DataRowView row = e.Item as System.Data.DataRowView;
     if( row != null )
     {
       if( row[ "UnitsInStock" ] != DBNull.Value )
       {
         e.Value = ( double )( ( short )row[ "UnitsInStock" ] * ( decimal )row[ "UnitPrice" ] );
       }
     }
    }

    Unbound Columns

    <Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
      <Grid.Resources>
         <xcdg:DataGridCollectionViewSource x:Key="cvs_products"
                                            Source="{Binding Source={x:Static Application.Current}, Path=Products}" />

      </Grid.Resources>
      <xcdg:DataGridControl x:Name="OrdersGrid"
                            ItemsSource="{Binding Source={StaticResource cvs_products}}">
         <xcdg:DataGridControl.Columns>

           <xcdg:UnboundColumn FieldName="EditRowColumn"
                               Width="30"
                               MinWidth="30"
                               MaxWidth="30">
              <xcdg:UnboundColumn.CellContentTemplate>
                 <DataTemplate>
                    <Button Click="Button_Click"
                            Content="..." />
                 </DataTemplate>
              </xcdg:UnboundColumn.CellContentTemplate>
           </xcdg:UnboundColumn>
            <xcdg:Column FieldName="Photo"
                         Visible="False" />
         </xcdg:DataGridControl.Columns>
      </xcdg:DataGridControl>
    </Grid>

    private void Button_Click( object sender, RoutedEventArgs e )
    {
      Cell cell = Cell.FindFromChild( sender as DependencyObject );

      ProductsEditorWindow editor = new ProductsEditorWindow( DataGridControl.GetParentDataGridControl( cell ).GetItemFromContainer( cell.ParentRow ) as DataRowView );

      editor.ShowDialog();
    }

    Notice that although both the UnboundColumn and DataGridUnboundItemProperty classes both use the term "unbound" they are not meant to be used together. Meaning that an unbound item property's corresponding column is a Column and not an Unbound column.

    A little feature, but one that has been often requested, is to enable and disable grouping and sorting on a per-column basis. If you are one of those who requested this feature, version 3.2 is what you need!

    So there you go! Version 3.2 is scheduled to be released on June 15th. So mark the date on your calendar!

    Any questions or comments? Feel free to post them in the comments section below.

  • I notify, we notify, we all... Wait. No we don't!

    Our support department often answers questions from customers who are wondering why grouping, sorting, and statistical functions are not updated when items in the underlying data source are changed, even though they are using a grid bound to an ObservableCollection. After all, an ObservableCollection is supposed to observe its content, isn't it? Well, actually, an ObservableCollection observes its items as a whole. In other words, it only notices when items are added or removed, not when the values of its items change, even if those items implement INotifyPropertyChanged. In comparison, a BindingList DOES listen to INotifyPropertyChanged, and therefore, if its items are modified, the changes will be reflected in the grid. As a result grouping, sorting, and statistical functions will be updated.


    This nifty animation illustrates the differences between the two.
     
     


     
    Now, if you apply these changes through the grid—in other words, edit an item in the grid—you don't have to worry about it.
  • From WinForms to WPF – PART 3: Master/Detail

    Rarely has a feature sparked so much debate at Xceed as hierarchical master/detail. And I think that this feature made it in both the WinForms and WPF grids solely on the fact that our customer base kept requesting it. So here we are, a couple of years later, with a feature that when used appropriately can be a valid way to display information, but that can easily spiral into an incomprehensible mess. What I mean by this is that one level of detail information per master row is perfectly acceptable and legible, but throw in grouping and things start getting ugly. Add more than one detail level as well as grouping and you are in for a world of confusion. So whether you agree with me or not, let’s get started!

    In order to be able to display master/detail data, the grid must be bound through a DataGridCollectionView to an underlying data source that contains hierarchical detail relations (not 100% true, but I will get to that later). The content of those relations will be displayed as the details of the data items in a grid or in another detail. Each relation is represented by a DataGridDetailDescription, which will automatically be created for:

    Detail descriptions can also be explicitly defined by adding them to DetailDescriptions collection of the DataGridCollectionView to which the grid is bound.

    In order for the detail to appear in the grid, the grid’s AutoCreateDetailConfigurations property must be set to true. At this point, you are probably wondering what the difference is between a “detail description” and a “detail configuration”, so I will explain it before continuing:

    A “detail description” is the data representation of a detail relation while a “detail configuration” is its visual representation.  See? Easy. Smile

    So let’s see how this would look in code:

    DataGridCollectionView view = new DataGridCollectionView( this.Orders.DefaultView );
    DataGridControl grid = new DataGridControl();

    grid.AutoCreateDetailConfigurations = true;

    grid.ItemsSource = view;

    If you have been following this series of posts, you knowthat “Orders” is a DataTable from the Northwind DataSet that also happens to contain a DataRelation (remember, these are automatically detected) to the OrderDetails DataTable.

    In XAML, it would look like the following:

    <Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
       <Grid.Resources>        
         <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                                                Source="{Binding Path=Orders}"/>           
       </Grid.Resources>

      <xcdg:DataGridControl x:Name="OrdersGrid"
                                        ItemsSource="{Binding Source={StaticResource cvs_orders}}"
                                        AutoCreateDetailConfigurations="True"/>     
    </Grid>

    Notice that the DataGridCollectionViewSource is used in exactly the same way to display master/detail data as it is to display data that does not have relations, that is, when you let the automatic detection of detail relations do its thing. Now, if you want to intervene in this process, you are more than welcome to do so. Here’s how.

    Detail Descriptions

    Every detail description must have a unique, identifying relation name that can be provided through its RelationName property and that will be used by detail configurations to identify which description their configuration will be applied to. If the detail descriptions are automatically created, their relation name will be extracted from the underlying source or a default one will be provided. If they are explicitly provided, then their relation name must also be explicitly set.

    The AutoCreateDetailDescriptions property of the DataGridCollectionView class determines whether detail descriptions are automatically created when a data relation is encountered in the data source and can only be set at construction time.

    OK, enough copy and paste from the documentation. Let’s look at some code that changes the title that will be used to represent the resulting details in the HierarchicalGroupByControl and in other locations where the detail title is displayed:

    view.DetailDescriptions[ "OrdersOrderDetails" ].Title = "Order Details";

    In XAML:

     <xcdg:DataGridCollectionViewSource.DetailDescriptions>
         <xcdg:DataRelationDetailDescription RelationName="OrdersOrderDetails"
                                                               Title="Order Details"/>    
     </xcdg:DataGridCollectionViewSource.DetailDescriptions>

    Now, if we were to bypass the automatic creation of the detail descriptions, it would be necessary to create the detail description and provide it to the DetailDescriptions collection of the DataGridCollectionView before it can be accessed. Of course, adding it to the collection with its Title property already set is probably a better idea. Smile

    DataGridCollectionView view = new DataGridCollectionView( this.Orders.DefaultView, typeof( System.Data.DataRowView ), true, false );

    DataRelationDetailDescription relation = new DataRelationDetailDescription();
    relation.RelationName = "OrdersOrderDetails";
    relation.Title = "Order Details";

    view.DetailDescriptions.Add( relation );     

    And now the XAML version, which does not do much in this case since we only have one detail relation; however, if the source were to contain more than one relation, by setting the AutoCreateDetailDescriptions property to false, only the “OrdersOrderDetails” relation would have been created.

    <xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
                                                            Source="{Binding Path=Orders}"
                                                            AutoCreateDetailDescriptions="False">
       <xcdg:DataGridCollectionViewSource.DetailDescriptions>
           <xcdg:DataRelationDetailDescription RelationName="OrdersOrderDetails"
                                                                 Title="Order Details"/>
       </xcdg:DataGridCollectionViewSource.DetailDescriptions>
    </xcdg:DataGridCollectionViewSource>  

    Sibling and Child Detail Descriptions

    Not only can each detail description can have one or more sibling detail descriptions (they will be contained in the same DetailDescriptions collection), they can also have one or more child detail descriptions, and so on and so forth.

    Now I won’t go into detail about sibling and child detail descriptions. Just keep in mind that they work like the first-level detail descriptions, and that each has its own DetailDescriptions collection.

    Ok, so now you know how to get your detail data displayed in a grid, and you know the difference between a detail description and a detail configuration.Let’s take a look at how to use the detail configurations to change the look of the details.

    I have the sudden urge to count the number of times I have written the word “detail” so far...

    Detail Configurations

    As I previously mentioned, detail descriptions are the data representation of detail relations while detail configurations are their visual representation. Each detail that results from a detail description is configured according to its corresponding detail configuration, which is identified by its relation name (the same relation name that was used for or extrapolated by the corresponding detail description). By default, a detail configuration that contains a ColumnManagerRow and a TextBlock containing the detail title will be applied to all resulting details. Now, you can decide to change the configuration of all details, regardless of their level, through the grid’s DefaultDetailConfiguration property, or you can provide a detail configuration for a specific detail level through the grid’s DetailConfigurations collection.

    Before I continue, I want to explain the difference between the default detail configuration and the configurations contained in the configuration collection. The main difference is their type: the DefaultDetailConfiguration property expects a DefaultDetailConfiguration while the DetailConfigurations collection expects DetailConfiguration types. Why? Well, since there is a direct one-to-one relation between a detail description and a detail configuration, we did not think it was a good idea to expose properties, such as RelationName and Title in the default detail configuration,since these properties could not be applied to all. So we decided to create the DefaultDetailConfiguration class, which only exposes the properties that can be applied to all details indiscriminately.

    So let’s take a look at how we could use the default detail description to provide an InsertionRow in the footers of every detail:

    DataTemplate template = new DataTemplate();
    template.VisualTree = new FrameworkElementFactory( typeof( InsertionRow ) );

    DefaultDetailConfiguration defaultConfiguration = new DefaultDetailConfiguration();
    defaultConfiguration.Footers.Add( template );

    grid.DefaultDetailConfiguration = defaultConfiguration;               

    And the XAML:

    <xcdg:DataGridControl.DefaultDetailConfiguration>
       <xcdg:DefaultDetailConfiguration>
          <xcdg:DefaultDetailConfiguration.Footers>
              <DataTemplate>
                 <xcdg:InsertionRow>
              </DataTemplate>
          </xcdg:DefaultDetailConfiguration.Footers>
       </xcdg:DefaultDetailConfiguration>
    </xcdg:DataGridControl.DefaultDetailConfiguration>


    Now if we were to explicitly provide a detail configuration for a specific detail relation, the default detail configuration would be ignored and the explicit detail configuration would be used. So let’s see how that would look:

    grid.AutoCreateDetailConfigurations= false;

    DetailConfiguration configuration = new DetailConfiguration();
    configuration.RelationName = "OrdersOrderDetails";
    configuration.Title = "Order Details";

    // Do not use the default headers ( TextBlock andColumnManagerRow )
    configuration.UseDefaultHeadersFooters = false;

    DataTemplate cmrTemplate = new DataTemplate();
    cmrTemplate.VisualTree = new FrameworkElementFactory( typeof( ColumnManagerRow) );

    configuration.Headers.Add( cmrTemplate );
    grid.DetailConfigurations.Add( configuration );  

    XAML:

    <xcdg:DataGridControl.DetailConfigurations>
      <xcdg:DetailConfiguration RelationName="OrdersOrderDetails"
                                            Title="Order Details"
                                            UseDefaultHeadersFooters="False">
        <xcdg:DetailConfiguration.Headers>
            <DataTemplate>
               <xcdg:ColumnManagerRow/>
            </DataTemplate>
        </xcdg:DetailConfiguration.Headers>
      </xcdg:DetailConfiguration>
    </xcdg:DataGridControl.DetailConfigurations>  

    In this situation there is one MAJOR difference between the code and the XAML: in code, the value of the AutoCreateDetailConfigurations property (which I will refer to as ACDC from now on because you can’t get a better acronym) is set to false, while in XAML, its value is of no importance in this situation. Let me explain: in XAML, when the ACDC property is set to true, the DetailDescriptions collection will be automatically populated with the appropriate detail configurations, and any items that are added to the collection will be “linked” to the existing item in the collection. If set to false (still in XAML), the collection will not automatically be populated, but by defining a configuration in the collection, it is also added at the same time. In code, as I have painstakingly learned, the ACDC property takes on a different personality... sort of... I have already explained that, when set to true, detail configurations will automatically be created and added to the DetailConfigurations collection. BUT what is not so obvious is that setting values of properties on the automatically created detail configuration does not provide the same result as creating a new DetailConfiguration for the specific detail relation. So, if ACDC remains true, you will need to replace the one that was automatically created with the new one. So to keep things simple, I would suggest you set the ACDC property to false and add the DetailConfiguration to the DetailConfigurations collection.

    Custom Detail Descriptions

    Remember in the first few paragraphs when I said that it was not 100% true that a data source must contain detail relations in order to display detail data? What I meant by that is that although the most common types of detail relations are automatically detected, it is also possible to create and use custom detail descriptions that will return detail items for a parent item, whatever the definition of the detail relation is.

    Custom detail descriptions can be created by deriving from the DataGridDetailDescription class and overriding its GetDetailsForParentItem method to return the desired details for a data item. Now the detail data returned can be anything, as long as it is at least an IEnumerable. If a data item does not have detail data, simply return null.

    I will not go into details about how to create a custom detail description, so if you want an example, you can take a look at the “Custom Detail Descriptions” topic in the documentation.

    What Now?

    This post ended up being much longer than I had originally anticipated! So if you read all the way to the end and have any questions or would like more information, please post it here or in the forums. Smile

    For those of you who are interested, I wrote “detail” 156 times.

  • Public, Private, Protected, OH MY!

    How an API is designed can make or break a product. A good API will make it easy and intuitive to use the product while a poorly designed API can be an immediate turnoff. At Xceed, we spend a great deal of time designing our APIs and take great pride in knowing that, although they might not be perfect, we have made every effort to get it as close as possible to perfection. Because of my involvement in this process and my, well, strict rules and guidelines, the guys have dubbed me the "Interface Nazi". So for any of you who are interested in knowing the process that goes into a design/API meeting at Xceed, here are some inside looks at how our APIs are designed Smile.
     
    Design Meetings
     
    In a normal design meeting where how a feature will work and how it will be exposed is discussed, the developers working on the feature, the project leader, and I will be there. We usually start by looking at the work that has been done by the developer and how it behaves. Next, we take a look at how the feature is exposed. This part usually involves opening up Reflector and looking at the initial version of the API as designed by the developer. Now, for an API review, we only really care about the "public" members, which includes all members that have the public, protected, and protected internal accessors. During these meetings, I bring in my dictionary (yes, the big book with lots of pages), my thesaurus (yes, I prefer books), and most importantly my copy—and I repeat MY copy—of Framework Design Guidelines, signed by Brad Abrams (yes, I am nerdy enough to get my design guidelines book signed). On a side note: EVERY .NET developer should have a copy of the Framework Design Guidelines in his or her bookshelf. If you don't, order it now. To continue: Once we start reviewing the API, this is where the fun part starts. It usually goes something like this:
     
    Me:  Why is that member public?
    Dev: Because it might be useful in the future.
    Me:  Is it useful now?
    Dev: No.
    Me:  Make it internal. We can always make it public later in another version when it is needed.
     
    This, to me, is a very important part of the API design: If it does not need to be public, don't make it public. Only expose what is important to the user. You can always expose members later, but you can't remove them from the API without breaking the interface. And I HATE breaking the interface.
     
    Spelling and the correct usage of words in an API is also very important, especially in a team where English is not always the native language. At Xceed, most of our developers can read and write in English, but sometimes, French words creep into the API. One common example is the word "edition". In French, in the context of the datagrid, this word means "to be in edit mode"; however, in English, this word usually relates to printed materials, such as books. So using the word "edition" in the API does not make sense. (If you have the Blueprint Edition, you may notice that some of the internal members still have the word "edition" in them!) Another point of contention was the verb "canceling". Do we spell it with 1 "L" or 2? It was decided that we would follow "US English" for all of our APIs, which was an easy decision since we wanted to follow the .NET Framework API as closely as possible. So 1 "L" it was! 
     
    Although these are simple examples, many people would be surprised at how much effort goes into designing an API at Xceed. I remember when we were designing the Cardflow 3D View. It took 3 weeks and about 21 API iterations before we finally settled on a design. And I say "settled" because I think that no one involved in that design is still 100% satisfied with the API.
     
    Nearing the End
     
    The big API review comes near the end of the development cycle. At this point I generally need the API to be finalized so I can finish the documentation in time. The meeting usually involves the lead developer and me, and we compare the latest public version of an assembly and the latest version of the one currently under development to examine the differences in their APIs. Although we still use LibCheck for this, there are many different tools that you can use such as Framework Design Studio. Equipped with coffee and lots of treats, this meeting usually lasts for the better part of the day, and it is during this final review that we try to pinpoint any breaking changes (so I can document them) and any API inconsistencies that we may have missed during the other meetings.
     
    After this meeting and once the developers have corrected any API issues we might have found, we call an "API freeze", meaning that if a developer wants to add, remove, or rename anything that is publicly exposed, he better have a good reason and a nice bribe Wink.
     
    What Would Xceed Do?
     
    Here are some guidelines that I try to enforce in our APIs:
     
    1- It is better to make everything internal or private and expose what is needed when it is needed than to make everything public.
     
    2- Do not sacrifice clarity for character count. If the name of a member has to be 57 characters long to make its function clear, then do it. With Intellisense, who types out full member names anyway?
     
    3- Do what Microsoft does. If you are not certain how a member should be named, look for precedents! Often, there will be a class in the Framework that does something similar to yours. Look at its members and try to keep the naming similar. HOWEVER, you should ALWAYS follow your own precedents before adopting someone else's. For example, if throughout your API you have used ReadOnly but the Framework uses IsReadOnly, stick to yours. Uniformity in an API is a key feature of its ease of use.

More Posts Next page »
Contact | Site Map | Reviews | Legal Terms of Use | Trademarks | Privacy Statement Copyright 2010 Xceed Software Inc.