diff --git a/NEW/src/Utils/JdeScoping.ConfigManager/ViewModels/Forms/SearchFormViewModel.cs b/NEW/src/Utils/JdeScoping.ConfigManager/ViewModels/Forms/SearchFormViewModel.cs
new file mode 100644
index 0000000..e2f6d20
--- /dev/null
+++ b/NEW/src/Utils/JdeScoping.ConfigManager/ViewModels/Forms/SearchFormViewModel.cs
@@ -0,0 +1,69 @@
+using JdeScoping.ConfigManager.Models;
+
+namespace JdeScoping.ConfigManager.ViewModels.Forms;
+
+///
+/// ViewModel for editing Search configuration section.
+///
+public class SearchFormViewModel : ViewModelBase
+{
+ private readonly SearchSection _model;
+ private readonly Action _onChanged;
+
+ public SearchFormViewModel(SearchSection model, Action onChanged)
+ {
+ _model = model ?? throw new ArgumentNullException(nameof(model));
+ _onChanged = onChanged ?? throw new ArgumentNullException(nameof(onChanged));
+ }
+
+ ///
+ /// Gets or sets the maximum number of result rows returned by a search.
+ ///
+ public int MaxResultRows
+ {
+ get => _model.MaxResultRows;
+ set
+ {
+ if (_model.MaxResultRows != value)
+ {
+ _model.MaxResultRows = value;
+ OnPropertyChanged();
+ _onChanged();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the timeout in seconds for search operations.
+ ///
+ public int TimeoutSeconds
+ {
+ get => _model.TimeoutSeconds;
+ set
+ {
+ if (_model.TimeoutSeconds != value)
+ {
+ _model.TimeoutSeconds = value;
+ OnPropertyChanged();
+ _onChanged();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum number of concurrent search operations allowed.
+ ///
+ public int MaxConcurrentSearches
+ {
+ get => _model.MaxConcurrentSearches;
+ set
+ {
+ if (_model.MaxConcurrentSearches != value)
+ {
+ _model.MaxConcurrentSearches = value;
+ OnPropertyChanged();
+ _onChanged();
+ }
+ }
+ }
+}
diff --git a/NEW/tests/JdeScoping.ConfigManager.Tests/ViewModels/Forms/SearchFormViewModelTests.cs b/NEW/tests/JdeScoping.ConfigManager.Tests/ViewModels/Forms/SearchFormViewModelTests.cs
new file mode 100644
index 0000000..342968d
--- /dev/null
+++ b/NEW/tests/JdeScoping.ConfigManager.Tests/ViewModels/Forms/SearchFormViewModelTests.cs
@@ -0,0 +1,108 @@
+using JdeScoping.ConfigManager.Models;
+using JdeScoping.ConfigManager.ViewModels.Forms;
+
+namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
+
+public class SearchFormViewModelTests
+{
+ [Fact]
+ public void Constructor_InitializesFromModel()
+ {
+ // Arrange
+ var model = new SearchSection
+ {
+ MaxResultRows = 50000,
+ TimeoutSeconds = 600,
+ MaxConcurrentSearches = 10
+ };
+
+ // Act
+ var sut = new SearchFormViewModel(model, () => { });
+
+ // Assert
+ sut.MaxResultRows.ShouldBe(50000);
+ sut.TimeoutSeconds.ShouldBe(600);
+ sut.MaxConcurrentSearches.ShouldBe(10);
+ }
+
+ [Fact]
+ public void PropertyChange_UpdatesModel()
+ {
+ // Arrange
+ var model = new SearchSection { MaxResultRows = 100000 };
+ var sut = new SearchFormViewModel(model, () => { });
+
+ // Act
+ sut.MaxResultRows = 25000;
+
+ // Assert
+ model.MaxResultRows.ShouldBe(25000);
+ }
+
+ [Fact]
+ public void PropertyChange_InvokesOnChanged()
+ {
+ // Arrange
+ var model = new SearchSection();
+ var changedInvoked = false;
+ var sut = new SearchFormViewModel(model, () => changedInvoked = true);
+
+ // Act
+ sut.TimeoutSeconds = 120;
+
+ // Assert
+ changedInvoked.ShouldBeTrue();
+ }
+
+ [Fact]
+ public void PropertyChange_RaisesPropertyChanged()
+ {
+ // Arrange
+ var model = new SearchSection();
+ var sut = new SearchFormViewModel(model, () => { });
+ var propertyChangedRaised = false;
+ sut.PropertyChanged += (s, e) =>
+ {
+ if (e.PropertyName == nameof(SearchFormViewModel.MaxConcurrentSearches))
+ propertyChangedRaised = true;
+ };
+
+ // Act
+ sut.MaxConcurrentSearches = 8;
+
+ // Assert
+ propertyChangedRaised.ShouldBeTrue();
+ }
+
+ [Fact]
+ public void Constructor_ThrowsOnNullModel()
+ {
+ // Act & Assert
+ Should.Throw(() => new SearchFormViewModel(null!, () => { }));
+ }
+
+ [Fact]
+ public void Constructor_ThrowsOnNullCallback()
+ {
+ // Arrange
+ var model = new SearchSection();
+
+ // Act & Assert
+ Should.Throw(() => new SearchFormViewModel(model, null!));
+ }
+
+ [Fact]
+ public void PropertyChange_DoesNotInvokeOnChangedWhenValueUnchanged()
+ {
+ // Arrange
+ var model = new SearchSection { MaxResultRows = 50000 };
+ var changedCount = 0;
+ var sut = new SearchFormViewModel(model, () => changedCount++);
+
+ // Act - set to same value
+ sut.MaxResultRows = 50000;
+
+ // Assert
+ changedCount.ShouldBe(0);
+ }
+}