wpfreactiveuiviewmodellocator

ReactiveUI ViewModelViewHost Is Very Slow, When using it With HandyControl


Hi,

About 6 months ago I started playing with reactiveUI and build simple applications with it

And just four months ago, I started to build an application that monitors the network on low level

So, I implement network part in C++ and then build UI, and database models and logic in C#

Then create an intermediate library to marshal this low-level API,

So as you know this API will provide a huge amount of packets.

So, in C# I decided to use reactiveUI and reactive programming, in general, to work with those streams of data

and Rx works perfectly and save me days of works with this high-performance reactive system

But now I have a big problem:

When I navigate through the application, the initial time of resolving view / ViewModel is so much, it's about 1200-506 ms on average and this cause a problem because this makes the app look like its frozen

Also, notice a strange behavior described

in this StackOverflow question: WhenActivated is called twice: And try that solution but does not work.

So i try to implement my custom SimpleViewModelViewHost

SimpleViewModelViewHost.xaml

<UserControl x:Class="Sample.EnhancedViewModelViewHost"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
             d:DesignHeight="450" d:DesignWidth="800">
    <ContentPresenter
        x:Name="MainContent"
        Content="{Binding Path=View}"
    />
</UserControl>

SimpleViewModelViewHost.xaml.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ReactiveUI;

namespace Sample
{

    // https://stackoverflow.com/questions/36433709/whenactivated-is-called-twice-when-used-in-views-and-viewmodels-hosted-in-viewmo/36813328#36813328

    /// <summary>
    /// Interaction logic for EnhancedViewModelViewHost.xaml
    /// </summary>
    public partial class EnhancedViewModelViewHost : UserControl
    {
        public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
            "ViewModel", typeof(object), typeof(EnhancedViewModelViewHost), new PropertyMetadata(default(object)));

        public object ViewModel
        {
            get => GetValue(ViewModelProperty);
            set
            {
                SetValue(ViewModelProperty, value);
                if (value == null) { return; }
                var view = ViewLocator.Current.ResolveView(value);
                if (view != null)
                {
                    View = view;
                    View.ViewModel = value;

                }
                else
                {
                    MainContent.Content = value;
                }
            }
        }


        public static readonly DependencyProperty ViewProperty = DependencyProperty.Register(
            "View", typeof(IViewFor), typeof(EnhancedViewModelViewHost), new PropertyMetadata(default(IViewFor)));

        public IViewFor View
        {
            get => (IViewFor)GetValue(ViewProperty);
            set => SetValue(ViewProperty, value);
        }

        public EnhancedViewModelViewHost()
        {
            DataContext = this;
            InitializeComponent();
        }
    }
}


I know this leak a lot of features disposing of old view/viewModel .....

But now performance is good: now it just takes about 250-300 ms, but still not good at all Because human eys can notice this delay

So NowI am in a big problem, so I create another simple app in ReactiveUI with empty View

With no binding

and guess what: the problem still existed

I Used Visual studio profiler to track time between the start of a constructor of ViewModel

and the end of WhenActivated in the View

So my question: is reactive team aware of this problem or just I do something wrong, and if yes, what is the solution

Also Notice:

I try to improve the performance in complex layouts by Creating and implement an interface called IHotReloadViewModel and implement some logic to re-use current ViewModel instead of replacing it

and gain performance from about 1350 ms -> 10 ms


Snippets

Part of ViewModel

 public class ManageProjectsViewModel : ReactiveObject, IActivatableViewModel
    {

        // .....
        [Reactive] public EditProjectViewModel SelectedProject { get; set; }
        //.....

        private AppDbManager AppDbManager { get; set; }

        #region Commands

        public ReactiveCommand<Unit, Unit> EditProject { get; set; }
        public ReactiveCommand<Unit, Unit> CreateNewProject { get; set; }
        public ReactiveCommand<Unit, Unit> DeleteProject { get; set; }
        // ....

        #endregion

        private IDisposable LastProjectTrack { get; set; }

        private Subject<Unit> FreeSelectedProject { get; set; }

        public ManageProjectsViewModel()
        {
            Activator = new ViewModelActivator();
            AppDbManager = Locator.Current.GetService<AppDbManager>();

            #region Commands

            var canOperateOnProject = this.WhenValueChanged(vm => vm.SelectedProjectLookup).Select(p => p != null);
            EditProject = ReactiveCommand.Create(EditProjectImpl, canOperateOnProject);
            CreateNewProject = ReactiveCommand.Create(CreateNewProjectImpl);
            DeleteProject = ReactiveCommand.Create(DeleteProjectImpl, canOperateOnProject);

            #endregion

            FreeSelectedProject = new Subject<Unit>();

            this.WhenActivated(disposables =>
            {
                ProjectAddedNotify.ObserveOnDispatcher().Subscribe(ProjectAddedNotifyImpl).DisposeWith(disposables);

                FreeSelectedProject.ObserveOnDispatcher().Subscribe(FreeSelectedProjectImpl).DisposeWith(disposables);
            });


        }

// ...........

Part of View.xaml.cs


 public partial class ManageProjectsView : ReactiveUserControl<ManageProjectsViewModel>
    {
        private bool called = false;
        public ManageProjectsView()
        {
            InitializeComponent();

            IDisposable mainDisposable = null;

            mainDisposable = this.WhenActivated(disposable =>
            {

              // ........

                this.BindCommand(ViewModel, vm => vm.CreateNewProject, v => v.NewProject).DisposeWith(disposable);
                this.BindCommand(ViewModel, vm => vm.EditProject, v => v.EditProject).DisposeWith(disposable);
                this.BindCommand(ViewModel, vm => vm.DeleteProject, v => v.DeleteProject).DisposeWith(disposable);
                this.Bind(ViewModel, vm => vm.SelectedProject, v => v.SelectedProject.ViewModel).DisposeWith(disposable);

                ProjectLookups.Events().SelectionChanged.Subscribe(args =>
                {
                    if (args.AddedItems.Count > 0)
                    {
                        ViewModel.SelectedProjectLookup = (NPProjectLookup)args.AddedItems[0];
                    }
                }).DisposeWith(disposable);

                ProjectLookups.ApplyHorizontalScrolling();
                ProjectLookups.AllowZoom();

                mainDisposable.DisposeWith(disposable);

            }, this); // either use this or not: performance issue exists
        }
    }

Most of Views/ViewModels uses the same structure


So why I think this is a problem

Because I test the same UI with my simple implementation of ViewModel-View Locator and everything work instantly

Also tested it with Prism With DryIoC i work with it for a long time and everything works instantly

so know is there any solution for that, or I will need to use a prism in the current reactiveUI app?

Notice


Updates (1)

After testing more than one app 5 apps i found that

Update (2)

I Created This Issue On ReactiveUI Repository Thanks.


Solution

  • Can you create a usable reproduction of the issue please, put it in a github repository, link it here AND create an issue in https://github.com/reactiveui/ReactiveUI/issues

    As for questions about "not providing any performance considerations". There are numerous discussions during pull requests about performance and impact (i.e. https://github.com/reactiveui/ReactiveUI/pull/1311 https://github.com/reactiveui/ReactiveUI/pull/1289 and https://github.com/reactiveui/splat/pull/360). In terms of benchmarks, indeed we're short on them, but we have an open issue https://github.com/reactiveui/ReactiveUI/issues/1734 for them. The documentation could be better, there is a lot of knowledge out there on how to get the best out of ReactiveUI, people are welcome to help us improve how to make that knowledge accessible.

    As for confidence in a project that has 5000 stars. That 5000 stars is great as an indication of interest, but only interest. The number of people helping to maintain it equates to ~1% with a few people spending their time and passion on a project, some for almost a decade. They want people to be using the project and want to help you get the best out of it. You want confidence in what you are using which is only sensible, but there are companies using it in real-time applications and\or applications used by thousands of users everyday.

    I could point you to posts about the NET framework having a magnitude of stars greater than us, and it has perf\knowledge\usability issues as well. But my point will be the maintainers of projects only learn by customers\communities trying things and feeding back.

    For your actual problem, there's a team of people who are willing to help. But we need evidence in a reproducible issue, ideally with a test and possibly trace of what you're seeing. We can then assist and understand if you're project is doing something we can help solve, or whether ReactiveUI or the underlying Splat library needs some investigation.