|
Displaying a "collection of UI CONTROLS" in a data grid is probably the worst thing you can do with a data grid for a "large collection".
You're supposed to bind data grid "cell types" to a "DATA collection" if you want to "virtualize" (the UI).
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Thanks. Can you expand on your answer? I don't know what you mean by data grid "cell types".
I assume you are saying that I should not create an instance of a UI control for each data item. Instead I should have a minimal set of UI controls, and change the data each displays. Clearly for a uniform grid where all controls are the same, or all cells in a given column are the same, that is easy, and it can be done in the view model. However, I might have one row of check boxes, followed by several rows of combo boxes, followed by several rows of hex number edit boxes etc and this can be scrolled vertically.
|
|
|
|
|
You need to be more specific than "rows of checkboxes and combo boxes";
I've used "list views" to display "user controls" that contained "rows".
I said before that there was more than one way to get a "grid effect"; your description of "why" and from "what" is non-existent so it is hard to make more concrete suggestions.
ListView basics and virtualization concepts – Blog Alain Zanchetta
Bottom line: no "data binding", no virtualization.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
modified 15-Jun-18 14:47pm.
|
|
|
|
|
Some requirements were given in my previous question along with example code, that you replied to, however it was a long post.
We need to display tabular data, in a table with rows and columns. A given data value can be a Boolean, or a combo box selection, or a numeric value (signed or unsigned). In general each value is editable. The table of settings is to be displayed in a WPF view and hence the size will depend on the view size. Clearly not all of the items will be visible to the user as the table is so large, so there will be vertical and horizontal scroll bars.
I had hoped that the DataGrid implemented virtualisation well enough to reduce the overhead, but it appears that WPF is very very memory intensive. When I turn on virtualisation for both columns and rows, the performance is unacceptable (slow).
I haven't tried the list view. I guess I could define a list view item as a GRID control with one row, and multiple columns and use the
SharedSizeGroup property to create columns, assuming that would work, but I can't see why it would reduce the massive memory overhead.
A possible solution I am working on is to use a DataGrid with each item being a Label control. That massively reduces the memory usage. Double clicking on an item makes it editable, displaying the appropriate edit control e.g. CheckBox.
|
|
|
|
|
You still haven't provided any sense of the logic or purpose for the "UI".
All data has some sort of "structure" that drives the design.
Nothing you've described justifies your reasoning about the "technical" details.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Why not change your UI design to a more sensible layout. List all the setting values in a grid but make the user edit them in a dialog box. The user should have to double click on the grid row he wants to edit, pop a dialog with the data from that row, allow the user to edit the data of the single data row in discreet controls, save the data back to the underlying collection.
The design eliminates the need to editable controls in the data grid and will simplify your life enormously.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Thanks. I might well use that approach, depending on how much progress I make.
Currently I am implementing a solution whereby the grid is read only but when the user double clicks on a cell, it becomes editable. So instead of plain text, they now see a combo box, or a tick box, or a numeric value editor. Double click elsewhere, and the cell becomes read only. This works well with the Microsoft WPF DataGrid though the memory usage is still rather high. The non editable cell control is a Label.
I am also looking at the WPF Table View control on CodePlex. As yet I cannot find out how to make the current cell active when double clicked on, but the memory usage and speed are better. Clearly I can easily pop up a dialog to edit a cell, or a row as you suggest since I can trap the mouse double click.
From searching around it does seem that the performance of grids is a widespread problem.
|
|
|
|
|
Don't use a Label if you can help it. Use a TextBlock instead. A Label is, relatively speaking, a much heavier weight control than a TextBlock.
This space for rent
|
|
|
|
|
Thanks, that's very helpful.
|
|
|
|
|
You're welcome.
This space for rent
|
|
|
|
|
That design would drive nuts, double click to edit a single cell, double click to end editing, rinse and repeat. Plus you have horizontal scroll so you cannot see the entire row of data.
I use the dialog style because the user can double click on a row and see and edit the entire set of data. The users often double click the row just to view the data.
I have a rule that no more than 2 fields may be edited in a grid and absolutely no controls other than a textblock/box are to be used in a grid.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
I agree. However, this is an application for engineers rather than general consumers, and this particular view displays configuration settings that will only be examined by two or three people who are based in our company (super users). The values are rather abstruse, and control fine details of a display device. Most of the time users will just examine the values, and maybe tweak one or two. For general editing we stream the data to and from a .CSV file. So in practice this horrible view will be okay.
|
|
|
|
|
Leif Simon Goodwin wrote: this is an application for engineers You don't like your engineers!!! Sorry but that is no excuse for a lousy design.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
This is the design requested by the firmware engineers including the technical director, and does all that they need. They have the same layout in the current application, which the new one replaces.
|
|
|
|
|
Could you put up a mocked up screenshot so that we can get a better idea of what you're after?
This space for rent
|
|
|
|
|
Probably not for reasons of commercial secrecy. What I am working on has NDA agreements in place.
However, the current implementation now works well. We generate and compile the form code on the fly, and the form runs pretty quickly to provide a decent user experience. I am using the extended WPF toolkit DataGrid in place of the standard DataGrid which has solved the performance issues.
From searching on the internet, it's clear that the performance of data grids is an issue for a lot of people, and I am sure many have their own custom implementations.
I might be able to significantly reduce the memory footprint, and increase execution time but it would require significant development time which we do not have. We need to focus on the features that our customers will use, rather than gold plate something used from time to time by a couple of our own engineers.
|
|
|
|
|
I accidentally marked your post as trolling. Apologies for the error, your posts are constructive.
|
|
|
|
|
Don't worry about it. I have the feeling you're going to have to drop back to dotTrace to trace the performance of what's going on.
This space for rent
|
|
|
|
|
I get the impression you don't "understand" the purpose of these "configuration settings"; and because there are "a lot of them", a "grid" should do the trick.
Have you "talked" to the "engineers"?
WPF has the ability to "expand" "details" of a selected row.
One can have a list view of "grids"; etc.
What's missing is a UI "visual" designer; who's familiar with WPF (or not at all) ... oh, and familiar with the problem domain.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Are you always so completely obnoxious?
You come across as a troll. Every single one of your posts is a put down. The suggestions you make are vague abstract comments (the kind that someone with no real knowledge could make), with no helpful content and nothing to indicate any actual technical knowledge on your part. Do you have anything constructive to say or is your goal simply to piss on me? Frankly I am getting pissed off with your obnoxious trolling.
The layout was designed by the engineers. It's what they have asked for and what they want. They've seen the current prototype and like it. It's the same as in the previous application which has served them well.
|
|
|
|
|
You talk in abstracts.
You're looking for advice, but won't accept any in terms of being more clear about the "use case".
Whatever the "layout" the engineers decided, it's only known to you apparently.
It's simple: you don't understand "virtualization" and refuse to.
(Later: I get it now why you were being so obtuse... it was because of the "NDA"; you should have made that clear from the beginning; then I wouldn't have bothered trying to help).
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
modified 25-Jun-18 11:56am.
|
|
|
|
|
Get lost.
|
|
|
|
|
NB: The solution is at the end.
We need to configure a hard ware device, which provides configuration settings as tabular data. There are lots of different sets of tabular data. We want to display a given table in a view, so that the user can edit the fields. All fields in a given row have the same kind of data e.g. integer. However, one row might display integers, another might display a combo box, and another a check box. The problem is how to automatically create the WPF view and view model code.
The nature of the data in each table is defined in an XML file. Unfortunately for one table the number of columns is defined by a value in another table which is only known at run-time.
Thus we can create some tables at build time, but one must be created at run-time.
I created a parser which generates the WPF view and view model for each table of data. This parser can be run at run-time to generate view and view model source code, which is then compiled at run-time, and displayed. So far so good. This works well with one exception, as the number of fields is almost always modest.
Unfortunately one table has ~120,000 cells, and the resulting view model will not run as the number of controls exceeds the maximum allowed by WPF. The view will display and looks good. Clearly having so many properties in a view model is a tad ridiculous and crude.
So, we need a more elegant solution, and one which allows the values to be displayed in a table with cells all nicely aligned.
The view model contains a list of field objects, each of which provides data in the correct format for the corresponding table cell e.g. "true" and "false" for tick box. Is it possible to create a DataGrid, such that it gets its data from my table of fields, and displays the correct kind of control dependent on the nature of the data (which my field object knows)? Thus row 0 might be tick boxes, row 1 edit boxes, and row 2 combo boxes?
==================================================================
Solution:
I came across some example code that almost solved our problem, and with some changes it does the job in my demo application which I will explain below in case anyone finds this useful. The link is as follows:
WPF DataGrid and binding to matrix of objects using UserControl per cell[^]
The first step is to declare some data templates in the XAML:
<DataTemplate x:Key="LabelDataTemplate">
<Label Margin="2" Content="{Binding Text}" Background="White"/>
</DataTemplate>
<DataTemplate x:Key="TextDataTemplate">
<TextBox Margin="2" Text="{Binding Text}" Foreground="Green" Height="20"/>
</DataTemplate>
<DataTemplate x:Key="ComboBoxDataTemplate">
<ComboBox Margin="2" Text="{Binding ComboText}" ItemsSource="{Binding Values}" Foreground="Red" Height="20" VerticalAlignment="Center"/>
</DataTemplate>
<DataTemplate x:Key="CheckBoxDataTemplate">
<CheckBox Margin="2" IsChecked="{Binding IsChecked}" Foreground="Red" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
The data grid is very basic:
<DataGrid Grid.Row="2" Name="_dataGrid2" ItemsSource="{Binding Matrix}" AlternationCount="2" AutoGenerateColumns="False" Style="{StaticResource styleDataGridNoSelection}" HeadersVisibility="None">
The style simply makes it look nice for our usage. The key point is the binding to Matrix, which is a matrix of nodes:
namespace WpfGridDemo.Model
{
public class Node
{
public NodeType Type { get; set; }
public string Text { get; set; }
public bool IsChecked { get; set; }
public string ComboText { get; set; }
public List<string> Values { get; set; }
}
}
The type indicates whether it is text, a combo box, a checkbox or a label. In real code this would be better as a polymorphic base class, but since this was a quick demo, the above is good enough.
A Row class wraps an array of nodes and represents a row from the matrix:
namespace WpfGridDemo
{
public class Row
{
private string name;
private Model.Node[] values;
public string Name { get { return name; } }
public Model.Node[] Values { get { return values; } }
public int Index { get; set; }
const int constColumnCount = 40;
}
}
And the Matrix is defined in the view model as an array of rows:
private Row[] _matrix;
public Row[] Matrix { get { return _matrix; } }
So it is just a 2D matrix of rows.
In the main window class I set up the binding between the matrix nodes and data grid:
public MainWindow()
{
InitializeComponent();
MainViewModel viewModel = new MainViewModel();
DataContext = viewModel;
for (int i = 0; i < 40; i++)
{
DataGridTemplateColumn col = new DataGridTemplateColumn();
FrameworkElementFactory fef = new FrameworkElementFactory(typeof(ContentPresenter));
Binding binding = new Binding();
fef.SetBinding(ContentPresenter.ContentProperty, binding);
GridDataTemplateSelector gridDataTemplateSelector = new GridDataTemplateSelector();
gridDataTemplateSelector.LabelDataTemplate = Resources["LabelDataTemplate"] as DataTemplate;
gridDataTemplateSelector.TextDataTemplate = Resources["TextDataTemplate"] as DataTemplate;
gridDataTemplateSelector.CheckBoxDataTemplate = Resources["CheckBoxDataTemplate"] as DataTemplate;
gridDataTemplateSelector.ComboBoxDataTemplate = Resources["ComboBoxDataTemplate"] as DataTemplate;
fef.SetValue(ContentPresenter.ContentTemplateSelectorProperty, gridDataTemplateSelector);
binding.Path = new PropertyPath("Values[" + i + "]");
DataTemplate dataTemplate = new DataTemplate();
dataTemplate.VisualTree = fef;
col.CellTemplate = dataTemplate;
_dataGrid2.Columns.Add(col);
}
}
A key point is the DataTemplateSelector which tells the grid which data template to use for a given cell.
class GridDataTemplateSelector : DataTemplateSelector
{
public DataTemplate LabelDataTemplate { get; set; }
public DataTemplate TextDataTemplate { get; set; }
public DataTemplate CheckBoxDataTemplate { get; set; }
public DataTemplate ComboBoxDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
Model.Node node = item as Model.Node;
if (node != null)
{
if (node.Type == Model.NodeType.Label)
{
return LabelDataTemplate;
}
if (node.Type == Model.NodeType.Text)
{
return TextDataTemplate;
}
if (node.Type == Model.NodeType.CheckBox)
{
return CheckBoxDataTemplate;
}
if (node.Type == Model.NodeType.ComboBox)
{
return ComboBoxDataTemplate;
}
}
return TextDataTemplate;
}
}
I'm quite certain I would not have solved this without the online code I found. Hopefully I can put this into a little demo project in a short article as I feel it is useful.
modified 8-Jun-18 3:33am.
|
|
|
|
|
Leif Simon Goodwin wrote: Unfortunately one table has ~120,000 cells, and the resulting view model will not run as the number of controls exceeds the maximum allowed by WPF Virtualization will be your friend here. You don't actually need to hold all those cells in memory - especially as you wouldn't be able to physically see them on the screen at the same time.
This space for rent
|
|
|
|
|
The fact it "looks" like a grid, doesn't mean you need to use a "DataGrid" control.
You haven't even established a "view port"; i.e. how many cells you expect to "see" at any one time.
Then you can start thinking in terms of "pages" (and virtualization; as has been already suggested).
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|