ReactiveUI - STARIONGROUP/COMET-IME-Community-Edition GitHub Wiki
Start with updating the project file.
File to use as an example are CDP4Composition.csproj
for normal projects, CDP4ServicesDal.csproj
for Dal plugins and EngineeringModel.csproj
for normal plugins.
Change the Target framework version
<TargetFramework>net6.0-windows</TargetFramework>
Add Windows Desktop indications, so that WPF and also WindowsForms frameworks are available at runtime.
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
Upgrade CDP4-COMET SDK versions
PackageReferences
<PackageReference Include="CDP4Common-CE" Version="9.0.0-750-PR236" />
<PackageReference Include="CDP4Dal-CE" Version="9.0.0-750-PR236" />
<PackageReference Include="CDP4JsonSerializer-CE" Version="9.0.0-750-PR236" />
<PackageReference Include="CDP4RequirementsVerification-CE" Version="9.0.0-750-PR236" />
Upgrade the CommonServiceLocator to use version 2.0.5
<PackageReference Include="CommonServiceLocator" Version="2.0.5" />
Upgrade the DevExpress packages to use the NetStandard ones. Also install the latest version
<PackageReference Include="DevExpress.WindowsDesktop.CodeParser" Version="20.2.11" />
<PackageReference Include="DevExpress.WindowsDesktop.Data" Version="20.2.11" />
<PackageReference Include="DevExpress.WindowsDesktop.DataDesktop" Version="20.2.11" />
<PackageReference Include="DevExpress.WindowsDesktop.Images" Version="20.2.11" />
<PackageReference Include="DevExpress.WindowsDesktop.Mvvm" Version="20.2.11" />
<PackageReference Include="DevExpress.WindowsDesktop.Wpf" Version="20.2.11" />
<PackageReference Include="DevExpress.WindowsDesktop.Wpf.Themes.Seven" Version="20.2.11" />
Exception reporter and MarkDown.Xaml don't work, but for now convert to the latest version:
<PackageReference Include="ExceptionReporter" Version="5.0.0" />
<PackageReference Include="Handlebars.Net" Version="2.0.2" />
Add System.ComponentModel.Composition
<PackageReference Include="System.ComponentModel.Composition" Version="6.0.0" />
The Interactivity library has been implemented in a new package
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
Upgrade Reactive packages. Use ReactiveUI.WPF
if UI components are implemented in a library and use reactiveui
for all other projects that need Reactive parts.
<PackageReference Include="ReactiveUI.WPF" Version="18.0.10" />
or
<PackageReference Include="reactiveui" Version="18.0.10" />
-
Remove <Reference Include=" Net Framework legacy ItemGroups
-
Remove Compile (xaml) ItemGroups
-
For (DAL) plugins: be sure to exclude runtime Assets for assemblies referenced by Nuget packages that result in assemblies being written to Plugin*pluginname* folder. Also use
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
for IDal related plugins.
Start with building the project Rebuilding and fixing errors until no errors were found is a good first step to start the conversion. Also we recommend using ReSharper during the manual conversion, as it helps to fix a lot of things, like using statements, super easy and fast.
Steps you will have to take during conversion are:
-
We implemented our own version of ReactiveList, using
DynamicData
, in the CDP4Composition.Mvvm namespace. As a result we need to be sure thatusing CDP4Composition.Mvvm;
is present in all code files that references ReactiveList. After adding that using statement, you might be able to remove theusing ReactiveUI;
statement, as it might not be used anymore. -
Specifically for ReactiveLists that handle ItemChanged observables, we created a
TrackedReactiveList
. It listens to changes on objects in the list, so the list's generic type needs to implement INotifyPropertyChanged. -
Replace
using System.Windows.Interactivity;
withusing Microsoft.Xaml.Behaviors;
in all code files. For XAML files that use interactivity/behaviors the reference should be updated. Please use Resharper's suggestions for that as it is the fastest way.xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
. Be sure to remove the old reference (`xmlns:i=....."). -
Replace
using Microsoft.Practices.ServiceLocation;
withusing CommonServiceLocator;
in all code files. -
Remove All
ChangeTrackingEnabled = true
statements, because we did not use the concept behind it. If we need it in the future, we cannot implement it the way it was implemented before as suspending notifications is implemented differently in DynamicData. -
All ReactiveCommands need to be changed manually, as they all need to have two generic types instead of one. So a normal ReactiveCommand usage (
ReactiveCommand<object>
) that is not using a specific input parameter, needs to become aReactiveCommand<Unit, Unit>
. A ReactiveCommand that does use an input parameter, for example OpenSelectIterationsCommand in ShellViewModel.cs, has to be changed toReactiveCommand<T, Unit>
. So the OpenSelectIterationsCommand in ShellViewModel.cs becomes aReactiveCommand<ISession, Unit>
. -
Every ReactiveCommand should be instanciated using the
ReactiveCommandCreator
. This wrapper class makes sure that minor changes in the ReactiveUI API can be fixed more easily. -
Before, we needed to create a ReactiveCommand and Subscribe to it using a separate call to its
Subscribe()
method, that returned anIDisposable
that officially needed to be manually disposed, which we almost never did. Now this can be done in a single call toReactiveCommandCreator.Create
as we can add the Subscription as a parameter to that method call.
-
If you want to execute a command synchronously, cast the command to an ICommand, as this is the correct Execute overload:
menuItemToRemove.ClosePanelsCommand.Execute(null);
=>((ICommand)menuItemToRemove.ClosePanelsCommand).Execute(default);
and Add an extra namespaceusing System.Windows.Input;
-
Execute a command in unit tests should be done asynchronously by default:
vm.CancelCommand.Execute(null);
=>await vm.CancelCommand.Execute();
Also be sure that the unit test itself becomes anasync Task
instead of avoid
. -
Checking CanExecute in unit tests can only be done via the (synchronous) ICommand interface. We need to cast the command to an ICommand to do this:
Assert.IsFalse(vm.OkCommand.CanExecute(null));
should becomeAssert.IsFalse(((ICommand)vm.OkCommand).CanExecute(null));
-
Checking DoesNotThrow in unit tests needs to be done asynchronous by default:
Assert.DoesNotThrow(() => configViewModel.CloseCommand.Execute())
should becomeAssert.DoesNotThrowAsync(async () => await configViewModel.CloseCommand.Execute());
-
Sometimes we implement
ThrownExceptions()
on an IObservable, that handles all errors in a Reactive pipeline. When we use that in a unit test, usingawait this.viewmodel.OkCommand.Execute()
the unit test will now fail. We need to useObservable.Return(Unit.Default).InvokeCommand(this.viewmodel.OkCommand);
as calling theExecute
method now bypasses all internal ReactiveUI logic (like the CanExecute logic) and also the ThrownExceptions system that "swallowed" errors before. See also: https://github.com/reactiveui/ReactiveUI/issues/1356 -
Tests that already use .Execute(someParameter) will build without errors, but will probably fail during Execution. These tests should become
async Task
s and theawait
statement should be added:await viewmodel.SelectAllCommand.Execute(someParameter);