Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion: Modern WinUI DataGrid - Input Needed #1500

Closed
anawishnoff opened this issue Oct 28, 2019 · 143 comments
Closed

Discussion: Modern WinUI DataGrid - Input Needed #1500

anawishnoff opened this issue Oct 28, 2019 · 143 comments
Labels
discussion General discussion wct

Comments

@anawishnoff
Copy link
Contributor

Discussion: Modern WinUI DataGrid

Hey community members! We've seen that DataGrid has been a valuable piece of the Windows Community Toolkit for many of you and we are interested in graduating it to a native WinUI control (!!). I need your help to figure out what would be needed to make a full-scale DataGrid the best it can be.

I want to hear your input on all things DataGrid. To get started, feel encouraged to answer as few or as many questions as you have time for:

  1. What are some wish-list items that you want for DataGrid?
  2. Where can DataGrid improve in terms of UI, UX, and overall design?
  3. What do you like/dislike about DataGrid?
  4. What are some scenarios in which you use DataGrid?
  5. Are there any scenarios that you wish it could fit better into?

Thanks in advance everyone! See below links for some refreshers and context.

Related Links

Read up on the DataGrid documentation
Download and interact with DataGrid via the DataGrid Nuget package download
Refresh your knowledge by checking out the existing open source DataGrid implementation in the WCT.

@anawishnoff anawishnoff added the discussion General discussion label Oct 28, 2019
@msft-github-bot msft-github-bot added this to Needs triage in Controls Triage Oct 28, 2019
@mdtauk
Copy link
Contributor

mdtauk commented Oct 28, 2019

More viewmodes. Take a look at File Explorer and it's Icon views. The DataGrid should be able to show icons/items in a grid with grouping, as well as the headed rows and columns.

I know this may not be possible, or out of scope - but it is an aspect of WinUI vs Win32 that isn't as easy as 1:1 - and a modern version of File Explorer, or the Common File Dialogs - may have need of such a control. This could be that control, not an internal custom control.

@Pinox
Copy link

Pinox commented Oct 28, 2019

My wishlist : to me a good datagrid includes all the following features by default. I'm borrowing some screenshots from the html world.

Easy filter the whole page with preferred row count.

1

Select / Deselect Visible Columns , Column Sorting , Copy , Print

2

Export data to specific format.

3

Column Reordering by dragging column.

4

Column Filtering

5

Fixed Header - where header stays on top even when scrolling

Row details with XAML template for details.

6

Row Grouping

7

Drag & Drop Row Order

8

The features above in my opinion should be standard in all datagrids.

If you want to make datagrid stand out to the html world then I would also include the following. I find myself many times looking at a datagrid and then settling on a listview as the datagrid lacks these features.

Sideswipe the row to include features like edit , delete , flag etc.

sideswipe

The above features handle mainly the "presentation of data" , what is still lacking from WinUI is what I believe should be a native WinUI feature (control ) like the Microsoft Pivot Control. to compliment the datagrid.

MS already has the source code for this and it was an absolute awesome control back in the days.

pivot1

https://www.youtube.com/watch?v=ZJIkQ9nebKY

Now you cover the presentation and visualization of data that should be the minimum to set WinUI apart from all the basic features out there.

Most importantly it really displays the power of "native apps" that should include awesome animations and visually appealing controls that is powerful and looks really cool.

I can take this one step further and say after the above features (visualizations) we can include 2D/3D animations that creates a concept of depth in the data and will take us to a different stratosphere , but I guess that is for another day ;))

uwp2

@ArchieCoder
Copy link

A good starting point is looking at your partner Telerik with their RadDataGrid (UWP Open Source). With one of my clients, I used it and it actually works well. It is hyper fast and versatile. The only thing that is difficult is their code, there is no way to modify anything from their engine without a good comprehension of the architecture.

I would expect the new DataGrid to be as performant as Telerik.

@lukeblevins
Copy link
Contributor

I'm very glad to see the WinUI team is considering a DataGrid control.

The most substantial improvements I'd like to see from a new control to replace the Toolkit DataGrid:

  • The ability to retrieve a DataGridRow by index
  • Events specific to the DataGridRows themselves. For instance, DoubleTapped and RightTapped.
  • Smooth scrolling
  • Input validation for cell renames
  • A DataGridColumn designed specifically for row icons
  • Easy customization of colors, etc.

@NoahFeller
Copy link

The option to have an edit button which puts draggable icons next to each row to move the order would be a useful feature. It would also be great to see some shadows behind the row being lifted up and moved (since one of the main Fluent attributes is depth). Small up and down arrows might be good next to the dragging icon to move the rows more easily with mouse input. If a row is moved using the arrows, a subtle sliding animation would be a nice touch. With touch input, the option to hold on a row and just move it without even needing to press the edit button might work well too. I'm not sure if this is already supported, but I could see selection check boxes like in File Explorer making obvious which DataGrids support row selection and just improve the overall UX. The iOS reminders app contains some of these ideas (examples pictured below). The iOS Stocks app also has a similar sort of deal with movable rows. Most of these ideas would also work great for columns.

123456

IMG_1820 (1)

@NoahFeller
Copy link

NoahFeller commented Oct 29, 2019

@NoahFeller That type of action makes more sense for a List View. With a Grid view you are not rearranging the order, there should be sort options but the user is not dragging things around.

*Edit
After thinking about it a little more, I do see how such an option can be useful. However, I definitely don't think this should be the default behavior.

Yeah you're probably right @yaichenbaum. I was envisioning those suggestions as the ability to do a custom sort and I thought dragging might be useful for that. So I agree, definitely not default but it might be pretty useful as an option. Thanks for the feedback!

@LazerPanth3r
Copy link

The simplicity of the WPF datatable to datagrid function. I've actually been fighting for the past week or so trying to get a UWP datagrid to work how I had it in WPF where I could just bind a Datagrid to a Dataview, then fill the Dataview from SQL with a datatable.defaultview. The grid then just displayed the data table. It was amazingly simple to do, and UWP so far has made this ridiculously complicated.

