0

I am creating dynamic UI based on some objects and the loading takes a lot of time because of the large number of items to be rendered. See below xml:-

 <ScrollViewer Grid.Row="2" ZoomMode="Enabled" x:Name="scrollviewer"  
        VirtualizingStackPanel.VirtualizationMode="Recycling"
        HorizontalScrollMode="Auto"
        VerticalScrollMode="Auto"
        VerticalSnapPointsType="None"
        HorizontalSnapPointsType="None"
        HorizontalScrollBarVisibility="Auto"
        VerticalScrollBarVisibility="Auto"
        MinZoomFactor="1" IsDoubleTapEnabled="True"
        RenderTransformOrigin="0,0">
        <Grid Background="#FFDDE6EB" x:Name="mygrid" VerticalAlignment="Center" HorizontalAlignment="Center">

        </Grid>
    </ScrollViewer>

The scrollviewer is inside a row with height as 10*.

Code to load items dynamically is below:-

  public static Grid LoadGridWithItems(Model m)
        {

            var grid = new Grid();
            grid.UseLayoutRounding = true;
            grid.RowDefinitions.Clear();
            grid.ColumnDefinitions.Clear();

            int count = m.Rows;
            for (int i = 0; i < count; i++)
            {
                grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) });
            }

            count = m.Columns;
            for (int i = 0; i < count; i++)
            {
                grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(m.GetColumnWidth(i), GridUnitType.Star) });
            }
            foreach (Control control in m.Controls)
            {
          UIElement uicontrol=null;
                if (control.Type != null)
                {

                    switch (control.Type.ToLower())
                    {
                        case "textbox":
                             uicontrol=new TextBox();
                            break;
                        case "label":
                            uicontrol = new Label();;
                            break;

                      //bunch of other custom controls
                    }
                  }


                grid.Children.Add(uicontrol);
            }
            return grid;
        }

This generated grid is then added as a child to the "mygrid" defined in above xaml. Sometimes items are more than 5000 and it takes around 20-25 seconds to load. Any suggestions on how to implement this scenario in a better way?

Thanks!

AjS
  • 341
  • 2
  • 13
  • 1
    Any reason why you are not using MvvM? I'm asking because if your requirements allow it you could use `ListView` with `Virtualization` which means it will create items only when they are brought to view. It will be significant performance boost as it would render 100 items instead of 5000. – XAMlMAX Aug 04 '17 at 07:08
  • the only reason mvvm cannot be used is because of the fact that each row has different combinations of control and hence there is no single data template i can use. The controls added to grid still binds to the properties in the 'control' class but its just that I am not sure how to generate a different template for each row. – AjS Aug 04 '17 at 08:26
  • Have a look at a `TemplateSelector` you will have the ability to choose which template you want to apply and if you change the container to `ListView` then you can use mentioned before `Virtualization`. – XAMlMAX Aug 04 '17 at 09:43

2 Answers2

2

We have the same issue of the slowness of adding child controls. A possible reason for it is that they are being added bottom up (in the tree) whereas adding them top down is inordinately faster. It is all explained in the following site: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/optimizing-performance-layout-and-design The pages linked from that are a goldmine of how to get the most from WPF.

CSharpie
  • 9,195
  • 4
  • 44
  • 71
1

You could improve your code quality and readability if you use an ItemsControl and MVVM. Furthermore, I think performance will be boosted. Something like this:

<ItemsControl ItemsSource="{Binding yourControlCollection}">
  <!-- Templates for your controls -->
  <ItemsControl.Resources>
    <DataTemplate DataType="{x:Type yourNameSpace:ComboBoxControl}">
        <ComboBox />
    </DataTemplate>

    <DataTemplate DataType="{x:Type yourNameSpace:TextControl}">
        <TextBox />
    </DataTemplate>
  </ItemsControl.Resources>
</ItemsControl>

Also, it will be easier for you to get the values that user introduces on the form. You can binding the value to a property in the model classes and you have it.

public class TextControl
{
  public string TextValue { get; set; }
}

.

<DataTemplate DataType="{x:Type yourNameSpace:TextControl}">
  <TextBox Text="{Binding TextValue}" />
</DataTemplate>

Edit. Sample:

xaml:

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication2"
    Title="MainWindow" Height="350" Width="525">
<ItemsControl ItemsSource="{Binding List}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type local:ControlText}">
            <StackPanel Orientation="Horizontal" Background="Yellow">
                <TextBlock Text="{Binding Label}" />
                <TextBlock Text="{Binding Text}" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:ControlBool}">
            <StackPanel Orientation="Horizontal" Background="Orange">
                <TextBlock Text="{Binding Label}" />
                <CheckBox IsChecked="{Binding Value}" />
            </StackPanel>
        </DataTemplate>

    </ItemsControl.Resources>
</ItemsControl>

cs:

using System;
using System.Collections.Generic;
using System.Windows;

namespace WpfApplication2
{
public partial class MainWindow : Window
{
    public IEnumerable<Control> List { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        DataContext = this;

        var l = new List<Control>();
        l.Add(new ControlText() { Text = "Michael", Label = "Name" });
        l.Add(new ControlBool() { Value = true, Label = "C#" });
        l.Add(new ControlBool() { Value = false, Label = "WPF" });
        l.Add(new ControlText() { Text = "Martinez", Label= "Surname" });

        List = l;
    }
}

public abstract class Control
{
    public String Label { get; set; }
}

public class ControlText : Control
{
    public String Text { get; set; }
}

public class ControlBool : Control
{
    public Boolean Value { get; set; }
}
}

:D

Lamelas84
  • 992
  • 1
  • 6
  • 20
  • each row have different combinations of control so a single data template cannot be used and hence I am using this way to dynamically generate the content of the container grid. – AjS Aug 04 '17 at 08:23
  • You can have multiple DataTemplate, one for each type of object you have on the collection. – Lamelas84 Aug 04 '17 at 08:31
  • Sorry but I can only set the DataTemplate on ItemsControl to a single template; and that particular template will apply to all rows in ItemsControl. Where and how can I set multiple template on ItemsControl? – AjS Aug 04 '17 at 08:35