DataGrid
The
DataGrid may display rows containing multiple Composites. Typically, there will be one Composite in each cell. Each Composite may in turn have multiple children, such as Labels in a Stack Panel as shown in the example pic above.
SPECIAL NOTES#1 I could be wrong, but I believe at this time (despite the example given here) that each Composite in a Row must have the
same number of child elements. "Jagged" rows with one cell that has three textblocks and another cell that has only two textblocks may likely cause issues? A likely workaround in this example is just to add an extra, empty textblock to the second cell/composite (to even them up.)
#2 It seems that when you add so many columns that a Horizontal scrollbar is required, when you horizontally scroll and then scroll back, the screen fails to repaint some of the grid cells.
(8/31/2014 - Microsoft may have fixed this. I haven't seen this happening lately.)
I'm not sure the cause of this #2. For the time being, either make the grid large enough to avoid scrolling, provide a means to call datagrid.Refresh() to reset the datagrid--although this takes time and you lose your place in the grid, or else use the free
Extended WPF Toolkit™ Community Edition DataGrid instead? I am investigating this issue further . . .
(4/29/2014-I have just noticed that re-sizing the columns will repaint those affected cells as long as you then call UpdateLayout();)
In WPF Composites, a DataGridRow stores a single Item of Type
ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>. This custom Type houses a ContentControl for each Cell and within the Content property of each ContentControl, it stores a Border.
HOW TOTo
Get the Selected DataGrid Row, in this example, in which the row happens to contain only two columns each with its ContentControl, I extract from each of these ContentControls a respective Border framework element. Each Border is itself an IParent in which children may have been added, and so I get handles to each child Text Block and extract the text out to potentially do something with it:
DataGridRow selectedRow = masterDetail.GetSelectedRow();
if (selectedRow != null)
{
var cells = selectedRow.Item as ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>;
Border contentAtCol0 = cells.Content0.Content as Border;
Border contentAtCol1 = cells.Content1.Content as Border;
string flight = contentAtCol0.GetChildFromComposite<TextBlock, DataGrid>(0, 1).GetText();
string parking = contentAtCol1.GetChildFromComposite<TextBlock, DataGrid>(0, 0).GetText();
. . . . . . .
I mention the above just to provide a foundation for understanding how the DataGrid works with WPF Composites. As you can see, it is purposely limited to just a relatively reasonable
11 columns (but since the code is open-source, one could easily add on support for more columns, I believe, if desired.)
To
Initialize and Add Settings, start by initializing it that same way as you would any IParent, and by choosing a Composite container type to place in the Borders of the cells. In this case, I've chosen a VerticalPanel. Then, add your settings and apply colors to the Scroll Bar as desired. Finally, define the titles for your Column Headers. See example below:
DataGrid masterDetail = new DataGrid();
masterDetail.Initialize(397D, 810D, ContainerType.VerticalPanel, Brushes.WhiteSmoke, Brushes.Gray, Brushes.Gray, new Thickness(0));
masterDetail.BeginSettings().SetBackgroundColorOnItemsControl
(Brushes.LightSlateGray).SetItemBorderColorAndThickness(Brushes.LightSteelBlue, new Thickness(0)).Set<TextBlock, DataGrid>("Padding", new Thickness(2,0,2,0)).SetTextColor(0, 1, Brushes.Black).EndSettings();
masterDetail.BorderBrush = Brushes.Black;
masterDetail.BorderThickness = new Thickness(0);
ContemporaryStyle.ApplyToScrollViewer(masterDetail, "LightSteelBlue", "LightSkyBlue", "#FF002050");
List<string> columnHeaderList = new List<String>();
columnHeaderList.Add(" Attractions");
columnHeaderList.Add(" Parking Garage");
You may leverage the method
ApplyDataGridStrategy to set DataGrid properties such as RowHeaderWidth, SelectionMode, SelectionUnit, RegularRowColor, SelectedContentControlColor, SelectedContentControlForeground, SelectedFocusBorder:
masterDetail.ApplyDataGridStrategy(20, DataGridSelectionMode.Single, DataGridSelectionUnit.FullRow, Brushes.LightSteelBlue, Brushes.LightSkyBlue, Brushes.Black, Brushes.LightSkyBlue, "Gray", "Silver", "1,1,1,1", 1D, "Gray", "Silver", "Gray", "LightSkyBlue", "Gray");
To
Add a DataGrid Row, add child Composites for each column of the DataGrid.
NOTE: you need only pass in the columnHeaderList as a parameter to DataGridArgs; for example, you may do this in the first Composite added (yet, it doesn't hurt anything, if you pass it in more than once):
//NOTE: y/column is the index for StackPanel. x/row is ignored.
//add column header list when adding the first composite
//Column 1
var borderA = masterDetail.BeginComposite<DataGrid>(trav.ID + "1")
.AddText(0, 0, trav.Counter.ToString())
.AddText(0, 1, "Flight: " + trav.Flight)
.AddText(0, 2, "Hotel: " + trav.Hotel)
.AddText(0, 3, "Attraction: " + trav.Attraction)
.SetMouseOverColorOnContainer<StackPanel, DataGrid>(Brushes.Silver)
.EndComposite<DataGrid, DataGridArgs>(new DataGridArgs(trav.ID, 0, 0, columnHeaderList));
//Column 2
masterDetail.BeginComposite<DataGrid>(trav.ParkingGarages[0].ID + "2")
.AddText(0, 0, "Color: " + trav.ParkingGarages[0].Color)
.AddText(0, 1, "Description: " + trav.ParkingGarages[0].Description)
.AddText(0, 2, " ")
.SetMouseOverColorOnContainer<StackPanel, DataGrid>(Brushes.Silver)
.EndComposite<DataGrid, DataGridArgs>(new DataGridArgs(trav.ParkingGarages[0].ID, 0, 1, columnHeaderList));
masterDetail.UpdateByKey<TextBlock, DataGrid>(borderA.GetKey(), 0, 0, txt => { txt.FontWeight = FontWeights.Bold; });
Once the columnHeaderList has been passed in, you may then access these columns to set properties on them such as Minimum Width, Background Color, and Foreground Color:
//set column setting after column header list has been added
masterDetail.Columns[0].MinWidth = 100D;
masterDetail.Columns[1].MinWidth = 90D;
masterDetail.SetBackgroundColor(BrushExt.CreateSolidColorBrushFromString("#FF002050"));
//Column Header Colors
masterDetail.SetColumnHeaderForeground(Brushes.White);
masterDetail.SetColumnHeaderColor(BrushExt.CreateSolidColorBrushFromString("#FF002050"));
To
Set the Selected Row, set the DataGrid's Selected Index:
masterDetail.SelectedIndex = 0;
masterDetail.ScrollIntoView(masterDetail.Items[0]);
To
Remove a DataGrid Row, unsubscribe child events (if needed) and remove all CHILD COMPOSITES for that row. Once the last Child Composite is removed the entire row will automatically be removed.
//Example removing a row that contains two child composites
theDataGrid.UnsubscribeChildEventsByKey(rowGuid + "1");
theDataGrid.UnsubscribeChildEventsByKey(rowGuid + "2");
theDataGrid.RemoveByKey(rowGuid + "1");
theDataGrid.RemoveByKey(rowGuid + "2");
To
Remove All DataGrid Rows, unsubscribe all child events recursively (if needed) via DisposeEventsOnAllParents and remove all rows via RemoveAll. (This may be useful if maintaining a separate collection in the background. You may want to perform a remove all and then re-add all to the UI from scratch every time you perform a create, update, or delete? . . . if you are sorting in the background collection or making a roundtrip back to a database? Just another option?)
FasterWPF.Selectors.DisposeEventsOnAllParents(theDataGrid);
FasterWPF.CommonExt.RemoveAll(theDataGrid); //Clear Grid
CaveatThe DataGrid support is for Composites in a DataGrid at this time.
For a regular DataSet, in contrast, I merely provide basic examples using the free, open-source DataGrid available from the
Extended WPF Toolkit™ Community Edition DataGrid here: http://wpftoolkit.codeplex.com/
In other words, the cells in a DataSet-based DataGrid are NOT considered "composites" but atomic values. The DataGrid itself could be added to a Composite such as it is added to a StackPanel in a TabControl in the Demo App. It is convenient to use this separate DataGrid for this purpose since it resides in a separate namespace.
NOTE: 12/29/2013 *******************************************
Also see WPF Composites Contrib on codeplex.
NOTE: 11/3/2013 *********************************************************
After adding a row, you may need to refresh like so: System.Windows.Data.CollectionViewSource.GetDefaultView(theDataGrid.ItemsSource).Refresh();
NOTE: IN VERSION 5.0, YOU MAY SIMPLY CALL REFRESH on the DATAGRID PARENT
UpdateLayout may also work in certain scenarios.
Also, you may likely need to turn off Virtualization since WPF-CPS works in a simplistic manner, removing all composites and re-adding all composites whenever sorting or filtering:
NOTE: THIS WILL LIKELY BE MADE AUTOMATIC IN VERSION 5.0 . . .
EXAMPLE CODE IN F#:
let mutable masterDetail = new DataGrid()
let factory:FrameworkElementFactory = new FrameworkElementFactory(typeof<StackPanel>)
factory.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, false)
masterDetail.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, false)
masterDetail.ItemsPanel<-new ItemsPanelTemplate(factory)