-
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 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
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!
|
-
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.
|
-
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!).

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 
Enjoy!
|
-
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 
|
-
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! 
See you in LA!
|
-
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!! 
|
-
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 
|
-
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 
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 
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.
|
-
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.
|
-
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. 
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. 
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. 
For those of you who are interested, I wrote “detail” 156 times.
|
-
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  . 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  . 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.
|
-
In Part 1 of this series, I demonstrated how to provide a grid with data. This post will continue where I left off and demonstrate how to group and sort the same data, so if you have not looked at Part 1, I suggest you do so.
Ready?
Data items can be sorted by adding SortDescription structures to the SortDescriptions collection of the DataGridCollectionView to which a grid is bound and specifying the field name of the column by whose values to sort, as well as the direction in which those values are to be sorted. Let's look at some code:
view.SortDescriptions.Add( new SortDescription( "ShipCountry", ListSortDirection.Ascending ) ); view.SortDescriptions.Add( new SortDescription( "ShipCity", ListSortDirection.Ascending ) );
In the above code, two SortDescription structures are added to the SortDescriptions collection of the DataGridCollectionView ("view") to which the grid is bound. The result is that the data items of the grid are sorted according to the values of the ShipCountry and ShipCity columns.
Groups are created in basically the same way; however, rather than adding SortDescription structures to the SortDescriptions collection, DataGridGroupDescription objects are added to the GroupDescriptions collection. For example:
view.GroupDescriptions.Add( new DataGridGroupDescription( "ShipCountry" ) ); view.GroupDescriptions.Add( new DataGridGroupDescription( "ShipCity" ) );
As mentioned in Part 1, the DataGridCollectionViewSource is the XAML representation of the DataGridCollectionView. So let's see how the above code translates to XAML. But before we do, we need to map the System.ComponentModel namespace in the WindowsBase assembly that will allow us to use the SortDescription structure. You can add the new namespace declaration to your window or to the grid that contains the datagrid control.
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
Let's get on with the show!
<xcdg:DataGridCollectionViewSource x:Key="cvs_orders" Source="{Binding Path=Orders}"> <xcdg:DataGridCollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="ShipCountry" Direction="Ascending" /> <scm:SortDescription PropertyName="ShipCity" Direction="Ascending" /> </xcdg:DataGridCollectionViewSource.SortDescriptions>
<xcdg:DataGridCollectionViewSource.GroupDescriptions> <xcdg:DataGridGroupDescription PropertyName="ShipCountry" /> <xcdg:DataGridGroupDescription PropertyName="ShipCity" /> </xcdg:DataGridCollectionViewSource.GroupDescriptions> </xcdg:DataGridCollectionViewSource>
Now, when SortDescriptions and DataGridGroupDescriptions are added to a DataGridCollectionView or DataGridCollectionViewSource, they represent the INITIAL sorting and grouping criteria that are applied to the content of a grid. Once an end user changes these, the ones that were initially added will no longer be used. This can be especially problematic with group descriptions since any customization that was done to the initial descriptions, such as providing a specific GroupConfiguration, will be lost. But don't panic! As you will see, there is a very easy solution to this problem!
Custom Group Descriptions and Configurations
Each DataGridGroupDescription exposes a GroupConfiguration property, which determines the appearance the groups will have once they are created. If a custom group configuration is provided to the initial DataGridGroupDescription, it will be lost once the group is removed by the end user. This is where the GroupDescription and GroupConfiguration properties, which are exposed by the Column class, come in handy. The DataGridGroupDescription and GroupConfiguration that are provided to these properties, respectively, will be used whenever the content of the grid is grouped by the values of the column. Of course, if you have a custom DataGridGroupDescription that already has a GroupConfiguration, you don't need to provide the GroupConfiguration to the column.
So, let's assume that we have a GroupConfiguration that needs to be used every time the content of the grid is grouped by the values of the ShipCountry column. If this were done in code, it would look something like the following:
DataTemplate template = new DataTemplate(); template.VisualTree = new FrameworkElementFactory( typeof( InsertionRow ) );
GroupConfiguration configuration = new GroupConfiguration(); configuration.Footers.Add( template );
grid.Columns[ "ShipCountry" ].GroupConfiguration = configuration;
Although the above code demonstrates how to create a template in code and add it to the Footers collection of the GroupConfiguration, DO NOT CREATE TEMPLATES IN CODE! EVER! FrameworkElementFactory is deprecated and the API in code does not allow you to do as much as it does in XAML. Now that I got that out of my system, let's look at the equivalent XAML:
<xcdg:DataGridControl.Columns> <xcdg:Column FieldName="ShipCountry"> <xcdg:Column.GroupConfiguration> <xcdg:GroupConfiguration> <xcdg:GroupConfiguration.Footers> <DataTemplate> <xcdg:InsertionRow /> </DataTemplate> </xcdg:GroupConfiguration.Footers> </xcdg:GroupConfiguration> </xcdg:Column.GroupConfiguration> </xcdg:Column> </xcdg:DataGridControl.Columns>
Much better 
Custom Sorting
In addition to the default type-based sorting, custom sorting can also be applied by providing an IComparer to the SortComparer property of one or more DataGridItemProperties defined in the DataGridCollectionView or DataGridCollectionViewSource to which a grid is bound. This comparer will be used whenever the values of the corresponding column are sorted (e.g., clicking on the column header). For example, I have created a custom sort comparer (AddressComparer class) that compares addresses first with their civic number, followed by the street. This comparer is then provided to the ShipAddress DataGridItemProperty and will be used whenever the ShipAddress column is sorted.
First, the code:
DataGridItemProperty addressProperty = new DataGridItemProperty( "ShipAddress", typeof( string ) ); addressProperty.SortComparer = new AddressComparer();
view.ItemProperties.Add( addressProperty );
Now, the XAML:
<local:AddressComparer x:Key="addressComparer"/>
<xcdg:DataGridCollectionViewSource.ItemProperties> <xcdg:DataGridItemProperty Name="ShipAddress" SortComparer="{StaticResource addressComparer}" /> </xcdg:DataGridCollectionViewSource.ItemProperties>
You'll notice that the AddressComparer class is located in the "local" namespace. This namespace must be declared in order to be able to use the AddressComparer class. The class also needs to be declared in the resources of the Grid (not the DataGridControl class), above the declaration of the DataGridCollectionViewSource.
xmlns:local="clr-namespace:WinFormsToWPF"
Knowing When Sorting and Grouping Change
A common question that has been asked by many clients is "How do I know when the grouping or sorting changes?". And many get frustrated when they don't find "group changed" or "sort changed" events. The solution, however, is easier than you might think!
Both the GroupDescriptions and SortDescriptions collections exposed by the DataGridCollectionView class implement the INotifyCollectionChanged event, which has a CollectionChanged event that can be handled to know when either collection changes. First, in code:
DataGridCollectionView view = new DataGridCollectionView( this.Orders.DefaultView );
( ( INotifyCollectionChanged )view.GroupDescriptions ).CollectionChanged += new NotifyCollectionChangedEventHandler( GroupDescriptions_CollectionChanged ); ( ( INotifyCollectionChanged )view.SortDescriptions ).CollectionChanged += new NotifyCollectionChangedEventHandler( SortDescriptions_CollectionChanged );
Now, in XAML:
Actually, this can only be done in code; however, when the grid and DataGridCollectionViewSource are defined in XAML, the location where the events are subscribed to is not the same. In the code above, the CollectionChanged events were subscribed to right after the DataGridCollectionView was created. In the case where the collection view is defined in XAML, the events can only be subscribed to after the grid and collection view have been created. The location I usually choose for this is the OnInitialized override of my main window:
DataGridCollectionView view = this.OrdersGrid.ItemsSource as DataGridCollectionView;
if( view != null ) { ( ( INotifyCollectionChanged )view.GroupDescriptions ).CollectionChanged += new NotifyCollectionChangedEventHandler( GroupDescriptions_CollectionChanged ); ( ( INotifyCollectionChanged )view.SortDescriptions ).CollectionChanged += new NotifyCollectionChangedEventHandler( SortDescriptions_CollectionChanged ); }
Conclusion
That's pretty much it! The next tutorial-and I promise to get it out much faster than it took me to get part 2 out-will deal with master-detail scenarios.
Thanks for reading!
|
-
2008 is finally over. For some, it has been a great year but for others it has been difficult. In my case, my husband of 8 years and father of my children killed himself at the beginning of December. Now, I am not writing this to get sympathy and especially not pity but rather to say "Thank you". I want to say thank you to Cat and Pascal who stayed with me that day for support. I want to thank Dan and Odi for allowing me to spend the last month at home to deal with what needed to be dealt with. I want to thank Chris, David, and Sig for taking over my responsibilities. And I want to thank Charles and Marc who were with me on the days when it was too hard to be alone. To all my other friends and co-workers at Xceed, thank you for being there for me and my children. I will be back at work on Monday to start the new year and my new life :) Happy 2009!
|
-
For most of the examples in the documentation, I chose to expose a static property in the application through which the data provided to the DataGridCollectionViewSource is retrieved. Now, this seems to have caused a lot of confusion; therefore, I will attempt to clarify the situation, but before I do I need to explain the difference between a Binding's Source, RelativeSource, and ElementName properties. The first major difference is that these properties are mutually exclusive, meaning that only one of them can be set on a Binding.
The Source property is used to specify an object reference on which the binding Path or XPath will be evaluated and is usually used when the object on which the Binding is set is known and differs from the DataContext.
<TextBox Text="{Binding Source={x:Static Application.Current}, Path=Information}"/>
In the example above, the Source is set to the Application.Current object and uses the x:Static markup extension to access a static object. Another example could be to use the Source property to access a resource defined in a resource dictionary:
<TextBox Text="{Binding Source={StaticResource myInformation}}"/>
The RelativeSource property is used to specify as a source an object that is positioned relatively to the current object. Once again, this property is used when there is a need to specify a source other than the DataContext of the object. There are three normal ways of using of the RelativeSource property.
The first usage is to access Self, which allows referencing itself as the source. This is particularly useful in styles or when we want to bind a property of an object on another property of the same object.
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"/>
The second usage of RelativeSource is FindAncestor mode. FindAncestor mode allows the retrieval of an ancestor of the current object based on its type and/or an ancestor level. While the Type is mandatory, the level is not. If level is not specified or if the provided value does not have a match, then the first element with matching Type is used (i.e., if 1 is specified, the first occurrence of Type is used; if 3 is used, the third occurrence of Type will be used).
<StackPanel Background="Blue"> <TextBlock Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}, AncestorLevel=1},Path=Background}" /> </StackPanel>
The third usage is TemplatedParent mode, which allows the retrieval of the object being templated (if within a template). If you are within a ControlTemplate, templating a Button, then the result of the TemplatedParent RelativeSource will be the Button object instance.
<Window> <Window.Resources> <ControlTemplate x:Key="myButtonTemplate" TargetType="{x:Type Button}"> <StackPanel Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}"/> </ControlTemplate> </Window.Resources>
<Button Template="{StaticResource myButtonTemplate}" Background="Blue"/> </Window>
The ElementName property is used to reference an object by the name of the object. This is particularly useful in XAML, where you can directly reference other elements defined in XAML.
<StackPanel Background="Blue"> <Button x:Name="refButton" Background="Orange"/> <Button Background="{Binding ElementName=refButton, Path=Background}"/> </StackPanel>
Any of these bindings could have been used to provide data to the DataGridCollectionViewSource; however, I decided to use a Source binding and reference the static property defined in the application (Appl.xaml.cs) using the x:Static markup extension, which evaluates the property before the remainder of the XAML is loaded, meaning that my data would be loaded before the grid is created. Unfortunately, this has caused many people to believe that the data can only be provided through a static property defined in the application, which is not the case. Now, using the Source property implies that the Path is not evaluated against the DataContext; however, even if I were to create a property and expose it through the Window that contains the datagrid, the binding would fail since the DataContext of the Window is not the Window itself. That said, I could use a RelativeSource/AncestorType binding to retrieve the Window and set the Path to the property:
<xcdg:DataGridCollectionViewSource x:Key="cvs_orders" Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:Window1}}, Path=Orders}"/>
or I could set the Window's DataContext to itself and bind directly to the property:
public MainWindow() { this.DataContext = this; InitializeComponent(); }
<xcdg:DataGridCollectionViewSource x:Key="cvs_orders" Source="{Binding Path=Orders}"/>
So remember: the location of the property is not important. Just choose the appropriate binding to access it and you will be good to go!
|
-
This post is the first in a series about how to accomplish various tasks using to Xceed DataGrid for WPF. But unlike the documentation where everything is XAML-centric, these posts will demonstrate how to get things done in ...wait for it... actual CODE! Now before you get all excited, I will show you how to do it in code; however, for each example that I provide, I will also post the WPF equivalent—and recommended—way of doing things.
Before I get started, I will have to assume a couple of things:
-
You know how to get your data source and expose it via a dependency property.
-
You know the basics of WPF.
-
-
You have licensed Xceed DataGrid for WPF in your application.
I will also be using the Orders DataTable from the Northwind sample database that I have exposed through a property named Orders defined on my window. The DataContext of the window has been set to the window itself!
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
this.Loaded += new RoutedEventHandler( MainWindow_Loaded );
}
Ready? Here we go!
Getting Data Displayed
Xceed DataGrid for WPF is an ItemsControl and as such exposes an ItemsSource property to which any data source that implements IEnumerable can be provided. The result is a grid that contains a column for every field in the data source and a row for every data item. For example, in the window’s Loaded event:
DataGridControl grid = new DataGridControl(); grid.ItemsSource = App.Orders.DefaultView;
// We know that the default content of the window is a grid, so add // the datagrid as a child of the grid.
( ( Grid )this.Content ).Children.Add( grid );
You may have noticed that I am using the DataView returned by the data table’s DefaultView property. Remember, the source must implement IEnumerable, which DataTable does not, when setting the ItemsSource property directly.
In XAML, the code is quite similar and the result will be the same:
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
<xcdg:DataGridControl x:Name="OrdersGrid" ItemsSource="{Binding Path=Orders}" />
</Grid>
An instance of the DataGridControl class was created, added to the default grid, and its ItemsSource property bound to the static Orders property. One notable difference is that it is not necessary to pass the data table’s DataView because it will automatically be retrieved from the data table and wrapped in a collection view.
Note: All ItemsControls will automatically create a collection view to wrap the data unless a collection view is passed to the ItemsSource property directly.
Why should you use a collection view?
A collection view provides a... well... view of your data, which can be grouped, sorted, and filtered, and through which you can navigate. The DataGridCollectionView class included with Xceed DataGrid for WPF provides the same benefits as the standard collection view, but is also the source through which many of the data grid’s features, such as master/detail and automatic filtering, are exposed.
Armed with this new information, let’s change the previous code to use a DataGridCollectionView:
DataGridCollectionView view = new DataGridCollectionView( App.Orders.DefaultView );
DataGridControl grid = new DataGridControl();
grid.ItemsSource = view;
In XAML, a DataGridCollectionViewSource, which is the XAML proxy of the DataGridCollectionView class, must be used and its Source property set. So let’s create an instance in the resources of our grid and bind the data grid’s ItemsSource property to the DataGridCollectionViewSource. Just like when the ItemsSource property was set directly, the data table’s view will be retrieved and wrapped in a collection view, which just happens to be a DataGridCollectionView.
<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}}" />
</Grid>
If you run the code, you will see that nothing has changed in the final result, but under the hood you will now be able to do much more with your data.
Conclusion
So now you know how to get your data in the data grid, which many of you probably already knew how to do J
As you can see, the XAML, although verbose, is not much different from the equivalent code. And for those who are saying “Bah, why do I need to do that in XAML?”: you’re right, you don’t need to, but the road will be longer and much more painful if you don’t!
|
|
|
|
|