diff --git a/Version.json b/Version.json index 437b8275..cd2c3879 100644 --- a/Version.json +++ b/Version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "1.0.13", + "version": "1.0.14", "publicReleaseRefSpec": [ "^refs/heads/master$", "^refs/heads/main$" diff --git a/src/CrissCross.WPF.UI.CC_Nav.Test/MainWindow.xaml.cs b/src/CrissCross.WPF.UI.CC_Nav.Test/MainWindow.xaml.cs index 1100e683..0e0ada93 100644 --- a/src/CrissCross.WPF.UI.CC_Nav.Test/MainWindow.xaml.cs +++ b/src/CrissCross.WPF.UI.CC_Nav.Test/MainWindow.xaml.cs @@ -22,7 +22,7 @@ public MainWindow() this.WhenActivated(d => { NavBack.Command = ReactiveCommand.Create(() => this.NavigateBack(), this.CanNavigateBack()).DisposeWith(d); - this.NavigateToView(); + this.NavigateToView(typeof(MainViewModel)); }); } } diff --git a/src/CrissCross.WPF.UI/Controls/ProgressBar/ProgressBar.xaml b/src/CrissCross.WPF.UI/Controls/ProgressBar/ProgressBar.xaml index 9c3e1be2..84a1d9bd 100644 --- a/src/CrissCross.WPF.UI/Controls/ProgressBar/ProgressBar.xaml +++ b/src/CrissCross.WPF.UI/Controls/ProgressBar/ProgressBar.xaml @@ -19,6 +19,26 @@ + + + + + + - + + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnClosing(CancelEventArgs e) + { + ////WebView2Wpf.Dispose(); + base.OnClosing(e); + } + private void WindowsXp_Click(object sender, RoutedEventArgs e) { Greeting.Text = $"Hello CrissCross {_clickedXTimes++}"; diff --git a/src/CrissCross.WPF.WebView2/WebView2Wpf.cs b/src/CrissCross.WPF.WebView2/WebView2Wpf.cs index 11eaabf4..0a9e158a 100644 --- a/src/CrissCross.WPF.WebView2/WebView2Wpf.cs +++ b/src/CrissCross.WPF.WebView2/WebView2Wpf.cs @@ -35,6 +35,16 @@ public class WebView2Wpf : ContentControl, IDisposable #pragma warning disable SA1202 // Elements should be ordered by access + /// + /// The automatic dispose property. + /// + public static readonly DependencyProperty AutoDisposeProperty = + DependencyProperty.Register( + nameof(AutoDispose), + typeof(bool), + typeof(WebView2Wpf), + new PropertyMetadata(true, AutoDisposePropertyChanged)); + /// /// The WPF DependencyProperty which backs the Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties property. /// @@ -51,7 +61,7 @@ public class WebView2Wpf : ContentControl, IDisposable nameof(Source), typeof(Uri), typeof(WebView2Wpf), - new PropertyMetadata(SourceChanged)); + new PropertyMetadata(SourcePropertyChanged)); /// /// The WPF DependencyProperty which backs the Microsoft.Web.WebView2.Wpf.WebView2.CanGoBack property. @@ -88,14 +98,73 @@ public class WebView2Wpf : ContentControl, IDisposable /// /// Initializes a new instance of the class. /// - public WebView2Wpf() + public WebView2Wpf() => _WebBrowser = new() { - _WebBrowser = new() - { - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - }; - Unloaded += (s, e) => Dispose(); + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch, + }; + + /// + /// Occurs when [core web view2 initialization completed]. + /// + public event EventHandler CoreWebView2InitializationCompleted + { + add => _WebBrowser.CoreWebView2InitializationCompleted += value; + remove => _WebBrowser.CoreWebView2InitializationCompleted -= value; + } + + /// + /// Occurs when [source changed]. + /// + public event EventHandler SourceChanged + { + add => _WebBrowser.SourceChanged += value; + remove => _WebBrowser.SourceChanged -= value; + } + + /// + /// Occurs when [navigation starting]. + /// + public event EventHandler NavigationStarting + { + add => _WebBrowser.NavigationStarting += value; + remove => _WebBrowser.NavigationStarting -= value; + } + + /// + /// Occurs when [navigation completed]. + /// + public event EventHandler NavigationCompleted + { + add => _WebBrowser.NavigationCompleted += value; + remove => _WebBrowser.NavigationCompleted -= value; + } + + /// + /// Occurs when [zoom factor changed]. + /// + public event EventHandler ZoomFactorChanged + { + add => _WebBrowser.ZoomFactorChanged += value; + remove => _WebBrowser.ZoomFactorChanged -= value; + } + + /// + /// Occurs when [content loading]. + /// + public event EventHandler ContentLoading + { + add => _WebBrowser.ContentLoading += value; + remove => _WebBrowser.ContentLoading -= value; + } + + /// + /// Occurs when [web message received]. + /// + public event EventHandler WebMessageReceived + { + add => _WebBrowser.WebMessageReceived += value; + remove => _WebBrowser.WebMessageReceived -= value; } /// @@ -245,6 +314,18 @@ public double ZoomFactor [EditorBrowsable(EditorBrowsableState.Never)] public new InputScope InputScope => _WebBrowser.InputScope; + /// + /// Gets or sets a value indicating whether [automatic dispose]. + /// + /// + /// true if [automatic dispose]; otherwise, false. + /// + public bool AutoDispose + { + get => (bool)GetValue(AutoDisposeProperty); + set => SetValue(AutoDisposeProperty, value); + } + /// /// Navigates the WebView to the previous page in the navigation history. Equivalent /// to calling Microsoft.Web.WebView2.Core.CoreWebView2.GoBack on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 @@ -288,6 +369,28 @@ public double ZoomFactor /// A string. public async Task ExecuteScriptAsync(string javaScript) => await _WebBrowser.ExecuteScriptAsync(javaScript); + /// + /// Ensures the core web view2 asynchronous. + /// + /// The environment. + /// The controller options. + /// A Task that represents the background initialization process. When the task completes + /// then the Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 property will be available + /// for use (i.e. non-null). Note that the control's Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted + /// event will be invoked before the task completes. + public Task EnsureCoreWebView2Async(CoreWebView2Environment? environment = null, CoreWebView2ControllerOptions? controllerOptions = null) => + _WebBrowser.EnsureCoreWebView2Async(environment, controllerOptions); + + /// + /// Ensures the core web view2 asynchronous. + /// + /// The environment. + /// A Task that represents the background initialization process. When the task completes + /// then the Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 property will be available + /// for use (i.e. non-null). Note that the control's Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted + /// event will be invoked before the task completes. + public Task EnsureCoreWebView2Async(CoreWebView2Environment environment) => _WebBrowser.EnsureCoreWebView2Async(environment); + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -312,6 +415,7 @@ protected override void OnInitialized(EventArgs e) layoutRoot.Children.Add(_WebBrowser); layoutRoot.Children.Add(_windowHost); base.Content = layoutRoot; + AutoDisposePropertyChanged(this, new DependencyPropertyChangedEventArgs(AutoDisposeProperty, null, null)); } /// @@ -341,7 +445,7 @@ private static void CreationPropertiesChanged(DependencyObject d, DependencyProp } } - private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + private static void SourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is WebView2Wpf browser && e.NewValue is Uri source) { @@ -356,4 +460,19 @@ private static void ContentChanged(DependencyObject d, DependencyPropertyChanged browser._windowHost.Window.Content = e.NewValue; } } + + private static void AutoDisposePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is WebView2Wpf browser) + { + if (browser.AutoDispose) + { + browser._WebBrowser.Unloaded += (s, e) => browser.Dispose(); + } + else + { + browser._WebBrowser.Unloaded -= (s, e) => browser.Dispose(); + } + } + } } diff --git a/src/CrissCross.WPF/NavigationWebView.cs b/src/CrissCross.WPF/NavigationWebView.cs index 4b249caf..2d51dddd 100644 --- a/src/CrissCross.WPF/NavigationWebView.cs +++ b/src/CrissCross.WPF/NavigationWebView.cs @@ -2,8 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.ComponentModel; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media.Effects; +using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.Wpf; using ReactiveUI; using static ReactiveUI.TransitioningContentControl; @@ -17,6 +22,16 @@ namespace CrissCross.WPF; /// public class NavigationWebView : ContentControl, IDisposable, IUseNavigation, IActivatableView { + /// + /// The automatic dispose property. + /// + public static readonly DependencyProperty AutoDisposeProperty = + DependencyProperty.Register( + nameof(AutoDispose), + typeof(bool), + typeof(NavigationWebView), + new PropertyMetadata(true, AutoDisposePropertyChanged)); + /// /// The navigate back is enabled property. /// @@ -52,6 +67,23 @@ public class NavigationWebView : ContentControl, IDisposable, IUseNavigation, IA typeof(NavigationWebView), new PropertyMetadata(string.Empty, SourceChanged)); + /// + /// The WPF DependencyProperty which backs the Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor property. + /// + public static readonly DependencyProperty ZoomFactorProperty = DependencyProperty.Register( + nameof(ZoomFactor), + typeof(double), + typeof(NavigationWebView)); + + /// + /// The navigate back is enabled property. + /// + public static new readonly DependencyProperty ContentProperty = DependencyProperty.Register( + nameof(Content), + typeof(object), + typeof(NavigationWebView), + new PropertyMetadata(true, ContentChanged)); + private readonly WebView2 _WebBrowser; private WindowHost? _navigationWindowHost; private bool _disposedValue; @@ -59,16 +91,11 @@ public class NavigationWebView : ContentControl, IDisposable, IUseNavigation, IA /// /// Initializes a new instance of the class. /// - public NavigationWebView() + public NavigationWebView() => _WebBrowser = new() { - _WebBrowser = new() - { - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch - }; - - Unloaded += (s, e) => Dispose(); - } + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch + }; /// /// Gets the can navigate back. @@ -103,6 +130,116 @@ public string? Source set => SetValue(SourceProperty, value); } + /// + /// Gets a value indicating whether this instance can go back. + /// if the WebView can navigate to a previous page in the navigation + /// history. Wrapper around the Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack + /// property of Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2. If Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 + /// isn't initialized yet then returns false. + /// + /// + /// true if this instance can go back; otherwise, false. + /// + [Browsable(false)] + public bool CanGoBack => _WebBrowser.CanGoBack; + + /// + /// Gets a value indicating whether this instance can go forward. + /// if the WebView can navigate to a next page in the navigation history. + /// Wrapper around the Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward property + /// of Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2. If Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 + /// isn't initialized yet then returns false. + /// + /// + /// true if this instance can go forward; otherwise, false. + /// + [Browsable(false)] + public bool CanGoForward => _WebBrowser.CanGoForward; + + /// + /// Gets or sets a value indicating whether [zoom factor]. + /// + /// + /// true if [zoom factor]; otherwise, false. + /// + [Category("Common")] + public double ZoomFactor + { + get => (double)GetValue(ZoomFactorProperty); + set => SetValue(ZoomFactorProperty, value); + } + + /// + /// Gets or sets the content of the XAML overlay />. + /// + [Bindable(true)] + [Category("Content")] + public new object Content + { + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + + /// + /// Gets the opacity mask. + /// + /// + /// The opacity mask. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new System.Windows.Media.Brush OpacityMask => _WebBrowser.OpacityMask; + + /// + /// Gets the opacity. + /// + /// + /// The opacity. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new double Opacity => _WebBrowser.Opacity; + + /// + /// Gets the effect. + /// + /// + /// The effect. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new Effect Effect => _WebBrowser.Effect; + + /// + /// Gets the context menu. + /// + /// + /// The context menu. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new ContextMenu ContextMenu => _WebBrowser.ContextMenu; + + /// + /// Gets the focus visual style. + /// + /// + /// The focus visual style. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new Style FocusVisualStyle => _WebBrowser.FocusVisualStyle; + + /// + /// Gets the input scope. + /// + /// + /// The input scope. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new InputScope InputScope => _WebBrowser.InputScope; + /// /// Gets the navigation frame. /// @@ -127,6 +264,83 @@ public TransitionType Transition set => SetValue(TransitionProperty, value); } + /// + /// Gets or sets a value indicating whether [automatic dispose]. + /// + /// + /// true if [automatic dispose]; otherwise, false. + /// + public bool AutoDispose + { + get => (bool)GetValue(AutoDisposeProperty); + set => SetValue(AutoDisposeProperty, value); + } + + /// + /// Navigates the WebView to the previous page in the navigation history. Equivalent + /// to calling Microsoft.Web.WebView2.Core.CoreWebView2.GoBack on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 + /// If CoreWebView2 hasn't been initialized yet then does nothing. + /// + public void GoBack() => _WebBrowser?.GoBack(); + + /// + /// Navigates the WebView to the next page in the navigation history. Equivalent + /// to calling Microsoft.Web.WebView2.Core.CoreWebView2.GoForward on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 + /// If CoreWebView2 hasn't been initialized yet then does nothing. + /// + public void GoForward() => _WebBrowser?.GoForward(); + + /// + /// Reloads the current page. Equivalent to calling Microsoft.Web.WebView2.Core.CoreWebView2.Reload + /// on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2. + /// + public void Reload() => _WebBrowser?.Reload(); + + /// + /// Stops all navigations and pending resource fetches. Equivalent to calling Microsoft.Web.WebView2.Core.CoreWebView2.Stop + /// on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2. + /// + public void Stop() => _WebBrowser?.Stop(); + + /// + /// Initiates a navigation to htmlContent as source HTML of a new document. Equivalent + /// to calling Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String) + /// on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2. + /// + /// Content of the HTML. + public void NavigateToString(string htmlContent) => _WebBrowser?.NavigateToString(htmlContent); + + /// + /// Executes JavaScript code from the javaScript parameter in the current top level + /// document rendered in the WebView. Equivalent to calling Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String) + /// on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2. + /// + /// The java script. + /// A string. + public async Task ExecuteScriptAsync(string javaScript) => await _WebBrowser.ExecuteScriptAsync(javaScript); + + /// + /// Ensures the core web view2 asynchronous. + /// + /// The environment. + /// The controller options. + /// A Task that represents the background initialization process. When the task completes + /// then the Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 property will be available + /// for use (i.e. non-null). Note that the control's Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted + /// event will be invoked before the task completes. + public Task EnsureCoreWebView2Async(CoreWebView2Environment? environment = null, CoreWebView2ControllerOptions? controllerOptions = null) => + _WebBrowser.EnsureCoreWebView2Async(environment, controllerOptions); + + /// + /// Ensures the core web view2 asynchronous. + /// + /// The environment. + /// A Task that represents the background initialization process. When the task completes + /// then the Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 property will be available + /// for use (i.e. non-null). Note that the control's Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2InitializationCompleted + /// event will be invoked before the task completes. + public Task EnsureCoreWebView2Async(CoreWebView2Environment environment) => _WebBrowser.EnsureCoreWebView2Async(environment); + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -154,6 +368,7 @@ protected override void OnInitialized(EventArgs e) layoutRoot.Children.Add(_WebBrowser); layoutRoot.Children.Add(_navigationWindowHost); Content = layoutRoot; + AutoDisposePropertyChanged(this, new DependencyPropertyChangedEventArgs(AutoDisposeProperty, null, null)); } /// @@ -198,4 +413,27 @@ private static void TransitionChanged(DependencyObject d, DependencyPropertyChan browser._navigationWindowHost!.Window.Transition = (TransitionType)e.NewValue; } } + + private static void ContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is NavigationWebView browser && browser._navigationWindowHost?.Window is not null) + { + browser._navigationWindowHost.Window.Content = e.NewValue; + } + } + + private static void AutoDisposePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is NavigationWebView browser) + { + if (browser.AutoDispose) + { + browser._WebBrowser.Unloaded += (s, e) => browser.Dispose(); + } + else + { + browser._WebBrowser.Unloaded -= (s, e) => browser.Dispose(); + } + } + } } diff --git a/src/CrissCross/ViewModelRoutedViewHostMixins.cs b/src/CrissCross/ViewModelRoutedViewHostMixins.cs index e1509581..564ade63 100644 --- a/src/CrissCross/ViewModelRoutedViewHostMixins.cs +++ b/src/CrissCross/ViewModelRoutedViewHostMixins.cs @@ -10,6 +10,7 @@ using System.Reactive.Subjects; using System.Runtime.CompilerServices; using ReactiveUI; +using Splat; [assembly: InternalsVisibleTo(" CrissCross.WPF")] [assembly: InternalsVisibleTo(" CrissCross.XamForms")] @@ -285,6 +286,41 @@ public static void NavigateToView(this IUseNavigation @this, string? contract } } + /// + /// Navigates the specified contract. + /// + /// The this. + /// The rx object. + /// The contract. + /// The parameter. + /// this. + /// No navigation host registered, please ensure that the NavigationShell has a Name. + public static void NavigateToView(this IUseNavigation @this, Type rxObject, string? contract = null, object? parameter = null) + { + if (@this == null) + { + throw new ArgumentNullException(nameof(@this)); + } + + if (NavigationHost.Count == 0) + { + throw new InvalidOperationException("No navigation host registered, please ensure that the NavigationShell has a Name."); + } + + if (NavigationHost.Count > 0 && @this.Name != null && Locator.Current.GetService(rxObject, contract) is IRxObject toViewModel) + { + switch (@this.Name.Length) + { + case 0: + NavigationHost.First().Value.Navigate(toViewModel, contract, parameter); + break; + default: + NavigationHost[@this.Name].Navigate(toViewModel, contract, parameter); + break; + } + } + } + /// /// Navigates to view. /// @@ -319,6 +355,40 @@ public static void NavigateToView(this IUseHostedNavigation dummy, string? ho } } + /// + /// Navigates to view. + /// + /// The dummy. + /// The rx object. + /// Name of the host. + /// The contract. + /// The parameter. + /// No navigation host registered, please ensure that the NavigationShell has a Name. + public static void NavigateToView(this IUseHostedNavigation dummy, Type rxObject, string? hostName = "", string? contract = null, object? parameter = null) + { + if (NavigationHost.Count == 0) + { + throw new InvalidOperationException("No navigation host registered, please ensure that the NavigationShell has a Name."); + } + + if (NavigationHost.Count > 0 && hostName != null && Locator.Current.GetService(rxObject, contract) is IRxObject toViewModel) + { + switch (hostName.Length) + { + case 0: + NavigationHost.First().Value.Navigate(toViewModel, contract, parameter); + break; + default: + if (NavigationHost.TryGetValue(hostName, out var value)) + { + value.Navigate(toViewModel, contract, parameter); + } + + break; + } + } + } + /// /// Navigates the and reset. ///