@kvarekamp
Copy link

make it cross platform

@keeganatorr
Copy link

I would like to request a single cell selection mode, excel style.
The ultimate goal being a mode with the exact behaviour as a WinForms DataGridView.

@chiefklondog
Copy link

Having an assortment of virtualization options that can support different use cases (From plain scrolling virtualization options like recycling to other concepts like data paging)
Often (In similar desktop controls that support this) some of these options begin to fail when used with custom templates for columns with unique data. The more they can support in those cases, the better.

@mdtauk
Copy link
Contributor

mdtauk commented Oct 29, 2019

If at all possible, the Paging Control #268 proposal, should be synced, and any hooks required be added to the DataGrid control.

@robloo
Copy link
Contributor

robloo commented Oct 29, 2019

This is good news! I've been waiting for something official on the DataGrid front for a while ever since it appeared in the community toolkit. This is really the last control missing from WinUI 3.0 that the community toolkit doesn't have a good alternative for.

First, please, don't reinvent the wheel. As a base start with the WPF implementation of the DataGrid (I know you can't use the code but please use 100% of the API) -- NOT the Silverlight one. The WPF DataGrid was much more feature-complete and has fewer bugs in my testing.

In using the community toolkit DataGrid I also requested the following API additions that came up for various use cases Discussion here:

  • RowPressed : As discussed above. Indicates a row has been pressed so further processing or context menu's can occur.
  • RowDoublePressed : Same as RowPressed, just the double click version.
  • ColumnHeaderPressed : Occurs whenever a column header is pressed. Must take priority over any sort direction internal updates. This will allow for custom filtering menus or right click options to turn on/off columns.
  • ColumnHeaderDoublePressed : Same as ColumnHeaderPressed, just the double click version.
  • ColumnHeaderWidthChanged : Occurs whenever the user resizes a column header width. This will allow for saving this information in a clean way. This is commonly needed when restoring UI state.

I also continue to have lots of problems with the community tookit DataGrid resetting the scroll position to the top after ItemsSource is changed. This needs to get fixed so the offset is preserved like in WPF; however, we also need to be able to control this which is why I would suggest the following API as well:

  • FirstRowInView() : Returns the first row visible in the DataGrid used to restore state.
  • VerticalOffset : Get/Set again to restore state of the scrollbar
  • HorizontalOffset : Get/Set to restore state of the scrollbar

@RBrid Also helped with some analysis here:

@verelpode
Copy link

Suggested new events, and some improvements to preexisting events:

Event Comments
ContextRequestedForColumnHeader To support showing a context menu (or other flyout) when a column header is right-clicked. The same as Windows.UI.Xaml.UIElement.ContextRequested but for column headers instead of the entire DataGrid.
ContextCanceledForColumnHeader The same as Windows.UI.Xaml.UIElement.ContextCanceled but for column headers instead of the entire DataGrid.
ContextRequestedForRow To support showing a context menu (or other flyout) when a row is right-clicked. The same as Windows.UI.Xaml.UIElement.ContextRequested but for rows instead of the entire DataGrid.
ContextCanceledForRow The same as Windows.UI.Xaml.UIElement.ContextCanceled but for rows instead of the entire DataGrid.
RowTapped Note also the case of tapping a row that is already selected.
RowDoubleTapped The same as Windows.UI.Xaml.UIElement.DoubleTapped but for rows instead of the entire DataGrid.
RowRightTapped The same as Windows.UI.Xaml.UIElement.RightTapped but for rows instead of the entire DataGrid. See also ContextRequestedForRow.
PointerPressedInRow The same as Windows.UI.Xaml.UIElement.PointerPressed but for rows instead of the entire DataGrid.
PointerReleasedInRow The same as Windows.UI.Xaml.UIElement.PointerReleased but for rows instead of the entire DataGrid.
PointerMovedInRow The same as Windows.UI.Xaml.UIElement.PointerMoved but for rows instead of the entire DataGrid. Possibly PointerMovedInRow would only be triggered while the pointer is captured.
ColumnHeaderTapped Note also the case of tapping a column header that is already selected.
ColumnHeaderDoubleTapped The same as Windows.UI.Xaml.UIElement.DoubleTapped but for column headers instead of the entire DataGrid.
ColumnHeaderRightTapped The same as Windows.UI.Xaml.UIElement.RightTapped but for column headers instead of the entire DataGrid. See also ContextRequestedForColumnHeader.
ColumnHeaderWidthChanged Alternatively could be named ColumnHeaderResized. In the event args, include a boolean property that specifies whether it was changed by the user versus changed programmatically. Obviously the event args should also specify which column header was resized/changed.
SortOrderChanged Should be triggered when the list of selected column headers changes. Also triggered when any of the selected columns is switched between ascending versus descending order. In the event args, include a boolean property that specifies whether the sort order was changed by the user versus changed programmatically. See also preexisting event Sorting. I think a new event SortOrderChanged would be triggered after the Sorting event is triggered, if the sorting isn't cancelled.
Sorting Already exists but consider adding a settable boolean property in event args that allows the sorting request to be cancelled. For example, the sorting may be invalid or unable to be performed and needs to be cancelled. Also add a boolean property to specify whether sorting was requested by the user versus programmatically.
ColumnSortDirectionChanged This event could be created but it could be unnecessary if the aforementioned SortOrderChanged event is created, if SortOrderChanged is also triggered when the sort-direction changes.
ColumnDisplayIndexChanged Already exists but please consider adding a boolean property (in event args) that specifies whether it was changed by the user versus changed programmatically. Also, please document why it's necessary to have both of the ColumnDisplayIndexChanged and ColumnReordered events. Alternatively, eliminate one of these events.
ColumnReordered Already exists but please consider adding a boolean property that specifies whether it was changed by the user versus changed programmatically.
SelectionChanged Already exists but please consider adding a boolean property (in event args) that specifies whether it was changed by the user versus changed programmatically.
CopyingRowClipboardContent Already exists but please consider adding a boolean property to the event args that specifies whether the operation is cut instead of copy. Alternatively make a separate event for cut.
CuttingRowClipboardContent Doesn't exist. Consider making an event that is triggered when user attempts to cut rows (via control-X hotkey, context menu, or otherwise). Alternatively make the aforementioned boolean property that would allow CopyingRowClipboardContent to be triggered for both of copy and cut operatoins
PastingRowClipboardContent Doesn't exist. Consider making an event that is triggered when user attempts to paste (via control-V hotkey, context menu, or otherwise).

@dotMorten
Copy link
Contributor

I think the most important feature for me would be the ability to easily handle a million rows or more, without having to load a millions rows into memory. The ISupportIncrementalLoading interface isn't good enough for this, because the scroll bar only reflects how many rows you have loaded so far (not total count), and to get to row 1 million, you'd have to keep scrolling to the end over and over and load more and more data, and hope I won't run out of memory. But if I know I have 1 million data records, let me tell the datagrid that, and if I scroll fast or jump to the end, I can be asked to just provide the last few rows.
Think scrolling through a list of rows in a database that I'll be dynamically pulling in.

@jkewley
Copy link

jkewley commented Oct 30, 2019

I think the most important feature for me would be the ability to easily handle a million rows or more, without having to load a millions rows into memory. The ISupportIncrementalLoading interface isn't good enough for this, because the scroll bar only reflects how many rows you have loaded so far (not total count), and to get to row 1 million, you'd have to keep scrolling to the end over and over and load more and more data, and hope I won't run out of memory. But if I know I have 1 million data records, let me tell the datagrid that, and if I scroll fast or jump to the end, I can be asked to just provide the last few rows.
Think scrolling through a list of rows in a database that I'll be dynamically pulling in.

This.

'Modern' means lightweight and quick, and enterprise apps are commonly about sorting through tons of internal data to find and explore the appropriate records. Paging, sorting, async incremental loading, endless scrolling - all of these are staples of desktop apps which leverage data grids.

MS should produce a sample app which highlights these features in the new grid and collect feedback from enterprise developers over time to make these experiences better. Have it run off of a large dataset like World Wide Importers to demonstrate its effectiveness.

@verelpode
Copy link

@dotMorten

I think the most important feature for me would be the ability to easily handle a million rows or more, without having to load a millions rows into memory.

I wish for that as well. In order to assist with supporting a million rows (and I mean assist, not necessarily the complete solution), I suggest that DataGrid should make its current data-binding technique optional. I believe currently data-binding is mandatory (unless my knowledge is no longer up-to-date with the latest version of DataGrid). DataGrid shouldn't require use of Windows.UI.Xaml.Data.Binding as the only supported way of retrieving the values for each cell/column in each visible row.

I suggest that DataGrid allow apps to give DataGrid a delegate or interface instance that DataGrid will invoke whenever DataGrid needs to retrieve the value of a cell/column in a row (alternatively, this delegate or interface could retrieve the entire row -- all column values for a specified row).

Ideally (if possible), DataGrid would allow this delegate or interface to operate asynchronously. For example, the delegate or interface might return a Task or IAsyncOperation<TResult> instead of immediately returning the requested value.

Alternatively, asynchronous support is also possible without Task and IAsyncOperation<TResult>, if DataGrid operates similar to the following procedure:

  1. DataGrid decides that it needs to retrieve the value of cell/column 5 in row ID 50001. Alternatively DataGrid decides to retrieve all column values for row ID 50001.
  2. DataGrid invokes a delegate or interface instance or event handler that notifies the app that DataGrid has requested the loading of the data values of the specified column/row or entire row.
  3. The app retrieves the requested row data asynchronously. For example, it could asynchronously perform an SQL database query.
  4. The app finishes retrieving the requested row data. The app invokes a method in DataGrid that gives the row data to DataGrid.
  5. DataGrid displays the row data, or does whatever else it needed to do with the data.

@anawishnoff
Copy link
Contributor Author

First off, wow!! Thanks so much to everyone who's contributing their ideas. I have a few specific responses below, but overall I wanted to say that I am saving a ton of these comments and really enjoying hearing how we can build an awesome modern DataGrid. Keep it coming!

@Pinox thanks so much for that detailed response and the screenshots. Your wishlist is awesome (especially those cool animations!) - I agree that those features found in html are a good inspiration point and are simple but would greatly improve quality of life. Will definitely be saving this comment for future reference!

@verelpode, @robloo, @duke7553 YES to DataGrid-specific events! Thank you all for the detail you put into these. As we design for touch-first and for a variety of inputs, events like these should definitely be implemented!

@dotMorten, @jkewley, @verelpode Performance is definitely one of the main motivations for this project and one of the main improvements we want to implement, through a new data virtualization story and the use of modern controls such as ItemsRepeater. Will keep you all updated once we have more specifics - but thanks again for these details youve suggested.

@Laz3rPanth3r, @robloo, @keeganatorr We hear you loud and clear about liking the DataGrids that belong to WPF and other UI frameworks - we are definitely taking this into account in this refresh!

@verelpode
Copy link

Thanks for the great work on DataGrid and the request for feedback! Here are a few more suggested enhancements.

Need more subclasses of DataGridColumn

The current subclasses/implementations of DataGridColumn are insufficient. I suggest making the following new subclasses of DataGridColumn:

Proposed Subclass Description
DataGridIconColumn A common requirement is to display an icon in each row of a list, therefore I suggest making a DataGridColumn subclass named DataGridIconColumn that typecasts the retrieved cell value to Windows.UI.Xaml.Controls.IconSource and renders the IconSource instance (a different IconSource instance is rendered in each cell).
DataGridImageColumn DataGridImageColumn should operate the same as the proposed DataGridIconColumn except that it should render Windows.UI.Xaml.Media.ImageSource instead of Windows.UI.Xaml.Controls.IconSource.
DataGridDateTimeColumn Displays date and/or time, depending on settings. Typecasts the cell value to System.DateTimeOffset or System.DateTime (both supported) and converts it to text to be displayed in the cell. The conversion to text should be controlled by formatting settings/properties in the DataGridDateTimeColumn class. Obviously sorting by date/time must also be supported.
DataGridTimeSpanColumn Typecasts the cell value to System.TimeSpan and converts it to text to be displayed in the cell. The conversion to text should be controlled by formatting settings/properties in the DataGridTimeSpanColumn class. Obviously sorting must also be supported, and it must sort the TimeSpan instances not the displayed text.
DataGridDataSizeColumn Typecasts the cell value to System.Int64, UInt64, Int32, or UInt32 (all supported) and converts it to a size-in-bytes (as text) to be displayed. For example, 1572864 would be displayed as "1.5 MB" because 1572864/1024/1024 = 1.5. The conversion to text should be controlled by settings/properties in the DataGridDataSizeColumn class. Sorting must be performed using the original integer value not the displayed text.
DataGridCustomColumn This should operate similar to the preexisting DataGridTemplateColumn except without the Windows.UI.Xaml.DataTemplate. It should invoke a delegate/callback or event to generate and/or update the displayed GUI element subtree.
DataGridTextColumn Already exists but please consider adding properties or events that allow apps to override the object-to-text conversion and the comparison function for sorting. I explain this in more detail later in my message.

DataGridCustomColumn would be like this:

public class DataGridCustomColumn : DataGridColumn
{
	public event EventHandler<DataGridDisplayingCellEventArgs> DisplayingCell;
}

public class DataGridDisplayingCellEventArgs : EventArgs
{
	public DataGridColumn Column { get; }
	public object CellValue { get; }
	public Windows.UI.Xaml.UIElement CellUIElement { get; set; } // settable
}

When the event DataGridCustomColumn.DisplayingCell is triggered, the event handler should generate or update its own UIElement subtree to display the DataGridDisplayingCellEventArgs.CellValue, and set the property DataGridDisplayingCellEventArgs.CellUIElement to that UIElement subtree, and then DataGrid will display it.

The next time DataGrid triggers this event, DataGrid should set the property DataGridDisplayingCellEventArgs.CellUIElement to the UIElement instance that was previously generated by the event handler, in order to allow the event handler to reuse/recycle and update the same UIElement subtree (potentially with a different value in DataGridDisplayingCellEventArgs.CellValue). The event handler is allowed to either recycle the same UIElement subtree OR set the property DataGridDisplayingCellEventArgs.CellUIElement to a newly created UIElement subtree.

Also consider the possibility of making the new DisplayingCell event directly in the base class DataGridColumn, instead of making the subclass DataGridCustomColumn. Thus the event DataGridColumn.DisplayingCell could allow customization or overriding of the generated UIElement for every subclass of DataGridColumn.

In the preexisting DataGridTextColumn, currently the cell value/object is converted to text for display by invoking System.Object.ToString. Please consider making a delegate/callback or event that allows apps to override this value-to-text conversion. Please also consider a property that allows apps to specify a System.Collections.IComparer instance to override the sorting behavior.

public class DataGridTextColumn : DataGridColumn
{
	public System.Collections.IComparer Comparer { get; set; }
	public DataGridCellValueToToStringConverter CellValueToToStringConverter { get; set; }
}

public delegate string DataGridCellValueToToStringConverter(object sourceObject);

Alternatively, instead of making the Comparer property in DataGridTextColumn, consider the possibility of making the Comparer property directly in the base class DataGridColumn, in order to allow apps to control the sorting behavior for all subclasses of DataGridColumn.

Likewise the CellValueToToStringConverter property could also be made in the base class DataGridColumn because it would be useful to have the ability to convert any cell value to text regardless of which subclass of DataGridColumn is used. For example, when copying a row to the clipboard, each cell value could be converted to text. (Both text and non-text formats may be simultaneously placed in the clipboard -- apps commonly do this in order to increase compatibility with other apps.)

Re sorting when a DataGridIconColumn or DataGridImageColumn is selected: Obviously DataGrid cannot sort icons or images, therefore it could use any one of these solutions:

  • Simply disallow end-users to select (sort by) a column header of type DataGridIconColumn or DataGridImageColumn.
  • Make an AlternativeText property in the IconSource class. When DataGridIconColumn sorts the rows, or when it converts the cell to text for the clipboard/copy, it would use the AlternativeText property.
  • Make a settable Comparer property (of type System.Collections.IComparer) in either DataGridColumn or DataGridIconColumn.

Access to selected rows/items

Please consider improving the functionality for getting and setting the selected rows/items, because the current functionality is insufficient. Currently these properties exist:

public int SelectedIndex { get; set; }
public object SelectedItem { get; set; }
public System.Collections.IList SelectedItems { get; }

I found that sometimes I needed access to the DataGridRow instances of the selected rows, thus I hope that the following properties can be added to DataGrid:

public DataGridRow SelectedRow { get; set; }
public IList<DataGridRow> SelectedRows { get; }

Alternatively, if the above is too difficult to implement, the following read-only variation is less powerful but still helpful:

public DataGridRow SelectedRow { get; }
public IReadOnlyCollection<DataGridRow> SelectedRows { get; }

If DataGridRow instances may be recycled, then the documentation for the above properties should warn that the returned info is only temporarily valid, thus it should be read/used immediately and not retained for a longer length of time.

Also, the preexisting SelectedIndex property gives access to one selected index, but what about getting access to all of the selected items? Therefore I suggest making the following SelectedIndexes property:

public IList<int> SelectedIndexes { get; } // Suggestion.
public int SelectedIndex { get; set; } // Already exists.

Personally I find the name "SelectedIndex" confusing because DataGrid is often used in conjunction with a database and the term "index" has a completely different meaning in a database, therefore I suggest renaming the properties as follows, but I expect that my naming suggestion will be rejected:

public IList<int> SelectedOrdinals { get; }
public int SelectedOrdinal { get; set; }

I also wish for the ability to scroll the specified DataGridRow and/or DataGridColumn into view. Currently this method exists:

public void ScrollIntoView(object item, DataGridColumn column);

I suggest replacing the preexisting ScrollIntoView method with these 2 methods:

public void ScrollIntoView(DataGridRow row, DataGridColumn column);
public void ScrollItemIntoView(object item, DataGridColumn column);

@verelpode
Copy link

Get and set sort-order in one hit

When apps are opened and closed, they need the ability to save and restore the end-user's configuration of the DataGrid, including the sort order and other settings. Sort order meaning the list of columns that were selected by the user. Note this is a list, not a single column. More than one column may be selected simultaneously, meaning the primary column for sorting, and the secondary column for sorting, and the tertiary column for sorting, etc.

Therefore the following property could be made in DataGrid, but actually this is only the first idea and it isn't ideal:

public IReadOnlyList<DataGridColumn> SortOrder { get; set; }

The above is non-ideal because it doesn't support the saving and restoring of the DataGridColumn.SortDirection of each column in one hit. I realize that apps could do it in multiple steps:

  1. Set the proposed SortOrder property to the list of selected columns.
  2. Set the SortDirection property of each of the selected columns (repeat this step for each column).

That's still problematic because it's multiple hits, meaning it causes multiple time-consuming resorts of the DataGrid. End-users could notice and suffer a significant delay in a DataGrid that contains a large number of rows, when the DataGrid is resorted multiple times unnecessarily. To eliminate this problem, I suggest the following solution that allows apps to restore the complete sort-order in one hit and cause no more than a single resort.

public class DataGrid
{
	public IReadOnlyList<DataGridColumnAndDirection> SortOrder { get; set; }
	...
}

public struct DataGridColumnAndDirection
{
	public readonly DataGridColumn Column;
	public readonly DataGridSortDirection SortDirection;
	public DataGridColumnAndDirection(DataGridColumn, DataGridSortDirection) { ... }
}

Note I deliberately wrote IReadOnlyList<DataGridColumnAndDirection> not IList<DataGridColumnAndDirection> above because apps shouldn't be able to modify the list returned by the SortOrder property. Instead of modifying the list, apps should set the SortOrder property to a list that DataGrid will copy and use to completely replace the entire sort order list. Thus it would operate in one hit and avoid multiple expensive resorts.

When DataGrid.SortOrder is set to a new list, DataGrid would also set the corresponding DataGridColumn.SortDirection property in each column in the new SortOrder list, but without causing multiple resorts. Thus DataGrid.SortOrder[i].SortDirection is equivalent to DataGridColumn.SortDirection.

Get and set displayed column order in one hit

When apps are opened and closed, they need the ability to save and restore the end-user's ordering of the columns, preferably in one hit. I know that the preexisting property DataGridColumn.DisplayIndex is settable, therefore theoretically apps could restore the end-user's configuration by iterating through all columns in DataGrid.Columns and set the DisplayIndex of each, but this might cause bugs. For example, DisplayIndex might fail or behave in an unexpected manner when an app needs to temporarily set the DisplayIndex of one column to the same DisplayIndex of another column. Also, if it isn't performed in one hit, then it could trigger multiple expensive redraws and/or recalculations.

Therefore I suggest making a ColumnDisplayOrder property in DataGrid:

public IReadOnlyList<DataGridColumn> ColumnDisplayOrder { get; set; }

The same as I mentioned previously for SortOrder, ColumnDisplayOrder is deliberately IReadOnlyList<DataGridColumn> not IList<DataGridColumn> because it should operate in one hit.
When DataGrid.ColumnDisplayOrder is set to a new list, DataGrid would update the DataGridColumn.DisplayIndex of each column to match.

Allow end-users to hide/show columns

Consistent with the preexisting properties DataGridColumn.CanUserReorder etc, I suggest making a CanUserChangeVisibility property in DataGridColumn. When DataGridColumn.CanUserChangeVisibility is true, the end-user would be able to cause changes to the property DataGridColumn.Visibility.

Support GUI elements generated dynamically at runtime

Allow the row-details GUI to be generated without requiring the use of DataGrid.RowDetailsTemplate (Windows.UI.Xaml.DataTemplate). In order to achieve this, some thought needs to be put into the API, but here's my first idea for how to do it: Maybe do it via the preexisting event DataGrid.LoadingRow by changing the property DataGridRowDetailsEventArgs.DetailsElement to be settable so that the event handler can generate (or update) the UIElement by itself and give it to DataGrid by setting the DetailsElement property.

The reason why mandatory use of Windows.UI.Xaml.DataTemplate is a problem: Although templates are a good feature, the template concept expects a precreated unchanging XAML snippet/document that was written by the developer in advance. Thus templates are a problem when the app needs to dynamically generate the GUI at runtime. Thus I wish for the aforementioned alternatives to DataTemplate.

Drag-and-drop of rows, inwards and outwards

I suggest making the ability to optionally enable outgoing drag-and-drop of rows. DataGrid would implement the drag but not the drop. When the drop occurs, DataGrid would trigger an event and the app responds to the event in order to perform the desired action when the row is dropped outside of the DataGrid.

Also the ability to optionally enable incoming drag-and-drop of rows or items/objects. DataGrid would accept incoming drag-and-drop but does not implement the drop. DataGrid would trigger an event when the row/item/object is dropped, and the app responds to the event in order to to perform the desired action when the row is dropped inside the DataGrid.

Individual cell backgrounds

We have a feature request from a customer who wants individual cells to be hilited (by changing the cell background color/brush) at certain times (such as when our app detects changes or important values or dangerous values exceeding safety limits etc).

Thus I could propose a DataGridCell.Background property (of type Brush), but I think this way of doing it is probably defective because DataGrid recycles DataGridCell instances, doesn't it? So I guess that hiliting individual cells by setting a DataGridCell.Background property would be an unreliable solution.

