Home > Software design >  WPF - UserControl constructing performance (very poor)
WPF - UserControl constructing performance (very poor)

Time:03-07

I have a more complex code on my hand, but to ask this question I am bringing a simpler example of code.

My App is going to iterate throughout all glyphs in a specific font (expected 500 to 5000 glyphs). Each glyph should have a certain custom visual, and some functionality in it. For that I thought that best way to achieve that is to create a UserControl for each glyph.

On the checking I have made, as my UserControl gets more complicated, it takes more time to construct it. Even a simple adding of Style makes a meaningful effect on the performance.

What I have tried in this example is to show in a ListBox 2000 glyphs. To notice the performance difference I put 2 ListBoxes - First is binding to a simple ObservableCollection of string. Second is binding to ObservableCollection of my UserControl.

This is my MainWindow xaml:

    <Grid Background="WhiteSmoke">
      <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
      </Grid.RowDefinitions>
      <ListBox Margin="10" ItemsSource="{Binding MyCollection}"></ListBox>
      <ListBox Margin="10" Grid.Row="1" ItemsSource="{Binding UCCollection}"
             VirtualizingPanel.IsVirtualizing="True"
             VirtualizingPanel.VirtualizationMode="Recycling"></ListBox>
    </Grid>

On code behind I have 2 ObservableCollection as mentioned:

public static ObservableCollection<string> MyCollection { get; set; } = new ObservableCollection<string>();
public static ObservableCollection<MyUserControl> UCCollection { get; set; } = new ObservableCollection<MyUserControl>();

For the first List of string I am adding like this:

    for (int i = 0; i < 2000; i  )
    {
       string glyph = ((char)(i   33)).ToString();
       string hex = "U "   i.ToString("X4");
       MyCollection.Add($"Index {i}, Hex {hex}:  {glyph}");
    }

For the second List of MyUserControl I am adding like this:

    for (int i = 0; i < 2000; i  )
    {
       UCCollection.Add(new MyUserControl(i   33));
    }

MyUserControl xaml looks like this:

<Border Background="Black" BorderBrush="Orange" BorderThickness="2" MinWidth="80" MinHeight="80">
    <Grid Margin="5">
      <Grid.RowDefinitions>
        <RowDefinition Height="2*"></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
      </Grid.RowDefinitions>
      <TextBlock HorizontalAlignment="Center" Foreground="White" FontSize="40" Text="{Binding Glyph}"/>
      <TextBlock HorizontalAlignment="Center" Foreground="OrangeRed" Text="{Binding Index}" Grid.Row="1"/>
      <TextBlock HorizontalAlignment="Center" Foreground="White" Text="{Binding Hex}" Grid.Row="2"/>
    </Grid>
  </Border>

And code behind of MyUserControl:

    public partial class MyUserControl : UserControl
    {
        private int OrgIndex { get; set; } = 0;
        public string Hex => "U "   OrgIndex.ToString("X4");
        public string Index => OrgIndex.ToString();
        public string Glyph => ((char)OrgIndex).ToString();

        public MyUserControl(int index)
        {
            InitializeComponent();
            OrgIndex = index;
        }
    }

In order to follow the performance issue I have used Stopwatch. Adding 2000 string items to the first list took 1ms. Adding 2000 UserControls to the second list took ~1100ms. And it is just a simple UserControl, when I add some stuff to it, it takes more time and performance getting poorer. For example if I just add this Style to Border time goes up to ~1900ms:

     <Style TargetType="{x:Type Border}" x:Key="BorderMouseOver">
      <Setter Property="Background" Value="Black" />
      <Setter Property="BorderBrush" Value="Orange"/>
      <Setter Property="MinWidth" Value="80"/>
      <Setter Property="MinHeight" Value="80"/>
      <Setter Property="BorderThickness" Value="2" />
      <Style.Triggers>
        <DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" Value="True">
          <Setter Property="Background" Value="#FF2A3137" />
          <Setter Property="BorderBrush" Value="#FF739922"></Setter>
        </DataTrigger>
      </Style.Triggers>
    </Style>

I am not fully familiar with WPF work around, so I will really appreciate your help. Is this a totally wrong way to do this? I have read some posts about it, but could not manage to go through this: enter image description here

  • Related