diff --git a/Toolkit.Foundation/AsyncHandlerInitialization.cs b/Toolkit.Foundation/AsyncHandlerInitialization.cs index 0163a19..0b1f2e6 100644 --- a/Toolkit.Foundation/AsyncHandlerInitialization.cs +++ b/Toolkit.Foundation/AsyncHandlerInitialization.cs @@ -37,6 +37,7 @@ public class AsyncHandlerInitialization(IServiceProvider pro foreach (IAsyncHandler handler in provider.GetServices>()) { handler.Handle(args.Message, args.CancellationToken); + args.Reply(Unit.Value); } }); } diff --git a/Toolkit.Foundation/IMessengerExtensions.cs b/Toolkit.Foundation/IMessengerExtensions.cs index 3fe4363..f1d8554 100644 --- a/Toolkit.Foundation/IMessengerExtensions.cs +++ b/Toolkit.Foundation/IMessengerExtensions.cs @@ -34,4 +34,12 @@ public static class IMessengerExtensions public static async Task SendAsync(this IMessenger messenger, TMessage message) where TMessage : class => await messenger.Send(new AsyncResponseEventArgs { Message = message }); + + public static async Task SendAsync(this IMessenger messenger) + where TMessage : class, new() => + await messenger.Send(new AsyncResponseEventArgs { Message = new TMessage() }); + + public static async Task SendAsync(this IMessenger messenger, + TMessage message) where TMessage : class => + await messenger.Send(new AsyncResponseEventArgs { Message = message }); } diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index 6fc9487..9173065 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -43,12 +43,12 @@ public static class IServiceCollectionExtensions if (key is { Length: > 0 }) { services.Add(new ServiceDescriptor(typeof(IAsyncHandler), key, typeof(THandler), lifetime)); - services.AddInitialization>>(key); + services.AddInitialization>>(key); } else { services.Add(new ServiceDescriptor(typeof(IAsyncHandler), typeof(THandler), lifetime)); - services.AddInitialization>>(); + services.AddInitialization>>(); } return services; diff --git a/Toolkit.Foundation/Observable.cs b/Toolkit.Foundation/Observable.cs index 957582d..b7e6846 100644 --- a/Toolkit.Foundation/Observable.cs +++ b/Toolkit.Foundation/Observable.cs @@ -67,13 +67,15 @@ public partial class Observable(IServiceProvider provider, protected override sealed void OnActivated() { - base.OnActivated(); + Messenger.RegisterAll(this); Activated(); } protected override sealed void OnDeactivated() { - base.OnDeactivated(); + Messenger.UnregisterAll(this); + Dispose(); + Deactivated(); } } diff --git a/Toolkit.Foundation/ObservableCollection.cs b/Toolkit.Foundation/ObservableCollection.cs index e475c80..8de841d 100644 --- a/Toolkit.Foundation/ObservableCollection.cs +++ b/Toolkit.Foundation/ObservableCollection.cs @@ -1,10 +1,8 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.DependencyInjection; -using System; using System.Collections; using System.Collections.Specialized; -using System.Diagnostics.CodeAnalysis; using System.Reactive.Disposables; namespace Toolkit.Foundation; @@ -38,8 +36,6 @@ public abstract partial class ObservableCollection : private readonly Dictionary trackedProperties = []; - private bool cleaning; - [ObservableProperty] private int count; @@ -173,7 +169,6 @@ public abstract partial class ObservableCollection : public void Clear(bool disposeItems = false) { - cleaning = true; if (disposeItems) { foreach (TViewModel item in this.ToList()) @@ -184,12 +179,10 @@ public abstract partial class ObservableCollection : } ClearItems(); - cleaning = false; } public void Clear() { - cleaning = true; foreach (TViewModel item in this.ToList()) { Disposer.Dispose(item); @@ -197,7 +190,6 @@ public abstract partial class ObservableCollection : } ClearItems(); - cleaning = false; } public void Commit() @@ -222,8 +214,8 @@ public abstract partial class ObservableCollection : public virtual void Dispose() { - GC.SuppressFinalize(this); Disposer.Dispose(this); + GC.SuppressFinalize(this); } public IEnumerator GetEnumerator() => @@ -471,7 +463,7 @@ public abstract partial class ObservableCollection : Disposer.Add(this, item); Disposer.Add(item, Disposable.Create(() => { - if (item is IDisposable && !cleaning) + if (item is IDisposable) { if (item is IList collection) { @@ -487,13 +479,15 @@ public abstract partial class ObservableCollection : protected override sealed void OnActivated() { - base.OnActivated(); + Messenger.RegisterAll(this); Activated(); } protected override sealed void OnDeactivated() { - base.OnDeactivated(); + Messenger.UnregisterAll(this); + Dispose(); + Deactivated(); } diff --git a/Toolkit.Foundation/Update.cs b/Toolkit.Foundation/Update.cs index 5703a48..06ce426 100644 --- a/Toolkit.Foundation/Update.cs +++ b/Toolkit.Foundation/Update.cs @@ -7,4 +7,4 @@ public record Update public static UpdateEventArgs As() where TValue : new() => new(new TValue()); -} \ No newline at end of file +} diff --git a/Toolkit.Foundation/UpdateEventArgs.cs b/Toolkit.Foundation/UpdateEventArgs.cs index 4f8f247..3b2e201 100644 --- a/Toolkit.Foundation/UpdateEventArgs.cs +++ b/Toolkit.Foundation/UpdateEventArgs.cs @@ -13,4 +13,4 @@ public record UpdateEventArgs { } -} \ No newline at end of file +} diff --git a/Toolkit.Foundation/Updated.cs b/Toolkit.Foundation/Updated.cs new file mode 100644 index 0000000..00dc3dc --- /dev/null +++ b/Toolkit.Foundation/Updated.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public record Updated +{ + public static UpdatedEventArgs As(TValue value) => + new(value); + + public static UpdatedEventArgs As() where TValue : new() => + new(new TValue()); +} \ No newline at end of file diff --git a/Toolkit.Foundation/UpdatedEventArgs.cs b/Toolkit.Foundation/UpdatedEventArgs.cs new file mode 100644 index 0000000..db2b1f9 --- /dev/null +++ b/Toolkit.Foundation/UpdatedEventArgs.cs @@ -0,0 +1,16 @@ +namespace Toolkit.Foundation; + +public record UpdatedEventArgs +{ + public TSender? Sender { get; } + + public UpdatedEventArgs(TSender sender) + { + Sender = sender; + } + + public UpdatedEventArgs() + { + + } +} \ No newline at end of file diff --git a/Toolkit.UI.WinUI/ContentTemplateBinding.cs b/Toolkit.UI.WinUI/ContentTemplateBinding.cs index 0a59d58..20a0195 100644 --- a/Toolkit.UI.WinUI/ContentTemplateBinding.cs +++ b/Toolkit.UI.WinUI/ContentTemplateBinding.cs @@ -23,21 +23,33 @@ public static class ContentTemplateBinding { if (dependencyObject is FrameworkElement content) { + IActivation? cachedActivation = null; + void HandleLoaded(object sender, RoutedEventArgs args) { - if (content.DataContext is IActivation activation) + if (sender is FrameworkElement content) { content.Loaded -= HandleLoaded; - activation.IsActive = true; + + if (content.DataContext is IActivation activation) + { + cachedActivation = activation; + activation.IsActive = true; + } } } void HandleUnloaded(object sender, RoutedEventArgs args) { - if (content.DataContext is IActivation activation) + if (cachedActivation is not null) { + cachedActivation.IsActive = false; + } + + if (sender is FrameworkElement content) + { + cachedActivation = null; content.Unloaded -= HandleUnloaded; - activation.IsActive = false; } } @@ -45,7 +57,6 @@ public static class ContentTemplateBinding { content.Loaded += HandleLoaded; content.Unloaded += HandleUnloaded; - } else { diff --git a/Toolkit.UI.WinUI/StringFormatConverter.cs b/Toolkit.UI.WinUI/StringFormatConverter.cs new file mode 100644 index 0000000..07e6a6f --- /dev/null +++ b/Toolkit.UI.WinUI/StringFormatConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Globalization; + +namespace Toolkit.UI.WinUI; + +public class StringFormatConverter : + ValueConverter +{ + public string? StringFormat { get; set; } + + protected override object? ConvertTo(object value, + Type? targetType, + object? parameter, + string? language) + { + if (value is null) + { + return null!; + } + + if (string.IsNullOrEmpty(StringFormat)) + { + return value.ToString()!; + } + + try + { + CultureInfo culture = string.IsNullOrWhiteSpace(language) ? CultureInfo.InvariantCulture : new CultureInfo(language); + return string.Format(culture, StringFormat, value); + } + catch + { + return value; + } + } +} diff --git a/Toolkit.UI.WinUI/ValueConverter.cs b/Toolkit.UI.WinUI/ValueConverter.cs new file mode 100644 index 0000000..fe26eee --- /dev/null +++ b/Toolkit.UI.WinUI/ValueConverter.cs @@ -0,0 +1,40 @@ +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Markup; +using System; + +namespace Toolkit.UI.WinUI; + +public abstract class ValueConverter : + MarkupExtension, + IValueConverter +{ + public object? Convert(object value, + Type targetType, + object parameter, + string language) => + ConvertTo((TSource)value, targetType, parameter, language); + + public object? ConvertBack(object value, + Type targetType, + object parameter, + string language) => + ConvertBackTo((TTarget)value, targetType, parameter, language); + + public TTarget? Convert(TSource value) => + ConvertTo(value, null, null, null); + + public TSource? ConvertBack(TTarget value) => + ConvertBackTo(value, null, null, null); + + protected virtual TTarget? ConvertTo(TSource value, + Type? targetType, + object? parameter, + string? language) => default; + + protected virtual TSource? ConvertBackTo(TTarget value, + Type? targetType, + object? parameter, + string? language) => default; + + protected override object ProvideValue() => this; +} diff --git a/Toolkit.WinUI/ContentControlHandler.cs b/Toolkit.WinUI/ContentControlHandler.cs index aeb4967..baf0cf3 100644 --- a/Toolkit.WinUI/ContentControlHandler.cs +++ b/Toolkit.WinUI/ContentControlHandler.cs @@ -40,11 +40,6 @@ public class ContentControlHandler : { activation.IsActive = false; } - - if (content is IDisposable disposable) - { - disposable.Dispose(); - } } } diff --git a/Toolkit.WinUI/IServiceCollectionExtensions.cs b/Toolkit.WinUI/IServiceCollectionExtensions.cs index efc6337..b80a4be 100644 --- a/Toolkit.WinUI/IServiceCollectionExtensions.cs +++ b/Toolkit.WinUI/IServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml.Controls; using Toolkit.Foundation; @@ -8,7 +9,7 @@ public static class IServiceCollectionExtensions { public static IServiceCollection AddWinUI(this IServiceCollection services) { - services.AddTransient(); + services.AddSingleton(provider => new WinUIDispatcher(DispatcherQueue.GetForCurrentThread())); services.AddTransient(); services.AddSingleton(); diff --git a/Toolkit.WinUI/WinUIDispatcher.cs b/Toolkit.WinUI/WinUIDispatcher.cs index 7ae5f17..26867e8 100644 --- a/Toolkit.WinUI/WinUIDispatcher.cs +++ b/Toolkit.WinUI/WinUIDispatcher.cs @@ -3,12 +3,12 @@ using Toolkit.Foundation; namespace Toolkit.WinUI; -public class WinUIDispatcher : +public class WinUIDispatcher(DispatcherQueue dispatcherQueue) : IDispatcher { public Task Invoke(Action action) { - DispatcherQueue.GetForCurrentThread().TryEnqueue(action.Invoke); + dispatcherQueue.TryEnqueue(action.Invoke); return Task.CompletedTask; } }