/// <summary> /// Gets or creates a <see cref="IDynamicCommand"/> that will be attached to the <paramref name="viewModel"/>. /// </summary> /// <param name="viewModel">This <see cref="IViewModel"/>.</param> /// <param name="name">The command name.</param> /// <param name="factory">The command factory.</param> /// <param name="configure">The optional function to configure the command builder.</param> /// <returns>The attached <see cref="IDynamicCommand"/>. Null is returned if the <see cref="IViewModel"/> is disposed.</returns> public static IDynamicCommand GetOrCreateCommand( this IViewModel viewModel, string name, Func <string, IDynamicCommandBuilder> factory, Func <IDynamicCommandBuilder, IDynamicCommandBuilder> configure = null ) { if (viewModel.IsDisposed) { return(null); } if (!viewModel.TryGetDisposable <IDynamicCommand>(name, out var command)) { var builder = factory(name); if (configure != null) { builder = configure(builder); } command = builder.Build(); viewModel.AddDisposable(command.Name, command); } return(command); }
/// <summary> /// Attaches a child <see cref="IViewModel"/> to a parent <see cref="IViewModel"/>. /// By being attached, the child will be disposed when the parent is disposed. /// Both also share the same <see cref="IViewModelView"/>. /// A child can only be attached once to a single <see cref="IViewModel"/>. /// </summary> /// <typeparam name="TChildViewModel">The type of child viewmodel.</typeparam> /// <param name="viewModel">The parent <see cref="IViewModel"/>.</param> /// <param name="childViewModel">The child <see cref="IViewModel"/> to attach.</param> /// <param name="name">The child ViewModel's name. This defaults to <paramref name="childViewModel"/>.Name when not provided.</param> /// <returns>The attached child <see cref="IViewModel"/>.</returns> public static TChildViewModel AttachChild <TChildViewModel>(this IViewModel viewModel, TChildViewModel childViewModel, string name = null) where TChildViewModel : IViewModel { if (childViewModel == null) { throw new ArgumentNullException(nameof(childViewModel)); } name = name ?? childViewModel.Name; if (viewModel.TryGetDisposable(name, out var _)) { throw new InvalidOperationException($"A child ViewModel with the name '{name}' is already attached to this ViewModel."); } viewModel.AddDisposable(name, childViewModel); childViewModel.View = viewModel.View; var parentViewChangedDisposable = new ParentViewChangedDisposable(viewModel, childViewModel); childViewModel.AddDisposable(ParentViewChangedSubscriptionKey, parentViewChangedDisposable); childViewModel.AddDisposable(RemoveSelfFromParentSubscriptionKey, new ActionDisposable(() => viewModel.RemoveDisposable(name))); return(childViewModel); }
/// <summary> /// Gets or creates a property of the specified <paramref name="name"/>. /// </summary> /// <param name="viewModel">The <see cref="IViewModel"/> owning the property.</param> /// <param name="name">The property's name.</param> /// <param name="factory">The property factory.</param> /// <returns>The <see cref="IDynamicProperty"/> matching the specified <paramref name="name"/>. Null is returned if the <see cref="IViewModel"/> is disposed.</returns> public static IDynamicProperty GetOrCreateDynamicProperty(this IViewModel viewModel, string name, Func <string, IDynamicProperty> factory) { if (viewModel.IsDisposed) { return(null); } if (!viewModel.TryGetDisposable <IDynamicProperty>(name, out var property)) { property = factory(name); property.ValueChanged += OnDynamicPropertyChanged; // We check the same condition twice because it's possible that the factory invocation already added the property. // This can happen when the property implements IViewModel and was added using AttachChild. if (!viewModel.TryGetDisposable <IDynamicProperty>(name, out var _)) { viewModel.AddDisposable(name, property); } } return(property); void OnDynamicPropertyChanged(IDynamicProperty dynamicProperty) { viewModel.RaisePropertyChanged(dynamicProperty.Name); } }
/// <summary> /// Gets or creates a <see cref="IDataLoader"/>. /// </summary> /// <param name="viewModel">The <see cref="IViewModel"/>.</param> /// <param name="key">The key holding the <see cref="IDataLoader"/> in the containing <see cref="IViewModel"/>. This also serves as the default name of the <see cref="IDataLoader"/>.</param> /// <param name="factory">The <see cref="IDataLoader"/> Factory.</param> /// <returns>The <see cref="IDataLoader"/>. Null is returned if the <see cref="IViewModel"/> is disposed.</returns> public static IDataLoader GetOrCreateDataLoader( this IViewModel viewModel, string key, Func <string, IDataLoader> factory ) { if (viewModel.IsDisposed) { return(null); } if (!viewModel.TryGetDisposable <IDataLoader>(key, out var dataLoader)) { dataLoader = factory(key); viewModel.AddDisposable(key, dataLoader); } return(dataLoader); }
/// <summary> /// Registers a custom back button handler for this ViewModel. /// </summary> /// <param name="vm">The ViewModel.</param> /// <param name="handle">The async handler method.</param> /// <param name="canHandle">The optional canHandle function. When null is provided, the default behavior is applied (canHandle returns true only when the current ViewModel is active in the <see cref="ISectionsNavigator"/>).</param> /// <param name="handlerName">The optional name of the handler. When null is provided, the ViewModel type name is used.</param> /// <param name="priority">The optional priority. (See <see cref="IBackButtonManager.AddHandler(IBackButtonHandler, int?)"/> for more info.)</param> public static void RegisterBackHandler(this IViewModel vm, Func <CancellationToken, Task> handle, Func <bool> canHandle = null, string handlerName = null, int?priority = null) { vm = vm ?? throw new ArgumentNullException(nameof(vm)); handlerName = handlerName ?? vm.GetType().Name; handle = handle ?? throw new ArgumentNullException(nameof(handle)); canHandle = canHandle ?? DefaultCanHandle; var backButtonManager = vm.GetService <IBackButtonManager>(); var handler = new BackButtonHandler(handlerName, canHandle, handle); var registration = backButtonManager.RegisterHandler(handler, priority); vm.AddDisposable("BackButtonHandler_" + handlerName, registration); bool DefaultCanHandle() { var navigator = vm.GetService <ISectionsNavigator>(); // The handler can handle the back only if the associated ViewModel is the one currently active in the navigator. return(navigator.GetActiveViewModel() == vm); } }
public static TDisposable GetOrCreateDisposable <TDisposable>(this IViewModel vm, Func <TDisposable> create, [CallerMemberName] string key = null) where TDisposable : IDisposable { if (vm is null) { throw new ArgumentNullException(nameof(vm)); } if (create is null) { throw new ArgumentNullException(nameof(create)); } if (vm.TryGetDisposable(key, out var existingDisposable)) { return((TDisposable)existingDisposable); } else { var disposable = create(); vm.AddDisposable(key, disposable); return(disposable); } }