using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using Avalonia; using Avalonia.Animation; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.VisualTree; using MiniMvvm; namespace ControlCatalog.ViewModels { public class TransitioningContentControlPageViewModel : ViewModelBase { public TransitioningContentControlPageViewModel() { var assetLoader = AvaloniaLocator.Current?.GetService()!; var images = new string[] { "delicate-arch-896885_640.jpg", "hirsch-899118_640.jpg", "maple-leaf-888807_640.jpg" }; foreach (var image in images) { var path = $"avares://ControlCatalog/Assets/{image}"; Images.Add(new Bitmap(assetLoader.Open(new Uri(path)))); } SetupTransitions(); _SelectedTransition = PageTransitions[1]; _SelectedImage = Images[0]; } public List PageTransitions { get; } = new List(); public List Images { get; } = new List(); private Bitmap _SelectedImage; /// /// Gets or Sets the selected image /// public Bitmap SelectedImage { get { return _SelectedImage; } set { this.RaiseAndSetIfChanged(ref _SelectedImage, value); } } private PageTransition _SelectedTransition; /// /// Gets or sets the transition to play /// public PageTransition SelectedTransition { get { return _SelectedTransition; } set { this.RaiseAndSetIfChanged(ref _SelectedTransition, value); } } private bool _ClipToBounds; /// /// Gets or sets if the content should be clipped to bounds /// public bool ClipToBounds { get { return _ClipToBounds; } set { this.RaiseAndSetIfChanged(ref _ClipToBounds, value); } } private int _Duration = 500; /// /// Gets or Sets the duration /// public int Duration { get { return _Duration; } set { this.RaiseAndSetIfChanged(ref _Duration , value); SetupTransitions(); } } private void SetupTransitions() { if (PageTransitions.Count == 0) { PageTransitions.AddRange(new[] { new PageTransition("None"), new PageTransition("CrossFade"), new PageTransition("Slide horizontally"), new PageTransition("Slide vertically"), new PageTransition("Composite"), new PageTransition("Custom") }); } PageTransitions[1].Transition = new CrossFade(TimeSpan.FromMilliseconds(Duration)); PageTransitions[2].Transition = new PageSlide(TimeSpan.FromMilliseconds(Duration), PageSlide.SlideAxis.Horizontal); PageTransitions[3].Transition = new PageSlide(TimeSpan.FromMilliseconds(Duration), PageSlide.SlideAxis.Vertical); var compositeTransition = new CompositePageTransition(); compositeTransition.PageTransitions.Add(PageTransitions[1].Transition!); compositeTransition.PageTransitions.Add(PageTransitions[2].Transition!); compositeTransition.PageTransitions.Add(PageTransitions[3].Transition!); PageTransitions[4].Transition = compositeTransition; PageTransitions[5].Transition = new CustomTransition(TimeSpan.FromMilliseconds(Duration)); } public void NextImage() { var index = Images.IndexOf(SelectedImage) + 1; if (index >= Images.Count) { index = 0; } SelectedImage = Images[index]; } public void PrevImage() { var index = Images.IndexOf(SelectedImage) - 1; if (index < 0) { index = Images.Count-1; } SelectedImage = Images[index]; } } public class PageTransition : ViewModelBase { public PageTransition(string displayTitle) { DisplayTitle = displayTitle; } public string DisplayTitle { get; } private IPageTransition? _Transition; /// /// Gets or sets the transition /// public IPageTransition? Transition { get { return _Transition; } set { this.RaiseAndSetIfChanged(ref _Transition, value); } } public override string ToString() { return DisplayTitle; } } public class CustomTransition : IPageTransition { /// /// Initializes a new instance of the class. /// public CustomTransition() { } /// /// Initializes a new instance of the class. /// /// The duration of the animation. public CustomTransition(TimeSpan duration) { Duration = duration; } /// /// Gets the duration of the animation. /// public TimeSpan Duration { get; set; } public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return; } var tasks = new List(); var parent = GetVisualParent(from, to); var scaleProperty = ScaleTransform.ScaleYProperty; if (from != null) { var animation = new Animation { FillMode = FillMode.Forward, Children = { new KeyFrame { Setters = { new Setter { Property = scaleProperty, Value = 1d } }, Cue = new Cue(0d) }, new KeyFrame { Setters = { new Setter { Property = scaleProperty, Value = 0d } }, Cue = new Cue(1d) } }, Duration = Duration }; tasks.Add(animation.RunAsync(from, null, cancellationToken)); } if (to != null) { to.IsVisible = true; var animation = new Animation { FillMode = FillMode.Forward, Children = { new KeyFrame { Setters = { new Setter { Property = scaleProperty, Value = 0d } }, Cue = new Cue(0d) }, new KeyFrame { Setters = { new Setter { Property = scaleProperty, Value = 1d } }, Cue = new Cue(1d) } }, Duration = Duration }; tasks.Add(animation.RunAsync(to, null, cancellationToken)); } await Task.WhenAll(tasks); if (from != null && !cancellationToken.IsCancellationRequested) { from.IsVisible = false; } } /// /// Gets the common visual parent of the two control. /// /// The from control. /// The to control. /// The common parent. /// /// The two controls do not share a common parent. /// /// /// Any one of the parameters may be null, but not both. /// private static Visual GetVisualParent(Visual? from, Visual? to) { var p1 = (from ?? to)!.GetVisualParent(); var p2 = (to ?? from)!.GetVisualParent(); if (p1 != null && p2 != null && p1 != p2) { throw new ArgumentException("Controls for PageSlide must have same parent."); } return p1 ?? throw new InvalidOperationException("Cannot determine visual parent."); } } }