A better way might be via the event DataGridColumn.DisplayingCell that I proposed in my previous message. The event handler could set a CellBackgroundBrush property in DataGridDisplayingCellEventArgs.

Dumb-down DataGrid's retrieval of data (cell values)

I know "dumb" sounds very bad, but in reality I would love it if DataGrid was made dumb in the area of data-retrieval (retrieval of the cell values to be displayed by DataGrid).

Currently DataGrid attempts to perform data retrieval in a convenient automatic manner via its property DataGrid.ItemsSource plus data-binding techniques (the property DataGridBoundColumn.Binding), and interfaces such as System.ComponentModel.INotifyPropertyChanged.

Yes data-binding is a convenient feature, but actually I'd much prefer to have DataGrid do less in that department! Several Microsoft components actually cause large difficulties for me via their behavior of trying to be smart but actually ending up being too smart. Overall I'd actually experience fewer difficulties and less work if these components would cease trying to be so smart/automatic/convenient. When designing an automatic behavior, I find it worthwhile to keep in mind that automatic sounds great but has the potential to backfire.

"Just simply let me do it by myself."
Sometimes the best solution is to just simply allow apps to perform the task by themselves, instead of trying-and-failing to do it "automatically" inside a component such as DataGrid. I wish that DataGrid would allow me to completely perform the data retrieval by myself, instead of trying to automatically retrieve the data via the properties DataGridBoundColumn.Binding and DataGrid.ItemsSource.

Ideally I'd like to eliminate the requirement to use the property DataGrid.ItemsSource to supply the items. @dotMorten mentioned a million rows. Currently DataGrid makes it mandatory for apps to set DataGrid.ItemsSource, but it is impractical to create a list object that contains a million items. Thus I wish that DataGrid would stop being smart in the data-retrieval department and instead let me completely perform the data retrieval by myself, without using DataGridBoundColumn.Binding and DataGrid.ItemsSource.

@anawishnoff wrote:

Performance is definitely one of the main motivations for this project and one of the main improvements we want to implement, through a new data virtualization story and the use of modern controls such as ItemsRepeater.

I see you said the new data virtualization story will improve performance, and that's good news, but will it also eliminate the requirement to use the property DataGridBoundColumn.Binding in order to supply the cell values that DataGrid displays?

If the entire data retrieval job is outsourced to the app that uses DataGrid, then the app has complete flexibility to perform the data retrieval in any manner that it needs. This would enable many different data-retrieval scenarios, including the million rows that @dotMorten mentioned. Dumb it down, please 😄

A few unnecessary features in WPF DataGrid?

The WPF version of DataGrid has a few features that seem unnecessary or impractical, and these features could be skipped in WinUI DataGrid, but I expect that some people probably have a different opinion about these features.

How many people would raise an objection if WinUI DataGrid abandoned the feature where end-users are able to edit cell values inline/directly inside the DataGrid? In our case, in every place where we use DataGrid, we always set DataGrid.IsReadOnly to true to disable the inline/direct editing feature. We allow users to edit the selected row in a separate pane or panel, or in RowDetails (DataGrid.RowDetailsTemplate), but not directly inside the DataGrid itself.

It seems like WinUI DataGrid has already moved in the direction of dropping support for inline/direct editing because WPF DataGrid had CanUserAddRows and CanUserDeleteRows but these properties don't exist anymore in WinUI DataGrid. Why not go all the way to dropping the inline/direct editing feature entirely? How many people have objections to this idea?

Individual cell selection: WPF DataGrid has a SelectionUnit property that you can set to Cell, FullRow, or CellOrRowHeader. In our case, we always set it to FullRow and never found a need to set it to Cell. However I see one person here has already requested single cell selection mode. My question is whether cell selection mode is a common or rare request.

On the other hand, individual cell selection might be useful when the user copies rows/cells to the clipboard. It would allow users to copy an individual cell (or several contiguous cells) to the clipboard instead of copying the entire row. However I'm unsure whether this feature is truly valuable or not.

Dubious Feature in WPF Comments
DataGrid.IsReadOnly Maybe no real need to ever set IsReadOnly to false?
DataGrid.CanUserAddRows Doesn't exist in WinUI DataGrid. Already abandoned, or maybe not yet implemented (I don't know the plan).
DataGrid.CanUserDeleteRows Doesn't exist in WinUI DataGrid. Already abandoned, or maybe not yet implemented (I don't know the plan).
DataGrid.SelectionUnit Doesn't exist in WinUI DataGrid.

@thomasclaudiushuber
Copy link
Contributor

thomasclaudiushuber commented Oct 31, 2019

Wow, incredible discussion here. ❤️

I also agree to the WPF DataGrid API. The WPF DataGrid is really the DataGrid I want for WinUI 3.0.

But even the WPF DataGrid lacks some features. In the past, most of my customers used a more powerful 3rd party DataGrid to get these features:

  • Filters in the Column Headers
  • A complete filter row at the top of the DataGrid
  • Powerful display of hierarchical data. Some 3rd party DataGrids really work like a powerful TreeView with nested DataGrids. While the WPF DataGrid allows to build something like that, it doesn't support it out of the box
  • Support for different data types. Like for example a DateTime column. In WPF's DataGrid you have to use the DataGridTemplateColumn

But anyway, what ever you do, I think building a DataGrid is a powerful statement and commitment to WinUI 3.0. For LOB applications you can't take a UI framework serious that doesn't contain a built-in DataGrid. And it's great to see this investment made for WinUI!

@verelpode
Copy link

@thomasclaudiushuber

For LOB applications you can't take a UI framework serious that doesn't contain a built-in DataGrid. And it's great to see this investment made for WinUI!

Agreed! In our case, DataGrid is a critically essential component that we use in multiple places and cannot live without.

Powerful display of hierarchical data. Some 3rd party DataGrids really work like a powerful TreeView with nested DataGrids.

