diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index 3b320e0..8de62b6 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -1,31 +1,50 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Nuclex.Windows.Forms")]
-[assembly: AssemblyProduct("Nuclex.Windows.Forms")]
-[assembly: AssemblyDescription("Lean and elegant MVVM library with extras for WinForms")]
-[assembly: AssemblyCompany("Nuclex Development Labs")]
-[assembly: AssemblyCopyright("Copyright © Nuclex Development Labs 2019")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("08254ad9-394e-4638-8412-098c8c4a4c39")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-[assembly: AssemblyVersion("1.0.0.0")]
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Nuclex.Windows.Forms")]
+[assembly: AssemblyProduct("Nuclex.Windows.Forms")]
+[assembly: AssemblyDescription("Lean and elegant MVVM library with extras for WinForms")]
+[assembly: AssemblyCompany("Nuclex Development Labs")]
+[assembly: AssemblyCopyright("Copyright © Markus Ewald / Nuclex Development Labs 2002-2024")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("08254ad9-394e-4638-8412-098c8c4a4c39")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
diff --git a/Source/AsyncProgressBar/AsyncProgressBar.Designer.cs b/Source/AsyncProgressBar/AsyncProgressBar.Designer.cs
index 12904bc..7a858cb 100644
--- a/Source/AsyncProgressBar/AsyncProgressBar.Designer.cs
+++ b/Source/AsyncProgressBar/AsyncProgressBar.Designer.cs
@@ -1,53 +1,52 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-namespace Nuclex.Windows.Forms {
-
- partial class AsyncProgressBar {
-
- /// Required designer variable.
- private System.ComponentModel.IContainer components = null;
-
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- ///
- protected override void Dispose(bool disposing) {
- if(disposing && (components != null)) {
- components.Dispose();
- }
-
- base.Dispose(disposing);
- }
-
- #region Component Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent() {
- components = new System.ComponentModel.Container();
- }
-
- #endregion
- }
-
-} // namespace Nuclex.Windows.Forms
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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
+
+namespace Nuclex.Windows.Forms {
+
+ partial class AsyncProgressBar {
+
+ /// Required designer variable.
+ private System.ComponentModel.IContainer components = null;
+
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ ///
+ protected override void Dispose(bool disposing) {
+ if(disposing && (components != null)) {
+ components.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ components = new System.ComponentModel.Container();
+ }
+
+ #endregion
+ }
+
+} // namespace Nuclex.Windows.Forms
diff --git a/Source/AsyncProgressBar/AsyncProgressBar.Test.cs b/Source/AsyncProgressBar/AsyncProgressBar.Test.cs
index 0995923..d2579cf 100644
--- a/Source/AsyncProgressBar/AsyncProgressBar.Test.cs
+++ b/Source/AsyncProgressBar/AsyncProgressBar.Test.cs
@@ -1,71 +1,70 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-#if UNITTEST
-
-using System;
-using System.IO;
-using System.Windows.Forms;
-
-using NUnit.Framework;
-
-using Nuclex.Support;
-
-namespace Nuclex.Windows.Forms {
-
- /// Unit Test for the asynchronously updating progress bar
- [TestFixture, Explicit]
- public class AsyncProgressBarTest {
-
- ///
- /// Verifies that asynchronous progress assignment is working
- ///
- [Test]
- public void TestProgressAssignment() {
- using(AsyncProgressBar progressBar = new AsyncProgressBar()) {
-
- // Let the control create its window handle
- progressBar.CreateControl();
- progressBar.Minimum = 0;
- progressBar.Maximum = 100;
-
- Assert.AreEqual(0, progressBar.Value);
-
- // Assign the new value. This will be done asynchronously, so we call
- // Application.DoEvents() to execute the message pump once, guaranteeing
- // that the call will have been executed after Application.DoEvents() returns.
- progressBar.AsyncSetValue(0.33f);
- Application.DoEvents();
-
- Assert.AreEqual(33, progressBar.Value);
-
- progressBar.AsyncSetValue(0.66f);
- Application.DoEvents();
-
- Assert.AreEqual(66, progressBar.Value);
-
- }
- }
-
- }
-
-} // namespace Nuclex.Windows.Forms
-
-#endif // UNITTEST
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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
+
+#if UNITTEST
+
+using System;
+using System.IO;
+using System.Windows.Forms;
+
+using NUnit.Framework;
+
+using Nuclex.Support;
+
+namespace Nuclex.Windows.Forms {
+
+ /// Unit Test for the asynchronously updating progress bar
+ [TestFixture, Explicit]
+ public class AsyncProgressBarTest {
+
+ ///
+ /// Verifies that asynchronous progress assignment is working
+ ///
+ [Test]
+ public void TestProgressAssignment() {
+ using(AsyncProgressBar progressBar = new AsyncProgressBar()) {
+
+ // Let the control create its window handle
+ progressBar.CreateControl();
+ progressBar.Minimum = 0;
+ progressBar.Maximum = 100;
+
+ Assert.AreEqual(0, progressBar.Value);
+
+ // Assign the new value. This will be done asynchronously, so we call
+ // Application.DoEvents() to execute the message pump once, guaranteeing
+ // that the call will have been executed after Application.DoEvents() returns.
+ progressBar.AsyncSetValue(0.33f);
+ Application.DoEvents();
+
+ Assert.AreEqual(33, progressBar.Value);
+
+ progressBar.AsyncSetValue(0.66f);
+ Application.DoEvents();
+
+ Assert.AreEqual(66, progressBar.Value);
+
+ }
+ }
+
+ }
+
+} // namespace Nuclex.Windows.Forms
+
+#endif // UNITTEST
diff --git a/Source/AsyncProgressBar/AsyncProgressBar.cs b/Source/AsyncProgressBar/AsyncProgressBar.cs
index e3b8fdf..237ff04 100644
--- a/Source/AsyncProgressBar/AsyncProgressBar.cs
+++ b/Source/AsyncProgressBar/AsyncProgressBar.cs
@@ -1,140 +1,139 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-using System;
-using System.Threading;
-using System.Windows.Forms;
-
-namespace Nuclex.Windows.Forms {
-
- /// Progress bar with optimized multi-threading behavior
- ///
- ///
- /// If a background thread is generating lots of progress updates, using synchronized
- /// calls can drastically reduce performance. This progress bar optimizes that case
- /// by performing the update asynchronously and keeping only the most recent update
- /// when multiple updates arrive while the asynchronous update call is still running.
- ///
- ///
- /// This design eliminates useless queueing of progress updates, thereby reducing
- /// CPU load occuring in the UI thread and at the same time avoids blocking the
- /// worker thread, increasing its performance.
- ///
- ///
- public partial class AsyncProgressBar : ProgressBar {
-
- /// Initializes a new asynchronous progress bar
- public AsyncProgressBar() {
- InitializeComponent();
-
- this.Disposed += new EventHandler(progressBarDisposed);
- this.updateProgressDelegate = new MethodInvoker(updateProgress);
-
- // Could probably use VolatileWrite() as well, but for consistency reasons
- // this is an Interlocked call, too. Mixing different synchronization measures
- // for a variable raises a red flag whenever I see it :)
- Interlocked.Exchange(ref this.newProgress, -1.0f);
- }
-
- /// Called when the progress bar is being disposed
- /// Progress bar that is being disposed
- /// Not used
- private void progressBarDisposed(object sender, EventArgs arguments) {
-
- // CHECK: This method is only called on an explicit Dispose() of the control.
- // It is legal to call Control.BeginInvoke() without calling Control.EndInvoke(),
- // so the code is quite correct even if no Dispose() occurs, but is it also clean?
- // http://www.interact-sw.co.uk/iangblog/2005/05/16/endinvokerequired
-
- // Since this has to occur in the UI thread, there's no way that updateProgress()
- // could be executing just now. But the final call to updateProgress() will not
- // have EndInvoke() called on it yet, so we do this here before the control
- // is finally disposed.
- if(this.progressUpdateAsyncResult != null) {
- EndInvoke(this.progressUpdateAsyncResult);
- this.progressUpdateAsyncResult = null;
- }
-
- }
-
- /// Asynchronously updates the value to be shown in the progress bar
- /// New value to set the progress bar to
- ///
- /// This will schedule an asynchronous update of the progress bar in the UI thread.
- /// If you change the progress value again before the progress bar has completed its
- /// update cycle, the original progress value will be skipped and the progress bar
- /// jumps directly to the latest progress value. Updates are not queued, there is
- /// at most one update waiting on the UI thread. It is also strictly guaranteed that
- /// the last most progress value set will be shown and never skipped.
- ///
- public void AsyncSetValue(float value) {
-
- // Update the value to be shown on the progress bar. If this happens multiple
- // times, that's not a problem, the progress bar updates as fast as it can
- // and always tries to show the most recent value assigned.
- float oldValue = Interlocked.Exchange(ref this.newProgress, value);
-
- // If the previous value was -1, the UI thread has already taken out the most recent
- // value and assigned it (or is about to assign it) to the progress bar control.
- // In this case, we'll wait until the current update has completed and immediately
- // begin the next update - since we know that the value the UI thread has extracted
- // is no longer the most recent one.
- if(oldValue == -1.0f) {
- if(this.progressUpdateAsyncResult != null) {
- EndInvoke(this.progressUpdateAsyncResult);
- }
-
- this.progressUpdateAsyncResult = BeginInvoke(this.updateProgressDelegate);
- }
-
- }
-
- /// Synchronously updates the value visualized in the progress bar
- private void updateProgress() {
-
- // Cache these to shorten the code that follows :)
- int minimum = base.Minimum;
- int maximum = base.Maximum;
-
- // Take out the most recent value that has been given to the asynchronous progress
- // bar up until now and replace it by -1. This enables the updater to see when
- // the update has actually been performed and whether it needs to start a new
- // invocation to ensure the most recent value will remain at the end.
- float progress = Interlocked.Exchange(ref this.newProgress, -1.0f);
-
- // Restrain the value to the progress bar's configured range and assign it.
- // This is done to prevent exceptions in the UI thread (theoretically the user
- // could change the progress bar's min and max just before the UI thread executes
- // this method, so we cannot validate the value in AsyncSetValue())
- int value = (int)(progress * (maximum - minimum)) + minimum;
- base.Value = Math.Min(Math.Max(value, minimum), maximum);
-
- }
-
- /// New progress being assigned to the progress bar
- private float newProgress;
- /// Delegate for the progress update method
- private MethodInvoker updateProgressDelegate;
- /// Async result for the invoked control state update method
- private volatile IAsyncResult progressUpdateAsyncResult;
-
- }
-
-} // namespace Nuclex.Windows.Forms
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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;
+using System.Windows.Forms;
+
+namespace Nuclex.Windows.Forms {
+
+ /// Progress bar with optimized multi-threading behavior
+ ///
+ ///
+ /// If a background thread is generating lots of progress updates, using synchronized
+ /// calls can drastically reduce performance. This progress bar optimizes that case
+ /// by performing the update asynchronously and keeping only the most recent update
+ /// when multiple updates arrive while the asynchronous update call is still running.
+ ///
+ ///
+ /// This design eliminates useless queueing of progress updates, thereby reducing
+ /// CPU load occuring in the UI thread and at the same time avoids blocking the
+ /// worker thread, increasing its performance.
+ ///
+ ///
+ public partial class AsyncProgressBar : ProgressBar {
+
+ /// Initializes a new asynchronous progress bar
+ public AsyncProgressBar() {
+ InitializeComponent();
+
+ this.Disposed += new EventHandler(progressBarDisposed);
+ this.updateProgressDelegate = new MethodInvoker(updateProgress);
+
+ // Could probably use VolatileWrite() as well, but for consistency reasons
+ // this is an Interlocked call, too. Mixing different synchronization measures
+ // for a variable raises a red flag whenever I see it :)
+ Interlocked.Exchange(ref this.newProgress, -1.0f);
+ }
+
+ /// Called when the progress bar is being disposed
+ /// Progress bar that is being disposed
+ /// Not used
+ private void progressBarDisposed(object sender, EventArgs arguments) {
+
+ // CHECK: This method is only called on an explicit Dispose() of the control.
+ // It is legal to call Control.BeginInvoke() without calling Control.EndInvoke(),
+ // so the code is quite correct even if no Dispose() occurs, but is it also clean?
+ // http://www.interact-sw.co.uk/iangblog/2005/05/16/endinvokerequired
+
+ // Since this has to occur in the UI thread, there's no way that updateProgress()
+ // could be executing just now. But the final call to updateProgress() will not
+ // have EndInvoke() called on it yet, so we do this here before the control
+ // is finally disposed.
+ if(this.progressUpdateAsyncResult != null) {
+ EndInvoke(this.progressUpdateAsyncResult);
+ this.progressUpdateAsyncResult = null;
+ }
+
+ }
+
+ /// Asynchronously updates the value to be shown in the progress bar
+ /// New value to set the progress bar to
+ ///
+ /// This will schedule an asynchronous update of the progress bar in the UI thread.
+ /// If you change the progress value again before the progress bar has completed its
+ /// update cycle, the original progress value will be skipped and the progress bar
+ /// jumps directly to the latest progress value. Updates are not queued, there is
+ /// at most one update waiting on the UI thread. It is also strictly guaranteed that
+ /// the last most progress value set will be shown and never skipped.
+ ///
+ public void AsyncSetValue(float value) {
+
+ // Update the value to be shown on the progress bar. If this happens multiple
+ // times, that's not a problem, the progress bar updates as fast as it can
+ // and always tries to show the most recent value assigned.
+ float oldValue = Interlocked.Exchange(ref this.newProgress, value);
+
+ // If the previous value was -1, the UI thread has already taken out the most recent
+ // value and assigned it (or is about to assign it) to the progress bar control.
+ // In this case, we'll wait until the current update has completed and immediately
+ // begin the next update - since we know that the value the UI thread has extracted
+ // is no longer the most recent one.
+ if(oldValue == -1.0f) {
+ if(this.progressUpdateAsyncResult != null) {
+ EndInvoke(this.progressUpdateAsyncResult);
+ }
+
+ this.progressUpdateAsyncResult = BeginInvoke(this.updateProgressDelegate);
+ }
+
+ }
+
+ /// Synchronously updates the value visualized in the progress bar
+ private void updateProgress() {
+
+ // Cache these to shorten the code that follows :)
+ int minimum = base.Minimum;
+ int maximum = base.Maximum;
+
+ // Take out the most recent value that has been given to the asynchronous progress
+ // bar up until now and replace it by -1. This enables the updater to see when
+ // the update has actually been performed and whether it needs to start a new
+ // invocation to ensure the most recent value will remain at the end.
+ float progress = Interlocked.Exchange(ref this.newProgress, -1.0f);
+
+ // Restrain the value to the progress bar's configured range and assign it.
+ // This is done to prevent exceptions in the UI thread (theoretically the user
+ // could change the progress bar's min and max just before the UI thread executes
+ // this method, so we cannot validate the value in AsyncSetValue())
+ int value = (int)(progress * (maximum - minimum)) + minimum;
+ base.Value = Math.Min(Math.Max(value, minimum), maximum);
+
+ }
+
+ /// New progress being assigned to the progress bar
+ private float newProgress;
+ /// Delegate for the progress update method
+ private MethodInvoker updateProgressDelegate;
+ /// Async result for the invoked control state update method
+ private volatile IAsyncResult progressUpdateAsyncResult;
+
+ }
+
+} // namespace Nuclex.Windows.Forms
diff --git a/Source/AutoBinding/ConventionBinder.cs b/Source/AutoBinding/ConventionBinder.cs
index 2faab8f..ef54ff8 100644
--- a/Source/AutoBinding/ConventionBinder.cs
+++ b/Source/AutoBinding/ConventionBinder.cs
@@ -1,47 +1,66 @@
-using Nuclex.Windows.Forms.Views;
-using System;
-using System.Windows.Forms;
-
-namespace Nuclex.Windows.Forms.AutoBinding {
-
- ///
- /// Binds a view to its model using a convention-over-configuration approach
- ///
- public class ConventionBinder : IAutoBinder {
-
- /// Binds the specified view to an explicitly selected view model
- ///
- /// Type of view model the view will be bound to
- ///
- /// View that will be bound to a view model
- /// View model the view will be bound to
- public void Bind(Control view, TViewModel viewModel)
- where TViewModel : class {
- bind(view, viewModel);
- }
-
- ///
- /// Binds the specified view to the view model specified in its DataContext
- ///
- /// View that will be bound
- public void Bind(Control viewControl) {
- IView viewControlAsView = viewControl as IView;
- if(viewControlAsView == null) {
- throw new InvalidOperationException(
- "The specified view has no view model associated. Either assign your " +
- "view model to the view's data context beforehand or use the overload " +
- "of Bind() that allows you to explicitly specify the view model."
- );
- }
-
- bind(viewControl, viewControlAsView.DataContext);
- }
-
- /// Binds a view to a view model
- /// View that will be bound
- /// View model the view will be bound to
- private void bind(Control view, object viewModel) {
- }
-
- }
-} // namespace Nuclex.Windows.Forms.AutoBinding
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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 Nuclex.Windows.Forms.Views;
+using System;
+using System.Windows.Forms;
+
+namespace Nuclex.Windows.Forms.AutoBinding {
+
+ ///
+ /// Binds a view to its model using a convention-over-configuration approach
+ ///
+ public class ConventionBinder : IAutoBinder {
+
+ /// Binds the specified view to an explicitly selected view model
+ ///
+ /// Type of view model the view will be bound to
+ ///
+ /// View that will be bound to a view model
+ /// View model the view will be bound to
+ public void Bind(Control view, TViewModel viewModel)
+ where TViewModel : class {
+ bind(view, viewModel);
+ }
+
+ ///
+ /// Binds the specified view to the view model specified in its DataContext
+ ///
+ /// View that will be bound
+ public void Bind(Control viewControl) {
+ IView viewControlAsView = viewControl as IView;
+ if(viewControlAsView == null) {
+ throw new InvalidOperationException(
+ "The specified view has no view model associated. Either assign your " +
+ "view model to the view's data context beforehand or use the overload " +
+ "of Bind() that allows you to explicitly specify the view model."
+ );
+ }
+
+ bind(viewControl, viewControlAsView.DataContext);
+ }
+
+ /// Binds a view to a view model
+ /// View that will be bound
+ /// View model the view will be bound to
+ private void bind(Control view, object viewModel) {
+ }
+
+ }
+} // namespace Nuclex.Windows.Forms.AutoBinding
diff --git a/Source/AutoBinding/IAutoBinder.cs b/Source/AutoBinding/IAutoBinder.cs
index 9220ea2..2a891e6 100644
--- a/Source/AutoBinding/IAutoBinder.cs
+++ b/Source/AutoBinding/IAutoBinder.cs
@@ -1,28 +1,47 @@
-using System;
-
-using System.Windows.Forms;
-using Nuclex.Windows.Forms.Views;
-
-namespace Nuclex.Windows.Forms.AutoBinding {
-
- /// Binds views to their view models
- public interface IAutoBinder {
-
- /// Binds the specified view to an explicitly selected view model
- ///
- /// Type of view model the view will be bound to
- ///
- /// View that will be bound to a view model
- /// View model the view will be bound to
- void Bind(Control view, TViewModel viewModel)
- where TViewModel : class;
-
- ///
- /// Binds the specified view to the view model specified in its DataContext
- ///
- /// View that will be bound
- void Bind(Control view);
-
- }
-
-} // namespace Nuclex.Windows.Forms.AutoBinding
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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.Windows.Forms;
+using Nuclex.Windows.Forms.Views;
+
+namespace Nuclex.Windows.Forms.AutoBinding {
+
+ /// Binds views to their view models
+ public interface IAutoBinder {
+
+ /// Binds the specified view to an explicitly selected view model
+ ///
+ /// Type of view model the view will be bound to
+ ///
+ /// View that will be bound to a view model
+ /// View model the view will be bound to
+ void Bind(Control view, TViewModel viewModel)
+ where TViewModel : class;
+
+ ///
+ /// Binds the specified view to the view model specified in its DataContext
+ ///
+ /// View that will be bound
+ void Bind(Control view);
+
+ }
+
+} // namespace Nuclex.Windows.Forms.AutoBinding
diff --git a/Source/CommonDialogs/CommonDialogManager.cs b/Source/CommonDialogs/CommonDialogManager.cs
index 3d50d78..4bd4027 100644
--- a/Source/CommonDialogs/CommonDialogManager.cs
+++ b/Source/CommonDialogs/CommonDialogManager.cs
@@ -1,187 +1,186 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-using System;
-using System.Text;
-using System.Windows.Forms;
-
-namespace Nuclex.Windows.Forms.CommonDialogs {
-
- /// Displays common dialogs for selecting files and directories
- public class CommonDialogManager : ICommonDialogService {
-
- /// Initializes a new task dialog message service
- public CommonDialogManager() : this(NullActiveWindowTracker.Default) { }
-
- /// Initializes a new task dialog message service
- ///
- /// Active window tracker used to obtain the parent window for message boxes
- ///
- public CommonDialogManager(IActiveWindowTracker tracker) {
- this.tracker = tracker;
- }
-
- /// Asks the user for a location to save a file under
- /// Caption of the dialog
- ///
- /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
- ///
- /// The full path of the file the user wishes to save as
- public string AskForSaveLocation(string caption, params string[] masks) {
- var saveDialog = new SaveFileDialog() {
- Title = caption,
- Filter = combineMasks(masks)
- };
-
- DialogResult result;
- {
- Form activeWindow = this.tracker.ActiveWindow;
- if(activeWindow == null) {
- result = saveDialog.ShowDialog();
- } else {
- result = saveDialog.ShowDialog(activeWindow);
- }
- }
-
- if(result == DialogResult.OK) {
- return saveDialog.FileName;
- } else {
- return null;
- }
- }
-
- /// Asks the user to select a file to open
- /// Caption of the dialog
- ///
- /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
- ///
- /// The full path of the file the user selected
- public string AskForFileToOpen(string caption, params string[] masks) {
- var openDialog = new OpenFileDialog() {
- Title = caption,
- Filter = combineMasks(masks),
- CheckFileExists = true,
- CheckPathExists = true,
- Multiselect = false
- };
-
- DialogResult result;
- {
- Form activeWindow = this.tracker.ActiveWindow;
- if(activeWindow == null) {
- result = openDialog.ShowDialog();
- } else {
- result = openDialog.ShowDialog(activeWindow);
- }
- }
-
- if(result == DialogResult.OK) {
- return openDialog.FileName;
- } else {
- return null;
- }
- }
-
- /// Asks the user to select one or more files to open
- /// Caption of the dialog
- ///
- /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
- ///
- /// The full path of all files the user selected
- public string[] AskForFilesToOpen(string caption, params string[] masks) {
- var openDialog = new OpenFileDialog() {
- Title = caption,
- Filter = combineMasks(masks),
- CheckFileExists = true,
- CheckPathExists = true,
- Multiselect = true
- };
-
- DialogResult result;
- {
- Form activeWindow = this.tracker.ActiveWindow;
- if(activeWindow == null) {
- result = openDialog.ShowDialog();
- } else {
- result = openDialog.ShowDialog(activeWindow);
- }
- }
-
- if(result == DialogResult.OK) {
- return openDialog.FileNames;
- } else {
- return null;
- }
- }
-
- /// Asks the user to select a directory
- /// The directory the user has selected
- public string AskForDirectory(string caption) {
- var folderDialog = new System.Windows.Forms.FolderBrowserDialog() {
- Description = caption
- };
-
- DialogResult result;
- {
- Form activeWindow = this.tracker.ActiveWindow;
- if(activeWindow == null) {
- result = folderDialog.ShowDialog();
- } else {
- result = folderDialog.ShowDialog(activeWindow);
- }
- }
-
- if(result == DialogResult.OK) {
- return folderDialog.SelectedPath;
- } else {
- return null;
- }
- }
-
- /// Combines an array of file masks into a single mask
- /// Masks that will be combined
- /// That combined masks
- private static string combineMasks(string[] masks) {
- if((masks == null) || (masks.Length == 0)) {
- return null;
- }
-
- int requiredCapacity = 0;
- for(int index = 0; index < masks.Length; ++index) {
- requiredCapacity += masks[index].Length;
- requiredCapacity += 1;
- }
-
- var maskBuilder = new StringBuilder(requiredCapacity);
- maskBuilder.Append(masks[0]);
- for(int index = 1; index < masks.Length; ++index) {
- maskBuilder.Append('|');
- maskBuilder.Append(masks[index]);
- }
-
- return maskBuilder.ToString();
- }
-
- /// Provides the currently active top-level window
- private IActiveWindowTracker tracker;
-
- }
-
-} // namespace Nuclex.Windows.Forms.CommonDialogs
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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.Text;
+using System.Windows.Forms;
+
+namespace Nuclex.Windows.Forms.CommonDialogs {
+
+ /// Displays common dialogs for selecting files and directories
+ public class CommonDialogManager : ICommonDialogService {
+
+ /// Initializes a new task dialog message service
+ public CommonDialogManager() : this(NullActiveWindowTracker.Default) { }
+
+ /// Initializes a new task dialog message service
+ ///
+ /// Active window tracker used to obtain the parent window for message boxes
+ ///
+ public CommonDialogManager(IActiveWindowTracker tracker) {
+ this.tracker = tracker;
+ }
+
+ /// Asks the user for a location to save a file under
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of the file the user wishes to save as
+ public string AskForSaveLocation(string caption, params string[] masks) {
+ var saveDialog = new SaveFileDialog() {
+ Title = caption,
+ Filter = combineMasks(masks)
+ };
+
+ DialogResult result;
+ {
+ Form activeWindow = this.tracker.ActiveWindow;
+ if(activeWindow == null) {
+ result = saveDialog.ShowDialog();
+ } else {
+ result = saveDialog.ShowDialog(activeWindow);
+ }
+ }
+
+ if(result == DialogResult.OK) {
+ return saveDialog.FileName;
+ } else {
+ return null;
+ }
+ }
+
+ /// Asks the user to select a file to open
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of the file the user selected
+ public string AskForFileToOpen(string caption, params string[] masks) {
+ var openDialog = new OpenFileDialog() {
+ Title = caption,
+ Filter = combineMasks(masks),
+ CheckFileExists = true,
+ CheckPathExists = true,
+ Multiselect = false
+ };
+
+ DialogResult result;
+ {
+ Form activeWindow = this.tracker.ActiveWindow;
+ if(activeWindow == null) {
+ result = openDialog.ShowDialog();
+ } else {
+ result = openDialog.ShowDialog(activeWindow);
+ }
+ }
+
+ if(result == DialogResult.OK) {
+ return openDialog.FileName;
+ } else {
+ return null;
+ }
+ }
+
+ /// Asks the user to select one or more files to open
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of all files the user selected
+ public string[] AskForFilesToOpen(string caption, params string[] masks) {
+ var openDialog = new OpenFileDialog() {
+ Title = caption,
+ Filter = combineMasks(masks),
+ CheckFileExists = true,
+ CheckPathExists = true,
+ Multiselect = true
+ };
+
+ DialogResult result;
+ {
+ Form activeWindow = this.tracker.ActiveWindow;
+ if(activeWindow == null) {
+ result = openDialog.ShowDialog();
+ } else {
+ result = openDialog.ShowDialog(activeWindow);
+ }
+ }
+
+ if(result == DialogResult.OK) {
+ return openDialog.FileNames;
+ } else {
+ return null;
+ }
+ }
+
+ /// Asks the user to select a directory
+ /// The directory the user has selected
+ public string AskForDirectory(string caption) {
+ var folderDialog = new System.Windows.Forms.FolderBrowserDialog() {
+ Description = caption
+ };
+
+ DialogResult result;
+ {
+ Form activeWindow = this.tracker.ActiveWindow;
+ if(activeWindow == null) {
+ result = folderDialog.ShowDialog();
+ } else {
+ result = folderDialog.ShowDialog(activeWindow);
+ }
+ }
+
+ if(result == DialogResult.OK) {
+ return folderDialog.SelectedPath;
+ } else {
+ return null;
+ }
+ }
+
+ /// Combines an array of file masks into a single mask
+ /// Masks that will be combined
+ /// That combined masks
+ private static string combineMasks(string[] masks) {
+ if((masks == null) || (masks.Length == 0)) {
+ return null;
+ }
+
+ int requiredCapacity = 0;
+ for(int index = 0; index < masks.Length; ++index) {
+ requiredCapacity += masks[index].Length;
+ requiredCapacity += 1;
+ }
+
+ var maskBuilder = new StringBuilder(requiredCapacity);
+ maskBuilder.Append(masks[0]);
+ for(int index = 1; index < masks.Length; ++index) {
+ maskBuilder.Append('|');
+ maskBuilder.Append(masks[index]);
+ }
+
+ return maskBuilder.ToString();
+ }
+
+ /// Provides the currently active top-level window
+ private IActiveWindowTracker tracker;
+
+ }
+
+} // namespace Nuclex.Windows.Forms.CommonDialogs
diff --git a/Source/CommonDialogs/ICommonDialogService.cs b/Source/CommonDialogs/ICommonDialogService.cs
index 6f94b26..d5772e8 100644
--- a/Source/CommonDialogs/ICommonDialogService.cs
+++ b/Source/CommonDialogs/ICommonDialogService.cs
@@ -1,58 +1,57 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-using System;
-
-namespace Nuclex.Windows.Forms.CommonDialogs {
-
- /// Displays common dialogs for selecting files and directories
- public interface ICommonDialogService {
-
- /// Asks the user for a location to save a file under
- /// Caption of the dialog
- ///
- /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
- ///
- /// The full path of the file the user wishes to save as
- string AskForSaveLocation(string caption, params string[] masks);
-
- /// Asks the user to select a file to open
- /// Caption of the dialog
- ///
- /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
- ///
- /// The full path of the file the user selected
- string AskForFileToOpen(string caption, params string[] masks);
-
- /// Asks the user to select one or more files to open
- /// Caption of the dialog
- ///
- /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
- ///
- /// The full path of all files the user selected
- string[] AskForFilesToOpen(string caption, params string[] masks);
-
- /// Asks the user to select a directory
- /// The directory the user has selected
- string AskForDirectory(string caption);
-
- }
-
-} // namespace Nuclex.Windows.Forms.CommonDialogs
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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;
+
+namespace Nuclex.Windows.Forms.CommonDialogs {
+
+ /// Displays common dialogs for selecting files and directories
+ public interface ICommonDialogService {
+
+ /// Asks the user for a location to save a file under
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of the file the user wishes to save as
+ string AskForSaveLocation(string caption, params string[] masks);
+
+ /// Asks the user to select a file to open
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of the file the user selected
+ string AskForFileToOpen(string caption, params string[] masks);
+
+ /// Asks the user to select one or more files to open
+ /// Caption of the dialog
+ ///
+ /// File masks in the form "Description|*.dat" or "Description2|*.da2;*.da3"
+ ///
+ /// The full path of all files the user selected
+ string[] AskForFilesToOpen(string caption, params string[] masks);
+
+ /// Asks the user to select a directory
+ /// The directory the user has selected
+ string AskForDirectory(string caption);
+
+ }
+
+} // namespace Nuclex.Windows.Forms.CommonDialogs
diff --git a/Source/ContainerListView/ContainerListView.Designer.cs b/Source/ContainerListView/ContainerListView.Designer.cs
index a39e339..d3aef6a 100644
--- a/Source/ContainerListView/ContainerListView.Designer.cs
+++ b/Source/ContainerListView/ContainerListView.Designer.cs
@@ -1,53 +1,52 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-namespace Nuclex.Windows.Forms {
-
- partial class ContainerListView {
-
- /// Required designer variable.
- private System.ComponentModel.IContainer components = null;
-
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- ///
- protected override void Dispose(bool disposing) {
- if(disposing && (components != null)) {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Component Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent() {
- components = new System.ComponentModel.Container();
- }
-
- #endregion
-
- }
-
-} // namespace Nuclex.Windows.Forms
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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
+
+namespace Nuclex.Windows.Forms {
+
+ partial class ContainerListView {
+
+ /// Required designer variable.
+ private System.ComponentModel.IContainer components = null;
+
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ ///
+ protected override void Dispose(bool disposing) {
+ if(disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ components = new System.ComponentModel.Container();
+ }
+
+ #endregion
+
+ }
+
+} // namespace Nuclex.Windows.Forms
diff --git a/Source/ContainerListView/ContainerListView.Test.cs b/Source/ContainerListView/ContainerListView.Test.cs
index 6867ff0..0f031cd 100644
--- a/Source/ContainerListView/ContainerListView.Test.cs
+++ b/Source/ContainerListView/ContainerListView.Test.cs
@@ -1,83 +1,82 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-#if UNITTEST
-
-using System;
-using System.IO;
-using System.Windows.Forms;
-
-using NUnit.Framework;
-
-using Nuclex.Support;
-
-namespace Nuclex.Windows.Forms {
-
- /// Unit Test for the control container list view
- [TestFixture, Explicit]
- public class ContainerListViewTest {
-
- ///
- /// Verifies that the asynchronous progress bar's constructor is working
- ///
- [Test]
- public void TestConstructor() {
- using(ContainerListView listView = new ContainerListView()) {
-
- // Let the control create its window handle
- listView.CreateControl();
- listView.Columns.Add("Numeric");
- listView.Columns.Add("Spelled");
- listView.Columns.Add("Nonsense");
-
- addRow(listView, "1", "One");
- addRow(listView, "2", "Two");
- addRow(listView, "3", "Three");
-
- using(CheckBox checkBox = new CheckBox()) {
- listView.EmbeddedControls.Add(new ListViewEmbeddedControl(checkBox, 2, 0));
- listView.EmbeddedControls.Clear();
-
- listView.Refresh();
-
- ListViewEmbeddedControl embeddedControl = new ListViewEmbeddedControl(
- checkBox, 2, 0
- );
- listView.EmbeddedControls.Add(embeddedControl);
- listView.EmbeddedControls.Remove(embeddedControl);
-
- listView.Refresh();
- }
-
- }
- }
-
- /// Adds a row to a control container list view
- /// List view control the row will be added to
- /// Values that will appear in the individual columns
- private void addRow(ContainerListView listView, params string[] columns) {
- listView.Items.Add(new ListViewItem(columns));
- }
-
- }
-
-} // namespace Nuclex.Windows.Forms
-
-#endif // UNITTEST
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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
+
+#if UNITTEST
+
+using System;
+using System.IO;
+using System.Windows.Forms;
+
+using NUnit.Framework;
+
+using Nuclex.Support;
+
+namespace Nuclex.Windows.Forms {
+
+ /// Unit Test for the control container list view
+ [TestFixture, Explicit]
+ public class ContainerListViewTest {
+
+ ///
+ /// Verifies that the asynchronous progress bar's constructor is working
+ ///
+ [Test]
+ public void TestConstructor() {
+ using(ContainerListView listView = new ContainerListView()) {
+
+ // Let the control create its window handle
+ listView.CreateControl();
+ listView.Columns.Add("Numeric");
+ listView.Columns.Add("Spelled");
+ listView.Columns.Add("Nonsense");
+
+ addRow(listView, "1", "One");
+ addRow(listView, "2", "Two");
+ addRow(listView, "3", "Three");
+
+ using(CheckBox checkBox = new CheckBox()) {
+ listView.EmbeddedControls.Add(new ListViewEmbeddedControl(checkBox, 2, 0));
+ listView.EmbeddedControls.Clear();
+
+ listView.Refresh();
+
+ ListViewEmbeddedControl embeddedControl = new ListViewEmbeddedControl(
+ checkBox, 2, 0
+ );
+ listView.EmbeddedControls.Add(embeddedControl);
+ listView.EmbeddedControls.Remove(embeddedControl);
+
+ listView.Refresh();
+ }
+
+ }
+ }
+
+ /// Adds a row to a control container list view
+ /// List view control the row will be added to
+ /// Values that will appear in the individual columns
+ private void addRow(ContainerListView listView, params string[] columns) {
+ listView.Items.Add(new ListViewItem(columns));
+ }
+
+ }
+
+} // namespace Nuclex.Windows.Forms
+
+#endif // UNITTEST
diff --git a/Source/ContainerListView/ContainerListView.cs b/Source/ContainerListView/ContainerListView.cs
index efb14da..0f8ea89 100644
--- a/Source/ContainerListView/ContainerListView.cs
+++ b/Source/ContainerListView/ContainerListView.cs
@@ -1,247 +1,246 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Drawing;
-using System.Data;
-using System.Text;
-using System.Windows.Forms;
-using System.Runtime.InteropServices;
-
-using Nuclex.Support.Collections;
-
-namespace Nuclex.Windows.Forms {
-
- /// ListView allowing for other controls to be embedded in its cells
- ///
- ///
- /// There basically were two possible design choices: Provide a specialized
- /// ListViewSubItem that carries a Control instead of a string or manage the
- /// embedded controls seperate of the ListView's items.
- ///
- ///
- /// The first option requires a complete rewrite of the ListViewItem class
- /// and its related support classes, all of which are surprisingly large and
- /// complex. Thus, I chose the less clean but more doable latter option.
- ///
- ///
- /// This control is useful for simple item lists where you want to provide
- /// a combobox, checkbox or other control to the user for a certain column.
- /// It will not perform well for lists with hundreds of items since it
- /// requires a control to be created per row and management of the embedded
- /// controls is designed for limited usage.
- ///
- ///
- public partial class ContainerListView : System.Windows.Forms.ListView {
-
- /// Message sent to a control to let it paint itself
- private const int WM_PAINT = 0x000F;
-
- /// Initializes a new ContainerListView
- public ContainerListView() {
- this.embeddedControlClickedDelegate = new EventHandler(embeddedControlClicked);
-
- this.embeddedControls = new ObservableList();
- this.embeddedControls.ItemAdded +=
- new EventHandler>(embeddedControlAdded);
- this.embeddedControls.ItemRemoved +=
- new EventHandler>(embeddedControlRemoved);
- this.embeddedControls.Clearing += new EventHandler(embeddedControlsClearing);
-
- InitializeComponent();
-
- // Eliminate flickering
- SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
- SetStyle(ControlStyles.AllPaintingInWmPaint, true);
-
- base.View = View.Details;
-
- this.columnHeaderHeight = Font.Height;
- }
-
- /// Controls being embedded in the ListView
- public ICollection EmbeddedControls {
- get { return this.embeddedControls; }
- }
-
- /// Updates the controls embeded into the list view
- public void UpdateEmbeddedControls() {
- if(View != View.Details) {
- for(int index = 0; index < this.embeddedControls.Count; ++index) {
- this.embeddedControls[index].Control.Visible = false;
- }
- } else {
- for(int index = 0; index < this.embeddedControls.Count; ++index) {
- ListViewEmbeddedControl embeddedControl = this.embeddedControls[index];
-
- Rectangle cellBounds = this.GetSubItemBounds(
- Items[embeddedControl.Row], embeddedControl.Column
- );
-
- bool intersectsColumnHeader =
- (base.HeaderStyle != ColumnHeaderStyle.None) &&
- (cellBounds.Top < base.Font.Height);
-
- embeddedControl.Control.Visible = !intersectsColumnHeader;
- embeddedControl.Control.Bounds = cellBounds;
- }
- }
- }
-
- /// Calculates the boundaries of a cell in the list view
- /// Item in the list view from which to calculate the cell
- /// Index der cell whose boundaries to calculate
- /// The boundaries of the specified list view cell
- ///
- /// When the specified sub item index is not in the range of valid sub items
- ///
- protected Rectangle GetSubItemBounds(ListViewItem item, int subItem) {
- int[] order = GetColumnOrder();
- if(order == null) { // No Columns
- return Rectangle.Empty;
- }
-
- if(subItem >= order.Length) {
- throw new IndexOutOfRangeException("SubItem " + subItem + " out of range");
- }
-
- // Determine the border of the entire ListViewItem, including all sub items
- Rectangle itemBounds = item.GetBounds(ItemBoundsPortion.Entire);
- int subItemX = itemBounds.Left;
-
- // Find the horizontal position of the sub item. Because the column order can vary,
- // we need to use Columns[order[i]] instead of simply doing Columns[i] here!
- ColumnHeader columnHeader;
- int i;
- for(i = 0; i < order.Length; ++i) {
- columnHeader = this.Columns[order[i]];
- if(columnHeader.Index == subItem) {
- break;
- }
-
- subItemX += columnHeader.Width;
- }
-
- return new Rectangle(
- subItemX, itemBounds.Top, this.Columns[order[i]].Width, itemBounds.Height
- );
- }
-
- /// Responds to window messages sent by the operating system
- /// Window message that will be processed
- protected override void WndProc(ref Message message) {
- switch(message.Msg) {
- case WM_PAINT: {
- UpdateEmbeddedControls();
- break;
- }
- }
-
- base.WndProc(ref message);
- }
-
- /// Called when the list of embedded controls has been cleared
- /// Collection that has been cleared of its controls
- /// Not used
- private void embeddedControlsClearing(object sender, EventArgs arguments) {
- this.BeginUpdate();
- try {
- foreach(ListViewEmbeddedControl embeddedControl in this.embeddedControls) {
- embeddedControl.Control.Click -= this.embeddedControlClickedDelegate;
- this.Controls.Remove(embeddedControl.Control);
- }
- }
- finally {
- this.EndUpdate();
- }
- }
-
- /// Called when a control gets removed from the embedded controls list
- /// List from which the control has been removed
- ///
- /// Event arguments providing a reference to the removed control
- ///
- private void embeddedControlAdded(
- object sender, ItemEventArgs arguments
- ) {
- arguments.Item.Control.Click += this.embeddedControlClickedDelegate;
- this.Controls.Add(arguments.Item.Control);
- }
-
- /// Called when a control gets added to the embedded controls list
- /// List to which the control has been added
- ///
- /// Event arguments providing a reference to the added control
- ///
- private void embeddedControlRemoved(
- object sender, ItemEventArgs arguments
- ) {
- if(this.Controls.Contains(arguments.Item.Control)) {
- arguments.Item.Control.Click -= this.embeddedControlClickedDelegate;
- this.Controls.Remove(arguments.Item.Control);
- }
- }
-
- /// Called when an embedded control has been clicked on
- /// Embedded control that has been clicked
- /// Not used
- private void embeddedControlClicked(object sender, EventArgs arguments) {
- this.BeginUpdate();
-
- try {
- SelectedItems.Clear();
-
- foreach(ListViewEmbeddedControl embeddedControl in this.embeddedControls) {
- if(ReferenceEquals(embeddedControl.Control, sender)) {
- if((embeddedControl.Row > 0) && (embeddedControl.Row < Items.Count)) {
- Items[embeddedControl.Row].Selected = true;
- }
- }
- }
- }
- finally {
- this.EndUpdate();
- }
- }
-
- /// Obtains the current column order of the list
- /// An array indicating the order of the list's columns
- private int[] GetColumnOrder() {
- int[] order = new int[this.Columns.Count];
-
- for(int index = 0; index < this.Columns.Count; ++index) {
- order[this.Columns[index].DisplayIndex] = index;
- }
-
- return order;
- }
-
- /// Height of the list view's column header
- private int columnHeaderHeight;
- /// Event handler for when embedded controls are clicked on
- private EventHandler embeddedControlClickedDelegate;
- /// Controls being embedded in this ListView
- private ObservableList embeddedControls;
-
- }
-
-} // namespace Nuclex.Windows.Forms
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Text;
+using System.Windows.Forms;
+using System.Runtime.InteropServices;
+
+using Nuclex.Support.Collections;
+
+namespace Nuclex.Windows.Forms {
+
+ /// ListView allowing for other controls to be embedded in its cells
+ ///
+ ///
+ /// There basically were two possible design choices: Provide a specialized
+ /// ListViewSubItem that carries a Control instead of a string or manage the
+ /// embedded controls seperate of the ListView's items.
+ ///
+ ///
+ /// The first option requires a complete rewrite of the ListViewItem class
+ /// and its related support classes, all of which are surprisingly large and
+ /// complex. Thus, I chose the less clean but more doable latter option.
+ ///
+ ///
+ /// This control is useful for simple item lists where you want to provide
+ /// a combobox, checkbox or other control to the user for a certain column.
+ /// It will not perform well for lists with hundreds of items since it
+ /// requires a control to be created per row and management of the embedded
+ /// controls is designed for limited usage.
+ ///
+ ///
+ public partial class ContainerListView : System.Windows.Forms.ListView {
+
+ /// Message sent to a control to let it paint itself
+ private const int WM_PAINT = 0x000F;
+
+ /// Initializes a new ContainerListView
+ public ContainerListView() {
+ this.embeddedControlClickedDelegate = new EventHandler(embeddedControlClicked);
+
+ this.embeddedControls = new ObservableList();
+ this.embeddedControls.ItemAdded +=
+ new EventHandler>(embeddedControlAdded);
+ this.embeddedControls.ItemRemoved +=
+ new EventHandler>(embeddedControlRemoved);
+ this.embeddedControls.Clearing += new EventHandler(embeddedControlsClearing);
+
+ InitializeComponent();
+
+ // Eliminate flickering
+ SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
+ SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+
+ base.View = View.Details;
+
+ this.columnHeaderHeight = Font.Height;
+ }
+
+ /// Controls being embedded in the ListView
+ public ICollection EmbeddedControls {
+ get { return this.embeddedControls; }
+ }
+
+ /// Updates the controls embeded into the list view
+ public void UpdateEmbeddedControls() {
+ if(View != View.Details) {
+ for(int index = 0; index < this.embeddedControls.Count; ++index) {
+ this.embeddedControls[index].Control.Visible = false;
+ }
+ } else {
+ for(int index = 0; index < this.embeddedControls.Count; ++index) {
+ ListViewEmbeddedControl embeddedControl = this.embeddedControls[index];
+
+ Rectangle cellBounds = this.GetSubItemBounds(
+ Items[embeddedControl.Row], embeddedControl.Column
+ );
+
+ bool intersectsColumnHeader =
+ (base.HeaderStyle != ColumnHeaderStyle.None) &&
+ (cellBounds.Top < base.Font.Height);
+
+ embeddedControl.Control.Visible = !intersectsColumnHeader;
+ embeddedControl.Control.Bounds = cellBounds;
+ }
+ }
+ }
+
+ /// Calculates the boundaries of a cell in the list view
+ /// Item in the list view from which to calculate the cell
+ /// Index der cell whose boundaries to calculate
+ /// The boundaries of the specified list view cell
+ ///
+ /// When the specified sub item index is not in the range of valid sub items
+ ///
+ protected Rectangle GetSubItemBounds(ListViewItem item, int subItem) {
+ int[] order = GetColumnOrder();
+ if(order == null) { // No Columns
+ return Rectangle.Empty;
+ }
+
+ if(subItem >= order.Length) {
+ throw new IndexOutOfRangeException("SubItem " + subItem + " out of range");
+ }
+
+ // Determine the border of the entire ListViewItem, including all sub items
+ Rectangle itemBounds = item.GetBounds(ItemBoundsPortion.Entire);
+ int subItemX = itemBounds.Left;
+
+ // Find the horizontal position of the sub item. Because the column order can vary,
+ // we need to use Columns[order[i]] instead of simply doing Columns[i] here!
+ ColumnHeader columnHeader;
+ int i;
+ for(i = 0; i < order.Length; ++i) {
+ columnHeader = this.Columns[order[i]];
+ if(columnHeader.Index == subItem) {
+ break;
+ }
+
+ subItemX += columnHeader.Width;
+ }
+
+ return new Rectangle(
+ subItemX, itemBounds.Top, this.Columns[order[i]].Width, itemBounds.Height
+ );
+ }
+
+ /// Responds to window messages sent by the operating system
+ /// Window message that will be processed
+ protected override void WndProc(ref Message message) {
+ switch(message.Msg) {
+ case WM_PAINT: {
+ UpdateEmbeddedControls();
+ break;
+ }
+ }
+
+ base.WndProc(ref message);
+ }
+
+ /// Called when the list of embedded controls has been cleared
+ /// Collection that has been cleared of its controls
+ /// Not used
+ private void embeddedControlsClearing(object sender, EventArgs arguments) {
+ this.BeginUpdate();
+ try {
+ foreach(ListViewEmbeddedControl embeddedControl in this.embeddedControls) {
+ embeddedControl.Control.Click -= this.embeddedControlClickedDelegate;
+ this.Controls.Remove(embeddedControl.Control);
+ }
+ }
+ finally {
+ this.EndUpdate();
+ }
+ }
+
+ /// Called when a control gets removed from the embedded controls list
+ /// List from which the control has been removed
+ ///
+ /// Event arguments providing a reference to the removed control
+ ///
+ private void embeddedControlAdded(
+ object sender, ItemEventArgs arguments
+ ) {
+ arguments.Item.Control.Click += this.embeddedControlClickedDelegate;
+ this.Controls.Add(arguments.Item.Control);
+ }
+
+ /// Called when a control gets added to the embedded controls list
+ /// List to which the control has been added
+ ///
+ /// Event arguments providing a reference to the added control
+ ///
+ private void embeddedControlRemoved(
+ object sender, ItemEventArgs arguments
+ ) {
+ if(this.Controls.Contains(arguments.Item.Control)) {
+ arguments.Item.Control.Click -= this.embeddedControlClickedDelegate;
+ this.Controls.Remove(arguments.Item.Control);
+ }
+ }
+
+ /// Called when an embedded control has been clicked on
+ /// Embedded control that has been clicked
+ /// Not used
+ private void embeddedControlClicked(object sender, EventArgs arguments) {
+ this.BeginUpdate();
+
+ try {
+ SelectedItems.Clear();
+
+ foreach(ListViewEmbeddedControl embeddedControl in this.embeddedControls) {
+ if(ReferenceEquals(embeddedControl.Control, sender)) {
+ if((embeddedControl.Row > 0) && (embeddedControl.Row < Items.Count)) {
+ Items[embeddedControl.Row].Selected = true;
+ }
+ }
+ }
+ }
+ finally {
+ this.EndUpdate();
+ }
+ }
+
+ /// Obtains the current column order of the list
+ /// An array indicating the order of the list's columns
+ private int[] GetColumnOrder() {
+ int[] order = new int[this.Columns.Count];
+
+ for(int index = 0; index < this.Columns.Count; ++index) {
+ order[this.Columns[index].DisplayIndex] = index;
+ }
+
+ return order;
+ }
+
+ /// Height of the list view's column header
+ private int columnHeaderHeight;
+ /// Event handler for when embedded controls are clicked on
+ private EventHandler embeddedControlClickedDelegate;
+ /// Controls being embedded in this ListView
+ private ObservableList embeddedControls;
+
+ }
+
+} // namespace Nuclex.Windows.Forms
diff --git a/Source/ContainerListView/ListViewEmbeddedControl.cs b/Source/ContainerListView/ListViewEmbeddedControl.cs
index 1d0536b..6d7cd91 100644
--- a/Source/ContainerListView/ListViewEmbeddedControl.cs
+++ b/Source/ContainerListView/ListViewEmbeddedControl.cs
@@ -1,64 +1,63 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Windows.Forms;
-
-namespace Nuclex.Windows.Forms {
-
- /// Stores informations about an embedded control
- public class ListViewEmbeddedControl {
-
- /// Initializes a new embedded control holder
- /// Control being embedded in a list view
- /// List row at which the control will be embedded
- /// List column at which the control will be embedded
- public ListViewEmbeddedControl(Control control, int row, int column) {
- this.control = control;
- this.row = row;
- this.column = column;
- }
-
- /// Control that is being embedded in the ListView
- public Control Control {
- get { return this.control; }
- }
-
- /// Row the control has been embedded in
- public int Row {
- get { return this.row; }
- }
-
- /// Column the control has been embedded in
- public int Column {
- get { return this.column; }
- }
-
- /// Embedded control
- private Control control;
- /// Row where the control is embedded
- private int row;
- /// Column where the control is embedded
- private int column;
-
- }
-
-} // namespace Nuclex.Windows.Forms
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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.Collections.Generic;
+using System.Windows.Forms;
+
+namespace Nuclex.Windows.Forms {
+
+ /// Stores informations about an embedded control
+ public class ListViewEmbeddedControl {
+
+ /// Initializes a new embedded control holder
+ /// Control being embedded in a list view
+ /// List row at which the control will be embedded
+ /// List column at which the control will be embedded
+ public ListViewEmbeddedControl(Control control, int row, int column) {
+ this.control = control;
+ this.row = row;
+ this.column = column;
+ }
+
+ /// Control that is being embedded in the ListView
+ public Control Control {
+ get { return this.control; }
+ }
+
+ /// Row the control has been embedded in
+ public int Row {
+ get { return this.row; }
+ }
+
+ /// Column the control has been embedded in
+ public int Column {
+ get { return this.column; }
+ }
+
+ /// Embedded control
+ private Control control;
+ /// Row where the control is embedded
+ private int row;
+ /// Column where the control is embedded
+ private int column;
+
+ }
+
+} // namespace Nuclex.Windows.Forms
diff --git a/Source/Controls/ProgressSpinner.Designer.cs b/Source/Controls/ProgressSpinner.Designer.cs
index f295673..48c4694 100644
--- a/Source/Controls/ProgressSpinner.Designer.cs
+++ b/Source/Controls/ProgressSpinner.Designer.cs
@@ -1,49 +1,68 @@
-namespace Nuclex.Windows.Forms.Controls {
-
- partial class ProgressSpinner {
-
- /// Required designer variable.
- private System.ComponentModel.IContainer components = null;
-
- /// Clean up any resources being used.
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing) {
- if(disposing && (components != null)) {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Component Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent() {
- this.animationUpdateTimer = new System.Windows.Forms.Timer();
- this.SuspendLayout();
- //
- // animationUpdateTimer
- //
- this.animationUpdateTimer.Tick += new System.EventHandler(this.animationTimerTicked);
- //
- // ProgressSpinner
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.BackColor = System.Drawing.Color.Transparent;
- this.DoubleBuffered = true;
- this.Name = "ProgressSpinner";
- this.ResumeLayout(false);
-
- }
-
- #endregion
-
- /// Timer used to update the progress animation
- private System.Windows.Forms.Timer animationUpdateTimer;
-
- }
-
-} // namespace Nuclex.Windows.Forms.Controls
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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
+
+namespace Nuclex.Windows.Forms.Controls {
+
+ partial class ProgressSpinner {
+
+ /// Required designer variable.
+ private System.ComponentModel.IContainer components = null;
+
+ /// Clean up any resources being used.
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing) {
+ if(disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ this.animationUpdateTimer = new System.Windows.Forms.Timer();
+ this.SuspendLayout();
+ //
+ // animationUpdateTimer
+ //
+ this.animationUpdateTimer.Tick += new System.EventHandler(this.animationTimerTicked);
+ //
+ // ProgressSpinner
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.Color.Transparent;
+ this.DoubleBuffered = true;
+ this.Name = "ProgressSpinner";
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ /// Timer used to update the progress animation
+ private System.Windows.Forms.Timer animationUpdateTimer;
+
+ }
+
+} // namespace Nuclex.Windows.Forms.Controls
diff --git a/Source/Controls/ProgressSpinner.cs b/Source/Controls/ProgressSpinner.cs
index 967bc58..eb94280 100644
--- a/Source/Controls/ProgressSpinner.cs
+++ b/Source/Controls/ProgressSpinner.cs
@@ -1,297 +1,316 @@
-using System;
-using System.Data;
-using System.Drawing;
-using System.Drawing.Drawing2D;
-using System.Linq;
-using System.Windows.Forms;
-
-namespace Nuclex.Windows.Forms.Controls {
-
- /// Displays a progress spinner to entertain the user while waiting
- public partial class ProgressSpinner : UserControl {
-
- /// Number of dots the progress spinner will display
- private const int DotCount = 8;
- /// Size of a normal dot (only ever assumed by the trailing dot)
- private const int DotRadius = 4;
- ///
- /// The leading dot will be DotCount times this larger than a normal dot
- ///
- private const int ScaleFactor = 1;
-
- /// Initializes a new progress spinner
- public ProgressSpinner() {
- SetStyle(
- (
- ControlStyles.AllPaintingInWmPaint |
- ControlStyles.OptimizedDoubleBuffer |
- ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
- ControlStyles.SupportsTransparentBackColor
- ),
- true
- );
-
- InitializeComponent();
-
- Disposed += new EventHandler(OnDisposed);
-
- if(!DesignMode) {
- StartSpinner();
- }
- }
-
- /// Releases all resources owned by the control when it is destroyed
- /// Control that is being destroyed
- /// Not used
- private void OnDisposed(object sender, EventArgs arguments) {
- if(this.dotOutlinePen != null) {
- this.dotOutlinePen.Dispose();
- this.dotOutlinePen = null;
- }
- if(this.dotFillBrush != null) {
- this.dotFillBrush.Dispose();
- this.dotFillBrush = null;
- }
- }
-
- /// Starts the spinner's animation
- public void StartSpinner() {
- this.spinnerRunning = true;
- this.animationUpdateTimer.Enabled = true;
- }
-
- /// Stops the spinner's animation
- public void StopSpinner() {
- this.animationUpdateTimer.Enabled = false;
- this.spinnerRunning = false;
- }
-
- /// Color used to fill the dots
- public Color DotFillColor {
- get { return this.dotFillColor; }
- set {
- if(value != this.dotFillColor) {
- this.dotFillColor = value;
- if(this.dotFillBrush != null) {
- this.dotFillBrush.Dispose();
- this.dotFillBrush = null;
- }
- }
- }
- }
-
- /// Color used for the dots' outline
- public Color DotOutlineColor {
- get { return this.dotOutlineColor; }
- set {
- if(value != this.dotOutlineColor) {
- this.dotOutlineColor = value;
- if(this.dotOutlinePen != null) {
- this.dotOutlinePen.Dispose();
- this.dotOutlinePen = null;
- }
- }
- }
- }
-
- /// Calculates the optimal size for the spinner control
- /// The optimal size for the spinner control to have
- ///
- /// Thanks to WinForms limited control transparency, the progress spinner needs to
- /// redraw every control behind it each time it updates. Thus it's wise to keep it
- /// as small as possible, but wide enough to fit the status text, if any.
- ///
- public Size GetOptimalSize() {
- SizeF textRectangle;
- using(var dummyImage = new Bitmap(1, 1)) {
- using(Graphics graphics = Graphics.FromImage(dummyImage)) {
- textRectangle = graphics.MeasureString(
- this.statusText, this.statusFont
- );
- }
- }
-
- return new Size(
- Math.Max(128, (int)(textRectangle.Width + 2.0f)),
- this.statusFont.Height + 128
- );
- }
-
- /// Font that is used to display the status text
- public Font StatusFont {
- get { return this.statusFont; }
- set { this.statusFont = value; }
- }
-
- /// Text that will be displayed as the control's status
- public string StatusText {
- get { return this.statusText; }
- set { this.statusText = value; }
- }
-
- /// Called when the control is hidden or shown
- /// Not used
- protected override void OnVisibleChanged(EventArgs arguments) {
- base.OnVisibleChanged(arguments);
- this.animationUpdateTimer.Enabled = this.spinnerRunning && Visible;
- }
-
- /// Called when the control should redraw itself
- /// Provides access to the drawing surface and tools
- protected override void OnPaint(PaintEventArgs arguments) {
- paintControlsBehindMe(arguments);
- paintAnimatedDots(arguments);
- paintStatusMessage(arguments);
- }
-
- /// Forcefully redraws the controls below this one
- /// Provides access to the drawing surface and tools
- ///
- ///
- /// WinForms has very poor transparency support. A transparent control will only
- /// be transparent to its immediate parent (so the parent needs to be a container
- /// control and hold the transparent control as its preferrably only child).
- ///
- ///
- /// Worse yet, if you manually establish this relationship in your .Designer.cs
- /// file, the Visual Studio WinForms designer will dismantle it next time you
- /// edit something. This method fixes those issues by repainting all controls
- /// that are behind this control and whose bounding box intersect this control.
- ///
- ///
- private void paintControlsBehindMe(PaintEventArgs arguments) {
- if(Parent != null && this.BackColor == Color.Transparent) {
- using(var bmp = new Bitmap(Parent.Width, Parent.Height)) {
- Parent.Controls.Cast()
- .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
- .Where(c => c.Bounds.IntersectsWith(this.Bounds))
- .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
- .ToList()
- .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
-
- arguments.Graphics.DrawImage(bmp, -Left, -Top);
- }
- }
- }
-
- /// Draws a simple animated dots animation
- /// Provides access to the drawing surface and tools
- private void paintAnimatedDots(PaintEventArgs arguments) {
- if(this.dotOutlinePen == null) {
- this.dotOutlinePen = new Pen(this.dotOutlineColor);
- }
- if(this.dotFillBrush == null) {
- this.dotFillBrush = new SolidBrush(this.dotFillColor);
- }
-
- SmoothingMode prevousSmoothingMode = arguments.Graphics.SmoothingMode;
- arguments.Graphics.SmoothingMode = SmoothingMode.HighQuality;
- try {
- PointF center = new PointF(Width / 2.0f, (Height - this.statusFont.Height - 2) / 2.0f);
-
- int diameter = Math.Min(Width, Height - this.statusFont.Height - 2);
- int bigRadius = diameter / 2 - DotRadius - (DotCount - 1) * ScaleFactor;
-
- // Draw the dots
- float unitAngle = 360.0f / DotCount;
- for(int index = 0; index < DotCount; ++index) {
- int dotIndex = (index + leadingDotIndex) % DotCount;
-
- var dotPosition = new PointF(
- center.X + (float)(bigRadius * Math.Cos(unitAngle * dotIndex * Math.PI / 180.0f)),
- center.Y + (float)(bigRadius * Math.Sin(unitAngle * dotIndex * Math.PI / 180.0f))
- );
-
- int currentDotRadius = DotRadius + index * ScaleFactor;
-
- var corner = new PointF(
- dotPosition.X - currentDotRadius, dotPosition.Y - currentDotRadius
- );
- arguments.Graphics.FillEllipse(
- this.dotFillBrush, corner.X, corner.Y, 2 * currentDotRadius, 2 * currentDotRadius
- );
- arguments.Graphics.DrawEllipse(
- this.dotOutlinePen, corner.X, corner.Y, 2 * currentDotRadius, 2 * currentDotRadius
- );
- }
- }
- finally {
- arguments.Graphics.SmoothingMode = prevousSmoothingMode;
- }
- }
-
- /// Draws the status message under the animated dots
- /// Provides access to the drawing surface and tools
- private void paintStatusMessage(PaintEventArgs arguments) {
- if(!string.IsNullOrEmpty(this.statusText)) {
- SizeF textRectangle = arguments.Graphics.MeasureString(
- this.statusText, this.statusFont
- );
-
- var messageArea = new RectangleF(
- (Width - textRectangle.Width) / 2.0f,
- Height - this.statusFont.Height - 1.0f,
- textRectangle.Width,
- this.statusFont.Height
- );
-
- // Draw text with a white halo. This is a little bit ugly...
- {
- messageArea.Offset(-1.0f, 0.0f);
- arguments.Graphics.DrawString(
- this.statusText, this.statusFont, Brushes.White, messageArea
- );
-
- messageArea.Offset(2.0f, 0.0f);
- arguments.Graphics.DrawString(
- this.statusText, this.statusFont, Brushes.White, messageArea
- );
-
- messageArea.Offset(-1.0f, -1.0f);
- arguments.Graphics.DrawString(
- this.statusText, this.statusFont, Brushes.White, messageArea
- );
-
- messageArea.Offset(0.0f, 2.0f);
- arguments.Graphics.DrawString(
- this.statusText, this.statusFont, Brushes.White, messageArea
- );
-
- messageArea.Offset(0.0f, -1.0f);
- arguments.Graphics.DrawString(
- this.statusText, this.statusFont, this.dotFillBrush, messageArea
- );
- }
- }
- }
-
-
- /// Called when the animation timer ticks to update the animation state
- /// Animation timer that has ticked
- /// Not used
- private void animationTimerTicked(object sender, EventArgs arguments) {
- this.leadingDotIndex = (this.leadingDotIndex + 1) % DotCount; // Advance the animation
- Invalidate(); // Request a redraw at the earliest opportune time
- }
-
- /// Whether the spinner has been started
- private bool spinnerRunning;
- /// Index of the currently leading dot
- private int leadingDotIndex = 0;
- /// Text that will be displayed under the control as the current status
- private string statusText;
-
- /// Color in which the dots will be filled
- private Color dotFillColor = Color.RoyalBlue;
- /// Color that will be used for the dots' outline
- private Color dotOutlineColor = Color.White;
- /// Brush used to fill the dots
- private Brush dotFillBrush;
- /// Brush used for the dots' outline
- private Pen dotOutlinePen;
- /// Font that is used to display the status text
- private Font statusFont = SystemFonts.SmallCaptionFont;
-
- }
-
-} // namespace Nuclex.Windows.Forms.Controls
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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.Data;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace Nuclex.Windows.Forms.Controls {
+
+ /// Displays a progress spinner to entertain the user while waiting
+ public partial class ProgressSpinner : UserControl {
+
+ /// Number of dots the progress spinner will display
+ private const int DotCount = 8;
+ /// Size of a normal dot (only ever assumed by the trailing dot)
+ private const int DotRadius = 4;
+ ///
+ /// The leading dot will be DotCount times this larger than a normal dot
+ ///
+ private const int ScaleFactor = 1;
+
+ /// Initializes a new progress spinner
+ public ProgressSpinner() {
+ SetStyle(
+ (
+ ControlStyles.AllPaintingInWmPaint |
+ ControlStyles.OptimizedDoubleBuffer |
+ ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
+ ControlStyles.SupportsTransparentBackColor
+ ),
+ true
+ );
+
+ InitializeComponent();
+
+ Disposed += new EventHandler(OnDisposed);
+
+ if(!DesignMode) {
+ StartSpinner();
+ }
+ }
+
+ /// Releases all resources owned by the control when it is destroyed
+ /// Control that is being destroyed
+ /// Not used
+ private void OnDisposed(object sender, EventArgs arguments) {
+ if(this.dotOutlinePen != null) {
+ this.dotOutlinePen.Dispose();
+ this.dotOutlinePen = null;
+ }
+ if(this.dotFillBrush != null) {
+ this.dotFillBrush.Dispose();
+ this.dotFillBrush = null;
+ }
+ }
+
+ /// Starts the spinner's animation
+ public void StartSpinner() {
+ this.spinnerRunning = true;
+ this.animationUpdateTimer.Enabled = true;
+ }
+
+ /// Stops the spinner's animation
+ public void StopSpinner() {
+ this.animationUpdateTimer.Enabled = false;
+ this.spinnerRunning = false;
+ }
+
+ /// Color used to fill the dots
+ public Color DotFillColor {
+ get { return this.dotFillColor; }
+ set {
+ if(value != this.dotFillColor) {
+ this.dotFillColor = value;
+ if(this.dotFillBrush != null) {
+ this.dotFillBrush.Dispose();
+ this.dotFillBrush = null;
+ }
+ }
+ }
+ }
+
+ /// Color used for the dots' outline
+ public Color DotOutlineColor {
+ get { return this.dotOutlineColor; }
+ set {
+ if(value != this.dotOutlineColor) {
+ this.dotOutlineColor = value;
+ if(this.dotOutlinePen != null) {
+ this.dotOutlinePen.Dispose();
+ this.dotOutlinePen = null;
+ }
+ }
+ }
+ }
+
+ /// Calculates the optimal size for the spinner control
+ /// The optimal size for the spinner control to have
+ ///
+ /// Thanks to WinForms limited control transparency, the progress spinner needs to
+ /// redraw every control behind it each time it updates. Thus it's wise to keep it
+ /// as small as possible, but wide enough to fit the status text, if any.
+ ///
+ public Size GetOptimalSize() {
+ SizeF textRectangle;
+ using(var dummyImage = new Bitmap(1, 1)) {
+ using(Graphics graphics = Graphics.FromImage(dummyImage)) {
+ textRectangle = graphics.MeasureString(
+ this.statusText, this.statusFont
+ );
+ }
+ }
+
+ return new Size(
+ Math.Max(128, (int)(textRectangle.Width + 2.0f)),
+ this.statusFont.Height + 128
+ );
+ }
+
+ /// Font that is used to display the status text
+ public Font StatusFont {
+ get { return this.statusFont; }
+ set { this.statusFont = value; }
+ }
+
+ /// Text that will be displayed as the control's status
+ public string StatusText {
+ get { return this.statusText; }
+ set { this.statusText = value; }
+ }
+
+ /// Called when the control is hidden or shown
+ /// Not used
+ protected override void OnVisibleChanged(EventArgs arguments) {
+ base.OnVisibleChanged(arguments);
+ this.animationUpdateTimer.Enabled = this.spinnerRunning && Visible;
+ }
+
+ /// Called when the control should redraw itself
+ /// Provides access to the drawing surface and tools
+ protected override void OnPaint(PaintEventArgs arguments) {
+ paintControlsBehindMe(arguments);
+ paintAnimatedDots(arguments);
+ paintStatusMessage(arguments);
+ }
+
+ /// Forcefully redraws the controls below this one
+ /// Provides access to the drawing surface and tools
+ ///
+ ///
+ /// WinForms has very poor transparency support. A transparent control will only
+ /// be transparent to its immediate parent (so the parent needs to be a container
+ /// control and hold the transparent control as its preferrably only child).
+ ///
+ ///
+ /// Worse yet, if you manually establish this relationship in your .Designer.cs
+ /// file, the Visual Studio WinForms designer will dismantle it next time you
+ /// edit something. This method fixes those issues by repainting all controls
+ /// that are behind this control and whose bounding box intersect this control.
+ ///
+ ///
+ private void paintControlsBehindMe(PaintEventArgs arguments) {
+ if(Parent != null && this.BackColor == Color.Transparent) {
+ using(var bmp = new Bitmap(Parent.Width, Parent.Height)) {
+ Parent.Controls.Cast()
+ .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
+ .Where(c => c.Bounds.IntersectsWith(this.Bounds))
+ .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
+ .ToList()
+ .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
+
+ arguments.Graphics.DrawImage(bmp, -Left, -Top);
+ }
+ }
+ }
+
+ /// Draws a simple animated dots animation
+ /// Provides access to the drawing surface and tools
+ private void paintAnimatedDots(PaintEventArgs arguments) {
+ if(this.dotOutlinePen == null) {
+ this.dotOutlinePen = new Pen(this.dotOutlineColor);
+ }
+ if(this.dotFillBrush == null) {
+ this.dotFillBrush = new SolidBrush(this.dotFillColor);
+ }
+
+ SmoothingMode prevousSmoothingMode = arguments.Graphics.SmoothingMode;
+ arguments.Graphics.SmoothingMode = SmoothingMode.HighQuality;
+ try {
+ PointF center = new PointF(Width / 2.0f, (Height - this.statusFont.Height - 2) / 2.0f);
+
+ int diameter = Math.Min(Width, Height - this.statusFont.Height - 2);
+ int bigRadius = diameter / 2 - DotRadius - (DotCount - 1) * ScaleFactor;
+
+ // Draw the dots
+ float unitAngle = 360.0f / DotCount;
+ for(int index = 0; index < DotCount; ++index) {
+ int dotIndex = (index + leadingDotIndex) % DotCount;
+
+ var dotPosition = new PointF(
+ center.X + (float)(bigRadius * Math.Cos(unitAngle * dotIndex * Math.PI / 180.0f)),
+ center.Y + (float)(bigRadius * Math.Sin(unitAngle * dotIndex * Math.PI / 180.0f))
+ );
+
+ int currentDotRadius = DotRadius + index * ScaleFactor;
+
+ var corner = new PointF(
+ dotPosition.X - currentDotRadius, dotPosition.Y - currentDotRadius
+ );
+ arguments.Graphics.FillEllipse(
+ this.dotFillBrush, corner.X, corner.Y, 2 * currentDotRadius, 2 * currentDotRadius
+ );
+ arguments.Graphics.DrawEllipse(
+ this.dotOutlinePen, corner.X, corner.Y, 2 * currentDotRadius, 2 * currentDotRadius
+ );
+ }
+ }
+ finally {
+ arguments.Graphics.SmoothingMode = prevousSmoothingMode;
+ }
+ }
+
+ /// Draws the status message under the animated dots
+ /// Provides access to the drawing surface and tools
+ private void paintStatusMessage(PaintEventArgs arguments) {
+ if(!string.IsNullOrEmpty(this.statusText)) {
+ SizeF textRectangle = arguments.Graphics.MeasureString(
+ this.statusText, this.statusFont
+ );
+
+ var messageArea = new RectangleF(
+ (Width - textRectangle.Width) / 2.0f,
+ Height - this.statusFont.Height - 1.0f,
+ textRectangle.Width,
+ this.statusFont.Height
+ );
+
+ // Draw text with a white halo. This is a little bit ugly...
+ {
+ messageArea.Offset(-1.0f, 0.0f);
+ arguments.Graphics.DrawString(
+ this.statusText, this.statusFont, Brushes.White, messageArea
+ );
+
+ messageArea.Offset(2.0f, 0.0f);
+ arguments.Graphics.DrawString(
+ this.statusText, this.statusFont, Brushes.White, messageArea
+ );
+
+ messageArea.Offset(-1.0f, -1.0f);
+ arguments.Graphics.DrawString(
+ this.statusText, this.statusFont, Brushes.White, messageArea
+ );
+
+ messageArea.Offset(0.0f, 2.0f);
+ arguments.Graphics.DrawString(
+ this.statusText, this.statusFont, Brushes.White, messageArea
+ );
+
+ messageArea.Offset(0.0f, -1.0f);
+ arguments.Graphics.DrawString(
+ this.statusText, this.statusFont, this.dotFillBrush, messageArea
+ );
+ }
+ }
+ }
+
+
+ /// Called when the animation timer ticks to update the animation state
+ /// Animation timer that has ticked
+ /// Not used
+ private void animationTimerTicked(object sender, EventArgs arguments) {
+ this.leadingDotIndex = (this.leadingDotIndex + 1) % DotCount; // Advance the animation
+ Invalidate(); // Request a redraw at the earliest opportune time
+ }
+
+ /// Whether the spinner has been started
+ private bool spinnerRunning;
+ /// Index of the currently leading dot
+ private int leadingDotIndex = 0;
+ /// Text that will be displayed under the control as the current status
+ private string statusText;
+
+ /// Color in which the dots will be filled
+ private Color dotFillColor = Color.RoyalBlue;
+ /// Color that will be used for the dots' outline
+ private Color dotOutlineColor = Color.White;
+ /// Brush used to fill the dots
+ private Brush dotFillBrush;
+ /// Brush used for the dots' outline
+ private Pen dotOutlinePen;
+ /// Font that is used to display the status text
+ private Font statusFont = SystemFonts.SmallCaptionFont;
+
+ }
+
+} // namespace Nuclex.Windows.Forms.Controls
diff --git a/Source/IActiveWindowTracker.cs b/Source/IActiveWindowTracker.cs
index aac4abe..a715709 100644
--- a/Source/IActiveWindowTracker.cs
+++ b/Source/IActiveWindowTracker.cs
@@ -1,38 +1,37 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-using System;
-using System.Windows.Forms;
-
-namespace Nuclex.Windows.Forms {
-
- /// Enables consumer to look up the currently active window
- public interface IActiveWindowTracker {
-
- /// The currently active top-level or modal window
- ///
- /// If windows live in multiple threads, the property change notification for
- /// this property, if supported, might be fired from a different thread.
- ///
- Form ActiveWindow { get; }
-
- }
-
-} // namespace Nuclex.Windows.Forms
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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.Windows.Forms;
+
+namespace Nuclex.Windows.Forms {
+
+ /// Enables consumer to look up the currently active window
+ public interface IActiveWindowTracker {
+
+ /// The currently active top-level or modal window
+ ///
+ /// If windows live in multiple threads, the property change notification for
+ /// this property, if supported, might be fired from a different thread.
+ ///
+ Form ActiveWindow { get; }
+
+ }
+
+} // namespace Nuclex.Windows.Forms
diff --git a/Source/IWindowManager.cs b/Source/IWindowManager.cs
index 31e75bd..4b08479 100644
--- a/Source/IWindowManager.cs
+++ b/Source/IWindowManager.cs
@@ -1,93 +1,92 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-using System;
-using System.Windows.Forms;
-
-namespace Nuclex.Windows.Forms {
-
- /// Interface for a window manager used in an MVVM environment
- public interface IWindowManager : IActiveWindowTracker {
-
- /// Opens a view as a new root window of the application
- ///
- /// Type of view model a root window will be opened for
- ///
- ///
- /// 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)
- ///
- ///
- /// Whether the view model should be disposed when the view is closed
- ///
- /// The window that has been opened by the window manager
- Form OpenRoot(
- TViewModel viewModel = null, bool disposeOnClose = true
- ) where TViewModel : class;
-
- /// Displays a view as a modal window
- ///
- /// Type of the view model for which a view will be displayed
- ///
- ///
- /// 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)
- ///
- ///
- /// Whether the view model should be disposed when the view is closed
- ///
- /// The return value of the modal window
- bool? ShowModal(
- TViewModel viewModel = null, bool disposeOnClose = true
- ) where TViewModel : class;
-
- /// Creates the view for the specified view model
- ///
- /// Type of view model for which a view will be created
- ///
- ///
- /// 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)
- ///
- /// The view for the specified view model
- Control CreateView(TViewModel viewModel = null)
- where TViewModel : class;
-
- /// Creates a view model without a matching view
- /// Type of view model that will be created
- /// The new view model
- ///
- ///
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- TViewModel CreateViewModel()
- where TViewModel : class;
-
- }
-
-} // namespace Nuclex.Windows.Forms
+#region Apache License 2.0
+/*
+Nuclex .NET Framework
+Copyright (C) 2002-2024 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.Windows.Forms;
+
+namespace Nuclex.Windows.Forms {
+
+ /// Interface for a window manager used in an MVVM environment
+ public interface IWindowManager : IActiveWindowTracker {
+
+ /// Opens a view as a new root window of the application
+ ///
+ /// Type of view model a root window will be opened for
+ ///
+ ///
+ /// 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)
+ ///
+ ///
+ /// Whether the view model should be disposed when the view is closed
+ ///
+ /// The window that has been opened by the window manager
+ Form OpenRoot(
+ TViewModel viewModel = null, bool disposeOnClose = true
+ ) where TViewModel : class;
+
+ /// Displays a view as a modal window
+ ///
+ /// Type of the view model for which a view will be displayed
+ ///
+ ///
+ /// 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)
+ ///
+ ///
+ /// Whether the view model should be disposed when the view is closed
+ ///
+ /// The return value of the modal window
+ bool? ShowModal(
+ TViewModel viewModel = null, bool disposeOnClose = true
+ ) where TViewModel : class;
+
+ /// Creates the view for the specified view model
+ ///
+ /// Type of view model for which a view will be created
+ ///
+ ///
+ /// 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)
+ ///
+ /// The view for the specified view model
+ Control CreateView(TViewModel viewModel = null)
+ where TViewModel : class;
+
+ /// Creates a view model without a matching view
+ /// Type of view model that will be created
+ /// The new view model
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ TViewModel CreateViewModel()
+ where TViewModel : class;
+
+ }
+
+} // namespace Nuclex.Windows.Forms
diff --git a/Source/LateCheckedSynchronizer.cs b/Source/LateCheckedSynchronizer.cs
index 65bbaf2..af371cf 100644
--- a/Source/LateCheckedSynchronizer.cs
+++ b/Source/LateCheckedSynchronizer.cs
@@ -1,157 +1,156 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2019 Nuclex Development Labs
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the IBM Common Public License as
-published by the IBM Corporation; either version 1.0 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-IBM Common Public License for more details.
-
-You should have received a copy of the IBM Common Public
-License along with this library
-*/
-#endregion
-
-using System;
-using System.ComponentModel;
-using System.Windows.Forms;
-
-namespace Nuclex.Windows.Forms {
-
- ///
- /// Proxy stand-in to delay checking for the main window until it has been created
- ///
- ///
- ///
- /// The issue: when the view model for the main window is created, the main window
- /// may only exist as a .NET object, without the underlying operating system window
- /// (done in checkable via
- /// ). Not only will things
- /// like fail, we can't
- /// even locate the main window at that stage.
- ///
- ///
- /// Thus, if the main window cannot be found at the time a view model is created,
- /// this late-checking synchronizer will jump into its place and re-check for
- /// the main window only when something needs to be executed in the UI thread.
- ///
- ///
- class LateCheckedSynchronizer : ISynchronizeInvoke {
-
- /// Initializes a new late-checked main window synchronizer
- ///
- public LateCheckedSynchronizer(Action uiContextFoundCallback) {
- this.uiContextFoundCallback = uiContextFoundCallback;
- }
-
- /// Finds the application's main window
- /// Main window of the application or null if none has been created
- ///
- /// The application's main window, if it has been created yet
- ///
- public static Form GetMainWindow() {
- IntPtr mainWindowHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
-
- // We can get two things: a list of all open windows and the handle of
- // the window that the process has registered as main window. Use the latter
- // to pick the correct window from the former.
- FormCollection openForms = Application.OpenForms;
- int openFormCount = openForms.Count;
- for(int index = 0; index < openFormCount; ++index) {
- Form form = openForms[index];
-
- IntPtr handle;
- if(form.InvokeRequired) {
- handle = (IntPtr)form.Invoke(new Func