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
So I try to solve this problem, or get work around it but nothing helps,
I track most/all of the guidelines of reactiveUI, but nothing seems to work
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 thestart 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
- First I did not post this in reactiveUI issues in Github: because I do not want to overflow issues panel with may unnecessary issue, so I need to be sure here that this issue exists on other users devices and if that I will report it to reactiveUI team
Updates (1)
After testing more than one app 5 apps
i found that
- Problem of calling WhenActivated Twice is related to reactiveUI
- Problem of performance
Delay of view
only occurs when using ReactiveUI with HandyControl UI Library, I still can not be sure about the source of problem but because it's only happens when usingHC
WithRI
i decide to Create This Issue at HandyControl in GitHub
Update (2)
I Created This Issue On ReactiveUI Repository Thanks.