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,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>TheXamlGuy.UI.Avalonia.Controls</RootNamespace>
<AssemblyName>TheXamlGuy.UI.Avalonia.Controls</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UI\UI.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,66 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Platform.Storage;
namespace TheXamlGuy.UI.Avalonia.Controls
{
public class FilePickerFilePickedEventArgs : EventArgs
{
}
public class FilePicker : TemplatedControl
{
public static readonly StyledProperty<bool> AllowMultipleProperty =
AvaloniaProperty.Register<FilePicker, bool>(nameof(AllowMultiple));
public static readonly StyledProperty<IReadOnlyList<FilePickerFileType>?> FileTypeFilterProperty =
AvaloniaProperty.Register<FilePicker, IReadOnlyList<FilePickerFileType>?>(nameof(FileTypeFilter));
public static readonly StyledProperty<IStorageFolder?> SuggestedStartLocationProperty =
AvaloniaProperty.Register<FilePicker, IStorageFolder?>(nameof(SuggestedStartLocation));
public static readonly StyledProperty<string?> TitleProperty =
AvaloniaProperty.Register<FilePicker, string?>(nameof(Title));
public bool AllowMultiple
{
get { return GetValue(AllowMultipleProperty); }
set { SetValue(AllowMultipleProperty, value); }
}
public IReadOnlyList<FilePickerFileType>? FileTypeFilter
{
get { return GetValue(FileTypeFilterProperty); }
set { SetValue(FileTypeFilterProperty, value); }
}
public IStorageFolder? SuggestedStartLocation
{
get { return GetValue(SuggestedStartLocationProperty); }
set { SetValue(SuggestedStartLocationProperty, value); }
}
public string? Title
{
get { return GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public event TypedEventHandler<FilePicker, FilePickerFilePickedEventArgs>? FilePicked;
public async void Open()
{
if (VisualRoot is Window window)
{
IReadOnlyList<IStorageFile> files = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
AllowMultiple = AllowMultiple,
FileTypeFilter = FileTypeFilter,
SuggestedStartLocation = SuggestedStartLocation,
Title = Title
});
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>TheXamlGuy.UI.Avalonia</RootNamespace>
<AssemblyName>TheXamlGuy.UI.Avalonia</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UI\UI.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="FluentAvalonia">
<HintPath>References\FluentAvalonia.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
@@ -0,0 +1,11 @@
using Avalonia.Data;
namespace TheXamlGuy.UI.Avalonia;
public static class MarkupExtensions
{
public static Binding ToBinding(this object value)
{
return value is Binding binding ? binding : new Binding() { Mode = BindingMode.OneWay, Source = value };
}
}
@@ -0,0 +1,82 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
namespace TheXamlGuy.UI.Avalonia;
public class ChangePropertyExtension : TriggerExtension
{
private static readonly AvaloniaProperty PropertyTargetProperty =
AvaloniaProperty.RegisterAttached<ChangePropertyExtension, Control, object>("PropertyTarget");
private static readonly AvaloniaProperty PropertyValueProperty =
AvaloniaProperty.RegisterAttached<ChangePropertyExtension, Control, object>("PropertyValue");
private readonly string name;
private readonly BindingBase targetBinding;
private readonly BindingBase valueBinding;
public ChangePropertyExtension(object target, string name, object value)
{
this.targetBinding = target is BindingBase targetBinding ? targetBinding : target.ToBinding();
this.valueBinding = value is BindingBase valueBinding ? valueBinding : value.ToBinding();
this.name = name;
}
protected override void OnInvoked(object sender, EventArgs args)
{
TargetObject?.Bind(PropertyTargetProperty, targetBinding);
if (TargetObject?.GetValue(PropertyTargetProperty) is { } target)
{
Type? targetType = target.GetType();
if (targetType.GetProperty(name) is PropertyInfo propertyInfo)
{
TargetObject?.Bind(PropertyValueProperty, valueBinding);
object? value = TargetObject?.GetValue(PropertyValueProperty);
TypeConverter? converter = TypeConverterHelper.GetTypeConverter(propertyInfo.PropertyType);
object? newValue = value;
if (value is not null)
{
if (converter?.CanConvertFrom(value.GetType()) == true)
{
newValue = converter.ConvertFrom(null, CultureInfo.InvariantCulture, value);
}
else
{
if (converter?.CanConvertTo(propertyInfo.PropertyType) == true)
{
newValue = converter.ConvertTo(null, CultureInfo.InvariantCulture, value, propertyInfo.PropertyType);
}
}
}
if (target is TemplatedControl control )
{
if (AvaloniaPropertyRegistry.Instance.FindRegistered(targetType, name) is AvaloniaProperty property)
{
control.SetValue(property, newValue);
}
if (propertyInfo.PropertyType == typeof(Classes))
{
control.Classes.Clear();
control.Classes.Add($"{newValue}");
}
}
else
{
propertyInfo?.SetValue(target, newValue, Array.Empty<object>());
}
}
}
}
}
+320
View File
@@ -0,0 +1,320 @@
using Avalonia.Markup.Xaml;
namespace TheXamlGuy.UI.Avalonia;
public class CompositeExtension : TriggerExtension
{
[ConstructorArgument(nameof(Triggers))]
public TriggerCollection Triggers { get; } = new TriggerCollection();
public CompositeExtension(object args0)
{
Triggers.Add(args0);
}
public CompositeExtension(object args0,
object args1)
{
Triggers.Add(args0);
Triggers.Add(args1);
}
public CompositeExtension(object args0,
object args1,
object args2)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
}
public CompositeExtension(object args0, object args1, object args2, object args3, object args4,
object args5, object args6, object args7, object args8, object args9, object args10)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
}
public CompositeExtension(object args0, object args1, object args2, object args3, object args4,
object args5, object args6, object args7, object args8, object args9, object args10, object args11)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
Triggers.Add(args12);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
Triggers.Add(args12);
Triggers.Add(args13);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
Triggers.Add(args12);
Triggers.Add(args13);
Triggers.Add(args14);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14,
object args15)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
Triggers.Add(args12);
Triggers.Add(args13);
Triggers.Add(args14);
Triggers.Add(args15);
}
protected override void OnInvoked(object sender, EventArgs args)
{
foreach (Delegate? trigger in Triggers)
{
trigger.Method.Invoke(trigger.Target, new object[] { sender, args });
}
base.OnInvoked(sender, args);
}
}
+451
View File
@@ -0,0 +1,451 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Xaml;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace TheXamlGuy.UI.Avalonia;
[ConstructorArgument(nameof(name))]
public class InvokeExtension : TriggerExtension
{
private static readonly AvaloniaProperty TargetProperty =
AvaloniaProperty.RegisterAttached<InvokeExtension, Control, object>("Target");
private readonly object name;
private readonly List<object> parameters = new();
public InvokeExtension(object name)
{
this.name = name;
}
public InvokeExtension(string name,
object args1)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
parameters.Add(args14 is MarkupExtension ? args14 : args14.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14,
object args15)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
parameters.Add(args14 is MarkupExtension ? args14 : args14.ToBinding());
parameters.Add(args15 is MarkupExtension ? args15 : args15.ToBinding());
}
// KEEP THIS
//protected override void OnAttached(IServiceProvider serviceProvider)
//{
// if (name.StartsWith("$"))
// {
// if (serviceProvider is ITypeDescriptorContext typeDescriptorContext)
// {
// targetBinding = new(name) { DefaultAnchor = new WeakReference(TargetObject) };
// if (serviceProvider.GetService(typeof(INameScope)) is INameScope nameScope)
// {
// targetBinding.NameScope = new WeakReference<INameScope>(nameScope);
// }
// if (typeDescriptorContext.GetService(typeof(IXamlTypeResolver)) is IXamlTypeResolver xamlTypeResolver)
// {
// targetBinding.TypeResolver = (prefix, type) =>
// {
// string name = string.IsNullOrEmpty(prefix) ? type : $"{prefix}:{type}";
// return xamlTypeResolver.Resolve(name);
// };
// }
// }
// }
// base.OnAttached(serviceProvider);
//}
protected override void OnInvoked(object sender, EventArgs args)
{
if (sender is AvaloniaObject avaloniaObject)
{
CreaterHandler(avaloniaObject, args);
}
}
private void CreaterHandler(AvaloniaObject sender, EventArgs args)
{
if (TryGetInvoke(sender, out (object? Target, MethodInfo? MethodInfo) invoker))
{
if (invoker.Target is object target)
{
if (invoker.MethodInfo is MethodInfo methodInfo)
{
ParameterInfo[] parameterInfo = methodInfo.GetParameters();
List<object> parameters = new();
//foreach (object? parameter in this.parameters)
//{
// switch (parameter)
// {
// case IParameter keyedParameter:
// BindingOperations.SetBinding(sender, ParameterProperty, parameter.ToBinding());
// parameters.Add(new KeyValuePair<string, object>(keyedParameter.Key, (dynamic)sender.GetValue(ParameterProperty)));
// break;
// case IEventParameter eventParameter:
// parameters.AddRange(eventParameter.GetParameters(args));
// break;
// default:
// BindingOperations.SetBinding(sender, ParameterProperty, parameter.ToBinding());
// parameters.Add((dynamic)sender.GetValue(ParameterProperty));
// break;
// }
//}
methodInfo.Invoke(target is Action action ? action.Target : target, parameters.Any() ? parameters.ToArray() : parameterInfo.Length > 0 ? new object?[] { null } : Array.Empty<object>());
}
}
}
}
private bool TryGetInvoke(AvaloniaObject sender, [AllowNull] out (object?, MethodInfo?) invoker)
{
if (name is Binding binding)
{
sender.Bind(TargetProperty, binding);
if (sender.GetValue(TargetProperty) is Action action)
{
invoker = new(action.Target, action.Method);
return true;
}
}
if (name is string)
{
if (sender.GetValue(StyledElement.DataContextProperty) is object dataContext)
{
if (dataContext.GetType().GetMethod((string)name, BindingFlags.Public | BindingFlags.Instance) is MethodInfo methodInfo)
{
invoker = new(dataContext, methodInfo);
return true;
}
}
}
invoker = default;
return false;
}
}
+14
View File
@@ -0,0 +1,14 @@
using System.Collections.ObjectModel;
namespace TheXamlGuy.UI.Avalonia;
public class TriggerCollection : Collection<Delegate>
{
public void Add(object item)
{
if (item is Delegate trigger)
{
base.Add(trigger);
}
}
}
+74
View File
@@ -0,0 +1,74 @@
using Avalonia;
using Avalonia.Markup.Xaml;
using System.Reflection;
namespace TheXamlGuy.UI.Avalonia;
public class TriggerExtension : MarkupExtension
{
public AvaloniaObject? TargetObject { get; protected set; }
protected object? TargetInvoke { get; private set; }
public void Invoke(object sender, EventArgs args)
{
OnInvoked(sender, args);
}
public override object? ProvideValue(IServiceProvider serviceProvider)
{
if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget target)
{
if (target.TargetObject is AvaloniaObject avaloniaObject)
{
TargetObject = avaloniaObject;
}
else if (serviceProvider.GetService(typeof(IRootObjectProvider)) is IRootObjectProvider root)
{
TargetObject = (AvaloniaObject)root.RootObject;
}
if (TargetObject is not null)
{
string? targetName = target.TargetProperty as string;
TargetInvoke = target.TargetProperty;
OnAttached(serviceProvider);
EventInfo? eventInfo = target.TargetProperty as EventInfo ?? (targetName is not null ? TargetObject.GetType().GetEvent(targetName) : null);
MethodInfo? methodInfo = eventInfo is not null ? null : target.TargetProperty as MethodInfo ?? (targetName is not null ? TargetObject.GetType().GetMethod(targetName) : null);
MethodInfo invokeMethod = GetType().GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public)!;
if (invokeMethod is null)
{
return this;
}
if (eventInfo is not null)
{
return Delegate.CreateDelegate(eventInfo.EventHandlerType!, this, invokeMethod);
}
if (methodInfo is not null)
{
if (methodInfo.GetParameters() is ParameterInfo[] methodParameters && methodParameters is { Length: 2 })
{
return Delegate.CreateDelegate(methodParameters[1].ParameterType, this, invokeMethod);
}
}
}
}
return null;
}
protected virtual void OnAttached(IServiceProvider serviceProvider)
{
}
protected virtual void OnInvoked(object sender, EventArgs args)
{
}
}
+4
View File
@@ -0,0 +1,4 @@
using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "FluentAvalonia.UI.Controls")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "TheXamlGuy.UI.Avalonia")]
+6
View File
@@ -0,0 +1,6 @@
namespace TheXamlGuy.UI;
public interface IEventParameter
{
List<object> GetParameters(EventArgs args);
}
+6
View File
@@ -0,0 +1,6 @@
namespace TheXamlGuy.UI;
public interface IParameter
{
string? Key { get; }
}
+42
View File
@@ -0,0 +1,42 @@
using System.ComponentModel;
using System.Globalization;
namespace TheXamlGuy.UI;
public static class TypeConverterHelper
{
public static object? DoConversionFrom(TypeConverter converter, object value)
{
object? returnValue = value;
try
{
if (converter != null && value != null && converter.CanConvertFrom(value.GetType()))
{
returnValue = converter.ConvertFrom(null, CultureInfo.InvariantCulture, value);
}
}
catch (Exception exception) when (ShouldEatException(exception))
{
}
return returnValue;
}
private static bool ShouldEatException(Exception exception)
{
bool shouldEat = false;
if (exception.InnerException != null)
{
shouldEat |= ShouldEatException(exception.InnerException);
}
shouldEat |= exception is FormatException;
return shouldEat;
}
public static TypeConverter GetTypeConverter(Type type)
{
return TypeDescriptor.GetConverter(type);
}
}
+3
View File
@@ -0,0 +1,3 @@
namespace TheXamlGuy.UI;
public delegate void TypedEventHandler<TSender, TResult>(TSender sender, TResult args);
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>TheXamlGuy.UI</RootNamespace>
<AssemblyName>TheXamlGuy.UI</AssemblyName>
</PropertyGroup>
</Project>
@@ -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>
+45
View File
@@ -0,0 +1,45 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace TheXamlGuy.UI.WPF
{
public abstract class ValueConverter<TSource, TTarget> : MarkupExtension, IValueConverter
{
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ConvertTo((TSource)value, targetType, parameter, culture);
}
public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ConvertBackTo((TTarget)value, targetType, parameter, culture);
}
public TTarget? Convert(TSource value)
{
return ConvertTo(value, null, null, null);
}
public TSource? ConvertBack(TTarget value)
{
return ConvertBackTo(value, null, null, null);
}
protected virtual TTarget? ConvertTo(TSource value, Type? targetType, object? parameter, CultureInfo? culture)
{
return default;
}
protected virtual TSource? ConvertBackTo(TTarget value, Type? targetType, object? parameter, CultureInfo? culture)
{
return default;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF
{
public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return this.Aggregate(value,
(current, converter) => converter.Convert(current, targetType, parameter, culture));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
+46
View File
@@ -0,0 +1,46 @@
using System;
using System.Windows.Controls;
using System.Windows;
namespace TheXamlGuy.UI.WPF;
internal static class ValueBoxes
{
internal static object CollapsedBox = Visibility.Collapsed;
internal static object Double01Box = .1;
internal static object Double0Box = .0;
internal static object Double100Box = 100.0;
internal static object Double10Box = 10.0;
internal static object Double1Box = 1.0;
internal static object Double200Box = 200.0;
internal static object Double20Box = 20.0;
internal static object Double300Box = 300.0;
internal static object DoubleNeg1Box = -1.0;
internal static object FalseBox = false;
internal static object HiddenBox = Visibility.Hidden;
internal static object HorizontalBox = Orientation.Horizontal;
internal static object Int0Box = 0;
internal static object Int1Box = 1;
internal static object Int2Box = 2;
internal static object Int5Box = 5;
internal static object Int99Box = 99;
internal static object TrueBox = true;
internal static object VerticalBox = Orientation.Vertical;
internal static object VisibleBox = Visibility.Visible;
internal static object BooleanBox(bool value) => value ? TrueBox : FalseBox;
internal static object OrientationBox(Orientation value) =>
value == Orientation.Horizontal ? HorizontalBox : VerticalBox;
internal static object VisibilityBox(Visibility value)
{
return value switch
{
Visibility.Visible => VisibleBox,
Visibility.Hidden => HiddenBox,
Visibility.Collapsed => CollapsedBox,
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
};
}
}
+37
View File
@@ -0,0 +1,37 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF;
public static class EventArgsExtensions
{
public static dynamic? GetEventArguments(this EventArgs args, string? path, IValueConverter? converter, object? converterParameter)
{
if (path is { Length: > 0 })
{
if (GetEventArgsPropertyPathValue(args, path) is object value)
{
if (converter is not null)
{
return converter.Convert(value, typeof(object), converterParameter, CultureInfo.CurrentCulture);
}
return value;
}
}
return args;
}
private static object GetEventArgsPropertyPathValue(object args, string path)
{
object? value = args;
if (path is { })
{
value = PropertyPathHelper.GetValue(args, path);
}
return value;
}
}
@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace TheXamlGuy.UI.WPF;
public static class FrameworkElementExtensions
{
public static Rect? BoundsRelativeTo(this FrameworkElement source, Visual parent)
{
try
{
GeneralTransform generalTransform = source.TransformToAncestor(parent);
return generalTransform.TransformBounds(new Rect(0, 0, source.ActualWidth, source.ActualHeight));
}
catch
{
}
return Rect.Empty;
}
public static bool IsTemplateParent(this FrameworkElement source, FrameworkElement target)
{
if (source is null)
{
return false;
}
if (ReferenceEquals(source, target))
{
return true;
}
if (ReferenceEquals(source?.Parent, target))
{
return true;
}
FrameworkElement? parent = source?.Parent as FrameworkElement;
while (true)
{
if (parent is null)
{
break;
}
if (ReferenceEquals(parent, target))
{
return true;
}
parent = parent.Parent as FrameworkElement;
}
FrameworkElement? templateParent = source?.TemplatedParent as FrameworkElement;
while (true)
{
if (templateParent is null)
{
break;
}
if (ReferenceEquals(templateParent, target))
{
return true;
}
templateParent = templateParent.TemplatedParent as FrameworkElement;
}
return false;
}
public static bool IsPointerWithin(this FrameworkElement frameworkElement)
{
Point position = Mouse.PrimaryDevice.GetPosition(frameworkElement);
return position.X >= 0 && position.X <= frameworkElement.ActualWidth && position.Y >= 0 &&
position.Y <= frameworkElement.ActualHeight;
}
public static IEnumerable<VisualStateGroup> FindVisualStateGroups(this FrameworkElement frameworkElement)
{
IEnumerable<VisualStateGroup> visualStateGroups = (IEnumerable<VisualStateGroup>)VisualStateManager.GetVisualStateGroups(frameworkElement);
return visualStateGroups ?? Enumerable.Empty<VisualStateGroup>();
}
public static VisualStateGroup? FindVisualGroup(this FrameworkElement frameworkElement, string name)
{
IEnumerable<VisualStateGroup> visualStateGroups = (IEnumerable<VisualStateGroup>)VisualStateManager.GetVisualStateGroups(frameworkElement);
return visualStateGroups?.FirstOrDefault(x => x.Name == name);
}
public static VisualState? FindVisualState(this FrameworkElement frameworkElement, string name)
{
Collection<VisualStateGroup> visualStateGroups = (Collection<VisualStateGroup>)VisualStateManager.GetVisualStateGroups(frameworkElement);
return visualStateGroups
?.SelectMany(visualStateGroup => visualStateGroup.States.Cast<VisualState>())
.FirstOrDefault(visualState => visualState.Name == name);
}
public static VisualTransition? FindVisualTransition(this FrameworkElement frameworkElement, string name)
{
Collection<VisualStateGroup> visualStateGroups = (Collection<VisualStateGroup>)VisualStateManager.GetVisualStateGroups(frameworkElement);
return visualStateGroups
?.SelectMany(visualStateGroup => visualStateGroup.Transitions.Cast<VisualTransition>())
.FirstOrDefault(visualTransition => visualTransition.To == name);
}
public static bool IsElementFullyVisibleInContainer(this FrameworkElement frameworkElement, UIElement element)
{
Rect panelRect = element.TransformToAncestor(frameworkElement)
.TransformBounds(new Rect(0.0, 0.0, element.DesiredSize.Width, element.DesiredSize.Height));
double roundedActualHeight = Math.Round(frameworkElement.ActualHeight, 2);
double roundedActualWidth = Math.Round(frameworkElement.ActualWidth, 2);
Rect containerRect = new(0.0, 0.0, roundedActualWidth, roundedActualHeight);
Point topLeftPointRounded = new(Math.Round(panelRect.TopLeft.X, 2), Math.Round(panelRect.TopLeft.Y, 2));
Point topRightPointRounded = new(Math.Round(panelRect.TopRight.X, 2), Math.Round(panelRect.TopRight.Y, 2));
Point bottomLeftPointRounded = new(Math.Round(panelRect.BottomLeft.X, 2), Math.Round(panelRect.BottomLeft.Y, 2));
Point bottomRightPointRounded = new(Math.Round(panelRect.BottomRight.X, 2), Math.Round(panelRect.BottomRight.Y, 2));
return containerRect.Contains(topLeftPointRounded) && containerRect.Contains(topRightPointRounded) &&
containerRect.Contains(bottomLeftPointRounded) && containerRect.Contains(bottomRightPointRounded);
}
}
+12
View File
@@ -0,0 +1,12 @@
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF
{
public static class MarkupExtensions
{
public static Binding ToBinding(this object value)
{
return value is Binding binding ? binding : new Binding() { Mode = BindingMode.OneWay, Source = value };
}
}
}
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Windows;
using System.Windows.Media.Animation;
namespace TheXamlGuy.UI.WPF;
public class AnimationHelper
{
public static ThicknessAnimation CreateAnimation(Thickness thickness = default, double milliseconds = 200)
{
return new(thickness, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
{
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
};
}
public static DoubleAnimation CreateAnimation(double toValue, double milliseconds = 200)
{
return new(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
{
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
};
}
}
+29
View File
@@ -0,0 +1,29 @@
using System.Windows;
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF
{
public static class PropertyPathHelper
{
private static readonly Dummy dummy = new();
public static object GetValue(object args, string path)
{
Binding binding = new(path)
{
Mode = BindingMode.OneTime,
Source = args
};
BindingOperations.SetBinding(dummy, Dummy.ValueProperty, binding);
return dummy.GetValue(Dummy.ValueProperty);
}
private class Dummy : DependencyObject
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(Dummy),
new UIPropertyMetadata(null));
}
}
}
+21
View File
@@ -0,0 +1,21 @@
using System.Windows;
namespace TheXamlGuy.UI.WPF;
public class BindingProxy : Freezable
{
public static readonly DependencyProperty DataContextProperty =
DependencyProperty.Register(nameof(DataContext),
typeof(object), typeof(BindingProxy));
public object DataContext
{
get => GetValue(DataContextProperty);
set => SetValue(DataContextProperty, value);
}
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
}
+71
View File
@@ -0,0 +1,71 @@
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Windows;
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF;
public class ChangePropertyExtension : TriggerExtension
{
private static readonly DependencyProperty PropertyTargetProperty =
DependencyProperty.RegisterAttached("PropertyTarget",
typeof(object), typeof(ChangePropertyExtension));
private static readonly DependencyProperty PropertyValueProperty =
DependencyProperty.RegisterAttached("PropertyValue",
typeof(object), typeof(ChangePropertyExtension));
private readonly string propertyName;
private readonly BindingBase targetBinding;
private readonly BindingBase valueBinding;
public ChangePropertyExtension(object target, string propertyName, object value)
{
this.targetBinding = target is BindingBase targetBinding ? targetBinding : target.ToBinding();
this.valueBinding = value is BindingBase valueBinding ? valueBinding : value.ToBinding();
this.propertyName = propertyName;
}
protected override void OnInvoked(object sender, EventArgs args)
{
BindingOperations.SetBinding(TargetObject, PropertyTargetProperty, targetBinding);
if (TargetObject?.GetValue(PropertyTargetProperty) is { } target)
{
Type? targetType = target.GetType();
if (targetType.GetProperty(propertyName) is PropertyInfo propertyInfo)
{
BindingOperations.SetBinding(TargetObject, PropertyValueProperty, valueBinding);
object? value = TargetObject?.GetValue(PropertyValueProperty);
TypeConverter? converter = TypeConverterHelper.GetTypeConverter(propertyInfo.PropertyType);
object? newValue = value;
if (value is not null)
{
if (converter?.CanConvertFrom(value.GetType()) == true)
{
newValue = converter.ConvertFrom(null, CultureInfo.InvariantCulture, value);
}
else
{
if (converter?.CanConvertTo(propertyInfo.PropertyType) == true)
{
newValue = converter.ConvertTo(null, CultureInfo.InvariantCulture, value, propertyInfo.PropertyType);
}
}
}
if (newValue is IEventParameter eventParameter)
{
newValue = eventParameter.GetParameters(args)[0];
}
propertyInfo?.SetValue(target, newValue, Array.Empty<object>());
}
}
}
}
+321
View File
@@ -0,0 +1,321 @@
using System.Windows.Markup;
namespace TheXamlGuy.UI.WPF;
[ContentProperty(nameof(Triggers))]
public class CompositeExtension : TriggerExtension
{
[ConstructorArgument(nameof(Triggers))]
public TriggerCollection Triggers { get; } = new TriggerCollection();
public CompositeExtension(object args0)
{
Triggers.Add(args0);
}
public CompositeExtension(object args0,
object args1)
{
Triggers.Add(args0);
Triggers.Add(args1);
}
public CompositeExtension(object args0,
object args1,
object args2)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
}
public CompositeExtension(object args0, object args1, object args2, object args3, object args4,
object args5, object args6, object args7, object args8, object args9, object args10)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
}
public CompositeExtension(object args0, object args1, object args2, object args3, object args4,
object args5, object args6, object args7, object args8, object args9, object args10, object args11)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
Triggers.Add(args12);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
Triggers.Add(args12);
Triggers.Add(args13);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
Triggers.Add(args12);
Triggers.Add(args13);
Triggers.Add(args14);
}
public CompositeExtension(object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14,
object args15)
{
Triggers.Add(args0);
Triggers.Add(args1);
Triggers.Add(args2);
Triggers.Add(args3);
Triggers.Add(args4);
Triggers.Add(args5);
Triggers.Add(args6);
Triggers.Add(args7);
Triggers.Add(args8);
Triggers.Add(args9);
Triggers.Add(args10);
Triggers.Add(args11);
Triggers.Add(args12);
Triggers.Add(args13);
Triggers.Add(args14);
Triggers.Add(args15);
}
protected override void OnInvoked(object sender, EventArgs args)
{
foreach (TriggerExtension? trigger in Triggers)
{
trigger.Invoke(sender, args);
}
base.OnInvoked(sender, args);
}
}
+238
View File
@@ -0,0 +1,238 @@
using System.Windows;
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF;
public class ConditionExtension : CompositeExtension
{
private readonly Binding conditionBinding;
private static readonly DependencyProperty ConditionProperty =
DependencyProperty.RegisterAttached("Condition",
typeof(bool), typeof(ConditionExtension));
public ConditionExtension(Binding conditionBinding,
object args0) : base(args0)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1) : base(args0, args1)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2) : base(args0, args1, args2)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3) : base(args0, args1, args2, args3)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4) : base(args0, args1, args2, args3, args4)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5) : base(args0, args1, args2, args3, args4, args5)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6) : base(args0, args1, args2, args3, args4, args5, args6)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7) : base(args0, args1, args2, args3, args4, args5, args6, args7)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8) : base(args0, args1, args2, args3, args4, args5, args6, args7, args8)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9) : base(args0, args1, args2, args3, args4, args5, args6, args7, args8, args9)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10) : base(args0, args1, args2, args3, args4, args5, args6, args7, args8, args9, args10)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11) : base(args0, args1, args2, args3, args4, args5, args6, args7, args8, args9, args10, args11)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12) : base(args0, args1, args2, args3, args4, args5, args6, args7, args8, args9, args10, args11, args12)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13) : base(args0, args1, args2, args3, args4, args5, args6, args7, args8, args9, args10, args11, args12, args13)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14) : base(args0, args1, args2, args3, args4, args5, args6, args7, args8, args9, args10, args11, args12, args13, args14)
{
this.conditionBinding = conditionBinding;
}
public ConditionExtension(Binding conditionBinding,
object args0,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14,
object args15) : base(args0, args1, args2, args3, args4, args5, args6, args7, args8, args9, args10, args11, args12, args13, args14, args15)
{
this.conditionBinding = conditionBinding;
}
protected override void OnInvoked(object sender, EventArgs args)
{
BindingOperations.SetBinding(TargetObject, ConditionProperty, conditionBinding);
if (TargetObject?.GetValue(ConditionProperty) is true)
{
base.OnInvoked(sender, args);
}
}
}
+70
View File
@@ -0,0 +1,70 @@
using System.Windows.Data;
using System.Windows.Markup;
namespace TheXamlGuy.UI.WPF;
public class EventParameterExtension : MarkupExtension, IEventParameter
{
private readonly IValueConverter? converter;
private readonly object? converterParameter;
private readonly string? key;
private readonly string? path;
public EventParameterExtension()
{
}
public EventParameterExtension(string key, string path)
{
this.key = key;
this.path = path;
}
public EventParameterExtension(string path)
{
this.path = path;
}
public EventParameterExtension(string path, IValueConverter converter)
{
this.path = path;
this.converter = converter;
}
public EventParameterExtension(string path, IValueConverter converter, object converterParameter)
{
this.path = path;
this.converter = converter;
this.converterParameter = converterParameter;
}
public List<object> GetParameters(EventArgs args)
{
List<object>? parameters = new();
dynamic? arguments = args.GetEventArguments(path, converter, converterParameter);
if (arguments is not null)
{
if (arguments is ICollection<object> collection)
{
foreach (object? argument in collection)
{
parameters.Add(key is not null ? new KeyValuePair<string, object>(key, (dynamic)argument) : argument);
}
}
else
{
parameters.Add(key is not null ? new KeyValuePair<string, object>(key, arguments) : arguments);
}
}
return parameters;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
+448
View File
@@ -0,0 +1,448 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace TheXamlGuy.UI.WPF;
public partial class InvokeExtension : TriggerExtension
{
private static readonly DependencyProperty ParameterProperty =
DependencyProperty.RegisterAttached("Parameter",
typeof(object), typeof(InvokeExtension));
private static readonly DependencyProperty TargetProperty =
DependencyProperty.RegisterAttached("Target",
typeof(object), typeof(InvokeExtension));
private readonly Dictionary<Type, Delegate?> dummyHandlers = new();
private readonly string name;
private readonly List<object> parameters = new();
private PropertyChangedRevoker? dataContextPropertyChangedRevoker;
public InvokeExtension(string name)
{
this.name = name;
}
public InvokeExtension(string name,
object args1)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
parameters.Add(args14 is MarkupExtension ? args14 : args14.ToBinding());
}
public InvokeExtension(string name,
object args1,
object args2,
object args3,
object args4,
object args5,
object args6,
object args7,
object args8,
object args9,
object args10,
object args11,
object args12,
object args13,
object args14,
object args15)
{
this.name = name;
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
parameters.Add(args14 is MarkupExtension ? args14 : args14.ToBinding());
parameters.Add(args15 is MarkupExtension ? args15 : args15.ToBinding());
}
public Binding? BindingTarget { get; set; }
protected override void OnInvoked(object sender, EventArgs args)
{
if (sender is DependencyObject dependencyObject)
{
if (!TryGetDataContext(dependencyObject, out object dataContext))
{
dataContextPropertyChangedRevoker = new PropertyChangedRevoker(dependencyObject, FrameworkElement.DataContextProperty, OnDataContextPropertyChangedChanged);
void OnDataContextPropertyChangedChanged(object sender, DependencyPropertyChangedEventArgs _)
{
if (TryGetDataContext(dependencyObject, out dataContext))
{
dataContextPropertyChangedRevoker?.Dispose();
CreaterHandler(dependencyObject, args, dataContext);
}
}
}
else
{
CreaterHandler(dependencyObject, args, dataContext);
}
}
}
private bool TryGetInvoke(DependencyObject sender, object dataContext, [AllowNull]out MethodInfo? methodInfo)
{
methodInfo = null;
if (dataContext.GetType().GetMethod(name, BindingFlags.Public | BindingFlags.Instance) is MethodInfo dataContextMethodInfo)
{
methodInfo = dataContextMethodInfo;
return true;
}
if (sender.GetType().GetMethod(name, BindingFlags.Public | BindingFlags.Instance) is MethodInfo senderMethodInfo)
{
methodInfo = senderMethodInfo;
return true;
}
return false;
}
private void CreaterHandler(DependencyObject sender, EventArgs args, object dataContext)
{
if (TryGetInvoke(sender, dataContext, out MethodInfo? methodInfo))
{
ParameterInfo[] parameterInfo = methodInfo!.GetParameters();
List<object> parameters = new();
foreach (object? parameter in this.parameters)
{
switch (parameter)
{
case IParameter keyedParameter:
BindingOperations.SetBinding(sender, ParameterProperty, parameter.ToBinding());
parameters.Add(new KeyValuePair<string, object>(keyedParameter.Key, (dynamic)sender.GetValue(ParameterProperty)));
break;
case IEventParameter eventParameter:
parameters.AddRange(eventParameter.GetParameters(args));
break;
default:
BindingOperations.SetBinding(sender, ParameterProperty, parameter.ToBinding());
parameters.Add((dynamic)sender.GetValue(ParameterProperty));
break;
}
}
if (methodInfo is { })
{
methodInfo.Invoke(dataContext, parameters.Any() ? parameters.ToArray() : parameterInfo.Length > 0 ? new object?[] { null } : Array.Empty<object>());
}
}
}
private bool TryGetDataContext(DependencyObject sender, out object dataContext)
{
if (BindingTarget is not null)
{
BindingOperations.SetBinding(sender, TargetProperty, BindingTarget);
dataContext = sender.GetValue(TargetProperty);
}
else
{
dataContext = sender.GetValue(FrameworkElement.DataContextProperty) ?? sender.GetValue(FrameworkContentElement.DataContextProperty);
}
return dataContext is not null;
}
}
+18
View File
@@ -0,0 +1,18 @@
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF;
public class ParameterExtension : Binding, IParameter
{
public ParameterExtension(string key)
{
Key = key;
}
public ParameterExtension(string key, string path) : base(path)
{
Key = key;
}
public string? Key { get; }
}
+38
View File
@@ -0,0 +1,38 @@
using System.Windows;
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF;
public class StateExtension : TriggerExtension
{
private static readonly DependencyProperty PropertyTargetProperty =
DependencyProperty.RegisterAttached("PropertyTarget",
typeof(object), typeof(StateExtension));
private static readonly DependencyProperty StateProperty =
DependencyProperty.RegisterAttached("State",
typeof(object), typeof(StateExtension));
private readonly BindingBase targetBinding;
private readonly BindingBase stateBinding;
public StateExtension(object target, object state)
{
this.targetBinding = target is BindingBase targetBinding ? targetBinding : target.ToBinding();
this.stateBinding = state is BindingBase stateBinding ? stateBinding : state.ToBinding();
}
protected override void OnInvoked(object sender, EventArgs args)
{
BindingOperations.SetBinding(TargetObject, PropertyTargetProperty, targetBinding);
if (TargetObject?.GetValue(PropertyTargetProperty) is FrameworkElement target)
{
BindingOperations.SetBinding(target, StateProperty, stateBinding);
object? state = target.GetValue(StateProperty);
target.ApplyTemplate();
VisualStateManager.GoToElementState(target, (string)state, true);
}
}
}
+15
View File
@@ -0,0 +1,15 @@
using System.Collections.ObjectModel;
using TheXamlGuy.UI.WPF;
namespace TheXamlGuy.UI.WPF;
public class TriggerCollection : Collection<TriggerExtension>
{
public void Add(object item)
{
if (item is TriggerExtension trigger)
{
base.Add(trigger);
}
}
}
+71
View File
@@ -0,0 +1,71 @@
using System.Reflection;
using System.Windows.Markup;
using System.Windows;
using System.Xaml;
namespace TheXamlGuy.UI.WPF;
[MarkupExtensionReturnType(typeof(Delegate))]
public class TriggerExtension : MarkupExtension
{
public DependencyObject? TargetObject { get; protected set; }
protected object? TargetInvoke { get; private set; }
public void Invoke(object sender, EventArgs args)
{
OnInvoked(sender, args);
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget target)
{
if (TargetObject is null)
{
if (target.TargetObject is DependencyObject dependencyObject)
{
TargetObject = dependencyObject;
}
else if (serviceProvider.GetService(typeof(IRootObjectProvider)) is IRootObjectProvider root)
{
TargetObject = (DependencyObject)root.RootObject;
}
}
TargetInvoke = target.TargetProperty;
MethodInfo invokeMethod = GetType().GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public)!;
if (invokeMethod is null)
{
return this;
}
switch (TargetInvoke)
{
case EventInfo info:
return Delegate.CreateDelegate(info.EventHandlerType!, this, invokeMethod);
case MethodInfo methodInfo:
{
if (methodInfo.GetParameters() is ParameterInfo[] methodParameters && methodParameters is { Length: 2 })
{
return Delegate.CreateDelegate(methodParameters[1].ParameterType, this, invokeMethod);
}
break;
}
default:
break;
}
}
return this;
}
protected virtual void OnInvoked(object sender, EventArgs args)
{
}
}
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
using System.Windows.Media.Effects;
using System.Windows;
using System;
namespace TheXamlGuy.UI.WPF;
public class BrightnessEffect : EffectBase
{
public static readonly DependencyProperty BrightnessProperty =
DependencyProperty.Register(nameof(Brightness), typeof(double),
typeof(BrightnessEffect), new PropertyMetadata(ValueBoxes.Double1Box, PixelShaderConstantCallback(0)));
private static readonly PixelShader Shader;
static BrightnessEffect()
{
Shader = new PixelShader
{
UriSource = new Uri("pack://application:,,,/TheXamlGuy.UI.WPF;component/Resources/Effects/BrightnessEffect.ps")
};
}
public BrightnessEffect()
{
PixelShader = Shader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(BrightnessProperty);
}
public double Brightness
{
get => (double)GetValue(BrightnessProperty);
set => SetValue(BrightnessProperty, value);
}
}
@@ -0,0 +1,24 @@
using System;
using System.Windows.Media.Effects;
namespace TheXamlGuy.UI.WPF;
public class ColorComplementEffect : EffectBase
{
private static readonly PixelShader Shader;
static ColorComplementEffect()
{
Shader = new PixelShader
{
UriSource = new Uri("pack://application:,,,/TheXamlGuy.UI.WPF;component/Resources/Effects/ColorComplementEffect.ps")
};
}
public ColorComplementEffect()
{
PixelShader = Shader;
UpdateShaderValue(InputProperty);
}
}
+243
View File
@@ -0,0 +1,243 @@
using System.Windows.Media.Effects;
using System.Windows;
using System;
namespace TheXamlGuy.UI.WPF;
public class ColorMatrixEffect : EffectBase
{
public static readonly DependencyProperty M11Property =
DependencyProperty.Register("M11",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double1Box, PixelShaderConstantCallback(0)));
public static readonly DependencyProperty M12Property =
DependencyProperty.Register("M12",
typeof(double),typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(5)));
public static readonly DependencyProperty M13Property =
DependencyProperty.Register("M13",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(10)));
public static readonly DependencyProperty M14Property =
DependencyProperty.Register("M14",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(15)));
public static readonly DependencyProperty M21Property =
DependencyProperty.Register("M21",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(1)));
public static readonly DependencyProperty M22Property =
DependencyProperty.Register("M22",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double1Box, PixelShaderConstantCallback(6)));
public static readonly DependencyProperty M23Property =
DependencyProperty.Register("M23",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(11)));
public static readonly DependencyProperty M24Property =
DependencyProperty.Register("M24",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(16)));
public static readonly DependencyProperty M31Property =
DependencyProperty.Register("M31",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(2)));
public static readonly DependencyProperty M32Property =
DependencyProperty.Register("M32",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(7)));
public static readonly DependencyProperty M33Property =
DependencyProperty.Register("M33",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double1Box, PixelShaderConstantCallback(12)));
public static readonly DependencyProperty M34Property =
DependencyProperty.Register("M34",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(17)));
public static readonly DependencyProperty M41Property =
DependencyProperty.Register("M41",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(3)));
public static readonly DependencyProperty M42Property =
DependencyProperty.Register("M42",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(8)));
public static readonly DependencyProperty M43Property =
DependencyProperty.Register("M43",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(13)));
public static readonly DependencyProperty M44Property =
DependencyProperty.Register("M44",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double1Box, PixelShaderConstantCallback(18)));
public static readonly DependencyProperty M51Property =
DependencyProperty.Register("M51",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(4)));
public static readonly DependencyProperty M52Property =
DependencyProperty.Register("M52",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(9)));
public static readonly DependencyProperty M53Property =
DependencyProperty.Register("M53",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(14)));
public static readonly DependencyProperty M54Property =
DependencyProperty.Register("M54",
typeof(double), typeof(ColorMatrixEffect), new PropertyMetadata(ValueBoxes.Double0Box, PixelShaderConstantCallback(19)));
private static readonly PixelShader Shader;
static ColorMatrixEffect()
{
Shader = new PixelShader
{
UriSource = new Uri("pack://application:,,,/TheXamlGuy.UI.WPF;component/Resources/Effects/ColorMatrixEffect.ps")
};
}
public ColorMatrixEffect()
{
PixelShader = Shader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(M11Property);
UpdateShaderValue(M21Property);
UpdateShaderValue(M31Property);
UpdateShaderValue(M41Property);
UpdateShaderValue(M51Property);
UpdateShaderValue(M12Property);
UpdateShaderValue(M22Property);
UpdateShaderValue(M32Property);
UpdateShaderValue(M42Property);
UpdateShaderValue(M52Property);
UpdateShaderValue(M13Property);
UpdateShaderValue(M23Property);
UpdateShaderValue(M33Property);
UpdateShaderValue(M43Property);
UpdateShaderValue(M53Property);
UpdateShaderValue(M14Property);
UpdateShaderValue(M24Property);
UpdateShaderValue(M34Property);
UpdateShaderValue(M44Property);
UpdateShaderValue(M54Property);
}
public double M11
{
get => (double)GetValue(M11Property);
set => SetValue(M11Property, value);
}
public double M12
{
get => (double)GetValue(M12Property);
set => SetValue(M12Property, value);
}
public double M13
{
get => (double)GetValue(M13Property);
set => SetValue(M13Property, value);
}
public double M14
{
get => (double)GetValue(M14Property);
set => SetValue(M14Property, value);
}
public double M21
{
get => (double)GetValue(M21Property);
set => SetValue(M21Property, value);
}
public double M22
{
get => (double)GetValue(M22Property);
set => SetValue(M22Property, value);
}
public double M23
{
get => (double)GetValue(M23Property);
set => SetValue(M23Property, value);
}
public double M24
{
get => (double)GetValue(M24Property);
set => SetValue(M24Property, value);
}
public double M31
{
get => (double)GetValue(M31Property);
set => SetValue(M31Property, value);
}
public double M32
{
get => (double)GetValue(M32Property);
set => SetValue(M32Property, value);
}
public double M33
{
get => (double)GetValue(M33Property);
set => SetValue(M33Property, value);
}
public double M34
{
get => (double)GetValue(M34Property);
set => SetValue(M34Property, value);
}
public double M41
{
get => (double)GetValue(M41Property);
set => SetValue(M41Property, value);
}
public double M42
{
get => (double)GetValue(M42Property);
set => SetValue(M42Property, value);
}
public double M43
{
get => (double)GetValue(M43Property);
set => SetValue(M43Property, value);
}
public double M44
{
get => (double)GetValue(M44Property);
set => SetValue(M44Property, value);
}
public double M51
{
get => (double)GetValue(M51Property);
set => SetValue(M51Property, value);
}
public double M52
{
get => (double)GetValue(M52Property);
set => SetValue(M52Property, value);
}
public double M53
{
get => (double)GetValue(M53Property);
set => SetValue(M53Property, value);
}
public double M54
{
get => (double)GetValue(M54Property);
set => SetValue(M54Property, value);
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Windows;
using System.Windows.Media.Effects;
namespace TheXamlGuy.UI.WPF;
public class ContrastEffect : EffectBase
{
public static readonly DependencyProperty ContrastProperty =
DependencyProperty.Register("Contrast",
typeof(double), typeof(ContrastEffect), new PropertyMetadata(ValueBoxes.Double1Box, PixelShaderConstantCallback(0)));
private static readonly PixelShader Shader;
static ContrastEffect()
{
Shader = new PixelShader
{
UriSource = new Uri("pack://application:,,,/TheXamlGuy.UI.WPF;component/Resources/Effects/ContrastEffect.ps")
};
}
public ContrastEffect()
{
PixelShader = Shader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(ContrastProperty);
}
public double Contrast
{
get => (double) GetValue(ContrastProperty);
set => SetValue(ContrastProperty, value);
}
}
+16
View File
@@ -0,0 +1,16 @@
using System.Windows.Media.Effects;
using System.Windows.Media;
using System.Windows;
namespace TheXamlGuy.UI.WPF;
public abstract class EffectBase : ShaderEffect
{
public static readonly DependencyProperty InputProperty = RegisterPixelShaderSamplerProperty("Input", typeof(EffectBase), 0);
public Brush Input
{
get => (Brush)GetValue(InputProperty);
set => SetValue(InputProperty, value);
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Windows;
using System.Windows.Media.Effects;
namespace TheXamlGuy.UI.WPF;
public class GrayScaleEffect : EffectBase
{
public static readonly DependencyProperty ScaleProperty =
DependencyProperty.Register("Scale",
typeof(double), typeof(GrayScaleEffect), new PropertyMetadata(ValueBoxes.Double1Box, PixelShaderConstantCallback(0)));
private static readonly PixelShader Shader;
static GrayScaleEffect()
{
Shader = new PixelShader
{
UriSource = new Uri("pack://application:,,,/TheXamlGuy.UI.WPF;component/Resources/Effects/GrayScaleEffect.ps")
};
}
public GrayScaleEffect()
{
PixelShader = Shader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(ScaleProperty);
}
public double Scale
{
get => (double) GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
+7
View File
@@ -0,0 +1,7 @@
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Markup;
[assembly: InternalsVisibleTo("TheXamlGuy.UI.WPF.Controls")]
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "TheXamlGuy.UI.WPF")]
+86
View File
@@ -0,0 +1,86 @@
using System;
using System.Windows;
using System.Windows.Data;
namespace TheXamlGuy.UI.WPF
{
public class PropertyChangedRevoker : DependencyObject, IDisposable
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(object),
typeof(PropertyChangedRevoker), new PropertyMetadata(null, OnValuePropertyChanged));
private readonly WeakReference weakPropertySource;
public PropertyChangedRevoker(DependencyObject propertySource, string path,
DependencyPropertyChangedEventHandler valueChangedHandler) : this(propertySource, new PropertyPath(path),
valueChangedHandler)
{
}
public PropertyChangedRevoker(DependencyObject propertySource, DependencyProperty property,
DependencyPropertyChangedEventHandler valueChangedHandler) : this(propertySource,
new PropertyPath(property), valueChangedHandler)
{
}
public PropertyChangedRevoker(DependencyObject propertySource, PropertyPath property,
DependencyPropertyChangedEventHandler valueChangedHandler)
{
weakPropertySource = new WeakReference(propertySource);
Binding binding = new Binding
{
Path = property,
Mode = BindingMode.OneWay,
Source = propertySource
};
BindingOperations.SetBinding(this, ValueProperty, binding);
ValueChanged = valueChangedHandler;
Property = property;
}
public DependencyObject PropertySource
{
get
{
try
{
return weakPropertySource.IsAlive ? weakPropertySource.Target as DependencyObject : null;
}
catch
{
return null;
}
}
}
public object Value
{
get => GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public PropertyPath Property { get; }
public void Dispose()
{
BindingOperations.ClearBinding(this, ValueProperty);
}
private event DependencyPropertyChangedEventHandler ValueChanged;
private static void OnValuePropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs args)
{
PropertyChangedRevoker sender = dependencyObject as PropertyChangedRevoker;
sender?.OnValuePropertyChanged(args);
}
private void OnValuePropertyChanged(DependencyPropertyChangedEventArgs args)
{
ValueChanged?.Invoke(this, args);
}
}
}
@@ -0,0 +1,10 @@
sampler2D implicitInput : register(s0);
float brightness : register(C0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
color.rgba *= brightness;
return color;
}
Binary file not shown.
@@ -0,0 +1,12 @@
sampler2D implicitInput : register(s0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
float4 complement;
complement.rgb = color.a - color.rgb;
complement.a = color.a;
return complement;
}
Binary file not shown.
@@ -0,0 +1,51 @@
sampler2D implicitInput : register(s0);
float a : register(c0);
float b : register(c1);
float c : register(c2);
float d : register(c3);
float e : register(c4);
float f : register(c5);
float g : register(c6);
float h : register(c7);
float i : register(c8);
float j : register(c9);
float k : register(c10);
float l : register(c11);
float m : register(c12);
float n : register(c13);
float o : register(c14);
float p : register(c15);
float q : register(c16);
float r : register(c17);
float s : register(c18);
float t : register(c19);
/*
-- -- -- --
| a b c d e | | R |
| f g h i j | | G |
A = | k l m n o | C = | B | R = AC
| p q r s t | | A |
-- -- | 1 |
-- --
*/
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
float4x4 matrixA1 =
{
a, b, c, d,
f, g, h, i,
k, l, m, n,
p, q, r, s
};
return mul(matrixA1, color) + float4( e, j, o, t );
}
Binary file not shown.
@@ -0,0 +1,12 @@
sampler2D implicitInput : register(s0);
float contrast : register(C0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
float value = (1 - contrast) / 2;
color.rgba = color.rgba * contrast + value;
return color;
}
Binary file not shown.
@@ -0,0 +1,15 @@
sampler2D implicitInput : register(s0);
float scale : register(c0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
float4 complement;
float intensity = (color.r + color.g + color.b) / 3;
complement.rgb = color.rgb * (1 - scale) + intensity * scale;
complement.a = color.a;
return complement;
}
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
using System.Collections.ObjectModel;
using System.Windows;
namespace TheXamlGuy.UI.WPF;
public class SetterBaseCollection : ObservableCollection<SetterBase>
{
}
+22
View File
@@ -0,0 +1,22 @@
using System.Windows;
namespace TheXamlGuy.UI.WPF;
public class StateTrigger : StateTriggerBase
{
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.Register(nameof(IsActive),
typeof(bool), typeof(StateTrigger),
new PropertyMetadata(false, OnIsActivePropertyChanged));
public bool IsActive
{
get => (bool)GetValue(IsActiveProperty);
set => SetValue(IsActiveProperty, value);
}
private static void OnIsActivePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as StateTrigger)?.SetActive((bool)args.NewValue);
}
}
+16
View File
@@ -0,0 +1,16 @@
using System.Windows;
namespace TheXamlGuy.UI.WPF;
public abstract class StateTriggerBase : DependencyObject
{
internal bool IsTriggerActive { get; private set; }
internal VisualStateExtension? Owner { get; set; }
protected void SetActive(bool isActive)
{
IsTriggerActive = isActive;
Owner?.SetActive(IsTriggerActive);
}
}
+20
View File
@@ -0,0 +1,20 @@
using System;
using System.Windows;
namespace TheXamlGuy.UI.WPF;
public static class TemplateGenerator
{
public static DataTemplate CreateDataTemplate(Func<object> factory)
{
FrameworkElementFactory frameworkElementFactory = new(typeof(TemplateGeneratorControl));
frameworkElementFactory.SetValue(TemplateGeneratorControl.FactoryProperty, factory);
DataTemplate dataTemplate = new(typeof(DependencyObject))
{
VisualTree = frameworkElementFactory
};
return dataTemplate;
}
}
+21
View File
@@ -0,0 +1,21 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace TheXamlGuy.UI.WPF;
public class TemplateGeneratorControl : ContentControl
{
internal static readonly DependencyProperty FactoryProperty =
DependencyProperty.Register("Factory", typeof(Func<object>),
typeof(TemplateGeneratorControl), new PropertyMetadata(null, OnFactoryPropertyChanged));
private static void OnFactoryPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
if (dependencyObject is TemplateGeneratorControl sender && args.NewValue is not null)
{
Func<object> factory = (Func<object>)args.NewValue;
sender.Content = factory();
}
}
}
+214
View File
@@ -0,0 +1,214 @@
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media.Animation;
namespace TheXamlGuy.UI.WPF;
public class VisualStateExtension : VisualState, ISupportInitialize
{
public static readonly DependencyProperty IsStateTriggersAttachedProperty =
DependencyProperty.RegisterAttached("IsStateTriggersAttached",
typeof(bool), typeof(VisualStateExtension),
new PropertyMetadata(false, EnableStateTriggersChanged));
private readonly ObservableCollection<StateTriggerBase> stateTriggers = new();
private Action? afterInit;
private FrameworkElement? element;
private bool isInitializing;
private bool lastState;
private bool storyboardStateInvalidated;
public VisualStateExtension()
{
Name = Guid.NewGuid().ToString();
Setters = new SetterBaseCollection();
Setters.CollectionChanged += OnSettersCollectionChanged;
stateTriggers.CollectionChanged += OnTriggersCollectionChanged;
}
public SetterBaseCollection Setters { get; }
public IList StateTriggers => stateTriggers;
internal FrameworkElement? Element
{
get => element;
set
{
element = value;
if (element is not null)
{
if (element.IsLoaded)
{
UpdateActiveState();
}
else
{
element.Loaded += OnElementLoaded;
}
}
}
}
public static bool GetIsStateTriggersAttached(FrameworkElement element)
{
return (bool)element.GetValue(IsStateTriggersAttachedProperty);
}
public static void SetIsStateTriggersAttached(FrameworkElement element, bool value)
{
element.SetValue(IsStateTriggersAttachedProperty, value);
}
void ISupportInitialize.BeginInit()
{
isInitializing = true;
}
void ISupportInitialize.EndInit()
{
isInitializing = false;
afterInit?.Invoke();
}
internal void SetActive(bool active)
{
if (Element is null)
{
return;
}
if (isInitializing)
{
afterInit = () => SetActive(active);
return;
}
if (Storyboard is not null && lastState != active)
{
if (active)
{
ClockState state = storyboardStateInvalidated ? Storyboard.GetCurrentState(Element) : ClockState.Stopped;
if (state == ClockState.Stopped)
{
Storyboard.Begin(Element, true);
}
}
else
{
if (storyboardStateInvalidated)
{
Storyboard.Stop(Element);
}
}
}
if (active)
{
foreach (Setter setter in Setters.OfType<Setter>())
{
DependencyProperty property = setter.Property;
object value = setter.Value;
string targetName = setter.TargetName;
DependencyObject? target = Element.FindName(targetName) as DependencyObject;
if (setter.Value is BindingBase binding)
{
BindingOperations.SetBinding(target, property, value as BindingBase);
}
else
{
target?.SetValue(property, value);
}
}
}
lastState = active;
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs args)
{
base.OnPropertyChanged(args);
if (args.Property.Name == nameof(Storyboard))
{
if (args.OldValue is Storyboard oldStoryBoard)
{
oldStoryBoard.CurrentStateInvalidated -= OnStoryboardCurrentStateInvalidated;
}
if (args.NewValue is Storyboard newStoryBoard)
{
newStoryBoard.CurrentStateInvalidated += OnStoryboardCurrentStateInvalidated;
}
storyboardStateInvalidated = false;
}
}
private static void EnableStateTriggersChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
if (dependencyObject is FrameworkElement element)
{
if ((bool)args.NewValue)
{
VisualStateManagerContext.Set(element);
}
else
{
VisualStateManagerContext.UnSet(element);
}
}
}
private void OnElementLoaded(object sender, RoutedEventArgs args)
{
((FrameworkElement)sender).Loaded -= OnElementLoaded;
UpdateActiveState();
}
private void OnSettersCollectionChanged(object? sender, EventArgs args)
{
UpdateActiveState();
}
private void OnStoryboardCurrentStateInvalidated(object? sender, EventArgs args)
{
storyboardStateInvalidated = true;
}
private void OnTriggersCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
if (args.NewItems is not null)
{
foreach (StateTriggerBase item in args.NewItems.OfType<StateTriggerBase>())
{
item.Owner = this;
}
}
if (args.OldItems is not null)
{
foreach (StateTriggerBase? item in args.OldItems.OfType<StateTriggerBase>().Where(item => item.Owner == this))
{
item.Owner = null;
}
}
UpdateActiveState();
}
private void UpdateActiveState()
{
SetActive(stateTriggers.Any(t => t.IsTriggerActive));
}
}
+85
View File
@@ -0,0 +1,85 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
namespace TheXamlGuy.UI.WPF;
internal class VisualStateManagerContext : IDisposable
{
private static readonly DependencyProperty VisualStateManagerContextProperty =
DependencyProperty.RegisterAttached("VisualStateManagerContext",
typeof(VisualStateManagerContext), typeof(VisualStateManagerContext));
private VisualStateManagerContext(FrameworkElement element)
{
Element = element;
VisualStateGroups = (ObservableCollection<VisualStateGroup>) element.GetValue(VisualStateManager.VisualStateGroupsProperty);
VisualStateGroups.CollectionChanged += CollectionChanged;
ApplyElement(VisualStateGroups);
}
private FrameworkElement? Element { get; set; }
private ObservableCollection<VisualStateGroup>? VisualStateGroups { get; set; }
public void Dispose()
{
RemoveElement(VisualStateGroups);
if (VisualStateGroups is not null)
{
VisualStateGroups.CollectionChanged -= CollectionChanged;
}
Element = null;
VisualStateGroups = null;
}
internal static void Set(FrameworkElement element)
{
VisualStateManagerContext hook = new(element);
element.SetValue(VisualStateManagerContextProperty, hook);
}
internal static void UnSet(FrameworkElement element)
{
if (element.GetValue(VisualStateManagerContextProperty) is VisualStateManagerContext hook)
{
element.SetValue(VisualStateManagerContextProperty, null);
hook.Dispose();
}
}
private void CollectionChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
if (args.NewItems != null)
{
ApplyElement(args.NewItems.OfType<VisualStateGroup>());
}
if (args.OldItems != null)
{
RemoveElement(args.OldItems.OfType<VisualStateGroup>());
}
}
private void ApplyElement(IEnumerable<VisualStateGroup> visualStateGroups)
{
foreach (VisualStateExtension? state in visualStateGroups.SelectMany(group => group.States.OfType<VisualStateExtension>()))
{
state.Element = Element;
}
}
private void RemoveElement(IEnumerable<VisualStateGroup>? visualStateGroups)
{
if (visualStateGroups is not null)
{
foreach (VisualStateExtension? state in visualStateGroups.SelectMany(group => group.States.OfType<VisualStateExtension>()))
{
state.Element = null;
}
}
}
}
+37
View File
@@ -0,0 +1,37 @@
<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</RootNamespace>
<AssemblyName>TheXamlGuy.UI.WPF</AssemblyName>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\Effects\BrightnessEffect.fx" />
<None Remove="Resources\Effects\BrightnessEffect.ps" />
<None Remove="Resources\Effects\ColorComplementEffect.fx" />
<None Remove="Resources\Effects\ColorComplementEffect.ps" />
<None Remove="Resources\Effects\ColorMatrixEffect.fx" />
<None Remove="Resources\Effects\ColorMatrixEffect.ps" />
<None Remove="Resources\Effects\ContrastEffect.fx" />
<None Remove="Resources\Effects\ContrastEffect.ps" />
<None Remove="Resources\Effects\GrayScaleEffect.fx" />
<None Remove="Resources\Effects\GrayScaleEffect.ps" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UI\UI.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Effects\BrightnessEffect.fx" />
<Resource Include="Resources\Effects\BrightnessEffect.ps" />
<Resource Include="Resources\Effects\ColorComplementEffect.fx" />
<Resource Include="Resources\Effects\ColorComplementEffect.ps" />
<Resource Include="Resources\Effects\ColorMatrixEffect.fx" />
<Resource Include="Resources\Effects\ColorMatrixEffect.ps" />
<Resource Include="Resources\Effects\ContrastEffect.fx" />
<Resource Include="Resources\Effects\ContrastEffect.ps" />
<Resource Include="Resources\Effects\GrayScaleEffect.fx" />
<Resource Include="Resources\Effects\GrayScaleEffect.ps" />
</ItemGroup>
</Project>