Add project files.

This commit is contained in:
Daniel Clark
2022-11-01 15:26:08 +00:00
parent daa7b59f22
commit 7e4f880821
408 changed files with 16863 additions and 0 deletions
@@ -0,0 +1,116 @@
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System.Windows;
namespace TheXamlGuy.UI.WPF.Controls;
public class AnimatedScrollViewer : ScrollViewer
{
public static readonly DependencyProperty CanMouseWheelProperty =
DependencyProperty.Register("CanMouseWheel",
typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.TrueBox));
public static readonly DependencyProperty IsInertiaEnabledProperty =
DependencyProperty.RegisterAttached("IsInertiaEnabled",
typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty IsPenetratingProperty =
DependencyProperty.RegisterAttached("IsPenetrating",
typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.FalseBox));
internal static readonly DependencyProperty CurrentHorizontalOffsetProperty =
DependencyProperty.Register("CurrentHorizontalOffset",
typeof(double), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.Double0Box, OnCurrentHorizontalOffsetChanged));
internal static readonly DependencyProperty CurrentVerticalOffsetProperty =
DependencyProperty.Register("CurrentVerticalOffset",
typeof(double), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.Double0Box, OnCurrentVerticalOffsetChanged));
public bool CanMouseWheel
{
get => (bool)GetValue(CanMouseWheelProperty);
set => SetValue(CanMouseWheelProperty, ValueBoxes.BooleanBox(value));
}
public bool IsInertiaEnabled
{
get => (bool)GetValue(IsInertiaEnabledProperty);
set => SetValue(IsInertiaEnabledProperty, ValueBoxes.BooleanBox(value));
}
public bool IsPenetrating
{
get => (bool)GetValue(IsPenetratingProperty);
set => SetValue(IsPenetratingProperty, ValueBoxes.BooleanBox(value));
}
internal double CurrentHorizontalOffset
{
get => (double)GetValue(CurrentHorizontalOffsetProperty);
set => SetValue(CurrentHorizontalOffsetProperty, value);
}
internal double CurrentVerticalOffset
{
get => (double)GetValue(CurrentVerticalOffsetProperty);
set => SetValue(CurrentVerticalOffsetProperty, value);
}
public static bool GetIsInertiaEnabled(DependencyObject element) => (bool)element.GetValue(IsInertiaEnabledProperty);
public static bool GetIsPenetrating(DependencyObject element) => (bool)element.GetValue(IsPenetratingProperty);
public static void SetIsInertiaEnabled(DependencyObject element, bool value) => element.SetValue(IsInertiaEnabledProperty, ValueBoxes.BooleanBox(value));
public static void SetIsPenetrating(DependencyObject element, bool value) => element.SetValue(IsPenetratingProperty, ValueBoxes.BooleanBox(value));
public void ScrollToHorizontalOffsetWithAnimation(double offset, double milliseconds = 500)
{
var animation = AnimationHelper.CreateAnimation(offset, milliseconds);
animation.EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
};
animation.FillBehavior = FillBehavior.Stop;
animation.Completed += (s, e1) =>
{
CurrentHorizontalOffset = offset;
};
BeginAnimation(CurrentHorizontalOffsetProperty, animation, HandoffBehavior.Compose);
}
public void ScrollToVerticalOffsetWithAnimation(double offset, double milliseconds = 500)
{
DoubleAnimation animation = AnimationHelper.CreateAnimation(offset, milliseconds);
animation.EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
};
animation.FillBehavior = FillBehavior.Stop;
animation.Completed += (s, e1) =>
{
CurrentVerticalOffset = offset;
};
BeginAnimation(CurrentVerticalOffsetProperty, animation, HandoffBehavior.Compose);
}
protected override HitTestResult? HitTestCore(PointHitTestParameters hitTestParameters) => IsPenetrating ? null : base.HitTestCore(hitTestParameters);
private static void OnCurrentHorizontalOffsetChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as AnimatedScrollViewer)?.ScrollToHorizontalOffset((double)args.NewValue);
}
private static void OnCurrentVerticalOffsetChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as AnimatedScrollViewer)?.ScrollToVerticalOffset((double)args.NewValue);
}
}
@@ -0,0 +1,85 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using TheXamlGuy.Media.Capture;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using System.Windows.Interop;
using System.Runtime.InteropServices;
namespace TheXamlGuy.UI.WPF.Controls;
public class CameraPreview : Control
{
private Image? image;
public CameraPreview()
{
DefaultStyleKey = typeof(CameraPreview);
}
public override void OnApplyTemplate()
{
image = GetTemplateChild("Image") as Image;
}
public async Task StartAsync(IMediaFrameSource source)
{
MediaCaptureInitializationSettings settings = new()
{
Source = source
};
MediaCapture mediaCapture = new();
mediaCapture.Initialize(settings);
if (await mediaCapture.CreateFrameReaderAsync() is IMediaFrameReader frameReader)
{
frameReader.FrameArrived += OnFrameArrived;
await frameReader.StartAsync();
}
}
public async Task StartAsync()
{
if (image is null)
{
return;
}
IReadOnlyList<IMediaFrameSource> sourceGroups = await MediaFrameSource.FindAllAsync();
if (sourceGroups.FirstOrDefault(x => x.DisplayName.Contains("USB", System.StringComparison.InvariantCultureIgnoreCase)) is IMediaFrameSource source)
{
if (source.SupportedFormats.OrderByDescending(x => x.Size.Width & x.Size.Height).FirstOrDefault() is MediaFrameFormat bestSupportedFormat)
{
source.SetFormat(bestSupportedFormat);
}
await StartAsync(source);
}
}
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
private async void OnFrameArrived(IMediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
if (image is null)
{
return;
}
if (await sender.TryAcquireLatestFrameAsync() is MediaFrame frame)
{
Dispatcher.Invoke(() =>
{
IntPtr handle = frame.Bitmap.GetHbitmap();
image.Source = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(frame.Width, frame.Height));
DeleteObject(handle);
});
}
}
}
@@ -0,0 +1,16 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TheXamlGuy.UI.WPF.Controls">
<Style TargetType="controls:CameraPreview">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:CameraPreview">
<Grid Background="{TemplateBinding Background}">
<Image x:Name="Image" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
+95
View File
@@ -0,0 +1,95 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace TheXamlGuy.UI.WPF.Controls;
public class Countdown : Control
{
public static readonly DependencyProperty CountdownIdentifierProperty =
DependencyProperty.Register(nameof(CountdownIdentifier),
typeof(CountdownIdentifier), typeof(Countdown));
private Storyboard? completingTransition;
private VisualStateGroup? countdownGroup;
private bool isTransitioning;
public Countdown()
{
DefaultStyleKey = typeof(Countdown);
}
public event TypedEventHandler<Countdown, CountdownCompletedEventArgs>? Completed;
public event TypedEventHandler<Countdown, CountdownStartedEventArgs>? Started;
public CountdownIdentifier CountdownIdentifier
{
get => (CountdownIdentifier)GetValue(CountdownIdentifierProperty);
set => SetValue(CountdownIdentifierProperty, value);
}
private Storyboard? CompletingTransition
{
get => completingTransition;
set
{
if (completingTransition is not null)
{
CompletingTransition!.Completed -= OnTransitionCompleted;
}
completingTransition = value;
if (completingTransition is not null)
{
CompletingTransition!.Completed += OnTransitionCompleted;
}
}
}
public override void OnApplyTemplate()
{
if (GetTemplateChild("Container") is Grid container)
{
countdownGroup = ((IEnumerable<VisualStateGroup>)VisualStateManager.GetVisualStateGroups(container))!.FirstOrDefault(x => x.Name == "CountdownStates");
}
}
public void Start()
{
if (isTransitioning)
{
return;
}
Started?.Invoke(this, new CountdownStartedEventArgs());
isTransitioning = true;
CompletingTransition = GetTransitionStoryboardByName($"{CountdownIdentifier}");
VisualStateManager.GoToState(this, $"{CountdownIdentifier}", true);
}
private Storyboard GetTransitionStoryboardByName(string transitionName)
{
if (countdownGroup is not null)
{
if (((IEnumerable<VisualState>)countdownGroup.States).Where(x => x.Name == transitionName).Select(x => x.Storyboard).FirstOrDefault() is Storyboard transition)
{
return transition;
}
}
return new Storyboard();
}
private void OnTransitionCompleted(object? sender, EventArgs args)
{
CompletingTransition!.Completed -= OnTransitionCompleted;
VisualStateManager.GoToState(this, "Pending", false);
isTransitioning = false;
Completed?.Invoke(this, new CountdownCompletedEventArgs());
}
}
+155
View File
@@ -0,0 +1,155 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TheXamlGuy.UI.WPF.Controls">
<Style TargetType="controls:Countdown">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:Countdown">
<Grid x:Name="Container">
<Grid
x:Name="TextRoot"
Opacity="0"
RenderTransformOrigin="0.5,0.5">
<TextBlock x:Name="TextBlock" />
<Grid.RenderTransform>
<ScaleTransform x:Name="ScaleTransform" />
</Grid.RenderTransform>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CountdownStates">
<VisualState x:Name="Pending" />
<VisualState x:Name="TenSecond">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="(TextBlock.Text)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="10" />
<DiscreteObjectKeyFrame KeyTime="00:00:01" Value="9" />
<DiscreteObjectKeyFrame KeyTime="00:00:02" Value="8" />
<DiscreteObjectKeyFrame KeyTime="00:00:03" Value="7" />
<DiscreteObjectKeyFrame KeyTime="00:00:04" Value="6" />
<DiscreteObjectKeyFrame KeyTime="00:00:05" Value="5" />
<DiscreteObjectKeyFrame KeyTime="00:00:06" Value="4" />
<DiscreteObjectKeyFrame KeyTime="00:00:07" Value="3" />
<DiscreteObjectKeyFrame KeyTime="00:00:08" Value="2" />
<DiscreteObjectKeyFrame KeyTime="00:00:09" Value="1" />
<DiscreteObjectKeyFrame KeyTime="00:00:10" Value="1" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="10x"
Storyboard.TargetName="TextRoot"
Storyboard.TargetProperty="(UIElement.Opacity)">
<LinearDoubleKeyFrame KeyTime="0" Value="0.0" />
<LinearDoubleKeyFrame KeyTime="00:00:00.167" Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="10x"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX">
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1.5" />
<SplineDoubleKeyFrame
KeySpline="0,0,0,1"
KeyTime="00:00:00.250"
Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="10x"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY">
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1.5" />
<SplineDoubleKeyFrame
KeySpline="0,0,0,1"
KeyTime="00:00:00.250"
Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="FiveSecond">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="(TextBlock.Text)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="5" />
<DiscreteObjectKeyFrame KeyTime="00:00:01" Value="4" />
<DiscreteObjectKeyFrame KeyTime="00:00:02" Value="3" />
<DiscreteObjectKeyFrame KeyTime="00:00:03" Value="2" />
<DiscreteObjectKeyFrame KeyTime="00:00:04" Value="1" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="5x"
Storyboard.TargetName="TextRoot"
Storyboard.TargetProperty="(UIElement.Opacity)">
<LinearDoubleKeyFrame KeyTime="0" Value="0.0" />
<LinearDoubleKeyFrame KeyTime="00:00:00.167" Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="5x"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX">
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1.5" />
<SplineDoubleKeyFrame
KeySpline="0,0,0,1"
KeyTime="00:00:00.250"
Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="5x"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY">
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1.5" />
<SplineDoubleKeyFrame
KeySpline="0,0,0,1"
KeyTime="00:00:00.250"
Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="TwoSecond">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="(TextBlock.Text)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="2" />
<DiscreteObjectKeyFrame KeyTime="00:00:01" Value="1" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="2x"
Storyboard.TargetName="TextRoot"
Storyboard.TargetProperty="(UIElement.Opacity)">
<LinearDoubleKeyFrame KeyTime="0" Value="0.0" />
<LinearDoubleKeyFrame KeyTime="00:00:00.167" Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="2x"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX">
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1.5" />
<SplineDoubleKeyFrame
KeySpline="0,0,0,1"
KeyTime="00:00:00.250"
Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="2x"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY">
<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1.5" />
<SplineDoubleKeyFrame
KeySpline="0,0,0,1"
KeyTime="00:00:00.250"
Value="1.0" />
<DiscreteDoubleKeyFrame KeyTime="0:0:01" Value="1.0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -0,0 +1,7 @@
using System;
namespace TheXamlGuy.UI.WPF.Controls;
public class CountdownCompletedEventArgs : EventArgs
{
}
@@ -0,0 +1,8 @@
namespace TheXamlGuy.UI.WPF.Controls;
public enum CountdownIdentifier
{
TenSecond,
FiveSecond,
TwoSecond
}
@@ -0,0 +1,8 @@
using System;
namespace TheXamlGuy.UI.WPF.Controls;
public class CountdownStartedEventArgs : EventArgs
{
}
+194
View File
@@ -0,0 +1,194 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace TheXamlGuy.UI.WPF.Controls;
public class FlipView : ListBox
{
public static readonly DependencyProperty ItemContainerTemplateSelectorProperty =
DependencyProperty.Register(nameof(ItemContainerTemplateSelector),
typeof(DataTemplateSelector), typeof(FlipView), new PropertyMetadata(new DefaultItemContainerTemplateSelector()));
public static readonly DependencyProperty UsesItemContainerTemplateSelectorProperty =
DependencyProperty.Register(nameof(UsesItemContainerTemplateSelector),
typeof(bool), typeof(FlipView));
private object? current;
private AnimatedScrollViewer? scrollViewer;
public FlipView()
{
DefaultStyleKey = typeof(FlipView);
}
public DataTemplateSelector ItemContainerTemplateSelector
{
get => (DataTemplateSelector)GetValue(ItemContainerTemplateSelectorProperty);
set => SetValue(ItemContainerTemplateSelectorProperty, value);
}
public bool UsesItemContainerTemplateSelector
{
get => (bool)GetValue(UsesItemContainerTemplateSelectorProperty);
set => SetValue(UsesItemContainerTemplateSelectorProperty, value);
}
public void Next()
{
if (SelectedIndex < Items.Count - 1)
{
SelectedIndex++;
}
else
{
SelectedIndex = 0;
}
if (scrollViewer is not null)
{
double scrollOffset = Math.Min(scrollViewer.CurrentHorizontalOffset + GetDesiredItemWidth(), scrollViewer.ScrollableWidth);
scrollViewer.ScrollToHorizontalOffsetWithAnimation(scrollOffset);
}
}
public override void OnApplyTemplate()
{
scrollViewer = GetTemplateChild("ScrollingHost") as AnimatedScrollViewer;
if (scrollViewer is not null)
{
scrollViewer.SizeChanged += OnScrollViewerSizeChanged;
}
base.OnApplyTemplate();
}
public void Previous()
{
if (SelectedIndex > 0)
{
SelectedIndex--;
}
else
{
SelectedIndex = Items.Count - 1;
}
if (scrollViewer is not null)
{
double scrollOffset = Math.Min(scrollViewer.CurrentHorizontalOffset - GetDesiredItemWidth(), scrollViewer.ScrollableWidth);
scrollViewer.ScrollToHorizontalOffsetWithAnimation(scrollOffset);
}
}
protected override DependencyObject GetContainerForItemOverride()
{
object? current = this.current;
this.current = null;
if (current is not null)
{
FlipViewItem? item = null;
if (UsesItemContainerTemplateSelector)
{
if (ItemContainerTemplateSelector.SelectTemplate(current, this) is DataTemplate dataTemplate)
{
DependencyObject container = dataTemplate.LoadContent();
switch (container)
{
case FlipViewItem:
item = container as FlipViewItem;
break;
case TemplateGeneratorControl template:
item = template.Content is FlipViewItem ? template.Content as FlipViewItem : new FlipViewItem { Content = template.Content };
break;
}
}
item ??= new FlipViewItem();
return item;
}
}
return new FlipViewItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
if (item is not FlipViewItem)
{
current = item;
return false;
}
return true;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
if (element is FlipViewItem flipViewItem)
{
Thickness flipViewItemMargin = flipViewItem.Margin;
double value = GetDesiredItemWidth();
value -= (flipViewItemMargin.Left + flipViewItemMargin.Right);
flipViewItem.Width = value;
value = GetDesiredItemHeight();
value -= (flipViewItemMargin.Top + flipViewItemMargin.Bottom);
flipViewItem.Height = value;
}
}
private double GetDesiredItemHeight()
{
double height = scrollViewer is not null ? scrollViewer.ActualHeight : ActualHeight;
if (height <= 0)
{
height = Height;
}
return height;
}
private double GetDesiredItemWidth()
{
double width = scrollViewer is not null ? scrollViewer.ActualWidth : ActualWidth;
if (width <= 0)
{
width = Width;
}
return width;
}
private void OnScrollViewerSizeChanged(object sender, SizeChangedEventArgs args)
{
double width = GetDesiredItemWidth();
double height = GetDesiredItemHeight();
for (int i = 0; i < Items.Count; i++)
{
if (ItemContainerGenerator.ContainerFromIndex(i) is FlipViewItem container)
{
Thickness margin = container.Margin;
double value = width - (margin.Left + margin.Right);
container.Width = value;
value = height - (margin.Top + margin.Bottom);
container.Height = value;
ScrollIntoView(SelectedItem);
}
}
}
}
+41
View File
@@ -0,0 +1,41 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TheXamlGuy.UI.WPF.Controls">
<Style TargetType="controls:FlipView">
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:FlipView">
<controls:AnimatedScrollViewer
x:Name="ScrollingHost"
Padding="{TemplateBinding Padding}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsInertiaEnabled="True"
IsTabStop="False"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
<ItemsPresenter />
</controls:AnimatedScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="controls:FlipViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:FlipViewItem">
<ContentPresenter Margin="{TemplateBinding Padding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
+11
View File
@@ -0,0 +1,11 @@
using System.Windows.Controls;
namespace TheXamlGuy.UI.WPF.Controls;
public class FlipViewItem : ContentControl
{
public FlipViewItem()
{
DefaultStyleKey = typeof(FlipViewItem);
}
}
+120
View File
@@ -0,0 +1,120 @@
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace TheXamlGuy.UI.WPF.Controls;
public class Arc : Shape
{
public static readonly DependencyProperty DirectionProperty =
DependencyProperty.Register(nameof(Direction),
typeof(SweepDirection), typeof(Arc), new PropertyMetadata(SweepDirection.Clockwise));
public static readonly DependencyProperty EndAngleProperty =
DependencyProperty.Register(nameof(EndAngle),
typeof(double), typeof(Arc), new PropertyMetadata(0.0d, OnEndAnglePropertyChanged));
public static readonly DependencyProperty OriginRotationDegreesProperty =
DependencyProperty.Register(nameof(OriginRotationDegrees),
typeof(double), typeof(Arc), new PropertyMetadata(270.0, new PropertyChangedCallback(OnOriginRotationDegreesPropertyChanged)));
public static readonly DependencyProperty StartAngleProperty =
DependencyProperty.Register(nameof(StartAngle),
typeof(double), typeof(Arc), new PropertyMetadata(0.0d, OnStartAnglePropertyChanged));
public SweepDirection Direction
{
get => (SweepDirection)GetValue(DirectionProperty);
set => SetValue(DirectionProperty, value);
}
public double EndAngle
{
get => (double)GetValue(EndAngleProperty);
set => SetValue(EndAngleProperty, value);
}
public double OriginRotationDegrees
{
get => (double)GetValue(OriginRotationDegreesProperty);
set => SetValue(OriginRotationDegreesProperty, value);
}
public double StartAngle
{
get => (double)GetValue(StartAngleProperty);
set => SetValue(StartAngleProperty, value);
}
protected override Geometry DefiningGeometry => GetDefiningGeometry();
protected override Size MeasureOverride(Size constraint)
{
return constraint;
}
private static void OnEndAnglePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as Arc)?.OnEndAnglePropertyChanged();
}
private static void OnOriginRotationDegreesPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as Arc)?.OnOriginRotationDegreesPropertyChanged();
}
private static void OnStartAnglePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as Arc)?.OnStartAnglePropertyChanged();
}
private Geometry GetDefiningGeometry()
{
Point startPoint = PointAtAngle(Math.Min(StartAngle, EndAngle), Direction);
Point endPoint = PointAtAngle(Math.Max(StartAngle, EndAngle), Direction);
Size arcSize = new(Math.Max(0, (ActualWidth - StrokeThickness) / 2), Math.Max(0, (ActualHeight - StrokeThickness) / 2));
bool isLargeArc = Math.Abs(EndAngle - StartAngle) > 180;
StreamGeometry streamGeometry = new();
using (StreamGeometryContext context = streamGeometry.Open())
{
context.BeginFigure(startPoint, false, false);
context.ArcTo(endPoint, arcSize, 0, isLargeArc, Direction, true, false);
}
streamGeometry.Transform = new TranslateTransform(StrokeThickness / 2, StrokeThickness / 2);
return streamGeometry;
}
private void OnEndAnglePropertyChanged()
{
InvalidateVisual();
}
private void OnOriginRotationDegreesPropertyChanged()
{
InvalidateVisual();
}
private void OnStartAnglePropertyChanged()
{
InvalidateVisual();
}
private Point PointAtAngle(double angle, SweepDirection sweep)
{
double translatedAngle = angle + OriginRotationDegrees;
double radAngle = translatedAngle * (Math.PI / 180);
double xr = (RenderSize.Width - StrokeThickness) / 2;
double yr = (RenderSize.Height - StrokeThickness) / 2;
double x = xr + xr * Math.Cos(radAngle);
double y = yr * Math.Sin(radAngle);
y = sweep == SweepDirection.Counterclockwise ? yr - y : yr + y;
return new Point(x, y);
}
}
@@ -0,0 +1,24 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace TheXamlGuy.UI.WPF.Controls;
public class DefaultItemContainerTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (container is ItemsControl itemsControl)
{
Type itemType = item.GetType();
DataTemplateKey key = new(itemType);
if (itemsControl.TryFindResource(key) is DataTemplate template)
{
return template;
}
}
return base.SelectTemplate(item, container);
}
}
@@ -0,0 +1,95 @@
using System.Windows;
using System.Windows.Controls.Primitives;
namespace TheXamlGuy.UI.WPF.Controls;
public class ProgressRing : RangeBase
{
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.Register(nameof(IsActive),
typeof(bool), typeof(ProgressRing), new PropertyMetadata(true, OnIsActivePropertyChanged));
public static readonly DependencyProperty IsIndeterminateProperty =
DependencyProperty.Register(nameof(IsIndeterminate),
typeof(bool), typeof(ProgressRing), new PropertyMetadata(false, OnIsIndeterminatePropertyChanged));
public static readonly DependencyProperty ThicknessProperty =
DependencyProperty.Register(nameof(Thickness),
typeof(double), typeof(ProgressRing));
public static DependencyProperty TemplateSettingsProperty =
DependencyProperty.Register(nameof(TemplateSettings),
typeof(ProgressRingTemplateSettings), typeof(ProgressRing));
public ProgressRing()
{
DefaultStyleKey = typeof(ProgressRing);
SetValue(TemplateSettingsProperty, new ProgressRingTemplateSettings());
}
public bool IsActive
{
get => (bool)GetValue(IsActiveProperty);
set => SetValue(IsActiveProperty, value);
}
public bool IsIndeterminate
{
get => (bool)GetValue(IsIndeterminateProperty);
set => SetValue(IsIndeterminateProperty, value);
}
public double Thickness
{
get => (double)GetValue(ThicknessProperty);
set => SetValue(ThicknessProperty, value);
}
public ProgressRingTemplateSettings TemplateSettings
{
get => (ProgressRingTemplateSettings)GetValue(TemplateSettingsProperty);
set => SetValue(TemplateSettingsProperty, value);
}
public override void OnApplyTemplate()
{
UpdateValue();
UpdateVisualState();
}
protected override void OnValueChanged(double oldValue, double newValue)
{
UpdateValue();
base.OnValueChanged(oldValue, newValue);
}
private void UpdateValue()
{
TemplateSettings.EndAngle = Value == Maximum ? 359.999 : 359.999 * (Value / (Maximum - Minimum));
}
private static void OnIsActivePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as ProgressRing)?.OnIsActivePropertyChanged();
}
private static void OnIsIndeterminatePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as ProgressRing)?.OnIsIndeterminatePropertyChanged();
}
private void OnIsActivePropertyChanged()
{
UpdateVisualState();
}
private void OnIsIndeterminatePropertyChanged()
{
UpdateVisualState();
}
private void UpdateVisualState()
{
VisualStateManager.GoToState(this, IsActive ? IsIndeterminate ? "IndeterminateActive" : "Active" : "Inactive", true);
}
}
@@ -0,0 +1,89 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TheXamlGuy.UI.WPF.Controls"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<SolidColorBrush x:Key="ProgressRingForeground" Color="White" />
<SolidColorBrush x:Key="ProgressRingBackground" Color="Transparent" />
<system:Double x:Key="ProgressRingStrokeThickness">4</system:Double>
<Style TargetType="controls:ProgressRing">
<Setter Property="Foreground" Value="{StaticResource ProgressRingForeground}" />
<Setter Property="Background" Value="{StaticResource ProgressRingBackground}" />
<Setter Property="Thickness" Value="{StaticResource ProgressRingStrokeThickness}" />
<Setter Property="IsHitTestVisible" Value="False" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="MinHeight" Value="16" />
<Setter Property="MinWidth" Value="16" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Width" Value="32" />
<Setter Property="Height" Value="32" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:ProgressRing">
<Grid x:Name="LayoutRoot">
<Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<controls:Arc
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
EndAngle="359"
StartAngle="0"
Stroke="{TemplateBinding Background}"
StrokeThickness="{TemplateBinding Thickness}" />
<controls:Arc
x:Name="ForegroundArc"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
EndAngle="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EndAngle}"
RenderTransformOrigin="0.5,0.5"
StartAngle="0"
Stroke="{TemplateBinding Foreground}"
StrokeEndLineCap="Round"
StrokeStartLineCap="Round"
StrokeThickness="{TemplateBinding Thickness}">
<controls:Arc.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="RotateTransform" />
</TransformGroup>
</controls:Arc.RenderTransform>
</controls:Arc>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Inactive">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ForegroundArc"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminateActive">
<Storyboard>
<DoubleAnimationUsingKeyFrames
RepeatBehavior="Forever"
Storyboard.TargetName="ForegroundArc"
Storyboard.TargetProperty="EndAngle">
<SplineDoubleKeyFrame KeyTime="0" Value="0" />
<SplineDoubleKeyFrame KeyTime="0:0:1" Value="179" />
<SplineDoubleKeyFrame KeyTime="0:0:2" Value="0" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimation
RepeatBehavior="Forever"
Storyboard.TargetName="RotateTransform"
Storyboard.TargetProperty="Angle"
From="00"
To="359"
Duration="0:0:0.65" />
</Storyboard>
</VisualState>
<VisualState x:Name="Active" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -0,0 +1,16 @@
using System.Windows;
namespace TheXamlGuy.UI.WPF.Controls;
public class ProgressRingTemplateSettings : DependencyObject
{
public static readonly DependencyProperty EndAngleProperty =
DependencyProperty.Register(nameof(EndAngle),
typeof(double), typeof(ProgressRingTemplateSettings));
public double EndAngle
{
get => (double)GetValue(EndAngleProperty);
set => SetValue(EndAngleProperty, value);
}
}
@@ -0,0 +1,5 @@
using System.Windows;
using System.Windows.Markup;
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "TheXamlGuy.UI.WPF.Controls")]
+9
View File
@@ -0,0 +1,9 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/TheXamlGuy.UI.WPF.Controls;component/CameraPreview/CameraPreview.xaml" />
<ResourceDictionary Source="/TheXamlGuy.UI.WPF.Controls;component/Countdown/Countdown.xaml" />
<ResourceDictionary Source="/TheXamlGuy.UI.WPF.Controls;component/FlipView/FlipView.xaml" />
<ResourceDictionary Source="/TheXamlGuy.UI.WPF.Controls;component/TransitioningContentControl/TransitioningContentControl.xaml" />
<ResourceDictionary Source="/TheXamlGuy.UI.WPF.Controls;component/ProgressRing/ProgressRing.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
@@ -0,0 +1,359 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
namespace TheXamlGuy.UI.WPF.Controls;
public class TransitioningContentControl : ContentControl
{
public static readonly DependencyProperty TransitionDirectionProperty =
DependencyProperty.RegisterAttached(nameof(TransitioningContentControlTransitionDirection),
typeof(TransitioningContentControlTransitionDirection), typeof(TransitioningContentControl), new PropertyMetadata(TransitioningContentControlTransitionDirection.Left));
public static readonly DependencyProperty TransitionDurationProperty =
DependencyProperty.RegisterAttached(nameof(Duration),
typeof(TimeSpan), typeof(TransitioningContentControl), new PropertyMetadata(TimeSpan.FromSeconds(0.3)));
public static readonly DependencyProperty TransitionProperty =
DependencyProperty.RegisterAttached(nameof(Transition),
typeof(TransitioningContentControlTransitionType), typeof(TransitioningContentControl), new PropertyMetadata(TransitioningContentControlTransitionType.Fade));
private Storyboard? completingTransition;
private Grid? container;
private ContentPresenter? currentContentPresentationSite;
private bool isTransitioning;
private VisualStateGroup? presentationGroup;
private Image? previousImageSite;
private Storyboard? startingTransition;
public TransitioningContentControl()
{
DefaultStyleKey = typeof(TransitioningContentControl);
}
public event RoutedEventHandler? TransitionCompleted;
public event RoutedEventHandler? TransitionStarted;
public TransitioningContentControlTransitionDirection Direction
{
get => (TransitioningContentControlTransitionDirection)GetValue(TransitionDirectionProperty);
set => SetValue(TransitionDirectionProperty, value);
}
public TimeSpan Duration
{
get => (TimeSpan)GetValue(TransitionDurationProperty);
set => SetValue(TransitionDurationProperty, value);
}
public TransitioningContentControlTransitionType Transition
{
get => (TransitioningContentControlTransitionType)GetValue(TransitionProperty);
set => SetValue(TransitionProperty, value);
}
private Storyboard? CompletingTransition
{
get => completingTransition;
set
{
if (completingTransition is not null)
{
CompletingTransition!.Completed -= OnTransitionCompleted;
}
completingTransition = value;
if (completingTransition is not null)
{
CompletingTransition!.Completed += OnTransitionCompleted;
SetTransitionDefaultValues();
}
}
}
private Storyboard? StartingTransition
{
get => startingTransition;
set
{
startingTransition = value;
if (startingTransition is not null)
{
SetTransitionDefaultValues();
}
}
}
public override void OnApplyTemplate()
{
container = GetTemplateChild("Container") as Grid;
if (container is not null)
{
presentationGroup = ((IEnumerable<VisualStateGroup>)VisualStateManager.GetVisualStateGroups(container))!.FirstOrDefault(x => x.Name == "PresentationStates");
}
currentContentPresentationSite = GetTemplateChild("CurrentContentPresentationSite") as ContentPresenter;
previousImageSite = GetTemplateChild("PreviousImageSite") as Image;
if (currentContentPresentationSite is not null)
{
currentContentPresentationSite.Content = Content;
}
VisualStateManager.GoToState(this, "Normal", false);
}
protected override void OnContentChanged(object oldContent, object newContent)
{
QueueTransition(newContent);
base.OnContentChanged(oldContent, newContent);
}
private static RenderTargetBitmap GetRenderTargetBitmapFromUiElement(UIElement uiElement)
{
if (uiElement.RenderSize.Height == 0 || uiElement.RenderSize.Width == 0)
{
return default!;
}
DpiScale dpiScale = VisualTreeHelper.GetDpi(uiElement);
double pixelWidth = Math.Max(1.0, uiElement.RenderSize.Width * dpiScale.DpiScaleX);
double pixelHeight = Math.Max(1.0, uiElement.RenderSize.Height * dpiScale.DpiScaleY);
RenderTargetBitmap renderTargetBitmap = new(Convert.ToInt32(pixelWidth), Convert.ToInt32(pixelHeight), dpiScale.PixelsPerInchX, dpiScale.PixelsPerInchY, PixelFormats.Pbgra32);
renderTargetBitmap.Render(uiElement);
renderTargetBitmap.Freeze();
return renderTargetBitmap;
}
private void AbortTransition()
{
VisualStateManager.GoToState(this, "Normal", false);
isTransitioning = false;
if (previousImageSite is not null)
{
if (previousImageSite.Source is RenderTargetBitmap renderTargetBitmap)
{
renderTargetBitmap.Clear();
}
previousImageSite.Source = null;
previousImageSite.UpdateLayout();
}
}
private Storyboard GetTransitionStoryboardByName(string transitionName)
{
if (presentationGroup is not null)
{
if (((IEnumerable<VisualState>)presentationGroup.States).Where(x => x.Name == transitionName).Select(x => x.Storyboard).FirstOrDefault() is Storyboard transition)
{
return transition;
}
}
return new Storyboard();
}
private void OnTransitionCompleted(object? sender, EventArgs args)
{
AbortTransition();
TransitionCompleted?.Invoke(this, new RoutedEventArgs());
}
private void QueueTransition(object newContent)
{
if (currentContentPresentationSite is null)
{
return;
}
if (isTransitioning || previousImageSite is null)
{
currentContentPresentationSite.Content = newContent;
return;
}
previousImageSite.Source = GetRenderTargetBitmapFromUiElement(currentContentPresentationSite);
currentContentPresentationSite.Content = newContent;
string transitionInName = string.Empty;
int statesRemaining = 0;
string startingTransitionName = string.Empty;
if (Transition == TransitioningContentControlTransitionType.Bounce)
{
transitionInName = $"{Transition}{Direction}In";
CompletingTransition = GetTransitionStoryboardByName(transitionInName);
startingTransitionName = $"{Transition}{Direction}Out";
StartingTransition = (Storyboard?)GetTransitionStoryboardByName(startingTransitionName);
statesRemaining = 2;
StartingTransition!.Completed += NextState;
}
else
{
if (StartingTransition is not null)
{
StartingTransition!.Completed -= NextState;
}
StartingTransition = null;
startingTransitionName = Transition == TransitioningContentControlTransitionType.Fade ? "Fade" : $"{Transition}{Direction}";
CompletingTransition = GetTransitionStoryboardByName(startingTransitionName);
statesRemaining = 1;
}
isTransitioning = true;
RaiseTransitionStarted();
statesRemaining--;
VisualStateManager.GoToState(this, startingTransitionName, false);
void NextState(object? sender, EventArgs args)
{
StartingTransition!.Completed -= NextState;
if (statesRemaining == 1)
{
statesRemaining--;
VisualStateManager.GoToState(this, transitionInName, false);
}
}
}
private void RaiseTransitionStarted()
{
TransitionStarted?.Invoke(this, new RoutedEventArgs());
}
private void SetTransitionDefaultValues()
{
switch (Transition)
{
case TransitioningContentControlTransitionType.Fade:
{
if (CompletingTransition is null)
{
return;
}
DoubleAnimation completingDoubleAnimation = (DoubleAnimation)CompletingTransition.Children[0];
completingDoubleAnimation.Duration = Duration;
DoubleAnimation startingDoubleAnimation = (DoubleAnimation)CompletingTransition.Children[1];
startingDoubleAnimation.Duration = Duration;
break;
}
case TransitioningContentControlTransitionType.Slide:
{
if (CompletingTransition is null)
{
return;
}
DoubleAnimation startingDoubleAnimation = (DoubleAnimation)CompletingTransition.Children[0];
startingDoubleAnimation.Duration = Duration;
startingDoubleAnimation.From = Direction switch
{
TransitioningContentControlTransitionDirection.Down => -ActualHeight,
TransitioningContentControlTransitionDirection.Up => ActualHeight,
TransitioningContentControlTransitionDirection.Right => -ActualWidth,
TransitioningContentControlTransitionDirection.Left => ActualWidth,
_ => throw new ArgumentOutOfRangeException(nameof(TransitioningContentControlTransitionDirection))
};
break;
}
case TransitioningContentControlTransitionType.Move:
{
if (CompletingTransition is null)
{
return;
}
DoubleAnimation completingDoubleAnimation = (DoubleAnimation)CompletingTransition.Children[0];
DoubleAnimation startingDoubleAnimation = (DoubleAnimation)CompletingTransition.Children[1];
startingDoubleAnimation.Duration = Duration;
completingDoubleAnimation.Duration = Duration;
switch (Direction)
{
case TransitioningContentControlTransitionDirection.Down:
startingDoubleAnimation.To = ActualHeight;
completingDoubleAnimation.From = -ActualHeight;
break;
case TransitioningContentControlTransitionDirection.Up:
startingDoubleAnimation.To = -ActualHeight;
completingDoubleAnimation.From = ActualHeight;
break;
case TransitioningContentControlTransitionDirection.Right:
startingDoubleAnimation.To = ActualWidth;
completingDoubleAnimation.From = -ActualWidth;
break;
case TransitioningContentControlTransitionDirection.Left:
startingDoubleAnimation.To = -ActualWidth;
completingDoubleAnimation.From = ActualWidth;
break;
default: throw new ArgumentOutOfRangeException(nameof(TransitioningContentControlTransitionDirection));
}
break;
}
case TransitioningContentControlTransitionType.Bounce:
{
if (CompletingTransition is not null)
{
DoubleAnimationUsingKeyFrames completingDoubleAnimation = (DoubleAnimationUsingKeyFrames)CompletingTransition.Children[0];
completingDoubleAnimation.KeyFrames[1].Value = ActualHeight;
}
if (StartingTransition is null)
{
return;
}
DoubleAnimation startingDoubleAnimation = (DoubleAnimation)StartingTransition.Children[0];
startingDoubleAnimation.To = Direction switch
{
TransitioningContentControlTransitionDirection.Down => ActualHeight,
TransitioningContentControlTransitionDirection.Up => -ActualHeight,
TransitioningContentControlTransitionDirection.Right => ActualWidth,
TransitioningContentControlTransitionDirection.Left => -ActualWidth,
_ => throw new ArgumentOutOfRangeException(nameof(TransitioningContentControlTransitionDirection))
};
break;
}
case TransitioningContentControlTransitionType.Drop: break;
default: throw new ArgumentOutOfRangeException(nameof(TransitioningContentControlTransitionDirection));
}
}
}
@@ -0,0 +1,496 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TheXamlGuy.UI.WPF.Controls">
<Style TargetType="controls:TransitioningContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:TransitioningContentControl">
<Grid
x:Name="Container"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Image
x:Name="PreviousImageSite"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Source="{x:Null}">
<Image.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<ScaleTransform ScaleX="1" ScaleY="1" />
<TranslateTransform X="0" Y="0" />
</TransformGroup.Children>
</TransformGroup>
</Image.RenderTransform>
</Image>
<ContentPresenter
x:Name="CurrentContentPresentationSite"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{x:Null}"
ContentTemplate="{TemplateBinding ContentTemplate}">
<ContentPresenter.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<ScaleTransform ScaleX="1" ScaleY="1" />
<TranslateTransform X="0" Y="0" />
</TransformGroup.Children>
</TransformGroup>
</ContentPresenter.RenderTransform>
</ContentPresenter>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PresentationStates">
<VisualState x:Name="Normal">
<Storyboard>
<ObjectAnimationUsingKeyFrames
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Fade">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="1"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="0"
To="1"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="SlideLeft">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="-30"
To="0"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="SlideRight">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="-30"
To="0"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="SlideDown">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="-30"
To="0"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="SlideUp">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="-30"
To="0"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="MoveLeft">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="-30"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="0"
To="30"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="MoveRight">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="-30"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="0"
To="30"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="MoveDown">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="-30"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="0"
To="30"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="MoveUp">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="-30"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="0"
To="30"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="DropDown">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="-30"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="0"
To="30"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="0"
To="1"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="1"
To="0"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="DropUp">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="0"
To="30"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="-30"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="0"
To="1"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="1"
To="0"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="DropRight">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="-30"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="0"
To="30"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="0"
To="1"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="1"
To="0"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="DropLeft">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="-30"
To="0"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="0"
To="30"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="CurrentContentPresentationSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="0"
To="1"
Duration="00:00:00.3" />
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="1"
To="0"
Duration="00:00:00.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="BounceLeftIn">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="-90" />
<EasingDoubleKeyFrame KeyTime="00:00:00.4" Value="-90" />
<EasingDoubleKeyFrame KeyTime="00:00:00.7" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.Opacity)">
<DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="0" />
<DiscreteDoubleKeyFrame KeyTime="00:00:00.4" Value="1" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousImageSite" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="BounceLeftOut">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="0"
To="-90"
Duration="00:00:00.2" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="BounceRightIn">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="-90" />
<EasingDoubleKeyFrame KeyTime="00:00:00.4" Value="-90" />
<EasingDoubleKeyFrame KeyTime="00:00:00.7" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.Opacity)">
<DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="0" />
<DiscreteDoubleKeyFrame KeyTime="00:00:00.4" Value="1" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousImageSite" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="BounceRightOut">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)"
From="0"
To="-90"
Duration="00:00:00.2" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="BounceUpIn">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="-90" />
<EasingDoubleKeyFrame KeyTime="00:00:00.4" Value="-90" />
<EasingDoubleKeyFrame KeyTime="00:00:00.7" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.Opacity)">
<DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="0" />
<DiscreteDoubleKeyFrame KeyTime="00:00:00.4" Value="1" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousImageSite" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="BounceUpOut">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="0"
To="-90"
Duration="00:00:00.2" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="BounceDownIn">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="-90" />
<EasingDoubleKeyFrame KeyTime="00:00:00.4" Value="-90" />
<EasingDoubleKeyFrame KeyTime="00:00:00.7" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.Opacity)">
<DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="0" />
<DiscreteDoubleKeyFrame KeyTime="00:00:00.4" Value="1" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousImageSite" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="BounceDownOut">
<Storyboard>
<DoubleAnimation
BeginTime="00:00:00"
Storyboard.TargetName="PreviousImageSite"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
From="0"
To="-90"
Duration="00:00:00.2" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentContentPresentationSite" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -0,0 +1,9 @@
namespace TheXamlGuy.UI.WPF.Controls;
public enum TransitioningContentControlTransitionDirection
{
Up,
Down,
Left,
Right
}
@@ -0,0 +1,10 @@
namespace TheXamlGuy.UI.WPF.Controls;
public enum TransitioningContentControlTransitionType
{
Fade,
Move,
Slide,
Drop,
Bounce
}
+14
View File
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows10.0.18362.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<RootNamespace>TheXamlGuy.UI.WPF.Controls</RootNamespace>
<AssemblyName>TheXamlGuy.UI.WPF.Controls</AssemblyName>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Media\Capture\Capture.csproj" />
<ProjectReference Include="..\WPF\WPF.csproj" />
</ItemGroup>
</Project>