I'm torn on that topic because -- like you -- I would find a hierarchical DataGrid useful in some situations, but my concern is that if DataGrid does implement hierarchical functionality, then it could become excessively complex and difficult, and the release of DataGrid would most likely be delayed substantially. New features would be difficult to add because of the complexity of making new features compatible with both hierarchical and non-hierarchical functionality.

To eliminate the aforementioned complexity/delays problem, I suggest this solution: Make a separate class named HierarchicalDataGrid that internally uses/wraps an instance of DataGrid. Alternatively HierarchicalDataGrid could publicly inherit DataGrid but I suspect this inheritance is impractical, therefore I prefer to suggest that HierarchicalDataGrid inherit Control and internally have a private field of type DataGrid. Thus the hierarchical functionality would be implemented in the HierarchicalDataGrid layer that directs an internal non-hierarchical DataGrid instance.

The key advantage of the above solution is that would deliver the desired hierarchical features without bogging down DataGrid, without making DataGrid so complex that it becomes unmanageable and slow to develop.

Support for different data types. Like for example a DateTime column.

I agree. Check out the DataGridDateTimeColumn and DataGridTimeSpanColumn that I proposed in a previous message.

@verelpode
Copy link

@Pinox

Easy filter the whole page

@thomasclaudiushuber

Filters in the Column Headers; A complete filter row at the top of the DataGrid;

Filters would be great for helping end-users quickly find a row or rows, instead of the current situation of being forced to manually look at a large number of rows to find what they're looking for.

But what about the problem of matching the user's filter text with non-text columns? Meaning columns other than DataGridTextColumn, such as filtering with DataGridTemplateColumn etc? I suggest solving this with something approximately like the following solution that gives DataGridColumn (and subclasses) the ability to convert a cell value to keywords for use with searching/filtering/matching.

public delegate void DataGridCellValueToKeywordsConverter(object cellValue, ICollection<string> outputKeywords);

public class DataGridColumn
{
	...
	public DataGridCellValueToKeywordsConverter CellValueToKeywordsConverter { get; set; }

	/// <param name="cellValue">The input/source value of the cell, to be converted to keywords.</param>
	/// <param name="outputKeywords">A collection that the method will add the keywords to.  The method invokes ICollection.Add for each keyword.</param>
	public virtual void ConvertCellValueToKeywords(object cellValue, ICollection<string> outputKeywords)
	{
		// Subclasses can override this method.  The default behavior is to invoke the delegate to do the job.
		DataGridCellValueToKeywordsConverter d = this.CellValueToKeywordsConverter;
		if (!(d is null)) d(cellValue, outputKeywords);
	}
}

Here is an example of how to use the ConvertCellValueToKeywords method:

void TestKeywords(DataGridColumn column)
{
	var keywords = new System.Collections.Generic.HashSet<string>();
	foreach (object cellValue in someCellValueList)
	{
		keywords.Clear();
		column.ConvertCellValueToKeywords(cellValue, keywords);
		CheckIfFilterTextMatchesAnyKeyword(keywords);
	}
}

Alternatively, the cell value could be converted to a single string instead of a collection of keywords, but the reason why I suggested a keywords collection is that keywords make it possible to use searching algorithms that run at high speed even when the quantity of rows is very large.

@cirrusone
Copy link

Blazor is now sampling an experimental data grid package QuickGrid which I believe could be used across different frameworks using Blazor hybrid. Performance is great but you would need to add your own functionality if you need custom filtering etc.

Lack of a decent WinUI grid is definitely holding back adoption of WinUI in business.

dotnet/aspnetcore#37908 (comment)

@MEK3DK
Copy link

MEK3DK commented Jul 5, 2022

I know this isn't really the thread for it, but @MEK3DK what would be the current app dev platform to build line-of-business-apps, with C#/.net, and with a good Datagrid component? Was Winforms the last platform that had a rocksolid msft-original Datagrid?

The safest bet is likely WPF. The big question is when (and if) Microsoft finish WinUI 3, but if they do then you should be able to move to WinUI 3 without to much work.

As the others suggest Blazor could also be an option, but depends on your app needs. I have no knowledge of the DataGrids in Blazor.

@StevoSM
Copy link

StevoSM commented Jul 5, 2022

@thomasclaudiushuber @MEK3DK Just to continue this off-topic question a bit more - what would you recommend for business apps with a heavy C++ cross-platform data model that also need heavy use of a Datagrid component?

@nlogozzo
Copy link

nlogozzo commented Jul 5, 2022

@StevoSM QT. It's cross platform and applications are written in C++ so must if the code should stay the same.

@robloo
Copy link
Contributor

robloo commented Jul 5, 2022

For those staying in the .NET/C# ecosystem recommend switching to Avalonia. They accept changes to the DataGrid though which is from the same code base as the WCT. Avalonia is what Microsoft should have done 10 years ago.

@tomasfil
Copy link

tomasfil commented Jul 5, 2022

Can someone please clean this up and keep it about winUi 3? I dont mind helping with current issue, but this is not the place to promote Avalonia nor chat room.

@robloo
Copy link
Contributor

robloo commented Jul 5, 2022

Microsoft is no longer accepting any changes to the Windows Community Toolkit DataGrid. The DataGrid issue here in WinUI3 is delayed indefinitely. The existing DataGrid has too many shortcomings for a lot of apps.

We are discussions what apps can do that require more from their DataGrid. 3rd party controls and switching to other frameworks is absolutely relevant with the mentioned constraints Microsoft had imposed.

@dotMorten
Copy link
Contributor

@robloo It's relevant - but it is not relevant here. This thread has become a mess of off-topic discussions (include my comment here - sorry). Hopefully we can agree to end it here, and continue that discussion elsewhere.

@Christopher-Ingram
Copy link

Any movement on this fundamentally important control? Having tried to use the WCT Datagrid with limited success in Winui 3 it would be encouraging to hear from MS that after the passage of several years, plenty of suggestions, and plenty of third-party alternatives to glean functionality from, there is a firm intention and timescale to deliver this

@heartacker
Copy link

It seems that the progress has become far away, now is the era of artificial intelligence, AI can better help to write code, whether can let the code implementation become faster?

@rept0id
Copy link

rept0id commented May 18, 2023

.

@DJRM2021
Copy link

Any updates on this matter?

@kmgallahan
Copy link
Contributor

@DJRM2021

If not using C++:

@vflich
Copy link

vflich commented Nov 13, 2023

Hello! I'm beginner in WinUI 3 in C++. Interested in a DataGrid for my applications. Any update about it?

We are finishing 2023 and see no news about a DataGrid native control en WinUI 3. Maybe any plan to implement soon?

@DJRM2021
Copy link

Hello! I'm beginner in WinUI 3 in C++. Interested in a DataGrid for my applications. Any update about it?

We are finishing 2023 and see no news about a DataGrid native control en WinUI 3. Maybe any plan to implement soon?

CommunityToolkit/Labs-Windows#415 or use syncfusion

@nikolayvpavlov
Copy link

There will be no DataGrid native control, at least not called so. According to latest roadmap, Microsoft are planning a TableView control.

@AndrewKeepCoding
Copy link
Contributor

I guess the team should close this discussion with a comment.

@ghost1372
Copy link
Contributor

Hello! I'm beginner in WinUI 3 in C++. Interested in a DataGrid for my applications. Any update about it?

We are finishing 2023 and see no news about a DataGrid native control en WinUI 3. Maybe any plan to implement soon?
In v1.5 we will see a new conrrol called tableview which is a simple and fast datagrid. So you should wait for it.

@cirrusone
Copy link

I guess the team should close this discussion with a comment.

I agree but It's a shame as this issue was actually opened by MS to canvas opinion on a new Datagrid and then abandoned. Blazor recognized need for a datagrid and developed QuickGrid for Blazor in almost no time at all.

Desktop UI on .NET is a complete mess right now. I know many devs who are abandoning .NET for any form of desktop UI and that includes myself at the moment (I've switched language to get better GUI support). MS doesn't really have anything today that is usable without major issues and with no real guidance provided the fear is these new tools will also be 'Silverlighted'.

Sorry to put a downer on net8.0 launch day.

@nikolayvpavlov
Copy link

I agree but It's a shame as this issue was actually opened by MS to canvas opinion on a new Datagrid and then abandoned. Blazor recognized need for a datagrid and developed QuickGrid for Blazor in almost no time at all.

In all fairness, Table View is just that: a successor to Data Grid, because, to be honest, the Data Grid from WCT is bloated, just like most grids from third party devs. A lean, fast and simple control with full MVVM focus is all we need.

Desktop UI on .NET is a complete mess right now. I know many devs who are abandoning .NET for any form of desktop UI and that includes myself at the moment (I've switched language to get better GUI support). MS doesn't really have anything today that is usable without major issues and with no real guidance provided the fear is these new tools will also be 'Silverlighted'.

You are right, but the mess has been around ever since Windows 8, where the roadmap became vague as Longon fog. Today the only really usable thing is WinForms and WPF to a good extend. Or WinRT, if you are a C++ guy. But, to be honest, I also don't know any other good tools for Windows native development.

@robloo
Copy link
Contributor

robloo commented Nov 14, 2023

Desktop UI on .NET is a complete mess right now. I know many devs who are abandoning .NET for any form of desktop UI and that includes myself at the moment (I've switched language to get better GUI support). MS doesn't really have anything today that is usable without major issues and with no real guidance provided the fear is these new tools will also be 'Silverlighted'.

You are right, but the mess has been around ever since Windows 8, where the roadmap became vague as Longon fog. Today the only really usable thing is WinForms and WPF to a good extend. Or WinRT, if you are a C++ guy. But, to be honest, I also don't know any other good tools for Windows native development.

Avalonia UI has fulfilled all needs in this space. With v11 it is feature complete to be at WPF's-level across all desktop platforms (more like WPF 2.0 with the more powerful styling system, et al.). I've personally ported a moderately sized UWP app to Avalonia and it actually makes quite a few things better. UWP needed a lot of work-arounds and was missing way more functionality compared to WPF. Avalonia UI is much easier to use across the board: Fully featured (more than UWP), More advanced features, fully cross-platform, right at home on Windows, Latest C# support, AOT support, better/faster dev-loop. I won't comment further as people become pretty polarized on this but it's undeniably a better road ahead than WinUI.

@nikolayvpavlov
Copy link

Avalonia UI has fulfilled all needs in this space...

Please. Every time there is a discussion about any Xaml-based development, a guy from Avalonia will jump into it and start preaching offtopic. I am not willing to bet my client's future on a third-party library with a tiny community and loud, arrogant attitude.

@robloo
Copy link
Contributor

robloo commented Nov 14, 2023

@nikolayvpavlov

Please. Every time there is a discussion about any Xaml-based development, a guy from Avalonia will jump into it and start preaching offtopic. I am not willing to bet my client's future on a third-party library with a tiny community and loud, arrogant attitude.

I have personally used WPF, UWP and soon Avalonia in commercial/professional apps. My day job is not Avalonia and is in fact WPF at present. There is no arrogance here other than what you bring. I am simply saying that from a high level other options are on the table. It's wise to fully consider them especially since Microsoft has moved away from this space and the WinUI dev team is probably around 5 developers.

You've got me to reply as I feel your response is unnecessarily antagonistic and missing the point; I'll leave it at that as we are now off topic.

@duncanmacmichael
Copy link
Member

Thank you for your comments, everyone! We really appreciate all the insightful feedback. As @nikolayvpavlov mentioned up above, we are indeed focusing on a Table View control moving forward. As mentioned in the 1.5 roadmap, we are doing initial investigations and design right now and will continue communicating further progress and timelines when we have them. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion General discussion wct
Projects
No open projects
Controls Triage
Needs triage
Development

No branches or pull requests