3 changed files with 219 additions and 126 deletions
@ -0,0 +1,127 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using Avalonia.Animation; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Styling; |
|||
|
|||
namespace Avalonia.ReactiveUI |
|||
{ |
|||
/// <summary>
|
|||
/// A ContentControl that animates the transition when its content is changed.
|
|||
/// </summary>
|
|||
public class TransitioningUserControl : UserControl |
|||
{ |
|||
/// <summary>
|
|||
/// <see cref="AvaloniaProperty"/> for the <see cref="FadeInAnimation"/> property.
|
|||
/// </summary>
|
|||
public static readonly AvaloniaProperty<IAnimation> FadeInAnimationProperty = |
|||
AvaloniaProperty.Register<TransitioningUserControl, IAnimation>(nameof(DefaultContent), |
|||
CreateOpacityAnimation(0d, 1d, TimeSpan.FromSeconds(0.25))); |
|||
|
|||
/// <summary>
|
|||
/// <see cref="AvaloniaProperty"/> for the <see cref="FadeOutAnimation"/> property.
|
|||
/// </summary>
|
|||
public static readonly AvaloniaProperty<IAnimation> FadeOutAnimationProperty = |
|||
AvaloniaProperty.Register<TransitioningUserControl, IAnimation>(nameof(DefaultContent), |
|||
CreateOpacityAnimation(1d, 0d, TimeSpan.FromSeconds(0.25))); |
|||
|
|||
/// <summary>
|
|||
/// <see cref="AvaloniaProperty"/> for the <see cref="DefaultContent"/> property.
|
|||
/// </summary>
|
|||
public static readonly AvaloniaProperty<object> DefaultContentProperty = |
|||
AvaloniaProperty.Register<TransitioningUserControl, object>(nameof(DefaultContent)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the animation played when content appears.
|
|||
/// </summary>
|
|||
public IAnimation FadeInAnimation |
|||
{ |
|||
get => GetValue(FadeInAnimationProperty); |
|||
set => SetValue(FadeInAnimationProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the animation played when content disappears.
|
|||
/// </summary>
|
|||
public IAnimation FadeOutAnimation |
|||
{ |
|||
get => GetValue(FadeOutAnimationProperty); |
|||
set => SetValue(FadeOutAnimationProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the content displayed whenever there is no page currently routed.
|
|||
/// </summary>
|
|||
public object DefaultContent |
|||
{ |
|||
get => GetValue(DefaultContentProperty); |
|||
set => SetValue(DefaultContentProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the content with animation.
|
|||
/// </summary>
|
|||
public new object Content |
|||
{ |
|||
get => base.Content; |
|||
set => UpdateContentWithTransition(value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Updates the content with transitions.
|
|||
/// </summary>
|
|||
/// <param name="content">New content to set.</param>
|
|||
private async void UpdateContentWithTransition(object content) |
|||
{ |
|||
if (FadeOutAnimation != null) |
|||
await FadeOutAnimation.RunAsync(this, Clock); |
|||
base.Content = content; |
|||
if (FadeInAnimation != null) |
|||
await FadeInAnimation.RunAsync(this, Clock); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates opacity animation for this routed view host.
|
|||
/// </summary>
|
|||
/// <param name="from">Opacity to start from.</param>
|
|||
/// <param name="to">Opacity to finish with.</param>
|
|||
/// <param name="duration">Duration of the animation.</param>
|
|||
/// <returns>Animation object instance.</returns>
|
|||
private static IAnimation CreateOpacityAnimation(double from, double to, TimeSpan duration) |
|||
{ |
|||
return new Avalonia.Animation.Animation |
|||
{ |
|||
Duration = duration, |
|||
Children = |
|||
{ |
|||
new KeyFrame |
|||
{ |
|||
Setters = |
|||
{ |
|||
new Setter |
|||
{ |
|||
Property = OpacityProperty, |
|||
Value = from |
|||
} |
|||
}, |
|||
Cue = new Cue(0d) |
|||
}, |
|||
new KeyFrame |
|||
{ |
|||
Setters = |
|||
{ |
|||
new Setter |
|||
{ |
|||
Property = OpacityProperty, |
|||
Value = to |
|||
} |
|||
}, |
|||
Cue = new Cue(1d) |
|||
} |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using ReactiveUI; |
|||
using Splat; |
|||
|
|||
namespace Avalonia.ReactiveUI |
|||
{ |
|||
/// <summary>
|
|||
/// This content control will automatically load the View associated with
|
|||
/// the ViewModel property and display it. This control is very useful
|
|||
/// inside a DataTemplate to display the View associated with a ViewModel.
|
|||
/// </summary>
|
|||
public class ViewModelViewHost : TransitioningUserControl, IViewFor, IEnableLogger |
|||
{ |
|||
/// <summary>
|
|||
/// <see cref="AvaloniaProperty"/> for the <see cref="ViewModel"/> property.
|
|||
/// </summary>
|
|||
public static readonly AvaloniaProperty<object> ViewModelProperty = |
|||
AvaloniaProperty.Register<ViewModelViewHost, object>(nameof(ViewModel)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the ViewModel to display.
|
|||
/// </summary>
|
|||
public object ViewModel |
|||
{ |
|||
get => GetValue(ViewModelProperty); |
|||
set => SetValue(ViewModelProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the view locator.
|
|||
/// </summary>
|
|||
public IViewLocator ViewLocator { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Updates the Content when ViewModel changes.
|
|||
/// </summary>
|
|||
/// <param name="e">Property changed event arguments.</param>
|
|||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
if (e.Property.Name == nameof(ViewModel)) NavigateToViewModel(e.NewValue); |
|||
base.OnPropertyChanged(e); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Invoked when ReactiveUI router navigates to a view model.
|
|||
/// </summary>
|
|||
/// <param name="viewModel">ViewModel to which the user navigates.</param>
|
|||
private void NavigateToViewModel(object viewModel) |
|||
{ |
|||
if (viewModel == null) |
|||
{ |
|||
this.Log().Info("ViewModel is null. Falling back to default content."); |
|||
Content = DefaultContent; |
|||
return; |
|||
} |
|||
|
|||
var viewLocator = ViewLocator ?? global::ReactiveUI.ViewLocator.Current; |
|||
var viewInstance = viewLocator.ResolveView(viewModel); |
|||
if (viewInstance == null) |
|||
{ |
|||
this.Log().Warn($"Couldn't find view for '{viewModel}'. Is it registered? Falling back to default content."); |
|||
Content = DefaultContent; |
|||
return; |
|||
} |
|||
|
|||
this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}."); |
|||
viewInstance.ViewModel = viewModel; |
|||
if (viewInstance is IStyledElement styled) |
|||
styled.DataContext = viewModel; |
|||
Content = viewInstance; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue