I have a requirement to create a simple tool for creating a menu / selection - the idea is to have a choice of categories and a larger selection of options that are filtered by the selected category. Ultimately, this will work in a kiosk style environment.
I use MVVM, and my project consists of a view containing two grids inside two ItemsControls - one for categories (2 rows of 10 columns) and one for selection (10 x 10). In my ViewModel, these ItemsControls are bound to ObservableCollection objects with some details (caption, color, etc.) Bound to the properties of the part. The DataTemplate is bound to a class of elements in a separate assembly, since I want to reuse them in a kiosk application.
I used the "ModifiedBehaviours" class to display right and left clicks on my grid objects on commands selected by ViewModel similar to this
How can I attach two attached behaviors to a single XAML element?
The design seems to be “clean” from what I read (relatively new to MVVM here) in that the view code has nothing to do with it except assign a ViewModel to the DataContext window, no x: name = tags in the view, and ViewModel does not directly refers to the view.
However, I have performance issues.
When a user clicks on a category, I create a new ObservableCollection containing the elements of the part for him - I also fill in the blanks in design mode, so in the end I have 100 options, and the empty ones are “Right click to edit” in them.
The creation time of this collection is tiny - <0.01 s on a 1.6 GHz netbook (the PC kiosk will be slow, so I'm testing slow equipment). However, as soon as the collection related to control is updated, the user interface takes about 2 seconds to update, which is much slower than the previous application that I recreate - it is written in a very old Delphi.
I have tried different things. Firstly, I simplified my XAML by removing some gradients and other things to make it as simple as possible - eventually going down to one text block. Then, when updating the collection, I create it separately, and then assign it to be linked, so the update happens "all at once." Thirdly, I slightly violated my design and added BeginInit () and EndInit () around the update code for the window and grid.
None of them made the slightest improvement.
The following was done: this leads to my question.
- I removed one of the command actions from the control element - I need both, although for right and left clicks. Can this fact associate an event for each cell of an element (100 of them) with a command to cause a problem?
Is there an alternative approach to right and left clicks for each grid cell?
- <- this is 2 in my markup! I copied the XAML for the element (border, text block and commands) from a separate assembly into the main XAML window. This gave the biggest, simplest improvement, but I don't understand why.
Both separately changed the situation, both together made the performance "acceptable" - still very slow. Is there anything else I can look at?
My control looks like this (in a separate assembly). Before anyone marks as mentioned above, I tried to remove a lot of this and even cut it directly, just to be a text block.
<Control.Resources> <Style x:Key="MyBorderStyle" TargetType="Border"> <Style.Triggers> <DataTrigger Binding="{Binding IsSelected}" Value="True"> <Setter Property="BorderBrush" Value="Red"/> <Setter Property="BorderThickness" Value="3"/> </DataTrigger> <DataTrigger Binding="{Binding IsSelected}" Value="False"> <Setter Property="BorderBrush" Value="{Binding BackColour}"/> <Setter Property="BorderThickness" Value="0"/> </DataTrigger> </Style.Triggers> </Style> </Control.Resources> <Grid> <Border Margin="1" Style="{StaticResource MyBorderStyle}" CornerRadius="8"> <CommandBehaviour:CommandBehaviorCollection.Behaviors> <CommandBehaviour:BehaviorBinding Event="MouseLeftButtonDown" Command="{Binding DataContext.SelectLeftCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding ItemKey}"/> <CommandBehaviour:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding DataContext.SelectRightCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding ItemKey}"/> </CommandBehaviour:CommandBehaviorCollection.Behaviors> <Border.Background> <LinearGradientBrush StartPoint="0.7,0" EndPoint="0.7,1"> <GradientStop Color="{Binding BackColour}" Offset="0"/> <GradientStop Color="#33333333" Offset="1.5"/> </LinearGradientBrush> </Border.Background> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{Binding ForeColour}" Text="{Binding Description}"/> </Border> </Grid>
I can leave a lot of this in place when I copy it to the ItemsTemplate in the main window, and sticks to improve performance.
Statement-Rant One of the biggest problems with WPF seems to be that many performance problems (and others) are “somewhere down the rabbit hole” - in the code behind the scenes - which makes it very difficult to work on what happens without external and sometimes quite sophisticated tools for punching and profiling, as well as time to download, install and study.
These are really trousers.
And breathe ... End of Statement-Rant