Restored previous attempt at IndexOf(), but it's broken either way
This commit is contained in:
		
							parent
							
								
									059c093ec3
								
							
						
					
					
						commit
						ee6a57b784
					
				
					 4 changed files with 354 additions and 345 deletions
				
			
		| 
						 | 
				
			
			@ -289,7 +289,19 @@ namespace Nuclex.Avalonia.Collections {
 | 
			
		|||
    /// <param name="item">Item whose index will be determined</param>
 | 
			
		||||
    /// <returns>The index of the item in the list or -1 if not found</returns>
 | 
			
		||||
    public int IndexOf(TItem item) {
 | 
			
		||||
      return this.typedList.IndexOf(item);
 | 
			
		||||
      requireCount();
 | 
			
		||||
      requireAllPages();
 | 
			
		||||
 | 
			
		||||
      // TODO: this won't work, it will compare the placeholder items :-/
 | 
			
		||||
 | 
			
		||||
      IComparer<TItem> itemComparer = Comparer<TItem>.Default;
 | 
			
		||||
      for(int index = 0; index < this.assumedCount.Value; ++index) {
 | 
			
		||||
        if(itemComparer.Compare(this.typedList[index], item) == 0) {
 | 
			
		||||
          return index;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Inserts an item into the list at the specified index</summary>
 | 
			
		||||
| 
						 | 
				
			
			@ -344,10 +356,8 @@ namespace Nuclex.Avalonia.Collections {
 | 
			
		|||
    /// <param name="item">Item the list will be checked for</param>
 | 
			
		||||
    /// <returns>True if the list contains the specified items</returns>
 | 
			
		||||
    public bool Contains(TItem item) {
 | 
			
		||||
      requireCount();
 | 
			
		||||
      requireAllPages();
 | 
			
		||||
 | 
			
		||||
      return this.typedList.Contains(item);
 | 
			
		||||
      // TODO: this won't work, it will compare the placeholder items :-/
 | 
			
		||||
      return (IndexOf(item) != -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Copies the contents of the list into an array</summary>
 | 
			
		||||
| 
						 | 
				
			
			@ -359,14 +369,13 @@ namespace Nuclex.Avalonia.Collections {
 | 
			
		|||
      requireCount();
 | 
			
		||||
      requireAllPages();
 | 
			
		||||
 | 
			
		||||
      // TODO: this won't work, it will copy the placeholder items :-/
 | 
			
		||||
      this.typedList.CopyTo(array, arrayIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Total number of items in the list</summary>
 | 
			
		||||
    public int Count {
 | 
			
		||||
      get {
 | 
			
		||||
        return requireCount();
 | 
			
		||||
      }
 | 
			
		||||
      get { return requireCount(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Whether the list is a read-only list</summary>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,35 +1,35 @@
 | 
			
		|||
#region Apache License 2.0
 | 
			
		||||
/*
 | 
			
		||||
Nuclex Foundation libraries for .NET
 | 
			
		||||
Copyright (C) 2002-2025 Markus Ewald / Nuclex Development Labs
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
#endregion // Apache License 2.0
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
using Avalonia.Controls;
 | 
			
		||||
 | 
			
		||||
namespace Nuclex.Avalonia {
 | 
			
		||||
 | 
			
		||||
  /// <summary>Allows the currently active top level window to be looked up</summary>
 | 
			
		||||
  public interface IActiveWindowTracker {
 | 
			
		||||
 | 
			
		||||
    /// <summary>The currently active top-level or modal window</summary>
 | 
			
		||||
    Window? ActiveWindow { get; }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace Nuclex.Avalonia
 | 
			
		||||
 | 
			
		||||
#region Apache License 2.0
 | 
			
		||||
/*
 | 
			
		||||
Nuclex Foundation libraries for .NET
 | 
			
		||||
Copyright (C) 2002-2025 Markus Ewald / Nuclex Development Labs
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
#endregion // Apache License 2.0
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
using Avalonia.Controls;
 | 
			
		||||
 | 
			
		||||
namespace Nuclex.Avalonia {
 | 
			
		||||
 | 
			
		||||
  /// <summary>Allows the currently active top level window to be looked up</summary>
 | 
			
		||||
  public interface IActiveWindowTracker {
 | 
			
		||||
 | 
			
		||||
    /// <summary>The currently active top-level or modal window</summary>
 | 
			
		||||
    Window? ActiveWindow { get; }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace Nuclex.Avalonia
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,114 +1,114 @@
 | 
			
		|||
#region Apache License 2.0
 | 
			
		||||
/*
 | 
			
		||||
Nuclex Foundation libraries for .NET
 | 
			
		||||
Copyright (C) 2002-2025 Markus Ewald / Nuclex Development Labs
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
#endregion // Apache License 2.0
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
using Avalonia.Controls;
 | 
			
		||||
 | 
			
		||||
namespace Nuclex.Avalonia {
 | 
			
		||||
 | 
			
		||||
  /// <summary>Manages open windows and connecting them to view models</summary>
 | 
			
		||||
  public interface IWindowManager : IActiveWindowTracker {
 | 
			
		||||
 | 
			
		||||
    /// <summary>Opens a view as a new root window of the application</summary>
 | 
			
		||||
    /// <typeparam name="TViewModel">
 | 
			
		||||
    ///   Type of view model a root window will be opened for
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <param name="viewModel">
 | 
			
		||||
    ///   View model a window will be opened for. If null, the view model will be
 | 
			
		||||
    ///   created as well (unless the dialog already specifies one as a resource)
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="disposeOnClose">
 | 
			
		||||
    ///   Whether the view model should be disposed when the view is closed
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>The window that has been opened by the window manager</returns>
 | 
			
		||||
    Window OpenRoot<TViewModel>(
 | 
			
		||||
      TViewModel? viewModel = null, bool disposeOnClose = true
 | 
			
		||||
    ) where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Displays a view as a modal window</summary>
 | 
			
		||||
    /// <typeparam name="TViewModel">
 | 
			
		||||
    ///   Type of the view model for which a view will be displayed
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <param name="viewModel">
 | 
			
		||||
    ///   View model a modal window will be displayed for. If null, the view model will
 | 
			
		||||
    ///   be created as well (unless the dialog already specifies one as a resource)
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="disposeOnClose">
 | 
			
		||||
    ///   Whether the view model should be disposed when the view is closed
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>The return value of the modal window</returns>
 | 
			
		||||
    Task ShowModalAsync<TViewModel>(
 | 
			
		||||
      TViewModel? viewModel = null, bool disposeOnClose = false
 | 
			
		||||
    ) where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Displays a view as a modal window</summary>
 | 
			
		||||
    /// <typeparam name="TResult">
 | 
			
		||||
    ///   Type of result the modal dialog will return to the caller
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <typeparam name="TViewModel">
 | 
			
		||||
    ///   Type of the view model for which a view will be displayed
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <param name="viewModel">
 | 
			
		||||
    ///   View model a modal window will be displayed for. If null, the view model will
 | 
			
		||||
    ///   be created as well (unless the dialog already specifies one as a resource)
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="disposeOnClose">
 | 
			
		||||
    ///   Whether the view model should be disposed when the view is closed
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>The return value of the modal window</returns>
 | 
			
		||||
    Task<TResult> ShowModalAsync<TResult, TViewModel>(
 | 
			
		||||
      TViewModel? viewModel = null, bool disposeOnClose = true
 | 
			
		||||
    ) where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Creates the view for the specified view model</summary>
 | 
			
		||||
    /// <typeparam name="TViewModel">
 | 
			
		||||
    ///   Type of view model for which a view will be created
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <param name="viewModel">
 | 
			
		||||
    ///   View model a view will be created for. If null, the view model will be
 | 
			
		||||
    ///   created as well (unless the dialog already specifies one as a resource)
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>The view for the specified view model</returns>
 | 
			
		||||
    Control CreateView<TViewModel>(TViewModel? viewModel = null)
 | 
			
		||||
      where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Creates a view model without a matching view</summary>
 | 
			
		||||
    /// <typeparam name="TViewModel">Type of view model that will be created</typeparam>
 | 
			
		||||
    /// <returns>The new view model</returns>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    ///   <para>
 | 
			
		||||
    ///     This is useful if a view model needs to create child view models (i.e. paged container
 | 
			
		||||
    ///     and wants to ensure the same dependency injector (if any) if used as the window
 | 
			
		||||
    ///     manager uses for other view models it creates.
 | 
			
		||||
    ///   </para>
 | 
			
		||||
    ///   <para>
 | 
			
		||||
    ///     This way, view models can set up their child view models without having to immediately
 | 
			
		||||
    ///     bind a view to them. Later on, views can use the window manager to create a matching
 | 
			
		||||
    ///     child view and store it in a container.
 | 
			
		||||
    ///   </para>
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    TViewModel CreateViewModel<TViewModel>()
 | 
			
		||||
      where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace Nuclex.Avalonia
 | 
			
		||||
 | 
			
		||||
#region Apache License 2.0
 | 
			
		||||
/*
 | 
			
		||||
Nuclex Foundation libraries for .NET
 | 
			
		||||
Copyright (C) 2002-2025 Markus Ewald / Nuclex Development Labs
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
#endregion // Apache License 2.0
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
using Avalonia.Controls;
 | 
			
		||||
 | 
			
		||||
namespace Nuclex.Avalonia {
 | 
			
		||||
 | 
			
		||||
  /// <summary>Manages open windows and connecting them to view models</summary>
 | 
			
		||||
  public interface IWindowManager : IActiveWindowTracker {
 | 
			
		||||
 | 
			
		||||
    /// <summary>Opens a view as a new root window of the application</summary>
 | 
			
		||||
    /// <typeparam name="TViewModel">
 | 
			
		||||
    ///   Type of view model a root window will be opened for
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <param name="viewModel">
 | 
			
		||||
    ///   View model a window will be opened for. If null, the view model will be
 | 
			
		||||
    ///   created as well (unless the dialog already specifies one as a resource)
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="disposeOnClose">
 | 
			
		||||
    ///   Whether the view model should be disposed when the view is closed
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>The window that has been opened by the window manager</returns>
 | 
			
		||||
    Window OpenRoot<TViewModel>(
 | 
			
		||||
      TViewModel? viewModel = null, bool disposeOnClose = true
 | 
			
		||||
    ) where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Displays a view as a modal window</summary>
 | 
			
		||||
    /// <typeparam name="TViewModel">
 | 
			
		||||
    ///   Type of the view model for which a view will be displayed
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <param name="viewModel">
 | 
			
		||||
    ///   View model a modal window will be displayed for. If null, the view model will
 | 
			
		||||
    ///   be created as well (unless the dialog already specifies one as a resource)
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="disposeOnClose">
 | 
			
		||||
    ///   Whether the view model should be disposed when the view is closed
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>The return value of the modal window</returns>
 | 
			
		||||
    Task ShowModalAsync<TViewModel>(
 | 
			
		||||
      TViewModel? viewModel = null, bool disposeOnClose = false
 | 
			
		||||
    ) where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Displays a view as a modal window</summary>
 | 
			
		||||
    /// <typeparam name="TResult">
 | 
			
		||||
    ///   Type of result the modal dialog will return to the caller
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <typeparam name="TViewModel">
 | 
			
		||||
    ///   Type of the view model for which a view will be displayed
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <param name="viewModel">
 | 
			
		||||
    ///   View model a modal window will be displayed for. If null, the view model will
 | 
			
		||||
    ///   be created as well (unless the dialog already specifies one as a resource)
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="disposeOnClose">
 | 
			
		||||
    ///   Whether the view model should be disposed when the view is closed
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>The return value of the modal window</returns>
 | 
			
		||||
    Task<TResult> ShowModalAsync<TResult, TViewModel>(
 | 
			
		||||
      TViewModel? viewModel = null, bool disposeOnClose = true
 | 
			
		||||
    ) where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Creates the view for the specified view model</summary>
 | 
			
		||||
    /// <typeparam name="TViewModel">
 | 
			
		||||
    ///   Type of view model for which a view will be created
 | 
			
		||||
    /// </typeparam>
 | 
			
		||||
    /// <param name="viewModel">
 | 
			
		||||
    ///   View model a view will be created for. If null, the view model will be
 | 
			
		||||
    ///   created as well (unless the dialog already specifies one as a resource)
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>The view for the specified view model</returns>
 | 
			
		||||
    Control CreateView<TViewModel>(TViewModel? viewModel = null)
 | 
			
		||||
      where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Creates a view model without a matching view</summary>
 | 
			
		||||
    /// <typeparam name="TViewModel">Type of view model that will be created</typeparam>
 | 
			
		||||
    /// <returns>The new view model</returns>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    ///   <para>
 | 
			
		||||
    ///     This is useful if a view model needs to create child view models (i.e. paged container
 | 
			
		||||
    ///     and wants to ensure the same dependency injector (if any) if used as the window
 | 
			
		||||
    ///     manager uses for other view models it creates.
 | 
			
		||||
    ///   </para>
 | 
			
		||||
    ///   <para>
 | 
			
		||||
    ///     This way, view models can set up their child view models without having to immediately
 | 
			
		||||
    ///     bind a view to them. Later on, views can use the window manager to create a matching
 | 
			
		||||
    ///     child view and store it in a container.
 | 
			
		||||
    ///   </para>
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    TViewModel CreateViewModel<TViewModel>()
 | 
			
		||||
      where TViewModel : class;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace Nuclex.Avalonia
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,188 +1,188 @@
 | 
			
		|||
#region Apache License 2.0
 | 
			
		||||
/*
 | 
			
		||||
Nuclex Foundation libraries for .NET
 | 
			
		||||
Copyright (C) 2002-2025 Markus Ewald / Nuclex Development Labs
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
#endregion // Apache License 2.0
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Runtime.Versioning;
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
using System.Runtime.Versioning;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using Avalonia.Controls;
 | 
			
		||||
 | 
			
		||||
using MessageBoxIcon = MsBox.Avalonia.Enums.Icon;
 | 
			
		||||
using MessageBoxButtons = MsBox.Avalonia.Enums.ButtonEnum;
 | 
			
		||||
using MessageDialogResult = MsBox.Avalonia.Enums.ButtonResult;
 | 
			
		||||
 | 
			
		||||
namespace Nuclex.Avalonia.Messages {
 | 
			
		||||
 | 
			
		||||
  /// <summary>Uses Avalonia to display message boxes</summary>
 | 
			
		||||
  public class AvaloniaMessagePresenter : IMessageService {
 | 
			
		||||
 | 
			
		||||
    #region class MessageScope
 | 
			
		||||
 | 
			
		||||
    /// <summary>Triggers the message displayed and acknowledged events</summary>
 | 
			
		||||
    private class MessageScope : IDisposable {
 | 
			
		||||
 | 
			
		||||
      /// <summary>
 | 
			
		||||
      ///   Initializes a new message scope, triggering the message displayed event
 | 
			
		||||
      /// </summary>
 | 
			
		||||
      /// <param name="self">Message service the scope belongs to</param>
 | 
			
		||||
      /// <param name="image">Image of the message being displayed</param>
 | 
			
		||||
      /// <param name="text">Text contained in the message being displayed</param>
 | 
			
		||||
      public MessageScope(
 | 
			
		||||
        AvaloniaMessagePresenter self, MessageBoxIcon image, MessageText text
 | 
			
		||||
      ) {
 | 
			
		||||
        EventHandler<MessageEventArgs>? messageDisplayed = self.MessageDisplaying;
 | 
			
		||||
        if(messageDisplayed != null) {
 | 
			
		||||
          messageDisplayed(this, new MessageEventArgs(image, text));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.self = self;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /// <summary>Triggers the message acknowledged event</summary>
 | 
			
		||||
      public void Dispose() {
 | 
			
		||||
        EventHandler? messageAcknowledged = self.MessageAcknowledged;
 | 
			
		||||
        if(messageAcknowledged != null) {
 | 
			
		||||
          messageAcknowledged(this, EventArgs.Empty);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /// <summary>Message service the scope belongs to</summary>
 | 
			
		||||
      private AvaloniaMessagePresenter self;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion // class MessageScope
 | 
			
		||||
 | 
			
		||||
    /// <summary>Triggered when a message is displayed to the user</summary>
 | 
			
		||||
    public event EventHandler<MessageEventArgs>? MessageDisplaying;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Triggered when the user has acknowledged the current message</summary>
 | 
			
		||||
    public event EventHandler? MessageAcknowledged;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Initializes a new Avalonia message service</summary>
 | 
			
		||||
    /// <param name="tracker">Used to determine the current top-level window</param>
 | 
			
		||||
    public AvaloniaMessagePresenter(IActiveWindowTracker tracker) {
 | 
			
		||||
      this.tracker = tracker;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Asks the user a question that can be answered via several buttons</summary>
 | 
			
		||||
    /// <param name="image">Image that will be shown on the message box</param>
 | 
			
		||||
    /// <param name="text">Text that will be shown to the user</param>
 | 
			
		||||
    /// <param name="buttons">Buttons available for the user to click on</param>
 | 
			
		||||
    /// <returns>The button the user has clicked on</returns>
 | 
			
		||||
    public Task<MessageDialogResult> ShowQuestionAsync(
 | 
			
		||||
      MessageBoxIcon image, MessageText text, MessageBoxButtons buttons
 | 
			
		||||
    ) {
 | 
			
		||||
      using(var scope = new MessageScope(this, image, text)) {
 | 
			
		||||
        MsBox.Avalonia.Base.IMsBox<MessageDialogResult> messageBox = (
 | 
			
		||||
          MsBox.Avalonia.MessageBoxManager.GetMessageBoxStandard(
 | 
			
		||||
            new MsBox.Avalonia.Dto.MessageBoxStandardParams() {
 | 
			
		||||
              ContentTitle = text.Caption,
 | 
			
		||||
              ContentHeader = text.Message,
 | 
			
		||||
              ContentMessage = text.Details ?? string.Empty,
 | 
			
		||||
              ButtonDefinitions = buttons,
 | 
			
		||||
              Icon = image,
 | 
			
		||||
              WindowStartupLocation = WindowStartupLocation.CenterOwner
 | 
			
		||||
            }
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        return messageBox.ShowAsync(); // TODO: Make modal to current or main window
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Displays a notification to the user</summary>
 | 
			
		||||
    /// <param name="image">Image that will be shown on the message bx</param>
 | 
			
		||||
    /// <param name="text">Text that will be shown to the user</param>
 | 
			
		||||
    public Task ShowNotificationAsync(MessageBoxIcon image, MessageText text) {
 | 
			
		||||
      using(var scope = new MessageScope(this, image, text)) {
 | 
			
		||||
        MsBox.Avalonia.Base.IMsBox<MessageDialogResult> messageBox = (
 | 
			
		||||
          MsBox.Avalonia.MessageBoxManager.GetMessageBoxStandard(
 | 
			
		||||
            new MsBox.Avalonia.Dto.MessageBoxStandardParams() {
 | 
			
		||||
              ContentTitle = text.Caption,
 | 
			
		||||
              ContentHeader = text.Message,
 | 
			
		||||
              ContentMessage = text.Details ?? string.Empty,
 | 
			
		||||
              ButtonDefinitions = MessageBoxButtons.Ok,
 | 
			
		||||
              Icon = image,
 | 
			
		||||
              WindowStartupLocation = WindowStartupLocation.CenterOwner
 | 
			
		||||
            }
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        Window? activeWindow = this.tracker.ActiveWindow;
 | 
			
		||||
        if(activeWindow == null) {
 | 
			
		||||
          return messageBox.ShowAsync();
 | 
			
		||||
        } else {
 | 
			
		||||
          //return messageBox.ShowAsPopupAsync(activeWindow);
 | 
			
		||||
          return messageBox.ShowWindowDialogAsync(activeWindow);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Reports an error using the system's message box functions</summary>
 | 
			
		||||
    /// <param name="title">Title of the message box</param>
 | 
			
		||||
    /// <param name="message">Message text that will be displayed</param>
 | 
			
		||||
    public static void FallbackReportError(string title, string message) {
 | 
			
		||||
      // TODO: Escape quotes for the command-line tools
 | 
			
		||||
      // TODO: Wait for the child process to exit so display is certain
 | 
			
		||||
 | 
			
		||||
      if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
 | 
			
		||||
        MessageBoxW(IntPtr.Zero, message, title, MB_OK | MB_ICONEXCLAMATION);
 | 
			
		||||
      } else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
 | 
			
		||||
        Process.Start("zenity", $"--error --title=\"{title}\" --text=\"{message}\"");
 | 
			
		||||
      } else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
 | 
			
		||||
        Process.Start("osascript", $"-e 'display dialog \"{message}\" with title \"{title}\" with icon stop'");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Windows only: display a message box with an OK button</summary>
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
  [SupportedOSPlatform("windows")]
 | 
			
		||||
#endif
 | 
			
		||||
    private const uint MB_OK = 0x00000000;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Windows only: display a message box with an Exclamation icon</summary>
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
  [SupportedOSPlatform("windows")]
 | 
			
		||||
#endif
 | 
			
		||||
    private const uint MB_ICONEXCLAMATION = 0x00000030;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Windows only: displays a native Windows message box</summary>
 | 
			
		||||
    /// <param name="parentWindowHandle">Handle of the window that owns the message box</param>
 | 
			
		||||
    /// <param name="text">Text that should be in the message box</param>
 | 
			
		||||
    /// <param name="caption">Caption or window title of the message box</param>
 | 
			
		||||
    /// <param name="type">Which icons and buttons that message box should have</param>
 | 
			
		||||
    /// <returns>How the user closed the message box and which button they clicked</returns>
 | 
			
		||||
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
  [SupportedOSPlatform("windows")]
 | 
			
		||||
#endif
 | 
			
		||||
    private static extern int MessageBoxW(IntPtr parentWindowHandle, string text, string caption, uint type);
 | 
			
		||||
 | 
			
		||||
    // <summary>Provides the currently active top-level window</summary>
 | 
			
		||||
    private IActiveWindowTracker tracker;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace Nuclex.Avalonia.Messages
 | 
			
		||||
 | 
			
		||||
#region Apache License 2.0
 | 
			
		||||
/*
 | 
			
		||||
Nuclex Foundation libraries for .NET
 | 
			
		||||
Copyright (C) 2002-2025 Markus Ewald / Nuclex Development Labs
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
#endregion // Apache License 2.0
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Runtime.Versioning;
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
using System.Runtime.Versioning;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using Avalonia.Controls;
 | 
			
		||||
 | 
			
		||||
using MessageBoxIcon = MsBox.Avalonia.Enums.Icon;
 | 
			
		||||
using MessageBoxButtons = MsBox.Avalonia.Enums.ButtonEnum;
 | 
			
		||||
using MessageDialogResult = MsBox.Avalonia.Enums.ButtonResult;
 | 
			
		||||
 | 
			
		||||
namespace Nuclex.Avalonia.Messages {
 | 
			
		||||
 | 
			
		||||
  /// <summary>Uses Avalonia to display message boxes</summary>
 | 
			
		||||
  public class AvaloniaMessagePresenter : IMessageService {
 | 
			
		||||
 | 
			
		||||
    #region class MessageScope
 | 
			
		||||
 | 
			
		||||
    /// <summary>Triggers the message displayed and acknowledged events</summary>
 | 
			
		||||
    private class MessageScope : IDisposable {
 | 
			
		||||
 | 
			
		||||
      /// <summary>
 | 
			
		||||
      ///   Initializes a new message scope, triggering the message displayed event
 | 
			
		||||
      /// </summary>
 | 
			
		||||
      /// <param name="self">Message service the scope belongs to</param>
 | 
			
		||||
      /// <param name="image">Image of the message being displayed</param>
 | 
			
		||||
      /// <param name="text">Text contained in the message being displayed</param>
 | 
			
		||||
      public MessageScope(
 | 
			
		||||
        AvaloniaMessagePresenter self, MessageBoxIcon image, MessageText text
 | 
			
		||||
      ) {
 | 
			
		||||
        EventHandler<MessageEventArgs>? messageDisplayed = self.MessageDisplaying;
 | 
			
		||||
        if(messageDisplayed != null) {
 | 
			
		||||
          messageDisplayed(this, new MessageEventArgs(image, text));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.self = self;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /// <summary>Triggers the message acknowledged event</summary>
 | 
			
		||||
      public void Dispose() {
 | 
			
		||||
        EventHandler? messageAcknowledged = self.MessageAcknowledged;
 | 
			
		||||
        if(messageAcknowledged != null) {
 | 
			
		||||
          messageAcknowledged(this, EventArgs.Empty);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /// <summary>Message service the scope belongs to</summary>
 | 
			
		||||
      private AvaloniaMessagePresenter self;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion // class MessageScope
 | 
			
		||||
 | 
			
		||||
    /// <summary>Triggered when a message is displayed to the user</summary>
 | 
			
		||||
    public event EventHandler<MessageEventArgs>? MessageDisplaying;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Triggered when the user has acknowledged the current message</summary>
 | 
			
		||||
    public event EventHandler? MessageAcknowledged;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Initializes a new Avalonia message service</summary>
 | 
			
		||||
    /// <param name="tracker">Used to determine the current top-level window</param>
 | 
			
		||||
    public AvaloniaMessagePresenter(IActiveWindowTracker tracker) {
 | 
			
		||||
      this.tracker = tracker;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Asks the user a question that can be answered via several buttons</summary>
 | 
			
		||||
    /// <param name="image">Image that will be shown on the message box</param>
 | 
			
		||||
    /// <param name="text">Text that will be shown to the user</param>
 | 
			
		||||
    /// <param name="buttons">Buttons available for the user to click on</param>
 | 
			
		||||
    /// <returns>The button the user has clicked on</returns>
 | 
			
		||||
    public Task<MessageDialogResult> ShowQuestionAsync(
 | 
			
		||||
      MessageBoxIcon image, MessageText text, MessageBoxButtons buttons
 | 
			
		||||
    ) {
 | 
			
		||||
      using(var scope = new MessageScope(this, image, text)) {
 | 
			
		||||
        MsBox.Avalonia.Base.IMsBox<MessageDialogResult> messageBox = (
 | 
			
		||||
          MsBox.Avalonia.MessageBoxManager.GetMessageBoxStandard(
 | 
			
		||||
            new MsBox.Avalonia.Dto.MessageBoxStandardParams() {
 | 
			
		||||
              ContentTitle = text.Caption,
 | 
			
		||||
              ContentHeader = text.Message,
 | 
			
		||||
              ContentMessage = text.Details ?? string.Empty,
 | 
			
		||||
              ButtonDefinitions = buttons,
 | 
			
		||||
              Icon = image,
 | 
			
		||||
              WindowStartupLocation = WindowStartupLocation.CenterOwner
 | 
			
		||||
            }
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        return messageBox.ShowAsync(); // TODO: Make modal to current or main window
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Displays a notification to the user</summary>
 | 
			
		||||
    /// <param name="image">Image that will be shown on the message bx</param>
 | 
			
		||||
    /// <param name="text">Text that will be shown to the user</param>
 | 
			
		||||
    public Task ShowNotificationAsync(MessageBoxIcon image, MessageText text) {
 | 
			
		||||
      using(var scope = new MessageScope(this, image, text)) {
 | 
			
		||||
        MsBox.Avalonia.Base.IMsBox<MessageDialogResult> messageBox = (
 | 
			
		||||
          MsBox.Avalonia.MessageBoxManager.GetMessageBoxStandard(
 | 
			
		||||
            new MsBox.Avalonia.Dto.MessageBoxStandardParams() {
 | 
			
		||||
              ContentTitle = text.Caption,
 | 
			
		||||
              ContentHeader = text.Message,
 | 
			
		||||
              ContentMessage = text.Details ?? string.Empty,
 | 
			
		||||
              ButtonDefinitions = MessageBoxButtons.Ok,
 | 
			
		||||
              Icon = image,
 | 
			
		||||
              WindowStartupLocation = WindowStartupLocation.CenterOwner
 | 
			
		||||
            }
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        Window? activeWindow = this.tracker.ActiveWindow;
 | 
			
		||||
        if(activeWindow == null) {
 | 
			
		||||
          return messageBox.ShowAsync();
 | 
			
		||||
        } else {
 | 
			
		||||
          //return messageBox.ShowAsPopupAsync(activeWindow);
 | 
			
		||||
          return messageBox.ShowWindowDialogAsync(activeWindow);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Reports an error using the system's message box functions</summary>
 | 
			
		||||
    /// <param name="title">Title of the message box</param>
 | 
			
		||||
    /// <param name="message">Message text that will be displayed</param>
 | 
			
		||||
    public static void FallbackReportError(string title, string message) {
 | 
			
		||||
      // TODO: Escape quotes for the command-line tools
 | 
			
		||||
      // TODO: Wait for the child process to exit so display is certain
 | 
			
		||||
 | 
			
		||||
      if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
 | 
			
		||||
        MessageBoxW(IntPtr.Zero, message, title, MB_OK | MB_ICONEXCLAMATION);
 | 
			
		||||
      } else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
 | 
			
		||||
        Process.Start("zenity", $"--error --title=\"{title}\" --text=\"{message}\"");
 | 
			
		||||
      } else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
 | 
			
		||||
        Process.Start("osascript", $"-e 'display dialog \"{message}\" with title \"{title}\" with icon stop'");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Windows only: display a message box with an OK button</summary>
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
  [SupportedOSPlatform("windows")]
 | 
			
		||||
#endif
 | 
			
		||||
    private const uint MB_OK = 0x00000000;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Windows only: display a message box with an Exclamation icon</summary>
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
  [SupportedOSPlatform("windows")]
 | 
			
		||||
#endif
 | 
			
		||||
    private const uint MB_ICONEXCLAMATION = 0x00000030;
 | 
			
		||||
 | 
			
		||||
    /// <summary>Windows only: displays a native Windows message box</summary>
 | 
			
		||||
    /// <param name="parentWindowHandle">Handle of the window that owns the message box</param>
 | 
			
		||||
    /// <param name="text">Text that should be in the message box</param>
 | 
			
		||||
    /// <param name="caption">Caption or window title of the message box</param>
 | 
			
		||||
    /// <param name="type">Which icons and buttons that message box should have</param>
 | 
			
		||||
    /// <returns>How the user closed the message box and which button they clicked</returns>
 | 
			
		||||
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
  [SupportedOSPlatform("windows")]
 | 
			
		||||
#endif
 | 
			
		||||
    private static extern int MessageBoxW(IntPtr parentWindowHandle, string text, string caption, uint type);
 | 
			
		||||
 | 
			
		||||
    // <summary>Provides the currently active top-level window</summary>
 | 
			
		||||
    private IActiveWindowTracker tracker;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace Nuclex.Avalonia.Messages
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue