4

In my project I have to use WPF to place a big set of similar user controls(around 2000) on Canvas object. Basically, it's just a set of rectangles, that can change visibility, can be selected and store data object inside.

I add new controls with help of attached property like this:

    public static readonly DependencyProperty VisualStaticBlocksProperty =
        DependencyProperty.RegisterAttached("VisualStaticBlocks", typeof(ObservableCollection<VisualBlockViewModel>), typeof(BindableBlocksBehaviour), 
        new UIPropertyMetadata(null, VisualStaticBlocksPropertyChanged));

    private static void VisualStaticBlocksPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        ClickSightView clickSight = source as ClickSightView;
        ObservableCollection<VisualBlockViewModel> visualBlocks = e.NewValue as ObservableCollection<VisualBlockViewModel>;
        if (clickSight != null && visualBlocks != null)
        {
            foreach (VisualBlockViewModel visualBlock in visualBlocks)
            {
                clickSight.StaticBlocksCanvas.Children.Add(new VisualBlockView(visualBlock));
            }
        }
    }

However, it takes a lot of time to build all of them(around 2 seconds). I used a profiler to check that main problem is in LoadBaml() method, which is called in InitializeComponent() method.

As I understand, LoadBaml() is used to parse xaml markup. Is it possible somehow to cache the LoadBaml() result for component and reuse it instead of parse xaml each time I create new control instance?

EDIT: To represent this set of objects visually I have created user control with Canvas on it, and created attached property VisualStaticBlocks to attachblock view models(type VisualBlockViewModel) to this control and insert visual block instances(type VisualBlockView) directly to Canvas.

EDIT2: I've solved the problem by giving up using user controls for this purpose at all. As my controls are quite simple, I used Rectangle() class instead with 3 manually added bindings and 3 manually added events. Of course, there were no InitializeComponent() calls at all.It allowed me to build the set of 2000 rectangles in 200 miliseconds, which is 10 times faster. Anyway, still will be grateful for information if I can clone similar objects without loading BAML each time.

  • Please avoid asking 'help me' type questions. Your question lacks a lot of information. Please read http://stackoverflow.com/help/how-to-ask – Tk1993 Feb 08 '17 at 11:31
  • Sorry, I've read the FAQ and still doesn't understand what other information should I provide here. Can you be more specific about what information is missing? – Roman Yavorskyi Feb 09 '17 at 08:46

1 Answers1

1

It sounds like you have an issue with the time it takes to create visual elements. I can see why you think you need to call InitializeComponent, but that is not how WPF works.

As noted here:

The call to InitializeComponent() (which is usually called in the default constructor of at least Window and UserControl) is actually a method call to the partial class of the control (rather than a call up the object hierarchy as I first expected).

Which leads me to suspect you do not understand how (or why) InitializeComponent works; it is impossible to call it once to build multiple elements and externally, no less.

You are using ObservableCollection, which neither works well with large data sets nor complex views. Consider using a thread-safe ObservableCollection and add the data objects on a background thread. This shouldn't be an issue because you're adding data objects (view models) versus visual objects (views); visual objects should be added on the same thread (UI) they are created.

It would help to provide additional information as you have not explained how you represent these objects visually. Is the collection bound to an ItemsControl and does it define a DataTemplate to visually represent each data object?

Community
  • 1
  • 1
  • I've edited my question a little, thanks. I used attached property with ObservableCollection of viewmodels to create instances of views. Creation and adding of views is in VisualStaticBlocksPropertyChanged() method I've included in my question. – Roman Yavorskyi Mar 09 '17 at 16:39
  • 1
    I guess my initial question was a little misleading. The problem is in LoadBaml() method, which is called by InitializeComponent() method and parses the same xaml file each time. As for me, it would be logical to parse it once, as it is the same for each control, and use the result each time one needs to create new user control of this type. – Roman Yavorskyi Mar 09 '17 at 16:45