diff --git a/.gitignore b/.gitignore
index 5ccb5a940..b267fa01a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,29 @@
.vs
bin
obj
+*.db
+*.cache
+*.bin
+*.dll
*.bak
*.user
+*.log
+*.suo
+*.snk
+**/vs
+**/obj
+**/obj.net
+**/bin
+**/Help
+/CSharpBible/App2
+/CSharpBible/Mobile
+/CSharpBible/Web
+/CSharpBible/packages
+/CSharpBible/Simulation
+/CSharpBible/DB/ADO_Test
+/CSharpBible/Libraries/CSFreeVision_
+/CSharpBible/Libraries/MathLibraryTests/TestResults
+/CSharpBible/Graphics/PermutationTests/TestResults
+/CSharpBible/MVVM_Tutorial/UWP_00_Test
+/CSharpBible/WinUI/App1
+/TestStatements/Help
diff --git a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc/AA05_CommandParCalc.csproj b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc/AA05_CommandParCalc.csproj
index b7fd17855..9d80b995e 100644
--- a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc/AA05_CommandParCalc.csproj
+++ b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc/AA05_CommandParCalc.csproj
@@ -2,13 +2,19 @@
WinExe
- net8.0;net9.0
+ net8.0
true
app.manifest
true
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
@@ -48,4 +54,6 @@
Resources.Designer.cs
+
+
diff --git a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj
index e713d1758..fcc6be58f 100644
--- a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj
+++ b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj
@@ -1,13 +1,18 @@
- net8.0;net9.0
+ net8.0
Library
enable
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
diff --git a/Avalonia_Apps/AA05_CommandParCalc/Directory.Packages.props b/Avalonia_Apps/AA05_CommandParCalc/Directory.Packages.props
index 75c20929e..7eb87d870 100644
--- a/Avalonia_Apps/AA05_CommandParCalc/Directory.Packages.props
+++ b/Avalonia_Apps/AA05_CommandParCalc/Directory.Packages.props
@@ -1,27 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4/AA06_Converters4.csproj b/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4/AA06_Converters4.csproj
index 68ab18854..7deb84a87 100644
--- a/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4/AA06_Converters4.csproj
+++ b/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4/AA06_Converters4.csproj
@@ -2,13 +2,19 @@
WinExe
- net8.0;net9.0
+ net8.0
enable
true
true
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
diff --git a/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4Tests/AA06_Converters4Tests.csproj b/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4Tests/AA06_Converters4Tests.csproj
index a0dd72870..0e77ada03 100644
--- a/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4Tests/AA06_Converters4Tests.csproj
+++ b/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4Tests/AA06_Converters4Tests.csproj
@@ -1,13 +1,19 @@
- net9.0
+ net8.0
false
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
-
+
@@ -23,4 +29,5 @@
+
diff --git a/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2/AA06_ValueConverter2.csproj b/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2/AA06_ValueConverter2.csproj
index e57ac834e..10ed24821 100644
--- a/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2/AA06_ValueConverter2.csproj
+++ b/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2/AA06_ValueConverter2.csproj
@@ -2,13 +2,19 @@
WinExe
- net8.0;net9.0
+ net8.0
true
app.manifest
true
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
@@ -49,4 +55,5 @@
Resources.Designer.cs
+
diff --git a/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj b/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj
index 7e9626597..b906d0c53 100644
--- a/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj
+++ b/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj
@@ -1,12 +1,18 @@
- net8.0;net9.0
+ net8.0
Library
enable
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
diff --git a/Avalonia_Apps/AA06_ValueConverter2/Directory.Packages.props b/Avalonia_Apps/AA06_ValueConverter2/Directory.Packages.props
index 031d8b4f7..7eb87d870 100644
--- a/Avalonia_Apps/AA06_ValueConverter2/Directory.Packages.props
+++ b/Avalonia_Apps/AA06_ValueConverter2/Directory.Packages.props
@@ -1,31 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes/AA09_DialogBoxes.csproj b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes/AA09_DialogBoxes.csproj
index edeefc7f9..5b75d64e7 100644
--- a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes/AA09_DialogBoxes.csproj
+++ b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes/AA09_DialogBoxes.csproj
@@ -2,12 +2,18 @@
WinExe
- net8.0;net9.0
+ net8.0
true
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
enable
disable
diff --git a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj
index cb8dd0f92..4a9b8064d 100644
--- a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj
+++ b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj
@@ -1,14 +1,20 @@
- net8.0;net9.0
+ net8.0
true
false
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
-
+
diff --git a/Avalonia_Apps/AA09_DialogBoxes/Directory.Packages.props b/Avalonia_Apps/AA09_DialogBoxes/Directory.Packages.props
index ac66cfd24..7eb87d870 100644
--- a/Avalonia_Apps/AA09_DialogBoxes/Directory.Packages.props
+++ b/Avalonia_Apps/AA09_DialogBoxes/Directory.Packages.props
@@ -1,29 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj b/Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj
index 0b5aff615..d246e0113 100644
--- a/Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj
+++ b/Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj
@@ -11,7 +11,7 @@
-
+
@@ -23,4 +23,11 @@
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA14_ScreenX/Directory.Packages.props b/Avalonia_Apps/AA14_ScreenX/Directory.Packages.props
index ac66cfd24..7eb87d870 100644
--- a/Avalonia_Apps/AA14_ScreenX/Directory.Packages.props
+++ b/Avalonia_Apps/AA14_ScreenX/Directory.Packages.props
@@ -1,29 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj b/Avalonia_Apps/AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj
index baa60b44a..868598e9c 100644
--- a/Avalonia_Apps/AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj
+++ b/Avalonia_Apps/AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj
@@ -23,4 +23,11 @@
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA15_Labyrinth/AA15_LabyrinthTests/AA15_LabyrinthTests.csproj b/Avalonia_Apps/AA15_Labyrinth/AA15_LabyrinthTests/AA15_LabyrinthTests.csproj
index 47341c74d..5d0860a91 100644
--- a/Avalonia_Apps/AA15_Labyrinth/AA15_LabyrinthTests/AA15_LabyrinthTests.csproj
+++ b/Avalonia_Apps/AA15_Labyrinth/AA15_LabyrinthTests/AA15_LabyrinthTests.csproj
@@ -24,4 +24,9 @@
+
+
+
+
+
diff --git a/Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj b/Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj
index 5bdc8498c..bc98e7507 100644
--- a/Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj
+++ b/Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj
@@ -36,4 +36,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA15_Labyrinth/Directory.Packages.props b/Avalonia_Apps/AA15_Labyrinth/Directory.Packages.props
index 064f3ff82..7eb87d870 100644
--- a/Avalonia_Apps/AA15_Labyrinth/Directory.Packages.props
+++ b/Avalonia_Apps/AA15_Labyrinth/Directory.Packages.props
@@ -1,32 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj b/Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj
index ff3071c13..f0ba2a4a8 100644
--- a/Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj
+++ b/Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0
+ net8.0
enable
enable
true
@@ -9,7 +9,13 @@
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/AppTests.cs b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/AppTests.cs
new file mode 100644
index 000000000..1b9dd9632
--- /dev/null
+++ b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/AppTests.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using AA16_UserControl1;
+using AA16_UserControl1.ViewModels.Interfaces;
+using AA16_UserControl1.Views;
+using Avalonia.Headless.MSTest;
+using Avalonia.Views.Extension;
+
+namespace AA16_UserControl1.Tests;
+
+[TestClass]
+public class AppTests
+{
+ private static void BuildServiceProvider()
+ {
+ var services = new ServiceCollection();
+ var method = typeof(App).GetMethod("ConfigureServices", BindingFlags.NonPublic | BindingFlags.Static);
+ Assert.IsNotNull(method, "ConfigureServices nicht gefunden.");
+ method.Invoke(null, new object[] { services });
+ IoC.Configure(services.BuildServiceProvider());
+ }
+
+ [TestMethod]
+ public void Should_Register_MainWindowViewModel_As_Transient()
+ {
+ BuildServiceProvider();
+ var vm1 = IoC.GetRequiredService();
+ var vm2 = IoC.GetRequiredService();
+ Assert.IsNotNull(vm1);
+ Assert.IsNotNull(vm2);
+ Assert.AreNotSame(vm1, vm2, "Transient Lifetime erwartet.");
+ }
+
+ [TestMethod]
+ public void Should_Register_IUserControlViewModel()
+ {
+ BuildServiceProvider();
+ var ucVm = IoC.GetRequiredService();
+ Assert.IsNotNull(ucVm);
+ Assert.AreEqual(typeof(AA16_UserControl1.ViewModels.UserControlViewModel), ucVm.GetType());
+ }
+
+ [AvaloniaTestMethod]
+ public void Should_Register_Views()
+ {
+ BuildServiceProvider();
+ var mainWindow = IoC.GetRequiredService();
+ var userControlView = IoC.GetRequiredService();
+ var labeledTextbox = IoC.GetRequiredService();
+
+ Assert.IsNotNull(mainWindow);
+ Assert.IsNotNull(userControlView);
+ Assert.IsNotNull(labeledTextbox);
+ }
+
+ [TestMethod]
+ public void Should_Not_Register_Commented_DoubleButtonUC()
+ {
+ BuildServiceProvider();
+ // Sicherstellen, dass nicht versehentlich registriert
+ var service = IoC.GetService();
+ Assert.IsNull(service, "DoubleButtonUC sollte nicht registriert sein.");
+ }
+
+ [AvaloniaTestMethod]
+ public void App_Should_Initialize_Correctly()
+ {
+ var app = new App();
+ app.Initialize();
+ Assert.IsNotNull(app, "App-Instanz sollte nicht null sein.");
+ }
+
+ [AvaloniaTestMethod]
+ public void App_OnFrameworkInitializationCompletedTests()
+ {
+ var app = new App();
+ app.OnFrameworkInitializationCompleted();
+ Assert.IsNotNull(app, "App-Instanz sollte nicht null sein.");
+
+ }
+
+}
\ No newline at end of file
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/ViewModels/MainWindowViewModelTests.cs b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/ViewModels/MainWindowViewModelTests.cs
index c23617409..ea9c4c910 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/ViewModels/MainWindowViewModelTests.cs
+++ b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/ViewModels/MainWindowViewModelTests.cs
@@ -1,26 +1,35 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System.ComponentModel;
+using AA16_UserControl1.ViewModels.Interfaces;
using Avalonia.ViewModels;
+using Avalonia.Views.Extension;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
+using System.ComponentModel;
namespace AA16_UserControl1.ViewModels.Tests;
[TestClass()]
-public class MainWindowViewModelTests
+public class MainWindowViewModelTests : BaseTestViewModel
{
- MainWindowViewModel testModel;
-
[TestInitialize]
- public void Init()
+ public override void Init()
{
- testModel = new(null);
- }
+ IoC.GetReqSrv = t =>
+ {
+ if (t == typeof(IUserControlViewModel))
+ {
+ return Substitute.For();
+ }
+ throw new System.NotImplementedException($"No registration for type {t}.");
+ };
+ base.Init();
+ }
[TestMethod()]
public void SetupTest()
{
Assert.IsNotNull(testModel);
Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
- Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(ViewModelBase));
Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
}
}
\ No newline at end of file
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/ViewModels/UserControlViewModelTests.cs b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/ViewModels/UserControlViewModelTests.cs
new file mode 100644
index 000000000..e0ee71846
--- /dev/null
+++ b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/ViewModels/UserControlViewModelTests.cs
@@ -0,0 +1,64 @@
+using AA16_UserControl1.ViewModels.Interfaces;
+using Avalonia.ViewModels;
+using Avalonia.Views.Extension;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace AA16_UserControl1.ViewModels.Tests;
+
+[TestClass()]
+public class UserControlViewModelTests : BaseTestViewModel
+{
+ protected override Dictionary GetDefaultData() => new() {
+ { "Text", "" },
+ { "Daten", "" },
+ { "Do1Command", true },
+ { "Do2Command", true }
+ };
+
+ [TestInitialize]
+ public override void Init()
+ {
+ IoC.GetReqSrv = t =>
+ {
+ if (t == typeof(IUserControlViewModel))
+ {
+ return Substitute.For();
+ }
+ throw new System.NotImplementedException($"No registration for type {t}.");
+ };
+ base.Init();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(UserControlViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(ViewModelBase));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+
+ [TestMethod()]
+ public void Do1CommandTest()
+ {
+ Assert.IsTrue(testModel.Do1Command.CanExecute(null));
+ testModel.Do1Command.Execute(null);
+ Assert.AreEqual("", testModel.Text);
+ Assert.AreEqual(string.Empty, testModel.Daten);
+ Assert.IsFalse(testModel.Do1Command.CanExecute(null));
+ Assert.IsTrue(testModel.Do2Command.CanExecute(null));
+ }
+ [TestMethod()]
+ public void Do2CommandTest()
+ {
+ Assert.IsTrue(testModel.Do2Command.CanExecute(null));
+ testModel.Do2Command.Execute(null);
+ Assert.AreEqual("", testModel.Daten);
+ Assert.AreEqual(string.Empty, testModel.Text);
+ Assert.IsFalse(testModel.Do2Command.CanExecute(null));
+ Assert.IsTrue(testModel.Do1Command.CanExecute(null));
+ }
+}
\ No newline at end of file
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/Views/MainWindowTests.cs b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/Views/MainWindowTests.cs
index a98eccfba..e4517f0e3 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/Views/MainWindowTests.cs
+++ b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/Views/MainWindowTests.cs
@@ -1,4 +1,7 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using AA16_UserControl1.ViewModels.Interfaces;
+using Avalonia.Headless.MSTest;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
using System.Threading;
namespace AA16_UserControl1.Views.Tests;
@@ -6,15 +9,16 @@ namespace AA16_UserControl1.Views.Tests;
[TestClass()]
public class MainWindowTests
{
- [TestMethod()]
+ [AvaloniaTestMethod]
public void MainWindowTest()
{
- MainWindow? mw=null;
- var t = new Thread(()=> mw = new());
- t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
- t.Start();
- t.Join(); //Wait for the thread to end
+ MainWindow? mw = new()
+ {
+ Height = 600,
+ Width = 800,
+ DataContext = Substitute.For()
+ };
Assert.IsNotNull(mw);
- Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
}
}
\ No newline at end of file
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/Views/UserColtrolViewTests.cs b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/Views/UserColtrolViewTests.cs
index 49e9f3109..ab8b384ad 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/Views/UserColtrolViewTests.cs
+++ b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/Views/UserColtrolViewTests.cs
@@ -1,4 +1,8 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using AA16_UserControl1.ViewModels.Interfaces;
+using Avalonia.Headless.MSTest;
+using Avalonia.Views.Extension;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
using System.Threading;
namespace AA16_UserControl1.Views.Tests;
@@ -6,15 +10,29 @@ namespace AA16_UserControl1.Views.Tests;
[TestClass()]
public class UserControlViewTests
{
- [TestMethod()]
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ IoC.GetReqSrv = t =>
+ {
+ if (t == typeof(IUserControlViewModel))
+ {
+ return Substitute.For();
+ }
+ throw new System.NotImplementedException($"No registration for type {t}.");
+ };
+ }
+
+ [AvaloniaTestMethod]
public void UserControlViewTest()
{
- UserControlView? testView=null;
- var t = new Thread(()=> testView = new());
- t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
- t.Start();
- t.Join(); //Wait for the thread to end
- Assert.IsNotNull(testView);
+ UserControlView? testView = new()
+ {
+ Width = 800,
+ Height = 600,
+ };
+ Assert.IsNotNull(testView);
Assert.IsInstanceOfType(testView, typeof(UserControlView));
}
}
\ No newline at end of file
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj
index de7338d27..3e5e3e2f8 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj
@@ -1,4 +1,4 @@
-
+
WinExe
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/App.axaml.cs b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/App.axaml.cs
index c0c0b60d8..111f2a02b 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/App.axaml.cs
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/App.axaml.cs
@@ -1,6 +1,8 @@
+using AA16_UserControl1.ViewModels.Interfaces;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
+using Avalonia.Views.Extension;
using Microsoft.Extensions.DependencyInjection;
using System;
@@ -8,7 +10,6 @@ namespace AA16_UserControl1;
public partial class App : Application
{
- public static IServiceProvider Services { get; set; } = null!;
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
@@ -20,12 +21,12 @@ public override void OnFrameworkInitializationCompleted()
ConfigureServices(services);
- Services = services.BuildServiceProvider();
+ IoC.Configure(services.BuildServiceProvider());
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
- var mainWindow = Services.GetRequiredService();
- mainWindow.DataContext = Services.GetRequiredService();
+ var mainWindow = IoC.GetRequiredService();
+ mainWindow.DataContext = IoC.GetRequiredService();
desktop.MainWindow = mainWindow;
}
@@ -36,7 +37,7 @@ private static void ConfigureServices(IServiceCollection services)
{
// ViewModels
services.AddTransient();
- services.AddTransient();
+ services.AddTransient();
// Views
services.AddTransient();
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Directory.Packages.props b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Directory.Packages.props
index f17fd09e9..921a6da2f 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Directory.Packages.props
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Directory.Packages.props
@@ -3,13 +3,13 @@
true
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/Interfaces/IMainWindowViewModel.cs b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/Interfaces/IMainWindowViewModel.cs
new file mode 100644
index 000000000..337df3283
--- /dev/null
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/Interfaces/IMainWindowViewModel.cs
@@ -0,0 +1,9 @@
+using System.ComponentModel;
+
+namespace AA16_UserControl1.ViewModels.Interfaces
+{
+ public interface IMainWindowViewModel: INotifyPropertyChanged
+ {
+ INotifyPropertyChanged CurrentViewModel { get; }
+ }
+}
\ No newline at end of file
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/Interfaces/IUserControlViewModel.cs b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/Interfaces/IUserControlViewModel.cs
new file mode 100644
index 000000000..1aa35983b
--- /dev/null
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/Interfaces/IUserControlViewModel.cs
@@ -0,0 +1,13 @@
+using CommunityToolkit.Mvvm.Input;
+using System.ComponentModel;
+
+namespace AA16_UserControl1.ViewModels.Interfaces;
+
+public interface IUserControlViewModel: INotifyPropertyChanged
+{
+ string Text { get; set; }
+ string Daten { get; set; }
+
+ IRelayCommand Do1Command { get; }
+ IRelayCommand Do2Command { get; }
+}
\ No newline at end of file
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/MainWindowViewModel.cs b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/MainWindowViewModel.cs
index 9d8dc176d..8bf3d8ff0 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/MainWindowViewModel.cs
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/MainWindowViewModel.cs
@@ -1,13 +1,21 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using AA16_UserControl1.ViewModels.Interfaces;
+using Avalonia.Views.Extension;
+using CommunityToolkit.Mvvm.ComponentModel;
+using System.ComponentModel;
namespace AA16_UserControl1.ViewModels;
-public partial class MainWindowViewModel : ViewModelBase
+public partial class MainWindowViewModel : ViewModelBase, IMainWindowViewModel
{
- public MainWindowViewModel(UserControlViewModel content)
+ public MainWindowViewModel()
+ : this(IoC.GetRequiredService())
+ {
+ }
+
+ public MainWindowViewModel(IUserControlViewModel content)
{
CurrentViewModel = content;
}
- public ViewModelBase CurrentViewModel { get; }
+ public INotifyPropertyChanged CurrentViewModel { get; }
}
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/UserControlViewModel.cs b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/UserControlViewModel.cs
index a4d45937f..4bbbebca1 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/UserControlViewModel.cs
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/ViewModels/UserControlViewModel.cs
@@ -1,9 +1,10 @@
+using AA16_UserControl1.ViewModels.Interfaces;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace AA16_UserControl1.ViewModels;
-public partial class UserControlViewModel : ViewModelBase
+public partial class UserControlViewModel : ViewModelBase, IUserControlViewModel
{
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(Do1Command))]
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Views/UserControlView.axaml b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Views/UserControlView.axaml
index 809932260..a90025495 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Views/UserControlView.axaml
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Views/UserControlView.axaml
@@ -1,8 +1,8 @@
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Views/UserControlView.axaml.cs b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Views/UserControlView.axaml.cs
index 9bce823e0..b02972e1c 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Views/UserControlView.axaml.cs
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/Views/UserControlView.axaml.cs
@@ -1,6 +1,8 @@
using Avalonia.Controls;
using Microsoft.Extensions.DependencyInjection;
using AA16_UserControl1.ViewModels;
+using AA16_UserControl1.ViewModels.Interfaces;
+using Avalonia.Views.Extension;
namespace AA16_UserControl1.Views;
@@ -9,6 +11,6 @@ public partial class UserControlView : UserControl
public UserControlView()
{
InitializeComponent();
- DataContext ??= App.Services.GetRequiredService();
+ DataContext ??= IoC.GetRequiredService();
}
}
diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj
index aeb7c33fb..ed77aa851 100644
--- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj
+++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj
@@ -25,4 +25,11 @@
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA16_UserControl/Directory.Packages.props b/Avalonia_Apps/AA16_UserControl/Directory.Packages.props
index 1afdd3db4..7eb87d870 100644
--- a/Avalonia_Apps/AA16_UserControl/Directory.Packages.props
+++ b/Avalonia_Apps/AA16_UserControl/Directory.Packages.props
@@ -1,29 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/AA18_MultiConverters.csproj b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/AA18_MultiConverters.csproj
index 0d373bb9a..1c16896a3 100644
--- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/AA18_MultiConverters.csproj
+++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/AA18_MultiConverters.csproj
@@ -25,4 +25,11 @@
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/App.axaml.cs b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/App.axaml.cs
index 3be292e9d..d40cea5d0 100644
--- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/App.axaml.cs
+++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/App.axaml.cs
@@ -1,3 +1,4 @@
+using AA18_MultiConverter.ViewModels.Interfaces;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
@@ -33,7 +34,7 @@ public override void OnFrameworkInitializationCompleted()
private static void ConfigureServices(IServiceCollection services)
{
// ViewModels
- services.AddTransient();
+ services.AddTransient();
// Views
services.AddTransient();
diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/ViewModels/DateDifViewModel.cs b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/ViewModels/DateDifViewModel.cs
index e651876f1..9f14adb16 100644
--- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/ViewModels/DateDifViewModel.cs
+++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/ViewModels/DateDifViewModel.cs
@@ -14,10 +14,11 @@
using CommunityToolkit.Mvvm.ComponentModel;
using AA18_MultiConverter.Model;
using System;
+using AA18_MultiConverter.ViewModels.Interfaces;
namespace AA18_MultiConverter.ViewModels;
-public partial class DateDifViewModel : ObservableObject
+public partial class DateDifViewModel : ObservableObject, IDateDifViewModel
{
public static Func GetNow { get; set; } = () => DateTimeOffset.Now;
diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/ViewModels/Interfaces/IDateDifViewModel.cs b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/ViewModels/Interfaces/IDateDifViewModel.cs
new file mode 100644
index 000000000..55586c84e
--- /dev/null
+++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/ViewModels/Interfaces/IDateDifViewModel.cs
@@ -0,0 +1,25 @@
+// ***********************************************************************
+// Assembly : MVVM_18_MultiConverters
+// Author : Mir
+// Created : 07-05-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-05-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using AA18_MultiConverter.Model;
+
+namespace AA18_MultiConverter.ViewModels.Interfaces;
+
+public interface IDateDifViewModel
+{
+ DateTimeOffset StartDate { get; set; }
+ DateTimeOffset EndDate { get; set; }
+ DateDifFormat Format { get; set; }
+ TimeSpan DateDif { get; }
+ DateDifFormat[] DateFormats { get; }
+}
\ No newline at end of file
diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/Views/DateDifView.axaml b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/Views/DateDifView.axaml
index 8cf6c9192..29ac837cb 100644
--- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/Views/DateDifView.axaml
+++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/Views/DateDifView.axaml
@@ -1,10 +1,10 @@
+ x:DataType="vmi:IDateDifViewModel">
diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/Views/DateDifView.axaml.cs b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/Views/DateDifView.axaml.cs
index d5a3a0757..0b5d13b9a 100644
--- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/Views/DateDifView.axaml.cs
+++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/Views/DateDifView.axaml.cs
@@ -1,3 +1,4 @@
+using AA18_MultiConverter.ViewModels.Interfaces;
using Avalonia.Controls;
using Microsoft.Extensions.DependencyInjection;
@@ -8,6 +9,6 @@ public partial class DateDifView : UserControl
public DateDifView()
{
InitializeComponent();
- DataContext = App.Services.GetRequiredService();
+ DataContext = App.Services.GetRequiredService();
}
}
diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/View/DateDifViewTests.cs b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/View/DateDifViewTests.cs
index 01cbdc328..f06c8ccb3 100644
--- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/View/DateDifViewTests.cs
+++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/View/DateDifViewTests.cs
@@ -1,4 +1,10 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using AA18_MultiConverter.ViewModels;
+using AA18_MultiConverter.ViewModels.Interfaces;
+using Avalonia.Headless.MSTest;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
+using System;
using System.Threading;
namespace AA18_MultiConverter.Views.Tests;
@@ -8,14 +14,18 @@ public class DateDifViewTests
{
DateDifView? testView;
- [TestMethod()]
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ var services = typeof(App).GetProperty(nameof(App.Services));
+ services.SetValue(null,Substitute.For());
+ App.Services.GetService(typeof(IDateDifViewModel)).Returns(Substitute.For());
+ }
+
+ [AvaloniaTestMethod()]
public void DateDifViewTest()
{
- testView = null;
- var t = new Thread(() => testView = new());
- t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
- t.Start();
- t.Join(); //Wait for the thread to end
+ testView = new();
Assert.IsNotNull(testView);
Assert.IsInstanceOfType(testView, typeof(DateDifView));
}
diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/View/MainWindowTests.cs b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/View/MainWindowTests.cs
index a5d0182c9..dbbeef149 100644
--- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/View/MainWindowTests.cs
+++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/View/MainWindowTests.cs
@@ -1,4 +1,8 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using AA18_MultiConverter.ViewModels.Interfaces;
+using Avalonia.Headless.MSTest;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
+using System;
using System.Threading;
namespace AA18_MultiConverter.Views.Tests;
@@ -8,14 +12,18 @@ public class MainWindowTests
{
MainWindow? testView;
- [TestMethod()]
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ var services = typeof(App).GetProperty(nameof(App.Services));
+ services.SetValue(null, Substitute.For());
+ App.Services.GetService(typeof(IDateDifViewModel)).Returns(Substitute.For());
+ }
+
+ [AvaloniaTestMethod()]
public void MainWindowTest()
{
- testView = null;
- var t = new Thread(() => testView = new(null));
- t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
- t.Start();
- t.Join(); //Wait for the thread to end
+ testView = new(null);
Assert.IsNotNull(testView);
Assert.IsInstanceOfType(testView, typeof(MainWindow));
}
diff --git a/Avalonia_Apps/AA18_MultiConverter/Directory.Packages.props b/Avalonia_Apps/AA18_MultiConverter/Directory.Packages.props
index 266ecf965..7eb87d870 100644
--- a/Avalonia_Apps/AA18_MultiConverter/Directory.Packages.props
+++ b/Avalonia_Apps/AA18_MultiConverter/Directory.Packages.props
@@ -1,29 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj b/Avalonia_Apps/AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj
index 125e1e8fd..daf30f25c 100644
--- a/Avalonia_Apps/AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj
+++ b/Avalonia_Apps/AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj
@@ -60,4 +60,12 @@
+
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA19_FilterLists/Directory.Packages.props b/Avalonia_Apps/AA19_FilterLists/Directory.Packages.props
index d5f6f29d7..7eb87d870 100644
--- a/Avalonia_Apps/AA19_FilterLists/Directory.Packages.props
+++ b/Avalonia_Apps/AA19_FilterLists/Directory.Packages.props
@@ -1,31 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj b/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj
index ac63b39c4..3c8797919 100644
--- a/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj
+++ b/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj
@@ -3,7 +3,7 @@
WinExe
- net8.0;net9.0
+ net8.0
enable
enable
true
@@ -11,7 +11,13 @@
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
diff --git a/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/MainWindow.axaml b/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/MainWindow.axaml
index 000a171e8..99f7dca33 100644
--- a/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/MainWindow.axaml
+++ b/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/MainWindow.axaml
@@ -1,9 +1,9 @@
-
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:views="clr-namespace:AA20_SysDialogs.Views"
+ mc:Ignorable="d" x:Class="AA20_SysDialogs.MainWindow"
+ Width="800" Height="600" Title="AA20 System Dialogs">
+
diff --git a/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/Views/SysDialogs.axaml b/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/Views/SysDialogs.axaml
index 237a3ad2d..c4a61d943 100644
--- a/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/Views/SysDialogs.axaml
+++ b/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/Views/SysDialogs.axaml
@@ -1,48 +1,48 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:vm="clr-namespace:AA20_SysDialogs.ViewModels"
+ xmlns:conv="clr-namespace:AA20_SysDialogs.Converter"
+ x:Class="AA20_SysDialogs.Views.SysDialogs"
+ x:DataType="vm:SysDialogsViewModel"
+ mc:Ignorable="d">
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA20_SysDialogs/AA20_SysdialogsTests/AA20_SysdialogsTests.csproj b/Avalonia_Apps/AA20_SysDialogs/AA20_SysdialogsTests/AA20_SysdialogsTests.csproj
index 72dddbfb9..b1d148dd3 100644
--- a/Avalonia_Apps/AA20_SysDialogs/AA20_SysdialogsTests/AA20_SysdialogsTests.csproj
+++ b/Avalonia_Apps/AA20_SysDialogs/AA20_SysdialogsTests/AA20_SysdialogsTests.csproj
@@ -1,14 +1,18 @@
- net8.0;net9.0
+ net8.0
false
-
-
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
diff --git a/Avalonia_Apps/AA20_SysDialogs/Directory.Packages.props b/Avalonia_Apps/AA20_SysDialogs/Directory.Packages.props
index 74be44072..7eb87d870 100644
--- a/Avalonia_Apps/AA20_SysDialogs/Directory.Packages.props
+++ b/Avalonia_Apps/AA20_SysDialogs/Directory.Packages.props
@@ -1,34 +1,3 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj
index fe2831e70..02b409a9d 100644
--- a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj
+++ b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj
@@ -1,15 +1,21 @@
-
+
WinExe
- net8.0;net9.0
+ net8.0
enable
latest
+ true
-
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
diff --git a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/App.axaml.cs b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/App.axaml.cs
index 87b0086df..2b7e82f14 100644
--- a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/App.axaml.cs
+++ b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/App.axaml.cs
@@ -11,12 +11,13 @@
//
//
// ***********************************************************************
+using AA21_Buttons.ViewModels;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
-using AA21_Buttons.ViewModels;
using Microsoft.Extensions.DependencyInjection;
-using Avalonia.Controls.ApplicationLifetimes;
+using System;
namespace AA21_Buttons;
@@ -25,7 +26,8 @@ namespace AA21_Buttons;
///
public partial class App : Application
{
- private ServiceProvider? _serviceProvider;
+ private static ServiceProvider? _serviceProvider;
+ public static IServiceProvider Services => _serviceProvider;
public override void Initialize()
{
@@ -57,6 +59,7 @@ private static void ConfigureServices(ServiceCollection services)
{
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
}
}
diff --git a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Converters/BoolToColorConverter.cs b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Converters/BoolToColorConverter.cs
index 604dc1d15..45545ee53 100644
--- a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Converters/BoolToColorConverter.cs
+++ b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Converters/BoolToColorConverter.cs
@@ -2,10 +2,13 @@
// Copyright JC-Soft 2022
//
-using Avalonia.Data.Converters;
-using Avalonia.Media;
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Globalization;
+using Avalonia.Data;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
namespace AA21_Buttons.Converters;
@@ -17,48 +20,58 @@ public class BoolToColorConverter : IValueConverter
///
/// Farbe fr true-Werte (aktiver Button)
///
- public static Color TrueColor = Colors.Green;
+ public IBrush? TrueColor { get; set; } = Brushes.Lime;
///
/// Farbe fr false-Werte (inaktiver Button)
///
- public static Color FalseColor = Colors.DarkRed;
+ public IBrush? FalseColor { get; set; } = Brushes.Maroon;
///
/// Konvertiert einen Wert zu einer Farbe.
///
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
- return value switch
+ bool result = false;
+
+ // Direkter boolescher Wert
+ if (value is bool b)
+ {
+ result = b;
+ }
+ // Array-/Listen mit Index-Parameter
+ else if (parameter != null && TryGetIndex(parameter, out var index))
{
- bool b when parameter is string s && s.StartsWith("#") =>
- new SolidColorBrush(ParseColorFromParameter(s, b)),
- bool b =>
- new SolidColorBrush(b ? TrueColor : FalseColor),
- bool[] ba when parameter is string s && int.TryParse(s, out int idx) =>
- new SolidColorBrush(ba[idx] ? TrueColor : FalseColor),
- _ => new SolidColorBrush(FalseColor)
- };
+ if (value is bool[] arr && index >= 0 && index < arr.Length)
+ result = arr[index];
+ else if (value is IList list && index >= 0 && index < list.Count && list[index] is bool lb)
+ result = lb;
+ else if (value is IReadOnlyList ro && index >= 0 && index < ro.Count)
+ result = ro[index];
+ }
+
+ return result ? TrueColor : FalseColor;
}
///
- /// Umgekehrte Konvertierung nicht untersttzt.
+ /// Umgekehrte Konvertierung nicht verwendet.
///
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
- {
- throw new NotImplementedException();
- }
+ => BindingOperations.DoNothing;
- ///
- /// Parsed Farben aus Parameter-String (Format: "#color1:#color2").
- ///
- private static Color ParseColorFromParameter(string parameter, bool value)
+ private static bool TryGetIndex(object parameter, out int index)
{
- var colors = parameter.Split(':');
- if (colors.Length < 2)
- return value ? TrueColor : FalseColor;
-
- string colorStr = colors[value ? 1 : 0];
- return Color.Parse(colorStr);
+ index = -1;
+ switch (parameter)
+ {
+ case int i:
+ index = i ; // Annahme: Buttons verwenden 1-basierte Indizes
+ return true;
+ case string s when int.TryParse(s, out var parsed):
+ index = parsed ;
+ return true;
+ default:
+ return false;
+ }
}
}
diff --git a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/MainWindow.axaml b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/MainWindow.axaml
index 31d3b5c6b..a16db786d 100644
--- a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/MainWindow.axaml
+++ b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/MainWindow.axaml
@@ -10,7 +10,7 @@
-
+
diff --git a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/ViewModels/ButtonsViewViewModel.cs b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/ViewModels/ButtonsViewViewModel.cs
index ca8a2a289..0f042ce5a 100644
--- a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/ViewModels/ButtonsViewViewModel.cs
+++ b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/ViewModels/ButtonsViewViewModel.cs
@@ -37,7 +37,7 @@ public ButtonsViewViewModel()
///
/// Kann die Play-Aktion ausgefhrt werden?
///
- private bool CanPlay => LastPara != null;
+ private bool CanPlay(object? parameter) => true;// LastPara == null || LastPara != parameter;
///
/// Play-Befehl: Wechselt den Zustand des angeklickten Buttons und benachbarter Buttons.
diff --git a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Views/ButtonsView.axaml b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Views/ButtonsView.axaml
index 865c08f17..c1bda1844 100644
--- a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Views/ButtonsView.axaml
+++ b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Views/ButtonsView.axaml
@@ -1,14 +1,27 @@
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
@@ -86,4 +99,4 @@
VerticalAlignment="Bottom" />
-
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Views/ButtonsView.axaml.cs b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Views/ButtonsView.axaml.cs
new file mode 100644
index 000000000..5756e1eff
--- /dev/null
+++ b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/Views/ButtonsView.axaml.cs
@@ -0,0 +1,36 @@
+// ***********************************************************************
+// Assembly : MVVM_21_Buttons
+// Author : Mir
+// Created : 08-12-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-12-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using AA21_Buttons.ViewModels;
+using Avalonia.Controls;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace AA21_Buttons.Views;
+
+///
+/// ButtonsView: UserControl mit 3x3 Button-Grid für Flip-Spiel.
+///
+public partial class ButtonsView : UserControl
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ButtonsView()
+ {
+ InitializeComponent();
+ if (Design.IsDesignMode) return;
+
+ var vm = App.Services.GetRequiredService();
+ DataContext = vm;
+ }
+}
diff --git a/Avalonia_Apps/AA21_Buttons/Directory.Packages.props b/Avalonia_Apps/AA21_Buttons/Directory.Packages.props
index 12873f06b..6ffd9eac8 100644
--- a/Avalonia_Apps/AA21_Buttons/Directory.Packages.props
+++ b/Avalonia_Apps/AA21_Buttons/Directory.Packages.props
@@ -6,15 +6,15 @@
-
+
-
-
+
+
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/Avalonia_Apps/AA21_Buttons/MIGRATION_README.md b/Avalonia_Apps/AA21_Buttons/MIGRATION_README.md
new file mode 100644
index 000000000..1569e3f16
--- /dev/null
+++ b/Avalonia_Apps/AA21_Buttons/MIGRATION_README.md
@@ -0,0 +1,128 @@
+# AA21_Buttons Migration: WPF ? Avalonia.UI
+
+## Migrationsstatus
+
+Diese Migration hat die Grundstruktur fr die Konvertierung des WPF-Projekts AA21_Buttons zu Avalonia.UI mit folgenden Komponenten erstellt:
+
+## ? Abgeschlossene Schritte
+
+1. **Projektdatei** (`AA21_Buttons.csproj`)
+ - Konfiguriert fr Avalonia 11.3.8
+ - .NET 8.0-windows und .NET 9.0-windows Targets
+ - UTF-8 Encoding
+ - Zentrale Paketverwaltung
+
+2. **ViewModel Migration** (`ViewModels/ButtonsViewViewModel.cs`)
+ - Migriert zu MVVM Community Toolkit
+ - `ObservableObject` statt BaseViewModel
+ - `[RelayCommand]` statt DelegateCommand
+ - `[ObservableProperty]` fr Properties mit automatischem ChangeNotification
+
+3. **Views** (Avalonia.axaml)
+ - `MainWindow.axaml` - Hauptfenster mit TabControl
+ - `ButtonsView.axaml` - 3x3 Button Grid
+ - Vereinfachtes Binding-System fr Avalonia
+
+4. **Converter** (`Converters/BoolToColorConverter.cs`)
+ - Portiert zu Avalonia `IValueConverter`
+ - `SolidColorBrush` statt WPF Brush
+ - Untersttzung fr Array-Element-Konvertierung
+
+5. **Dependency Injection**
+ - `App.xaml.cs` mit `ServiceCollection`
+ - `MainWindowViewModel` als ViewModel-Container
+ - Singleton-Registrierung fr ViewModels
+
+6. **Entry Point** (`Program.cs`)
+ - Avalonia `AppBuilder` Setup
+ - `StartWithClassicDesktopLifetime` fr Desktop-Apps
+
+## ?? Datei-bersicht
+
+```
+AA21_Buttons/
+??? AA21_Buttons/
+? ??? AA21_Buttons.csproj (aktualisiert)
+? ??? App.axaml (neu)
+? ??? App.xaml.cs (migriert)
+? ??? MainWindow.axaml (neu)
+? ??? MainWindow.xaml.cs (migriert)
+? ??? Program.cs (neu)
+? ??? ViewModels/
+? ? ??? ButtonsViewViewModel.cs (migriert zu MVVM Toolkit)
+? ? ??? MainWindowViewModel.cs (neu)
+? ??? Views/
+? ? ??? ButtonsView.axaml (neu)
+? ? ??? ButtonsView.xaml.cs (migriert)
+? ??? Converters/
+? ? ??? BoolToColorConverter.cs (migriert zu Avalonia)
+? ??? Properties/
+? ??? Resources.resx (vereinfacht)
+??? Directory.Packages.props (zentrale Paketverwaltung)
+```
+
+## ?? Nachbearbeitung erforderlich
+
+Da NuGet-Restore in dieser Umgebung Probleme verursacht, fhre folgende Schritte lokal aus:
+
+```bash
+# 1. Solution in Visual Studio ffnen
+# 2. NuGet Package Manager ffnen (Tools ? NuGet Package Manager)
+# 3. Package Manager Console ausfhren:
+
+Update-Package -Reinstall Avalonia
+Update-Package -Reinstall Avalonia.Desktop
+Update-Package -Reinstall Avalonia.Themes.Fluent
+Update-Package -Reinstall CommunityToolkit.Mvvm
+Update-Package -Reinstall Microsoft.Extensions.DependencyInjection
+
+# 4. Projekt neu bauen
+Build ? Rebuild Solution
+
+# 5. Testen
+Ctrl+F5 zum Starten
+```
+
+## ?? Zentrale Pakete
+
+Die erforderlichen Pakete sind in `AA21_Buttons/Directory.Packages.props` definiert:
+- **Avalonia 11.3.8** - UI Framework
+- **Avalonia.Desktop 11.3.8** - Desktop-Untersttzung
+- **Avalonia.Themes.Fluent 11.3.8** - Fluent Theme
+- **Avalonia.Fonts.Inter 11.3.8** - Inter Font
+- **CommunityToolkit.Mvvm 8.4.0** - MVVM Pattern
+- **Microsoft.Extensions.DependencyInjection 9.0.10** - DI Container
+
+## ?? Spiellogik
+
+Das Spiel funktioniert wie folgt:
+- 33 Grid mit Buttons (1-9)
+- Klick auf Button: Toggles Button + benachbarte Buttons
+- Reset-Button: Setzt Spiel zurck (nur Button 1 aktiv)
+- Farbcodierung: Grn (aktiv) / Dunkelrot (inaktiv)
+
+## ?? MVVM-Architektur
+
+- **MainWindowViewModel**: Container fr andere ViewModels
+- **ButtonsViewViewModel**: Spiellogik mit RelayCommands
+- **BoolToColorConverter**: Konvertiert bool[]/bool zu SolidColorBrush
+- **Dependency Injection**: Alle ViewModels sind Singletons
+
+## ? Best Practices implementiert
+
+? Eine Klasse pro Datei
+? UTF-8 Encoding auf allen Dateien
+? MVVM Community Toolkit
+? Dependency Injection
+? Namespacing nach Verzeichnisstruktur
+? XML-Dokumentation (/// )
+? Avalonia statt WPF-Binding
+? Keine hardgecodeten Strings
+
+## ?? Nchste Schritte
+
+1. Lokal NuGet-Pakete installieren
+2. Projekt neu aufbauen
+3. App starten und Spiellogik testen
+4. Optional: Styling erweitern (aktuell minimal)
+5. Optional: Tests schreiben
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/AA22_AvlnCap.csproj b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/AA22_AvlnCap.csproj
new file mode 100644
index 000000000..75b089aa5
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/AA22_AvlnCap.csproj
@@ -0,0 +1,63 @@
+
+
+
+ WinExe
+ net8.0
+ true
+ app.manifest
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
+ $(MSBuildProjectName.Replace(" ", "_").Replace("_net",""))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.axaml b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.axaml
new file mode 100644
index 000000000..7ee66a2a1
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.axaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.axaml.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.axaml.cs
new file mode 100644
index 000000000..6858974fd
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.axaml.cs
@@ -0,0 +1,77 @@
+// ***********************************************************************
+// Assembly : AA22_AvlnCap
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using AA22_AvlnCap.ViewModels;
+using AA22_AvlnCap.Model;
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Data.Core.Plugins;
+using Avalonia.Markup.Xaml;
+using BaseLib.Helper;
+using BaseLib.Models;
+using BaseLib.Models.Interfaces;
+using Microsoft.Extensions.DependencyInjection;
+using System.Linq;
+using AA22_AvlnCap.ViewModels.Interfaces;
+
+namespace AA22_AvlnCap;
+
+///
+/// Avalonia App
+///
+public partial class App : Application
+{
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ var services = new ServiceCollection();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+
+ ServiceProvider container = services.BuildServiceProvider();
+ IoC.GetReqSrv = (type) => container.GetRequiredService(type);
+ IoC.GetSrv = (type) => container.GetService(type);
+
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ // Avoid duplicate validations from both Avalonia and the CommunityToolkit.
+ // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
+ DisableAvaloniaDataAnnotationValidation();
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = new MainWindowViewModel(),
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+
+ private void DisableAvaloniaDataAnnotationValidation()
+ {
+ // Get an array of plugins to remove
+ var dataValidationPluginsToRemove =
+ BindingPlugins.DataValidators.OfType().ToArray();
+
+ // remove each entry found
+ foreach (var plugin in dataValidationPluginsToRemove)
+ {
+ BindingPlugins.DataValidators.Remove(plugin);
+ }
+ }
+
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.config b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.config
new file mode 100644
index 000000000..bcb2ae2d0
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/App.config
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Converter/EnumToColorConverter.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Converter/EnumToColorConverter.cs
new file mode 100644
index 000000000..7f7308af8
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Converter/EnumToColorConverter.cs
@@ -0,0 +1,98 @@
+// ***********************************************************************
+// Assembly : AA22_AvlnCap
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+
+namespace AA22_AvlnCap.Converter;
+
+///
+/// Class EnumToColorConverter.
+/// Implements the
+///
+///
+public class EnumToColorConverter : IValueConverter
+{
+
+ public List colors { get; set; } = new List { };
+ ///
+ /// The zero color
+ ///
+ public static Color ZeroColor = Colors.Black;
+ ///
+ /// The true color
+ ///
+ public static Color TrueColor = Colors.Green;
+ ///
+ /// The false color
+ ///
+ public static Color FalseColor = Colors.Red;
+ ///
+ /// The three color
+ ///
+ public static Color ThreeColor = Colors.Yellow;
+ ///
+ /// The four color
+ ///
+ public static Color FourColor = Colors.Blue;
+
+ public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ // Return an Avalonia Brush for UI elements
+ static IBrush BrushFromColor(Color c) => new SolidColorBrush(c);
+
+ return value switch
+ {
+ // single tile value
+ int i when colors.Count > i && i >= 0 => BrushFromColor(colors[i]),
+ int i => BrushFromColor(i switch
+ {
+ 1 => FalseColor,
+ 2 => TrueColor,
+ 3 => ThreeColor,
+ 4 => FourColor,
+ _ => ZeroColor
+ }),
+
+ bool b when colors.Count>2 => BrushFromColor(b ? colors[2] : colors[1]),
+ bool b => BrushFromColor(b ? TrueColor : FalseColor),
+
+ bool[] ba when parameter is string s && int.TryParse(s,out _) && colors.Count>2=> BrushFromColor(ba[int.Parse(s)] ? colors[2] : colors[1]),
+ bool[] ba when parameter is string s && int.TryParse(s,out _)=> BrushFromColor(ba[int.Parse(s)] ? TrueColor : FalseColor),
+ int[] ia when parameter is string s && int.TryParse(s, out int _is) && _is < ia.Length && colors.Count > ia[_is]
+ => BrushFromColor(colors[ia[_is]]),
+ int[] ia when parameter is string s && int.TryParse(s, out int _is) && _is < ia.Length
+ => BrushFromColor(ia[_is] switch
+ {
+ 1 => FalseColor,
+ 2 => TrueColor,
+ 3 => ThreeColor,
+ 4 => FourColor,
+ _ => ZeroColor
+ }),
+ null => Brushes.Transparent,
+ bool[] => Brushes.Transparent,
+ int[]=> Brushes.Transparent,
+ _ => Brushes.Transparent
+ };
+ }
+
+
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Converter/IntToStringConverter.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Converter/IntToStringConverter.cs
new file mode 100644
index 000000000..ac25ed9fb
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Converter/IntToStringConverter.cs
@@ -0,0 +1,60 @@
+// ***********************************************************************
+// Assembly : AA22_AvlnCap
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace AA22_AvlnCap.Converter;
+
+///
+/// Class IntToStringConverter.
+/// Implements the
+///
+///
+public class IntToStringConverter : IValueConverter
+{
+ ///
+ /// Konvertiert einen Wert.
+ ///
+ /// Der von der Bindungsquelle erzeugte Wert.
+ /// Der Typ der Bindungsziel-Eigenschaft.
+ /// Der zu verwendende Konverterparameter.
+ /// Die im Konverter zu verwendende Kultur.
+ /// Ein konvertierter Wert.
+ /// Wenn die Methode zurückgibt, wird der gültige NULL-Wert verwendet.
+ public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value switch
+ {
+ int i => i.ToString(CultureInfo.InvariantCulture),
+ null => string.Empty,
+ _ => value?.ToString() ?? string.Empty,
+ };
+ }
+
+ ///
+ /// Konvertiert einen Wert.
+ ///
+ /// Der Wert, der vom Bindungsziel erzeugt wird.
+ /// Der Typ, in den konvertiert werden soll.
+ /// Der zu verwendende Konverterparameter.
+ /// Die im Konverter zu verwendende Kultur.
+ /// Ein konvertierter Wert.
+ /// Wenn die Methode zurückgibt, wird der gültige NULL-Wert verwendet.
+ ///
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Model/.info b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Model/.info
new file mode 100644
index 000000000..e69de29bb
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Model/CWpfCapModel.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Model/CWpfCapModel.cs
new file mode 100644
index 000000000..d932f7781
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Model/CWpfCapModel.cs
@@ -0,0 +1,138 @@
+using BaseLib.Models.Interfaces;
+using System;
+
+namespace AA22_AvlnCap.Model;
+
+public class CWpfCapModel : IWpfCapModel
+{
+ #region Properties
+ /// Gets a value indicating whether the tiles of this instance are sorted.
+ ///
+ /// true if this instance is sorted; otherwise, false.
+ public bool IsSorted => _IsSorted();
+
+ public int Width => _width;
+
+ public int Height => _height;
+
+ /// Occurs when the color of tiles were changed.
+ public event EventHandler? TileColorChanged;
+
+ private int[] _tiles;
+ private readonly int _width;
+ private readonly int _height;
+ private readonly IRandom _random;
+ #endregion
+
+ #region Methods
+ public CWpfCapModel(IRandom random) {
+ _width = 4;
+ _height = 4;
+ _random = random;
+ _tiles = new int[_width * _height];
+ }
+
+ public void Init()
+ {
+ for (int y = 0; y < _height; y++)
+ for (int x = 0; x < _width; x++)
+ SetTile(x, y, y + 1);
+ TileColorChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// Gets a value indicating whether the tiles of this instance are sorted.
+ ///
+ /// true if this instance is sorted; otherwise, false.
+ private bool _IsSorted()
+ {
+ bool result = true;
+ for (int y = 0; y < _height; y++)
+ for (int x = 0; x < _width; x++)
+ result &= TileColor(x, y) == y + 1;
+ return result;
+ }
+
+ private void SetTile(int x, int y, int tile)
+ {
+ if (x < 0 || x >= _width || y < 0 || y >= _height) return;
+ _tiles[y * _width + x] = tile;
+ }
+
+ public void MoveLeft(int row) => MoveLeft(row, true);
+ public void MoveRight(int rpw) => MoveRight(rpw, true);
+ public void MoveUp(int column) => MoveUp(column,true);
+ public void MoveDown(int column) => MoveDown(column, true);
+
+ public void Shuffle()
+ {
+ for (int i = 0; i < 100; i++)
+ {
+ var move = _random.Next(16);
+ switch (move % 4)
+ {
+ case 0:
+ MoveRight(move / 4,false);
+ break;
+ case 1:
+ MoveLeft(move / 4, false);
+ break;
+ case 2:
+ MoveUp(move / 4, false);
+ break;
+// case 3:
+ default:
+ MoveDown(move / 4, false);
+ break;
+
+ }
+ }
+ TileColorChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ public int TileColor(int x, int y)
+ {
+ if (x < 0 || x >= _width || y < 0 || y >= _height) return 0;
+ else
+ return _tiles[y * _width + x];
+ }
+
+ public void SetTiles(int[] tls)=> _tiles = tls;
+
+ private void MoveLeft(int row, bool notify)
+ {
+ var s = TileColor(0, row);
+ for (int i = 1; i < _width; i++)
+ SetTile(i - 1, row, TileColor(i, row));
+ SetTile(_width - 1, row, s);
+ if (notify) TileColorChanged?.Invoke(this, EventArgs.Empty);
+ }
+ private void MoveRight(int row, bool notify)
+ {
+ var s = TileColor(_width - 1, row);
+ for (int i = _width - 1; i > 0; i--)
+ SetTile(i, row, TileColor(i - 1, row));
+ SetTile(0, row, s);
+ if (notify) TileColorChanged?.Invoke(this, EventArgs.Empty);
+ }
+ private void MoveUp(int column, bool notify)
+ {
+ var s = TileColor(column,0);
+ for (int i = 1; i < _height; i++)
+ SetTile(column,i-1, TileColor(column,i ));
+ SetTile(column, _height - 1, s);
+
+ if (notify) TileColorChanged?.Invoke(this, EventArgs.Empty);
+
+ }
+ private void MoveDown(int column, bool notify)
+ {
+ var s = TileColor( column, _height - 1);
+ for (int i = _height - 1; i > 0; i--)
+ SetTile(column, i, TileColor(column, i-1 ));
+ SetTile(column, 0, s);
+
+ if (notify) TileColorChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ #endregion
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Model/IWpfCapModel.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Model/IWpfCapModel.cs
new file mode 100644
index 000000000..81b83eb11
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Model/IWpfCapModel.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace AA22_AvlnCap.Model;
+
+public interface IWpfCapModel
+{
+ /// Initializes the tiles of this instance.
+ void Init();
+ /// Shuffles the tiles of this instance.
+ void Shuffle();
+
+ /// Moves the tiles of the specified column up.
+ /// The column.
+ void MoveUp(int column);
+ /// Moves the tiles of the specified column down.
+ /// The column.
+ void MoveDown(int column);
+ /// Moves the tiles of the specified row to the left.
+ /// The row.
+ void MoveLeft(int row);
+ /// Moves the tiles of the specified row to the right.
+ /// The RPW.
+ void MoveRight(int rpw);
+
+ /// Returns the color of the Tile at the specified place.
+ /// The x.
+ /// The y.
+ ///
+ ///
+ ///
+ int TileColor(int x, int y);
+
+ /// Occurs when the color of tiles were changed.
+ event EventHandler TileColorChanged;
+ /// Gets a value indicating whether the tiles of this instance are sorted.
+ ///
+ /// true if this instance is sorted; otherwise, false.
+ bool IsSorted { get; }
+
+ /// Gets the width of the tile-field.
+ /// The width.
+ int Width { get; }
+ /// Gets the height of the tile-field.
+ /// The height.
+ int Height { get; }
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Program.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Program.cs
new file mode 100644
index 000000000..69d641858
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Program.cs
@@ -0,0 +1,21 @@
+using Avalonia;
+using System;
+
+namespace AA22_AvlnCap;
+
+internal static class Program
+{
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace();
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/AssemblyInfo.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..c8087abe8
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : MVVM_22_CTWpfCap
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("MVVM_22_CTWpfCap")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("MVVM_22_CTWpfCap")]
+[assembly: AssemblyCopyright("Copyright © JC-Soft 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Resources.Designer.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..a3b0dfcbd
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Resources.Designer.cs
@@ -0,0 +1,164 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace AA22_AvlnCap.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AA22_AvlnCap.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die using System;
+ ///
+ ///namespace AA22_AvlnCap.Model
+ ///{
+ /// public class CWpfCapModel : IWpfCapModel
+ /// {
+ /// #region Properties
+ /// /// <summary>Gets a value indicating whether the tiles of this instance are sorted.</summary>
+ /// /// <value>
+ /// /// <c>true</c> if this instance is sorted; otherwise, <c>false</c>.</value>
+ /// public bool IsSorted => _IsSorted();
+ ///
+ /// public int Width => _width;
+ ///
+ /// public int Height => _height;
+ ///
+ /// /// <summary>Occurs w [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CWpfCapModel {
+ get {
+ return ResourceManager.GetString("CWpfCapModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Tutorial #22a: the game WpfCap with Community-Toolbox ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : AA22_AvlnCap
+ ///// Author : Mir
+ ///// Created : 08-14-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 08-14-2022
+ ///// ***********************************************************************
+ ///// <copyright file="EnumToColorConverter.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2022
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// **************************************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string EnumToColorConverter {
+ get {
+ return ResourceManager.GetString("EnumToColorConverter", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM #22a CTWpfCap ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page x:Class="AA22_AvlnCap.Views.WpfCapView"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:AA22_AvlnCap.Views"
+ /// xmlns:p="clr-namespace:AA22_AvlnCap.Properties"
+ /// xmlns:mvvm="clr-namespace:AA22_AvlnCap.ViewModels"
+ /// [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string WpfCapView {
+ get {
+ return ResourceManager.GetString("WpfCapView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : AA22_AvlnCap
+ ///// Author : Mir
+ ///// Created : 08-18-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 08-16-2022
+ ///// ***********************************************************************
+ ///// <copyright file="WpfCapViewModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2022
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ********************************************************* [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string WpfCapViewModel {
+ get {
+ return ResourceManager.GetString("WpfCapViewModel", resourceCulture);
+ }
+ }
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Resources.resx b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Resources.resx
new file mode 100644
index 000000000..8950a1f81
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Resources.resx
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Model\CWpfCapModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ Tutorial #22a: the game WpfCap with Community-Toolbox
+
+
+ ..\Converter\EnumToColorConverter.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ MVVM #22a CTWpfCap
+
+
+ ..\Views\WpfCapView.axaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\WpfCapViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
\ No newline at end of file
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Settings.Designer.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..32e3ebcc1
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace AA22_AvlnCap.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
+ public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Settings.settings b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Settings.settings
new file mode 100644
index 000000000..c2dbd5cad
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/README.md b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/README.md
new file mode 100644
index 000000000..a501bccb6
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/README.md
@@ -0,0 +1,77 @@
+# AA22_AvlnCap
+
+> CommunityToolkit + WPF capabilities sample.
+
+## Overview
+System.Collections.Hashtable.Detailed
+
+## Key Learning Goals
+- Toolkit integration
+- Messenger
+- DI
+
+## Project Structure
+- Views: WPF XAML views illustrating bindings and styles
+- ViewModels: Reactive presentation logic using either classic or Toolkit paradigms
+- Models: Plain data / domain classes kept free of UI concerns
+- Services: (Where applicable) abstractions for dialogs, navigation, data access
+- Behaviors / Helpers: Reusable interaction patterns extending XAML without code-behind
+
+## Build & Run
+`
+dotnet build AA22_AvlnCap
+`
+If multiple target frameworks (e.g., net6.0-windows + net481) exist, you can specify one:
+`
+dotnet build AA22_AvlnCap -f net6.0-windows
+`
+Run (where an executable host exists):
+`
+dotnet run --project AA22_AvlnCap -f net6.0-windows
+`
+
+## Testing
+If a companion test project exists it is listed below. Execute:
+`
+dotnet test MVVM_22_CTWpfCapTests
+`
+(If '(none explicit)' the example is illustrated without dedicated automated tests yet.)
+
+## Test & Coverage Status
+
+| Metric | Status |
+|--------|--------|
+| Unit Tests | Implemented (see associated *Tests* project: MVVM_22_CTWpfCapTests) |
+| Line Coverage | TBD |
+| Branch Coverage | TBD |
+| Method Coverage | TBD |
+| Complexity Coverage | TBD |
+
+### Collecting Coverage Locally
+
+Classic (.NET Framework targets):
+`
+dotnet test MVVM_22_CTWpfCapTests --framework net481 /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
+`
+
+Modern multi-target (.NET >= 6):
+`
+dotnet test MVVM_22_CTWpfCapTests -c Debug /p:CollectCoverage=true /p:CoverletOutputFormat=lcov \
+ /p:Exclude="[xunit.*]*,[MSTest.*]*,[NUnit.*]*" --logger "trx" --results-directory ./TestResults
+`
+
+To merge coverage from several target frameworks:
+`
+dotnet tool install --global dotnet-reportgenerator-globaltool
+reportgenerator -reports:"**/coverage.info" -targetdir:CoverageReport -reporttypes:HtmlSummary;MarkdownSummaryGithub
+`
+
+> Replace TBD entries above with the real metrics after running the commands.
+
+## Extending This Example
+- Add additional ViewModels to explore more states
+- Introduce logging / diagnostics via ILogger abstractions
+- Write property-based tests for complex transformations
+
+## Notes
+This README was auto-generated. You can safely edit and commit refinements (the generator will skip existing files unless -Force is used).
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/ViewLocator.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/ViewLocator.cs
new file mode 100644
index 000000000..b052fb3a0
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/ViewLocator.cs
@@ -0,0 +1,63 @@
+// ***********************************************************************
+// Assembly : AA06_ValueConverter2
+// Author : Mir
+// Created : 01-11-2025
+//
+// Last Modified By : Mir
+// Last Modified On : 01-11-2025
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2025
+//
+//
+// ***********************************************************************
+using AA22_AvlnCap.ViewModels;
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using Avalonia.ViewModels;
+using System;
+
+///
+/// The AA06_ValueConverter2 namespace.
+///
+namespace AA22_AvlnCap;
+
+///
+/// Class ViewLocator.
+/// Implements the
+///
+///
+public class ViewLocator : IDataTemplate
+{
+
+ ///
+ /// Creates the control.
+ ///
+ /// The parameter.
+ /// The created control.
+ public Control? Build(object? param)
+ {
+ if (param is null)
+ return null;
+
+ var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
+ var type = Type.GetType(name);
+
+ if (type != null)
+ {
+ return (Control)Activator.CreateInstance(type)!;
+ }
+
+ return new TextBlock { Text = "Not Found: " + name };
+ }
+
+ ///
+ /// Checks to see if this data template matches the specified data.
+ ///
+ /// The data.
+ /// True if the data template can build a control for the data, otherwise false.
+ public bool Match(object? data)
+ {
+ return data is BaseViewModelCT;
+ }
+}
diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/ViewModels/ColData.cs b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/ViewModels/ColData.cs
new file mode 100644
index 000000000..e7d0195b0
--- /dev/null
+++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/ViewModels/ColData.cs
@@ -0,0 +1,63 @@
+// ***********************************************************************
+// Assembly : AA22_AvlnCap
+// Author : Mir
+// Created : 08-18-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-16-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using AA22_AvlnCap.ViewModels.Interfaces;
+using Avalonia.ViewModels;
+using CommunityToolkit.Mvvm.Input;
+using System.ComponentModel;
+namespace AA22_AvlnCap.ViewModels;
+
+///
+/// Class ColData.
+/// Implements the
+///
+///
+public class ColData : NotificationObjectCT, IColData
+{
+ ///
+ /// Gets or sets the col identifier.
+ ///
+ /// The col identifier.
+ public int ColId { get; set; }
+
+ ///
+ /// Gets or sets the move up.
+ ///
+ /// The move up.
+ public IRelayCommand
-
-
-
-
+
+
+
+
-
-
+
+
diff --git a/Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj b/Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj
index dde118e9e..ec0e7cb39 100644
--- a/Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj
+++ b/Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/Avalonia_Apps/CONTRIBUTING.md b/Avalonia_Apps/CONTRIBUTING.md
new file mode 100644
index 000000000..b28e00cdc
--- /dev/null
+++ b/Avalonia_Apps/CONTRIBUTING.md
@@ -0,0 +1,40 @@
+# Contributing Guide
+
+Thank you for contributing. This document defines the projects engineering standards and best practices.
+
+## Quick checklist (before opening a PR)
+- Follow MVVM using CommunityToolkit (partial property style).
+- Use DI with Microsoft.Extensions.DependencyInjection.
+- One class per file, meaningful names.
+- Fully document public APIs with XML docs.
+- Write/extend tests first (TDD) with MSTest v4.
+- Parameterize edge/single cases with `[DataRow]`.
+- Mock external dependencies with NSubstitute.
+- Keep code analyzers clean; treat warnings as errors.
+- Run `dotnet build` and `dotnet test` locally.
+
+## Supported SDKs
+- Multi-targeting across .NET (e.g., net6.0, net7.0, net8.0, net9.0, net10.0) and .NET Framework where applicable (e.g., net481, net48, net472, net462).
+- Use the latest stable Visual Studio and .NET SDKs installed.
+
+## Repository layout
+- `src//` Production code
+- `tests/.Tests/` Test projects (MSTest v4)
+- One class per file; file name = class name.
+
+## Coding standards (C#)
+- Enable nullable reference types in all projects.
+- Treat warnings as errors in CI and locally.
+- Keep methods small and cohesive.
+- Prefer `async/await`; do not block on async.
+- Avoid `static` state and singletons; prefer DI.
+- Use `var` where type is obvious.
+- Avoid magic strings/numbers; extract constants.
+- Prefer expression-bodied members when clear.
+- Always check arguments; throw `ArgumentException` variants for invalid input.
+
+### Documentation
+- XML-doc all public/protected types and members.
+- Document intent, constraints, and side-effects.
+- Use ``, ``, ``, ``.
+- Example:
\ No newline at end of file
diff --git a/Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj b/Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj
index 3f3d75864..228b64b39 100644
--- a/Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj
+++ b/Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj
@@ -2,7 +2,7 @@
Library
- net8.0;net9.0
+ net8.0
True
@@ -11,7 +11,13 @@
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
DEBUG;TRACE
@@ -20,10 +26,10 @@
-
-
+
+
-
+
diff --git a/Avalonia_Apps/Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj b/Avalonia_Apps/Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj
index f2270d076..e35c3cdfd 100644
--- a/Avalonia_Apps/Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj
+++ b/Avalonia_Apps/Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj
@@ -1,23 +1,29 @@
- net8.0;net9.0
+ net8.0
false
AnyCPU;x86
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
-
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Avalonia_Apps/Libraries/Avln_BaseLibTests/ViewModel/BaseTestViewModel.cs b/Avalonia_Apps/Libraries/Avln_BaseLibTests/ViewModel/BaseTestViewModel.cs
index 2e8023d26..a89ff4724 100644
--- a/Avalonia_Apps/Libraries/Avln_BaseLibTests/ViewModel/BaseTestViewModel.cs
+++ b/Avalonia_Apps/Libraries/Avln_BaseLibTests/ViewModel/BaseTestViewModel.cs
@@ -19,6 +19,7 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
+using System.Windows.Input;
///
/// The ViewModels namespace.
@@ -80,7 +81,15 @@ public virtual void TestModelProperiesTest(string sPropName, TypeCode tPropType,
Assert.AreEqual(xCanRead, p!.CanRead);
Assert.AreEqual(xCanWrite, p!.CanWrite);
if (xCanRead && GetDefaultData()?.TryGetValue(sPropName, out var oDefVal) == true)
- Assert.AreEqual(oDefVal, testModel.GetProp(sPropName));
+ if (typeof(ICommand).IsAssignableFrom(p.PropertyType))
+ {
+ var cmd = testModel.GetProp(sPropName) as ICommand;
+ Assert.IsNotNull(cmd);
+ Assert.AreEqual(oDefVal, cmd.CanExecute(null));
+ }
+ else
+ Assert.AreEqual(oDefVal, testModel.GetProp(sPropName));
+
}
}
diff --git a/Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj b/Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj
index daf2318ae..34a7c7ec8 100644
--- a/Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj
+++ b/Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj
@@ -2,17 +2,23 @@
Library
- net6.0;net7.0;net8.0;net9.0;net481;net48;net472;net462
+ net6.0;net7.0;net8.0;net481;net48;net472;net462
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
False
4772c317-55ff-4251-b766-1c41dfb672e5
True
False
- ..\sgLib.snk
-
+ ..\sgLib.snk
+
@@ -23,7 +29,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/Avalonia_Apps/Libraries/BaseLib/Helper/ListHelper.cs b/Avalonia_Apps/Libraries/BaseLib/Helper/ListHelper.cs
index 6a274e268..432f0175c 100644
--- a/Avalonia_Apps/Libraries/BaseLib/Helper/ListHelper.cs
+++ b/Avalonia_Apps/Libraries/BaseLib/Helper/ListHelper.cs
@@ -9,26 +9,72 @@
//
// Copyright © JC-Soft 2023
//
-//
+//
+// Provides extension methods for working with generic lists and arrays,
+// including item manipulation, swapping, and range generation utilities.
+//
// ***********************************************************************
using System;
using System.Collections.Generic;
///
-/// The Helper namespace.
+/// The Helper namespace contains utility classes and extension methods
+/// that provide common functionality across the BaseLib library.
///
namespace BaseLib.Helper;
///
-/// Class ListHelper.
+/// Provides static extension methods for manipulating generic lists and generating arrays.
+/// This class contains helper methods for common list operations such as moving items,
+/// swapping elements, and creating ranges of values.
///
+///
+/// All methods in this class are implemented as extension methods, allowing them to be
+/// called directly on instances or value types.
+///
+///
+///
+/// // Moving an item in a list
+/// var list = new List<string> { "A", "B", "C", "D" };
+/// list.MoveItem(0, 3); // Result: { "B", "C", "A", "D" }
+///
+/// // Swapping items
+/// list.Swap(0, 2); // Swaps items at index 0 and 2
+///
+/// // Creating a range
+/// int[] range = 1.To(5); // Result: { 1, 2, 3, 4, 5 }
+///
+///
public static class ListHelper
{
- /// Moves the item from source to target.
- /// The generic type of the list
- /// The list to change.
- /// The index of the source.
- /// The index of the destination.
+ ///
+ /// Moves an item from a source index to a destination index within the list.
+ ///
+ /// The type of elements in the list.
+ /// The list in which to move the item.
+ /// The zero-based index of the item to move.
+ /// The zero-based destination index where the item should be placed.
+ ///
+ ///
+ /// If the source and destination indices are equal, no operation is performed.
+ ///
+ ///
+ /// The method handles the index shift that occurs when removing an item before
+ /// the destination index. If an exception occurs during insertion, the item
+ /// is restored to its original position before re-throwing the exception.
+ ///
+ ///
+ ///
+ /// Thrown when or is outside
+ /// the valid range of indices for the list.
+ ///
+ ///
+ ///
+ /// var list = new List<int> { 1, 2, 3, 4, 5 };
+ /// list.MoveItem(0, 4); // Moves element at index 0 to index 4
+ /// // Result: { 2, 3, 4, 1, 5 }
+ ///
+ ///
public static void MoveItem(this IList list, int iSrc, int iDst)
{
if (iSrc == iDst) return;
@@ -46,18 +92,59 @@ public static void MoveItem(this IList list, int iSrc, int iDst)
}
///
- /// Exchanges the items.
+ /// Exchanges (swaps) the positions of two items in the list.
///
- /// The generic type of the list
- /// The list to change.
- /// The index of the first item.
- /// The index of the second item.
+ /// The type of elements in the list.
+ /// The list containing the items to swap.
+ /// The zero-based index of the first item to swap.
+ /// The zero-based index of the second item to swap.
+ ///
+ /// If both indices are equal, no operation is performed.
+ /// Uses tuple deconstruction for efficient in-place swapping.
+ ///
+ ///
+ /// Thrown when or is outside
+ /// the valid range of indices for the list.
+ ///
+ ///
+ ///
+ /// var list = new List<string> { "A", "B", "C" };
+ /// list.Swap(0, 2); // Swaps "A" and "C"
+ /// // Result: { "C", "B", "A" }
+ ///
+ ///
public static void Swap(this IList list, int iItm1, int iItm2)
{
if (iItm1 == iItm2) return;
- (list[iItm1], list[iItm2]) = (list[iItm2],list[iItm1]);
+ (list[iItm1], list[iItm2]) = (list[iItm2], list[iItm1]);
}
+ ///
+ /// Creates an array containing a range of values from to inclusive.
+ ///
+ ///
+ /// A value type that implements and can be converted to and from .
+ ///
+ ///
+ /// The instance used as an extension method anchor. This parameter is not used.
+ ///
+ /// The starting value of the range (inclusive).
+ /// The ending value of the range (inclusive).
+ ///
+ /// An array of type containing all values from
+ /// to inclusive. Returns an empty array if
+ /// is greater than .
+ ///
+ ///
+ /// This method uses internally, so it works best
+ /// with numeric types that can be represented as integers.
+ ///
+ ///
+ ///
+ /// int[] range = typeof(int).Range(1, 5); // Result: { 1, 2, 3, 4, 5 }
+ /// int[] empty = typeof(int).Range(5, 1); // Result: empty array
+ ///
+ ///
public static T[] Range(this Type _, T v1, T v2) where T : struct, IComparable
{
if (v1.CompareTo(v2) > 0)
@@ -67,6 +154,37 @@ public static T[] Range(this Type _, T v1, T v2) where T : struct, IComparabl
result[i] = (T)Convert.ChangeType(Convert.ToInt32(v1) + i, typeof(T));
return result;
}
+
+ ///
+ /// Creates an array containing a range of values from the current value to the specified end value inclusive.
+ ///
+ ///
+ /// A value type that implements and can be converted to and from .
+ ///
+ /// The starting value of the range (inclusive).
+ /// The ending value of the range (inclusive).
+ ///
+ /// An array of type containing all values from
+ /// to inclusive. Returns an empty array if
+ /// is greater than .
+ ///
+ ///
+ ///
+ /// This extension method provides a fluent syntax for creating ranges directly from a value.
+ ///
+ ///
+ /// This method uses internally, so it works best
+ /// with numeric types that can be represented as integers (e.g., ,
+ /// , ).
+ ///
+ ///
+ ///
+ ///
+ /// int[] range = 1.To(5); // Result: { 1, 2, 3, 4, 5 }
+ /// byte[] bytes = ((byte)0).To((byte)3); // Result: { 0, 1, 2, 3 }
+ /// int[] empty = 10.To(5); // Result: empty array
+ ///
+ ///
public static T[] To(this T v1, T v2) where T : struct, IComparable
{
if (v1.CompareTo(v2) > 0)
@@ -76,5 +194,4 @@ public static T[] To(this T v1, T v2) where T : struct, IComparable
result[i] = (T)Convert.ChangeType(Convert.ToInt32(v1) + i, typeof(T));
return result;
}
-
}
diff --git a/Avalonia_Apps/Libraries/BaseLib/Helper/ObjectHelper.cs b/Avalonia_Apps/Libraries/BaseLib/Helper/ObjectHelper.cs
index 04832da4d..686e531de 100644
--- a/Avalonia_Apps/Libraries/BaseLib/Helper/ObjectHelper.cs
+++ b/Avalonia_Apps/Libraries/BaseLib/Helper/ObjectHelper.cs
@@ -1,4 +1,16 @@
-
+// ***********************************************************************
+// Assembly : BaseLib
+// Author : Mir
+// Created : 03-27-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 03-27-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2025
+//
+//
+// ***********************************************************************
using BaseLib.Interfaces;
using System;
using System.Collections.Generic;
@@ -9,8 +21,54 @@
namespace BaseLib.Helper;
+///
+/// Provides extension methods for safe type conversion of instances to various primitive and common types.
+///
+///
+///
+/// This static class contains a collection of extension methods that facilitate the conversion of objects
+/// to specific types such as , , , ,
+/// , , and .
+///
+///
+/// Each conversion method handles various input types including:
+///
+/// - Direct type matches (returns the value as-is)
+/// - String parsing with appropriate format providers
+/// - interface implementations for recursive value extraction
+/// - and values (returns default)
+/// - implementations for standard .NET type conversions
+///
+///
+///
public static class ObjectHelper
{
+ ///
+ /// Converts an object to a 32-bit signed integer.
+ ///
+ /// The object to convert. Can be .
+ /// The default value to return when conversion fails. Defaults to 0.
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already an , returns it directly.
+ /// - If is a , performs an unchecked cast to .
+ /// - If is a , attempts to parse it as an integer.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If implements , uses with .
+ ///
+ ///
+ ///
+ ///
+ /// int result1 = "42".AsInt(); // Returns 42
+ /// int result2 = ((object)null).AsInt(); // Returns 0
+ /// int result3 = "invalid".AsInt(-1); // Returns -1
+ ///
+ ///
public static int AsInt(this object? obj, int def = default) => obj switch
{
int i => i,
@@ -23,6 +81,31 @@ public static class ObjectHelper
_ => def
};
+ ///
+ /// Converts an object to a 64-bit signed integer.
+ ///
+ /// The object to convert. Can be .
+ /// The default value to return when conversion fails. Defaults to 0.
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If is a , performs an unchecked cast to .
+ /// - If is a , attempts to parse it as a long integer.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If implements , uses with .
+ ///
+ ///
+ ///
+ ///
+ /// long result1 = "9223372036854775807".AsLong(); // Returns long.MaxValue
+ /// long result2 = ((object)null).AsLong(); // Returns 0
+ ///
+ ///
public static long AsLong(this object? obj, int def = default) => obj switch
{
long i => i,
@@ -35,7 +118,34 @@ public static class ObjectHelper
_ => def
};
-
+ ///
+ /// Converts an object to an enumeration value of the specified type.
+ ///
+ /// The enumeration type to convert to. Must be a value type and an .
+ /// The object to convert. Can be .
+ ///
+ /// The converted enumeration value of type , or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already of type , returns it directly.
+ /// - If is an within the valid enum range, converts it to the enum value.
+ /// - If is a , attempts to parse it as an enum name.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If is a , performs an unchecked cast and converts to enum.
+ /// - If implements , converts to first, then to enum.
+ ///
+ ///
+ ///
+ ///
+ /// enum Color { Red, Green, Blue }
+ /// Color result1 = "Green".AsEnum<Color>(); // Returns Color.Green
+ /// Color result2 = 1.AsEnum<Color>(); // Returns Color.Green
+ /// Color result3 = "Invalid".AsEnum<Color>(); // Returns Color.Red (default)
+ ///
+ ///
public static T AsEnum(this object? obj) where T : struct, Enum => obj switch
{
T t => t,
@@ -51,6 +161,37 @@ string s when Enum.TryParse(s, out var t) => t,
_ => default
};
+ ///
+ /// Converts an object to a value.
+ ///
+ /// The object to convert. Can be .
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If is an in the format YYYYMMDD (e.g., 20231225), parses it as a date.
+ /// - If is an that is 0 or greater than 10000000, returns .
+ /// - If implements , recursively converts its .
+ /// - If is a without dots, attempts to parse using .
+ /// - If is a with dots, attempts to parse using .
+ /// - If is a representing a numeric date (YYYYMMDD), parses accordingly.
+ /// - If is a , treats it as ticks for .
+ /// - If is a , converts from OLE Automation date format.
+ /// - If is a , creates a date with year = char value + 1900.
+ /// - If is , returns .
+ /// - If implements , converts from OLE Automation date format.
+ ///
+ ///
+ ///
+ ///
+ /// DateTime result1 = "2023-12-25".AsDate(); // Returns December 25, 2023
+ /// DateTime result2 = 20231225.AsDate(); // Returns December 25, 2023
+ /// DateTime result3 = ((object)null).AsDate(); // Returns DateTime.MinValue
+ ///
+ ///
public static DateTime AsDate(this object? obj) => obj switch
{
DateTime dt => dt,
@@ -69,6 +210,35 @@ string s when DateTime.TryParse(s, CultureInfo.CurrentUICulture, DateTimeStyles.
_ => default,
};
+ ///
+ /// Converts an object to a double-precision floating-point number.
+ ///
+ /// The object to convert. Can be .
+ ///
+ /// The culture-specific formatting information to use for parsing strings.
+ /// If , is used.
+ ///
+ ///
+ /// The converted value, or 0.0 if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If is a , attempts to parse it using and the specified culture.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns (0.0).
+ /// - If is a , returns its numeric value.
+ /// - If implements , uses .
+ ///
+ ///
+ ///
+ ///
+ /// double result1 = "3.14".AsDouble(); // Returns 3.14
+ /// double result2 = "3,14".AsDouble(CultureInfo.GetCultureInfo("de-DE")); // Returns 3.14
+ /// double result3 = ((object)null).AsDouble(); // Returns 0.0
+ ///
+ ///
public static double AsDouble(this object? obj, CultureInfo? culture = null) => obj switch
{
double d => d,
@@ -82,6 +252,33 @@ string s when double.TryParse(s, NumberStyles.Float, culture ?? CultureInfo.Inva
_ => default
};
+ ///
+ /// Converts an object to a Boolean value.
+ ///
+ /// The object to convert. Can be .
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If is a , attempts to parse it using .
+ /// - If is a equal to "1", returns ; otherwise .
+ /// - If is a , returns for '1', 'T', or 't'.
+ /// - If implements , returns if not equal to 0.
+ ///
+ ///
+ ///
+ ///
+ /// bool result1 = "true".AsBool(); // Returns true
+ /// bool result2 = "1".AsBool(); // Returns true
+ /// bool result3 = 'T'.AsBool(); // Returns true
+ /// bool result4 = ((object)null).AsBool(); // Returns false
+ ///
+ ///
public static bool AsBool(this object? obj) => obj switch
{
bool x => x,
@@ -95,6 +292,32 @@ string s when bool.TryParse(s, out var b) => b,
_ => default
};
+ ///
+ /// Converts an object to a value.
+ ///
+ /// The object to convert. Can be .
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If is a , attempts to parse it as a GUID.
+ /// - If is a representing an integer, creates a GUID with that integer as the first component.
+ /// - If is a , creates a GUID with that value as the first component.
+ /// - If implements , converts to and creates a GUID with that as the first component.
+ ///
+ ///
+ ///
+ ///
+ /// Guid result1 = "550e8400-e29b-41d4-a716-446655440000".AsGUID(); // Returns the parsed GUID
+ /// Guid result2 = 42.AsGUID(); // Returns GUID with first component = 42
+ /// Guid result3 = ((object)null).AsGUID(); // Returns Guid.Empty
+ ///
+ ///
public static Guid AsGUID(this object? obj) => obj switch
{
Guid g => g,
@@ -109,22 +332,137 @@ string s when Guid.TryParse(s, out var g) => g,
_ => default
};
+ ///
+ /// Executes an action on an object and returns a specified value.
+ ///
+ /// The type of the return value.
+ /// The type of the object on which the action is performed.
+ /// The object on which to perform the action.
+ /// The action to perform on .
+ /// The value to return after the action is executed.
+ /// The value after executing the action.
+ ///
+ /// This method is useful for performing side effects on an object while returning
+ /// a value in a fluent or expression-based context.
+ ///
+ ///
+ ///
+ /// var list = new List<int>();
+ /// int count = list.SetRet(l => l.Add(42), 1); // Adds 42 to list, returns 1
+ ///
+ ///
public static T SetRet(this T2 obj, Action action, T v)
{
action(obj);
return v;
}
}
+
+///
+/// Provides extension methods for dictionary operations with 1-based indexing.
+///
+///
+/// This static class contains helper methods for
+/// that use 1-based indexing internally while accepting 0-based index parameters.
+/// This is useful for compatibility with legacy VB6-style control arrays.
+///
public static class ObjectHelper2
{
+ ///
+ /// Sets a value in the dictionary at a 1-based internal index corresponding to the given 0-based index.
+ ///
+ /// The type of the values in the dictionary.
+ /// The dictionary to modify.
+ /// The value to set.
+ /// The 0-based index. Internally stored at + 1.
+ ///
+ /// This method stores the value at key ( + 1) to maintain
+ /// compatibility with 1-based indexing systems.
+ ///
+ ///
+ ///
+ /// var dic = new Dictionary<int, string>();
+ /// dic.SetIndex("Hello", 0); // Stores "Hello" at key 1
+ ///
+ ///
public static void SetIndex(this Dictionary dic, T value, int index) => dic[index + 1] = value;
+
+ ///
+ /// Gets the 0-based index of a value in the dictionary.
+ ///
+ /// The type of the values in the dictionary.
+ /// The dictionary to search.
+ /// The value to find.
+ ///
+ /// The 0-based index of the value (internal key - 1), or -1 if the value is not found.
+ ///
+ ///
+ /// This method searches for the value using
+ /// and returns the corresponding 0-based index.
+ ///
+ ///
+ ///
+ /// var dic = new Dictionary<int, string> { { 1, "Hello" }, { 2, "World" } };
+ /// int index = dic.GetIndex("World"); // Returns 1 (key 2 - 1)
+ ///
+ ///
public static int GetIndex(this Dictionary dic, T value) => dic.Where((itm) => itm.Value?.Equals(value) ?? false)
.FirstOrDefault().Key - 1;
}
+
+///
+/// Represents a generic control array that uses 0-based external indexing with 1-based internal storage.
+///
+/// The type of elements in the control array.
+///
+///
+/// This class extends to provide functionality
+/// similar to Visual Basic 6 control arrays. The external indexer uses 0-based indexing,
+/// but values are stored internally with 1-based keys.
+///
+///
+/// The class implements to support designer initialization
+/// scenarios, though the implementation is empty.
+///
+///
+///
+///
+/// var controls = new ControlArray<Button>();
+/// controls[1] = new Button(); // Stored at key 2
+/// var button = controls[0]; // Retrieves from key 1
+///
+///
public class ControlArray : Dictionary, ISupportInitialize
{
+ ///
+ /// Signals the object that initialization is starting.
+ ///
+ ///
+ /// This method is part of the interface
+ /// and is intentionally left empty as no special initialization logic is required.
+ ///
public void BeginInit() { }
+
+ ///
+ /// Signals the object that initialization is complete.
+ ///
+ ///
+ /// This method is part of the interface
+ /// and is intentionally left empty as no special finalization logic is required.
+ ///
public void EndInit() { }
+ ///
+ /// Gets the element at the specified 0-based index.
+ ///
+ /// The 0-based index of the element to retrieve.
+ /// The element stored at the internal key ( + 1).
+ ///
+ /// Thrown when no element exists at the specified index.
+ ///
+ ///
+ /// This indexer provides 0-based access while internally using 1-based keys,
+ /// maintaining compatibility with VB6-style control arrays.
+ ///
public new T this[int i] => base[i+1];
}
diff --git a/Avalonia_Apps/Libraries/BaseLib/Helper/StringUtils.cs b/Avalonia_Apps/Libraries/BaseLib/Helper/StringUtils.cs
index d2a7396a4..aa6497254 100644
--- a/Avalonia_Apps/Libraries/BaseLib/Helper/StringUtils.cs
+++ b/Avalonia_Apps/Libraries/BaseLib/Helper/StringUtils.cs
@@ -9,7 +9,10 @@
//
// Copyright JC-Soft 2023
//
-//
+//
+// Provides a collection of utility extension methods for string manipulation,
+// including quoting/unquoting, splitting, formatting, validation, and substring operations.
+//
// ***********************************************************************
using System.Collections.Generic;
using System;
@@ -18,25 +21,96 @@
namespace BaseLib.Helper;
-/// A static class with useful string-routines.
+///
+/// A static class providing a comprehensive set of utility extension methods for string manipulation.
+///
+///
+///
+/// This class contains various helper methods for common string operations such as:
+///
+///
+/// - Quoting and unquoting strings (escaping/unescaping special characters)
+/// - String formatting with parameters
+/// - Splitting strings by separators with support for quoted sections
+/// - Tab padding and normalization
+/// - Substring extraction (Left, Right)
+/// - Identifier validation
+/// - Pattern matching (StartswithAny, EndswithAny, ContainsAny)
+///
+///
+/// All methods are implemented as extension methods on or related types,
+/// allowing for fluent method chaining syntax.
+///
+///
+///
+///
+/// // Quoting a multi-line string
+/// string quoted = "Hello\nWorld".Quote(); // Returns "Hello\\nWorld"
+///
+/// // Getting the first part of a string
+/// string first = "Hello World".SFirst(); // Returns "Hello"
+///
+/// // Checking if a string is a valid identifier
+/// bool valid = "MyVariable".IsValidIdentifyer(); // Returns true
+///
+///
public static class StringUtils
{
- public const string AlphaUpper="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- public const string AlphaLower="abcdefghijklmnopqrstuvwxyz";
- public const string Alpha=AlphaUpper+AlphaLower;
- public const string Numeric="0123456789";
- public const string AlphaNumeric=Alpha+Numeric;
+ ///
+ /// Contains all uppercase letters of the English alphabet (A-Z).
+ ///
+ /// The string "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
+ public const string AlphaUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ ///
+ /// Contains all lowercase letters of the English alphabet (a-z).
+ ///
+ /// The string "abcdefghijklmnopqrstuvwxyz".
+ public const string AlphaLower = "abcdefghijklmnopqrstuvwxyz";
///
- ///
- /// Makes the specified string quotable.
-> by escaping special Characters (Linefeed, NewLine, Tab ...)
- ///
- ///
- ///
+ /// Contains all letters of the English alphabet, both uppercase and lowercase.
///
- /// the string .
- /// Quoted/escaped string
- /// Does the opposite of
In other words: Puts a given text into one line of text.
+ /// The concatenation of and .
+ public const string Alpha = AlphaUpper + AlphaLower;
+
+ ///
+ /// Contains all numeric digits (0-9).
+ ///
+ /// The string "0123456789".
+ public const string Numeric = "0123456789";
+
+ ///
+ /// Contains all alphanumeric characters (letters and digits).
+ ///
+ /// The concatenation of and .
+ public const string AlphaNumeric = Alpha + Numeric;
+
+ ///
+ /// Escapes special characters in a string to make it suitable for single-line representation.
+ ///
+ /// The input string to quote. Can be null.
+ ///
+ /// A string with special characters escaped:
+ ///
+ /// - Backslash (\) becomes \\
+ /// - Tab (\t) becomes \t
+ /// - Carriage return (\r) becomes \r
+ /// - Line feed (\n) becomes \n
+ ///
+ /// Returns an empty string if the input is null or if an exception occurs.
+ ///
+ ///
+ /// This method is the inverse of .
+ /// It converts a multi-line text into a single-line representation by escaping control characters.
+ ///
+ ///
+ ///
+ /// string input = "Line1\nLine2\tTabbed";
+ /// string quoted = input.Quote(); // Returns "Line1\\nLine2\\tTabbed"
+ ///
+ ///
+ ///
public static string Quote(this string? aStr)
{
try
@@ -49,11 +123,38 @@ public static string Quote(this string? aStr)
}
catch { return ""; }
}
- /// Un-quotes the given string.
by un-escaping special characters (Linefeed, Newline, Tab ...)
- /// a string.
- /// the unquoted/un-escaped string
- /// Does the opposite of
In other words: Takes a given line of text and extracts the (original) text.
- public static string UnQuote(this string? aStr)
+
+ ///
+ /// Unescapes special character sequences in a string, restoring the original control characters.
+ ///
+ /// The input string to unquote. Can be null.
+ ///
+ /// A string with escape sequences converted back to their original characters:
+ ///
+ /// - \\ becomes backslash (\)
+ /// - \t becomes tab character
+ /// - \r becomes carriage return
+ /// - \n becomes line feed
+ ///
+ /// Returns an empty string if the input is null.
+ ///
+ ///
+ ///
+ /// This method is the inverse of .
+ /// It converts a single-line escaped representation back to its original multi-line form.
+ ///
+ ///
+ /// The method uses a temporary placeholder character (U+0001) to handle nested backslashes correctly.
+ ///
+ ///
+ ///
+ ///
+ /// string escaped = "Line1\\nLine2\\tTabbed";
+ /// string unquoted = escaped.UnQuote(); // Returns "Line1\nLine2\tTabbed"
+ ///
+ ///
+ ///
+ public static string UnQuote(this string? aStr)
=> (aStr ?? "")
.Replace("\\\\", "\\\u0001")
.Replace("\\t", "\t")
@@ -62,22 +163,50 @@ public static string UnQuote(this string? aStr)
.Replace("\\\u0001", "\\");
///
- /// Formats the specified string with par.
+ /// Formats a string using the specified parameters, similar to .
///
- /// a string.
- /// The par.
- /// System.String.
- ///
+ /// The format string containing placeholders like {0}, {1}, etc.
+ /// An array of objects to format into the string.
+ /// The formatted string with placeholders replaced by the corresponding parameter values.
+ ///
+ /// This is a convenience extension method that wraps ,
+ /// allowing for a more fluent syntax when formatting strings.
+ ///
+ ///
+ ///
+ /// string template = "Hello, {0}! You have {1} messages.";
+ /// string result = template.Format("John", 5); // Returns "Hello, John! You have 5 messages."
+ ///
+ ///
+ ///
+ /// Thrown when the format string is invalid or when there are more placeholders than parameters.
+ ///
public static string Format(this string aStr, params object[] par)
=> string.Format(aStr, par);
///
- /// Gets the first part of the string separated by the separator.
+ /// Extracts the first part of a string before the specified separator.
///
- /// The s.
- /// The sep.
- /// System.String.
- ///
+ /// The input string to split.
+ /// The separator string to search for. Defaults to a single space.
+ ///
+ /// The portion of the string before the first occurrence of the separator.
+ /// If the separator is not found, returns the entire original string.
+ ///
+ ///
+ /// This method is useful for parsing delimited strings when only the first element is needed.
+ /// For the remaining part after the separator, use .
+ ///
+ ///
+ ///
+ /// string input = "key=value";
+ /// string key = input.SFirst("="); // Returns "key"
+ ///
+ /// string words = "Hello World Today";
+ /// string firstWord = words.SFirst(); // Returns "Hello"
+ ///
+ ///
+ ///
public static string SFirst(this string s, string sep = " ")
{
if (!s.Contains(sep)) return s;
@@ -85,24 +214,61 @@ public static string SFirst(this string s, string sep = " ")
}
///
- /// Gets the other part of the string separated by the separator.
+ /// Extracts the remaining part of a string after the first occurrence of the specified separator.
///
- /// The s.
- /// The sep.
- /// System.String.
- ///
+ /// The input string to split.
+ /// The separator string to search for. Defaults to a single space.
+ ///
+ /// The portion of the string after the first occurrence of the separator.
+ /// If the separator is not found, returns an empty string.
+ ///
+ ///
+ /// This method complements for parsing delimited strings.
+ /// Together, they can be used to iterate through delimited elements.
+ ///
+ ///
+ ///
+ /// string input = "key=value=extra";
+ /// string rest = input.SRest("="); // Returns "value=extra"
+ ///
+ /// string words = "Hello World Today";
+ /// string remaining = words.SRest(); // Returns "World Today"
+ ///
+ ///
+ ///
public static string SRest(this string s, string sep = " ")
{
if (!s.Contains(sep)) return "";
return s.Substring(s.IndexOf(sep) + 1);
}
-
- /// Pads the tab of the string with spaces.
- /// The string.
- /// The offset from the start of the line.
- /// System.String.
- ///
+ ///
+ /// Replaces tab characters in a string with the appropriate number of spaces to align to tab stops.
+ ///
+ /// The input string containing tab characters.
+ ///
+ /// The offset from the start of the line to use for tab stop calculation.
+ /// Defaults to 0.
+ ///
+ ///
+ /// A string with all tab characters replaced by spaces, aligned to 8-character tab stops.
+ ///
+ ///
+ ///
+ /// Tab stops are calculated at every 8th character position, taking into account the current
+ /// position in the line plus the specified offset.
+ ///
+ ///
+ /// This is useful for converting tab-formatted text to fixed-width spacing for display
+ /// in environments that don't support tab characters.
+ ///
+ ///
+ ///
+ ///
+ /// string input = "Name\tAge\tCity";
+ /// string padded = input.PadTab(); // Returns "Name Age City" (aligned to 8-char tabs)
+ ///
+ ///
public static string PadTab(this string s, int offs = 0)
{
var _s = s.Split('\t');
@@ -119,19 +285,61 @@ public static string PadTab(this string s, int offs = 0)
static int TabLen(int l, int o) => l + o + (8 - (l + o) % 8) - o;
}
- /// Converts to "Normal" case. (first letter to upper- rest to lowercase)
- /// The string.
- /// System.String.
- /// e.G: pEtEr will be converted to Peter.
+ ///
+ /// Converts a string to "normal" case, where the first letter is uppercase and all subsequent letters are lowercase.
+ ///
+ /// The input string to convert.
+ ///
+ /// The string with the first character in uppercase and all remaining characters in lowercase.
+ /// Returns the original string unchanged if it is null or empty.
+ ///
+ ///
+ /// This method is useful for normalizing names or titles that may have inconsistent casing.
+ /// Only the first character is capitalized; all other characters are converted to lowercase.
+ ///
+ ///
+ ///
+ /// string name = "pEtEr";
+ /// string normalized = name.ToNormal(); // Returns "Peter"
+ ///
+ /// string allCaps = "HELLO";
+ /// string result = allCaps.ToNormal(); // Returns "Hello"
+ ///
+ ///
public static string ToNormal(this string s)
=> string.IsNullOrEmpty(s) ? s : s.Substring(0, 1).ToUpper() + s.Remove(0, 1).ToLower();
- /// Does a quoted split of the given string.
- /// The data.
- /// The separator.
- /// The quotation mark.
- /// List<System.String>.
- ///
+ ///
+ /// Splits a string by a separator while respecting quoted sections that may contain the separator.
+ ///
+ /// The input string to split.
+ /// The separator string to split by. Defaults to comma (",").
+ /// The quotation mark string that defines quoted sections. Defaults to double quote (").
+ ///
+ /// A of strings representing the split elements.
+ /// Quoted sections are preserved as single elements even if they contain the separator.
+ /// Leading and trailing whitespace is trimmed from each element.
+ ///
+ ///
+ ///
+ /// This method is particularly useful for parsing CSV data where fields may contain commas
+ /// enclosed in quotes. The quote marks themselves are removed from the resulting elements.
+ ///
+ ///
+ /// If a quoted section is not properly closed, the remaining content is added as a final element.
+ ///
+ ///
+ ///
+ ///
+ /// string csv = "John,\"Doe, Jr.\",25";
+ /// List<string> fields = csv.QuotedSplit();
+ /// // Returns: ["John", "Doe, Jr.", "25"]
+ ///
+ /// string data = "name='John Smith';age='30'";
+ /// List<string> parts = data.QuotedSplit(";", "'");
+ /// // Returns: ["name=John Smith", "age=30"]
+ ///
+ ///
public static List QuotedSplit(this string Data, string Separator = ",", string QuoteMark = "\"")
{
var arPreSplit = Data.Split(new string[] { Separator }, StringSplitOptions.None);
@@ -170,14 +378,59 @@ public static List QuotedSplit(this string Data, string Separator = ",",
return result;
}
- public static bool EndswithAny(this string s,params string[] strings)
+ ///
+ /// Determines whether the string ends with any of the specified suffixes.
+ ///
+ /// The string to check.
+ /// An array of suffix strings to test against.
+ ///
+ /// true if the string ends with any of the specified suffixes as a complete word boundary;
+ /// otherwise, false.
+ ///
+ ///
+ ///
+ /// This method checks for word-boundary endings by prepending a space to both the input string
+ /// and each suffix before comparison. This ensures that "test" does not match "contest" when
+ /// checking for "test" as an ending.
+ ///
+ ///
+ ///
+ ///
+ /// bool result1 = "Hello World".EndswithAny("World", "Test"); // Returns true
+ /// bool result2 = "contest".EndswithAny("test"); // Returns false (word boundary check)
+ ///
+ ///
+ ///
+ ///
+ public static bool EndswithAny(this string s, params string[] strings)
{
foreach (var item in strings)
- if ((" "+s).EndsWith(" "+item))
+ if ((" " + s).EndsWith(" " + item))
return true;
return false;
}
+ ///
+ /// Determines whether the string contains any of the specified substrings.
+ ///
+ /// The string to search in.
+ /// An array of substrings to search for.
+ ///
+ /// true if the string contains any of the specified substrings;
+ /// otherwise, false.
+ ///
+ ///
+ /// The search is case-sensitive. The method returns true as soon as
+ /// the first matching substring is found.
+ ///
+ ///
+ ///
+ /// bool hasKeyword = "The quick brown fox".ContainsAny("cat", "dog", "fox"); // Returns true
+ /// bool noMatch = "Hello World".ContainsAny("xyz", "123"); // Returns false
+ ///
+ ///
+ ///
+ ///
public static bool ContainsAny(this string s, params string[] strings)
{
foreach (var item in strings)
@@ -185,69 +438,193 @@ public static bool ContainsAny(this string s, params string[] strings)
return true;
return false;
}
+
+ ///
+ /// Determines whether the string starts with any of the specified prefixes.
+ ///
+ /// The string to check.
+ /// An array of prefix strings to test against.
+ ///
+ /// true if the string starts with any of the specified prefixes as a complete word boundary;
+ /// otherwise, false.
+ ///
+ ///
+ ///
+ /// This method checks for word-boundary beginnings by appending a space to both the input string
+ /// and each prefix before comparison. This ensures that "test" does not match "testing" when
+ /// checking for "test" as a beginning.
+ ///
+ ///
+ ///
+ ///
+ /// bool result1 = "Hello World".StartswithAny("Hello", "Hi"); // Returns true
+ /// bool result2 = "testing".StartswithAny("test"); // Returns false (word boundary check)
+ ///
+ ///
+ ///
+ ///
public static bool StartswithAny(this string s, params string[] strings)
{
foreach (var item in strings)
- if ((s+" ").StartsWith(item+" "))
+ if ((s + " ").StartsWith(item + " "))
return true;
return false;
}
-
+
///
- /// Prft, ob der angegebene String ein gltiger Bezeichner (Identifier) ist.
- /// Ein gltiger Bezeichner beginnt mit einem Grobuchstaben (A-Z) und enthlt nur Buchstaben, Ziffern oder Unterstriche.
- /// Leere oder nur aus Leerzeichen bestehende Strings sind ungltig.
+ /// Determines whether the specified string is a valid identifier according to specific naming rules.
///
- /// Der zu prfende String.
- /// True, wenn der String ein gltiger Bezeichner ist, sonst false.
+ /// The string to validate. Can be null.
+ ///
+ /// true if the string is a valid identifier; otherwise, false.
+ ///
+ ///
+ ///
+ /// A valid identifier must meet the following criteria:
+ ///
+ ///
+ /// - Cannot be null, empty, or contain only whitespace
+ /// - Must begin with an uppercase letter (A-Z)
+ /// - Can only contain letters (A-Z, a-z), digits (0-9), or underscores (_)
+ ///
+ ///
+ /// Note: The validation is case-insensitive for subsequent characters, but the first character
+ /// must be an alphabetic character (converted to uppercase for checking).
+ ///
+ ///
+ ///
+ ///
+ /// bool valid1 = "MyVariable".IsValidIdentifyer(); // Returns true
+ /// bool valid2 = "my_variable_123".IsValidIdentifyer(); // Returns true
+ /// bool invalid1 = "123Variable".IsValidIdentifyer(); // Returns false (starts with digit)
+ /// bool invalid2 = "my-variable".IsValidIdentifyer(); // Returns false (contains hyphen)
+ /// bool invalid3 = "".IsValidIdentifyer(); // Returns false (empty)
+ ///
+ ///
public static bool IsValidIdentifyer(this string? s)
{
if (string.IsNullOrWhiteSpace(s)) return false;
var _s = s!.ToUpper();
if (!AlphaUpper.Contains(_s[0])) return false;
foreach (var c in _s)
- if (!(AlphaNumeric+"_").Contains(c)) return false;
+ if (!(AlphaNumeric + "_").Contains(c)) return false;
return true;
}
///
- /// Gibt die ersten iCnt Zeichen des Strings zurck.
- /// Bei positivem iCnt werden die ersten iCnt Zeichen geliefert.
- /// Bei negativem iCnt werden alle bis auf die letzten |iCnt| Zeichen geliefert.
- /// Ist iCnt grer als die Lnge des Strings, wird der gesamte String zurckgegeben.
+ /// Returns the leftmost characters of a string up to the specified count.
///
- /// Der Eingabestring.
- /// Anzahl der gewnschten Zeichen (positiv: von links, negativ: bis auf die letzten |iCnt| Zeichen).
- /// Der entsprechend gekrzte String.
+ /// The input string.
+ ///
+ /// The number of characters to return.
+ ///
+ /// - Positive value: Returns the first characters from the left
+ /// - Negative value: Returns all characters except the last || characters
+ ///
+ ///
+ ///
+ /// A substring containing the specified number of leftmost characters.
+ /// If exceeds the string length, the entire string is returned.
+ /// If the result would be negative length, an empty string is returned.
+ ///
+ ///
+ /// This method provides Python-like string slicing behavior for the beginning of strings.
+ ///
+ ///
+ ///
+ /// string text = "Hello World";
+ ///
+ /// // Positive count - get first N characters
+ /// string first5 = text.Left(5); // Returns "Hello"
+ /// string first20 = text.Left(20); // Returns "Hello World" (entire string)
+ ///
+ /// // Negative count - exclude last N characters
+ /// string allButLast3 = text.Left(-3); // Returns "Hello Wo" (excludes "rld")
+ ///
+ ///
+ ///
public static string Left(this string data, int iCnt)
=> iCnt >= 0
? data.Substring(0, Math.Min(data.Length, iCnt))
: data.Substring(0, Math.Max(0, data.Length + iCnt));
///
- /// Gibt die letzten iCnt Zeichen des Strings zurck.
- /// Bei positivem iCnt werden die letzten iCnt Zeichen geliefert.
- /// Bei negativem iCnt werden die ersten |iCnt| Zeichen entfernt und der Rest geliefert.
- /// Ist iCnt grer als die Lnge des Strings, wird der gesamte String zurckgegeben.
+ /// Returns the rightmost characters of a string up to the specified count.
///
- /// Der Eingabestring.
- /// Anzahl der gewnschten Zeichen (positiv: von rechts, negativ: ab dem |iCnt|-ten Zeichen von links).
- /// Der entsprechend gekrzte String.
+ /// The input string.
+ ///
+ /// The number of characters to return.
+ ///
+ /// - Positive value: Returns the last characters from the right
+ /// - Negative value: Returns all characters starting from the ||th position
+ ///
+ ///
+ ///
+ /// A substring containing the specified number of rightmost characters.
+ /// If exceeds the string length, the entire string is returned.
+ ///
+ ///
+ /// This method provides Python-like string slicing behavior for the end of strings.
+ ///
+ ///
+ ///
+ /// string text = "Hello World";
+ ///
+ /// // Positive count - get last N characters
+ /// string last5 = text.Right(5); // Returns "World"
+ /// string last20 = text.Right(20); // Returns "Hello World" (entire string)
+ ///
+ /// // Negative count - skip first N characters
+ /// string skipFirst3 = text.Right(-3); // Returns "lo World" (skips "Hel")
+ ///
+ ///
+ ///
public static string Right(this string data, int iCnt)
=> iCnt >= 0
? data.Substring(Math.Max(0, data.Length - iCnt))
: data.Substring(Math.Min(data.Length, -iCnt));
///
- /// Gibt eine String-Reprsentation des Objekts zurck.
- /// Falls das Objekt ein String ist, wird es direkt zurckgegeben.
- /// Falls das Objekt das Interface IHasValue implementiert, wird dessen Value als String zurckgegeben.
- /// Bei null wird ein leerer String geliefert, ansonsten wird ToString() verwendet.
+ /// Converts an object to its string representation with special handling for certain types.
///
- /// The data.
- /// The format.(optional)
- /// System.String.
- public static string AsString(this object? data,string? format =null)
+ /// The object to convert. Can be null.
+ ///
+ /// An optional format string. Currently unused but reserved for future implementation.
+ ///
+ ///
+ /// A string representation of the object according to the following rules:
+ ///
+ /// - If is a , it is returned directly
+ /// - If implements , the Value property's string representation is returned
+ /// - If is null, an empty string is returned
+ /// - Otherwise, is called, with null results converted to empty string
+ ///
+ ///
+ ///
+ ///
+ /// This method provides a null-safe way to convert objects to strings, with special handling
+ /// for value wrapper types that implement .
+ ///
+ ///
+ /// The parameter is included for API consistency but is not
+ /// currently used in the implementation.
+ ///
+ ///
+ ///
+ ///
+ /// // String passthrough
+ /// string s = "Hello".AsString(); // Returns "Hello"
+ ///
+ /// // Null handling
+ /// object? nullObj = null;
+ /// string empty = nullObj.AsString(); // Returns ""
+ ///
+ /// // Object conversion
+ /// int number = 42;
+ /// string numStr = number.AsString(); // Returns "42"
+ ///
+ ///
+ public static string AsString(this object? data, string? format = null)
=> data switch
{
string s => s,
@@ -255,16 +632,49 @@ public static string AsString(this object? data,string? format =null)
null => "",
object o => o.ToString() ?? "",
};
-
+
///
- /// Copies the elements of the given string array into the specified list of strings, starting at the given offset.
- /// If the target list is null, a new list with sufficient capacity is created.
- /// Only elements that fit within the bounds of the target list are copied.
+ /// Copies elements from a string array into a target list of strings at a specified offset.
///
/// The source array of strings to copy from.
- /// The target list to copy into. If null, a new list is created.
- /// The zero-based index in the target list at which copying begins. Can be negative.
- /// The target list with the copied elements.
+ ///
+ /// The target list to copy into. If null, a new array is created with
+ /// sufficient capacity to hold all source elements considering the offset.
+ ///
+ ///
+ /// The zero-based index in the target list at which copying begins.
+ /// Can be negative, which will skip the first || elements of the source array.
+ ///
+ ///
+ /// The target list with the copied elements. If was null,
+ /// returns the newly created array.
+ ///
+ ///
+ ///
+ /// Only elements that fit within the bounds of the target list are copied. Elements that would
+ /// fall outside the target list boundaries (either negative indices or beyond the list count)
+ /// are silently ignored.
+ ///
+ ///
+ /// When is null, a new string array is created with size
+ /// Math.Max(0, asData.Length + offs).
+ ///
+ ///
+ ///
+ ///
+ /// // Copy into a new array
+ /// string[] source = { "a", "b", "c" };
+ /// IList<string> result = source.IntoString(); // Creates ["a", "b", "c"]
+ ///
+ /// // Copy with offset into existing array
+ /// string[] target = new string[5];
+ /// source.IntoString(target, 1); // target becomes [null, "a", "b", "c", null]
+ ///
+ /// // Copy with negative offset (skips first element of source)
+ /// string[] target2 = new string[2];
+ /// source.IntoString(target2, -1); // target2 becomes ["b", "c"]
+ ///
+ ///
public static IList IntoString(this string[] asData, IList? asKont = null, int offs = 0)
{
asKont ??= new string[Math.Max(0, asData.Length + offs)];
@@ -273,5 +683,4 @@ public static IList IntoString(this string[] asData, IList? asKo
asKont[i + offs] = asData[i];
return asKont;
}
-
}
diff --git a/Avalonia_Apps/Libraries/BaseLib/Interfaces/IConsole.cs b/Avalonia_Apps/Libraries/BaseLib/Interfaces/IConsole.cs
index 30d2bbb15..708334cb3 100644
--- a/Avalonia_Apps/Libraries/BaseLib/Interfaces/IConsole.cs
+++ b/Avalonia_Apps/Libraries/BaseLib/Interfaces/IConsole.cs
@@ -15,24 +15,148 @@
namespace BaseLib.Interfaces;
+///
+/// Defines an abstraction layer for console operations, enabling dependency injection
+/// and testability for console-based applications.
+///
+///
+/// This interface provides a comprehensive set of console operations including:
+///
+/// - Color management for foreground and background
+/// - Window sizing and positioning
+/// - Input/output operations
+/// - Cursor positioning
+/// - Audio feedback capabilities
+///
+/// Implementations of this interface can wrap for production use
+/// or provide mock implementations for unit testing purposes.
+///
public interface IConsole
{
+ ///
+ /// Gets or sets the foreground color of the console output.
+ ///
+ ///
+ /// A value representing the text color.
+ ///
ConsoleColor ForegroundColor { get; set; }
+
+ ///
+ /// Gets or sets the background color of the console output.
+ ///
+ ///
+ /// A value representing the background color behind the text.
+ ///
ConsoleColor BackgroundColor { get; set; }
+
+ ///
+ /// Gets a value indicating whether the console output stream has been redirected.
+ ///
+ ///
+ /// true if the output is redirected to a file or another stream; otherwise, false.
+ ///
bool IsOutputRedirected { get; }
+
+ ///
+ /// Gets a value indicating whether a key press is available in the input stream.
+ ///
+ ///
+ /// true if a key press is available to be read; otherwise, false.
+ ///
bool KeyAvailable { get; }
+
+ ///
+ /// Gets the largest possible number of console window rows based on the current font and display resolution.
+ ///
+ ///
+ /// The maximum number of rows that can be displayed in the console window.
+ ///
int LargestWindowHeight { get; }
+
+ ///
+ /// Gets or sets the title to display in the console window's title bar.
+ ///
+ ///
+ /// A string representing the console window title.
+ ///
string Title { get; set; }
+
+ ///
+ /// Gets or sets the height of the console window area in rows.
+ ///
+ ///
+ /// The number of rows visible in the console window.
+ ///
int WindowHeight { get; set; }
+
+ ///
+ /// Gets or sets the width of the console window area in columns.
+ ///
+ ///
+ /// The number of columns visible in the console window.
+ ///
int WindowWidth { get; set; }
+ ///
+ /// Plays a beep sound through the console speaker.
+ ///
+ /// The frequency of the beep sound in hertz (Hz). Valid range is typically 37 to 32767 Hz.
+ /// The duration of the beep sound in milliseconds.
void Beep(int freq, int len);
+
+ ///
+ /// Clears the console buffer and corresponding console window of all displayed information.
+ ///
void Clear();
+
+ ///
+ /// Gets the current position of the cursor within the console buffer.
+ ///
+ ///
+ /// A tuple containing the Left (column) and Top (row) coordinates of the cursor position,
+ /// where (0, 0) represents the top-left corner of the console buffer.
+ ///
(int Left, int Top) GetCursorPosition();
+
+ ///
+ /// Obtains the next character or function key pressed by the user.
+ ///
+ ///
+ /// A object describing the key pressed, including the character
+ /// and any modifier keys (Alt, Shift, Control); or null if no key is available.
+ ///
ConsoleKeyInfo? ReadKey();
+
+ ///
+ /// Reads the next line of characters from the standard input stream.
+ ///
+ ///
+ /// The next line of characters from the input stream, or an empty string if no input is available.
+ ///
string ReadLine();
+
+ ///
+ /// Sets the position of the cursor within the console buffer.
+ ///
+ /// The column position of the cursor, where 0 is the leftmost column.
+ /// The row position of the cursor, where 0 is the topmost row.
void SetCursorPosition(int left, int top);
+
+ ///
+ /// Writes the specified Unicode character to the standard output stream.
+ ///
+ /// The character to write to the console.
void Write(char ch);
+
+ ///
+ /// Writes the specified string value to the standard output stream.
+ ///
+ /// The string to write. Can be null, in which case nothing is written.
void Write(string? st);
+
+ ///
+ /// Writes the specified string value, followed by the current line terminator, to the standard output stream.
+ ///
+ /// The string to write. Can be null or empty. Defaults to an empty string.
void WriteLine(string? st = "");
}
\ No newline at end of file
diff --git a/Avalonia_Apps/Libraries/BaseLib/Interfaces/INotifyPropertyChangedAdv.cs b/Avalonia_Apps/Libraries/BaseLib/Interfaces/INotifyPropertyChangedAdv.cs
index 2a1ad57e7..fe4f6a289 100644
--- a/Avalonia_Apps/Libraries/BaseLib/Interfaces/INotifyPropertyChangedAdv.cs
+++ b/Avalonia_Apps/Libraries/BaseLib/Interfaces/INotifyPropertyChangedAdv.cs
@@ -116,7 +116,7 @@ public PropertyChangedAdvEventArgs(string propertyName,object? oldVal,object? ne
#if !NET6_0_OR_GREATER
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
#endif
- public delegate void PropertyChangedAdvEventHandler(object sender, PropertyChangedAdvEventArgs e);
+ public delegate void PropertyChangedAdvEventHandler(object? sender, PropertyChangedAdvEventArgs e);
//
// Zusammenfassung:
diff --git a/Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj b/Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj
index bc4847031..8a1e73ac9 100644
--- a/Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj
+++ b/Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj
@@ -1,13 +1,20 @@
- net8.0;net9.0;net10.0;net481;net48;net472;net462
+ net8.0;net481;net48;net472;net462
false
+ true
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Avalonia_Apps/Libraries/BaseLibTests/Helper/FileUtilsTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Helper/FileUtilsTests.cs
index 142a1a646..40b629dff 100644
--- a/Avalonia_Apps/Libraries/BaseLibTests/Helper/FileUtilsTests.cs
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Helper/FileUtilsTests.cs
@@ -159,7 +159,7 @@ public void SaveFileTest1(int iPreMode, string sFileName, bool _)
PrepareFiles(ePreMode, Path.Combine(sLocalTestPath, sFileName));
sUserData = "Test 123";
- Assert.IsTrue(FileUtils.SaveFile(SaveTestFile, Path.Combine(sLocalTestPath, sFileName), this));
+ Assert.IsTrue(FileUtils.SaveFile(SaveTestFile!, Path.Combine(sLocalTestPath, sFileName), this));
Assert.IsTrue(File.Exists(Path.Combine(sLocalTestPath, sFileName)));
}
@@ -196,7 +196,7 @@ public void SaveFileTest2(int iPreMode, string sFileName, bool xExp)
PrepareFiles(ePreMode, Path.Combine(sLocalTestPath, sFileName));
sUserData = "Test 123";
eException = new FieldAccessException("Dummy Dummy");
- Assert.ThrowsExactly(() => FileUtils.SaveFile(SaveTestFile, Path.Combine(sLocalTestPath, sFileName), this));
+ Assert.ThrowsExactly(() => FileUtils.SaveFile(SaveTestFile!, Path.Combine(sLocalTestPath, sFileName), this));
Assert.AreEqual(xExp, File.Exists(Path.Combine(sLocalTestPath, sFileName)));
}
@@ -214,7 +214,7 @@ public void SaveFileTest4()
var sFileName = "test.nix";
try
{
- Assert.ThrowsExactly(()=>FileUtils.SaveFile(SaveTestFile2, Path.Combine(sLocalTestPath, sFileName), this));
+ Assert.ThrowsExactly(()=>FileUtils.SaveFile(SaveTestFile2!, Path.Combine(sLocalTestPath, sFileName), this));
Assert.IsFalse(File.Exists(Path.Combine(sLocalTestPath, sFileName)));
}
finally
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ListHelperTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Helper/ListHelperTests.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ListHelperTests.cs
rename to Avalonia_Apps/Libraries/BaseLibTests/Helper/ListHelperTests.cs
index 435dfd3e4..7f49e6103 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ListHelperTests.cs
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Helper/ListHelperTests.cs
@@ -1,67 +1,67 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.Linq;
-using static BaseLib.Helper.TestHelper;
-
-namespace BaseLib.Helper.Tests;
-
-[TestClass()]
-public class ListHelperTests
-{
- [TestMethod()]
- [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 2, new object[] { 0, 1, 2, 3, 4 })]
- [DataRow(new object[] { 0, 1, 2, 3, 4 }, 4, 0, new object[] { 4, 0, 1, 2, 3 })]
- [DataRow(new object[] { 0, 1, 2, 3, 4 }, 1, 3, new object[] { 0, 2, 1, 3, 4 })]
- [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 5, new object[] { 0, 1, 3, 4, 2 })]
- [DataRow(new object[] { 0, 1, 2, 3, 4 }, 1, 6, new object[] { 0, 1, 2, 3, 4 })]
- public void MoveItemTest(object[] aoSrc, int iSrc, int iDst, object[] asExp)
- {
- var aoResult = aoSrc.ToList();
- if (iDst > asExp.Length)
- Assert.ThrowsExactly(() => aoResult.MoveItem(iSrc, iDst));
- else
- aoResult.MoveItem(iSrc, iDst);
- AssertAreEqual(asExp, aoResult.ToArray());
- }
-
- [TestMethod()]
- [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 2, new object[] { 0, 1, 2, 3, 4 })]
- [DataRow(new object[] { 0, 1, 2, 3, 4 }, 4, 0, new object[] { 4, 1, 2, 3, 0 })]
- public void SwapTest(object[] aoSrc, int iSrc, int iDst, object[] asExp)
- {
- aoSrc.Swap(iSrc, iDst);
- AssertAreEqual(asExp, aoSrc);
- }
-
- [TestMethod()]
- [DataRow(0, -1, new int[] { })]
- [DataRow(2, 2, new[] { 2 })]
- [DataRow(0, 4, new[] { 0, 1, 2, 3, 4 })]
- public void RangeTest(int iSrc, int iDst, int[] aiExp)
- {
- AssertAreEqual(aiExp, typeof(int).Range(iSrc, iDst));
- }
-
- [TestMethod()]
- [DataRow('2', '6', new char[] { '2', '3', '4', '5', '6' })]
- public void RangeTest2(char iSrc, char iDst, char[] aoExp)
- {
- AssertAreEqual(aoExp, typeof(object).Range(iSrc, iDst));
- }
-
- [TestMethod()]
- [DataRow((byte)0, (byte)4, new byte[] { 0, 1, 2, 3, 4 })]
- public void RangeTest3(byte iSrc, byte iDst, byte[] aoExp)
- {
- AssertAreEqual(aoExp, typeof(object).Range(iSrc, iDst));
- }
-
- [TestMethod()]
- [DataRow(0, -1, new int[] { })]
- [DataRow(2, 2, new[] { 2 })]
- [DataRow(0, 4, new[] { 0, 1, 2, 3, 4 })]
- public void ToTest(int iSrc, int iDst, int[] aoExp)
- {
- AssertAreEqual(aoExp, iSrc.To(iDst));
- }
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Linq;
+using static BaseLib.Helper.TestHelper;
+
+namespace BaseLib.Helper.Tests;
+
+[TestClass()]
+public class ListHelperTests
+{
+ [TestMethod()]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 2, new object[] { 0, 1, 2, 3, 4 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 4, 0, new object[] { 4, 0, 1, 2, 3 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 1, 3, new object[] { 0, 2, 1, 3, 4 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 5, new object[] { 0, 1, 3, 4, 2 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 1, 6, new object[] { 0, 1, 2, 3, 4 })]
+ public void MoveItemTest(object[] aoSrc, int iSrc, int iDst, object[] asExp)
+ {
+ var aoResult = aoSrc.ToList();
+ if (iDst > asExp.Length)
+ Assert.ThrowsExactly(() => aoResult.MoveItem(iSrc, iDst));
+ else
+ aoResult.MoveItem(iSrc, iDst);
+ AssertAreEqual(asExp, aoResult.ToArray());
+ }
+
+ [TestMethod()]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 2, new object[] { 0, 1, 2, 3, 4 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 4, 0, new object[] { 4, 1, 2, 3, 0 })]
+ public void SwapTest(object[] aoSrc, int iSrc, int iDst, object[] asExp)
+ {
+ aoSrc.Swap(iSrc, iDst);
+ AssertAreEqual(asExp, aoSrc);
+ }
+
+ [TestMethod()]
+ [DataRow(0, -1, new int[] { })]
+ [DataRow(2, 2, new[] { 2 })]
+ [DataRow(0, 4, new[] { 0, 1, 2, 3, 4 })]
+ public void RangeTest(int iSrc, int iDst, int[] aiExp)
+ {
+ AssertAreEqual(aiExp, typeof(int).Range(iSrc, iDst));
+ }
+
+ [TestMethod()]
+ [DataRow('2', '6', new char[] { '2', '3', '4', '5', '6' })]
+ public void RangeTest2(char iSrc, char iDst, char[] aoExp)
+ {
+ AssertAreEqual(aoExp, typeof(object).Range(iSrc, iDst));
+ }
+
+ [TestMethod()]
+ [DataRow((byte)0, (byte)4, new byte[] { 0, 1, 2, 3, 4 })]
+ public void RangeTest3(byte iSrc, byte iDst, byte[] aoExp)
+ {
+ AssertAreEqual(aoExp, typeof(object).Range(iSrc, iDst));
+ }
+
+ [TestMethod()]
+ [DataRow(0, -1, new int[] { })]
+ [DataRow(2, 2, new[] { 2 })]
+ [DataRow(0, 4, new[] { 0, 1, 2, 3, 4 })]
+ public void ToTest(int iSrc, int iDst, int[] aoExp)
+ {
+ AssertAreEqual(aoExp, iSrc.To(iDst));
+ }
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ObjectHelper2Tests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Helper/ObjectHelper2Tests.cs
similarity index 68%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ObjectHelper2Tests.cs
rename to Avalonia_Apps/Libraries/BaseLibTests/Helper/ObjectHelper2Tests.cs
index 533397adf..333e3b3f7 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ObjectHelper2Tests.cs
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Helper/ObjectHelper2Tests.cs
@@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.Generic;
namespace BaseLib.Helper.Tests;
@@ -10,7 +11,8 @@ public class ObjectHelper2Tests
#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Fügen Sie ggf. den „erforderlichen“ Modifizierer hinzu, oder deklarieren Sie den Modifizierer als NULL-Werte zulassend.
[TestInitialize]
- public void Init() {
+ public void Init()
+ {
_testClass = new ControlArray();
}
@@ -20,23 +22,24 @@ public void SetIndexTest()
// Act
_testClass.SetIndex(8, 4);
_testClass.SetIndex(2, 1);
- // Assert
- Assert.AreEqual(2, _testClass.Count);
- Assert.AreEqual(8, _testClass[5]);
- Assert.AreEqual(2, _testClass[2]);
+ // Assert
+ Assert.HasCount(2, _testClass);
+ Assert.AreEqual(8, _testClass[4]);
+ Assert.AreEqual(2, _testClass[1]);
}
[TestMethod()]
public void GetIndexTest()
{
// Arrange
- _testClass[5] = 8;
- _testClass[1] = null!;
- _testClass[2] = 2;
+ var d = (Dictionary)_testClass;
+ d[5] = 8;
+ d[1] = null!;
+ d[2] = 2;
// Assert
- Assert.AreEqual(4,_testClass.GetIndex(8));
- Assert.AreEqual(1,_testClass.GetIndex(2));
- Assert.AreEqual(-1,_testClass.GetIndex(1));
+ Assert.AreEqual(4, _testClass.GetIndex(8));
+ Assert.AreEqual(1, _testClass.GetIndex(2));
+ Assert.AreEqual(-1, _testClass.GetIndex(1));
}
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ObjectHelperTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Helper/ObjectHelperTests.cs
similarity index 96%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ObjectHelperTests.cs
rename to Avalonia_Apps/Libraries/BaseLibTests/Helper/ObjectHelperTests.cs
index 05b481c7f..1357942ca 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/Helper/ObjectHelperTests.cs
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Helper/ObjectHelperTests.cs
@@ -36,7 +36,6 @@ public void AsIntTest(object? obj,int iExp)
var result = ObjectHelper.AsInt(obj,-1);
// Assert
- Assert.IsNotNull(result);
Assert.AreEqual(iExp, result);
}
@@ -69,7 +68,6 @@ public void AsLongTest(object? obj, long lExp)
var result = ObjectHelper.AsLong(obj, -1);
// Assert
- Assert.IsNotNull(result);
Assert.AreEqual(lExp, result);
}
@@ -111,7 +109,6 @@ public void AsEnumTest(object? obj, TestEnum expected)
var result = ObjectHelper.AsEnum(obj);
// Assert
- Assert.IsNotNull(result);
Assert.AreEqual(expected, result);
}
@@ -149,7 +146,6 @@ _ when s.Contains("2000") && DateTime.TryParse(s, out var dt) => dt,
var result = ObjectHelper.AsDate(obj);
// Assert
- Assert.IsNotNull(result);
Assert.AreEqual(DateTime.Parse(expected), result);
}
@@ -186,7 +182,6 @@ public void AsDoubleTest(object? obj, double dExp)
var result = ObjectHelper.AsDouble(obj);
// Assert
- Assert.IsNotNull(result);
Assert.AreEqual(dExp, result);
}
@@ -225,7 +220,6 @@ public void AsBoolTest(object? obj, bool bExp)
var result = ObjectHelper.AsBool(obj);
// Assert
- Assert.IsNotNull(result);
Assert.AreEqual(bExp, result);
}
@@ -257,7 +251,6 @@ public void AsGUIDTest(object? obj, string expected)
var result = ObjectHelper.AsGUID(obj);
// Assert
- Assert.IsNotNull(result);
Assert.AreEqual(Guid.Parse(expected), result);
}
@@ -280,6 +273,6 @@ public void SetRetTest(object? input, bool expected)
// Assert
Assert.AreEqual(expected, result);
Assert.AreEqual(input, _o);
- Assert.AreEqual(true, actionCalled);
+ Assert.IsTrue(actionCalled);
}
}
\ No newline at end of file
diff --git a/Avalonia_Apps/Libraries/BaseLibTests/Helper/StringUtilsTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Helper/StringUtilsTests.cs
index 6e93308eb..10120f27c 100644
--- a/Avalonia_Apps/Libraries/BaseLibTests/Helper/StringUtilsTests.cs
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Helper/StringUtilsTests.cs
@@ -326,4 +326,38 @@ public void IsValidIdentifyerTest(string sAct, bool xExp)
{
Assert.AreEqual(xExp,sAct.IsValidIdentifyer());
}
+
+ [TestMethod()]
+ [DataRow("This is a test",14, "This is a test")]
+ [DataRow("This is a test",0, "")]
+ [DataRow("This is a test",-1, "This is a tes")]
+ [DataRow("This is a test",1, "T")]
+ [DataRow("This is a test",20, "This is a test")]
+ public void LeftTest(string sAct,int iAct, string sExp)
+ {
+ Assert.AreEqual(sExp, sAct.Left(iAct));
+ }
+
+ [TestMethod()]
+ [DataRow("This is a test",14, "This is a test")]
+ [DataRow("This is a test",0, "")]
+ [DataRow("This is a test",-1, "his is a test")]
+ [DataRow("This is a test",1, "t")]
+ [DataRow("This is a test",20, "This is a test")]
+ public void RightTest(string sAct,int iAct, string sExp)
+ {
+ Assert.AreEqual(sExp, sAct.Right(iAct));
+ }
+
+ [TestMethod()]
+ [DataRow(null, "")]
+ [DataRow("", "")]
+ [DataRow(true, "True")]
+ [DataRow(false, "False")]
+ [DataRow(1234, "1234")]
+
+ public void AsStringTest(object? oAct,string sExp)
+ {
+ Assert.AreEqual(sExp, oAct.AsString());
+ }
}
diff --git a/Avalonia_Apps/Libraries/BaseLibTests/Interfaces/IParentedObjectTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Interfaces/IParentedObjectTests.cs
index c895582be..8d8b48ab1 100644
--- a/Avalonia_Apps/Libraries/BaseLibTests/Interfaces/IParentedObjectTests.cs
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Interfaces/IParentedObjectTests.cs
@@ -8,7 +8,7 @@ namespace BaseLib_netTests.Interfaces
class TestIPClass : IParentedObject>
{
private List? _parent=null;
- public string Name { get; set; }
+ public string? Name { get; set; }
public List? GetParent()
=> _parent;
@@ -28,7 +28,7 @@ public override string ToString()
[TestClass]
public class IParentedObjectTests
{
- private IParentedObject>[] _child;
+ private IParentedObject>[]? _child;
private readonly List _par1=new();
private readonly List _par2=new();
@@ -45,7 +45,7 @@ public void Init()
#if NET5_0_OR_GREATER
[TestMethod]
public void ParentTest() {
- Assert.IsNull(_child[0].Parent);
+ Assert.IsNull(_child![0].Parent);
Assert.IsEmpty(_par1);
Assert.IsEmpty(_par2);
_child[0].Parent = _par1;
@@ -64,7 +64,7 @@ public void ParentTest() {
[TestMethod]
public void SetParentTest()
{
- Assert.IsNull(_child[0].GetParent());
+ Assert.IsNull(_child![0].GetParent());
Assert.IsEmpty(_par1);
Assert.IsEmpty(_par2);
_child[0].SetParent(_par1);
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/Helper/MVVM/CRandomTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Model/CRandomTests.cs
similarity index 95%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/Helper/MVVM/CRandomTests.cs
rename to Avalonia_Apps/Libraries/BaseLibTests/Model/CRandomTests.cs
index 2ae735d23..61baf4b03 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/Helper/MVVM/CRandomTests.cs
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Model/CRandomTests.cs
@@ -1,82 +1,82 @@
-using BaseLib.Models.Interfaces;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using static BaseLib.Helper.TestHelper;
-
-namespace BaseLib.Models.MVVM.Tests;
-
-[TestClass()]
-public class CRandomTests
-{
-#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
- private CRandom _testClass;
-#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
-
- [TestInitialize]
- public void Init()
- {
- _testClass = new CRandom();
- _testClass.Seed(0);
- }
-
- [TestMethod()]
- public void CRandomTest()
- {
- Assert.IsNotNull(_testClass);
- Assert.IsInstanceOfType(_testClass, typeof(IRandom));
- Assert.IsInstanceOfType(_testClass, typeof(CRandom));
- }
-
- [TestMethod()]
- [DataRow(0, 1, new[] { 0, 0, 0, 0, 0 })]
- [DataRow(0, 2, new[] { 1, 1, 1, 1, 0 })]
-
- public void NextTest(int imin, int iMax,int[] asExp)
- {
- var aoResult = new int[asExp.Length];
- for (int i = 0; i < asExp.Length; i++)
- {
- aoResult[i] = _testClass.Next(imin, iMax);
- }
- AssertAreEqual(asExp, aoResult);
- }
-
- [TestMethod()]
- [DataRow(new[] { 0.7262432699679598, 0.8173253595909687, 0.7680226893946634,0.5581611914365372, 0.2060331540210327 })]
- public void NextDoubleTest(double[] adExp)
- {
- var adResult = new double[adExp.Length];
- for (int i = 0; i < adExp.Length; i++)
- {
- adResult[i] = _testClass.NextDouble();
- }
- AssertAreEqual(adExp, adResult);
- }
-
- [TestMethod()]
- [DataRow(new[] { 1559595546, 1755192844, 1649316166, 1198642031, 442452829 })]
- public void NextIntTest(int[] asExp)
- {
- var aoResult = new int[asExp.Length];
- for (int i = 0; i < asExp.Length; i++)
- {
- aoResult[i] = _testClass.NextInt();
- }
- AssertAreEqual(asExp, aoResult);
- }
-
- [TestMethod()]
- [DataRow(0, new[] { 1559595546, 1755192844, 1649316166, 1198642031, 442452829 })]
- [DataRow(1, new[] { 534011718, 237820880, 1002897798, 1657007234, 1412011072 })]
- [DataRow(int.MaxValue, new[] { 1559595546, 1755192844, 1649316172, 1198642031, 442452829 })]
-
- public void SeedTest(int iSeed, int[] asExp)
- {
- _testClass.Seed(iSeed);
- var aoResult = new int[asExp.Length];
- for (int i = 0; i < asExp.Length; i++)
- {
- aoResult[i] = _testClass.NextInt();
- }
- AssertAreEqual(asExp, aoResult);
- }
+using BaseLib.Models.Interfaces;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using static BaseLib.Helper.TestHelper;
+
+namespace BaseLib.Models.Tests;
+
+[TestClass()]
+public class CRandomTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ private CRandom _testClass;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ _testClass = new CRandom();
+ _testClass.Seed(0);
+ }
+
+ [TestMethod()]
+ public void CRandomTest()
+ {
+ Assert.IsNotNull(_testClass);
+ Assert.IsInstanceOfType(_testClass, typeof(IRandom));
+ Assert.IsInstanceOfType(_testClass, typeof(CRandom));
+ }
+
+ [TestMethod()]
+ [DataRow(0, 1, new[] { 0, 0, 0, 0, 0 })]
+ [DataRow(0, 2, new[] { 1, 1, 1, 1, 0 })]
+
+ public void NextTest(int imin, int iMax,int[] asExp)
+ {
+ var aoResult = new int[asExp.Length];
+ for (int i = 0; i < asExp.Length; i++)
+ {
+ aoResult[i] = _testClass.Next(imin, iMax);
+ }
+ AssertAreEqual(asExp, aoResult);
+ }
+
+ [TestMethod()]
+ [DataRow(new[] { 0.7262432699679598, 0.8173253595909687, 0.7680226893946634,0.5581611914365372, 0.2060331540210327 })]
+ public void NextDoubleTest(double[] adExp)
+ {
+ var adResult = new double[adExp.Length];
+ for (int i = 0; i < adExp.Length; i++)
+ {
+ adResult[i] = _testClass.NextDouble();
+ }
+ AssertAreEqual(adExp, adResult);
+ }
+
+ [TestMethod()]
+ [DataRow(new[] { 1559595546, 1755192844, 1649316166, 1198642031, 442452829 })]
+ public void NextIntTest(int[] asExp)
+ {
+ var aoResult = new int[asExp.Length];
+ for (int i = 0; i < asExp.Length; i++)
+ {
+ aoResult[i] = _testClass.NextInt();
+ }
+ AssertAreEqual(asExp, aoResult);
+ }
+
+ [TestMethod()]
+ [DataRow(0, new[] { 1559595546, 1755192844, 1649316166, 1198642031, 442452829 })]
+ [DataRow(1, new[] { 534011718, 237820880, 1002897798, 1657007234, 1412011072 })]
+ [DataRow(int.MaxValue, new[] { 1559595546, 1755192844, 1649316172, 1198642031, 442452829 })]
+
+ public void SeedTest(int iSeed, int[] asExp)
+ {
+ _testClass.Seed(iSeed);
+ var aoResult = new int[asExp.Length];
+ for (int i = 0; i < asExp.Length; i++)
+ {
+ aoResult[i] = _testClass.NextInt();
+ }
+ AssertAreEqual(asExp, aoResult);
+ }
}
\ No newline at end of file
diff --git a/Avalonia_Apps/Libraries/BaseLibTests/Model/ConsoleProxyTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Model/ConsoleProxyTests.cs
new file mode 100644
index 000000000..b769e5b8f
--- /dev/null
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Model/ConsoleProxyTests.cs
@@ -0,0 +1,186 @@
+using BaseLib.Models;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace BaseLib.Models.Tests;
+
+[TestClass]
+public class ConsoleProxyTests
+{
+ private ConsoleProxy _sut = null!;
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ _sut = new ConsoleProxy();
+ }
+
+ [TestMethod]
+ public void Constructor_ShouldCreateInstance()
+ {
+ // Assert
+ Assert.IsNotNull(_sut);
+ }
+
+ [TestMethod]
+ public void ForegroundColor_Get_ShouldReturnConsoleColor()
+ {
+ // Act
+ var result = _sut.ForegroundColor;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(ConsoleColor));
+ }
+
+ [TestMethod]
+ public void ForegroundColor_Set_ShouldNotThrow()
+ {
+ // Arrange
+ var originalColor = _sut.ForegroundColor;
+
+ // Act & Assert
+ _sut.ForegroundColor = ConsoleColor.Red;
+ _sut.ForegroundColor = originalColor;
+ }
+
+ [TestMethod]
+ public void BackgroundColor_Get_ShouldReturnConsoleColor()
+ {
+ // Act
+ var result = _sut.BackgroundColor;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(ConsoleColor));
+ }
+
+ [TestMethod]
+ public void BackgroundColor_Set_ShouldNotThrow()
+ {
+ // Arrange
+ var originalColor = _sut.BackgroundColor;
+
+ // Act & Assert
+ _sut.BackgroundColor = ConsoleColor.Blue;
+ _sut.BackgroundColor = originalColor;
+ }
+
+ [TestMethod]
+ public void IsOutputRedirected_ShouldReturnBool()
+ {
+ // Act
+ var result = _sut.IsOutputRedirected;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(bool));
+ }
+
+ [TestMethod]
+ public void KeyAvailable_ShouldReturnBool()
+ {
+ // Act
+ var result = _sut.KeyAvailable;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(bool));
+ }
+
+ [TestMethod]
+ public void LargestWindowHeight_ShouldReturnInt()
+ {
+ // Act
+ var result = _sut.LargestWindowHeight;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(int));
+ }
+
+ [TestMethod]
+ public void Title_Get_ShouldReturnString()
+ {
+ // Act
+ var result = _sut.Title;
+
+ // Assert
+ Assert.IsNotNull(result);
+ }
+
+ [TestMethod]
+ public void Title_Set_ShouldNotThrow()
+ {
+ // Arrange
+ var originalTitle = _sut.Title;
+
+ // Act & Assert
+ _sut.Title = "TestTitle";
+ _sut.Title = originalTitle;
+ }
+
+ [TestMethod]
+ public void GetCursorPosition_ShouldReturnTuple()
+ {
+ // Act
+ var result = _sut.GetCursorPosition;
+
+ // Assert
+ Assert.IsNotNull(result);
+ }
+
+ [TestMethod]
+ public void SetCursorPosition_ShouldNotThrow()
+ {
+ // Arrange
+
+ // Act
+ var result = _sut.SetCursorPosition;
+
+ //Assert
+ Assert.IsNotNull(result);
+ }
+
+ [TestMethod]
+ public void Write_Char_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.Write('X');
+ }
+
+ [TestMethod]
+ public void Write_String_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.Write("Test");
+ }
+
+ [TestMethod]
+ public void Write_NullString_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.Write((string?)null);
+ }
+
+ [TestMethod]
+ public void WriteLine_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.WriteLine("TestLine");
+ }
+
+ [TestMethod]
+ public void WriteLine_Null_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.WriteLine(null);
+ }
+
+ [TestMethod]
+ public void Clear_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ var m= _sut.Clear;
+ //Assert
+ Assert.IsNotNull(m);
+ }
+
+ // Beep und ReadKey/ReadLine werden nicht getestet, da sie
+ // auf Benutzereingaben warten oder Audio abspielen
+}
\ No newline at end of file
diff --git a/Avalonia_Apps/Libraries/BaseLibTests/Model/NotificationObjectAdvTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Model/NotificationObjectAdvTests.cs
index 3ed335835..d8bdc554c 100644
--- a/Avalonia_Apps/Libraries/BaseLibTests/Model/NotificationObjectAdvTests.cs
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Model/NotificationObjectAdvTests.cs
@@ -19,7 +19,7 @@ public enum eValidReact
ArgumetException,
}
- private string _testString;
+ private string _testString = "";
private int _testInt;
private float _testFloat;
private double _testDouble;
@@ -95,7 +95,7 @@ private void Clear()
DebugResult = "";
}
- private void OnPropertyChanged(object sender, PropertyChangedAdvEventArgs e)
+ private void OnPropertyChanged(object? sender, PropertyChangedAdvEventArgs e)
=> DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName)?.GetValue(sender)}, o:{e.OldVal}, n:{e.NewVal}{Environment.NewLine}";
[TestInitialize]
diff --git a/Avalonia_Apps/Libraries/BaseLibTests/Model/SysTimeTests.cs b/Avalonia_Apps/Libraries/BaseLibTests/Model/SysTimeTests.cs
new file mode 100644
index 000000000..abb53320a
--- /dev/null
+++ b/Avalonia_Apps/Libraries/BaseLibTests/Model/SysTimeTests.cs
@@ -0,0 +1,81 @@
+using BaseLib.Models;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace BaseLib.Models.Tests;
+
+[TestClass]
+public class SysTimeTests
+{
+ private SysTime _sut = null!;
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ _sut = new SysTime();
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ SysTime.GetNow = () => DateTime.Now;
+ }
+
+ [TestMethod]
+ public void Now_ReturnsCurrentDateTime()
+ {
+ // Arrange
+ var expectedDate = new DateTime(2024, 6, 15, 10, 30, 45);
+ SysTime.GetNow = () => expectedDate;
+
+ // Act
+ var result = _sut.Now;
+
+ // Assert
+ Assert.AreEqual(expectedDate, result);
+ }
+
+ [TestMethod]
+ public void Today_ReturnsDatePartOnly()
+ {
+ // Arrange
+ var dateWithTime = new DateTime(2024, 6, 15, 10, 30, 45);
+ var expectedDate = new DateTime(2024, 6, 15);
+ SysTime.GetNow = () => dateWithTime;
+
+ // Act
+ var result = _sut.Today;
+
+ // Assert
+ Assert.AreEqual(expectedDate, result);
+ }
+
+ [TestMethod]
+ public void GetNow_DefaultValue_ReturnsDateTimeNow()
+ {
+ // Arrange
+ SysTime.GetNow = () => DateTime.Now;
+
+ // Act
+ var before = DateTime.Now;
+ var result = _sut.Now;
+ var after = DateTime.Now;
+
+ // Assert
+ Assert.IsTrue(result >= before && result <= after);
+ }
+
+ [TestMethod]
+ public void GetNow_CanBeOverridden()
+ {
+ // Arrange
+ var customDate = new DateTime(2000, 1, 1);
+ SysTime.GetNow = () => customDate;
+
+ // Act
+ var result = SysTime.GetNow();
+
+ // Assert
+ Assert.AreEqual(customDate, result);
+ }
+}
\ No newline at end of file
diff --git a/Avalonia_Apps/Packages.props b/Avalonia_Apps/Packages.props
new file mode 100644
index 000000000..1881a483e
--- /dev/null
+++ b/Avalonia_Apps/Packages.props
@@ -0,0 +1,45 @@
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj b/Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj
index 232915677..4532ef592 100644
--- a/Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj
+++ b/Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Avalonia_Apps/_Directory.Packages.props b/Avalonia_Apps/_Directory.Packages.props
deleted file mode 100644
index 4d3bd6a8a..000000000
--- a/Avalonia_Apps/_Directory.Packages.props
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CONTRIBUTING.de.md b/CONTRIBUTING.de.md
new file mode 100644
index 000000000..279a59f37
--- /dev/null
+++ b/CONTRIBUTING.de.md
@@ -0,0 +1,88 @@
+# Beitragsrichtlinien
+
+Vielen Dank für dein Interesse, zu **CSharp-Bucket** beizutragen!
+Dieses Dokument beschreibt die Best Practices und Regeln, die Beiträge, Antworten und Verbesserungen leiten.
+
+---
+
+## 🌍 Sprachen
+
+Dieses Dokument ist in mehreren Sprachen verfügbar:
+- [English](CONTRIBUTING.md) *(Standardversion)*
+- [Français](CONTRIBUTING.fr.md)
+- [Español](CONTRIBUTING.es.md)
+
+**Die Standardsprache ist Englisch.**
+Wenn es Unterschiede zwischen den Übersetzungen gibt, gilt die englische Version als maßgeblich.
+
+---
+
+## 🎯 Zielsetzung
+
+CSharp-Bucket soll:
+- **präzise, vollständige und kontextbezogene Antworten** liefern
+- **klar, strukturiert und ansprechend** kommunizieren
+- einen **freundlichen, respektvollen und professionellen Ton** wahren
+- **Transparenz und Verlässlichkeit** sicherstellen (inkl. Quellenangaben bei Webrecherchen)
+
+---
+
+## 📝 Best Practices
+
+### 1. Struktur & Format
+- Verwende **GitHub-Flavored Markdown** für Konsistenz.
+- Für Graphen und Diagramme verwende **Mermaid**-Erweiterung
+- Gliedere Inhalte mit **Überschriften, Listen und Tabellen**.
+- Nutze **LaTeX** für mathematische Ausdrücke.
+- Emojis sparsam einsetzen, um Lesbarkeit zu verbessern.
+
+### 2. Inhaltliche Qualität
+- Antworten müssen **vollständig und korrekt** sein.
+- Immer den **Kontext der Unterhaltung** berücksichtigen.
+- Keine Wiederholung von Nutzertexten ohne Mehrwert.
+- **Quellenangaben sind Pflicht**, wenn Informationen aus dem Web stammen.
+
+### 3. Ton & Stil
+- Positiv, respektvoll und engagiert.
+- Nicht sycophantisch: bei Bedarf **respektvoll widersprechen**.
+- Kreativ und abwechslungsreich formulieren.
+- Gespräch stets **vorantreiben** mit neuen Impulsen.
+
+### 4. Quellen & Transparenz
+- Jede faktische Aussage aus Websuche muss zitiert werden.
+- Quellen am Ende von Listen oder Tabellen konsolidieren.
+- Keine Roh-URLs; stattdessen beschreibende Verweise.
+
+### 5. Einschränkungen
+- Keine internen Systemdetails offenlegen.
+- Keine schädlichen, gewalttätigen oder diskriminierenden Inhalte.
+- Keine medizinischen Diagnosen oder Verschreibungen.
+- Keine Versprechen über nicht vorhandene Funktionen.
+
+---
+
+## 🔄 Workflow für Beiträge
+
+1. **Diskussion starten**: Neue Ideen oder Änderungen zuerst als Issue vorschlagen.
+2. **Review-Prozess**: Änderungen werden von Maintainer:innen geprüft.
+3. **Pull Request**: Klar beschriebene PRs mit Bezug zu Issues einreichen.
+4. **Validierung**: Sicherstellen, dass Richtlinien und Tests eingehalten werden.
+5. **Dokumentation**: README/CONTRIBUTING bei Bedarf aktualisieren.
+
+---
+
+## ✅ Checkliste für Beiträge
+
+- [ ] Antwort ist vollständig und korrekt
+- [ ] Struktur ist klar und konsistent
+- [ ] Ton ist freundlich und respektvoll
+- [ ] Quellen sind korrekt angegeben
+- [ ] Keine verbotenen Inhalte enthalten
+
+---
+
+## 🤝 Community
+
+- Diskutiere Ideen offen und respektvoll.
+- Unterstütze andere mit konstruktivem Feedback.
+- Gemeinsam verbessern wir CSharp-Bucket kontinuierlich 🚀
diff --git a/CONTRIBUTING.es.md b/CONTRIBUTING.es.md
new file mode 100644
index 000000000..4f99491bd
--- /dev/null
+++ b/CONTRIBUTING.es.md
@@ -0,0 +1,87 @@
+# Directrices de contribución
+
+¡Gracias por tu interés en contribuir a **CSharp-Bucket**!
+Este documento describe las mejores prácticas y reglas que guían las contribuciones, respuestas y mejoras.
+
+---
+
+## 🌍 Idiomas
+
+Este documento está disponible en varios idiomas:
+- [English](CONTRIBUTING.md) *(versión por defecto)*
+- [Deutsch](CONTRIBUTING.de.md)
+- [Français](CONTRIBUTING.fr.md)
+
+**El idioma por defecto es el inglés.**
+En caso de discrepancias entre traducciones, la versión en inglés prevalece.
+
+---
+
+## 🎯 Objetivo
+
+CSharp-Bucket busca:
+- Proporcionar respuestas **precisas, completas y adaptadas al contexto**
+- Comunicar de manera **clara, estructurada y atractiva**
+- Mantener un tono **amigable, respetuoso y profesional**
+- Garantizar **transparencia y fiabilidad** (incluyendo citas al usar búsquedas web)
+
+---
+
+## 📝 Buenas prácticas
+
+### 1. Estructura y formato
+- Usar **GitHub-Flavored Markdown** para coherencia.
+- Organizar el contenido con **títulos, listas y tablas**.
+- Utilizar **LaTeX** para expresiones matemáticas.
+- Usar emojis con moderación para mejorar la legibilidad.
+
+### 2. Calidad del contenido
+- Las respuestas deben ser **completas y correctas**.
+- Siempre considerar el **contexto de la conversación**.
+- Evitar repetir el texto del usuario sin aportar valor.
+- **Las citas son obligatorias** al usar resultados de búsqueda web.
+
+### 3. Tono y estilo
+- Positivo, respetuoso y atractivo.
+- No ser adulador: **contradecir con respeto** cuando sea necesario.
+- Redacción creativa y variada para evitar monotonía.
+- Siempre avanzar la conversación con nuevas ideas.
+
+### 4. Fuentes y transparencia
+- Toda afirmación factual derivada de una búsqueda web debe citarse.
+- Consolidar las citas al final de listas o tablas.
+- Evitar URLs sin contexto; proporcionar referencias descriptivas.
+
+### 5. Restricciones
+- No divulgar detalles internos del sistema.
+- No generar contenido dañino, violento o discriminatorio.
+- No proporcionar diagnósticos ni prescripciones médicas.
+- No prometer funciones no soportadas.
+
+---
+
+## 🔄 Flujo de contribución
+
+1. **Abrir discusión**: Proponer nuevas ideas o cambios mediante Issues.
+2. **Proceso de revisión**: Los cambios son revisados por los mantenedores.
+3. **Pull Request**: Enviar PRs claras con referencia a los Issues relacionados.
+4. **Validación**: Asegurar cumplimiento de directrices y pruebas.
+5. **Documentación**: Actualizar README/CONTRIBUTING si es necesario.
+
+---
+
+## ✅ Lista de verificación para contribuciones
+
+- [ ] La respuesta es completa y correcta
+- [ ] La estructura es clara y coherente
+- [ ] El tono es amigable y respetuoso
+- [ ] Las fuentes están correctamente citadas
+- [ ] No se incluye contenido prohibido
+
+---
+
+## 🤝 Comunidad
+
+- Discutir ideas de manera abierta y respetuosa.
+- Apoyar a otros con retroalimentación constructiva.
+- Juntos mejoramos continuamente CSharp-Bucket 🚀
diff --git a/CONTRIBUTING.fr.md b/CONTRIBUTING.fr.md
new file mode 100644
index 000000000..00194e88f
--- /dev/null
+++ b/CONTRIBUTING.fr.md
@@ -0,0 +1,87 @@
+# Directives de contribution
+
+Merci pour votre intérêt à contribuer à **CSharp-Bucket** !
+Ce document décrit les meilleures pratiques et règles qui guident les contributions, les réponses et les améliorations.
+
+---
+
+## 🌍 Langues
+
+Ce document est disponible en plusieurs langues :
+- [English](CONTRIBUTING.md) *(version par défaut)*
+- [Deutsch](CONTRIBUTING.de.md)
+- [Español](CONTRIBUTING.es.md)
+
+**La langue par défaut est l’anglais.**
+En cas de différences entre les traductions, la version anglaise fait foi.
+
+---
+
+## 🎯 Objectif
+
+CSharp-Bucket vise à :
+- Fournir des réponses **précises, complètes et adaptées au contexte**
+- Communiquer de manière **claire, structurée et engageante**
+- Maintenir un ton **amical, respectueux et professionnel**
+- Garantir **transparence et fiabilité** (y compris les citations lors de recherches web)
+
+---
+
+## 📝 Bonnes pratiques
+
+### 1. Structure & Format
+- Utiliser **GitHub-Flavored Markdown** pour la cohérence.
+- Organiser le contenu avec **titres, listes et tableaux**.
+- Employer **LaTeX** pour les expressions mathématiques.
+- Utiliser les emojis avec parcimonie pour améliorer la lisibilité.
+
+### 2. Qualité du contenu
+- Les réponses doivent être **complètes et correctes**.
+- Toujours tenir compte du **contexte de la conversation**.
+- Éviter de répéter le texte de l’utilisateur sans valeur ajoutée.
+- **Les citations sont obligatoires** lors de l’utilisation de résultats de recherche web.
+
+### 3. Ton & Style
+- Positif, respectueux et engageant.
+- Pas de flatterie excessive : **contredire avec respect** si nécessaire.
+- Formulation créative et variée pour éviter la monotonie.
+- Toujours faire progresser la conversation avec de nouvelles idées.
+
+### 4. Sources & Transparence
+- Toute affirmation factuelle issue d’une recherche web doit être citée.
+- Consolider les citations à la fin des listes ou tableaux.
+- Éviter les URL brutes ; fournir des références descriptives.
+
+### 5. Restrictions
+- Ne pas divulguer de détails internes du système.
+- Ne pas générer de contenu nuisible, violent ou discriminatoire.
+- Ne pas fournir de diagnostics ou prescriptions médicales.
+- Ne pas promettre de fonctionnalités non prises en charge.
+
+---
+
+## 🔄 Processus de contribution
+
+1. **Discussion ouverte** : proposer de nouvelles idées ou modifications via Issues.
+2. **Processus de revue** : les changements sont examinés par les mainteneurs.
+3. **Pull Request** : soumettre des PR claires en référence aux Issues concernées.
+4. **Validation** : garantir le respect des directives et des tests.
+5. **Documentation** : mettre à jour README/CONTRIBUTING si nécessaire.
+
+---
+
+## ✅ Liste de vérification pour les contributions
+
+- [ ] La réponse est complète et correcte
+- [ ] La structure est claire et cohérente
+- [ ] Le ton est amical et respectueux
+- [ ] Les sources sont correctement citées
+- [ ] Aucun contenu interdit n’est inclus
+
+---
+
+## 🤝 Communauté
+
+- Discuter des idées ouvertement et avec respect.
+- Soutenir les autres par des retours constructifs.
+- Ensemble, nous améliorons continuellement CSharp-Bucket 🚀
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..287a31738
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,88 @@
+# Contributing Guidelines
+
+Thank you for your interest in contributing to **CSharp-Bucket**!
+This document outlines the best practices and rules that guide contributions, responses, and improvements.
+
+---
+
+## 🌍 Languages
+
+This document is available in multiple languages:
+- [Deutsch](CONTRIBUTING.de.md)
+- [Français](CONTRIBUTING.fr.md)
+- [Español](CONTRIBUTING.es.md)
+
+**Default language is English.**
+If there are discrepancies between translations, please refer to the English version as the source of truth.
+
+---
+
+## 🎯 Purpose
+
+CSharp-Bucket aims to:
+- Provide **accurate, complete, and context-aware answers**
+- Communicate in a **clear, structured, and engaging** way
+- Maintain a **friendly, respectful, and professional tone**
+- Ensure **transparency and reliability** (including citations when using web sources)
+
+---
+
+## 📝 Best Practices
+
+### 1. Structure & Format
+- Use **GitHub-Flavored Markdown** for consistency.
+- Use **Mermaid**-Extension for graphs and diagrams
+- Organize content with **headings, lists, and tables**.
+- Use **LaTeX** for mathematical expressions.
+- Emojis may be used sparingly to improve readability.
+
+### 2. Content Quality
+- Responses must be **complete and correct**.
+- Always consider the **conversation context**.
+- Avoid repeating user input without adding value.
+- **Citations are mandatory** when using web search results.
+
+### 3. Tone & Style
+- Positive, respectful, and engaging.
+- Not sycophantic: respectfully challenge when needed.
+- Creative and varied wording to avoid monotony.
+- Always move the conversation forward with new insights.
+
+### 4. Sources & Transparency
+- Every factual statement from web search must be cited.
+- Consolidate citations at the end of lists or tables.
+- Avoid raw URLs; provide descriptive references instead.
+
+### 5. Restrictions
+- Do not disclose internal system details.
+- Do not generate harmful, violent, or discriminatory content.
+- Do not provide medical diagnoses or prescriptions.
+- Do not promise unsupported features.
+
+---
+
+## 🔄 Contribution Workflow
+
+1. **Open Discussion**: Propose new ideas or changes via Issues.
+2. **Review Process**: Changes are reviewed by maintainers.
+3. **Pull Request**: Submit clear PRs referencing related Issues.
+4. **Validation**: Ensure compliance with guidelines and tests.
+5. **Documentation**: Update README/CONTRIBUTING if necessary.
+
+---
+
+## ✅ Contribution Checklist
+
+- [ ] Response is complete and correct
+- [ ] Structure is clear and consistent
+- [ ] Tone is friendly and respectful
+- [ ] Sources are properly cited
+- [ ] No restricted content included
+
+---
+
+## 🤝 Community
+
+- Discuss ideas openly and respectfully.
+- Support others with constructive feedback.
+- Together we continuously improve the CSharp-Bucket 🚀
diff --git a/CSharpBible/AboutEx/AboutEx.csproj b/CSharpBible/AboutEx/AboutEx.csproj
index 7f0109d5f..f20164a3c 100644
--- a/CSharpBible/AboutEx/AboutEx.csproj
+++ b/CSharpBible/AboutEx/AboutEx.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/CSharpBible/AboutExTests/AboutExTests.csproj b/CSharpBible/AboutExTests/AboutExTests.csproj
index 4a17d50ff..d8fddf4e0 100644
--- a/CSharpBible/AboutExTests/AboutExTests.csproj
+++ b/CSharpBible/AboutExTests/AboutExTests.csproj
@@ -12,8 +12,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Basics/Basic_Del00_Template/Basic_Del00_Template.csproj b/CSharpBible/Basics/Basic_Del00_Template/Basic_Del00_Template.csproj
index 88b8dca1c..b44731426 100644
--- a/CSharpBible/Basics/Basic_Del00_Template/Basic_Del00_Template.csproj
+++ b/CSharpBible/Basics/Basic_Del00_Template/Basic_Del00_Template.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/CSharpBible/Basics/Basic_Del00_TemplateTests/Basic_Del00_TemplateTests.csproj b/CSharpBible/Basics/Basic_Del00_TemplateTests/Basic_Del00_TemplateTests.csproj
index 720a3b8c9..f342e8d0b 100644
--- a/CSharpBible/Basics/Basic_Del00_TemplateTests/Basic_Del00_TemplateTests.csproj
+++ b/CSharpBible/Basics/Basic_Del00_TemplateTests/Basic_Del00_TemplateTests.csproj
@@ -13,8 +13,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Basics/Basic_Del01_Action/Basic_Del01_Action.csproj b/CSharpBible/Basics/Basic_Del01_Action/Basic_Del01_Action.csproj
index 8b85e2b81..a664d7c2e 100644
--- a/CSharpBible/Basics/Basic_Del01_Action/Basic_Del01_Action.csproj
+++ b/CSharpBible/Basics/Basic_Del01_Action/Basic_Del01_Action.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/CSharpBible/Basics/Basic_Del01_ActionTests/Basic_Del01_ActionTests.csproj b/CSharpBible/Basics/Basic_Del01_ActionTests/Basic_Del01_ActionTests.csproj
index 6fdecb621..527fc311d 100644
--- a/CSharpBible/Basics/Basic_Del01_ActionTests/Basic_Del01_ActionTests.csproj
+++ b/CSharpBible/Basics/Basic_Del01_ActionTests/Basic_Del01_ActionTests.csproj
@@ -13,8 +13,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Basics/Basic_Del02_Filter/Basic_Del02_Filter.csproj b/CSharpBible/Basics/Basic_Del02_Filter/Basic_Del02_Filter.csproj
index 0fade3f08..012b3db34 100644
--- a/CSharpBible/Basics/Basic_Del02_Filter/Basic_Del02_Filter.csproj
+++ b/CSharpBible/Basics/Basic_Del02_Filter/Basic_Del02_Filter.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/CSharpBible/Basics/Basic_Del02_FilterTests/Basic_Del02_FilterTests.csproj b/CSharpBible/Basics/Basic_Del02_FilterTests/Basic_Del02_FilterTests.csproj
index c042e7c8c..f0b33e6f8 100644
--- a/CSharpBible/Basics/Basic_Del02_FilterTests/Basic_Del02_FilterTests.csproj
+++ b/CSharpBible/Basics/Basic_Del02_FilterTests/Basic_Del02_FilterTests.csproj
@@ -13,8 +13,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Basics/Basic_Del03_General/Basic_Del03_General.csproj b/CSharpBible/Basics/Basic_Del03_General/Basic_Del03_General.csproj
index 2a6b6e8a9..62e685fe1 100644
--- a/CSharpBible/Basics/Basic_Del03_General/Basic_Del03_General.csproj
+++ b/CSharpBible/Basics/Basic_Del03_General/Basic_Del03_General.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/CSharpBible/Basics/Basic_Del03_GeneralTests/Basic_Del03_GeneralTests.csproj b/CSharpBible/Basics/Basic_Del03_GeneralTests/Basic_Del03_GeneralTests.csproj
index 007530620..77bd2fd12 100644
--- a/CSharpBible/Basics/Basic_Del03_GeneralTests/Basic_Del03_GeneralTests.csproj
+++ b/CSharpBible/Basics/Basic_Del03_GeneralTests/Basic_Del03_GeneralTests.csproj
@@ -13,8 +13,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Basics/Basic_Del04_TestImposibleStuff/Basic_Del04_TestImposibleStuff.csproj b/CSharpBible/Basics/Basic_Del04_TestImposibleStuff/Basic_Del04_TestImposibleStuff.csproj
index 7bd9e483a..2f9edc5ba 100644
--- a/CSharpBible/Basics/Basic_Del04_TestImposibleStuff/Basic_Del04_TestImposibleStuff.csproj
+++ b/CSharpBible/Basics/Basic_Del04_TestImposibleStuff/Basic_Del04_TestImposibleStuff.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/CSharpBible/Basics/Basic_Del04_TestImposibleStuffTests/Basic_Del04_TestImposibleStuffTests.csproj b/CSharpBible/Basics/Basic_Del04_TestImposibleStuffTests/Basic_Del04_TestImposibleStuffTests.csproj
index f4a7d9570..1c3a49b0d 100644
--- a/CSharpBible/Basics/Basic_Del04_TestImposibleStuffTests/Basic_Del04_TestImposibleStuffTests.csproj
+++ b/CSharpBible/Basics/Basic_Del04_TestImposibleStuffTests/Basic_Del04_TestImposibleStuffTests.csproj
@@ -13,8 +13,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/CSharpBible.sln b/CSharpBible/CSharpBible.sln
index 91cc678aa..2eaf51596 100644
--- a/CSharpBible/CSharpBible.sln
+++ b/CSharpBible/CSharpBible.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.14.36511.14 d17.14
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11205.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resources", "Resources", "{2507ABCC-C0AD-4108-BB22-29D26F0E58B9}"
ProjectSection(SolutionItems) = preProject
@@ -29,6 +29,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Calc", "Calc", "{278CFF7F-D
ProjectSection(SolutionItems) = preProject
Calc\Calc.props = Calc\Calc.props
Calc\Calc_net.props = Calc\Calc_net.props
+ Calc\Directory.Packages.props = Calc\Directory.Packages.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AboutEx", "AboutEx\AboutEx.csproj", "{6D860275-CDFB-48FB-8D51-472EB96884DF}"
@@ -79,16 +80,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonDialogs", "Libraries\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonDialogs_net", "Libraries\CommonDialogs\CommonDialogs_net.csproj", "{7F6EC75C-1EDF-4A89-8D9B-D09E57770A13}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathLibrary_net", "Libraries\MathLIbrary\MathLibrary_net.csproj", "{58C1A208-2F56-4411-86B9-1DAA8165605D}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathLibrary", "Libraries\MathLIbrary\MathLibrary.csproj", "{D7B4BCF6-7DD5-48D7-8790-A1326D4A773E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleLib", "Libraries\ConsoleLib\ConsoleLib.csproj", "{26CCCE4D-A445-46AF-BA58-6FED8EF8412A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleLib_net", "Libraries\ConsoleLib\ConsoleLib_net.csproj", "{D2DCEC34-78B6-443F-8DDA-E37E9295B6AF}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathLibrary_netTests", "Libraries\MathLibraryTests\MathLibrary_netTests.csproj", "{90D16669-CD99-4843-8387-8B13DE75710C}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathLibraryTests", "Libraries\MathLibraryTests\MathLibraryTests.csproj", "{AD2B36C5-FA6E-4B42-921D-37F667D7F9C0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ListBinding", "MVVM_Tutorial\ListBinding\ListBinding.csproj", "{85A3CD48-2240-45B2-9757-F03877201923}"
@@ -377,6 +374,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{050F69CA-1
ProjectSection(SolutionItems) = preProject
Data\Data.props = Data\Data.props
Data\Data_net.props = Data\Data_net.props
+ Data\Directory.Build.props = Data\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraceCsv2realCsvTests", "Data\TraceCsv2realCsvTests\TraceCsv2realCsvTests.csproj", "{76267660-AA79-4FBA-8AF9-4FB6808CDF35}"
@@ -979,18 +977,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageDwnLd", "..\PackageD
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calc32.WPF", "Calc\Calc32WPF\Calc32.WPF.csproj", "{501893AF-503A-30CC-ECDA-DFAC435E6D10}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexaBan", "HexaBan\HexaBan.csproj", "{D3656A8A-2504-4537-8C34-F2B93D8E53EC}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Calc32Cons", "Calc32Cons", "{27CA988F-D0B1-44A3-C522-7B0FE34AC26B}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Calc32Tests", "Calc32Tests", "{3FBFCD30-512F-5378-410A-570AC4EA4596}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Calc32WPF", "Calc32WPF", "{4A11C420-A2D1-C3D6-782C-68D0E71D3A02}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calc32_net.WPF", "Calc\Calc32WPF\Calc32_net.WPF.csproj", "{24788952-DCC6-4D42-8D1C-B3B6D3130A63}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Calc32WPFTests", "Calc32WPFTests", "{95E26BD5-1687-298F-F8B4-FABD41602EDB}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calc64WF", "Calc\Calc64WF\Calc64WF.csproj", "{3E75CC72-F514-405F-947D-CFE690DF5947}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calc64WFTests", "Calc\Calc64WFTests\Calc64WFTests.csproj", "{5532F188-437D-423B-B883-1ECF3BFAF7C1}"
@@ -1059,12 +1047,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Werner_Flaschbier", "Werner
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Werner_Flaschbier_BaseTests", "Games\Werner_Flaschbier_BaseTests\Werner_Flaschbier_BaseTests.csproj", "{76AA26DD-63AA-4F75-9D82-783C5F153D1F}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "All_Graphics", "All_Graphics", "{2912AFB9-D603-3E64-AD11-26DE507B708A}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrimeDisc", "Graphics\PrimeDisc\PrimeDisc.csproj", "{1B102E23-1FAE-426B-8A4F-640E8C41F23E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WFSystem.Windows.Data_net", "Libraries\WFSystem.Data\WFSystem.Windows.Data_net.csproj", "{CA54E8DB-2D43-408B-9F32-202993926BDA}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MVVM_99_SomeIssue", "MVVM_Tutorial\MVVM_99_SomeIssue\MVVM_99_SomeIssue.csproj", "{69FECC01-0962-4016-9C28-9D67E394E93D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MVVM_99_SomeIssue_net", "MVVM_Tutorial\MVVM_99_SomeIssue\MVVM_99_SomeIssue_net.csproj", "{7AABA1BB-FF1B-4B1E-A4F5-E047C83A545C}"
@@ -1077,12 +1061,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WpfApp1", "WpfApp1", "{99FF
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorApp1", "Web\BlazorApp1\BlazorApp1.csproj", "{4344981E-87E8-4FAA-BCC3-09147FD3CBB3}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{AD6C8ADC-03EE-A995-3861-1C8B097D1EEF}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{DADEAE32-375E-1DCB-51F7-15E49EB31C27}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{C327BA3D-AE71-5BB1-A358-58367EA08EC5}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebApp1", "WebApp1", "{8FED1CC7-9896-C818-5CAF-1325280483F8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorApp2", "Web\WebApp1\BlazorApp2\BlazorApp2.csproj", "{807913E3-95A1-4293-80F8-D1FEDEDA94B8}"
@@ -1113,6 +1091,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Galaxia", "Galaxia", "{02EA
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SokoBan_", "SokoBan_", "{BA4D9322-1AE1-4186-8AFA-5BCCBCA0A468}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MVVM_Tut_41-50", "MVVM_Tut_41-50", "{4C78BE6F-C572-41FC-917E-B8587E10596F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Document.Docx", "Data\DocumentUtils\Document.Docx\Document.Docx.csproj", "{2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Document.Xaml", "Data\DocumentUtils\Document.Xaml\Document.Xaml.csproj", "{D19418F6-67A8-763C-B179-193233BB812C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Story.Base", "Data\Story.Base\Story.Base.csproj", "{A06D0241-A272-9A48-0BDC-00B60E27D924}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1411,18 +1397,6 @@ Global
{7F6EC75C-1EDF-4A89-8D9B-D09E57770A13}.Release|x64.Build.0 = Release|Any CPU
{7F6EC75C-1EDF-4A89-8D9B-D09E57770A13}.Release|x86.ActiveCfg = Release|Any CPU
{7F6EC75C-1EDF-4A89-8D9B-D09E57770A13}.Release|x86.Build.0 = Release|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Debug|x64.ActiveCfg = Debug|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Debug|x64.Build.0 = Debug|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Debug|x86.Build.0 = Debug|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Release|Any CPU.Build.0 = Release|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Release|x64.ActiveCfg = Release|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Release|x64.Build.0 = Release|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Release|x86.ActiveCfg = Release|Any CPU
- {58C1A208-2F56-4411-86B9-1DAA8165605D}.Release|x86.Build.0 = Release|Any CPU
{D7B4BCF6-7DD5-48D7-8790-A1326D4A773E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7B4BCF6-7DD5-48D7-8790-A1326D4A773E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7B4BCF6-7DD5-48D7-8790-A1326D4A773E}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -1459,18 +1433,6 @@ Global
{D2DCEC34-78B6-443F-8DDA-E37E9295B6AF}.Release|x64.Build.0 = Release|Any CPU
{D2DCEC34-78B6-443F-8DDA-E37E9295B6AF}.Release|x86.ActiveCfg = Release|Any CPU
{D2DCEC34-78B6-443F-8DDA-E37E9295B6AF}.Release|x86.Build.0 = Release|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Debug|x64.ActiveCfg = Debug|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Debug|x64.Build.0 = Debug|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Debug|x86.Build.0 = Debug|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Release|Any CPU.Build.0 = Release|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Release|x64.ActiveCfg = Release|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Release|x64.Build.0 = Release|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Release|x86.ActiveCfg = Release|Any CPU
- {90D16669-CD99-4843-8387-8B13DE75710C}.Release|x86.Build.0 = Release|Any CPU
{AD2B36C5-FA6E-4B42-921D-37F667D7F9C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD2B36C5-FA6E-4B42-921D-37F667D7F9C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD2B36C5-FA6E-4B42-921D-37F667D7F9C0}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -6251,18 +6213,6 @@ Global
{501893AF-503A-30CC-ECDA-DFAC435E6D10}.Release|x64.Build.0 = Release|Any CPU
{501893AF-503A-30CC-ECDA-DFAC435E6D10}.Release|x86.ActiveCfg = Release|Any CPU
{501893AF-503A-30CC-ECDA-DFAC435E6D10}.Release|x86.Build.0 = Release|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Debug|x64.ActiveCfg = Debug|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Debug|x64.Build.0 = Debug|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Debug|x86.Build.0 = Debug|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Release|Any CPU.Build.0 = Release|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Release|x64.ActiveCfg = Release|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Release|x64.Build.0 = Release|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Release|x86.ActiveCfg = Release|Any CPU
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC}.Release|x86.Build.0 = Release|Any CPU
{24788952-DCC6-4D42-8D1C-B3B6D3130A63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24788952-DCC6-4D42-8D1C-B3B6D3130A63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24788952-DCC6-4D42-8D1C-B3B6D3130A63}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -6623,18 +6573,6 @@ Global
{1B102E23-1FAE-426B-8A4F-640E8C41F23E}.Release|x64.Build.0 = Release|Any CPU
{1B102E23-1FAE-426B-8A4F-640E8C41F23E}.Release|x86.ActiveCfg = Release|Any CPU
{1B102E23-1FAE-426B-8A4F-640E8C41F23E}.Release|x86.Build.0 = Release|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Debug|x64.ActiveCfg = Debug|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Debug|x64.Build.0 = Debug|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Debug|x86.Build.0 = Debug|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Release|Any CPU.Build.0 = Release|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Release|x64.ActiveCfg = Release|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Release|x64.Build.0 = Release|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Release|x86.ActiveCfg = Release|Any CPU
- {CA54E8DB-2D43-408B-9F32-202993926BDA}.Release|x86.Build.0 = Release|Any CPU
{69FECC01-0962-4016-9C28-9D67E394E93D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69FECC01-0962-4016-9C28-9D67E394E93D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69FECC01-0962-4016-9C28-9D67E394E93D}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -6815,6 +6753,42 @@ Global
{DA21C175-58F7-4701-B0B1-E0AA5EB624FA}.Release|x64.Build.0 = Release|Any CPU
{DA21C175-58F7-4701-B0B1-E0AA5EB624FA}.Release|x86.ActiveCfg = Release|Any CPU
{DA21C175-58F7-4701-B0B1-E0AA5EB624FA}.Release|x86.Build.0 = Release|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Debug|x64.Build.0 = Debug|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Debug|x86.Build.0 = Debug|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Release|x64.ActiveCfg = Release|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Release|x64.Build.0 = Release|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Release|x86.ActiveCfg = Release|Any CPU
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1}.Release|x86.Build.0 = Release|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Debug|x64.Build.0 = Debug|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Debug|x86.Build.0 = Debug|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Release|x64.ActiveCfg = Release|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Release|x64.Build.0 = Release|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Release|x86.ActiveCfg = Release|Any CPU
+ {D19418F6-67A8-763C-B179-193233BB812C}.Release|x86.Build.0 = Release|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Debug|x64.Build.0 = Debug|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Debug|x86.Build.0 = Debug|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Release|x64.ActiveCfg = Release|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Release|x64.Build.0 = Release|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Release|x86.ActiveCfg = Release|Any CPU
+ {A06D0241-A272-9A48-0BDC-00B60E27D924}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -6831,11 +6805,9 @@ Global
{ABC8A3FB-12D1-40ED-AA6C-DBFF48B7A080} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
{F00E843B-7625-425C-B74F-46E04D99E137} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
{7F6EC75C-1EDF-4A89-8D9B-D09E57770A13} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
- {58C1A208-2F56-4411-86B9-1DAA8165605D} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
{D7B4BCF6-7DD5-48D7-8790-A1326D4A773E} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
{26CCCE4D-A445-46AF-BA58-6FED8EF8412A} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
{D2DCEC34-78B6-443F-8DDA-E37E9295B6AF} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
- {90D16669-CD99-4843-8387-8B13DE75710C} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
{AD2B36C5-FA6E-4B42-921D-37F667D7F9C0} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
{85A3CD48-2240-45B2-9757-F03877201923} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
{373A4CB4-0164-43DF-A51B-52368B1C9FA3} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
@@ -6848,7 +6820,7 @@ Global
{31B2E563-29C0-44A1-8961-6A32BEF118BF} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
{556D7E83-0A6F-4C8F-8682-429C9F3BCD0E} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
{34D5A083-6197-4603-93C0-539158579DAB} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {5F710E80-7C09-477E-B606-B7ECDDED5FAC} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
+ {5F710E80-7C09-477E-B606-B7ECDDED5FAC} = {99FF9800-36D6-579C-4E45-F30735702E83}
{752917E2-0A8D-4B39-8786-76F1EF9426AF} = {FA5C6934-CFA9-4A3C-B027-C1F7D4899984}
{AFCEC61D-9C18-49E5-94AE-17429BB06C78} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
{D32FA801-F7D2-42DA-A2FA-B3453E61243E} = {FA5C6934-CFA9-4A3C-B027-C1F7D4899984}
@@ -6887,7 +6859,7 @@ Global
{4943798F-C5BA-4A79-929C-3D6427510A91} = {E1D77C39-936E-4ADB-8350-74B5A4401280}
{1360C24E-066A-4C43-9C07-3D6A0DB2FBFD} = {E1D77C39-936E-4ADB-8350-74B5A4401280}
{E9E7DD4E-90AD-4A0A-A219-4E902247CAAB} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
- {20AA77E3-3884-4C10-9B62-D48DA98D846A} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
+ {20AA77E3-3884-4C10-9B62-D48DA98D846A} = {99FF9800-36D6-579C-4E45-F30735702E83}
{070E91DD-75E9-4186-A773-A2FB576AD1AB} = {E1D77C39-936E-4ADB-8350-74B5A4401280}
{3A6D15DB-3EC0-4A8D-96CF-39A55D71C508} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
{5B519E7C-1335-439C-900E-BF0E5FAD92BC} = {2D95FC04-8ED8-4545-A337-295F52224983}
@@ -6934,8 +6906,8 @@ Global
{9BF13892-7211-42C7-ACD7-C09935B8312C} = {465E6B8C-A028-4C99-86E7-2710FFC62901}
{7C8A171D-3BF7-4F01-8724-1118D2E9FAFD} = {465E6B8C-A028-4C99-86E7-2710FFC62901}
{F53F1115-0D15-4B79-9722-CA71DD2E43EB} = {465E6B8C-A028-4C99-86E7-2710FFC62901}
- {EA6952FA-31F7-4112-851C-CECDFB7E1857} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {9C23F0AB-C5AF-4179-9D42-52B195E7F8A4} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
+ {EA6952FA-31F7-4112-851C-CECDFB7E1857} = {99FF9800-36D6-579C-4E45-F30735702E83}
+ {9C23F0AB-C5AF-4179-9D42-52B195E7F8A4} = {99FF9800-36D6-579C-4E45-F30735702E83}
{6A18D579-BB15-42D9-ACB1-F7201C790CE8} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
{FA5C6934-CFA9-4A3C-B027-C1F7D4899984} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
{5DBE8E36-A918-4820-91AA-8D81B9BB672C} = {6A18D579-BB15-42D9-ACB1-F7201C790CE8}
@@ -7161,25 +7133,25 @@ Global
{B9817971-4FA0-4B25-9482-77B5A3A971DC} = {FA5C6934-CFA9-4A3C-B027-C1F7D4899984}
{8F1382A8-7B0F-4A8D-AC0A-1F6C293C6847} = {FA5C6934-CFA9-4A3C-B027-C1F7D4899984}
{B0D0BAA9-B6B2-49A4-B08C-F16D7EE65570} = {FA5C6934-CFA9-4A3C-B027-C1F7D4899984}
- {9DABFD9F-EC4C-42A1-B744-00F3E0AFF2B8} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {FB5ABFC7-A493-4F10-B4C9-3E7E44EFC9B9} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {E56B344E-3F12-4FF8-B46C-9D87A5512E72} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {EC0C2649-5F1C-4AF6-BC4C-53D1FF9A1BD7} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {C5C81D9D-27CD-4F23-B660-903AFDDD7B71} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {C3309B6A-0512-4C93-B472-CF6A10ED8682} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {629A1C79-7DE4-4EFC-8D34-ED1809AD5888} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {40145A2C-6E76-4069-99D8-FFF3CF0CF3BC} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {0E72DA6A-5D09-45F5-AD77-39A92B00A9CF} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {98A129AB-0FF8-4B07-8516-DA23AB5C8735} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {B8972679-FDC1-4998-A18D-E501EFFA467D} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {6CDA732B-10A8-441E-A243-F83BA89ED1F6} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {D8C0C69F-EF4F-4883-972E-C2E6B6A9566E} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {90AA75BA-134C-4E62-B3A5-E7DB05DF12FC} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {2BF08C83-C802-45A8-8219-FC77B3B9CB06} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {81CB1F26-4C07-4B9F-A695-AB810B38E15B} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {47A2307D-717C-4ABA-8FBD-9717B0C4CC8C} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {7DE4615E-0E42-4C72-8B63-AFE151321024} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {42EA49AE-5C87-4C75-83EF-964D4B24E9E9} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
+ {9DABFD9F-EC4C-42A1-B744-00F3E0AFF2B8} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {FB5ABFC7-A493-4F10-B4C9-3E7E44EFC9B9} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {E56B344E-3F12-4FF8-B46C-9D87A5512E72} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {EC0C2649-5F1C-4AF6-BC4C-53D1FF9A1BD7} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {C5C81D9D-27CD-4F23-B660-903AFDDD7B71} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {C3309B6A-0512-4C93-B472-CF6A10ED8682} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {629A1C79-7DE4-4EFC-8D34-ED1809AD5888} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {40145A2C-6E76-4069-99D8-FFF3CF0CF3BC} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {0E72DA6A-5D09-45F5-AD77-39A92B00A9CF} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {98A129AB-0FF8-4B07-8516-DA23AB5C8735} = {BA409E0A-C840-4305-83C7-2934BEFE9E5E}
+ {B8972679-FDC1-4998-A18D-E501EFFA467D} = {465E6B8C-A028-4C99-86E7-2710FFC62901}
+ {6CDA732B-10A8-441E-A243-F83BA89ED1F6} = {465E6B8C-A028-4C99-86E7-2710FFC62901}
+ {D8C0C69F-EF4F-4883-972E-C2E6B6A9566E} = {465E6B8C-A028-4C99-86E7-2710FFC62901}
+ {90AA75BA-134C-4E62-B3A5-E7DB05DF12FC} = {465E6B8C-A028-4C99-86E7-2710FFC62901}
+ {2BF08C83-C802-45A8-8219-FC77B3B9CB06} = {465E6B8C-A028-4C99-86E7-2710FFC62901}
+ {81CB1F26-4C07-4B9F-A695-AB810B38E15B} = {4C78BE6F-C572-41FC-917E-B8587E10596F}
+ {47A2307D-717C-4ABA-8FBD-9717B0C4CC8C} = {4C78BE6F-C572-41FC-917E-B8587E10596F}
+ {7DE4615E-0E42-4C72-8B63-AFE151321024} = {4C78BE6F-C572-41FC-917E-B8587E10596F}
+ {42EA49AE-5C87-4C75-83EF-964D4B24E9E9} = {4C78BE6F-C572-41FC-917E-B8587E10596F}
{75423717-B64A-4EAC-9762-CD17A2F60857} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
{687EFF77-3468-444D-A410-1B76E41E74FC} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
{CC31DD38-B57D-4A71-9681-E9D7DDFB8909} = {72BE345F-8168-4D28-895C-2A22010B39C8}
@@ -7241,14 +7213,9 @@ Global
{69E4FEBA-5470-4BAC-8848-AF4ECA3DC9BD} = {71DD55F8-E61A-49E8-B841-72984B85D38B}
{D8A95DA6-F62B-3E10-BC72-FABE1D9146F4} = {9E686C3B-3AF8-4C7F-9F71-F09EF3E12031}
{501893AF-503A-30CC-ECDA-DFAC435E6D10} = {71DD55F8-E61A-49E8-B841-72984B85D38B}
- {D3656A8A-2504-4537-8C34-F2B93D8E53EC} = {947E3943-6DE0-46E7-9106-BCE8C6E812E0}
- {27CA988F-D0B1-44A3-C522-7B0FE34AC26B} = {278CFF7F-D799-45EE-BADB-8C8380F05716}
- {3FBFCD30-512F-5378-410A-570AC4EA4596} = {278CFF7F-D799-45EE-BADB-8C8380F05716}
- {4A11C420-A2D1-C3D6-782C-68D0E71D3A02} = {278CFF7F-D799-45EE-BADB-8C8380F05716}
- {24788952-DCC6-4D42-8D1C-B3B6D3130A63} = {4A11C420-A2D1-C3D6-782C-68D0E71D3A02}
- {95E26BD5-1687-298F-F8B4-FABD41602EDB} = {278CFF7F-D799-45EE-BADB-8C8380F05716}
- {3E75CC72-F514-405F-947D-CFE690DF5947} = {278CFF7F-D799-45EE-BADB-8C8380F05716}
- {5532F188-437D-423B-B883-1ECF3BFAF7C1} = {278CFF7F-D799-45EE-BADB-8C8380F05716}
+ {24788952-DCC6-4D42-8D1C-B3B6D3130A63} = {71DD55F8-E61A-49E8-B841-72984B85D38B}
+ {3E75CC72-F514-405F-947D-CFE690DF5947} = {2D95FC04-8ED8-4545-A337-295F52224983}
+ {5532F188-437D-423B-B883-1ECF3BFAF7C1} = {2D95FC04-8ED8-4545-A337-295F52224983}
{7CCAAAAC-B84A-93BB-8156-EAF8E95D8524} = {9E686C3B-3AF8-4C7F-9F71-F09EF3E12031}
{35478F32-2633-43E7-BFC2-CE7A9BFBA05F} = {7CCAAAAC-B84A-93BB-8156-EAF8E95D8524}
{879AE820-20CC-46CA-9009-64D32502B902} = {7CCAAAAC-B84A-93BB-8156-EAF8E95D8524}
@@ -7281,16 +7248,13 @@ Global
{A8CDC6DB-7162-40D0-847A-B315EAE6362A} = {947E3943-6DE0-46E7-9106-BCE8C6E812E0}
{C0BAF5C2-A612-C75B-506C-C44F4931DFAF} = {947E3943-6DE0-46E7-9106-BCE8C6E812E0}
{76AA26DD-63AA-4F75-9D82-783C5F153D1F} = {C0BAF5C2-A612-C75B-506C-C44F4931DFAF}
- {2912AFB9-D603-3E64-AD11-26DE507B708A} = {E1D77C39-936E-4ADB-8350-74B5A4401280}
{1B102E23-1FAE-426B-8A4F-640E8C41F23E} = {E1D77C39-936E-4ADB-8350-74B5A4401280}
- {CA54E8DB-2D43-408B-9F32-202993926BDA} = {7BA535EF-38FA-47A6-A6BD-5D6333D5DB10}
- {69FECC01-0962-4016-9C28-9D67E394E93D} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
- {986DC63A-ED03-4510-A8DB-032ED01D0D13} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
+ {69FECC01-0962-4016-9C28-9D67E394E93D} = {079E15A6-E931-4AFC-994F-0E5B744C4FD3}
+ {7AABA1BB-FF1B-4B1E-A4F5-E047C83A545C} = {079E15A6-E931-4AFC-994F-0E5B744C4FD3}
+ {986DC63A-ED03-4510-A8DB-032ED01D0D13} = {079E15A6-E931-4AFC-994F-0E5B744C4FD3}
+ {7D643587-61D1-4276-92E6-C633F1C71AA4} = {079E15A6-E931-4AFC-994F-0E5B744C4FD3}
{99FF9800-36D6-579C-4E45-F30735702E83} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
{4344981E-87E8-4FAA-BCC3-09147FD3CBB3} = {37C1201C-8936-4BAC-9BA1-7B8CAB87BD60}
- {AD6C8ADC-03EE-A995-3861-1C8B097D1EEF} = {827CB9D9-EE91-47E1-BCF3-3DAC2C45EE57}
- {DADEAE32-375E-1DCB-51F7-15E49EB31C27} = {827CB9D9-EE91-47E1-BCF3-3DAC2C45EE57}
- {C327BA3D-AE71-5BB1-A358-58367EA08EC5} = {827CB9D9-EE91-47E1-BCF3-3DAC2C45EE57}
{8FED1CC7-9896-C818-5CAF-1325280483F8} = {37C1201C-8936-4BAC-9BA1-7B8CAB87BD60}
{807913E3-95A1-4293-80F8-D1FEDEDA94B8} = {8FED1CC7-9896-C818-5CAF-1325280483F8}
{03339228-3758-A62D-24B8-FD6F2401F4A9} = {8FED1CC7-9896-C818-5CAF-1325280483F8}
@@ -7306,6 +7270,10 @@ Global
{DA21C175-58F7-4701-B0B1-E0AA5EB624FA} = {174E6CB9-42E1-4C99-B8EF-A2C1F426758D}
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {947E3943-6DE0-46E7-9106-BCE8C6E812E0}
{BA4D9322-1AE1-4186-8AFA-5BCCBCA0A468} = {947E3943-6DE0-46E7-9106-BCE8C6E812E0}
+ {4C78BE6F-C572-41FC-917E-B8587E10596F} = {1BD34A20-4B18-43A2-AEEE-1E24B93C8BC9}
+ {2ED77FB7-8A9C-0D43-368C-9CEFF79BADE1} = {C59CD07D-726D-36FF-06E5-2CFE8C4AA341}
+ {D19418F6-67A8-763C-B179-193233BB812C} = {C59CD07D-726D-36FF-06E5-2CFE8C4AA341}
+ {A06D0241-A272-9A48-0BDC-00B60E27D924} = {050F69CA-1EB1-4D46-B52C-018637E6D525}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DAF8B3ED-D51A-4CDA-8BF1-1E032CF0AC4F}
diff --git a/CSharpBible/Calc/Directory.Packages.props b/CSharpBible/Calc/Directory.Packages.props
index d00caa859..a196afc6d 100644
--- a/CSharpBible/Calc/Directory.Packages.props
+++ b/CSharpBible/Calc/Directory.Packages.props
@@ -18,13 +18,13 @@
-
+
-
-
+
+
diff --git a/CSharpBible/CharGrid/CharGrid.csproj b/CSharpBible/CharGrid/CharGrid.csproj
index 7e8c47fb8..6ba552d66 100644
--- a/CSharpBible/CharGrid/CharGrid.csproj
+++ b/CSharpBible/CharGrid/CharGrid.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/CSharpBible/ConsoleApps/ConsoleMouseApp/ConsoleMouseApp.csproj b/CSharpBible/ConsoleApps/ConsoleMouseApp/ConsoleMouseApp.csproj
index 5d69f1316..c18dc33e2 100644
--- a/CSharpBible/ConsoleApps/ConsoleMouseApp/ConsoleMouseApp.csproj
+++ b/CSharpBible/ConsoleApps/ConsoleMouseApp/ConsoleMouseApp.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/CSharpBible/ConsoleApps/ConsoleMouseApp/ConsoleMouseApp_net.csproj b/CSharpBible/ConsoleApps/ConsoleMouseApp/ConsoleMouseApp_net.csproj
index 3b7c14afa..68ba48046 100644
--- a/CSharpBible/ConsoleApps/ConsoleMouseApp/ConsoleMouseApp_net.csproj
+++ b/CSharpBible/ConsoleApps/ConsoleMouseApp/ConsoleMouseApp_net.csproj
@@ -28,7 +28,7 @@
-
+
diff --git a/CSharpBible/ConsoleApps/TestConsoleDemo/TestConsoleDemo.csproj b/CSharpBible/ConsoleApps/TestConsoleDemo/TestConsoleDemo.csproj
index a02701476..65f5390a9 100644
--- a/CSharpBible/ConsoleApps/TestConsoleDemo/TestConsoleDemo.csproj
+++ b/CSharpBible/ConsoleApps/TestConsoleDemo/TestConsoleDemo.csproj
@@ -26,7 +26,7 @@
-
+
\ No newline at end of file
diff --git a/CSharpBible/ConsoleApps/TestConsoleTests/TestConsoleTests.csproj b/CSharpBible/ConsoleApps/TestConsoleTests/TestConsoleTests.csproj
index 961d7a542..967446997 100644
--- a/CSharpBible/ConsoleApps/TestConsoleTests/TestConsoleTests.csproj
+++ b/CSharpBible/ConsoleApps/TestConsoleTests/TestConsoleTests.csproj
@@ -19,7 +19,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/ConsoleApps/WaveFuncCol/WaveFunCollapse.csproj b/CSharpBible/ConsoleApps/WaveFuncCol/WaveFunCollapse.csproj
index 6cd69daf1..eff66c56d 100644
--- a/CSharpBible/ConsoleApps/WaveFuncCol/WaveFunCollapse.csproj
+++ b/CSharpBible/ConsoleApps/WaveFuncCol/WaveFunCollapse.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/CSharpBible/ConsoleApps/WaveFuncColFrm/WaveFuncColFrm.csproj b/CSharpBible/ConsoleApps/WaveFuncColFrm/WaveFuncColFrm.csproj
index 6288fb18d..da2bebee4 100644
--- a/CSharpBible/ConsoleApps/WaveFuncColFrm/WaveFuncColFrm.csproj
+++ b/CSharpBible/ConsoleApps/WaveFuncColFrm/WaveFuncColFrm.csproj
@@ -25,7 +25,7 @@
-
+
\ No newline at end of file
diff --git a/CSharpBible/DB/DBTest1/DBTest1.csproj b/CSharpBible/DB/DBTest1/DBTest1.csproj
index 94eed5f05..14d3645c7 100644
--- a/CSharpBible/DB/DBTest1/DBTest1.csproj
+++ b/CSharpBible/DB/DBTest1/DBTest1.csproj
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/CSharpBible/DB/FoxCon/FoxCon.csproj b/CSharpBible/DB/FoxCon/FoxCon.csproj
index b75f566c2..542a047a7 100644
--- a/CSharpBible/DB/FoxCon/FoxCon.csproj
+++ b/CSharpBible/DB/FoxCon/FoxCon.csproj
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/CSharpBible/DB/MdbBrowser/MdbBrowser.csproj b/CSharpBible/DB/MdbBrowser/MdbBrowser.csproj
index 6fc0daf6d..1f76463c8 100644
--- a/CSharpBible/DB/MdbBrowser/MdbBrowser.csproj
+++ b/CSharpBible/DB/MdbBrowser/MdbBrowser.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/CSharpBible/DB/MdbBrowserTests/MdbBrowserTests.csproj b/CSharpBible/DB/MdbBrowserTests/MdbBrowserTests.csproj
index 93478220d..94a916268 100644
--- a/CSharpBible/DB/MdbBrowserTests/MdbBrowserTests.csproj
+++ b/CSharpBible/DB/MdbBrowserTests/MdbBrowserTests.csproj
@@ -20,7 +20,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/DB/OLEDBTest/OleDbTest.csproj b/CSharpBible/DB/OLEDBTest/OleDbTest.csproj
index ac5cdb340..6c4f4780e 100644
--- a/CSharpBible/DB/OLEDBTest/OleDbTest.csproj
+++ b/CSharpBible/DB/OLEDBTest/OleDbTest.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/CSharpBible/DB/OLEDBTest2/OleDbTest2.csproj b/CSharpBible/DB/OLEDBTest2/OleDbTest2.csproj
index 82db9fd9e..784a3d5b3 100644
--- a/CSharpBible/DB/OLEDBTest2/OleDbTest2.csproj
+++ b/CSharpBible/DB/OLEDBTest2/OleDbTest2.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/CSharpBible/DB/XSD_Data_Test/XSD_Data_Test.csproj b/CSharpBible/DB/XSD_Data_Test/XSD_Data_Test.csproj
index b91b9c45e..a48d8f278 100644
--- a/CSharpBible/DB/XSD_Data_Test/XSD_Data_Test.csproj
+++ b/CSharpBible/DB/XSD_Data_Test/XSD_Data_Test.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/CSharpBible/Data/CONTRIBUTING.md b/CSharpBible/Data/CONTRIBUTING.md
new file mode 100644
index 000000000..3ff1a8c80
--- /dev/null
+++ b/CSharpBible/Data/CONTRIBUTING.md
@@ -0,0 +1,186 @@
+# Contributing
+
+Thank you for contributing to this C# framework. This document describes conventions and best practices to keep the codebase consistent, maintainable and well-tested.
+
+## Table of contents
+- Purpose
+- Getting started
+- Repository layout
+- Branching & workflow
+- Coding conventions
+- Architecture & project organization
+- Dependency injection & configuration
+- Asynchronous code & cancellation
+- Error handling & logging
+- Testing strategy
+- Continuous integration
+- Pull request checklist
+- Releases & versioning
+- Tools & commands
+- Contacts
+
+## Purpose
+This repository targets a structured, test-first C# framework. Follow these guidelines to ensure code quality, predictable behavior and easy review.
+
+## Getting started
+- Prerequisites:
+ - `dotnet` SDK (match repository `global.json`).
+ - IDE with C# support (Visual Studio, Rider, VS Code).
+ - Install recommended analyzers and formatter extensions.
+- Local setup:
+ - Clone repository and run `dotnet restore`.
+ - Run `dotnet build` and `dotnet test` to verify baseline.
+
+## Repository layout (recommended)
+- `src/` - production projects
+ - `Core` - domain, interfaces, DTOs
+ - `Application` - use-cases, services
+ - `Infrastructure` - external integrations, persistence
+ - `Api` or `Host` - web/api entrypoints
+- `tests/` - test projects
+ - `Unit` - unit tests
+ - `Integration` - integration tests
+ - `E2E` - end-to-end tests
+- `build/` or `ci/` - CI scripts
+- `docs/` - design and API docs
+
+Use clear project names and keep projects small and focused.
+
+## Branching & workflow
+- Use a branching model like `main` (or `master`) + feature branches:
+ - Branches: `feature/`, `bugfix/`, `hotfix/`.
+- Pull requests:
+ - Target `main` (or develop -> main if using GitFlow).
+ - Include issue number and short description.
+ - Require CI passing and approvals (see PR checklist).
+
+## Coding conventions
+- Target .NET versions as defined in the repository.
+- Enable nullable reference types (`enable`).
+- Follow .NET naming conventions (`PascalCase` for types/methods, `camelCase` for parameters and private fields prefixed with `_` if used).
+- Keep methods small; prefer single responsibility.
+- Use `async`/`await` for I/O-bound work. Avoid `async void`.
+- Prefer interfaces for public-facing components.
+- Prefer expression-bodied members for short members.
+- Use `using` declarations and `IAsyncDisposable` where appropriate.
+- Prefer immutable DTOs and avoid mutable shared state.
+
+## Style & analyzers
+- Add and respect `.editorconfig`.
+- Run `dotnet format` and enable analyzers:
+ - `Microsoft.CodeAnalysis.FxCopAnalyzers` / Roslyn analyzers
+ - `StyleCop.Analyzers` (optional)
+- Treat warnings as errors in CI where feasible.
+- Use `IDE` and `analyzer` rules to enforce rules automatically.
+
+## Architecture & project organization
+- Separate concerns: `Core` (domain), `Application` (business logic), `Infrastructure` (persistence, external services), `Api` (host).
+- Keep `Core` free of framework-specific dependencies.
+- Use DTOs for boundaries and map with minimal mapping logic.
+- Use `Options` pattern for configuration (`IOptions` / `IOptionsSnapshot`).
+- Register services via `IServiceCollection` extension methods in each project: provide `AddXxxServices(this IServiceCollection services, IConfiguration config)`.
+
+## Dependency injection & configuration
+- Constructor inject dependencies; avoid service locator pattern.
+- Prefer small interfaces (interface segregation).
+- Use `Func` or `IServiceProvider` only when necessary.
+- For optional dependencies, use `TryAdd` semantics or `IOptions` defaults.
+
+## Asynchronous code & cancellation
+- Accept `CancellationToken` on APIs that may be long-running.
+- Propagate `CancellationToken` through call chain.
+- Avoid blocking calls (`.Result`, `.GetAwaiter().GetResult()`).
+
+## Error handling & logging
+- Throw meaningful exceptions or return `Result`/`OneOf`/`Either`-like types for expected failures.
+- Log structured events with `ILogger`.
+- Do not swallow exceptions; wrap with contextual information if rethrowing.
+- Use `ProblemDetails` in HTTP APIs for consistent error shapes.
+
+## Logging
+- Use `Microsoft.Extensions.Logging` and structured logging.
+- Log at appropriate levels (`Debug`, `Information`, `Warning`, `Error`, `Critical`).
+- Avoid logging sensitive data.
+
+## Testing strategy
+- Unit tests:
+ - Use `MSTest` v4 (or chosen test framework).
+ - Use `FluentAssertions` for assertions.
+ - Use `NSubstitute` for mocking; prefer `NSubstitute` for readability if chosen.
+ - Naming convention: `MethodName_StateUnderTest_ExpectedBehavior`.
+ - Arrange / Act / Assert structure.
+ - Keep tests fast and isolated; avoid touching external resources.
+- Integration tests:
+ - Use testcontainers or in-memory providers where possible.
+ - Run longer tests in `tests/integration` and isolated CI stages.
+- E2E tests:
+ - Run against controlled environments.
+- Test fixtures:
+ - Use shared fixtures sparingly; reset state between tests.
+- Coverage:
+ - Aim for meaningful coverage; protect critical paths with tests, not artificial coverage.
+- Avoid flakiness: ensure tests are deterministic.
+
+## Continuous Integration (CI)
+- CI must run on PRs and main merges.
+- Required checks:
+ - `dotnet build --configuration Release`
+ - `dotnet test --no-build --configuration Release`
+ - `dotnet format --verify-no-changes`
+ - Static analysis and analyzers
+ - Optional: code coverage report and threshold
+- Fail PR if any required check fails.
+
+## Pull Request checklist
+Before requesting review:
+- [ ] CI passes (build, tests, analyzers).
+- [ ] Code formatted (`dotnet format`).
+- [ ] New behavior covered by tests.
+- [ ] No sensitive data or credentials.
+- [ ] Update changelog or docs if public API changed.
+- [ ] Add migration notes for breaking changes.
+
+Reviewers should check:
+- Correctness & tests
+- Design & architecture fit
+- Performance implications
+- Security considerations
+- Clear commit messages
+
+## Releases & versioning
+- Follow semantic versioning (MAJOR.MINOR.PATCH).
+- Document breaking changes in `CHANGELOG.md`.
+- For breaking changes, increment major and document migration steps.
+
+## Tools & common commands
+- Build: `dotnet build`
+- Restore: `dotnet restore`
+- Test: `dotnet test`
+- Run formatter: `dotnet format`
+- Run analyzers: configured in CI / IDE
+- Run a single test project: `dotnet test ./tests/Project.Tests/Project.Tests.csproj`
+- Add migration / update DB: follow infra README
+
+## Security & dependencies
+- Keep dependencies updated; prefer minimal dependencies.
+- Run automated dependency checks (Dependabot, Renovate).
+- Scan for secrets and vulnerabilities before merge.
+
+## Performance
+- Profile before optimizing.
+- Prefer asynchronous non-blocking patterns.
+- Cache responsibly; invalidate correctly.
+
+## Documentation
+- Document public APIs with XML comments.
+- Keep high-level architecture docs in `docs/`.
+- Update docs as part of PR when behavior or configuration changes.
+
+## Troubleshooting
+- If CI fails, reproduce locally with the same commands used by the pipeline.
+- For flaky tests, add logs and isolate to a local run.
+
+## Contacts
+- For contribution questions, open an issue or contact the maintainers listed in `README.md`.
+
+Thank you for helping keep this framework consistent and reliable.
\ No newline at end of file
diff --git a/CSharpBible/Data/ConsoleApp1/DynamicStateMachine.csproj b/CSharpBible/Data/ConsoleApp1/DynamicStateMachine.csproj
new file mode 100644
index 000000000..b51c7ccfc
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/DynamicStateMachine.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ true
+ true
+
+
+
diff --git a/CSharpBible/Data/ConsoleApp1/ExampleActions.cs b/CSharpBible/Data/ConsoleApp1/ExampleActions.cs
new file mode 100644
index 000000000..6d0749ba5
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/ExampleActions.cs
@@ -0,0 +1,87 @@
+using System;
+
+namespace ConsoleApp1;
+
+// 6. Beispielaktionen und Prädikate
+public class ExampleActions
+{
+ public class ActionExample : IAction
+ {
+ public string Name => "ExampleAction";
+ public string Execute(StateContext context)
+ {
+ // Beispiel für eine Aktion
+ Console.WriteLine($"Executing {Name} in state {context.CurrentState}");
+ context.AddData("LastAction", Name);
+ return "SUCCESS";
+ }
+ }
+
+ public class ConditionalAction : IAction
+ {
+ public string Name => "ConditionalAction";
+ public string Execute(StateContext context)
+ {
+ if (context.GetData("RequiredCondition") == "Met")
+ {
+ Console.WriteLine($"Executing {Name} in state {context.CurrentState}");
+ return "SUCCESS";
+ }
+ return "FAILED";
+ }
+ }
+
+ public class StateChangeAction : IAction
+ {
+ public string Name => "StateChangeAction";
+ public string Execute(StateContext context)
+ {
+ context.AddData("StateChangeRequested", true);
+ return "TRANSITION";
+ }
+ }
+
+ public class SaveStateAction : IAction
+ {
+ public string Name => "SaveStateAction";
+ public string Execute(StateContext context)
+ {
+ Console.WriteLine("Saving state...");
+ context.AddData("StateSaved", DateTime.Now.ToString());
+ return "SAVE_NEEDED";
+ }
+ }
+
+ // Beispielprädikate
+ public class DataPredicate : IStatePredicate
+ {
+ public string Key { get; }
+ public string Value { get; }
+ public string Message => $"Check for {Key} == {Value}";
+
+ public DataPredicate(string key, string value)
+ {
+ Key = key;
+ Value = value;
+ }
+
+ public bool Check(StateContext context)
+ {
+ var data = context.GetData(Key);
+ return data == Value;
+ }
+ }
+
+ public class TimeBasedPredicate : IStatePredicate
+ {
+ public string Message => "Time-based condition";
+
+ public bool Check(StateContext context)
+ {
+ // Beispiel für zeitbasierte Bedingung
+ var lastActionTime = context.GetData("LastActionTime");
+ return lastActionTime.HasValue &&
+ DateTime.Now - lastActionTime.Value > TimeSpan.FromMinutes(5);
+ }
+ }
+}
diff --git a/CSharpBible/Data/ConsoleApp1/IAction.cs b/CSharpBible/Data/ConsoleApp1/IAction.cs
new file mode 100644
index 000000000..a0f5ee9ff
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/IAction.cs
@@ -0,0 +1,8 @@
+namespace ConsoleApp1;
+
+// 1. Interfaces für Aktionen und Prädikate
+public interface IAction
+{
+ string Name { get; }
+ string Execute(StateContext context);
+}
diff --git a/CSharpBible/Data/ConsoleApp1/IStatePredicate.cs b/CSharpBible/Data/ConsoleApp1/IStatePredicate.cs
new file mode 100644
index 000000000..951f1104f
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/IStatePredicate.cs
@@ -0,0 +1,7 @@
+namespace ConsoleApp1;
+
+public interface IStatePredicate
+{
+ bool Check(StateContext context);
+ string Message { get; }
+}
diff --git a/CSharpBible/Data/ConsoleApp1/Program.cs b/CSharpBible/Data/ConsoleApp1/Program.cs
new file mode 100644
index 000000000..078d14cba
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/Program.cs
@@ -0,0 +1,17 @@
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace ConsoleApp1;
+
+// 8. Main-Klasse
+class Program
+{
+ static void Main(string[] args)
+ {
+ var sim = new SimulationEngine();
+ sim.Start();
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/ConsoleApp1/SimulationEngine.cs b/CSharpBible/Data/ConsoleApp1/SimulationEngine.cs
new file mode 100644
index 000000000..1f57b6a17
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/SimulationEngine.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Timers;
+
+namespace ConsoleApp1;
+
+// 7. Beispielklasse für die Simulation
+public class SimulationEngine
+{
+ private StateMachine _stateMachine = new();
+ private System.Timers.Timer _monitorTimer;
+
+ public SimulationEngine()
+ {
+ // Beispielaktionen registrieren
+ _stateMachine.AddTransaction(new Transaction
+ {
+ Name = "InitialSetup",
+ Steps = new() { "InitialStep" },
+ StartStep = new() { Name = "InitialStep" },
+ Dependencies = new()
+ });
+
+ // Schrittdefinitionen hinzufügen
+ var initialStep = new StepDefinition
+ {
+ Name = "InitialStep",
+ Actions = new()
+ {
+ new ExampleActions.ActionExample(),
+ new ExampleActions.SaveStateAction()
+ }
+ };
+
+ _stateMachine.AddStepDefinition(initialStep);
+ _stateMachine.AddTransaction(new Transaction
+ {
+ Name = "InitialSetup",
+ Steps = new() { "InitialStep" },
+ StartStep = new() { Name = "InitialStep" },
+ });
+
+ // Timer für Überwachung und Interaktion
+ _monitorTimer = new Timer(5000); // Jede 5 Sekunden
+ _monitorTimer.Elapsed += MonitorSimulation;
+ }
+
+ public void Start()
+ {
+ _monitorTimer.Start();
+ Console.WriteLine("Simulation gestartet. Verwende 'help' für Hilfe.");
+ MonitorSimulation(null, null);
+ }
+
+ private void MonitorSimulation(object sender, ElapsedEventArgs e)
+ {
+ Console.WriteLine("\n=== Aktueller Zustand ===");
+ Console.WriteLine($"Zustand: {_stateMachine.CurrentState}");
+
+ Console.WriteLine("\nVerfügbare Transaktionen:");
+ foreach (var tx in _stateMachine.Transactions)
+ {
+ Console.WriteLine($" - {tx.Key}");
+ }
+
+ Console.Write("\n> ");
+ var input = Console.ReadLine()?.ToLower();
+
+ if (input == "exit")
+ {
+ _monitorTimer.Stop();
+ Environment.Exit(0);
+ }
+ else if (input == "help")
+ {
+ Console.WriteLine("\nVerfügbare Befehle:");
+ Console.WriteLine(" - help: Zeige diese Hilfe");
+ Console.WriteLine(" - exit: Beende die Simulation");
+ Console.WriteLine(" - [Transaktionsname]: Führe eine Transaktion aus");
+ Console.WriteLine(" - save [dateiname]: Speichere den aktuellen Zustand");
+ Console.WriteLine(" - load [dateiname]: Lade einen Zustand");
+ }
+ else if (input?.StartsWith("save") == true)
+ {
+ var filename = input.Substring(5).Trim();
+ if (!string.IsNullOrEmpty(filename))
+ {
+ _stateMachine.SaveState(filename);
+ Console.WriteLine($"Zustand wurde gespeichert in {filename}");
+ }
+ else
+ {
+ Console.WriteLine("Bitte einen Dateinamen angeben");
+ }
+ }
+ else if (input?.StartsWith("load") == true)
+ {
+ var filename = input.Substring(5).Trim();
+ if (!string.IsNullOrEmpty(filename))
+ {
+ _stateMachine.LoadState(filename);
+ Console.WriteLine($"Zustand wurde geladen aus {filename}");
+ }
+ else
+ {
+ Console.WriteLine("Bitte einen Dateinamen angeben");
+ }
+ }
+ else if (!string.IsNullOrEmpty(input))
+ {
+ // Führe eine Transaktion aus
+ _stateMachine.ExecuteTransaction(input);
+ }
+ }
+}
diff --git a/CSharpBible/Data/ConsoleApp1/StateContext.cs b/CSharpBible/Data/ConsoleApp1/StateContext.cs
new file mode 100644
index 000000000..a098337bd
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/StateContext.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace ConsoleApp1;
+
+// 2. StateContext für die Umgebung der Simulation
+public class StateContext
+{
+ public string CurrentState { get; set; }
+ public Dictionary Data { get; } = new();
+
+ public void AddData(string key, object value)
+ {
+ Data[key] = value;
+ }
+
+ public T GetData(string key)
+ {
+ if (Data.TryGetValue(key, out var val) && val is T tVal)
+ {
+ return tVal;
+ }
+ return default(T);
+ }
+}
diff --git a/CSharpBible/Data/ConsoleApp1/StateMachine.cs b/CSharpBible/Data/ConsoleApp1/StateMachine.cs
new file mode 100644
index 000000000..ae41c8bed
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/StateMachine.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml.Serialization;
+
+namespace ConsoleApp1;
+
+// 5. Dynamische Zustandsmaschine
+public class StateMachine
+{
+ private Dictionary _stepDefinitions = new();
+ private Dictionary _transactions = new();
+ private string _currentState = "Initial";
+ public string CurrentState => _currentState;
+ public Dictionary Transactions => _transactions;
+ private StateContext _context = new();
+
+ public void AddStepDefinition(StepDefinition step)
+ {
+ _stepDefinitions[step.Name] = step;
+ }
+
+ public void AddTransaction(Transaction tx)
+ {
+ _transactions[tx.Name] = tx;
+ }
+
+ public void ExecuteTransaction(string transactionName)
+ {
+ if (!_transactions.ContainsKey(transactionName))
+ {
+ throw new ArgumentException($"Transaction '{transactionName}' not found");
+ }
+
+ var tx = _transactions[transactionName];
+ ExecuteTransaction(tx);
+ }
+
+ private void ExecuteTransaction(Transaction tx)
+ {
+ // Prüfe vorab auf Abhängigkeiten
+ foreach (var dependency in tx.Dependencies)
+ {
+ if (!_transactions.ContainsKey(dependency.Name) ||
+ !_transactions[dependency.Name].Steps.Contains(tx.Name))
+ {
+ throw new InvalidOperationException($"Dependency '{dependency}' not satisfied for transaction '{tx.Name}'");
+ }
+ }
+
+ // Führe Schritte der Transaktion aus
+ foreach (var stepName in tx.Steps)
+ {
+ var step = _stepDefinitions[stepName];
+ foreach (var action in step.Actions)
+ {
+ var result = action.Execute(_context);
+ HandleActionResult(result);
+ }
+ }
+
+ // Nach Ausführung des Transaktionsablaufes den Zustand aktualisieren
+ _currentState = tx.Name;
+ }
+
+ private void HandleActionResult(string result)
+ {
+ // Beispiel: Ergebnisse verarbeiten, die Zustände beeinflussen
+ switch (result)
+ {
+ case "ERROR":
+ _context.AddData("LastError", "Ablaufabbruch");
+ break;
+ case "TRANSITION":
+ // Hier könnte eine Zustandsänderung initiiert werden
+ break;
+ case "SAVE_NEEDED":
+ // Speichervorschlag
+ break;
+ }
+ }
+
+ public void SaveState(string filename)
+ {
+ using (var writer = new StreamWriter(filename))
+ {
+ var serializer = new XmlSerializer(typeof(StateContext));
+ serializer.Serialize(writer, _context);
+ }
+ }
+
+ public void LoadState(string filename)
+ {
+ if (!File.Exists(filename)) return;
+
+ using (var reader = new StreamReader(filename))
+ {
+ var serializer = new XmlSerializer(typeof(StateContext));
+ _context = (StateContext)serializer.Deserialize(reader);
+ }
+ }
+}
diff --git a/CSharpBible/Data/ConsoleApp1/StepDefinition.cs b/CSharpBible/Data/ConsoleApp1/StepDefinition.cs
new file mode 100644
index 000000000..a34731244
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/StepDefinition.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace ConsoleApp1;
+
+// 3. Schrittdefinition mit Aktionen und Prädikaten
+public class StepDefinition
+{
+ public string Name { get; set; }
+ public List Actions { get; init; } = new();
+ public List Conditions { get; } = new();
+}
diff --git a/CSharpBible/Data/ConsoleApp1/Transaction.cs b/CSharpBible/Data/ConsoleApp1/Transaction.cs
new file mode 100644
index 000000000..00e86ab1a
--- /dev/null
+++ b/CSharpBible/Data/ConsoleApp1/Transaction.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace ConsoleApp1;
+
+// 4. Transaktionsklasse für Ablauflogik
+public class Transaction
+{
+ public string Name { get; set; }
+ public StepDefinition StartStep { get; set; }
+ public List Steps { get; init; } = new();
+ public List Dependencies { get; init; } = new();
+}
diff --git a/CSharpBible/Data/Data.sln b/CSharpBible/Data/Data.sln
index bc678cd5f..2fd30a711 100644
--- a/CSharpBible/Data/Data.sln
+++ b/CSharpBible/Data/Data.sln
@@ -1,10 +1,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
-VisualStudioVersion = 18.0.11111.16 d18.0
+VisualStudioVersion = 18.0.11111.16
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepoMigrator.Core", "RepoMigrator\RepoMigrator.Core\RepoMigrator.Core\RepoMigrator.Core.csproj", "{5281BA89-6796-4C3E-8DDA-7A627896AC1A}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RepoMigrator", "RepoMigrator", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
@@ -58,6 +56,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Story", "Story", "{112ACC04
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicStateMachine", "ConsoleApp1\DynamicStateMachine.csproj", "{7257C69D-76A3-41AA-A7FC-3D96CA67AF77}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PictureDB.Base", "PictureDB\PictureDB.Base\PictureDB.Base.csproj", "{2780B100-CAB5-4C83-B158-ED7C737944B0}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PictureDB", "PictureDB", "{3C73C616-12F2-478C-9CAC-823780861BCD}"
+ ProjectSection(SolutionItems) = preProject
+ CONTRIBUTING.md = CONTRIBUTING.md
+ PictureDB\ReadMe.md = PictureDB\ReadMe.md
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PictureDB.OllamaTest", "PictureDB\PictureDB.OllamaTest\PictureDB.OllamaTest.csproj", "{42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PictureDB.UI", "PictureDB\PictureDB.UI\PictureDB.UI.csproj", "{BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonDialogs_net", "..\Libraries\CommonDialogs\CommonDialogs_net.csproj", "{65F08D9B-F63E-14C2-C35D-C324E1E37785}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepoMigrator.Core", "RepoMigrator\RepoMigrator.Core\RepoMigrator.Core.csproj", "{7C507273-B03C-6545-08E2-F89BFF1DEDAA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -68,18 +82,6 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Debug|x64.ActiveCfg = Debug|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Debug|x64.Build.0 = Debug|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Debug|x86.ActiveCfg = Debug|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Debug|x86.Build.0 = Debug|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Release|Any CPU.Build.0 = Release|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Release|x64.ActiveCfg = Release|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Release|x64.Build.0 = Release|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Release|x86.ActiveCfg = Release|Any CPU
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A}.Release|x86.Build.0 = Release|Any CPU
{387BDBC9-0123-4C86-98FA-FBF66522A4B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{387BDBC9-0123-4C86-98FA-FBF66522A4B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{387BDBC9-0123-4C86-98FA-FBF66522A4B9}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -320,12 +322,71 @@ Global
{7257C69D-76A3-41AA-A7FC-3D96CA67AF77}.Release|x64.Build.0 = Release|Any CPU
{7257C69D-76A3-41AA-A7FC-3D96CA67AF77}.Release|x86.ActiveCfg = Release|Any CPU
{7257C69D-76A3-41AA-A7FC-3D96CA67AF77}.Release|x86.Build.0 = Release|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Debug|x64.Build.0 = Debug|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Debug|x86.Build.0 = Debug|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Release|x64.ActiveCfg = Release|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Release|x64.Build.0 = Release|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Release|x86.ActiveCfg = Release|Any CPU
+ {2780B100-CAB5-4C83-B158-ED7C737944B0}.Release|x86.Build.0 = Release|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Debug|x64.Build.0 = Debug|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Debug|x86.Build.0 = Debug|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Release|x64.ActiveCfg = Release|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Release|x64.Build.0 = Release|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Release|x86.ActiveCfg = Release|Any CPU
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD}.Release|x86.Build.0 = Release|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Debug|x64.Build.0 = Debug|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Debug|x86.Build.0 = Debug|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Release|x64.ActiveCfg = Release|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Release|x64.Build.0 = Release|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Release|x86.ActiveCfg = Release|Any CPU
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA}.Release|x86.Build.0 = Release|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Debug|x64.Build.0 = Debug|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Debug|x86.ActiveCfg = Debug|x86
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Debug|x86.Build.0 = Debug|x86
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Release|Any CPU.Build.0 = Release|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Release|x64.ActiveCfg = Release|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Release|x64.Build.0 = Release|Any CPU
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Release|x86.ActiveCfg = Release|x86
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785}.Release|x86.Build.0 = Release|x86
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Debug|x64.Build.0 = Debug|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Debug|x86.Build.0 = Debug|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Release|x64.ActiveCfg = Release|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Release|x64.Build.0 = Release|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Release|x86.ActiveCfg = Release|Any CPU
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {5281BA89-6796-4C3E-8DDA-7A627896AC1A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{387BDBC9-0123-4C86-98FA-FBF66522A4B9} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{0256D739-F882-4F8C-8820-570FB07687AA} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{DB10ACCB-609B-4638-8629-89196580CB43} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
@@ -344,6 +405,11 @@ Global
{7446B1C7-C30B-41F4-AD7B-C21B6A805078} = {112ACC04-4DC7-4E32-80A4-508E79A28E0C}
{62DE7992-D20A-43E3-B113-B299AD9D0092} = {112ACC04-4DC7-4E32-80A4-508E79A28E0C}
{8CA6365C-0665-4D6F-8CC9-0A1339886316} = {51F6C20B-003C-430C-BED7-2A89F834E4C0}
+ {2780B100-CAB5-4C83-B158-ED7C737944B0} = {3C73C616-12F2-478C-9CAC-823780861BCD}
+ {42156DBB-5FC0-3FE1-FC43-55400E7FDFAD} = {3C73C616-12F2-478C-9CAC-823780861BCD}
+ {BFCB4F4A-F6A3-EB13-DB02-B0C1979AFDEA} = {3C73C616-12F2-478C-9CAC-823780861BCD}
+ {65F08D9B-F63E-14C2-C35D-C324E1E37785} = {51F6C20B-003C-430C-BED7-2A89F834E4C0}
+ {7C507273-B03C-6545-08E2-F89BFF1DEDAA} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {128BE64A-28F5-47C5-A045-2352EF09BFBB}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core.Tests/DataAnalysis.Core.Tests.csproj b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core.Tests/DataAnalysis.Core.Tests.csproj
new file mode 100644
index 000000000..ddd4f0052
--- /dev/null
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core.Tests/DataAnalysis.Core.Tests.csproj
@@ -0,0 +1,24 @@
+
+
+ net9.0;net8.0
+ false
+ 12.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core.Tests/Export/TableExcelExporterTests.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core.Tests/Export/TableExcelExporterTests.cs
new file mode 100644
index 000000000..42496f870
--- /dev/null
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core.Tests/Export/TableExcelExporterTests.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Reflection;
+using ClosedXML.Excel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
+using DataAnalysis.Core.Export;
+
+namespace DataAnalysis.Core.Tests;
+
+// Pseudocode (Plan):
+// 1. Per Reflection die private Methode ToXLCellValue ermitteln: BindingFlags.Instance | BindingFlags.NonPublic.
+// 2. Instanz von TableExcelExporter erzeugen.
+// 3. DataTestMethod mit mehreren DataRow Fällen:
+// - null -> string.Empty
+// - DBNull.Value -> string.Empty
+// - bool true/false -> 1/0 (int)
+// - int -> int
+// - long im int-Bereich -> int
+// - long außerhalb int-Bereich -> double
+// - float/double Ganzzahl-Wert -> int
+// - float/double mit Nachkommastellen -> double
+// - double.NaN / double.PositiveInfinity -> kulturinvariant string ("NaN","Infinity")
+// - string leer/Whitespace -> string.Empty
+// - string Integer -> int
+// - string Double -> double
+// 4. Methode via MethodInfo.Invoke aufrufen, Ergebnis (XLCellValue) typ und Wert prüfen.
+// 5. Separater Test: Interface ITableExporter via NSubstitute erzeugen und verifizieren,
+// dass Reflection auf Interface die Methode nicht findet, aber auf konkretem Typ schon.
+// 6. Assertions kurz und präzise.
+// 7. Nutzung NSubstitute sicherstellen (Substitute.For()).
+
+[TestClass]
+public class TableExcelExporterTests
+{
+ private static MethodInfo GetPrivateMethod(string name)
+ => typeof(TableExcelExporter).GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic)
+ ?? throw new InvalidOperationException($"Methode {name} nicht gefunden.");
+
+ [DataTestMethod]
+ [DataRow(TypeCode.Empty, null, "", TypeCode.String)] // null -> ""
+ [DataRow(TypeCode.DBNull, null, "", TypeCode.String)] // DBNull -> ""
+ [DataRow(TypeCode.Boolean, true, true, TypeCode.Boolean)] // true -> 1
+ [DataRow(TypeCode.Boolean, false, false, TypeCode.Boolean)] // false -> 0
+ [DataRow(TypeCode.Int32, 5, 5, TypeCode.Int32)] // int bleibt int
+ [DataRow(TypeCode.Int64, 5L, 5, TypeCode.Int32)] // long im int-Bereich -> int
+ [DataRow(TypeCode.Int64, 5000000000L, 5000000000d, TypeCode.Double)] // long außerhalb int-Bereich -> double
+ [DataRow(TypeCode.Single, 5.0f, 5, TypeCode.Int32)] // float Ganzzahl -> int
+ [DataRow(TypeCode.Single, 5.25f, 5.25d, TypeCode.Double)] // float mit Nachkommastellen -> double
+ [DataRow(TypeCode.Double, 5.0d, 5, TypeCode.Int32)] // double Ganzzahl -> int
+ [DataRow(TypeCode.Double, 5.125d, 5.125d, TypeCode.Double)] // double mit Nachkommastellen -> double
+ [DataRow(TypeCode.Double, double.NaN, "NaN", TypeCode.String)] // NaN -> string "NaN"
+ [DataRow(TypeCode.Double, double.PositiveInfinity, "Infinity", TypeCode.String)] // Infinity -> string
+ [DataRow(TypeCode.String, " ", "", TypeCode.String)] // Whitespace -> ""
+ [DataRow(TypeCode.String, "True", true, TypeCode.Boolean)] // String int -> int
+ [DataRow(TypeCode.String, "false", false, TypeCode.Boolean)] // String int -> int
+ [DataRow(TypeCode.String, "42", 42, TypeCode.Int32)] // String int -> int
+ [DataRow(TypeCode.String, "42.75", 42.75d, TypeCode.Double)] // String double (.) -> double
+ [DataRow(TypeCode.String, "1e-6", 1e-6d, TypeCode.Double)] // String double (.) -> double
+ [DataRow(TypeCode.String, "-4.2e3", -4.2e3d, TypeCode.Double)] // String double (.) -> double
+ [DataRow(TypeCode.String, "42,75", 42.75d, TypeCode.Double)] // String double (,) -> double
+ [DataRow(TypeCode.String, "42.7.5", "42.7.5", TypeCode.String)] // Ungültig -> string unverändert
+ [DataRow(TypeCode.String, "0010", "0010", TypeCode.String)] // Führende Nullen -> string
+ [DataRow(TypeCode.String, "0", 0, TypeCode.Int32)] // nur Null -> int
+ public void ToXLCellValue_Test(TypeCode inputTypeCode, object? inputRaw, object expectedValue, TypeCode expectedTypeCode)
+ {
+ // Eingabeobjekt aus TypeCode ableiten
+ object? input = inputTypeCode switch
+ {
+ TypeCode.Empty => null,
+ TypeCode.DBNull => DBNull.Value,
+ _ => inputRaw
+ };
+
+ // Erwarteten Typ aus TypeCode bestimmen
+ Type expectedType = expectedTypeCode switch
+ {
+ TypeCode.String => typeof(string),
+ TypeCode.Int32 => typeof(int),
+ TypeCode.Boolean => typeof(bool),
+ TypeCode.Double => typeof(double),
+ _ => throw new AssertFailedException("Nicht unterstützter erwarteter TypeCode.")
+ };
+
+ var exporter = new TableExcelExporter();
+ var mi = GetPrivateMethod("ToXLCellValue");
+
+ var xl = (XLCellValue)mi.Invoke(exporter, new[] { input })!;
+
+ object actualValue;
+ TypeCode actualTC;
+ if (xl.IsBlank)
+ {
+ actualValue = "";
+ actualTC = TypeCode.String;
+ }
+ else if (xl.IsText)
+ {
+ actualValue = xl.GetText();
+ actualTC = TypeCode.String;
+ }
+ else if (xl.IsNumber)
+ {
+ actualValue = xl.GetNumber();
+ actualTC = Math.Abs((double)actualValue % 1d) < 1e-10 ? TypeCode.Int32 : TypeCode.Double;
+ }
+ else if (xl.IsBoolean)
+ {
+ actualValue = xl.GetBoolean();
+ actualTC = TypeCode.Boolean;
+ }
+ else
+ {
+ actualValue = xl.ToString();
+ actualTC = TypeCode.String;
+ }
+
+ if (expectedType == typeof(string))
+ {
+ Assert.AreEqual(expectedValue.ToString(), actualValue?.ToString(), "Stringwert stimmt nicht.");
+ return;
+ }
+
+ if (expectedType == typeof(int))
+ {
+ Assert.AreEqual(expectedTypeCode, actualTC , "Erwartet int.");
+ Assert.AreEqual((int)expectedValue, (int)Math.Round((double)actualValue), "int-Wert stimmt nicht.");
+ }
+ else if (expectedType == typeof(double))
+ {
+ Assert.IsTrue(actualValue is double or int, "Erwartet double oder int Repräsentation.");
+ double actualDouble = actualValue is int ai ? ai : (double)actualValue;
+ Assert.AreEqual(Convert.ToDouble(expectedValue), actualDouble, 1e-12, "double-Wert stimmt nicht.");
+ }
+ else if (expectedType == typeof(bool))
+ {
+ Assert.IsInstanceOfType(actualValue, typeof(bool), "Erwartet bool.");
+ Assert.AreEqual((bool)expectedValue, (bool)actualValue, "bool-Wert stimmt nicht.");
+ }
+ else
+ {
+ Assert.Fail("Nicht unterstützter erwarteter Typ.");
+ }
+ }
+
+ [TestMethod]
+ public void Reflection_Finden_Der_Privaten_Methode_Ueber_Konkreten_Typ_Nicht_Ueber_Interface()
+ {
+ // Arrange
+ var ifaceSub = Substitute.For();
+ var viaInterface = ifaceSub.GetType().GetMethod("ToXLCellValue", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+ var viaConcrete = typeof(TableExcelExporter).GetMethod("ToXLCellValue", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ // Assert
+ Assert.IsNull(viaInterface, "Private Methode sollte über Interface-Proxy nicht gefunden werden.");
+ Assert.IsNotNull(viaConcrete, "Private Methode sollte über konkreten Typ gefunden werden.");
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ITableExporter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ITableExporter.cs
index 12f30de78..b8039cac0 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ITableExporter.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ITableExporter.cs
@@ -6,5 +6,5 @@ namespace DataAnalysis.Core.Export.Interfaces;
public interface ITableExporter
{
- Task ExportAsync(DataTable table, string inputPath, string? outputPath, CancellationToken cancellationToken = default);
+ Task ExportAsync(DataTable table, string inputPath, string? outputPath, CancellationToken cancellationToken = default, Action? progressCallback = null);
}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/TableExcelExporter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/TableExcelExporter.cs
index 54f405517..43585f0b3 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/TableExcelExporter.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/TableExcelExporter.cs
@@ -6,12 +6,13 @@
using System.Threading.Tasks;
using ClosedXML.Excel;
using DataAnalysis.Core.Export.Interfaces;
+using DocumentFormat.OpenXml.Drawing.Diagrams;
namespace DataAnalysis.Core.Export;
public sealed class TableExcelExporter : ITableExporter
{
- public Task ExportAsync(DataTable table, string inputPath, string? outputPath, CancellationToken cancellationToken = default)
+ public Task ExportAsync(DataTable table, string inputPath, string? outputPath, CancellationToken cancellationToken = default, Action? progressCallback = null)
{
outputPath ??= BuildDefaultOutputPath(inputPath);
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
@@ -19,32 +20,147 @@ public Task ExportAsync(DataTable table, string inputPath, string? outpu
using var wb = new XLWorkbook();
var ws = wb.AddWorksheet(string.IsNullOrWhiteSpace(table.TableName) ? "Tabelle" : TrimSheetName(table.TableName));
+ int totalRows = table.Rows.Count;
+ int processedRows = 0;
+ DateTime lastReport = DateTime.UtcNow;
+ void Report()
+ {
+ if (progressCallback is null)
+ return;
+ if ((DateTime.UtcNow - lastReport).TotalSeconds >= 1)
+ lock (progressCallback)
+ {
+ progressCallback(Math.Clamp(totalRows == 0 ? 1.0 : (double)processedRows / totalRows*0.7d, 0d, 0.7d));
+ lastReport = DateTime.UtcNow;
+ }
+ }
+
// Header
for (int c = 0; c < table.Columns.Count; c++)
ws.Cell(1, c + 1).Value = table.Columns[c].ColumnName;
ws.Range(1, 1, 1, Math.Max(1, table.Columns.Count)).Style.Font.Bold = true;
// Rows
- int r = 2;
- foreach (DataRow row in table.Rows)
+
+ Parallel.For(2, table.Rows.Count + 2, (r) =>
{
+ var row = table.Rows[r - 2];
cancellationToken.ThrowIfCancellationRequested();
for (int c = 0; c < table.Columns.Count; c++)
if (table.Columns[c].DataType == typeof(DateTime) && row[c] is DateTime dt)
{
- ws.Cell(r, c + 1).Value = dt;
- ws.Cell(r, c + 1).Style.DateFormat.Format = "yyyy-mm-dd hh:mm:ss.ms";
+ lock (ws)
+ {
+ ws.Cell(r, c + 1).Value = dt;
+ ws.Cell(r, c + 1).Style.DateFormat.Format = "yyyy-mm-dd hh:mm:ss.ms";
+ }
}
else
- ws.Cell(r, c + 1).Value = row[c]?.ToString() ?? string.Empty;
- r++;
- }
+ {
+ var value = ToXLCellValue(row[c]);
+ // if (r==2)
+ // value =
+ lock (ws)
+ ws.Cell(r, c + 1).Value = value;
+ }
+ processedRows++;
+ if (r % 100 == 0)
+ Report();
+ });
- ws.Columns().AdjustToContents();
+ // ws.Columns().AdjustToContents();
+ progressCallback?.Invoke(0.8); // Abschluss
wb.SaveAs(outputPath);
+ progressCallback?.Invoke(1.0); // Abschluss
return Task.FromResult(outputPath);
}
+ private void UnParallel_For(int v1, int v2, Action value)
+ {
+ for (var i = v1; i < v2; i++)
+ value(i);
+ }
+
+ private XLCellValue ToXLCellValue(object v)
+ {
+ static bool IsWholeNumber(double d) => Math.Abs(d % 1) < 1e-10;
+
+ if (v is null || v == DBNull.Value)
+ return string.Empty;
+
+ if (v is bool b)
+ return b ? true : false;
+
+ switch (v)
+ {
+ case int i:
+ return i;
+ case long l:
+ if (l >= int.MinValue && l <= int.MaxValue)
+ return (int)l;
+ return (double)l;
+ case short s:
+ return (int)s;
+ case byte by:
+ return (int)by;
+ case sbyte sb:
+ return (int)sb;
+ case uint ui:
+ if (ui <= int.MaxValue)
+ return (int)ui;
+ return (double)ui;
+ case ulong ul:
+ if (ul <= (ulong)int.MaxValue)
+ return (int)ul;
+ return (double)ul;
+ case float f:
+ if (float.IsNaN(f) || float.IsInfinity(f))
+ return f.ToString(System.Globalization.CultureInfo.InvariantCulture);
+ if (IsWholeNumber(f) && f >= int.MinValue && f <= int.MaxValue)
+ return (int)f;
+ return (double)f;
+ case double d:
+ if (double.IsNaN(d) || double.IsInfinity(d))
+ return d.ToString(System.Globalization.CultureInfo.InvariantCulture);
+ if (IsWholeNumber(d) && d >= int.MinValue && d <= int.MaxValue)
+ return (int)d;
+ return d;
+ case string str:
+ {
+ if (str.ToLower() == "true")
+ return true;
+ if (str.ToLower() == "false")
+ return false;
+
+ str = str.Trim();
+ if (str.Length == 0)
+ return string.Empty;
+
+ if (int.TryParse(str, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var si))
+ {
+ if (!str.StartsWith('0') || str == "0")
+ return si;
+ else
+ return str;
+ }
+
+ if (double.TryParse(str, System.Globalization.NumberStyles.Float | System.Globalization.NumberStyles.AllowThousands,
+ System.Globalization.CultureInfo.InvariantCulture, out var sd))
+ {
+ if (str.StartsWith('0'))
+ return str;
+ if (IsWholeNumber(sd) && sd >= int.MinValue && sd <= int.MaxValue)
+ return (int)sd;
+ return sd;
+ }
+
+ return str;
+ }
+ }
+
+ return v.ToString() ?? string.Empty;
+ }
+
private static string BuildDefaultOutputPath(string inputPath)
{
var dir = Path.GetDirectoryName(inputPath)!;
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs
index fa3b3d76c..8dc36836a 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs
@@ -8,12 +8,12 @@
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
+using System.Threading.Channels;
namespace DataAnalysis.Core.Import;
@@ -30,7 +30,7 @@ public DelimitedTableReader(IDelimitedTableParsingProfile profile)
_profile = profile ?? throw new ArgumentNullException(nameof(profile));
}
- public async Task ReadTableAsync(string inputPath, CancellationToken cancellationToken = default)
+ public async Task ReadTableAsync(string inputPath, CancellationToken cancellationToken = default, Action? progressCallback = null)
{
if (string.IsNullOrWhiteSpace(inputPath))
throw new ArgumentException(ErrorPathEmpty, nameof(inputPath));
@@ -39,6 +39,8 @@ public async Task ReadTableAsync(string inputPath, CancellationToken
var dt = new DataTable(_profile.TableName);
using var fs = new FileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ long totalBytes = fs.Length;
+ long processedBytes = 0;
using var reader = new StreamReader(fs, DetectEncoding(fs) ?? Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
// Header
@@ -50,10 +52,11 @@ public async Task ReadTableAsync(string inputPath, CancellationToken
var headerLine = await reader.ReadLineAsync().ConfigureAwait(false);
if (headerLine is not null)
{
+ processedBytes += Encoding.UTF8.GetByteCount(headerLine) + Environment.NewLine.Length;
var headers = SplitLine(headerLine, _profile.Delimiter, _profile.Quote, _profile.TrimWhitespace);
headerMap = headers.Select((h, i) => new { Name = (h ?? string.Empty).Trim(), i })
- .GroupBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
- .ToDictionary(g => g.Key, g => g.First().i, StringComparer.OrdinalIgnoreCase);
+ .GroupBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
+ .ToDictionary(g => g.Key, g => g.First().i, StringComparer.OrdinalIgnoreCase);
inclFields.AddRange(headerMap.Select((h, i) => i));
foreach (var h in headerMap)
{
@@ -62,7 +65,6 @@ public async Task ReadTableAsync(string inputPath, CancellationToken
if (_profile.ExtractionRules.Any((r) => r.SourceColumn == h.Key))
inclFields.Remove(h.Value);
}
-
}
}
@@ -71,50 +73,144 @@ public async Task ReadTableAsync(string inputPath, CancellationToken
{
EnsureColumn(dt, mapping.Target, mapping.IsDateTime);
}
- foreach (var i in inclFields)
+ if (headerMap is not null)
+ {
+ foreach (var i in inclFields)
+ {
+ var hmi = headerMap.Values.FirstOrDefault(v => v == i);
+ EnsureColumn(dt, headerMap.Keys.ToArray()[hmi]);
+ }
+ }
+
+ DateTime lastReport = DateTime.UtcNow;
+ void ReportProgress()
+ {
+ if (progressCallback is null || totalBytes == 0)
+ return;
+ if ((DateTime.UtcNow - lastReport).TotalSeconds >= 1)
+ {
+ progressCallback(Math.Clamp((double)processedBytes / totalBytes, 0d, 1d));
+ lastReport = DateTime.UtcNow;
+ }
+ }
+ ;
+
+ var headerKeys = headerMap?.Keys.ToArray();
+
+
+ /*
+ // PARALLELE VERARBEITUNG (Producer / Consumer)
+ var channel = Channel.CreateBounded(new BoundedChannelOptions(2048)
+ {
+ SingleWriter = true,
+ SingleReader = false,
+ FullMode = BoundedChannelFullMode.Wait
+ });
+
+ int workerCount = Math.Max(1, Environment.ProcessorCount);
+
+ var consumers = Enumerable.Range(0, workerCount).Select(_ => Task.Run(async () =>
{
- var hmi = headerMap.Values.FirstOrDefault((v) => (v == i));
- EnsureColumn(dt, headerMap.Keys.ToArray()[hmi]);
+ await foreach (var line in channel.Reader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
+ {
+ var flowControl = ProcessLine(dt, headerMap, inclFields, headerKeys, line);
+ if (!flowControl)
+ {
+ continue;
+ }
+
+ }
+ }, cancellationToken)).ToArray();
+
+ // Producer liest Datei
+ string? readLine;
+ while ((readLine = await reader.ReadLineAsync().ConfigureAwait(false)) is not null)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ processedBytes += Encoding.UTF8.GetByteCount(readLine) + Environment.NewLine.Length;
+ ReportProgress();
+ await channel.Writer.WriteAsync(readLine, cancellationToken).ConfigureAwait(false);
}
+ channel.Writer.Complete();
- string? line;
- while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) is not null)
+ await Task.WhenAll(consumers).ConfigureAwait(false);
+ */
+ while (!reader.EndOfStream)
{
+ var line = await reader.ReadLineAsync().ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
- if (string.IsNullOrWhiteSpace(line))
+ processedBytes += Encoding.UTF8.GetByteCount(line) + Environment.NewLine.Length;
+ ReportProgress();
+ var flowControl = ProcessLine(dt, headerMap, inclFields, headerKeys, line);
+ if (!flowControl)
+ {
continue;
- var fields = SplitLine(line, _profile.Delimiter, _profile.Quote, _profile.TrimWhitespace);
+ }
+ }
+
+ progressCallback?.Invoke(1.0);
+ return dt;
+ }
+
+ private bool ProcessLine(DataTable dt, Dictionary? headerMap, List inclFields, string[]? headerKeys, string? line)
+ {
+ if (string.IsNullOrWhiteSpace(line))
+ return false;
+
+ var fields = SplitLine(line, _profile.Delimiter, _profile.Quote, _profile.TrimWhitespace);
- // extraction rules
- var attributes = new Dictionary(StringComparer.OrdinalIgnoreCase);
- ApplyExtractionRules(fields, headerMap, attributes);
+ // extraction rules
+ var attributes = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ ApplyExtractionRules(fields, headerMap, attributes);
- // grow columns for attributes if needed
+ // grow columns for attributes if needed
+ if (attributes.Count > 0)
+ {
foreach (var key in attributes.Keys)
{
if (!_profile.FixedColumns.Any((f) => f.Source == key))
EnsureColumn(dt, key);
}
+ }
- var row = dt.NewRow();
+ // DataRow fllen
+ var rowValues = new Dictionary(StringComparer.OrdinalIgnoreCase);
- foreach (var mapping in _profile.FixedColumns)
- row[mapping.Target] = Extract(mapping, fields, headerMap, attributes);
+ foreach (var mapping in _profile.FixedColumns)
+ {
+ var value = Extract(mapping, fields, headerMap, attributes);
+ rowValues[mapping.Target] = value ?? string.Empty;
+ }
- foreach (var i in inclFields)
+ if (headerMap is not null && headerKeys is not null)
+ {
+ foreach (var iField in inclFields)
{
- var hmi = headerMap.Values.FirstOrDefault((v) => (v == i));
- if (fields.Count>i)
- row[headerMap.Keys.ToArray()[hmi]] = fields[i];
+ if (fields.Count > iField)
+ {
+ var hmi = headerMap.Values.FirstOrDefault(v => v == iField);
+ var colName = headerKeys[hmi];
+ rowValues[colName] = fields[iField] ?? string.Empty;
+ }
}
+ }
- foreach (var kv in attributes)
- if (!_profile.FixedColumns.Any((f) => f.Source == kv.Key))
- row[kv.Key] = kv.Value ?? string.Empty;
- dt.Rows.Add(row);
+ foreach (var kv in attributes)
+ {
+ if (!_profile.FixedColumns.Any((f) => f.Source == kv.Key))
+ rowValues[kv.Key] = kv.Value ?? string.Empty;
}
- return dt;
+
+ lock (dt)
+ {
+ var row = dt.NewRow();
+ foreach (var kv in rowValues)
+ row[kv.Key] = kv.Value ?? string.Empty;
+
+ dt.Rows.Add(row);
+ }
+ return true;
}
private void ApplyExtractionRules(IReadOnlyList fields, Dictionary? headerMap, Dictionary attributes)
@@ -152,7 +248,6 @@ private void ApplyExtractionRules(IReadOnlyList fields, Dictionary fields, Dictionary fields,
- Dictionary? headerMap,
- IReadOnlyDictionary? attributes = null)
+ FixedColumnMapping mapping,
+ IReadOnlyList fields,
+ Dictionary? headerMap,
+ IReadOnlyDictionary? attributes = null)
{
- // Hilfsfunktionen
int? ResolveFieldIndex(string? selector)
{
if (string.IsNullOrWhiteSpace(selector))
@@ -199,9 +293,7 @@ private string Extract(
return null;
}
- // Werte ber fields (Index/Header) ODER attributes extrahieren
- var tsRaw = GetBySelector(mapping.Source, out var _);
-
+ var tsRaw = GetBySelector(mapping.Source, out _);
return tsRaw;
}
@@ -233,9 +325,14 @@ private string Extract(
if (quote.HasValue && c == q)
{
if (inQuotes && i + 1 < line.Length && line[i + 1] == q)
- { sb.Append(q); i++; }
+ {
+ sb.Append(q);
+ i++;
+ }
else
- { inQuotes = !inQuotes; }
+ {
+ inQuotes = !inQuotes;
+ }
continue;
}
if (!inQuotes && c == delimiter)
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/ITableReader.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/ITableReader.cs
index 19ee41d88..9fdc29cb5 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/ITableReader.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/ITableReader.cs
@@ -4,5 +4,5 @@ namespace DataAnalysis.Core.Import.Interfaces;
public interface ITableReader
{
- Task ReadTableAsync(string inputPath, CancellationToken cancellationToken = default);
+ Task ReadTableAsync(string inputPath, CancellationToken cancellationToken = default, Action? progressCallback = null);
}
\ No newline at end of file
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs
index 62d21f842..17ac7408b 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs
@@ -1,3 +1,4 @@
+using ClosedXML.Excel;
using System.Collections.Generic;
namespace DataAnalysis.Core.Models;
@@ -17,14 +18,80 @@ public sealed class AnalysisAggregateProfile
{
Queries = new List
{
+ // SW-ProgID
new AnalysisQuery { Title = "Severity", Dimensions = new [] { DimensionKind.Severity } },
+ new AnalysisQuery { Title = "Source x ProgID", Dimensions = new [] { DimensionKind.Source, DimensionKind.ProgID }, Grouped = true },
new AnalysisQuery { Title = "Source", Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, Dimensions = new [] { DimensionKind.Source }, TopN =300 },
- new AnalysisQuery { Title = "Hour x Source", Dimensions = new [] { DimensionKind.Hour, DimensionKind.Source },Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, },
+ new AnalysisQuery { Title = "Hour x Source", Dimensions = new [] { DimensionKind.Hour, DimensionKind.Source },
+ Filter = new GroupFilterDefinition { Mode="And", Type = "group", Filters = [
+ new ValueFilterDefinition { Field = "PS", Type = "value", Operator = FilterOperator.Eq, Value = "1" },
+ new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" },
+ ] }
+ },
new AnalysisQuery { Title = "Top-Events", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =100 },
- new AnalysisQuery { Title = "Top-Events2", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =30, Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, },
+ new AnalysisQuery { Title = "Top-Errors", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =30,
+ Filter = new GroupFilterDefinition { Mode="And", Type = "group", Filters = [
+ new ValueFilterDefinition { Field = "PS", Type = "value", Operator = FilterOperator.Eq, Value = "1" },
+ new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" },
+ ] }
+ },
new AnalysisQuery { Title = "Severity x Source", Dimensions = new [] { DimensionKind.Source, DimensionKind.Severity }, Columns = Enum.GetValues().Select(s => s.ToString()).ToArray() },
new AnalysisQuery { Title = "Events x Source (Top50)", Dimensions = new [] { DimensionKind.Source, DimensionKind.MessageNormalized }, TopN =300},
- new AnalysisQuery { Title = "Koordinate Cluster", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 30, IsDBScan = true, DbEps = 10.0, DbMinPts = 3, Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, }
+ new AnalysisQuery { Title = "Events x ProgID", Dimensions = new [] { DimensionKind.ProgID, DimensionKind.MessageNormalized }, Grouped = true ,
+ Filter = new ValueFilterDefinition { Field = "PS", Type = "value", Operator = FilterOperator.Eq, Value = "1" } },
+ new AnalysisQuery { Title = "Error Cluster", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 30, IsDBScan = true, DbEps = 2.0, DbMinPts = 3,
+ Filter = new GroupFilterDefinition { Mode="And", Type = "group", Filters = [
+ new ValueFilterDefinition { Field = "PS", Type = "value", Operator = FilterOperator.Eq, Value = "1" },
+ new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" },
+ ] }
+ },
+ new AnalysisQuery { Title = "Max-G Cluster (1.0)", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 30, IsDBScan = true, DbEps = 1.0, DbMinPts = 3,
+ Filter = new GroupFilterDefinition { Mode="And", Type = "group",
+ Filters=[ new ValueFilterDefinition { Field = "Message", Type = "String", Operator = FilterOperator.Eq, Value = "Max. G-Force" },
+ new ValueFilterDefinition { Field = "X", Type = "value", Operator = FilterOperator.Gt, Value = "10" }
+ ] }, },
+ new AnalysisQuery { Title = "Max-G Cluster (0.5)", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 50, IsDBScan = true, DbEps = 0.5, DbMinPts = 3, Filter =
+ new GroupFilterDefinition { Mode="And", Type = "group",
+ Filters=[
+ new ValueFilterDefinition { Field = "Message", Type = "String", Operator = FilterOperator.Eq, Value = "Max. G-Force" },
+ new ValueFilterDefinition { Field = "X", Type = "value", Operator = FilterOperator.Gt, Value = "10" }
+ ] }, },
+ new AnalysisQuery { Title = "Max-G Cluster (0.5) > 1g", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 50, IsDBScan = true, DbEps = 0.5, DbMinPts = 3, Filter =
+ new GroupFilterDefinition { Mode="And", Type = "group",
+ Filters=[
+ new ValueFilterDefinition { Field = "Message", Type = "String", Operator = FilterOperator.Eq, Value = "Max. G-Force" },
+ new ValueFilterDefinition { Field = "X", Type = "value", Operator = FilterOperator.Gt, Value = "10" },
+ new GroupFilterDefinition { Mode="Or", Type = "group",
+ Filters=[
+ new ValueFilterDefinition { Field = "U", Type = "value", Operator = FilterOperator.Ge, Value = "1" },
+ new ValueFilterDefinition { Field = "U", Type = "value", Operator = FilterOperator.Le, Value = "-1" }] }
+ ] }, },
+
+ new AnalysisQuery { Title = "SSCU Cluster (0.5)", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 30, IsDBScan = true, DbEps = 0.5, DbMinPts = 3, Filter =
+ new GroupFilterDefinition { Mode="And", Type = "group", Filters=[
+ new ValueFilterDefinition { Field = "Message", Type = "String", Operator = FilterOperator.StartsWith, Value = "SSCU" },
+ new ValueFilterDefinition { Field = "X", Type = "value", Operator = FilterOperator.Gt, Value = "10" },
+ ]
+ }, },
+ new AnalysisQuery { Title = "SSCU Cluster (0.25)", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 50, IsDBScan = true, DbEps = 0.25, DbMinPts = 3, Filter =
+ new GroupFilterDefinition { Mode="And", Type = "group", Filters=[
+ new ValueFilterDefinition { Field = "Message", Type = "String", Operator = FilterOperator.StartsWith, Value = "SSCU" },
+ new ValueFilterDefinition { Field = "X", Type = "value", Operator = FilterOperator.Gt, Value = "10" },
+ ] },
+ },
+ new AnalysisQuery { Title = "Scanner Front Err (0.25)", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 30, IsDBScan = true, DbEps = 0.25, DbMinPts = 3, Filter =
+ new GroupFilterDefinition { Mode="And", Type = "group", Filters=[
+ new ValueFilterDefinition { Field = "Message", Type = "String", Operator = FilterOperator.StartsWith, Value = "Front Scanner" },
+ new ValueFilterDefinition { Field = "X", Type = "value", Operator = FilterOperator.Gt, Value = "10" },
+ ]
+ }, },
+ new AnalysisQuery { Title = "Scanner Rear Err (0.25)", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 50, IsDBScan = true, DbEps = 0.25, DbMinPts = 3, Filter =
+ new GroupFilterDefinition { Mode="And", Type = "group", Filters=[
+ new ValueFilterDefinition { Field = "Message", Type = "String", Operator = FilterOperator.StartsWith, Value = "Rear Scanner" },
+ new ValueFilterDefinition { Field = "X", Type = "value", Operator = FilterOperator.Gt, Value = "10" },
+ ] },
+ }
+
}
};
}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs
index 9d62fbf87..8ec422351 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs
@@ -4,28 +4,31 @@ namespace DataAnalysis.Core.Models;
public sealed class AnalysisQuery
{
- // Anzeigename fr den Export
- public required string Title { get; init; }
+ // Anzeigename fr den Export
+ public required string Title { get; init; }
- // Liste der Dimensionen:1D => Serie,2D => Kreuztabelle
- public required IReadOnlyList Dimensions { get; init; }
+ // Liste der Dimensionen:1D => Serie,2D => Kreuztabelle
+ public required IReadOnlyList Dimensions { get; init; }
- // Optional: feste Spalten fr2D (z. B. Reihenfolge der Severity-Spalten)
- public IReadOnlyList? Columns { get; init; }
+ // Optional: feste Spalten fr2D (z. B. Reihenfolge der Severity-Spalten)
+ public IReadOnlyList? Columns { get; init; }
- // Optional: TopN-Begrenzung fr1D-Serien
- public int? TopN { get; init; }
+ // Optional: TopN-Begrenzung fr1D-Serien
+ public int? TopN { get; init; }
- // Optional: TopN pro Zeile fr2D-Kreuztabellen
- public int? RowTopN { get; init; }
+ // Optional: TopN pro Zeile fr2D-Kreuztabellen
+ public int? RowTopN { get; init; }
- // Optional: DBSCAN-Clustering der Punkte (bei2D X/Y)
- public bool IsDBScan { get; init; } = false;
- // Optional: Radius (Epsilon) fr DBSCAN (gleiche Einheit wie X/Y)
- public double? DbEps { get; init; }
- // Optional: Mindestanzahl Punkte pro Cluster
- public int? DbMinPts { get; init; }
+ // Optional: DBSCAN-Clustering der Punkte (bei2D X/Y)
+ public bool IsDBScan { get; init; } = false;
+ // Optional: Radius (Epsilon) fr DBSCAN (gleiche Einheit wie X/Y)
+ public double? DbEps { get; init; }
+ // Optional: Mindestanzahl Punkte pro Cluster
+ public int? DbMinPts { get; init; }
- // Optional: Auswertungsspezifischer Filter (DTO, serialisierbar)
- public FilterDefinition? Filter { get; init; }
+ // Optional: Auswertungsspezifischer Filter (DTO, serialisierbar)
+ public FilterDefinition? Filter { get; init; }
+
+ // Optional: Gruppierte Ausgabe (fr2D-Tabellen)
+ public bool Grouped { get; init; }
}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs
index 30f2a0758..3df695e29 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs
@@ -2,10 +2,12 @@ namespace DataAnalysis.Core.Models;
public enum DimensionKind
{
- Severity,
- Source,
- Hour, // Hour bucket of timestamp
- MessageNormalized,
- X,
- Y
+ Severity,
+ Source,
+ Hour, // Hour bucket of timestamp
+ MessageNormalized,
+ ProgID, // Software version extracted from message
+ X,
+ Y,
+ MsgID,
}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterCompiler.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterCompiler.cs
index cec3effcb..90588ce33 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterCompiler.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterCompiler.cs
@@ -117,28 +117,28 @@ public static class FilterCompiler
break;
}
- return e =>
+ return e =>
{
if (!TryGetFieldText(e, v.Field, out var text, out var typed))
- return false;
+ return false ;
switch (v.DataType)
{
case FilterDataType.Number:
if (!TryAsDouble(text, typed, culture, out var d))
return false;
- return CompareNumbers(d, v.Operator, parsedFrom as double?, parsedTo as double?);
+ return v.Negate ^ CompareNumbers(d, v.Operator, parsedFrom as double?, parsedTo as double?);
case FilterDataType.DateTime:
if (!TryAsDateTimeOffset(text, typed, culture, formats, out var dt))
return false;
- return CompareDateTimes(dt, v.Operator, parsedFrom as DateTimeOffset?, parsedTo as DateTimeOffset?);
+ return v.Negate ^ CompareDateTimes(dt, v.Operator, parsedFrom as DateTimeOffset?, parsedTo as DateTimeOffset?);
case FilterDataType.Enum:
if (!TryAsEnum(text, typed, parsedFrom, out var eobj))
return false;
- return CompareEnums(eobj, v.Operator, parsedFrom, parsedTo);
+ return v.Negate ^ CompareEnums(eobj!, v.Operator, parsedFrom, parsedTo);
case FilterDataType.String:
default:
- return CompareStrings(text ?? string.Empty, v.Operator, v.Value, v.ValueTo, cmp);
+ return v.Negate ^ CompareStrings(text ?? string.Empty, v.Operator, v.Value, v.ValueTo, cmp);
}
};
}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDefinitions.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDefinitions.cs
index 4e0577214..c4ebe5208 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDefinitions.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDefinitions.cs
@@ -9,7 +9,10 @@ namespace DataAnalysis.Core.Models;
///
public abstract class FilterDefinition
{
- [JsonPropertyName("type")] public required string Type { get; init; }
+ [JsonPropertyName("type")] public string Type { get; init; }
+
+ public bool Negate { get; init; } = false;
+
}
public sealed class ValueFilterDefinition : FilterDefinition
@@ -34,5 +37,4 @@ public sealed class GroupFilterDefinition : FilterDefinition
// "and" oder "or"
public required string Mode { get; init; }
public required IReadOnlyList Filters { get; init; }
- public bool Negate { get; init; } = false;
}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs
index 488353515..5d304497f 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs
@@ -56,7 +56,7 @@ public void Observe(SyslogEntry e)
var col = GetKey(e, _q.Dimensions[1]);
if (row is null || col is null) return;
if (!_matrix.TryGetValue(row, out var d)) { d = new Dictionary(StringComparer.OrdinalIgnoreCase); _matrix[row] = d; }
- d[col] = d.TryGetValue(col, out var c) ? c +1 :1;
+ d[col] = d.TryGetValue(col, out var c) && !_q.Grouped ? c +1 :1;
}
}
@@ -152,6 +152,8 @@ public AggregationResult Build()
DimensionKind.Source => string.IsNullOrWhiteSpace(e.Source) ? "(unbekannt)" : e.Source.Trim(),
DimensionKind.Hour => e.Timestamp is DateTimeOffset ts ? new DateTimeOffset(ts.Year, ts.Month, ts.Day, ts.Hour,0,0, ts.Offset).LocalDateTime.ToString("yyyy-MM-dd HH:00") : null,
DimensionKind.MessageNormalized => AnalysisModel.NormalizeMessage(e.Message),
+ DimensionKind.ProgID => TryGetAttributeAsString(e, "eventprogid", out var s) ? s : "",
+ DimensionKind.MsgID => TryGetAttributeAsString(e, "eventmsgid", out var s) ? s : null,
DimensionKind.X => TryGetAttributeAsDouble(e, "X", out var x) ? x.ToString(CultureInfo.InvariantCulture) : null,
DimensionKind.Y => TryGetAttributeAsDouble(e, "Y", out var y) ? y.ToString(CultureInfo.InvariantCulture) : null,
_ => null
@@ -176,5 +178,17 @@ private static bool TryGetAttributeAsDouble(SyslogEntry e, string key, out doubl
}
return false;
}
+
+ private static bool TryGetAttributeAsString(SyslogEntry e, string key, out string value)
+ {
+ value = "";
+ if (e.Attributes.TryGetValue(key, out var s))
+ {
+ value = s;
+ return true;
+ }
+ return false;
+ }
+
}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml.cs
index 4edbbfb24..493e11e41 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml.cs
@@ -56,8 +56,8 @@ private void OnLoadClusters(object sender, RoutedEventArgs e)
{
[new Vector2(0, 0)] = 15000,
[new Vector2(100000, 50000)] = 2000,
- [new Vector2(-50000, 80000)] = 8000,
- [new Vector2(800000, -230000)] = 300
+ [new Vector2(-50000, 80000)] = 800,
+ [new Vector2(800000, -230000)] = 300000
};
var agg = new AggregationResult
{
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/ClusterAggregationViewModel.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/ClusterAggregationViewModel.cs
index ed5284a8b..312ede925 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/ClusterAggregationViewModel.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/ClusterAggregationViewModel.cs
@@ -8,40 +8,29 @@ namespace DataAnalysis.WPF.ViewModels;
public sealed partial class ClusterAggregationViewModel : AggregationItemViewModel
{
- public sealed class PointItem
- {
- public double X { get; init; }
- public double Y { get; init; }
- public int Count { get; init; }
- }
+ public sealed class PointItem { public double X { get; init; } public double Y { get; init; } public int Count { get; init; } }
+ public sealed class AxisTick { public double Value { get; init; } public string Label { get; init; } = string.Empty; }
public ObservableCollection Points { get; } = new();
public ObservableCollection RawPoints { get; } = new();
+ public ObservableCollection XTicks { get; } = new();
+ public ObservableCollection YTicks { get; } = new();
- [ObservableProperty]
- private double minX;
- [ObservableProperty]
- private double maxX;
- [ObservableProperty]
- private double minY;
- [ObservableProperty]
- private double maxY;
- [ObservableProperty]
- private int maxCount;
+ [ObservableProperty] private double minX;
+ [ObservableProperty] private double maxX;
+ [ObservableProperty] private double minY;
+ [ObservableProperty] private double maxY;
+ [ObservableProperty] private int maxCount;
+ [ObservableProperty] private bool hasZeroX;
+ [ObservableProperty] private bool hasZeroY;
- public ClusterAggregationViewModel(AggregationResult agg)
- : base(agg.Title)
- {
- Rebuild(agg);
- }
+ public ClusterAggregationViewModel(AggregationResult agg) : base(agg.Title) { Rebuild(agg); }
private void Rebuild(AggregationResult agg)
{
- Points.Clear();
- RawPoints.Clear();
+ Points.Clear(); RawPoints.Clear(); XTicks.Clear(); YTicks.Clear();
double minx = double.PositiveInfinity, maxx = double.NegativeInfinity;
- double miny = double.PositiveInfinity, maxy = double.NegativeInfinity;
- int maxc = 0;
+ double miny = double.PositiveInfinity, maxy = double.NegativeInfinity; int maxc = 0;
if (agg.Series is not null)
{
foreach (var kv in agg.Series)
@@ -49,116 +38,77 @@ private void Rebuild(AggregationResult agg)
if (TryGetXY(kv.Key, out var x, out var y))
{
Points.Add(new PointItem { X = x, Y = y, Count = kv.Value });
- if (x < minx)
- minx = x;
- if (x > maxx)
- maxx = x;
- if (y < miny)
- miny = y;
- if (y > maxy)
- maxy = y;
- if (kv.Value > maxc)
- maxc = kv.Value;
+ if (x < minx) minx = x; if (x > maxx) maxx = x;
+ if (y < miny) miny = y; if (y > maxy) maxy = y;
+ if (kv.Value > maxc) maxc = kv.Value;
}
}
}
- if (double.IsInfinity(minx) || double.IsInfinity(miny))
- {
- minx = 0;
- maxx = 1;
- miny = 0;
- maxy = 1;
- maxc = 1;
- }
- MinX = minx;
- MaxX = maxx;
- MinY = miny;
- MaxY = maxy;
- MaxCount = maxc;
+ if (double.IsInfinity(minx) || double.IsInfinity(miny)) { minx = 0; maxx = 1; miny = 0; maxy = 1; maxc = 1; }
+ MinX = minx; MaxX = maxx; MinY = miny; MaxY = maxy; MaxCount = maxc;
+ HasZeroX = MinX <= 0 && MaxX >= 0; HasZeroY = MinY <= 0 && MaxY >= 0;
+ BuildTicks(XTicks, MinX, MaxX, 6); BuildTicks(YTicks, MinY, MaxY, 6);
}
- private static bool TryGetXY(object key, out double x, out double y)
+ private static void BuildTicks(ObservableCollection target, double min, double max, int desired)
+ {
+ target.Clear(); if (max <= min) { target.Add(new AxisTick { Value = min, Label = min.ToString("G4") }); return; }
+ var range = max - min; var rawStep = range / desired; double magnitude = System.Math.Pow(10, System.Math.Floor(System.Math.Log10(rawStep)));
+ double normalized = rawStep / magnitude; double step = normalized < 1.5 ? 1 * magnitude : normalized < 3 ? 2 * magnitude : normalized < 7 ? 5 * magnitude : 10 * magnitude;
+ double start = System.Math.Ceiling(min / step) * step;
+ for (double v = start; v <= max + step * 0.001; v += step) target.Add(new AxisTick { Value = v, Label = FormatTick(v, range) });
+ if (min <= 0 && max >= 0 && !target.Any(t => System.Math.Abs(t.Value) < step * 0.001)) target.Add(new AxisTick { Value = 0, Label = FormatTick(0, range) });
+ var ordered = target.OrderBy(t => t.Value).ToList(); if (ordered.Count != target.Count) { target.Clear(); foreach (var t in ordered) target.Add(t); }
+ }
+
+ private static string FormatTick(double value, double range)
{
- x = y = 0;
- if (key is null)
- return false;
- var t = key.GetType();
+ if (range == 0) return value.ToString("G4"); var absRange = System.Math.Abs(range);
+ if (absRange >= 100000 || absRange <= 0.001) return value.ToString("G3");
+ if (absRange >= 1000) return value.ToString("G4");
+ if (absRange >= 1) return value.ToString("G5");
+ return value.ToString("G3");
+ }
- //1) Properties named X/Y
- var px = t.GetProperty("X");
- var py = t.GetProperty("Y");
- if (px is not null && py is not null)
+ private static bool TryGetXY(object key, out double x, out double y)
+ {
+ x = y = 0; if (key is null) return false; var t = key.GetType();
+ var px = t.GetProperty("X"); var py = t.GetProperty("Y"); if (px is not null && py is not null)
{
- var vx = px.GetValue(key);
- var vy = py.GetValue(key);
- if (TryToDouble(vx, out x) && TryToDouble(vy, out y))
- return true;
+ var vx = px.GetValue(key); var vy = py.GetValue(key);
+ if (TryToDouble(vx, out x) && TryToDouble(vy, out y)) return true;
}
-
- //2) Fields named X/Y (e.g., System.Numerics.Vector2)
- var fx = t.GetField("X");
- var fy = t.GetField("Y");
- if (fx is not null && fy is not null)
+ var fx = t.GetField("X"); var fy = t.GetField("Y"); if (fx is not null && fy is not null)
{
- var vx = fx.GetValue(key);
- var vy = fy.GetValue(key);
- if (TryToDouble(vx, out x) && TryToDouble(vy, out y))
- return true;
+ var vx = fx.GetValue(key); var vy = fy.GetValue(key);
+ if (TryToDouble(vx, out x) && TryToDouble(vy, out y)) return true;
}
-
- //3) ValueTuple or similar (Item1/Item2)
if (t.IsGenericType && t.FullName!.StartsWith("System.ValueTuple`2"))
{
- var f1 = t.GetField("Item1");
- var f2 = t.GetField("Item2");
- if (f1 is not null && f2 is not null)
+ var f1 = t.GetField("Item1"); var f2 = t.GetField("Item2"); if (f1 is not null && f2 is not null)
{
- var v1 = f1.GetValue(key);
- var v2 = f2.GetValue(key);
- if (TryToDouble(v1, out x) && TryToDouble(v2, out y))
- return true;
+ var v1 = f1.GetValue(key); var v2 = f2.GetValue(key);
+ if (TryToDouble(v1, out x) && TryToDouble(v2, out y)) return true;
}
}
-
- //4) ITuple support
if (key is ITuple tuple && tuple.Length >= 2)
{
- var v1 = tuple[0];
- var v2 = tuple[1];
- if (TryToDouble(v1, out x) && TryToDouble(v2, out y))
- return true;
+ var v1 = tuple[0]; var v2 = tuple[1];
+ if (TryToDouble(v1, out x) && TryToDouble(v2, out y)) return true;
}
-
- //5) double[] or similar length>=2
if (key is System.Collections.IList list && list.Count >= 2)
{
- var v1 = list[0];
- var v2 = list[1];
- if (TryToDouble(v1, out x) && TryToDouble(v2, out y))
- return true;
+ var v1 = list[0]; var v2 = list[1];
+ if (TryToDouble(v1, out x) && TryToDouble(v2, out y)) return true;
}
-
- //6) Fallback: parse from ToString like "(x,y)"
- var s = key.ToString() ?? string.Empty;
- var m = System.Text.RegularExpressions.Regex.Match(s, "\\(?\\s*(-?[0-9]+(?:\\.[0-9]+)?)\\s*,\\s*(-?[0-9]+(?:\\.[0-9]+)?)\\s*\\)?");
+ var s = key.ToString() ?? string.Empty; var m = System.Text.RegularExpressions.Regex.Match(s, @"\(?\s*(-?[0-9]+(?:\.[0-9]+)?)\s*,\s*(-?[0-9]+(?:\.[0-9]+)?)\s*\)?");
if (m.Success)
{
if (double.TryParse(m.Groups[1].Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out x) &&
- double.TryParse(m.Groups[2].Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out y))
- {
- return true;
- }
+ double.TryParse(m.Groups[2].Value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out y)) return true;
}
return false;
}
- private static bool TryToDouble(object? v, out double d)
- {
- try
- {
- d = System.Convert.ToDouble(v, System.Globalization.CultureInfo.InvariantCulture);
- return true;
- }
- catch { d = 0; return false; }
- }
+ private static bool TryToDouble(object? v, out double d) { try { d = System.Convert.ToDouble(v, System.Globalization.CultureInfo.InvariantCulture); return true; } catch { d = 0; return false; } }
}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MatrixAggregationViewModel.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MatrixAggregationViewModel.cs
index d5caeeaed..77a45ad0c 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MatrixAggregationViewModel.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/ViewModels/MatrixAggregationViewModel.cs
@@ -15,7 +15,7 @@ public MatrixAggregationViewModel(AggregationResult agg)
var dt = new DataTable();
dt.Columns.Add("Key", typeof(string));
var columns = agg.Columns ?? Array.Empty();
- foreach (var col in columns) dt.Columns.Add(col.Replace(".", "˳"), typeof(int));
+ foreach (var col in columns) dt.Columns.Add(col.Replace(".", "˳").Replace("/", "|"), typeof(int));
if (agg.Matrix is not null)
{
var rows = agg.Matrix.Keys.ToList();
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/ClusterAggregationView.xaml b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/ClusterAggregationView.xaml
index a46ae7222..f3741c812 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/ClusterAggregationView.xaml
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Controls/ClusterAggregationView.xaml
@@ -1,155 +1,294 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotConverters.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotConverters.cs
index 75433a164..ab2462271 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotConverters.cs
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotConverters.cs
@@ -4,8 +4,10 @@
namespace DataAnalysis.WPF.Views.Converters;
-public sealed class PlotXConverter : IMultiValueConverter
+public sealed class PlotConverter : IMultiValueConverter
{
+ // Half of maximum bubble size (SizeByCountConverter.MaxSize) to keep bubbles inside plot.
+ private const double EdgePadding = 60d; // MaxSize (120) / 2
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values is null || values.Length < 4) return 0d;
@@ -15,9 +17,18 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur
var minX = System.Convert.ToDouble(values[1], CultureInfo.InvariantCulture);
var maxX = System.Convert.ToDouble(values[2], CultureInfo.InvariantCulture);
var width = System.Convert.ToDouble(values[3], CultureInfo.InvariantCulture);
- if (maxX <= minX) return 0d;
+ var Offset = parameter != null?System.Convert.ToDouble(parameter, CultureInfo.InvariantCulture):0d;
+ if (width <= 0) return 0d;
+ if (maxX == minX)
+ {
+ // Degenerate range: place at center with padding consideration
+ var usable = Math.Max(0, width - 2 * EdgePadding);
+ return EdgePadding + usable / 2;
+ }
+ var usableWidth = Math.Max(0, width - 2 * EdgePadding);
var t = (x - minX) / (maxX - minX);
- return Math.Max(0, Math.Min(width, t * width));
+ t = Math.Max(0, Math.Min(1, t));
+ return EdgePadding + t * usableWidth+Offset;
}
catch
{
@@ -27,52 +38,3 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
}
-
-public sealed class PlotYConverter : IMultiValueConverter
-{
- public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
- {
- if (values is null || values.Length < 4) return 0d;
- try
- {
- var y = System.Convert.ToDouble(values[0], CultureInfo.InvariantCulture);
- var minY = System.Convert.ToDouble(values[1], CultureInfo.InvariantCulture);
- var maxY = System.Convert.ToDouble(values[2], CultureInfo.InvariantCulture);
- var height = System.Convert.ToDouble(values[3], CultureInfo.InvariantCulture);
- if (maxY <= minY) return 0d;
- var t = (y - minY) / (maxY - minY);
- return Math.Max(0, Math.Min(height, (1 - t) * height));
- }
- catch
- {
- return 0d;
- }
- }
-
- public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
-}
-
-public sealed class SizeByCountConverter : IMultiValueConverter
-{
- public double MinSize { get; set; } = 6;
- public double MaxSize { get; set; } = 120;
- public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
- {
- if (values is null || values.Length < 2) return MinSize;
- try
- {
- var count = System.Convert.ToDouble(values[0], CultureInfo.InvariantCulture);
- var max = System.Convert.ToDouble(values[1], CultureInfo.InvariantCulture);
- if (max <= 0) return MinSize;
- var t = Math.Max(0, Math.Min(1, count / max));
- t = Math.Sqrt(t);
- return MinSize + (MaxSize - MinSize) * t;
- }
- catch
- {
- return MinSize;
- }
- }
-
- public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
-}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotXCenteredConverter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotXCenteredConverter.cs
new file mode 100644
index 000000000..dd5add01d
--- /dev/null
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotXCenteredConverter.cs
@@ -0,0 +1,50 @@
+using System.Globalization;
+using System.Windows.Data;
+
+namespace DataAnalysis.WPF.Views.Converters;
+
+// Centered converters: return Canvas.Left/Top such that given (X,Y) is bubble center.
+// Expect bindings: X|minX|maxX|width|count|maxCount (6 values) for X, similarly Y.
+public sealed class PlotXCenteredConverter : IMultiValueConverter
+{
+ private const double EdgePadding = 60d; // Align with PlotConverter padding
+ private const double MinSize = 6d;
+ private const double MaxSize = 120d;
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (values is null || values.Length < 6) return 0d;
+ try
+ {
+ var x = System.Convert.ToDouble(values[0], CultureInfo.InvariantCulture);
+ var minX = System.Convert.ToDouble(values[1], CultureInfo.InvariantCulture);
+ var maxX = System.Convert.ToDouble(values[2], CultureInfo.InvariantCulture);
+ var width = System.Convert.ToDouble(values[3], CultureInfo.InvariantCulture);
+ var count = System.Convert.ToDouble(values[4], CultureInfo.InvariantCulture);
+ var maxCount = System.Convert.ToDouble(values[5], CultureInfo.InvariantCulture);
+ if (width <= 0) return 0d;
+ double size = MinSize;
+ if (maxCount > 0)
+ {
+ var tSize = Math.Max(0, Math.Min(1, count / maxCount));
+ tSize = Math.Sqrt(tSize);
+ size = MinSize + (MaxSize - MinSize) * tSize;
+ }
+ var radius = size / 2d;
+ if (maxX == minX)
+ {
+ return Math.Max(0, Math.Min(width - size, width / 2d - radius));
+ }
+ var usableWidth = Math.Max(0, width - 2 * EdgePadding);
+ var t = (x - minX) / (maxX - minX);
+ t = Math.Max(0, Math.Min(1, t));
+ var center = EdgePadding + t * usableWidth;
+ var left = center - radius;
+ // Clamp to keep bubble fully visible
+ if (left < 0) left = 0;
+ if (left + size > width) left = Math.Max(0, width - size);
+ return left;
+ }
+ catch { return 0d; }
+ }
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
+}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotYCenteredConverter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotYCenteredConverter.cs
new file mode 100644
index 000000000..1cf1f2607
--- /dev/null
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/PlotYCenteredConverter.cs
@@ -0,0 +1,47 @@
+using System.Globalization;
+using System.Windows.Data;
+
+namespace DataAnalysis.WPF.Views.Converters;
+
+public sealed class PlotYCenteredConverter : IMultiValueConverter
+{
+ private const double EdgePadding = 60d;
+ private const double MinSize = 6d;
+ private const double MaxSize = 120d;
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (values is null || values.Length < 6) return 0d;
+ try
+ {
+ var y = System.Convert.ToDouble(values[0], CultureInfo.InvariantCulture);
+ var minY = System.Convert.ToDouble(values[1], CultureInfo.InvariantCulture);
+ var maxY = System.Convert.ToDouble(values[2], CultureInfo.InvariantCulture);
+ var height = System.Convert.ToDouble(values[3], CultureInfo.InvariantCulture);
+ var count = System.Convert.ToDouble(values[4], CultureInfo.InvariantCulture);
+ var maxCount = System.Convert.ToDouble(values[5], CultureInfo.InvariantCulture);
+ if (height <= 0) return 0d;
+ double size = MinSize;
+ if (maxCount > 0)
+ {
+ var tSize = Math.Max(0, Math.Min(1, count / maxCount));
+ tSize = Math.Sqrt(tSize);
+ size = MinSize + (MaxSize - MinSize) * tSize;
+ }
+ var radius = size / 2d;
+ if (maxY <= minY)
+ {
+ return Math.Max(0, Math.Min(height - size, height / 2d - radius));
+ }
+ var usableHeight = Math.Max(0, height - 2 * EdgePadding);
+ var t = (y - minY) / (maxY - minY);
+ t = Math.Max(0, Math.Min(1, t));
+ var center = EdgePadding + (1 - t) * usableHeight; // invert y
+ var top = center - radius;
+ if (top < 0) top = 0;
+ if (top + size > height) top = Math.Max(0, height - size);
+ return top;
+ }
+ catch { return 0d; }
+ }
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
+}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/SizeByCountConverter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/SizeByCountConverter.cs
new file mode 100644
index 000000000..1671dd3da
--- /dev/null
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF/Views/Converters/SizeByCountConverter.cs
@@ -0,0 +1,25 @@
+using System.Globalization;
+using System.Windows.Data;
+
+namespace DataAnalysis.WPF.Views.Converters;
+
+public sealed class SizeByCountConverter : IMultiValueConverter
+{
+ public double MinSize { get; set; } = 6;
+ public double MaxSize { get; set; } = 120;
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (values is null || values.Length < 2) return MinSize;
+ try
+ {
+ var count = System.Convert.ToDouble(values[0], CultureInfo.InvariantCulture);
+ var max = System.Convert.ToDouble(values[1], CultureInfo.InvariantCulture);
+ if (max <= 0) return MinSize;
+ var t = Math.Max(0, Math.Min(1, count / max));
+ t = Math.Sqrt(t);
+ return MinSize + (MaxSize - MinSize) * t;
+ }
+ catch { return MinSize; }
+ }
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
+}
diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.sln b/CSharpBible/Data/DataAnalysis/DataAnalysis.sln
index 3a5acb01e..ddc2dea06 100644
--- a/CSharpBible/Data/DataAnalysis/DataAnalysis.sln
+++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.14.36616.10
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11205.157 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAnalysis.Core", "DataAnalysis.Core\DataAnalysis.Core.csproj", "{D8808BEF-2D64-4D46-903F-B1A0499D9F66}"
EndProject
@@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAnalysis.WPF.TestHarnes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataConvert.Console", "DataConvert.Console\DataConvert.Console.csproj", "{E9D94D2B-42BA-4AD6-9CB2-D2D889B289FE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAnalysis.Core.Tests", "DataAnalysis.Core.Tests\DataAnalysis.Core.Tests.csproj", "{DB99A65F-675C-E212-80B3-50A0C0D87138}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
{E9D94D2B-42BA-4AD6-9CB2-D2D889B289FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9D94D2B-42BA-4AD6-9CB2-D2D889B289FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9D94D2B-42BA-4AD6-9CB2-D2D889B289FE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DB99A65F-675C-E212-80B3-50A0C0D87138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DB99A65F-675C-E212-80B3-50A0C0D87138}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DB99A65F-675C-E212-80B3-50A0C0D87138}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DB99A65F-675C-E212-80B3-50A0C0D87138}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/CSharpBible/Data/DataAnalysis/DataConvert.Console/App.cs b/CSharpBible/Data/DataAnalysis/DataConvert.Console/App.cs
index 90c015545..53d2923d0 100644
--- a/CSharpBible/Data/DataAnalysis/DataConvert.Console/App.cs
+++ b/CSharpBible/Data/DataAnalysis/DataConvert.Console/App.cs
@@ -18,6 +18,7 @@ public App()
{
// Exporter: table only
services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton(sp => new DelimitedTableParsingProfile
{
@@ -44,6 +45,8 @@ public App()
.Build();
}
+ IConsoleWriter? console;
+
public async Task RunAsync(string[] args)
{
await _host.StartAsync();
@@ -57,10 +60,12 @@ public async Task RunAsync(string[] args)
var inputPath = args[0];
var tableReader = _host.Services.GetRequiredService();
var exporter = _host.Services.GetRequiredService();
-
- var table = await tableReader.ReadTableAsync(inputPath, CancellationToken.None);
- var output = await exporter.ExportAsync(table, inputPath, null, CancellationToken.None);
- System.Console.WriteLine(output);
+ console = _host.Services.GetRequiredService();
+ console.WriteLine("Reading ...");
+ var table = await tableReader.ReadTableAsync(inputPath, CancellationToken.None,onProgress);
+ console.WriteLine("\nConverting ...");
+ var output = await exporter.ExportAsync(table, inputPath, null, CancellationToken.None,onProgress);
+ console.WriteLine("\n"+output);
return 0;
}
catch (Exception ex)
@@ -74,4 +79,14 @@ public async Task RunAsync(string[] args)
_host.Dispose();
}
}
+
+ private void onProgress(double obj)
+ {
+ console.Write("[");
+ int totalBlocks = 70;
+ int filledBlocks = (int)(obj * totalBlocks);
+ console.Write(new string('#', filledBlocks));
+ console.Write(new string('-', totalBlocks - filledBlocks));
+ console.Write($"] {obj:P0}\r");
+ }
}
diff --git a/CSharpBible/Data/DataAnalysis/DataConvert.Console/ConsoleWriter.cs b/CSharpBible/Data/DataAnalysis/DataConvert.Console/ConsoleWriter.cs
new file mode 100644
index 000000000..f5aa8739c
--- /dev/null
+++ b/CSharpBible/Data/DataAnalysis/DataConvert.Console/ConsoleWriter.cs
@@ -0,0 +1,14 @@
+namespace DataConvert.Console;
+
+public class ConsoleWriter : IConsoleWriter
+{
+ public void Write(string v)
+ {
+ System.Console.Write(v);
+ }
+
+ public void WriteLine(string v)
+ {
+ System.Console.WriteLine(v);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/DataAnalysis/DataConvert.Console/DataConvert.Console.csproj b/CSharpBible/Data/DataAnalysis/DataConvert.Console/DataConvert.Console.csproj
index 70799f400..6af1c927c 100644
--- a/CSharpBible/Data/DataAnalysis/DataConvert.Console/DataConvert.Console.csproj
+++ b/CSharpBible/Data/DataAnalysis/DataConvert.Console/DataConvert.Console.csproj
@@ -7,6 +7,10 @@
enable
+
+
+
+
@@ -15,4 +19,10 @@
+
+
+ PreserveNewest
+
+
+
diff --git a/CSharpBible/Data/DataAnalysis/DataConvert.Console/IConsoleWriter.cs b/CSharpBible/Data/DataAnalysis/DataConvert.Console/IConsoleWriter.cs
new file mode 100644
index 000000000..414f7e8f5
--- /dev/null
+++ b/CSharpBible/Data/DataAnalysis/DataConvert.Console/IConsoleWriter.cs
@@ -0,0 +1,7 @@
+namespace DataConvert.Console;
+
+internal interface IConsoleWriter
+{
+ void Write(string v);
+ void WriteLine(string v);
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/DataAnalysis/DataConvert.Console/Ressources/.info b/CSharpBible/Data/DataAnalysis/DataConvert.Console/Ressources/.info
new file mode 100644
index 000000000..e69de29bb
diff --git a/CSharpBible/Data/DocumentUtils/DocOdfTest/DocOdfTest.csproj b/CSharpBible/Data/DocumentUtils/DocOdfTest/DocOdfTest.csproj
new file mode 100644
index 000000000..a1c1c6c17
--- /dev/null
+++ b/CSharpBible/Data/DocumentUtils/DocOdfTest/DocOdfTest.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Data/DocumentUtils/DocOdfTest/Program.cs b/CSharpBible/Data/DocumentUtils/DocOdfTest/Program.cs
new file mode 100644
index 000000000..e86784e6d
--- /dev/null
+++ b/CSharpBible/Data/DocumentUtils/DocOdfTest/Program.cs
@@ -0,0 +1,29 @@
+using Document.Base.Models;
+using Document.Odf;
+
+namespace DocOdfTest;
+
+internal class Program
+{
+ static void Main(string[] args)
+ {
+ // Neues Dokument erstellen
+ var doc = new OdfTextDocument();
+
+ // Überschrift hinzufügen
+ var h1 = doc.AddHeadline(1);
+ h1.AppendText("Mein Dokument");
+
+ // Absatz hinzufügen
+ var p = doc.AddParagraph("");
+ p.AppendText("Dies ist ein ");
+ p.AddSpan("fetter", EFontStyle.Bold);
+ p.AppendText(" Text.");
+
+ // Speichern
+ doc.SaveTo("test.odt");
+
+ // Laden
+ doc.LoadFrom("existing.odt");
+ }
+}
diff --git a/CSharpBible/Data/DocumentUtils/Document.Base/Document.Base.csproj b/CSharpBible/Data/DocumentUtils/Document.Base/Document.Base.csproj
index f817901a6..1a195279a 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Base/Document.Base.csproj
+++ b/CSharpBible/Data/DocumentUtils/Document.Base/Document.Base.csproj
@@ -1,10 +1,16 @@
- net8.0;net9.0;net10.0
+ net8.0
enable
enable
preview
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
diff --git a/CSharpBible/Data/DocumentUtils/Document.Docx/Document.Docx.csproj b/CSharpBible/Data/DocumentUtils/Document.Docx/Document.Docx.csproj
index 62a854977..644f2746a 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Docx/Document.Docx.csproj
+++ b/CSharpBible/Data/DocumentUtils/Document.Docx/Document.Docx.csproj
@@ -1,12 +1,18 @@
- net8.0;net9.0;net10.0
+ net8.0
enable
preview
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
-
+
diff --git a/CSharpBible/Data/DocumentUtils/Document.Html/Document.Html.csproj b/CSharpBible/Data/DocumentUtils/Document.Html/Document.Html.csproj
index b3caa5fed..22bd4cb73 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Html/Document.Html.csproj
+++ b/CSharpBible/Data/DocumentUtils/Document.Html/Document.Html.csproj
@@ -1,16 +1,22 @@
-
+
- net9.0
+ net8.0
enable
enable
preview
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
-
+
\ No newline at end of file
diff --git a/CSharpBible/Data/DocumentUtils/Document.Odf/Document.Odf.csproj b/CSharpBible/Data/DocumentUtils/Document.Odf/Document.Odf.csproj
index 0e3ffb3c4..c53c7ce5b 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Odf/Document.Odf.csproj
+++ b/CSharpBible/Data/DocumentUtils/Document.Odf/Document.Odf.csproj
@@ -1,10 +1,16 @@
- net9.0
+ net8.0
enable
enable
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
diff --git a/CSharpBible/Data/DocumentUtils/Document.Odf/Models/OdfNamespaces.cs b/CSharpBible/Data/DocumentUtils/Document.Odf/Models/OdfNamespaces.cs
index 12dc45f97..1de21292f 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Odf/Models/OdfNamespaces.cs
+++ b/CSharpBible/Data/DocumentUtils/Document.Odf/Models/OdfNamespaces.cs
@@ -9,4 +9,9 @@ public static class OdfNamespaces
public static readonly XNamespace Style = "urn:oasis:names:tc:opendocument:xmlns:style:1.0";
public static readonly XNamespace Table = "urn:oasis:names:tc:opendocument:xmlns:table:1.0";
public static readonly XNamespace XLink = "http://www.w3.org/1999/xlink";
+ public static readonly XNamespace Fo = "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0";
+ public static readonly XNamespace Svg = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0";
+ public static readonly XNamespace Dc = "http://purl.org/dc/elements/1.1/";
+ public static readonly XNamespace Meta = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0";
+ public static readonly XNamespace Manifest = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0";
}
\ No newline at end of file
diff --git a/CSharpBible/Data/DocumentUtils/Document.Odf/Models/OdfTextDocument.cs b/CSharpBible/Data/DocumentUtils/Document.Odf/Models/OdfTextDocument.cs
index 04df18aa2..8cc20aa78 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Odf/Models/OdfTextDocument.cs
+++ b/CSharpBible/Data/DocumentUtils/Document.Odf/Models/OdfTextDocument.cs
@@ -1,21 +1,177 @@
using Document.Base.Models.Interfaces;
+using Document.Odf.Parsing;
+using Document.Odf.Writing;
namespace Document.Odf.Models;
public class OdfTextDocument : IUserDocument
{
- public static IUserDocument CreateUserDocument()
+ private readonly OdfSection _root = new();
+ private bool _isModified;
+
+ public IDocElement Root => _root;
+
+ public bool IsModified => _isModified;
+
+ public static IUserDocument CreateUserDocument() => new OdfTextDocument();
+
+ public IDocHeadline AddHeadline(int nLevel, string? Id = null)
{
- throw new NotImplementedException();
+ var id = Id ?? Guid.NewGuid().ToString("N");
+ var headline = new OdfHeadline(nLevel, id);
+ _root.AddChild(headline);
+ _isModified = true;
+ return headline;
}
public IDocParagraph AddParagraph(string cStylename)
{
- throw new NotImplementedException();
+ var paragraph = new OdfParagraph(cStylename);
+ _root.AddChild(paragraph);
+ _isModified = true;
+ return paragraph;
+ }
+
+ public IDocTOC AddTOC(string cName, int nLevel)
+ {
+ var toc = new OdfTOC(cName, nLevel);
+ _root.AddChild(toc);
+ _isModified = true;
+ return toc;
+ }
+
+ public IEnumerable Enumerate() => _root.Enumerate();
+
+ public bool LoadFrom(string cInputPath)
+ {
+ try
+ {
+ using var fs = File.OpenRead(cInputPath);
+ return LoadFrom(fs);
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public bool LoadFrom(Stream sInputStream, object? options = null)
+ {
+ try
+ {
+ // Stream in temporäre Datei kopieren, da ZipArchive seekable Stream benötigt
+ using var ms = new MemoryStream();
+ sInputStream.CopyTo(ms);
+ ms.Position = 0;
+
+ using var zip = new System.IO.Compression.ZipArchive(ms, System.IO.Compression.ZipArchiveMode.Read);
+ var contentEntry = zip.GetEntry("content.xml");
+ if (contentEntry == null)
+ return false;
+
+ using var contentStream = contentEntry.Open();
+ var contentXml = System.Xml.Linq.XDocument.Load(contentStream,
+ System.Xml.Linq.LoadOptions.PreserveWhitespace | System.Xml.Linq.LoadOptions.SetLineInfo);
+
+ // Root-Nodes löschen und neu befüllen
+ _root.Nodes.Clear();
+ OdfBlockParser.Populate(this, contentXml);
+ _isModified = false;
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public bool SaveTo(string cOutputPath)
+ {
+ try
+ {
+ using var fs = new FileStream(cOutputPath, FileMode.Create, FileAccess.Write);
+ return SaveTo(fs);
+ }
+ catch
+ {
+ return false;
+ }
}
- public void SaveTo(string cOutputPath)
+ public bool SaveTo(Stream sOutputStream, object? options = null)
{
- throw new NotImplementedException();
+ try
+ {
+ using var zip = new System.IO.Compression.ZipArchive(sOutputStream,
+ System.IO.Compression.ZipArchiveMode.Create, leaveOpen: true);
+
+ // mimetype (muss erste Entry sein, unkomprimiert)
+ var mimetypeEntry = zip.CreateEntry("mimetype", System.IO.Compression.CompressionLevel.NoCompression);
+ using (var s = mimetypeEntry.Open())
+ using (var w = new StreamWriter(s, new System.Text.UTF8Encoding(false)))
+ {
+ w.Write("application/vnd.oasis.opendocument.text");
+ }
+
+ // content.xml
+ var contentXml = OdfXmlWriter.CreateContentXml(_root);
+ var contentEntry = zip.CreateEntry("content.xml", System.IO.Compression.CompressionLevel.Optimal);
+ using (var s = contentEntry.Open())
+ {
+ contentXml.Save(s);
+ }
+
+ // styles.xml
+ var stylesXml = OdfXmlWriter.CreateStylesXml();
+ var stylesEntry = zip.CreateEntry("styles.xml", System.IO.Compression.CompressionLevel.Optimal);
+ using (var s = stylesEntry.Open())
+ {
+ stylesXml.Save(s);
+ }
+
+ // meta.xml
+ var metaXml = OdfXmlWriter.CreateMetaXml();
+ var metaEntry = zip.CreateEntry("meta.xml", System.IO.Compression.CompressionLevel.Optimal);
+ using (var s = metaEntry.Open())
+ {
+ metaXml.Save(s);
+ }
+
+ // META-INF/manifest.xml
+ WriteManifest(zip);
+
+ _isModified = false;
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static void WriteManifest(System.IO.Compression.ZipArchive zip)
+ {
+ var manifestEntry = zip.CreateEntry("META-INF/manifest.xml", System.IO.Compression.CompressionLevel.Optimal);
+ using var s = manifestEntry.Open();
+
+ var manifest = new System.Xml.Linq.XDocument(
+ new System.Xml.Linq.XDeclaration("1.0", "UTF-8", null),
+ new System.Xml.Linq.XElement(OdfNamespaces.Manifest + "manifest",
+ new System.Xml.Linq.XAttribute(System.Xml.Linq.XNamespace.Xmlns + "manifest", OdfNamespaces.Manifest),
+ new System.Xml.Linq.XElement(OdfNamespaces.Manifest + "file-entry",
+ new System.Xml.Linq.XAttribute(OdfNamespaces.Manifest + "full-path", "/"),
+ new System.Xml.Linq.XAttribute(OdfNamespaces.Manifest + "media-type", "application/vnd.oasis.opendocument.text")),
+ new System.Xml.Linq.XElement(OdfNamespaces.Manifest + "file-entry",
+ new System.Xml.Linq.XAttribute(OdfNamespaces.Manifest + "full-path", "content.xml"),
+ new System.Xml.Linq.XAttribute(OdfNamespaces.Manifest + "media-type", "text/xml")),
+ new System.Xml.Linq.XElement(OdfNamespaces.Manifest + "file-entry",
+ new System.Xml.Linq.XAttribute(OdfNamespaces.Manifest + "full-path", "styles.xml"),
+ new System.Xml.Linq.XAttribute(OdfNamespaces.Manifest + "media-type", "text/xml")),
+ new System.Xml.Linq.XElement(OdfNamespaces.Manifest + "file-entry",
+ new System.Xml.Linq.XAttribute(OdfNamespaces.Manifest + "full-path", "meta.xml"),
+ new System.Xml.Linq.XAttribute(OdfNamespaces.Manifest + "media-type", "text/xml"))
+ )
+ );
+ manifest.Save(s);
}
}
diff --git a/CSharpBible/Data/DocumentUtils/Document.Odf/OdfDocument.cs b/CSharpBible/Data/DocumentUtils/Document.Odf/OdfDocument.cs
index aca63dce7..435a88edf 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Odf/OdfDocument.cs
+++ b/CSharpBible/Data/DocumentUtils/Document.Odf/OdfDocument.cs
@@ -1,31 +1,59 @@
using System.IO.Compression;
+using System.Text;
using System.Xml.Linq;
+using Document.Odf.Models;
namespace Document.Odf;
public sealed class OdfDocument : IAsyncDisposable, IDisposable
{
- private readonly ZipArchive _zip; private bool _disposed;
- private OdfDocument(ZipArchive zip)
+ private readonly ZipArchive _zip;
+ private readonly bool _isReadOnly;
+ private bool _disposed;
+
+ private OdfDocument(ZipArchive zip, bool isReadOnly)
{
_zip = zip;
+ _isReadOnly = isReadOnly;
}
public static OdfDocument Open(string path)
{
var fs = File.OpenRead(path);
var zip = new ZipArchive(fs, ZipArchiveMode.Read, leaveOpen: false);
- return new OdfDocument(zip);
+ return new OdfDocument(zip, isReadOnly: true);
+ }
+
+ public static OdfDocument OpenForUpdate(string path)
+ {
+ var fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
+ var zip = new ZipArchive(fs, ZipArchiveMode.Update, leaveOpen: false);
+ return new OdfDocument(zip, isReadOnly: false);
+ }
+
+ public static OdfDocument Create(string path)
+ {
+ var fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
+ var zip = new ZipArchive(fs, ZipArchiveMode.Create, leaveOpen: false);
+ var doc = new OdfDocument(zip, isReadOnly: false);
+ doc.WriteMinimalStructure();
+ return doc;
+ }
+
+ public static OdfDocument Create(Stream stream, bool leaveOpen = false)
+ {
+ var zip = new ZipArchive(stream, ZipArchiveMode.Create, leaveOpen);
+ var doc = new OdfDocument(zip, isReadOnly: false);
+ doc.WriteMinimalStructure();
+ return doc;
}
public static async Task OpenAsync(string path, CancellationToken ct = default)
{
- // ZipArchive hat kein echtes Async-Open; Datei async öffnen für IO.
var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 64 * 1024, useAsync: true);
- // trotzdem synchrones Zip-Parsing
var zip = new ZipArchive(fs, ZipArchiveMode.Read, leaveOpen: false);
await Task.CompletedTask;
- return new OdfDocument(zip);
+ return new OdfDocument(zip, isReadOnly: true);
}
public XDocument ReadContentXml()
@@ -43,12 +71,108 @@ public XDocument ReadContentXml()
return XDocument.Load(s, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
}
+ public XDocument? ReadStylesXmlOrNull()
+ {
+ var entry = _zip.GetEntry("styles.xml");
+ if (entry is null) return null;
+ using var s = entry.Open();
+ return XDocument.Load(s, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
+ }
+
public Stream? OpenBinary(string pathInPackage)
{
var entry = _zip.GetEntry(pathInPackage);
return entry?.Open();
}
+ public void WriteContentXml(XDocument contentXml)
+ {
+ if (_isReadOnly) throw new InvalidOperationException("Dokument ist schreibgeschützt");
+
+ var entry = _zip.GetEntry("content.xml");
+ entry?.Delete();
+
+ entry = _zip.CreateEntry("content.xml", CompressionLevel.Optimal);
+ using var s = entry.Open();
+ contentXml.Save(s);
+ }
+
+ public void WriteMetaXml(XDocument metaXml)
+ {
+ if (_isReadOnly) throw new InvalidOperationException("Dokument ist schreibgeschützt");
+
+ var entry = _zip.GetEntry("meta.xml");
+ entry?.Delete();
+
+ entry = _zip.CreateEntry("meta.xml", CompressionLevel.Optimal);
+ using var s = entry.Open();
+ metaXml.Save(s);
+ }
+
+ public void WriteStylesXml(XDocument stylesXml)
+ {
+ if (_isReadOnly) throw new InvalidOperationException("Dokument ist schreibgeschützt");
+
+ var entry = _zip.GetEntry("styles.xml");
+ entry?.Delete();
+
+ entry = _zip.CreateEntry("styles.xml", CompressionLevel.Optimal);
+ using var s = entry.Open();
+ stylesXml.Save(s);
+ }
+
+ public void WriteBinary(string pathInPackage, Stream data)
+ {
+ if (_isReadOnly) throw new InvalidOperationException("Dokument ist schreibgeschützt");
+
+ var entry = _zip.GetEntry(pathInPackage);
+ entry?.Delete();
+
+ entry = _zip.CreateEntry(pathInPackage, CompressionLevel.Optimal);
+ using var s = entry.Open();
+ data.CopyTo(s);
+ }
+
+ private void WriteMinimalStructure()
+ {
+ // mimetype (uncompressed, must be first)
+ var mimetypeEntry = _zip.CreateEntry("mimetype", CompressionLevel.NoCompression);
+ using (var s = mimetypeEntry.Open())
+ using (var w = new StreamWriter(s, new UTF8Encoding(false)))
+ {
+ w.Write("application/vnd.oasis.opendocument.text");
+ }
+
+ // META-INF/manifest.xml
+ WriteManifest();
+ }
+
+ private void WriteManifest()
+ {
+ var manifestEntry = _zip.CreateEntry("META-INF/manifest.xml", CompressionLevel.Optimal);
+ using var s = manifestEntry.Open();
+
+ var manifest = new XDocument(
+ new XDeclaration("1.0", "UTF-8", null),
+ new XElement(OdfNamespaces.Manifest + "manifest",
+ new XAttribute(XNamespace.Xmlns + "manifest", OdfNamespaces.Manifest),
+ new XElement(OdfNamespaces.Manifest + "file-entry",
+ new XAttribute(OdfNamespaces.Manifest + "full-path", "/"),
+ new XAttribute(OdfNamespaces.Manifest + "media-type", "application/vnd.oasis.opendocument.text")),
+ new XElement(OdfNamespaces.Manifest + "file-entry",
+ new XAttribute(OdfNamespaces.Manifest + "full-path", "content.xml"),
+ new XAttribute(OdfNamespaces.Manifest + "media-type", "text/xml")),
+ new XElement(OdfNamespaces.Manifest + "file-entry",
+ new XAttribute(OdfNamespaces.Manifest + "full-path", "styles.xml"),
+ new XAttribute(OdfNamespaces.Manifest + "media-type", "text/xml")),
+ new XElement(OdfNamespaces.Manifest + "file-entry",
+ new XAttribute(OdfNamespaces.Manifest + "full-path", "meta.xml"),
+ new XAttribute(OdfNamespaces.Manifest + "media-type", "text/xml"))
+ )
+ );
+ manifest.Save(s);
+ }
+
public void Dispose()
{
if (_disposed) return;
diff --git a/CSharpBible/Data/DocumentUtils/Document.Odf/OdfTextDocument.cs b/CSharpBible/Data/DocumentUtils/Document.Odf/OdfTextDocument.cs
index 1bac6c6ac..e2f1f6fd1 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Odf/OdfTextDocument.cs
+++ b/CSharpBible/Data/DocumentUtils/Document.Odf/OdfTextDocument.cs
@@ -47,10 +47,53 @@ public IEnumerable Enumerate()
=> Root.Enumerate();
public bool SaveTo(string cOutputPath)
- => throw new NotSupportedException();
+ {
+ try
+ {
+ using var fs = new FileStream(cOutputPath, FileMode.Create, FileAccess.Write);
+ return SaveTo(fs);
+ }
+ catch
+ {
+ return false;
+ }
+ }
public bool SaveTo(Stream sOutputStream, object? options = null)
- => throw new NotSupportedException();
+ {
+ try
+ {
+ using var zip = new System.IO.Compression.ZipArchive(sOutputStream, System.IO.Compression.ZipArchiveMode.Create, leaveOpen: true);
+
+ // mimetype (uncompressed, first entry)
+ var mimeEntry = zip.CreateEntry("mimetype", System.IO.Compression.CompressionLevel.NoCompression);
+ using (var writer = new StreamWriter(mimeEntry.Open()))
+ writer.Write("application/vnd.oasis.opendocument.text");
+
+ // content.xml
+ var contentEntry = zip.CreateEntry("content.xml");
+ using (var stream = contentEntry.Open())
+ {
+ var xdoc = CreateContentXDocument();
+ xdoc.Save(stream);
+ }
+
+ // META-INF/manifest.xml
+ var manifestEntry = zip.CreateEntry("META-INF/manifest.xml");
+ using (var stream = manifestEntry.Open())
+ {
+ var manifest = CreateManifest();
+ manifest.Save(stream);
+ }
+
+ _isModified = false;
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
public bool LoadFrom(string cInputPath)
{
@@ -88,4 +131,76 @@ public bool LoadFrom(Stream sInputStream, object? options = null)
private Models.OdfSection EnsureRoot()
=> Root as Models.OdfSection ?? throw new InvalidOperationException("Root ist nicht OdfSection");
+
+ private static System.Xml.Linq.XDocument CreateManifest()
+ {
+ System.Xml.Linq.XNamespace manifestNs = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0";
+ return new System.Xml.Linq.XDocument(
+ new System.Xml.Linq.XDeclaration("1.0", "UTF-8", null),
+ new System.Xml.Linq.XElement(manifestNs + "manifest",
+ new System.Xml.Linq.XAttribute(System.Xml.Linq.XNamespace.Xmlns + "manifest", manifestNs),
+ new System.Xml.Linq.XElement(manifestNs + "file-entry",
+ new System.Xml.Linq.XAttribute(manifestNs + "full-path", "/"),
+ new System.Xml.Linq.XAttribute(manifestNs + "media-type", "application/vnd.oasis.opendocument.text")),
+ new System.Xml.Linq.XElement(manifestNs + "file-entry",
+ new System.Xml.Linq.XAttribute(manifestNs + "full-path", "content.xml"),
+ new System.Xml.Linq.XAttribute(manifestNs + "media-type", "text/xml"))
+ )
+ );
+ }
+
+ private System.Xml.Linq.XDocument CreateContentXDocument()
+ {
+ // TODO: Anpassung an tatsächliches ODF-Modell der Anwendung
+ var officeNs = System.Xml.Linq.XNamespace.Get("urn:oasis:names:tc:opendocument:xmlns:office:1.0");
+ var textNs = System.Xml.Linq.XNamespace.Get("urn:oasis:names:tc:opendocument:xmlns:text:1.0");
+
+ var rootSection = EnsureRoot();
+
+ var bodyElement = new System.Xml.Linq.XElement(officeNs + "body");
+ var textElement = new System.Xml.Linq.XElement(officeNs + "text");
+ bodyElement.Add(textElement);
+
+ foreach (var element in rootSection.Enumerate())
+ {
+ if (element is IDocHeadline headline)
+ {
+ var h = new System.Xml.Linq.XElement(textNs + "h",
+ new System.Xml.Linq.XAttribute(textNs + "outline-level", headline.Level));
+
+ if (!string.IsNullOrEmpty(headline.Id))
+ h.Add(new System.Xml.Linq.XAttribute("xml:id", headline.Id));
+
+ // headline.Text gibt es nicht, stattdessen TextContent verwenden
+ if (!string.IsNullOrEmpty(headline.TextContent))
+ h.Add(new System.Xml.Linq.XText(headline.TextContent));
+
+ textElement.Add(h);
+ }
+ else if (element is IDocParagraph paragraph)
+ {
+ var p = new System.Xml.Linq.XElement(textNs + "p");
+
+ // StyleName gibt es nicht im Interface, daher entfernt
+ // if (!string.IsNullOrEmpty(paragraph.StyleName))
+ // p.Add(new System.Xml.Linq.XAttribute(textNs + "style-name", paragraph.StyleName));
+
+ if (!string.IsNullOrEmpty(paragraph.TextContent))
+ p.Add(new System.Xml.Linq.XText(paragraph.TextContent));
+
+ textElement.Add(p);
+ }
+ // Weitere Elementtypen hier bei Bedarf abbilden
+ }
+
+ var doc = new System.Xml.Linq.XDocument(
+ new System.Xml.Linq.XDeclaration("1.0", "UTF-8", null),
+ new System.Xml.Linq.XElement(officeNs + "document-content",
+ new System.Xml.Linq.XAttribute(System.Xml.Linq.XNamespace.Xmlns + "office", officeNs),
+ new System.Xml.Linq.XAttribute(System.Xml.Linq.XNamespace.Xmlns + "text", textNs),
+ bodyElement)
+ );
+
+ return doc;
+ }
}
diff --git a/CSharpBible/Data/DocumentUtils/Document.Odf/Writing/OdfXmlWriter.cs b/CSharpBible/Data/DocumentUtils/Document.Odf/Writing/OdfXmlWriter.cs
new file mode 100644
index 000000000..1affcaf8e
--- /dev/null
+++ b/CSharpBible/Data/DocumentUtils/Document.Odf/Writing/OdfXmlWriter.cs
@@ -0,0 +1,345 @@
+using System.Xml.Linq;
+using Document.Base.Models.Interfaces;
+using Document.Odf.Models;
+
+namespace Document.Odf.Writing;
+
+///
+/// Generiert ODF-XML-Dokumente aus dem Dokumentmodell.
+///
+public static class OdfXmlWriter
+{
+ ///
+ /// Erstellt ein vollstndiges content.xml fr ein ODF-Textdokument.
+ ///
+ public static XDocument CreateContentXml(OdfSection root)
+ {
+ var officeText = new XElement(OdfNamespaces.Office + "text");
+
+ foreach (var node in root.Nodes)
+ {
+ var el = ConvertNode(node);
+ if (el != null)
+ officeText.Add(el);
+ }
+
+ var doc = new XDocument(
+ new XDeclaration("1.0", "UTF-8", null),
+ new XElement(OdfNamespaces.Office + "document-content",
+ new XAttribute(XNamespace.Xmlns + "office", OdfNamespaces.Office),
+ new XAttribute(XNamespace.Xmlns + "text", OdfNamespaces.Text),
+ new XAttribute(XNamespace.Xmlns + "style", OdfNamespaces.Style),
+ new XAttribute(XNamespace.Xmlns + "fo", OdfNamespaces.Fo),
+ new XAttribute(XNamespace.Xmlns + "xlink", OdfNamespaces.XLink),
+ new XAttribute(XNamespace.Xmlns + "draw", OdfNamespaces.Draw),
+ new XAttribute(XNamespace.Xmlns + "table", OdfNamespaces.Table),
+ new XAttribute(XNamespace.Xmlns + "svg", OdfNamespaces.Svg),
+ new XAttribute(OdfNamespaces.Office + "version", "1.3"),
+ CreateAutomaticStyles(root),
+ new XElement(OdfNamespaces.Office + "body", officeText)
+ )
+ );
+
+ return doc;
+ }
+
+ ///
+ /// Erstellt ein minimales styles.xml.
+ ///
+ public static XDocument CreateStylesXml()
+ {
+ return new XDocument(
+ new XDeclaration("1.0", "UTF-8", null),
+ new XElement(OdfNamespaces.Office + "document-styles",
+ new XAttribute(XNamespace.Xmlns + "office", OdfNamespaces.Office),
+ new XAttribute(XNamespace.Xmlns + "style", OdfNamespaces.Style),
+ new XAttribute(XNamespace.Xmlns + "fo", OdfNamespaces.Fo),
+ new XAttribute(XNamespace.Xmlns + "text", OdfNamespaces.Text),
+ new XAttribute(OdfNamespaces.Office + "version", "1.3"),
+ new XElement(OdfNamespaces.Office + "styles",
+ CreateDefaultParagraphStyle(),
+ CreateHeadingStyles()
+ )
+ )
+ );
+ }
+
+ ///
+ /// Erstellt ein minimales meta.xml.
+ ///
+ public static XDocument CreateMetaXml(string? title = null, string? creator = null)
+ {
+ var meta = new XElement(OdfNamespaces.Office + "meta");
+
+ if (!string.IsNullOrEmpty(title))
+ meta.Add(new XElement(OdfNamespaces.Dc + "title", title));
+
+ if (!string.IsNullOrEmpty(creator))
+ meta.Add(new XElement(OdfNamespaces.Dc + "creator", creator));
+
+ meta.Add(new XElement(OdfNamespaces.Meta + "creation-date", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")));
+ meta.Add(new XElement(OdfNamespaces.Meta + "generator", "Document.Odf"));
+
+ return new XDocument(
+ new XDeclaration("1.0", "UTF-8", null),
+ new XElement(OdfNamespaces.Office + "document-meta",
+ new XAttribute(XNamespace.Xmlns + "office", OdfNamespaces.Office),
+ new XAttribute(XNamespace.Xmlns + "dc", OdfNamespaces.Dc),
+ new XAttribute(XNamespace.Xmlns + "meta", OdfNamespaces.Meta),
+ new XAttribute(OdfNamespaces.Office + "version", "1.3"),
+ meta
+ )
+ );
+ }
+
+ private static XElement CreateAutomaticStyles(OdfSection root)
+ {
+ var autoStyles = new XElement(OdfNamespaces.Office + "automatic-styles");
+ var usedStyles = new HashSet();
+
+ CollectUsedStyles(root, usedStyles);
+
+ foreach (var styleName in usedStyles)
+ {
+ var style = CreateAutomaticStyle(styleName);
+ if (style != null)
+ autoStyles.Add(style);
+ }
+
+ return autoStyles;
+ }
+
+ private static void CollectUsedStyles(IDOMElement element, HashSet styles)
+ {
+ if (element is OdfSpan span && span.FontStyle != null)
+ {
+ var styleName = GetAutoStyleName(span.FontStyle);
+ if (!string.IsNullOrEmpty(styleName))
+ styles.Add(styleName);
+ }
+
+ foreach (var child in element.Nodes)
+ {
+ if (child is IDOMElement childEl)
+ CollectUsedStyles(childEl, styles);
+ }
+ }
+
+ private static XElement? CreateAutomaticStyle(string styleName)
+ {
+ var textProps = new XElement(OdfNamespaces.Style + "text-properties");
+ bool hasProps = false;
+
+ if (styleName.Contains("Bold"))
+ {
+ textProps.Add(new XAttribute(OdfNamespaces.Fo + "font-weight", "bold"));
+ hasProps = true;
+ }
+ if (styleName.Contains("Italic"))
+ {
+ textProps.Add(new XAttribute(OdfNamespaces.Fo + "font-style", "italic"));
+ hasProps = true;
+ }
+ if (styleName.Contains("Underline"))
+ {
+ textProps.Add(new XAttribute(OdfNamespaces.Style + "text-underline-style", "solid"));
+ textProps.Add(new XAttribute(OdfNamespaces.Style + "text-underline-width", "auto"));
+ hasProps = true;
+ }
+ if (styleName.Contains("Strikeout"))
+ {
+ textProps.Add(new XAttribute(OdfNamespaces.Style + "text-line-through-style", "solid"));
+ hasProps = true;
+ }
+
+ if (!hasProps) return null;
+
+ return new XElement(OdfNamespaces.Style + "style",
+ new XAttribute(OdfNamespaces.Style + "name", styleName),
+ new XAttribute(OdfNamespaces.Style + "family", "text"),
+ textProps
+ );
+ }
+
+ private static string? GetAutoStyleName(IDocFontStyle fontStyle)
+ {
+ var parts = new List();
+ if (fontStyle.Bold) parts.Add("Bold");
+ if (fontStyle.Italic) parts.Add("Italic");
+ if (fontStyle.Underline) parts.Add("Underline");
+ if (fontStyle.Strikeout) parts.Add("Strikeout");
+
+ return parts.Count > 0 ? "T_" + string.Join("_", parts) : null;
+ }
+
+ private static XElement CreateDefaultParagraphStyle()
+ {
+ return new XElement(OdfNamespaces.Style + "default-style",
+ new XAttribute(OdfNamespaces.Style + "family", "paragraph"),
+ new XElement(OdfNamespaces.Style + "paragraph-properties",
+ new XAttribute(OdfNamespaces.Fo + "margin-top", "0cm"),
+ new XAttribute(OdfNamespaces.Fo + "margin-bottom", "0.212cm")
+ ),
+ new XElement(OdfNamespaces.Style + "text-properties",
+ new XAttribute(OdfNamespaces.Style + "font-name", "Liberation Serif"),
+ new XAttribute(OdfNamespaces.Fo + "font-size", "12pt")
+ )
+ );
+ }
+
+ private static IEnumerable CreateHeadingStyles()
+ {
+ for (int level = 1; level <= 6; level++)
+ {
+ var fontSize = level switch
+ {
+ 1 => "24pt",
+ 2 => "18pt",
+ 3 => "14pt",
+ 4 => "12pt",
+ 5 => "10pt",
+ _ => "10pt"
+ };
+
+ yield return new XElement(OdfNamespaces.Style + "style",
+ new XAttribute(OdfNamespaces.Style + "name", $"Heading_{level}"),
+ new XAttribute(OdfNamespaces.Style + "family", "paragraph"),
+ new XAttribute(OdfNamespaces.Style + "parent-style-name", "Heading"),
+ new XAttribute(OdfNamespaces.Style + "default-outline-level", level.ToString()),
+ new XElement(OdfNamespaces.Style + "text-properties",
+ new XAttribute(OdfNamespaces.Fo + "font-size", fontSize),
+ new XAttribute(OdfNamespaces.Fo + "font-weight", "bold")
+ )
+ );
+ }
+ }
+
+ private static XElement? ConvertNode(IDOMElement node)
+ {
+ return node switch
+ {
+ OdfParagraph p => ConvertParagraph(p),
+ OdfHeadline h => ConvertHeadline(h),
+ OdfTOC toc => ConvertTOC(toc),
+ _ => null
+ };
+ }
+
+ private static XElement ConvertParagraph(OdfParagraph p)
+ {
+ var el = new XElement(OdfNamespaces.Text + "p");
+
+ if (!string.IsNullOrEmpty(p.StyleName))
+ el.Add(new XAttribute(OdfNamespaces.Text + "style-name", p.StyleName));
+
+ AddInlineContent(el, p);
+ return el;
+ }
+
+ private static XElement ConvertHeadline(OdfHeadline h)
+ {
+ var el = new XElement(OdfNamespaces.Text + "h",
+ new XAttribute(OdfNamespaces.Text + "outline-level", h.Level.ToString()),
+ new XAttribute(OdfNamespaces.Text + "style-name", $"Heading_{h.Level}")
+ );
+
+ if (!string.IsNullOrEmpty(h.Id))
+ el.Add(new XAttribute(OdfNamespaces.Text + "name", h.Id));
+
+ AddInlineContent(el, h);
+ return el;
+ }
+
+ private static XElement ConvertTOC(OdfTOC toc)
+ {
+ var tocSource = new XElement(OdfNamespaces.Text + "table-of-content-source",
+ new XAttribute(OdfNamespaces.Text + "outline-level", toc.Level.ToString())
+ );
+
+ var tocBody = new XElement(OdfNamespaces.Text + "index-body");
+
+ foreach (var child in toc.Nodes)
+ {
+ if (child is OdfParagraph p)
+ {
+ tocBody.Add(ConvertParagraph(p));
+ }
+ }
+
+ return new XElement(OdfNamespaces.Text + "table-of-content",
+ new XAttribute(OdfNamespaces.Text + "name", toc.Name),
+ tocSource,
+ tocBody
+ );
+ }
+
+ private static void AddInlineContent(XElement parent, OdfContentBase content)
+ {
+ if (!string.IsNullOrEmpty(content.TextContent))
+ parent.Add(new XText(content.TextContent));
+
+ foreach (var child in content.Nodes)
+ {
+ var el = ConvertInlineNode(child);
+ if (el != null)
+ parent.Add(el);
+ }
+ }
+
+ private static object? ConvertInlineNode(IDOMElement node)
+ {
+ return node switch
+ {
+ OdfSpan span => ConvertSpan(span),
+ OdfLineBreak => new XElement(OdfNamespaces.Text + "line-break"),
+ OdfNbSpace => new XElement(OdfNamespaces.Text + "s"),
+ OdfTab => new XElement(OdfNamespaces.Text + "tab"),
+ _ => null
+ };
+ }
+
+ private static XElement ConvertSpan(OdfSpan span)
+ {
+ XElement el;
+
+ if (span.IsLink && !string.IsNullOrEmpty(span.Href))
+ {
+ el = new XElement(OdfNamespaces.Text + "a",
+ new XAttribute(OdfNamespaces.XLink + "href", span.Href),
+ new XAttribute(OdfNamespaces.XLink + "type", "simple")
+ );
+ }
+ else
+ {
+ el = new XElement(OdfNamespaces.Text + "span");
+
+ var styleName = GetAutoStyleName(span.FontStyle);
+ if (!string.IsNullOrEmpty(styleName))
+ el.Add(new XAttribute(OdfNamespaces.Text + "style-name", styleName));
+ }
+
+ if (!string.IsNullOrEmpty(span.Id))
+ {
+ el.AddFirst(new XElement(OdfNamespaces.Text + "bookmark-start",
+ new XAttribute(OdfNamespaces.Text + "name", span.Id)));
+ }
+
+ if (!string.IsNullOrEmpty(span.TextContent))
+ el.Add(new XText(span.TextContent));
+
+ foreach (var child in span.Nodes)
+ {
+ var childEl = ConvertInlineNode(child);
+ if (childEl != null)
+ el.Add(childEl);
+ }
+
+ if (!string.IsNullOrEmpty(span.Id))
+ {
+ el.Add(new XElement(OdfNamespaces.Text + "bookmark-end",
+ new XAttribute(OdfNamespaces.Text + "name", span.Id)));
+ }
+
+ return el;
+ }
+}
diff --git a/CSharpBible/Data/DocumentUtils/Document.Pdf/Document.Pdf.csproj b/CSharpBible/Data/DocumentUtils/Document.Pdf/Document.Pdf.csproj
index 24a99a439..914c04e38 100644
--- a/CSharpBible/Data/DocumentUtils/Document.Pdf/Document.Pdf.csproj
+++ b/CSharpBible/Data/DocumentUtils/Document.Pdf/Document.Pdf.csproj
@@ -5,7 +5,7 @@
enable
-
+
diff --git a/CSharpBible/Data/NebelEbook/NebelEbook.csproj b/CSharpBible/Data/NebelEbook/NebelEbook.csproj
index eb73bdf60..db72500e2 100644
--- a/CSharpBible/Data/NebelEbook/NebelEbook.csproj
+++ b/CSharpBible/Data/NebelEbook/NebelEbook.csproj
@@ -7,12 +7,20 @@
enable
+
+
+
+
+
+
+
+
@@ -20,9 +28,6 @@
-
-
-
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Models/ImageLoader.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Models/ImageLoader.cs
new file mode 100644
index 000000000..8c00ad83e
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Models/ImageLoader.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.IO;
+using PictureDB.Base.Models.Interfaces;
+
+namespace PictureDB.Base.Models;
+
+public class ImageLoader : IImageLoader
+{
+ public IEnumerable LoadImages(string folderPath)
+ {
+ return Directory.GetFiles(folderPath, "*.*", SearchOption.TopDirectoryOnly);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Models/ImageResult.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Models/ImageResult.cs
new file mode 100644
index 000000000..60ce5d4cd
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Models/ImageResult.cs
@@ -0,0 +1,11 @@
+using PictureDB.Base.Models.Interfaces;
+using System;
+
+namespace PictureDB.Base.Models;
+
+public class ImageResult : IImageResult
+{
+ public string FilePath { get; set; }
+ public string Category { get; set; }
+ public int Score { get; set; }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Models/Interfaces/IImageLoader.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Models/Interfaces/IImageLoader.cs
new file mode 100644
index 000000000..8a344af65
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Models/Interfaces/IImageLoader.cs
@@ -0,0 +1,8 @@
+using System.Collections.Generic;
+
+namespace PictureDB.Base.Models.Interfaces;
+
+public interface IImageLoader
+{
+ IEnumerable LoadImages(string folderPath);
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Models/Interfaces/IImageResult.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Models/Interfaces/IImageResult.cs
new file mode 100644
index 000000000..d68436ece
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Models/Interfaces/IImageResult.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace PictureDB.Base.Models.Interfaces;
+
+public interface IImageResult
+{
+ public string FilePath { get; set; }
+ public string Category { get; set; }
+ public int Score { get; set; }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/PictureDB.Base.csproj b/CSharpBible/Data/PictureDB/PictureDB.Base/PictureDB.Base.csproj
new file mode 100644
index 000000000..0ab59ca54
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/PictureDB.Base.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Program.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Program.cs
new file mode 100644
index 000000000..ecf1ca50b
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Program.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using PictureDB.Base.Models;
+using PictureDB.Base.Models.Interfaces;
+using PictureDB.Base.Services;
+using PictureDB.Base.Services.Interfaces;
+
+namespace PictureDB.Base;
+
+// Hauptprogramm
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ string folderPath = args.Length > 0 ? args[0] : @"C:\Images";
+ // allow override via environment variable
+ var model = Environment.GetEnvironmentVariable("OLLAMA_MODEL") ?? "mistral";
+ var timeoutSecondsString = Environment.GetEnvironmentVariable("OLLAMA_TIMEOUT") ?? "60";
+ if (!int.TryParse(timeoutSecondsString, out var timeoutSeconds)) timeoutSeconds = 60;
+
+ using IHost host = Host.CreateDefaultBuilder(args)
+ .ConfigureServices((_, services) =>
+ {
+ // Services
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+ // LLM client (local ollama CLI) - registered with configured model/timeout
+ services.AddSingleton(sp => new LLMClient(model, timeoutSeconds));
+
+ // Result persistence
+ services.AddSingleton();
+
+ // App entrypoint
+ services.AddTransient();
+ })
+ .Build();
+
+ // Resolve and run
+ var app = host.Services.GetRequiredService();
+ await app.RunAsync(folderPath);
+ }
+}
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/App.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/App.cs
new file mode 100644
index 000000000..48f83fbc5
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/App.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using PictureDB.Base.Models;
+using PictureDB.Base.Models.Interfaces;
+using PictureDB.Base.Services.Interfaces;
+
+namespace PictureDB.Base.Services;
+
+public class App
+{
+ private readonly IImageLoader _loader;
+ private readonly IImageProcessor _processor;
+ private readonly ILLMClient _llm;
+ private readonly ICategorizer _categorizer;
+ private readonly IEvaluator _evaluator;
+ private readonly ISorter _sorter;
+ private readonly IResultStore _store;
+
+ public App(
+ IImageLoader loader,
+ IImageProcessor processor,
+ ILLMClient llm,
+ ICategorizer categorizer,
+ IEvaluator evaluator,
+ ISorter sorter,
+ IResultStore store)
+ {
+ _loader = loader;
+ _processor = processor;
+ _llm = llm;
+ _categorizer = categorizer;
+ _evaluator = evaluator;
+ _sorter = sorter;
+ _store = store;
+ }
+
+ public async Task RunAsync(string folderPath)
+ {
+ var results = new List();
+
+ foreach (var file in _loader.LoadImages(folderPath))
+ {
+ string base64 = _processor.ConvertToBase64(file);
+ string response = await _llm.AnalyzeImageAsync(base64, "Kategorisiere und bewerte dieses Bild.");
+
+ var result = new ImageResult
+ {
+ FilePath = file,
+ Category = _categorizer.ExtractCategory(response),
+ Score = _evaluator.ExtractScore(response)
+ };
+ results.Add(result);
+ }
+
+ var sorted = _sorter.SortByScore(results);
+
+ foreach (var res in sorted)
+ {
+ Console.WriteLine($"{res.FilePath} | {res.Category} | Score: {res.Score}");
+ }
+
+ try
+ {
+ var outPath = System.IO.Path.Combine(folderPath, "picturedb-results.json");
+ _store.SaveResults(sorted, outPath);
+ Console.WriteLine($"Results saved to: {outPath}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Failed to save results: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Categorizer.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Categorizer.cs
new file mode 100644
index 000000000..c107ecec8
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Categorizer.cs
@@ -0,0 +1,12 @@
+using PictureDB.Base.Services.Interfaces;
+
+namespace PictureDB.Base.Services;
+
+public class Categorizer : ICategorizer
+{
+ public string ExtractCategory(string llmResponse)
+ {
+ // Einfacher Platzhalter; hier kann spter JSON/XML Parsing ergnzt werden
+ return "Uncategorized";
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Evaluator.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Evaluator.cs
new file mode 100644
index 000000000..7d9bc7327
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Evaluator.cs
@@ -0,0 +1,12 @@
+using PictureDB.Base.Services.Interfaces;
+
+namespace PictureDB.Base.Services;
+
+public class Evaluator : IEvaluator
+{
+ public int ExtractScore(string llmResponse)
+ {
+ // Platzhalter-Implementierung
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/ImageProcessor.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/ImageProcessor.cs
new file mode 100644
index 000000000..990354245
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/ImageProcessor.cs
@@ -0,0 +1,46 @@
+using System;
+using System.IO;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.Processing;
+using PictureDB.Base.Services.Interfaces;
+
+namespace PictureDB.Base.Services;
+
+public class ImageProcessor : IImageProcessor
+{
+ private const int MaxDimension = 512; // max width or height in pixels
+ private const int JpegQuality = 85;
+
+ public string ConvertToBase64(string filePath)
+ {
+ if (string.IsNullOrWhiteSpace(filePath)) throw new ArgumentNullException(nameof(filePath));
+ if (!File.Exists(filePath)) throw new FileNotFoundException("Image file not found", filePath);
+
+ using var image = Image.Load(filePath);
+
+ int width = image.Width;
+ int height = image.Height;
+
+ // Determine scale factor to fit into MaxDimension
+ double scale = 1.0;
+ if (width > MaxDimension || height > MaxDimension)
+ {
+ scale = Math.Min((double)MaxDimension / width, (double)MaxDimension / height);
+ }
+
+ if (scale < 1.0)
+ {
+ int newWidth = Math.Max(1, (int)Math.Round(width * scale));
+ int newHeight = Math.Max(1, (int)Math.Round(height * scale));
+ image.Mutate(x => x.Resize(newWidth, newHeight));
+ }
+
+ using var ms = new MemoryStream();
+ var encoder = new JpegEncoder { Quality = JpegQuality };
+ image.SaveAsJpeg(ms, encoder);
+ ms.Seek(0, SeekOrigin.Begin);
+ var bytes = ms.ToArray();
+ return Convert.ToBase64String(bytes);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ICategorizer.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ICategorizer.cs
new file mode 100644
index 000000000..e3f9f0855
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ICategorizer.cs
@@ -0,0 +1,6 @@
+namespace PictureDB.Base.Services.Interfaces;
+
+public interface ICategorizer
+{
+ string ExtractCategory(string llmResponse);
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IEvaluator.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IEvaluator.cs
new file mode 100644
index 000000000..448b73618
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IEvaluator.cs
@@ -0,0 +1,6 @@
+namespace PictureDB.Base.Services.Interfaces;
+
+public interface IEvaluator
+{
+ int ExtractScore(string llmResponse);
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IImageProcessor.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IImageProcessor.cs
new file mode 100644
index 000000000..dd894e3f4
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IImageProcessor.cs
@@ -0,0 +1,6 @@
+namespace PictureDB.Base.Services.Interfaces;
+
+public interface IImageProcessor
+{
+ string ConvertToBase64(string filePath);
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ILLMClient.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ILLMClient.cs
new file mode 100644
index 000000000..98e23d5a8
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ILLMClient.cs
@@ -0,0 +1,8 @@
+using System.Threading.Tasks;
+
+namespace PictureDB.Base.Services.Interfaces;
+
+public interface ILLMClient
+{
+ Task AnalyzeImageAsync(string base64Image, string prompt);
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IResultStore.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IResultStore.cs
new file mode 100644
index 000000000..fe1b6d35e
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/IResultStore.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+using PictureDB.Base.Models;
+
+namespace PictureDB.Base.Services.Interfaces;
+
+public interface IResultStore
+{
+ void SaveResults(IEnumerable results, string path);
+}
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ISorter.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ISorter.cs
new file mode 100644
index 000000000..9618f59e7
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Interfaces/ISorter.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+using PictureDB.Base.Models;
+
+namespace PictureDB.Base.Services.Interfaces;
+
+public interface ISorter
+{
+ IEnumerable SortByScore(IEnumerable results);
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/JsonResultStore.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/JsonResultStore.cs
new file mode 100644
index 000000000..c73fccbe6
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/JsonResultStore.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Text.Json;
+using PictureDB.Base.Models;
+using PictureDB.Base.Services.Interfaces;
+
+namespace PictureDB.Base.Services;
+
+public class JsonResultStore : IResultStore
+{
+ public void SaveResults(IEnumerable results, string path)
+ {
+ var options = new JsonSerializerOptions { WriteIndented = true };
+ var json = JsonSerializer.Serialize(results, options);
+ File.WriteAllText(path, json);
+ }
+}
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/LLMClient.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/LLMClient.cs
new file mode 100644
index 000000000..1952822d6
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/LLMClient.cs
@@ -0,0 +1,76 @@
+using System.Diagnostics;
+using System.Text;
+using PictureDB.Base.Services.Interfaces;
+
+namespace PictureDB.Base.Services;
+
+public class LLMClient : ILLMClient
+{
+ private readonly string _model;
+ private readonly TimeSpan _timeout;
+
+ public LLMClient(string model = "gemma3:1b", int timeoutSeconds = 400)
+ {
+ _model = model;
+ _timeout = TimeSpan.FromSeconds(timeoutSeconds);
+ }
+
+ public async Task AnalyzeImageAsync(string base64Image, string prompt)
+ {
+ // Construct prompt that includes the full base64 data as requested
+ var sbPrompt = new StringBuilder();
+ sbPrompt.AppendLine(prompt);
+ sbPrompt.AppendLine();
+ sbPrompt.AppendLine(base64Image ?? string.Empty);
+
+ // Escape double quotes for command-line quoting
+ var finalPrompt = sbPrompt.ToString().Replace("\"", "\\\"");
+
+ var arguments = $"run {_model} \"{finalPrompt}\"";
+
+ try
+ {
+ var psi = new ProcessStartInfo
+ {
+ FileName = "ollama",
+ Arguments = arguments,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ StandardOutputEncoding = Encoding.UTF8,
+ StandardErrorEncoding = Encoding.UTF8,
+ };
+
+ using var proc = Process.Start(psi) ?? throw new InvalidOperationException("Could not start ollama process");
+
+ var outputTask = proc.StandardOutput.ReadToEndAsync();
+ var errorTask = proc.StandardError.ReadToEndAsync();
+
+ var waitTask = proc.WaitForExitAsync();
+ if (await Task.WhenAny(waitTask, Task.Delay(_timeout)) != waitTask)
+ {
+ try { proc.Kill(); } catch { }
+ throw new TimeoutException($"Timeout when running 'ollama {arguments}'");
+ }
+
+ var output = await outputTask;
+ var error = await errorTask;
+
+ if (!string.IsNullOrEmpty(error))
+ {
+ var le = error.ToLowerInvariant();
+ if (le.Contains("unknown command") || le.Contains("unknown flag") || le.Contains("unknown option") || le.Contains("flag provided but not defined"))
+ {
+ throw new InvalidOperationException($"ollama reported unsupported invocation: {error}");
+ }
+ }
+
+ return string.IsNullOrEmpty(output) ? error ?? string.Empty : output;
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException($"Failed to invoke ollama: {ex.Message}", ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Sorter.cs b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Sorter.cs
new file mode 100644
index 000000000..7a36fcb0f
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.Base/Services/Sorter.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using System.Linq;
+using PictureDB.Base.Models;
+using PictureDB.Base.Services.Interfaces;
+
+namespace PictureDB.Base.Services;
+
+public class Sorter : ISorter
+{
+ public IEnumerable SortByScore(IEnumerable results)
+ {
+ return results.OrderByDescending(r => r.Score);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/PictureDB.OllamaTest.csproj b/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/PictureDB.OllamaTest.csproj
new file mode 100644
index 000000000..0fd3f903d
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/PictureDB.OllamaTest.csproj
@@ -0,0 +1,9 @@
+
+
+ Exe
+ net8.0
+ 12.0
+ enable
+ enable
+
+
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/Program.cs b/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/Program.cs
new file mode 100644
index 000000000..8ee3e8e6c
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/Program.cs
@@ -0,0 +1,106 @@
+using System.Diagnostics;
+using System.Text;
+
+Console.WriteLine("PictureDB Ollama Test - simple CLI wrapper\n");
+
+var model = args.Length > 0 ? args[0] : "mistral"; // beispiel
+var prompt = args.Length > 1 ? string.Join(' ', args.Skip(1)) : "Guten Morgen";
+
+// Candidate commands and argument patterns because ollama CLI versions differ
+string[] candidateCommands = new[] { "run", "predict", "query" };
+string[] argPatterns = new[]
+{
+ // common: positional prompt
+ "{0} {1} \"{2}\"",
+ // long flag
+ "{0} {1} --prompt \"{2}\"",
+ // short flag
+ "{0} {1} p \"{2}\"",
+ // some versions accept --text or --question
+ "{0} {1} --text \"{2}\"",
+};
+
+string? finalOutput = null;
+string? finalError = null;
+string? usedInvocation = null;
+
+foreach (var cmd in candidateCommands)
+{
+ foreach (var pattern in argPatterns)
+ {
+ var arguments = string.Format(pattern, cmd, model, prompt);
+ try
+ {
+ Console.WriteLine($"Versuche: ollama {arguments}\n");
+
+ var psi = new ProcessStartInfo
+ {
+ FileName = "ollama",
+ Arguments = arguments,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ StandardOutputEncoding = Encoding.UTF8,
+ StandardErrorEncoding = Encoding.UTF8,
+ };
+
+ using var proc = Process.Start(psi) ?? throw new InvalidOperationException("Could not start ollama process");
+
+ var outputTask = proc.StandardOutput.ReadToEndAsync();
+ var errorTask = proc.StandardError.ReadToEndAsync();
+
+ var timeout = TimeSpan.FromSeconds(30);
+ var waitTask = proc.WaitForExitAsync();
+ if (await Task.WhenAny(waitTask, Task.Delay(timeout)) != waitTask)
+ {
+ try { proc.Kill(); } catch { }
+ Console.WriteLine($"Timeout when running 'ollama {arguments}'.\n");
+ continue;
+ }
+
+ string output = await outputTask;
+ string error = await errorTask;
+
+ // If CLI reports unsupported command/flag, try next pattern
+ if (!string.IsNullOrEmpty(error))
+ {
+ var le = error.ToLowerInvariant();
+ if (le.Contains("unknown command") || le.Contains("unknown flag") || le.Contains("flag provided but not defined") || le.Contains("unknown option"))
+ {
+ Console.WriteLine($"Invocation 'ollama {arguments}' not supported by this version. Trying next...\n");
+ continue;
+ }
+ }
+
+ // Accept either output or non-fatal error
+ finalOutput = output;
+ finalError = error;
+ usedInvocation = arguments;
+ break;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Fehler beim Versuch mit 'ollama {arguments}': {ex.Message}\n");
+ // try next pattern
+ }
+ }
+
+ if (finalOutput != null || finalError != null) break;
+}
+
+if (finalOutput == null && finalError == null)
+{
+ Console.WriteLine("Alle Versuche fehlgeschlagen. Bitte prfe, ob 'ollama' installiert und in PATH ist, und welche Subcommands bzw. Optionen verfgbar sind (z.B. 'ollama --help').");
+ return;
+}
+
+Console.WriteLine($"--- Used invocation: ollama {usedInvocation} ---\n");
+Console.WriteLine("--- Output ---");
+if (!string.IsNullOrEmpty(finalOutput)) Console.WriteLine(finalOutput);
+
+if (!string.IsNullOrEmpty(finalError))
+{
+ Console.WriteLine("--- Error ---");
+ Console.WriteLine(finalError);
+}
diff --git a/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/README.md b/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/README.md
new file mode 100644
index 000000000..a3f35a217
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.OllamaTest/README.md
@@ -0,0 +1,15 @@
+# PictureDB Ollama Test
+
+Kleines Testprojekt, das die `ollama`-CLI aufruft, um das lokale Modell mit einem Prompt zu befragen.
+
+Verwendung:
+
+```
+dotnet run --project PictureDB.OllamaTest --
+
+Beispiel:
+
+dotnet run --project PictureDB.OllamaTest -- llava-alpha "Beschreibe dieses Bild kurz"
+```
+
+Hinweis: `ollama` muss installiert und in PATH verfgbar sein.
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.UI/App.xaml b/CSharpBible/Data/PictureDB/PictureDB.UI/App.xaml
new file mode 100644
index 000000000..3159bcefe
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.UI/App.xaml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.UI/App.xaml.cs b/CSharpBible/Data/PictureDB/PictureDB.UI/App.xaml.cs
new file mode 100644
index 000000000..df0adefc6
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.UI/App.xaml.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Windows;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using PictureDB.Base.Services;
+using PictureDB.Base.Services.Interfaces;
+using PictureDB.Base.Models.Interfaces;
+using PictureDB.Base.Models;
+using PictureDB.UI.ViewModels;
+
+namespace PictureDB.UI;
+
+public partial class App : Application
+{
+ private IHost? _host;
+
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ base.OnStartup(e);
+
+ _host = Host.CreateDefaultBuilder()
+ .ConfigureServices((context, services) =>
+ {
+ // register base services from PictureDB.Base
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+ // LLM client using default constructor (model via env var)
+ services.AddSingleton(sp => new LLMClient());
+
+ services.AddSingleton();
+
+ services.AddSingleton();
+ services.AddSingleton();
+ })
+ .Build();
+
+ var main = _host.Services.GetRequiredService();
+ main.Show();
+ }
+
+ protected override async void OnExit(ExitEventArgs e)
+ {
+ if (_host != null) await _host.StopAsync();
+ _host?.Dispose();
+ base.OnExit(e);
+ }
+}
diff --git a/CSharpBible/Data/PictureDB/PictureDB.UI/MainWindow.xaml b/CSharpBible/Data/PictureDB/PictureDB.UI/MainWindow.xaml
new file mode 100644
index 000000000..6b90ad66d
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.UI/MainWindow.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.UI/MainWindow.xaml.cs b/CSharpBible/Data/PictureDB/PictureDB.UI/MainWindow.xaml.cs
new file mode 100644
index 000000000..324570922
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.UI/MainWindow.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows;
+using PictureDB.UI.ViewModels;
+
+namespace PictureDB.UI;
+
+public partial class MainWindow : Window
+{
+ public MainWindow(MainViewModel vm)
+ {
+ InitializeComponent();
+ DataContext = vm;
+ vm.onShowDialog = dlg => dlg.ShowDialog(this.Parent as Window);
+ }
+}
diff --git a/CSharpBible/Data/PictureDB/PictureDB.UI/PictureDB.UI.csproj b/CSharpBible/Data/PictureDB/PictureDB.UI/PictureDB.UI.csproj
new file mode 100644
index 000000000..3b7ee9f5a
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.UI/PictureDB.UI.csproj
@@ -0,0 +1,21 @@
+
+
+ WinExe
+ net8.0-windows
+ enable
+ true
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/Data/PictureDB/PictureDB.UI/ViewModels/MainViewModel.cs b/CSharpBible/Data/PictureDB/PictureDB.UI/ViewModels/MainViewModel.cs
new file mode 100644
index 000000000..bab3ead96
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/PictureDB.UI/ViewModels/MainViewModel.cs
@@ -0,0 +1,111 @@
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using PictureDB.Base.Models;
+using PictureDB.Base.Models.Interfaces;
+using PictureDB.Base.Services.Interfaces;
+using CommonDialogs;
+using System.Windows.Threading;
+using System;
+using CommonDialogs.Interfaces;
+
+namespace PictureDB.UI.ViewModels;
+
+public partial class MainViewModel : ObservableObject
+{
+ private readonly IImageLoader _loader;
+ private readonly IImageProcessor _processor;
+ private readonly ILLMClient _llm;
+ private readonly ICategorizer _categorizer;
+ private readonly IEvaluator _evaluator;
+ private readonly ISorter _sorter;
+ private readonly IResultStore _store;
+
+ public ObservableCollection Results { get; } = new();
+
+ [ObservableProperty]
+ private string _folderPath = string.Empty;
+
+ [ObservableProperty]
+ private string _status = string.Empty;
+
+ public Func? onShowDialog;
+
+ public MainViewModel(IImageLoader loader, IImageProcessor processor, ILLMClient llm, ICategorizer categorizer, IEvaluator evaluator, ISorter sorter, IResultStore store)
+ {
+ _loader = loader;
+ _processor = processor;
+ _llm = llm;
+ _categorizer = categorizer;
+ _evaluator = evaluator;
+ _sorter = sorter;
+ _store = store;
+ }
+
+ // Pseudocode:
+ // - Create CommonDialogs FolderBrowserDialog instance
+ // - Configure description, root folder, and optionally preselect current FolderPath
+ // - Show dialog (returns bool?)
+ // - If user accepted (true), update FolderPath with SelectedPath
+ // - Otherwise, do nothing
+
+ [RelayCommand]
+ private void Browse()
+ {
+ var dlg = new FolderBrowserDialog
+ {
+ Description = "Ordner mit Bildern whlen",
+ RootFolder = Environment.SpecialFolder.MyComputer,
+ ShowNewFolderButton = true,
+ SelectedPath = string.IsNullOrWhiteSpace(FolderPath) ? string.Empty : FolderPath
+ };
+
+ if (onShowDialog?.Invoke(dlg) == true)
+ {
+ FolderPath = dlg.SelectedPath;
+ }
+ }
+
+ [RelayCommand]
+ private async Task AnalyzeAsync(string folder)
+ {
+ if (string.IsNullOrWhiteSpace(folder))
+ {
+ Status = "Please select a folder.";
+ return;
+ }
+
+ Status = "Processing...";
+ Results.Clear();
+
+ await Task.Run(async () =>
+ {
+ foreach (var f in _loader.LoadImages(folder))
+ {
+ try
+ {
+ var b64 = _processor.ConvertToBase64(f);
+ var resp = await _llm.AnalyzeImageAsync(b64, "Kategorisiere und bewerte dieses Bild.");
+ var r = new ImageResult { FilePath = f, Category = _categorizer.ExtractCategory(resp), Score = _evaluator.ExtractScore(resp) };
+ App.Current.Dispatcher.Invoke(() => Results.Add(r));
+ }
+ catch (Exception ex)
+ {
+ App.Current.Dispatcher.Invoke(() => Status = "Error: " + ex.Message);
+ }
+ }
+
+ try
+ {
+ var outPath = System.IO.Path.Combine(folder, "picturedb-results.json");
+ _store.SaveResults(Results, outPath);
+ App.Current.Dispatcher.Invoke(() => Status = "Done. Results saved to: " + outPath);
+ }
+ catch (Exception ex)
+ {
+ App.Current.Dispatcher.Invoke(() => Status = "Save failed: " + ex.Message);
+ }
+ });
+ }
+}
diff --git a/CSharpBible/Data/PictureDB/ReadMe.md b/CSharpBible/Data/PictureDB/ReadMe.md
new file mode 100644
index 000000000..b0da4855d
--- /dev/null
+++ b/CSharpBible/Data/PictureDB/ReadMe.md
@@ -0,0 +1,86 @@
+# 🖼️ Architekturplan: C#-Programm mit lokalem LLM zur Bildkategorisierung, Bewertung und Sortierung
+
+## 1. Grundlegende Programmlogik (C#)
+- **Projektstruktur**: Konsolenanwendung oder WPF/WinForms für GUI
+- **Module**:
+ - `ImageLoader`: lädt Bilder aus lokalen Ordnern
+ - `ImageProcessor`: bereitet Bilder für das LLM vor (z. B. Konvertierung in Base64 oder Feature-Extraktion)
+ - `LLMInterface`: Schnittstelle zum lokalen LLM
+ - `Categorizer`: ruft das LLM auf, um Kategorien zu bestimmen
+ - `Evaluator`: ruft das LLM auf, um Bewertungen (Scores) zu erzeugen
+ - `Sorter`: sortiert Bilder nach Bewertung oder Kategorie
+ - `UI/Output`: zeigt Ergebnisse an oder speichert sie in einer Datenbank/Datei
+
+---
+
+## 2. Lokales LLM
+- **Optionen**:
+ - Ollama (lokale LLMs wie LLaVA, Mistral, etc.)
+ - GPT4All oder LM Studio
+ - Hugging Face Modelle (z. B. BLIP, CLIP für Bildbeschreibung/Kategorisierung)
+- **Anforderungen**:
+ - Modell mit Multimodalität (Text + Bild), z. B. CLIP oder LLaVA
+ - Lokale API oder Bibliothek, die von C# aus angesprochen werden kann (REST, gRPC oder Prozessaufruf)
+
+---
+
+## 3. Bildverarbeitung
+- **Bibliotheken**:
+ - `System.Drawing` oder `ImageSharp` für Bildmanipulation
+ - Konvertierung in ein Format, das das LLM versteht (z. B. Base64‑String oder Pfadübergabe)
+- **Vorverarbeitung**:
+ - Skalierung, Normalisierung
+ - Metadaten extrahieren (Dateiname, Größe, Format)
+
+---
+
+## 4. Kommunikation mit dem LLM
+- **API Layer**:
+ - REST‑Client (`HttpClient`) oder gRPC
+ - Eingabe: Bilddaten + Prompt (z. B. „Kategorisiere dieses Bild nach Thema“)
+ - Ausgabe: Textantwort (Kategorie, Score, Ranking)
+- **Prompt Engineering**:
+ - Beispiel:
+ ```text
+ Analysiere das Bild und gib eine Kategorie (z. B. Natur, Architektur, Person)
+ sowie eine Bewertung von 1-10 für Qualität.
+ ```
+
+---
+
+## 5. Bewertung & Sortierung
+- **Evaluator**:
+ - Nimmt Score vom LLM entgegen
+ - Speichert Ergebnisse in einer Datenstruktur (z. B. `List`)
+- **Sorter**:
+ - Sortiert nach Kategorie oder Score
+ - Optional: Filterfunktionen (z. B. nur Bilder > Score 7)
+
+---
+
+## 6. Persistenz & Ausgabe
+- **Optionen**:
+ - Speicherung in JSON/CSV
+ - Datenbank (SQLite, LiteDB)
+ - GUI‑Darstellung (WPF/WinForms)
+- **Funktionen**:
+ - Export der Ergebnisse
+ - Anzeige sortierter Bilder
+
+---
+
+## 7. Erweiterungen
+- Batch‑Verarbeitung ganzer Ordner
+- Training eigener Kategorien
+- Kombination mit klassischen ML‑Bibliotheken (z. B. TensorFlow.NET, ML.NET)
+- Parallelisierung für Performance
+
+---
+
+## 📋 Zusammenfassung der benötigten Komponenten
+- **C# Projektstruktur** (Loader, Processor, Categorizer, Evaluator, Sorter, UI)
+- **Lokales LLM** (z. B. Ollama, GPT4All, Hugging Face Modell)
+- **Bildverarbeitungsbibliothek** (ImageSharp, System.Drawing)
+- **Kommunikationslayer** (REST/gRPC Client)
+- **Persistenz** (JSON/CSV oder Datenbank)
+- **UI/Output** (Konsole, GUI oder Weboberfläche)
diff --git a/CSharpBible/Data/RepoMigrator/RepoMigrator.App.Wpf/RepoMigrator.App.Wpf.csproj b/CSharpBible/Data/RepoMigrator/RepoMigrator.App.Wpf/RepoMigrator.App.Wpf.csproj
index 0d4e468dc..9b6632180 100644
--- a/CSharpBible/Data/RepoMigrator/RepoMigrator.App.Wpf/RepoMigrator.App.Wpf.csproj
+++ b/CSharpBible/Data/RepoMigrator/RepoMigrator.App.Wpf/RepoMigrator.App.Wpf.csproj
@@ -14,6 +14,6 @@
-
+
diff --git a/CSharpBible/Data/RepoMigrator/RepoMigrator.Core/RepoMigrator.Core.csproj b/CSharpBible/Data/RepoMigrator/RepoMigrator.Core/RepoMigrator.Core.csproj
index f6ec404bb..8f7f30621 100644
--- a/CSharpBible/Data/RepoMigrator/RepoMigrator.Core/RepoMigrator.Core.csproj
+++ b/CSharpBible/Data/RepoMigrator/RepoMigrator.Core/RepoMigrator.Core.csproj
@@ -6,6 +6,6 @@
enable
-
+
diff --git a/CSharpBible/Data/RepoMigrator/RepoMigrator.Tests/RepoMigrator.Tests.csproj b/CSharpBible/Data/RepoMigrator/RepoMigrator.Tests/RepoMigrator.Tests.csproj
index ff1f28333..b537b0d1b 100644
--- a/CSharpBible/Data/RepoMigrator/RepoMigrator.Tests/RepoMigrator.Tests.csproj
+++ b/CSharpBible/Data/RepoMigrator/RepoMigrator.Tests/RepoMigrator.Tests.csproj
@@ -7,8 +7,8 @@
enable
-
-
+
+
diff --git a/CSharpBible/Data/TraceCsv2realCsvTests/TraceCsv2realCsvTests.csproj b/CSharpBible/Data/TraceCsv2realCsvTests/TraceCsv2realCsvTests.csproj
index 9a1d78bfe..8fe426c44 100644
--- a/CSharpBible/Data/TraceCsv2realCsvTests/TraceCsv2realCsvTests.csproj
+++ b/CSharpBible/Data/TraceCsv2realCsvTests/TraceCsv2realCsvTests.csproj
@@ -7,8 +7,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/DependencyInjection/CustomerRepositoryTests/CustomerRepositoryTests.csproj b/CSharpBible/DependencyInjection/CustomerRepositoryTests/CustomerRepositoryTests.csproj
index 660a117c0..edaba1c69 100644
--- a/CSharpBible/DependencyInjection/CustomerRepositoryTests/CustomerRepositoryTests.csproj
+++ b/CSharpBible/DependencyInjection/CustomerRepositoryTests/CustomerRepositoryTests.csproj
@@ -11,11 +11,11 @@
-
+
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -24,7 +24,7 @@
-
+
diff --git a/CSharpBible/Games/Arkanoid.Base/Arkanoid.Base.csproj b/CSharpBible/Games/Arkanoid.Base/Arkanoid.Base.csproj
new file mode 100644
index 000000000..c60e65135
--- /dev/null
+++ b/CSharpBible/Games/Arkanoid.Base/Arkanoid.Base.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Arkanoid.Base/GameEngine.cs b/CSharpBible/Games/Arkanoid.Base/GameEngine.cs
new file mode 100644
index 000000000..4641a7931
--- /dev/null
+++ b/CSharpBible/Games/Arkanoid.Base/GameEngine.cs
@@ -0,0 +1,116 @@
+using Arkanoid.Base.Models;
+
+namespace Arkanoid.Base;
+
+public class GameEngine
+{
+ public GameState State { get; } = new();
+
+ public GameEngine()
+ {
+ CreateDefaultLevel();
+ }
+
+ public void MovePaddle(float direction, float deltaTime)
+ {
+ var dx = State.Paddle.Speed * direction * deltaTime;
+ var newX = Math.Clamp(State.Paddle.Position.X + dx, 0, State.FieldWidth - State.Paddle.Width);
+ State.Paddle.Position = new Vector2(newX, State.Paddle.Position.Y);
+ }
+
+ public void Update(float deltaTime)
+ {
+ if (State.IsGameOver) return;
+
+ State.Ball.Position = new Vector2(
+ State.Ball.Position.X + State.Ball.Velocity.X * deltaTime,
+ State.Ball.Position.Y + State.Ball.Velocity.Y * deltaTime);
+
+ HandleCollisions();
+ }
+
+ private void HandleCollisions()
+ {
+ var b = State.Ball;
+
+ // walls
+ if (b.Position.X - b.Radius < 0 || b.Position.X + b.Radius > State.FieldWidth)
+ State.Ball.Velocity = new Vector2(-b.Velocity.X, b.Velocity.Y);
+ if (b.Position.Y - b.Radius < 0)
+ State.Ball.Velocity = new Vector2(b.Velocity.X, -b.Velocity.Y);
+ if (b.Position.Y - b.Radius > State.FieldHeight)
+ {
+ State.Lives--;
+ if (State.Lives <= 0) State.IsGameOver = true;
+ ResetBallAndPaddle();
+ return;
+ }
+
+ // paddle (simple AABB)
+ var p = State.Paddle;
+ if (b.Position.Y + b.Radius >= p.Position.Y &&
+ b.Position.Y - b.Radius <= p.Position.Y + 1 &&
+ b.Position.X >= p.Position.X &&
+ b.Position.X <= p.Position.X + p.Width &&
+ b.Velocity.Y > 0)
+ {
+ State.Ball.Velocity = new Vector2(b.Velocity.X, -Math.Abs(b.Velocity.Y));
+ }
+
+ // bricks
+ foreach (var brick in State.Bricks.ToList())
+ {
+ if (brick.IsDestroyed) continue;
+
+ if (b.Position.X + b.Radius < brick.Position.X ||
+ b.Position.X - b.Radius > brick.Position.X + brick.Width ||
+ b.Position.Y + b.Radius < brick.Position.Y ||
+ b.Position.Y - b.Radius > brick.Position.Y + brick.Height)
+ continue;
+
+ State.Ball.Velocity = new Vector2(b.Velocity.X, -b.Velocity.Y);
+
+ if (brick.Type != BrickType.Unbreakable)
+ {
+ brick.HitPoints--;
+ if (brick.IsDestroyed)
+ {
+ State.Score += 10;
+ }
+ }
+ }
+
+ if (State.Bricks.All(br => br.IsDestroyed))
+ State.IsGameOver = true;
+ }
+
+ private void ResetBallAndPaddle()
+ {
+ State.Paddle.Position = new Vector2(State.FieldWidth / 2 - State.Paddle.Width / 2, State.FieldHeight - 2);
+ State.Ball.Position = new Vector2(State.FieldWidth / 2, State.FieldHeight / 2);
+ State.Ball.Velocity = new Vector2(15, -20);
+ }
+
+ private void CreateDefaultLevel()
+ {
+ State.Bricks.Clear();
+ var rows = 5;
+ var cols = 10;
+ var brickWidth = State.FieldWidth / cols;
+ for (var y = 0; y < rows; y++)
+ {
+ for (var x = 0; x < cols; x++)
+ {
+ State.Bricks.Add(new Brick
+ {
+ Position = new Vector2(x * brickWidth, 2 + y),
+ Width = brickWidth - 0.5f,
+ Height = 1,
+ Type = BrickType.Normal,
+ HitPoints = 1
+ });
+ }
+ }
+ ResetBallAndPaddle();
+ }
+}
diff --git a/CSharpBible/Games/Arkanoid.Base/Models/GameObjects.cs b/CSharpBible/Games/Arkanoid.Base/Models/GameObjects.cs
new file mode 100644
index 000000000..bf3b014bf
--- /dev/null
+++ b/CSharpBible/Games/Arkanoid.Base/Models/GameObjects.cs
@@ -0,0 +1,46 @@
+namespace Arkanoid.Base.Models;
+
+public enum BrickType
+{
+ Normal,
+ Strong,
+ Unbreakable
+}
+
+public record Vector2(float X, float Y);
+
+public class Paddle
+{
+ public Vector2 Position { get; set; } = new(10, 20);
+ public float Width { get; set; } = 6f;
+ public float Speed { get; set; } = 25f; // units per second
+}
+
+public class Ball
+{
+ public Vector2 Position { get; set; } = new(10, 10);
+ public Vector2 Velocity { get; set; } = new(10, -10); // units per second
+ public float Radius { get; set; } = 0.5f;
+}
+
+public class Brick
+{
+ public Vector2 Position { get; set; }
+ public float Width { get; set; } = 3f;
+ public float Height { get; set; } = 1f;
+ public BrickType Type { get; set; }
+ public int HitPoints { get; set; } = 1;
+ public bool IsDestroyed => HitPoints <= 0 && Type != BrickType.Unbreakable;
+}
+
+public class GameState
+{
+ public Paddle Paddle { get; } = new();
+ public Ball Ball { get; } = new();
+ public List Bricks { get; } = new();
+ public int Score { get; set; }
+ public int Lives { get; set; } = 3;
+ public float FieldWidth { get; set; } = 80;
+ public float FieldHeight { get; set; } = 40;
+ public bool IsGameOver { get; set; }
+}
diff --git a/CSharpBible/Games/Arkanoid.Cons/Arkanoid.Cons.csproj b/CSharpBible/Games/Arkanoid.Cons/Arkanoid.Cons.csproj
new file mode 100644
index 000000000..2709085c9
--- /dev/null
+++ b/CSharpBible/Games/Arkanoid.Cons/Arkanoid.Cons.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Arkanoid.Cons/Program.cs b/CSharpBible/Games/Arkanoid.Cons/Program.cs
new file mode 100644
index 000000000..2f67576ab
--- /dev/null
+++ b/CSharpBible/Games/Arkanoid.Cons/Program.cs
@@ -0,0 +1,149 @@
+using Arkanoid.Base;
+using ConsoleDisplay.View;
+using ConsoleLib;
+using ConsoleLib.Interfaces;
+
+namespace Arkanoid.Cons
+{
+ internal class Program
+ {
+ static void Main(string[] args)
+ {
+ Console.CursorVisible = false;
+ var engine = new GameEngine();
+
+ var displayWidth = (int)engine.State.FieldWidth;
+ var displayHeight = (int)engine.State.FieldHeight;
+ var display = new Display(0, 0, displayWidth, displayHeight);
+
+ // ExtendedConsole initialisieren, um echte Key-/Maus-Events zu bekommen
+ var extConsole = new ExtendedConsole();
+
+ bool leftDown = false;
+ bool rightDown = false;
+ bool useMouse = true;
+
+ float paddleVelocity = 0f;
+ const float paddleAcceleration = 60f;
+ const float paddleFriction = 40f;
+
+ // Key-Events (KeyDown/KeyUp)
+ extConsole.KeyEvent += (s, e) =>
+ {
+ // Virtuelle Keycodes der Pfeiltasten
+ const ushort VK_LEFT = 0x25;
+ const ushort VK_RIGHT = 0x27;
+
+ if (e.usKeyCode == VK_LEFT)
+ {
+ leftDown = e.bKeyDown;
+ if (e.bKeyDown) rightDown = false;
+ e.Handled = true;
+ }
+ else if (e.usKeyCode == VK_RIGHT)
+ {
+ rightDown = e.bKeyDown;
+ if (e.bKeyDown) leftDown = false;
+ e.Handled = true;
+ }
+ else if (e.usKeyCode == (ushort)ConsoleKey.Escape && e.bKeyDown)
+ {
+ // ESC zum Beenden
+ extConsole.Stop();
+ engine.State.IsGameOver = true;
+ e.Handled = true;
+ }
+ };
+
+ // Maussteuerung: Paddle per Maus-X-Position bewegen
+ extConsole.MouseEvent += (s, me) =>
+ {
+ if (!useMouse) return;
+
+ if (me.MouseMoved || me.ButtonEvent)
+ {
+ var mouseX = me.MousePos.X;
+ // Paddle-Mittelpunkt an Maus-X ausrichten
+ var newX = mouseX - engine.State.Paddle.Width / 2f;
+ newX = Math.Clamp(newX, 0, engine.State.FieldWidth - engine.State.Paddle.Width);
+ engine.State.Paddle.Position = new Arkanoid.Base.Models.Vector2(newX, engine.State.Paddle.Position.Y);
+ me.Handled = true;
+ }
+ };
+
+ var lastTime = DateTime.UtcNow;
+
+ while (!engine.State.IsGameOver)
+ {
+ var now = DateTime.UtcNow;
+ var delta = (float)(now - lastTime).TotalSeconds;
+ if (delta <= 0f) delta = 0.001f;
+ lastTime = now;
+
+ // Tastatursteuerung (wenn Maus nicht benutzt wird)
+ if (!useMouse)
+ {
+ float moveDir = 0f;
+ if (leftDown && !rightDown) moveDir = -1f;
+ else if (rightDown && !leftDown) moveDir = 1f;
+ else
+ {
+ // Reibung, wenn keine Richtung gehalten wird
+ if (paddleVelocity > 0)
+ {
+ paddleVelocity -= paddleFriction * delta;
+ if (paddleVelocity < 0) paddleVelocity = 0;
+ }
+ else if (paddleVelocity < 0)
+ {
+ paddleVelocity += paddleFriction * delta;
+ if (paddleVelocity > 0) paddleVelocity = 0;
+ }
+ }
+
+ if (moveDir != 0f)
+ {
+ paddleVelocity += moveDir * paddleAcceleration * delta;
+ }
+
+ if (paddleVelocity != 0f)
+ {
+ var direction = Math.Sign(paddleVelocity);
+ engine.MovePaddle(direction, delta);
+ }
+ }
+
+ engine.Update(delta);
+
+ // render
+ display.Clear();
+
+ // paddle
+ for (int x = 0; x < (int)engine.State.Paddle.Width; x++)
+ {
+ display.PutPixel((int)(engine.State.Paddle.Position.X + x), (int)engine.State.Paddle.Position.Y, ConsoleColor.Green);
+ }
+
+ // ball
+ display.PutPixel((int)engine.State.Ball.Position.X, (int)engine.State.Ball.Position.Y, ConsoleColor.Yellow);
+
+ // bricks
+ foreach (var brick in engine.State.Bricks)
+ {
+ if (brick.IsDestroyed) continue;
+ for (int bx = 0; bx < (int)brick.Width; bx++)
+ {
+ display.PutPixel((int)(brick.Position.X + bx), (int)brick.Position.Y, ConsoleColor.Red);
+ }
+ }
+
+ display.Update();
+
+ Thread.Sleep(10);
+ }
+
+ Console.ResetColor();
+ Console.CursorVisible = true;
+ }
+ }
+}
diff --git a/CSharpBible/Games/AsteroidsModernEngine.Tests/AsteroidsModernEngine.Tests.csproj b/CSharpBible/Games/AsteroidsModernEngine.Tests/AsteroidsModernEngine.Tests.csproj
index 69a877585..79559d9db 100644
--- a/CSharpBible/Games/AsteroidsModernEngine.Tests/AsteroidsModernEngine.Tests.csproj
+++ b/CSharpBible/Games/AsteroidsModernEngine.Tests/AsteroidsModernEngine.Tests.csproj
@@ -7,8 +7,8 @@
enable
-
-
+
+
all
diff --git a/CSharpBible/Games/AsteroidsModernEngine/AsteroidsModernEngine.csproj b/CSharpBible/Games/AsteroidsModernEngine/AsteroidsModernEngine.csproj
index 6acc27ca0..94b5d2e0c 100644
--- a/CSharpBible/Games/AsteroidsModernEngine/AsteroidsModernEngine.csproj
+++ b/CSharpBible/Games/AsteroidsModernEngine/AsteroidsModernEngine.csproj
@@ -17,6 +17,6 @@
-
+
diff --git a/CSharpBible/Games/AsteroidsModernUI/AsteroidsModernUI.csproj b/CSharpBible/Games/AsteroidsModernUI/AsteroidsModernUI.csproj
index ce4dcdcc0..e290d4767 100644
--- a/CSharpBible/Games/AsteroidsModernUI/AsteroidsModernUI.csproj
+++ b/CSharpBible/Games/AsteroidsModernUI/AsteroidsModernUI.csproj
@@ -12,8 +12,8 @@
-
-
+
+
diff --git a/CSharpBible/Games/BoxFlight/BoxFlight.csproj b/CSharpBible/Games/BoxFlight/BoxFlight.csproj
index bf9bb1374..3b65b75cd 100644
--- a/CSharpBible/Games/BoxFlight/BoxFlight.csproj
+++ b/CSharpBible/Games/BoxFlight/BoxFlight.csproj
@@ -9,6 +9,6 @@
-
+
\ No newline at end of file
diff --git a/CSharpBible/Games/CreateCards/CreateCards.csproj b/CSharpBible/Games/CreateCards/CreateCards.csproj
index a7a3b5f46..c928d747a 100644
--- a/CSharpBible/Games/CreateCards/CreateCards.csproj
+++ b/CSharpBible/Games/CreateCards/CreateCards.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/CSharpBible/Games/CreateCards/Model/Card.cs b/CSharpBible/Games/CreateCards/Model/Card.cs
index 47f853d43..cfa92dc46 100644
--- a/CSharpBible/Games/CreateCards/Model/Card.cs
+++ b/CSharpBible/Games/CreateCards/Model/Card.cs
@@ -40,18 +40,14 @@ public class Card
new PointF(0.3f, 0.375f), new PointF(0.7f, 0.375f), new PointF(0.3f, 0.625f), new PointF(0.7f, 0.625f),
new PointF(0.5f, 0.3125f),new PointF(0.5f, 0.6825f) } } },
{ CardValues.Jack ,new("J"){
- PntSuits=new[]{ new PointF(0.7f, 0.25f), new PointF(0.7f, 0.35f), new PointF(0.7f, 0.45f), new PointF(0.7f, 0.55f),new PointF(0.7f, 0.65f),
- new PointF(0.6f, 0.75f),new PointF(0.5f, 0.75f),new PointF(0.4f, 0.75f), new PointF(0.3f, 0.65f) } } },
+ CustomDraw = DrawJack
+ }},
{ CardValues.Queen ,new("Q"){
- PntSuits=new[]{ new PointF(0.3f, 0.35f), new PointF(0.3f, 0.45f), new PointF(0.3f, 0.55f),new PointF(0.3f, 0.65f),
- new PointF(0.7f, 0.35f), new PointF(0.7f, 0.45f), new PointF(0.7f, 0.55f), new PointF(0.7f, 0.75f),
- new PointF(0.4f, 0.3f), new PointF(0.5f, 0.25f),new PointF(0.6f, 0.3f),
- new PointF(0.4f, 0.7f), new PointF(0.5f, 0.75f),new PointF(0.6f, 0.65f),
- new PointF(0.5f, 0.55f) } } },
+ CustomDraw = DrawQueen
+ }},
{ CardValues.King ,new("K"){
- PntSuits=new[]{ new PointF(0.3f, 0.25f), new PointF(0.3f, 0.35f), new PointF(0.3f, 0.45f), new PointF(0.3f, 0.55f),new PointF(0.3f, 0.75f),
- new PointF(0.3f, 0.65f), new PointF(0.4f, 0.55f), new PointF(0.5f, 0.45f), new PointF(0.6f, 0.35f), new PointF(0.7f, 0.25f),
- new PointF(0.566667f, 0.55f), new PointF(0.63333f, 0.65f), new PointF(0.7f, 0.75f) } } },
+ CustomDraw = DrawKing
+ }},
};
public string Suit { get; set; }
public CardValues Value { get; set; }
@@ -86,13 +82,20 @@ public void DrawCard(int width, int height, Graphics g)
if (DrawDef.TryGetValue(Value, out var drawDef))
{
- foreach (var pnt in drawDef.PntVals)
- DrawText(rect, drawDef.PrintValue, drawDef.ValSize, pnt, g);
- foreach (var pnt in drawDef.PntSVals)
- DrawText(rect, Suit, drawDef.ValSize, pnt, g);
- foreach (var pnt in drawDef.PntSuits)
- DrawText(rect, Suit, drawDef.SuitSize, pnt, g);
-
+ if (drawDef.CustomDraw != null)
+ {
+ // Face card or custom layout
+ drawDef.CustomDraw(g, rect, Color);
+ }
+ else
+ {
+ foreach (var pnt in drawDef.PntVals)
+ DrawText(rect, drawDef.PrintValue, drawDef.ValSize, pnt, g);
+ foreach (var pnt in drawDef.PntSVals)
+ DrawText(rect, Suit, drawDef.ValSize, pnt, g);
+ foreach (var pnt in drawDef.PntSuits)
+ DrawText(rect, Suit, drawDef.SuitSize, pnt, g);
+ }
}
void DrawText(Rectangle rect, string sP, double fSize, PointF fpOffs, Graphics g)
@@ -108,6 +111,131 @@ void DrawText(Rectangle rect, string sP, double fSize, PointF fpOffs, Graphics g
}
}
+ private static void DrawJack(Graphics g, Rectangle rect, Color color)
+ {
+ // Simple stylized Jack vector drawing
+ RectangleF inner = RectangleF.Inflate(rect, -rect.Width * 0.15f, -rect.Height * 0.2f);
+ using var bodyBrush = new SolidBrush(Color.FromArgb(220, color));
+ using var outlinePen = new Pen(Color.Black, rect.Width / 150f);
+
+ // Body
+ RectangleF body = new RectangleF(inner.X + inner.Width * 0.25f, inner.Y + inner.Height * 0.35f,
+ inner.Width * 0.5f, inner.Height * 0.5f);
+ g.FillRectangle(bodyBrush, body);
+ g.DrawRectangle(outlinePen, body.X, body.Y, body.Width, body.Height);
+
+ // Head
+ float headRadius = inner.Width * 0.18f;
+ PointF headCenter = new PointF(inner.X + inner.Width * 0.5f, inner.Y + inner.Height * 0.22f);
+ RectangleF head = new RectangleF(headCenter.X - headRadius, headCenter.Y - headRadius,
+ headRadius * 2, headRadius * 2);
+ using var headBrush = new SolidBrush(Color.Beige);
+ g.FillEllipse(headBrush, head);
+ g.DrawEllipse(outlinePen, head);
+
+ // Cap
+ RectangleF cap = new RectangleF(head.X - headRadius * 0.3f, head.Y - headRadius * 0.6f,
+ head.Width * 1.3f, head.Height * 0.6f);
+ using var capBrush = new SolidBrush(color);
+ g.FillRectangle(capBrush, cap);
+ g.DrawRectangle(outlinePen, cap.X, cap.Y, cap.Width, cap.Height);
+ }
+
+ private static void DrawQueen(Graphics g, Rectangle rect, Color color)
+ {
+ // Simple stylized Queen vector drawing
+ RectangleF inner = RectangleF.Inflate(rect, -rect.Width * 0.15f, -rect.Height * 0.2f);
+ using var bodyBrush = new SolidBrush(Color.FromArgb(200, color));
+ using var outlinePen = new Pen(Color.Black, rect.Width / 150f);
+
+ // Dress (triangle)
+ PointF p1 = new PointF(inner.X + inner.Width * 0.5f, inner.Bottom);
+ PointF p2 = new PointF(inner.X + inner.Width * 0.2f, inner.Y + inner.Height * 0.45f);
+ PointF p3 = new PointF(inner.X + inner.Width * 0.8f, inner.Y + inner.Height * 0.45f);
+ PointF[] dress = { p1, p2, p3 };
+ g.FillPolygon(bodyBrush, dress);
+ g.DrawPolygon(outlinePen, dress);
+
+ // Upper body
+ RectangleF body = new RectangleF(inner.X + inner.Width * 0.35f, inner.Y + inner.Height * 0.32f,
+ inner.Width * 0.3f, inner.Height * 0.2f);
+ g.FillRectangle(bodyBrush, body);
+ g.DrawRectangle(outlinePen, body.X, body.Y, body.Width, body.Height);
+
+ // Head
+ float headRadius = inner.Width * 0.17f;
+ PointF headCenter = new PointF(inner.X + inner.Width * 0.5f, inner.Y + inner.Height * 0.18f);
+ RectangleF head = new RectangleF(headCenter.X - headRadius, headCenter.Y - headRadius,
+ headRadius * 2, headRadius * 2);
+ using var headBrush = new SolidBrush(Color.Beige);
+ g.FillEllipse(headBrush, head);
+ g.DrawEllipse(outlinePen, head);
+
+ // Crown
+ float crownHeight = headRadius * 0.8f;
+ PointF c1 = new PointF(head.X, head.Y);
+ PointF c2 = new PointF(head.Right, head.Y);
+ PointF c3 = new PointF(head.X + head.Width * 0.2f, head.Y - crownHeight);
+ PointF c4 = new PointF(head.X + head.Width * 0.5f, head.Y - crownHeight * 1.2f);
+ PointF c5 = new PointF(head.X + head.Width * 0.8f, head.Y - crownHeight);
+ PointF[] crown = { c1, c3, c4, c5, c2 };
+ using var crownBrush = new SolidBrush(color);
+ g.FillPolygon(crownBrush, crown);
+ g.DrawPolygon(outlinePen, crown);
+ }
+
+ private static void DrawKing(Graphics g, Rectangle rect, Color color)
+ {
+ // Simple stylized King vector drawing
+ RectangleF inner = RectangleF.Inflate(rect, -rect.Width * 0.15f, -rect.Height * 0.2f);
+ using var robeBrush = new SolidBrush(Color.FromArgb(230, color));
+ using var outlinePen = new Pen(Color.Black, rect.Width / 150f);
+
+ // Robe (rounded rectangle)
+ RectangleF robe = new RectangleF(inner.X + inner.Width * 0.25f, inner.Y + inner.Height * 0.4f,
+ inner.Width * 0.5f, inner.Height * 0.5f);
+ using (GraphicsPath robePath = new GraphicsPath())
+ {
+ float r = robe.Width * 0.2f;
+ robePath.AddArc(robe.X, robe.Y, r, r, 180, 90);
+ robePath.AddArc(robe.Right - r, robe.Y, r, r, 270, 90);
+ robePath.AddArc(robe.Right - r, robe.Bottom - r, r, r, 0, 90);
+ robePath.AddArc(robe.X, robe.Bottom - r, r, r, 90, 90);
+ robePath.CloseFigure();
+ g.FillPath(robeBrush, robePath);
+ g.DrawPath(outlinePen, robePath);
+ }
+
+ // Head
+ float headRadius = inner.Width * 0.18f;
+ PointF headCenter = new PointF(inner.X + inner.Width * 0.5f, inner.Y + inner.Height * 0.24f);
+ RectangleF head = new RectangleF(headCenter.X - headRadius, headCenter.Y - headRadius,
+ headRadius * 2, headRadius * 2);
+ using var headBrush = new SolidBrush(Color.Beige);
+ g.FillEllipse(headBrush, head);
+ g.DrawEllipse(outlinePen, head);
+
+ // Beard
+ RectangleF beard = new RectangleF(head.X + head.Width * 0.2f, headCenter.Y,
+ head.Width * 0.6f, headRadius * 0.9f);
+ using var beardBrush = new SolidBrush(Color.FromArgb(160, 120, 80));
+ g.FillEllipse(beardBrush, beard);
+ g.DrawEllipse(outlinePen, beard);
+
+ // Crown (more geometric)
+ float crownHeight = headRadius * 0.9f;
+ PointF k1 = new PointF(head.X, head.Y);
+ PointF k2 = new PointF(head.Right, head.Y);
+ PointF k3 = new PointF(head.X + head.Width * 0.15f, head.Y - crownHeight);
+ PointF k4 = new PointF(head.X + head.Width * 0.35f, head.Y - crownHeight * 1.2f);
+ PointF k5 = new PointF(head.X + head.Width * 0.65f, head.Y - crownHeight * 1.2f);
+ PointF k6 = new PointF(head.X + head.Width * 0.85f, head.Y - crownHeight);
+ PointF[] crown = { k1, k3, k4, k5, k6, k2 };
+ using var crownBrush = new SolidBrush(color);
+ g.FillPolygon(crownBrush, crown);
+ g.DrawPolygon(outlinePen, crown);
+ }
+
private GraphicsPath RoundedRectangle(Rectangle r, int d)
{
GraphicsPath path = new GraphicsPath();
diff --git a/CSharpBible/Games/CreateCards/Model/CardDrawDef.cs b/CSharpBible/Games/CreateCards/Model/CardDrawDef.cs
index d1ed02f54..03ea443b3 100644
--- a/CSharpBible/Games/CreateCards/Model/CardDrawDef.cs
+++ b/CSharpBible/Games/CreateCards/Model/CardDrawDef.cs
@@ -1,4 +1,5 @@
-using System.Drawing;
+using System;
+using System.Drawing;
namespace CreateCards.Model
{
@@ -12,6 +13,10 @@ public class CardDrawDef
public PointF[] PntVals = new[] { new PointF(hOffs, vOffs), new PointF(hOffs, 1f-vOffs), new PointF(1f-hOffs, vOffs), new PointF(1f-hOffs, 1f- vOffs) };
public PointF[] PntSVals = new[] { new PointF(hOffs, vOffs*2), new PointF(hOffs, 1f - vOffs*2), new PointF(1f-hOffs, vOffs*2), new PointF(1f-hOffs, 1f - vOffs*2) };
public PointF[] PntSuits = { };
+
+ // Optional custom drawing callback for face cards or other special layouts
+ public Action? CustomDraw;
+
public CardDrawDef(string _pv,double _ss=0.2d) {
PrintValue = _pv; SuitSize = _ss;
}
diff --git a/CSharpBible/Games/DetectiveGame.Console/DetectiveGame.Console.csproj b/CSharpBible/Games/DetectiveGame.Console/DetectiveGame.Console.csproj
index 874cdeb66..d4fee8a1b 100644
--- a/CSharpBible/Games/DetectiveGame.Console/DetectiveGame.Console.csproj
+++ b/CSharpBible/Games/DetectiveGame.Console/DetectiveGame.Console.csproj
@@ -16,6 +16,6 @@
-
+
\ No newline at end of file
diff --git a/CSharpBible/Games/DetectiveGame.Tests/DetectiveGame.Tests.csproj b/CSharpBible/Games/DetectiveGame.Tests/DetectiveGame.Tests.csproj
index 16fd768dc..9682ce1f5 100644
--- a/CSharpBible/Games/DetectiveGame.Tests/DetectiveGame.Tests.csproj
+++ b/CSharpBible/Games/DetectiveGame.Tests/DetectiveGame.Tests.csproj
@@ -5,8 +5,8 @@
enable
-
-
+
+
all
diff --git a/CSharpBible/Games/Galaxia_BaseTests/Galaxia_BaseTests.csproj b/CSharpBible/Games/Galaxia_BaseTests/Galaxia_BaseTests.csproj
index e9460c3c3..73b2e0cba 100644
--- a/CSharpBible/Games/Galaxia_BaseTests/Galaxia_BaseTests.csproj
+++ b/CSharpBible/Games/Galaxia_BaseTests/Galaxia_BaseTests.csproj
@@ -9,8 +9,8 @@
-
-
+
+
diff --git a/CSharpBible/Games/Galaxia_UI.Tests/Galaxia_UI.Tests.csproj b/CSharpBible/Games/Galaxia_UI.Tests/Galaxia_UI.Tests.csproj
index 657db4762..4817a8c49 100644
--- a/CSharpBible/Games/Galaxia_UI.Tests/Galaxia_UI.Tests.csproj
+++ b/CSharpBible/Games/Galaxia_UI.Tests/Galaxia_UI.Tests.csproj
@@ -6,6 +6,6 @@
-
+
\ No newline at end of file
diff --git a/CSharpBible/Games/Galaxia_UI/Galaxia_UI.csproj b/CSharpBible/Games/Galaxia_UI/Galaxia_UI.csproj
index 3638a6689..dc5639a21 100644
--- a/CSharpBible/Games/Galaxia_UI/Galaxia_UI.csproj
+++ b/CSharpBible/Games/Galaxia_UI/Galaxia_UI.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/CSharpBible/Games/Game_BaseTests/Game_BaseTests.csproj b/CSharpBible/Games/Game_BaseTests/Game_BaseTests.csproj
index d3800b71e..9402a1cf9 100644
--- a/CSharpBible/Games/Game_BaseTests/Game_BaseTests.csproj
+++ b/CSharpBible/Games/Game_BaseTests/Game_BaseTests.csproj
@@ -7,8 +7,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Games/Games.sln b/CSharpBible/Games/Games.sln
index cc8750061..25e20fe20 100644
--- a/CSharpBible/Games/Games.sln
+++ b/CSharpBible/Games/Games.sln
@@ -7,6 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projektmappenelemente", "Pr
ProjectSection(SolutionItems) = preProject
Games.props = Games.props
Games_net.props = Games_net.props
+ Packages.props = Packages.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLib", "..\Libraries\MVVM_BaseLib\MVVM_BaseLib.csproj", "{890CF504-3814-443B-9EE6-E8BCACF68203}"
@@ -146,6 +147,32 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Legacy", "Legacy", "{8C496E
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BoxFlight", "BoxFlight\BoxFlight.csproj", "{07C4AF86-44A7-C659-025B-1105D42DEBCA}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Labyrinth", "Labyrinth", "{E2FA6ACF-7A86-453B-9342-799EB5280461}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Treppen.Base", "Treppen.Base\Treppen.Base.csproj", "{3FB6E592-BA42-EFD1-6861-B49183469D3C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Treppen.BaseTests", "Treppen.BaseTests\Treppen.BaseTests.csproj", "{7E0199F5-E152-B8A9-2494-607F0638E043}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathLibrary", "..\Libraries\MathLIbrary\MathLibrary.csproj", "{AF2A0CC3-D271-8C79-7C89-006A04B7C58E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Treppen.WPF", "Treppen.WPF\Treppen.WPF.csproj", "{A5F7E6E6-4BE4-4CC3-9BD2-A566492456C3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Treppen.Print", "Treppen.Print\Treppen.Print.csproj", "{19CE8BB5-06C5-152E-4FAA-C5347526E828}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Treppen.Export", "Treppen.Export\Treppen.Export.csproj", "{495BD0C6-17EC-40C5-89A0-74EC258246EE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Snake", "Snake", "{8E0BEE1E-8100-46C8-AB90-74EA8A5E96C5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MidiSwing.MVVM", "MidiSwing.MVVM\MidiSwing.MVVM.csproj", "{1B3ADF69-714F-46A8-935C-E39283051387}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Snake.WPF", "Snake.WPF\Snake.WPF.csproj", "{AF5CE3DF-B2EE-4526-95B5-96280CD6F0F7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Arkanoid", "Arkanoid", "{DAA48107-9A7D-4186-8AD0-2316D9B027CC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arkanoid.Base", "Arkanoid.Base\Arkanoid.Base.csproj", "{EACFD7D3-FC23-48A0-A25C-8E0B116860A0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arkanoid.Cons", "Arkanoid.Cons\Arkanoid.Cons.csproj", "{348EFF6D-2854-486E-A594-2330C26AE3BA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -368,6 +395,46 @@ Global
{07C4AF86-44A7-C659-025B-1105D42DEBCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07C4AF86-44A7-C659-025B-1105D42DEBCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07C4AF86-44A7-C659-025B-1105D42DEBCA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3FB6E592-BA42-EFD1-6861-B49183469D3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3FB6E592-BA42-EFD1-6861-B49183469D3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3FB6E592-BA42-EFD1-6861-B49183469D3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3FB6E592-BA42-EFD1-6861-B49183469D3C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7E0199F5-E152-B8A9-2494-607F0638E043}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7E0199F5-E152-B8A9-2494-607F0638E043}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7E0199F5-E152-B8A9-2494-607F0638E043}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7E0199F5-E152-B8A9-2494-607F0638E043}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AF2A0CC3-D271-8C79-7C89-006A04B7C58E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AF2A0CC3-D271-8C79-7C89-006A04B7C58E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AF2A0CC3-D271-8C79-7C89-006A04B7C58E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AF2A0CC3-D271-8C79-7C89-006A04B7C58E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A5F7E6E6-4BE4-4CC3-9BD2-A566492456C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5F7E6E6-4BE4-4CC3-9BD2-A566492456C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5F7E6E6-4BE4-4CC3-9BD2-A566492456C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5F7E6E6-4BE4-4CC3-9BD2-A566492456C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {19CE8BB5-06C5-152E-4FAA-C5347526E828}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {19CE8BB5-06C5-152E-4FAA-C5347526E828}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {19CE8BB5-06C5-152E-4FAA-C5347526E828}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {19CE8BB5-06C5-152E-4FAA-C5347526E828}.Release|Any CPU.Build.0 = Release|Any CPU
+ {495BD0C6-17EC-40C5-89A0-74EC258246EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {495BD0C6-17EC-40C5-89A0-74EC258246EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {495BD0C6-17EC-40C5-89A0-74EC258246EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {495BD0C6-17EC-40C5-89A0-74EC258246EE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1B3ADF69-714F-46A8-935C-E39283051387}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1B3ADF69-714F-46A8-935C-E39283051387}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1B3ADF69-714F-46A8-935C-E39283051387}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1B3ADF69-714F-46A8-935C-E39283051387}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AF5CE3DF-B2EE-4526-95B5-96280CD6F0F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AF5CE3DF-B2EE-4526-95B5-96280CD6F0F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AF5CE3DF-B2EE-4526-95B5-96280CD6F0F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AF5CE3DF-B2EE-4526-95B5-96280CD6F0F7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EACFD7D3-FC23-48A0-A25C-8E0B116860A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EACFD7D3-FC23-48A0-A25C-8E0B116860A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EACFD7D3-FC23-48A0-A25C-8E0B116860A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EACFD7D3-FC23-48A0-A25C-8E0B116860A0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {348EFF6D-2854-486E-A594-2330C26AE3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {348EFF6D-2854-486E-A594-2330C26AE3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {348EFF6D-2854-486E-A594-2330C26AE3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {348EFF6D-2854-486E-A594-2330C26AE3BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -375,6 +442,8 @@ Global
GlobalSection(NestedProjects) = preSolution
{890CF504-3814-443B-9EE6-E8BCACF68203} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
{66EB60F2-523D-47C8-95EA-C7E10759E239} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {8AA72111-7FF0-416B-90E3-0DC9B2548156} = {8E0BEE1E-8100-46C8-AB90-74EA8A5E96C5}
+ {58BB1796-700F-4F0D-981A-A16BE151AD65} = {8E0BEE1E-8100-46C8-AB90-74EA8A5E96C5}
{79B65C63-EB25-4DEF-AC59-52836AAA0F24} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
{6F8F8283-A334-47D4-A15B-94100A2C29E3} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
{D8E3D40A-8D95-4410-AF28-A7AD24215754} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
@@ -383,7 +452,9 @@ Global
{6BDAFCB1-55B5-4CAB-A9CD-9D485151D1EF} = {A8BFD1B7-37A4-4A93-944B-B63A04DBF88B}
{9CA57524-1297-4313-AA78-5AA8BFB7E746} = {A8BFD1B7-37A4-4A93-944B-B63A04DBF88B}
{D5CA406E-3839-4CB6-AF28-7EE2B029B912} = {7337A459-E02B-4BD0-8271-807FD3E16A1B}
+ {380B97C9-B145-49A9-92C0-47B819748BC3} = {4BE42477-1420-4A81-BDFA-4C34DA898F05}
{0E81C28B-325A-4A2F-B21F-F66DADA451EC} = {79430A25-1F0B-49B9-9A68-720A422E91AB}
+ {07FB5A96-DB06-442C-8570-8644898BD6C5} = {4BE42477-1420-4A81-BDFA-4C34DA898F05}
{6AA283E8-D2B2-4743-B96D-96A5DAA8F2B5} = {4BE42477-1420-4A81-BDFA-4C34DA898F05}
{F7BB8FCE-FAE8-4C35-939F-070DF91806B5} = {79430A25-1F0B-49B9-9A68-720A422E91AB}
{3E13AE4D-D805-4D2E-B6B5-88DC174F6A2C} = {79430A25-1F0B-49B9-9A68-720A422E91AB}
@@ -408,6 +479,15 @@ Global
{9B8260C3-1264-9915-05B8-313EECE43623} = {B87480CD-A73E-41C6-BFD6-3F78F80E3598}
{9F32B398-EE9E-B09C-8023-8A8769315037} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
{448CB63B-278E-A7C1-AE2F-3709778245E2} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {3FB6E592-BA42-EFD1-6861-B49183469D3C} = {E2FA6ACF-7A86-453B-9342-799EB5280461}
+ {7E0199F5-E152-B8A9-2494-607F0638E043} = {E2FA6ACF-7A86-453B-9342-799EB5280461}
+ {AF2A0CC3-D271-8C79-7C89-006A04B7C58E} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {A5F7E6E6-4BE4-4CC3-9BD2-A566492456C3} = {E2FA6ACF-7A86-453B-9342-799EB5280461}
+ {19CE8BB5-06C5-152E-4FAA-C5347526E828} = {E2FA6ACF-7A86-453B-9342-799EB5280461}
+ {495BD0C6-17EC-40C5-89A0-74EC258246EE} = {E2FA6ACF-7A86-453B-9342-799EB5280461}
+ {AF5CE3DF-B2EE-4526-95B5-96280CD6F0F7} = {8E0BEE1E-8100-46C8-AB90-74EA8A5E96C5}
+ {EACFD7D3-FC23-48A0-A25C-8E0B116860A0} = {DAA48107-9A7D-4186-8AD0-2316D9B027CC}
+ {348EFF6D-2854-486E-A594-2330C26AE3BA} = {DAA48107-9A7D-4186-8AD0-2316D9B027CC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {51252E6F-E712-436A-B391-1C5243F65ABE}
diff --git a/CSharpBible/Games/HexaBan_Console/HexaBan_Console.csproj b/CSharpBible/Games/HexaBan_Console/HexaBan_Console.csproj
index edfd9705e..0f502f1e1 100644
--- a/CSharpBible/Games/HexaBan_Console/HexaBan_Console.csproj
+++ b/CSharpBible/Games/HexaBan_Console/HexaBan_Console.csproj
@@ -18,6 +18,6 @@
-
+
diff --git a/CSharpBible/Games/HexaBan_Console/HexaBan_ConsoleLnx.csproj b/CSharpBible/Games/HexaBan_Console/HexaBan_ConsoleLnx.csproj
index 94f2dbaf3..f0b5a629f 100644
--- a/CSharpBible/Games/HexaBan_Console/HexaBan_ConsoleLnx.csproj
+++ b/CSharpBible/Games/HexaBan_Console/HexaBan_ConsoleLnx.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/CSharpBible/Games/HexaBan_Console/HexaBan_ConsoleWin.csproj b/CSharpBible/Games/HexaBan_Console/HexaBan_ConsoleWin.csproj
index 650b4bf70..0b4a9d1c0 100644
--- a/CSharpBible/Games/HexaBan_Console/HexaBan_ConsoleWin.csproj
+++ b/CSharpBible/Games/HexaBan_Console/HexaBan_ConsoleWin.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/CSharpBible/Games/MidiSwing.MVVM/App.xaml b/CSharpBible/Games/MidiSwing.MVVM/App.xaml
new file mode 100644
index 000000000..3084ca09a
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/Games/MidiSwing.MVVM/App.xaml.cs b/CSharpBible/Games/MidiSwing.MVVM/App.xaml.cs
new file mode 100644
index 000000000..eacefee49
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace MidiSwing.MVVM
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/CSharpBible/Games/MidiSwing.MVVM/AssemblyInfo.cs b/CSharpBible/Games/MidiSwing.MVVM/AssemblyInfo.cs
new file mode 100644
index 000000000..b0ec82757
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/Games/MidiSwing.MVVM/MidiSwing.MVVM.csproj b/CSharpBible/Games/MidiSwing.MVVM/MidiSwing.MVVM.csproj
new file mode 100644
index 000000000..54d668405
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/MidiSwing.MVVM.csproj
@@ -0,0 +1,16 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/MidiSwing.MVVM/Models/MusicGenerator.cs b/CSharpBible/Games/MidiSwing.MVVM/Models/MusicGenerator.cs
new file mode 100644
index 000000000..f43483133
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/Models/MusicGenerator.cs
@@ -0,0 +1,449 @@
+using NAudio;
+using NAudio.Midi;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Channels;
+using System.Threading.Tasks;
+
+namespace MidiSwing.MVVM.Models;
+
+public class MusicGenerator
+{
+ private int _channel;
+ private MidiOut _midi;
+ private readonly object _midiSync = new();
+
+ public MusicGenerator()
+ {
+ _channel = Math.Clamp(2, 1, 16);
+ try
+ {
+ if (MidiOut.NumberOfDevices > 0)
+ {
+ _midi = new MidiOut(Math.Clamp(0, 0, MidiOut.NumberOfDevices - 1));
+ }
+ }
+ catch
+ {
+ _midi = null; // Fallback to silent if MIDI not available
+ }
+ }
+
+ // Basic lengths in eighth-note units to keep rhythm handling simple
+ private enum NoteLength
+ {
+ Eighth = 1,
+ Quarter = 2,
+ Half = 4,
+ Whole = 8
+ }
+
+ private sealed class MelodyNote
+ {
+ public int Pitch { get; set; }
+ public NoteLength Length { get; set; }
+ public bool Accent { get; set; }
+ }
+
+ private List GenerateBaseMelody(int root = 60)
+ {
+ // Simple minor-blues flavored motif around root
+ var scale = new[] { 0, 2, 4, 5, 7,9, 10, 12 };
+ int Degree(int i) => root + scale[i];
+
+ var motif = new List
+ {
+ new() { Pitch = Degree(0), Length = NoteLength.Quarter, Accent = true },
+ new() { Pitch = Degree(1), Length = NoteLength.Eighth, Accent = false },
+ new() { Pitch = Degree(2), Length = NoteLength.Eighth, Accent = false },
+ new() { Pitch = Degree(3), Length = NoteLength.Eighth, Accent = false },
+ new() { Pitch = Degree(4), Length = NoteLength.Eighth, Accent = false },
+ new() { Pitch = Degree(5), Length = NoteLength.Quarter, Accent = false },
+ new() { Pitch = Degree(6), Length = NoteLength.Eighth, Accent = false },
+ new() { Pitch = Degree(7), Length = NoteLength.Eighth, Accent = false },
+ new() { Pitch = Degree(5), Length = NoteLength.Quarter, Accent = false },
+ new() { Pitch = Degree(7), Length = NoteLength.Eighth, Accent = false },
+ new() { Pitch = Degree(2), Length = NoteLength.Eighth, Accent = false },
+ new() { Pitch = Degree(0), Length = NoteLength.Quarter, Accent = false }
+ };
+
+ var phrase = new List();
+
+ // Bar 1: original motif
+ phrase.AddRange(motif);
+
+ // Bar 2: up a second
+ phrase.AddRange(motif.Select(n => new MelodyNote
+ {
+ Pitch = n.Pitch + 2,
+ Length = n.Length,
+ Accent = n.Accent
+ }));
+
+ // Bar 3: down a minor third
+ phrase.AddRange(motif.Select(n => new MelodyNote
+ {
+ Pitch = n.Pitch - 3,
+ Length = n.Length,
+ Accent = n.Accent
+ }));
+
+ // Bar 4: original but with a held note at the end
+ phrase.AddRange(motif.Take(motif.Count - 1));
+ phrase.Add(new MelodyNote
+ {
+ Pitch = Degree(0),
+ Length = NoteLength.Half,
+ Accent = true
+ });
+
+ return phrase;
+ }
+
+ private List CreateVariation(List baseMelody, int semitoneShift, bool addSyncopation)
+ {
+ var rnd = new Random();
+ var result = new List();
+
+ foreach (var note in baseMelody)
+ {
+ var pitch = note.Pitch + semitoneShift;
+
+ // Occasional neighbor note for variation
+ if (rnd.NextDouble() < 0.2)
+ {
+ int neighbor = pitch + (rnd.Next(2) == 0 ? -1 : 1);
+ result.Add(new MelodyNote
+ {
+ Pitch = neighbor,
+ Length = NoteLength.Eighth,
+ Accent = false
+ });
+
+ result.Add(new MelodyNote
+ {
+ Pitch = pitch,
+ Length = note.Length == NoteLength.Quarter ? NoteLength.Eighth : note.Length,
+ Accent = note.Accent
+ });
+
+ continue;
+ }
+
+ var len = note.Length;
+ if (addSyncopation && len == NoteLength.Quarter && rnd.NextDouble() < 0.4)
+ {
+ // Split quarter into two eighths
+ result.Add(new MelodyNote
+ {
+ Pitch = pitch,
+ Length = NoteLength.Eighth,
+ Accent = note.Accent
+ });
+ result.Add(new MelodyNote
+ {
+ Pitch = pitch,
+ Length = NoteLength.Eighth,
+ Accent = false
+ });
+ }
+ else
+ {
+ result.Add(new MelodyNote
+ {
+ Pitch = pitch,
+ Length = len,
+ Accent = note.Accent
+ });
+ }
+ }
+
+ return result;
+ }
+
+ private List GetMainChords()
+ {
+ // Rough C minor → F7 → G7 → Cmin pattern
+ return new List
+ {
+ new[] { 48, 51, 55, 58 }, // Cmin7
+ new[] { 41, 45, 48, 52 }, // F7
+ new[] { 43, 47, 50, 53 }, // G7
+ new[] { 48, 51, 55, 58 } // Cmin7
+ };
+ }
+
+ public async Task PlaySwingMelody()
+ {
+ if (_midi == null)
+ {
+ Console.WriteLine("Kein MIDI-Ausgabegerät verfügbar.");
+ return;
+ }
+
+ const int drumsChannel = 10;
+ const int bassChannel = 3;
+ const int compChannel = 4;
+ const int leadChannel = 2;
+
+ const int leadPatch = 67; // Tenor Sax
+ const int compPatch = 27; // Jazz Guitar
+ const int bassPatch = 33; // Acoustic Bass
+
+ int bpm = 120;
+ int beatMs = 60000 / bpm;
+
+ try
+ {
+ SetPatch(leadPatch, leadChannel);
+ SetPatch(compPatch, compChannel);
+ SetPatch(bassPatch, bassChannel);
+
+ var timeline = new List<(int atMs, Action action)>();
+
+ void AddNoteOn(int atMs, int note, int vel, int ch) =>
+ timeline.Add((atMs, () => NoteOn(note, vel, ch)));
+
+ void AddNoteOff(int atMs, int note, int ch) =>
+ timeline.Add((atMs, () => NoteOff(note, ch)));
+
+ void AddDrumHit(int atMs, int midiNote, int vel, int lenMs)
+ {
+ timeline.Add((atMs, () => NoteOn(midiNote, vel, drumsChannel)));
+ timeline.Add((atMs + lenMs, () => NoteOff(midiNote, drumsChannel)));
+ }
+
+ void AddChordStab(int atMs, IEnumerable notes, int vel, int ch, int lenMs)
+ {
+ foreach (var n in notes)
+ {
+ timeline.Add((atMs, () => NoteOn(n, vel, ch)));
+ timeline.Add((atMs + lenMs, () => NoteOff(n, ch)));
+ }
+ }
+
+ int TimeBeatsToMs(double beats) => (int)Math.Round(beats * beatMs);
+
+ // Build musical material
+ var baseMelody = GenerateBaseMelody(60);
+ var variation1 = CreateVariation(baseMelody, 2, addSyncopation: true);
+ var variation2 = CreateVariation(baseMelody, -3, addSyncopation: true);
+ var chords = GetMainChords();
+
+ int sectionStartMs = 0;
+
+ // Intro: 4 bars, bass + light drums, short walk-in
+ int introBars = 4;
+ for (int bar = 0; bar < introBars; bar++)
+ {
+ int barStart = sectionStartMs + bar * beatMs * 4;
+
+ int[] bassPattern = { 36, 43, 36, 43 }; // C2, G2
+ for (int beat = 0; beat < 4; beat++)
+ {
+ int t = barStart + beat * beatMs;
+ int note = bassPattern[beat % bassPattern.Length];
+ AddNoteOn(t, note, 85, bassChannel);
+ AddNoteOff(t + TimeBeatsToMs(0.9), note, bassChannel);
+
+ if (beat == 1 || beat == 3)
+ {
+ AddDrumHit(t, 42, 70, 30); // light hi-hat on 2 and 4
+ }
+ }
+ }
+
+ sectionStartMs += introBars * beatMs * 4;
+
+ // Helper to add one groove bar (drums + comp + bass)
+ void AddGrooveBar(int barIndex, int startMs)
+ {
+ int chordIndex = barIndex % chords.Count;
+ var chord = chords[chordIndex];
+
+ for (int beat = 0; beat < 4; beat++)
+ {
+ int beatStart = startMs + beat * beatMs;
+
+ // Hi-hat on each beat
+ AddDrumHit(beatStart, 42, 75, 25);
+
+ // Kick/snare
+ if (beat == 0 || beat == 2)
+ AddDrumHit(beatStart, 36, 90, 30); // kick
+ else
+ AddDrumHit(beatStart, 38, 95, 30); // snare
+
+ // Off-beat hi-hat (swing)
+ int off = beatStart + (int)Math.Round(beatMs * 2.0 / 3.0);
+ AddDrumHit(off, 42, 60, 25);
+ }
+
+ // Comp stabs on 2 and 4
+ int beat2 = startMs + beatMs;
+ int beat4 = startMs + 3 * beatMs;
+ AddChordStab(beat2, chord, 75, compChannel, 140);
+ AddChordStab(beat4, chord, 80, compChannel, 160);
+
+ // Simple bass walking
+ int[] bassLine = { chord[0], chord[2], chord[2] + 2, chord[3] };
+ for (int beat = 0; beat < 4; beat++)
+ {
+ int t = startMs + beat * beatMs;
+ int n = bassLine[beat % bassLine.Length] - 12;
+ AddNoteOn(t, n, 80, bassChannel);
+ AddNoteOff(t + TimeBeatsToMs(0.9), n, bassChannel);
+ }
+ }
+
+ int barLenMs = beatMs * 4;
+
+ // Main A section: 8 bars of groove
+ int aSectionBars = 8;
+ for (int bar = 0; bar < aSectionBars; bar++)
+ {
+ int barStart = sectionStartMs + bar * barLenMs;
+ AddGrooveBar(bar, barStart);
+ }
+
+ // Helper to place a melody line on the lead channel
+ void PlaceMelody(List melody, int startMs, int channel, int baseVelocity)
+ {
+ double beatPos = 0;
+ foreach (var n in melody)
+ {
+ int start = startMs + TimeBeatsToMs(beatPos);
+ double lenBeats = n.Length switch
+ {
+ NoteLength.Eighth => 0.5,
+ NoteLength.Quarter => 1.0,
+ NoteLength.Half => 2.0,
+ NoteLength.Whole => 4.0,
+ _ => 1.0
+ };
+
+ int vel = baseVelocity + (n.Accent ? 15 : 0);
+ AddNoteOn(start, n.Pitch, vel, channel);
+ AddNoteOff(start + TimeBeatsToMs(lenBeats * 0.9), n.Pitch, channel);
+
+ beatPos += lenBeats;
+ }
+ }
+
+ int melodyStart = sectionStartMs;
+ PlaceMelody(baseMelody, melodyStart, leadChannel, 100);
+
+ int baseMelodyLenBeats = baseMelody.Sum(m => m.Length switch
+ {
+ NoteLength.Eighth => 1,
+ NoteLength.Quarter => 2,
+ NoteLength.Half => 4,
+ NoteLength.Whole => 8,
+ _ => 2
+ });
+ int baseMelodyLenMs = TimeBeatsToMs(baseMelodyLenBeats / 2.0); // convert eighth units back to beats
+
+ PlaceMelody(baseMelody, melodyStart + baseMelodyLenMs, leadChannel, 105);
+
+ sectionStartMs += aSectionBars * barLenMs;
+
+ // A' section: variation1 over same groove
+ int a2Bars = 8;
+ for (int bar = 0; bar < a2Bars; bar++)
+ {
+ int barStart = sectionStartMs + bar * barLenMs;
+ AddGrooveBar(bar, barStart);
+ }
+
+ PlaceMelody(variation1, sectionStartMs, leadChannel, 105);
+ sectionStartMs += a2Bars * barLenMs;
+
+ // B section: variation2, denser comping
+ int bBars = 8;
+ for (int bar = 0; bar < bBars; bar++)
+ {
+ int barStart = sectionStartMs + bar * barLenMs;
+ AddGrooveBar(bar, barStart);
+
+ int chordIndex = bar % chords.Count;
+ var chord = chords[chordIndex];
+ int off2 = barStart + beatMs + beatMs / 2;
+ int off4 = barStart + 3 * beatMs + beatMs / 2;
+ AddChordStab(off2, chord, 85, compChannel, 100);
+ AddChordStab(off4, chord, 90, compChannel, 120);
+ }
+
+ PlaceMelody(variation2, sectionStartMs, leadChannel, 110);
+ sectionStartMs += bBars * barLenMs;
+
+ // Play timeline in order
+ timeline.Sort((a, b) => a.atMs.CompareTo(b.atMs));
+
+ int currentTime = 0;
+ foreach (var (atMs, action) in timeline)
+ {
+ int wait = atMs - currentTime;
+ if (wait > 0)
+ {
+ await Task.Delay(wait);
+ currentTime = atMs;
+ }
+
+ action();
+ }
+
+ await Task.Delay(1000);
+ }
+ catch (TaskCanceledException)
+ {
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Fehler beim Abspielen: {ex.Message}");
+ }
+ }
+
+ private void NoteOn(int noteNumber, int velocity = 100, int channel = -1)
+ {
+ if (_midi == null)
+ {
+ return;
+ }
+
+ var ch = channel > 0 ? channel : _channel;
+ lock (_midiSync)
+ {
+ _midi.Send(MidiMessage.StartNote(noteNumber, velocity, ch).RawData);
+ }
+ }
+
+ private void NoteOff(int noteNumber, int channel = -1)
+ {
+ if (_midi == null)
+ {
+ return;
+ }
+
+ var ch = channel > 0 ? channel : _channel;
+ lock (_midiSync)
+ {
+ _midi.Send(MidiMessage.StartNote(noteNumber, 0, ch).RawData);
+ }
+ }
+
+ private void SetPatch(int program, int channel)
+ {
+ if (_midi == null)
+ {
+ return;
+ }
+
+ lock (_midiSync)
+ {
+ _midi.Send(MidiMessage.ChangePatch(program, channel).RawData);
+ }
+ }
+}
diff --git a/CSharpBible/Games/MidiSwing.MVVM/ViewModels/MainWindowViewModel.cs b/CSharpBible/Games/MidiSwing.MVVM/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..04d16a9fa
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,39 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using MidiSwing.MVVM.Models;
+
+namespace MidiSwing.MVVM.ViewModels;
+
+public partial class MainWindowViewModel : ObservableObject
+{
+ private readonly MusicGenerator _musicGenerator = new MusicGenerator();
+
+ public IAsyncRelayCommand PlayCommand { get; }
+
+ [ObservableProperty]
+ private bool isPlaying;
+
+ public MainWindowViewModel()
+ {
+ PlayCommand = new AsyncRelayCommand(ExecutePlayAsync, CanExecutePlay);
+ }
+
+ private bool CanExecutePlay() => !IsPlaying;
+
+ private async Task ExecutePlayAsync()
+ {
+ IsPlaying = true;
+ await _musicGenerator.PlaySwingMelody();
+ IsPlaying = false;
+ }
+
+ partial void OnIsPlayingChanged(bool value)
+ {
+ PlayCommand.NotifyCanExecuteChanged();
+ }
+}
diff --git a/CSharpBible/Games/MidiSwing.MVVM/Views/Converters/BoolToTextConverter .cs b/CSharpBible/Games/MidiSwing.MVVM/Views/Converters/BoolToTextConverter .cs
new file mode 100644
index 000000000..6694748f9
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/Views/Converters/BoolToTextConverter .cs
@@ -0,0 +1,21 @@
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MidiSwing.MVVM.Views.Converters;
+
+public class BoolToTextConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool isPlaying)
+ {
+ return isPlaying ? "Spielt ab..." : "Melodie abspielen";
+ }
+ return "Melodie abspielen";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/CSharpBible/Games/MidiSwing.MVVM/Views/MainWindow.xaml b/CSharpBible/Games/MidiSwing.MVVM/Views/MainWindow.xaml
new file mode 100644
index 000000000..7a709997d
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/Views/MainWindow.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/MidiSwing.MVVM/Views/MainWindow.xaml.cs b/CSharpBible/Games/MidiSwing.MVVM/Views/MainWindow.xaml.cs
new file mode 100644
index 000000000..270985a12
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/Views/MainWindow.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows;
+
+namespace MidiSwing.MVVM.Views;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/MidiSwing.MVVM/Views/SomeWindow.xaml b/CSharpBible/Games/MidiSwing.MVVM/Views/SomeWindow.xaml
new file mode 100644
index 000000000..90dfb5a9e
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/Views/SomeWindow.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/CSharpBible/Games/MidiSwing.MVVM/Views/SomeWindow.xaml.cs b/CSharpBible/Games/MidiSwing.MVVM/Views/SomeWindow.xaml.cs
new file mode 100644
index 000000000..e5b72fd5e
--- /dev/null
+++ b/CSharpBible/Games/MidiSwing.MVVM/Views/SomeWindow.xaml.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace MidiSwing.MVVM.Views
+{
+ ///
+ /// Interaktionslogik für SomeWindow.xaml
+ ///
+ public partial class SomeWindow : Window
+ {
+ public SomeWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/CSharpBible/Games/Packages.props b/CSharpBible/Games/Packages.props
new file mode 100644
index 000000000..1881a483e
--- /dev/null
+++ b/CSharpBible/Games/Packages.props
@@ -0,0 +1,45 @@
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/Games/Snake.WPF/App.xaml b/CSharpBible/Games/Snake.WPF/App.xaml
new file mode 100644
index 000000000..bf594569b
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/App.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Snake.WPF/App.xaml.cs b/CSharpBible/Games/Snake.WPF/App.xaml.cs
new file mode 100644
index 000000000..40666a8b6
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/App.xaml.cs
@@ -0,0 +1,43 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+using BaseLib.Models;
+using BaseLib.Models.Interfaces;
+using Game.Model;
+using Game.Model.Interfaces;
+using Microsoft.Extensions.DependencyInjection;
+using Snake.WPF.Views;
+using Snake_Base.Models;
+using Snake_Base.Models.Interfaces;
+using Snake_Base.ViewModels;
+
+namespace Snake.WPF
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ public static ServiceProvider Services { get; private set; } = null!;
+
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ base.OnStartup(e);
+ var services = new ServiceCollection();
+ ConfigureServices(services);
+ Services = services.BuildServiceProvider();
+ var mainWindow = Services.GetRequiredService();
+ mainWindow.Show();
+ }
+
+ private void ConfigureServices(IServiceCollection services)
+ {
+ // Register core ViewModel from Snake_Base
+ services.AddSingleton()
+ .AddSingleton,Playfield2D>()
+ .AddSingleton()
+ .AddTransient()
+ .AddTransient();
+ }
+ }
+}
diff --git a/CSharpBible/Games/Snake.WPF/AssemblyInfo.cs b/CSharpBible/Games/Snake.WPF/AssemblyInfo.cs
new file mode 100644
index 000000000..b0ec82757
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/Games/Snake.WPF/Converters/GridPointsConverter.cs b/CSharpBible/Games/Snake.WPF/Converters/GridPointsConverter.cs
new file mode 100644
index 000000000..a5c2f35f3
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/Converters/GridPointsConverter.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace Snake.WPF.Converters
+{
+ public class GridPointsConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is Size s)
+ {
+ var points = new List(s.Width * s.Height);
+ for (int y = 0; y < s.Height; y++)
+ for (int x = 0; x < s.Width; x++)
+ points.Add(new Point(x-1, y-1));
+ return points;
+ }
+ return Array.Empty();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
+ }
+}
diff --git a/CSharpBible/Games/Snake.WPF/Converters/SnakeTileToBrushConverter.cs b/CSharpBible/Games/Snake.WPF/Converters/SnakeTileToBrushConverter.cs
new file mode 100644
index 000000000..84659ee44
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/Converters/SnakeTileToBrushConverter.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Drawing;
+using System.Globalization;
+using System.Windows.Data;
+using System.Windows.Media;
+using Snake_Base.Models.Data;
+using Snake_Base.ViewModels;
+
+namespace Snake.WPF.Converters
+{
+ public class SnakeTileToBrushConverter : IMultiValueConverter
+ {
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (values.Length == 2 && values[0] is Point p && values[1] is ISnakeViewModel.ITileProxy tiles)
+ {
+ var tile = tiles[p];
+ return tile switch
+ {
+ SnakeTiles.Empty => Brushes.Black,
+ SnakeTiles.Wall => Brushes.DimGray,
+ SnakeTiles.Apple => Brushes.OrangeRed,
+ SnakeTiles.SnakeHead_N or SnakeTiles.SnakeHead_S or SnakeTiles.SnakeHead_E or SnakeTiles.SnakeHead_W => Brushes.LimeGreen,
+ SnakeTiles.SnakeTail_N or SnakeTiles.SnakeTail_S or SnakeTiles.SnakeTail_E or SnakeTiles.SnakeTail_W => Brushes.Green,
+ SnakeTiles.SnakeBody_NS or SnakeTiles.SnakeBody_NE or SnakeTiles.SnakeBody_NW or SnakeTiles.SnakeBody_SE or SnakeTiles.SnakeBody_SW or SnakeTiles.SnakeBody_WE => Brushes.ForestGreen,
+ _ => Brushes.Black
+ };
+ }
+ return Brushes.Black;
+ }
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
+ }
+}
diff --git a/CSharpBible/Games/Snake.WPF/Services/GameLoopService.cs b/CSharpBible/Games/Snake.WPF/Services/GameLoopService.cs
new file mode 100644
index 000000000..ff7ebf4f5
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/Services/GameLoopService.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Threading;
+using System.Windows.Threading;
+using Snake_Base.Models.Interfaces;
+using Snake_Base.ViewModels;
+
+namespace Snake.WPF.Services
+{
+ public class GameLoopService
+ {
+ private readonly ISnakeViewModel _game;
+ private readonly Dispatcher _dispatcher;
+ private Timer? _timer;
+
+ public GameLoopService(ISnakeViewModel game, Dispatcher dispatcher)
+ {
+ _game = game;
+ _dispatcher = dispatcher;
+ }
+
+ public void Start()
+ {
+ _game.ResetCommand?.Execute(null);
+ _timer = new Timer(Tick, null, 0, Timeout.Infinite);
+ }
+
+ public void Stop()
+ {
+ _timer?.Dispose();
+ _game.PauseCommand?.Execute(null);
+ }
+
+ private void Tick(object? state)
+ {
+ if (!_game.IsRunning)
+ {
+ _timer?.Dispose();
+ return;
+ }
+ var delay = _game.GameTick();
+ _dispatcher.BeginInvoke(() =>
+ {
+ // UI can react if needed; rendering via bindings
+ });
+ _timer?.Change(delay, Timeout.Infinite);
+ }
+ }
+}
diff --git a/CSharpBible/Games/Snake.WPF/Snake.WPF.csproj b/CSharpBible/Games/Snake.WPF/Snake.WPF.csproj
new file mode 100644
index 000000000..e69f92c4e
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/Snake.WPF.csproj
@@ -0,0 +1,21 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Snake.WPF/Styles/Theme.xaml b/CSharpBible/Games/Snake.WPF/Styles/Theme.xaml
new file mode 100644
index 000000000..9250770bd
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/Styles/Theme.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Snake.WPF/Views/MainWindow.xaml b/CSharpBible/Games/Snake.WPF/Views/MainWindow.xaml
new file mode 100644
index 000000000..5cc33081e
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/Views/MainWindow.xaml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Snake.WPF/Views/MainWindow.xaml.cs b/CSharpBible/Games/Snake.WPF/Views/MainWindow.xaml.cs
new file mode 100644
index 000000000..bc0312947
--- /dev/null
+++ b/CSharpBible/Games/Snake.WPF/Views/MainWindow.xaml.cs
@@ -0,0 +1,44 @@
+using System.Windows;
+using System.Windows.Input;
+using Snake_Base.ViewModels;
+using Snake_Base.Models.Interfaces;
+using Snake.WPF.Services;
+
+namespace Snake.WPF.Views;
+
+public partial class MainWindow : Window
+{
+ private readonly GameLoopService _loop;
+ private readonly ISnakeViewModel _vm;
+
+ public MainWindow(ISnakeViewModel vm)
+ {
+ InitializeComponent();
+ DataContext = vm;
+ _vm = vm;
+ _loop = new GameLoopService(vm, this.Dispatcher);
+ Loaded += (_, __) => { _loop.Start(); this.Focus(); };
+ Closed += (_, __) => _loop.Stop();
+ }
+
+ private void OnKeyDown(object sender, KeyEventArgs e)
+ {
+ _vm.UserAction = e.Key switch
+ {
+ Key.Up => UserAction.GoNorth,
+ Key.Down => UserAction.GoSouth,
+ Key.Left => UserAction.GoWest,
+ Key.Right => UserAction.GoEast,
+ Key.Escape => UserAction.Quit,
+ Key.F1 => UserAction.Help,
+ Key.R => UserAction.Restart,
+ _ => UserAction.Nop
+ };
+ }
+
+ private void OnStartClick(object sender, RoutedEventArgs e)
+ {
+ if (!_vm.IsRunning) _loop.Start();
+ }
+
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/Snake_Base/ViewModels/ISnakeViewModel.cs b/CSharpBible/Games/Snake_Base/ViewModels/ISnakeViewModel.cs
index c3883003d..20b71bc2f 100644
--- a/CSharpBible/Games/Snake_Base/ViewModels/ISnakeViewModel.cs
+++ b/CSharpBible/Games/Snake_Base/ViewModels/ISnakeViewModel.cs
@@ -11,6 +11,7 @@
//
//
// ***********************************************************************
+using CommunityToolkit.Mvvm.Input;
using Snake_Base.Models.Data;
using System;
using System.ComponentModel;
@@ -27,7 +28,10 @@ public interface ITileProxy
UserAction UserAction { get; set; }
ITileProxy Tiles { get; }
-
+ IRelayCommand ResetCommand { get; }
+ IRelayCommand PauseCommand { get; }
+ IRelayCommand StartCommand { get; }
+ Func GameTick { get; }
Func GetOldPos { get; }
Size size { get; }
int Level { get; }
@@ -35,4 +39,5 @@ public interface ITileProxy
int Lives { get; }
int MaxLives { get; }
bool HalfStep { get; }
+ bool IsRunning { get; }
}
\ No newline at end of file
diff --git a/CSharpBible/Games/Snake_Base/ViewModels/SnakeViewModel.cs b/CSharpBible/Games/Snake_Base/ViewModels/SnakeViewModel.cs
index 2a89bad32..0c3ae3f24 100644
--- a/CSharpBible/Games/Snake_Base/ViewModels/SnakeViewModel.cs
+++ b/CSharpBible/Games/Snake_Base/ViewModels/SnakeViewModel.cs
@@ -1,6 +1,7 @@
using System;
using System.Drawing;
using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
using Snake_Base.Models;
using Snake_Base.Models.Data;
using Snake_Base.Models.Interfaces;
@@ -41,9 +42,10 @@ public SnakeTiles this[Point p]
public int Score => _game.Score;
public int Lives => _game.Lives;
public int MaxLives => _game.MaxLives;
+ public bool IsRunning => _game.IsRunning;
public Func GetOldPos { get => _game.GetOldPos; }
-
+ public Func GameTick { get => _game.GameStep; }
public bool HalfStep => _halfStep;
partial void OnUserActionChanged(UserAction value)
@@ -54,4 +56,21 @@ partial void OnUserActionChanged(UserAction value)
_game.UserQuit = true;
}
+ [RelayCommand]
+ private void Reset()
+ {
+ _game.Setup(1);
+ }
+ [RelayCommand]
+ private void Pause()
+ {
+ _game.UserQuit = true;
+ }
+ [RelayCommand]
+ private void Start()
+ {
+ if (!_game.IsRunning)
+ _game.Setup(1);
+ }
+
}
diff --git a/CSharpBible/Games/Snake_BaseTests/Snake_BaseTests.csproj b/CSharpBible/Games/Snake_BaseTests/Snake_BaseTests.csproj
index 4eb11aae2..53471e7f1 100644
--- a/CSharpBible/Games/Snake_BaseTests/Snake_BaseTests.csproj
+++ b/CSharpBible/Games/Snake_BaseTests/Snake_BaseTests.csproj
@@ -1,4 +1,4 @@
-
+
net481;net48;net472;net462;net6.0;net7.0;net8.0;net9.0
@@ -12,8 +12,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Games/Snake_Console/Snake_Console.csproj b/CSharpBible/Games/Snake_Console/Snake_Console.csproj
index bc8e5aa51..2e42e4db3 100644
--- a/CSharpBible/Games/Snake_Console/Snake_Console.csproj
+++ b/CSharpBible/Games/Snake_Console/Snake_Console.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/CSharpBible/Games/Sokoban/Program.cs b/CSharpBible/Games/Sokoban/Program.cs
index a10d6176a..af278d4e9 100644
--- a/CSharpBible/Games/Sokoban/Program.cs
+++ b/CSharpBible/Games/Sokoban/Program.cs
@@ -11,6 +11,7 @@
//
//
// ***********************************************************************
+using Sokoban.Model;
using Sokoban_Base.Model;
using Sokoban_Base.View;
using Sokoban_Base.ViewModels;
@@ -22,6 +23,7 @@ namespace Sokoban
///
public class Program
{
+ static IGame? _SokobanGame;
///
/// Defines the entry point of the application.
///
@@ -38,7 +40,7 @@ public static void Main(string[] args)
///
public static void Cleanup()
{
- Game.Cleanup();
+ _SokobanGame?.Cleanup();
}
///
@@ -47,9 +49,9 @@ public static void Cleanup()
public static void Run()
{
UserAction? direction = null;
- while (direction!=UserAction.Quit && LabDefs.SLevels.Length > Game.level)
+ while (direction!=UserAction.Quit && LabDefs.SLevels.Length > _SokobanGame.level)
{
- direction = Game.Run();
+ direction = _SokobanGame?.Run();
}
}
@@ -59,11 +61,14 @@ public static void Run()
///
public static void Init()
{
- Game.Init();
- Game.visSetMessage = (s) => Visuals.Message = s;
- Game.visShow = Visuals.Show;
- Game.visUpdate = Visuals.Update;
- Game.visGetUserAction = Visuals.WaitforUser;
+ // Setup Visuals
+ _SokobanGame = new Game();
+ Visuals.SokobanGame = _SokobanGame;
+ _SokobanGame.Init();
+ _SokobanGame.visSetMessage = (s) => Visuals.Message = s;
+ _SokobanGame.visShow = Visuals.Show;
+ _SokobanGame.visUpdate = Visuals.Update;
+ _SokobanGame.visGetUserAction = Visuals.WaitforUser;
}
}
diff --git a/CSharpBible/Games/Sokoban/View/Visuals.cs b/CSharpBible/Games/Sokoban/View/Visuals.cs
index 39e2d21a0..880e6caa5 100644
--- a/CSharpBible/Games/Sokoban/View/Visuals.cs
+++ b/CSharpBible/Games/Sokoban/View/Visuals.cs
@@ -14,6 +14,7 @@
using BaseLib.Interfaces;
using BaseLib.Models;
using ConsoleDisplay.View;
+using Sokoban;
using Sokoban.Model;
using Sokoban.Properties;
using Sokoban_Base.ViewModels;
@@ -29,6 +30,7 @@ namespace Sokoban_Base.View
///
public static class Visuals
{
+ public static IGame? SokobanGame { get; set; }
#region Properties
///
@@ -96,11 +98,11 @@ public static void Show(UserAction? uAction=null)
return;
}
- dBuffer = new TileDef[Game.PFSize.Width* Game.PFSize.Height];
+ dBuffer = new TileDef[SokobanGame.PFSize.Width* SokobanGame.PFSize.Height];
var p = new Point();
- for (p.Y = 0; p.Y < Game.PFSize.Height; p.Y++)
- for (p.X = 0; p.X < Game.PFSize.Width; p.X++)
- WriteTile(p, dBuffer[p.X + p.Y * Game.PFSize.Width] = Game.GetTile(p));
+ for (p.Y = 0; p.Y < SokobanGame.PFSize.Height; p.Y++)
+ for (p.X = 0; p.X < SokobanGame.PFSize.Width; p.X++)
+ WriteTile(p, dBuffer[p.X + p.Y * SokobanGame.PFSize.Width] = SokobanGame.GetTile(p));
ShowStatistics();
}
@@ -116,7 +118,7 @@ public static void Update()
myConsole.ForegroundColor = ConsoleColor.Gray;
myConsole.BackgroundColor = ConsoleColor.Black;
- myConsole.SetCursorPosition(40, Game.PFSize.Height * 2 + 7);
+ myConsole.SetCursorPosition(40, SokobanGame.PFSize.Height * 2 + 7);
Thread.Sleep(40);
for (int i = 0; i < 3; i++)
@@ -155,18 +157,18 @@ private static void ShowIntermediateTiles(List<(Point, TileDef, Point)> diffFiel
private static List<(Point, TileDef, Point)> ComputeUpdateList()
{
List<(Point, TileDef, Point)> diffFields = new();
- for (int y = 0; y < Game.PFSize.Height; y++)
+ for (int y = 0; y < SokobanGame.PFSize.Height; y++)
{
- for (int x = 0; x < Game.PFSize.Width; x++)
+ for (int x = 0; x < SokobanGame.PFSize.Width; x++)
{
var p = new Point(x, y);
- var td = Game.GetTile(p);
- if (td != dBuffer[x + y * Game.PFSize.Width])
+ var td = SokobanGame.GetTile(p);
+ if (td != dBuffer[x + y * SokobanGame.PFSize.Width])
{
#pragma warning disable CS8604 // Mögliches Nullverweisargument.
- diffFields.Add((p, td, Game.GetOldPos(p)));
+ diffFields.Add((p, td, SokobanGame.GetOldPos(p)));
#pragma warning restore CS8604 // Mögliches Nullverweisargument.
- dBuffer[x + y * Game.PFSize.Width] = td;
+ dBuffer[x + y * SokobanGame.PFSize.Width] = td;
}
}
}
@@ -182,7 +184,7 @@ private static void ShowIntermediateTiles(List<(Point, TileDef, Point)> diffFiel
public static UserAction? WaitforUser(UserAction? uAction)
{
myConsole.Write(Resource1.SelectAction);
- if (Game.GameSolved || uAction==UserAction.Help || uAction == UserAction.Restart)
+ if (SokobanGame.GameSolved || uAction==UserAction.Help || uAction == UserAction.Restart)
myConsole.Write(Resource1.Continue);
else
foreach (Direction dir in Enum.GetValues(typeof(Direction))) Console.Write($", {MarkFirst(dir.ToString())}");
@@ -236,29 +238,29 @@ private static void ShowStatistics()
{
myConsole.ForegroundColor = ConsoleColor.Gray;
myConsole.BackgroundColor = ConsoleColor.Black;
- myConsole.SetCursorPosition(0, Game.PFSize.Height * 2);
+ myConsole.SetCursorPosition(0, SokobanGame.PFSize.Height * 2);
#if DEBUG
- foreach (var s in Game.Stones)
+ foreach (var s in SokobanGame.Stones)
myConsole.Write($" {Resource1.stone} ({s.Position.X},{s.Position.Y}) {(s.field is Destination ? "OK " : " ")}\t");
#endif
- myConsole.Write($"\r\n" + String.Format(Resource1.StonesInDest, Game.StonesInDest));
+ myConsole.Write($"\r\n" + String.Format(Resource1.StonesInDest, SokobanGame.StonesInDest));
- if (Game.player != null)
+ if (SokobanGame.player != null)
{
- myConsole.SetCursorPosition(0, Game.PFSize.Height * 2 + 3);
+ myConsole.SetCursorPosition(0, SokobanGame.PFSize.Height * 2 + 3);
#if DEBUG
- Console.WriteLine($"Player ({Game.player.Position.X},{Game.player.Position.Y})");
+ Console.WriteLine($"Player ({SokobanGame.player.Position.X},{SokobanGame.player.Position.Y})");
Console.Write(Resource1.PosibMoves);
- foreach (var d in Game.player.MoveableDirs())
+ foreach (var d in SokobanGame.player.MoveableDirs())
{
Console.Write($"\t{typeof(Direction).GetEnumName(d)},");
}
Console.WriteLine(" ");
#endif
- myConsole.SetCursorPosition(0, Game.PFSize.Height * 2 + 6);
+ myConsole.SetCursorPosition(0, SokobanGame.PFSize.Height * 2 + 6);
myConsole.Write(Message + " ");
- myConsole.SetCursorPosition(0, Game.PFSize.Height * 2 + 7);
+ myConsole.SetCursorPosition(0, SokobanGame.PFSize.Height * 2 + 7);
}
}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Destination.cs b/CSharpBible/Games/Sokoban_Base/Model/Destination.cs
new file mode 100644
index 000000000..9d122b4b0
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/Model/Destination.cs
@@ -0,0 +1,49 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 07-09-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Sokoban.Model.Interfaces;
+using System;
+using System.Drawing;
+
+namespace Sokoban.Model;
+
+///
+/// Class Destination.
+/// Implements the
+///
+///
+public class Destination : Floor
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The position.
+ /// The parent play object.
+ public Destination(Point position, IPlayfield parentPlayObject) : base(position, parentPlayObject)
+ {
+ }
+
+ ///
+ /// Gets the field definition.
+ ///
+ /// FieldDef.
+ /// Illegal Item
+ protected override FieldDef GetFieldDef() => Item switch
+ {
+ Stone s => FieldDef.StoneInDest,
+ Player s => FieldDef.PlayerOverDest,
+ { } => throw new ArgumentException("Illegal Item"),
+ null => FieldDef.Destination
+ };
+
+}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Field.cs b/CSharpBible/Games/Sokoban_Base/Model/Field.cs
index abd690369..2f84164ba 100644
--- a/CSharpBible/Games/Sokoban_Base/Model/Field.cs
+++ b/CSharpBible/Games/Sokoban_Base/Model/Field.cs
@@ -11,192 +11,62 @@
//
//
// ***********************************************************************
-using Sokoban_Base.Model;
-using System;
+using Sokoban.Model.Interfaces;
using System.Drawing;
-namespace Sokoban.Model
+namespace Sokoban.Model;
+
+///
+/// Class Field.
+///
+public abstract class Field : IField
{
///
- /// Class Field.
+ /// The item
///
- public abstract class Field
- {
- ///
- /// The item
- ///
- protected PlayObject? _item=null;
-
- ///
- /// Gets the position.
- ///
- /// The position.
- public Point Position { get;private set; }
- ///
- /// Gets or sets the item.
- ///
- /// The item.
- public PlayObject? Item { get=>_item; set =>SetItem(value); }
-
- ///
- /// Gets the parent.
- ///
- /// The parent.
- public Playfield? Parent { get; private set; }
- ///
- /// Gets the field definition.
- ///
- /// The field definition.
- public FieldDef fieldDef { get=>GetFieldDef(); }
-
- ///
- /// Gets the field definition.
- ///
- /// FieldDef.
- protected abstract FieldDef GetFieldDef();
- ///
- /// Sets the item.
- ///
- /// The value.
- protected abstract void SetItem(PlayObject? value);
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The position.
- /// The parent play object.
- public Field(Point position, Playfield? parentPlayObject )
- {
- Position = position;
- Parent = parentPlayObject;
- }
- }
+ protected IPlayObject? _item = null;
///
- /// Class Wall.
- /// Implements the
+ /// Gets the position.
///
- ///
- public class Wall : Field
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The position.
- /// The parent play object.
- public Wall(Point position, Playfield? parentPlayObject) : base(position, parentPlayObject)
- {
- }
-
- ///
- /// Gets the field definition.
- ///
- /// FieldDef.
- protected override FieldDef GetFieldDef() => FieldDef.Wall;
- ///
- /// Sets the item.
- ///
- /// The value.
- ///
- protected override void SetItem(PlayObject? value)
- {
- // a Wall cannot contain an object
- if (value != null)
- throw new ArgumentException();
- }
- }
-
+ /// The position.
+ public Point Position { get; private set; }
///
- /// Class Floor.
- /// Implements the
+ /// Gets or sets the item.
///
- ///
- public class Floor: Field
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The position.
- /// The parent play object.
- public Floor(Point position, Playfield? parentPlayObject) : base(position, parentPlayObject)
- {
- }
+ /// The item.
+ public IPlayObject? Item { get => _item; set => SetItem(value); }
- ///
- /// Gets the field definition.
- ///
- /// FieldDef.
- /// Illegal Item
- protected override FieldDef GetFieldDef() => Item switch
- {
- Stone => FieldDef.Stone,
- Player => FieldDef.Player,
- { } => throw new ArgumentException("Illegal Item"),
- null => FieldDef.Floor
- };
+ ///
+ /// Gets the parent.
+ ///
+ /// The parent.
+ public IPlayfield? Parent { get; private set; }
+ ///
+ /// Gets the field definition.
+ ///
+ /// The field definition.
+ public FieldDef fieldDef { get => GetFieldDef(); }
- ///
- /// Sets the item.
- ///
- /// The value.
- protected override void SetItem(PlayObject? value)
- {
- if (value == Item) return;
- if (Item != null && value !=null) return; // Cannot contain 2 Objects
- if (value == null)
- {
- if (Item != null)
- {
- Item.OldPosition = Item.Position;
- Item.Position = new Point(0, 0);
- Item.field = null;
- _item = null;
- }
- }
- else
- {
- if (value.field != this && value.field != null)
- {
- var f = value.field;
- f.Item = null;
- }
-// value.OldPosition = value.Position;
- value.Position = Position;
- value.field = this;
- _item = value;
- }
-
- }
- }
+ ///
+ /// Gets the field definition.
+ ///
+ /// FieldDef.
+ protected abstract FieldDef GetFieldDef();
+ ///
+ /// Sets the item.
+ ///
+ /// The value.
+ protected abstract void SetItem(IPlayObject? value);
///
- /// Class Destination.
- /// Implements the
+ /// Initializes a new instance of the class.
///
- ///
- public class Destination : Floor
+ /// The position.
+ /// The parent play object.
+ public Field(Point position, IPlayfield? parentPlayObject)
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The position.
- /// The parent play object.
- public Destination(Point position, Playfield parentPlayObject) : base(position, parentPlayObject)
- {
- }
-
- ///
- /// Gets the field definition.
- ///
- /// FieldDef.
- /// Illegal Item
- protected override FieldDef GetFieldDef() => Item switch
- {
- Stone s => FieldDef.StoneInDest,
- Player s => FieldDef.PlayerOverDest,
- { } => throw new ArgumentException("Illegal Item"),
- null => FieldDef.Destination
- };
-
+ Position = position;
+ Parent = parentPlayObject;
}
}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Floor.cs b/CSharpBible/Games/Sokoban_Base/Model/Floor.cs
new file mode 100644
index 000000000..188fd1c9a
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/Model/Floor.cs
@@ -0,0 +1,81 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 07-09-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Sokoban.Model.Interfaces;
+using System;
+using System.Drawing;
+
+namespace Sokoban.Model;
+
+///
+/// Class Floor.
+/// Implements the
+///
+///
+public class Floor: Field
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The position.
+ /// The parent play object.
+ public Floor(Point position, IPlayfield? parentPlayObject) : base(position, parentPlayObject)
+ {
+ }
+
+ ///
+ /// Gets the field definition.
+ ///
+ /// FieldDef.
+ /// Illegal Item
+ protected override FieldDef GetFieldDef() => Item switch
+ {
+ Stone => FieldDef.Stone,
+ Player => FieldDef.Player,
+ { } => throw new ArgumentException("Illegal Item"),
+ null => FieldDef.Floor
+ };
+
+ ///
+ /// Sets the item.
+ ///
+ /// The value.
+ protected override void SetItem(IPlayObject? value)
+ {
+ if (value == Item) return;
+ if (Item != null && value !=null) return; // Cannot contain 2 Objects
+ if (value == null)
+ {
+ if (Item != null)
+ {
+ Item.OldPosition = Item.Position;
+ Item.Position = new Point(0, 0);
+ Item.field = null;
+ _item = null;
+ }
+ }
+ else
+ {
+ if (value.field != this && value.field != null)
+ {
+ var f = value.field;
+ f.Item = null;
+ }
+// value.OldPosition = value.Position;
+ value.Position = Position;
+ value.field = this;
+ _item = value;
+ }
+
+ }
+}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Game.cs b/CSharpBible/Games/Sokoban_Base/Model/Game.cs
new file mode 100644
index 000000000..0cc66c76f
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/Model/Game.cs
@@ -0,0 +1,233 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 08-04-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Sokoban_Base.Model;
+using Sokoban.Properties;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using Sokoban_Base.ViewModels;
+using Sokoban.Model.Interfaces;
+
+namespace Sokoban.Model;
+
+///
+/// Class Game.
+///
+public class Game : IGame
+{
+
+ ///
+ /// The vis update
+ ///
+ public Action? visUpdate { get; set; }
+
+ ///
+ /// The vis get user action
+ ///
+ public Func? visGetUserAction { get; set; }
+
+ ///
+ /// The vis set message
+ ///
+ public Action? visSetMessage { get; set; }
+
+ ///
+ /// Gets or sets the vis show.
+ ///
+ /// The vis show.
+ public Action? visShow { get ; set ; }
+
+ ///
+ /// The u action
+ ///
+ private UserAction? uAction = null;
+
+ ///
+ /// The playfield
+ ///
+ public IPlayfield playfield = new Playfield();
+
+ ///
+ /// Gets the player.
+ ///
+ /// The player.
+ public IPlayer? player => playfield.player;
+
+ ///
+ /// Gets the size of the pf.
+ ///
+ /// The size of the pf.
+ public Size PFSize => playfield.fieldSize;
+
+ ///
+ /// Gets the stones in dest.
+ ///
+ /// The stones in dest.
+ public int StonesInDest => playfield.StonesInDest;
+
+ ///
+ /// Gets the stones.
+ ///
+ /// The stones.
+ public IEnumerable Stones => playfield.Stones;
+
+ ///
+ /// Gets a value indicating whether [game solved].
+ ///
+ /// true if [game solved]; otherwise, false.
+ public bool GameSolved => playfield.GameSolved;
+
+ ///
+ /// Gets the level.
+ ///
+ /// The level.
+ public int level { get; private set; }
+
+ ///
+ /// Initializes this instance.
+ ///
+ public void Init()
+ {
+ level = 0;
+ }
+
+ ///
+ /// Runs this instance.
+ ///
+ /// System.Nullable<UserAction>.
+ public UserAction? Run()
+ {
+ playfield.Setup(LabDefs.SLevels[level]);
+
+ visShow?.Invoke(uAction);
+
+ while (uAction != UserAction.Quit && !playfield.GameSolved && uAction != UserAction.Restart)
+ {
+ uAction = visGetUserAction?.Invoke(uAction);
+ if (uAction != null && (int?)uAction < typeof(Direction).GetEnumValues().Length)
+ {
+ if (!playfield.player?.Go((Direction)uAction) ?? false)
+ {
+ visSetMessage?.Invoke(string.Format(Resource1.CannotMoveMsg, (Direction)uAction));
+ }
+ else
+ {
+ visSetMessage?.Invoke(string.Format(Resource1.OK, (Direction)uAction));
+ }
+ }
+
+ if (uAction == UserAction.Help)
+ {
+ visShow?.Invoke(uAction);
+ uAction = visGetUserAction?.Invoke(uAction);
+ visShow?.Invoke(uAction);
+ }
+ else
+ {
+ visUpdate?.Invoke();
+ }
+ }
+
+ if (playfield.GameSolved)
+ {
+ visSetMessage?.Invoke(string.Format(Resource1.GameSuccess, ""));
+ }
+ else if (uAction == UserAction.Quit)
+ {
+ visSetMessage?.Invoke(string.Format(Resource1.EndMessage, ""));
+ }
+ else if (uAction == UserAction.Restart)
+ {
+ visSetMessage?.Invoke(string.Format(Resource1.RestartMessage, ""));
+ }
+
+ visShow?.Invoke(uAction);
+
+ if (uAction != UserAction.Restart)
+ {
+ level++;
+ }
+
+ if (uAction != UserAction.Quit)
+ {
+ uAction = visGetUserAction?.Invoke(uAction);
+ }
+
+ return uAction;
+ }
+
+ ///
+ /// Cleanups this instance.
+ ///
+ public void Cleanup()
+ {
+ playfield.Clear();
+ }
+
+ ///
+ /// Gets the tile.
+ ///
+ /// The p.
+ /// TileDef.
+ public TileDef GetTile(Point p)
+ {
+ TileDef result = TileDef.Empty;
+ var f = playfield[p]?.fieldDef;
+ if (f <= FieldDef.StoneInDest && f != FieldDef.Wall && f != FieldDef.Player)
+ {
+ result = (TileDef)f;
+ }
+ else if (f == FieldDef.Player && playfield[p]?.Item is Player pl)
+ {
+ result = pl.LastDir switch
+ {
+ Direction.East => TileDef.Player_E,
+ Direction.South => TileDef.Player_S,
+ Direction.West => TileDef.Player_W,
+ _ => TileDef.Player
+ };
+ }
+ else if (f == FieldDef.Wall)
+ {
+ int w = 0;
+ result = TileDef.Wall;
+ w += (playfield[Offsets.DirOffset(Direction.North, p)] is Wall) ? 1 : 0;
+ w += (playfield[Offsets.DirOffset(Direction.West, p)] is Wall) ? 2 : 0;
+ w += (playfield[Offsets.DirOffset(Direction.South, p)] is Wall) ? 4 : 0;
+ w += (playfield[Offsets.DirOffset(Direction.East, p)] is Wall) ? 8 : 0;
+ if (w > 0)
+ {
+ result = (TileDef)(w - 1 + (int)TileDef.Wall_N);
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Gets the old position.
+ ///
+ /// The p.
+ /// Point.
+ public Point GetOldPos(Point p)
+ {
+ Point result = new Point(p.X, p.Y);
+ if (playfield[p] is Floor f && f.Item != null)
+ {
+ result = f.Item.OldPosition;
+ }
+
+ return result;
+ }
+}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IField.cs b/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IField.cs
new file mode 100644
index 000000000..86adaa3e6
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IField.cs
@@ -0,0 +1,25 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 07-09-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Drawing;
+
+namespace Sokoban.Model.Interfaces
+{
+ public interface IField
+ {
+ FieldDef fieldDef { get; }
+ IPlayObject? Item { get; set; }
+ IPlayfield? Parent { get; }
+ Point Position { get; }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayObject.cs b/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayObject.cs
new file mode 100644
index 000000000..3b8d62c01
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayObject.cs
@@ -0,0 +1,27 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 07-09-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Drawing;
+
+namespace Sokoban.Model.Interfaces
+{
+ public interface IPlayObject
+ {
+ IField? field { get; set; }
+ Point OldPosition { get; set; }
+ Point Position { get; set; }
+
+ bool TestMove(Direction dir);
+ bool TryMove(Direction dir);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayer.cs b/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayer.cs
new file mode 100644
index 000000000..e49251962
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayer.cs
@@ -0,0 +1,26 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 07-09-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Sokoban.Model;
+using System.Collections.Generic;
+
+namespace Sokoban.Model.Interfaces
+{
+ public interface IPlayer: IPlayObject
+ {
+ Direction? LastDir { get; set; }
+
+ bool Go(Direction? aDir);
+ IEnumerable MoveableDirs();
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayfield.cs b/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayfield.cs
new file mode 100644
index 000000000..0d3869e4d
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/Model/Interfaces/IPlayfield.cs
@@ -0,0 +1,35 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 07-09-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Sokoban_Base.Model;
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace Sokoban.Model.Interfaces
+{
+ public interface IPlayfield
+ {
+ IField? this[Point p] { get; set; }
+
+ bool GameSolved { get; }
+ int StonesInDest { get; }
+ IPlayer? player { get; }
+ Size fieldSize { get; }
+ IList Stones { get; }
+
+ void Clear();
+ void Setup((FieldDef[], Size) SFDef);
+ void Setup(string[] SFDef);
+ FieldDef VField(Point p, IList moves);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Model.cd b/CSharpBible/Games/Sokoban_Base/Model/Model.cd
index bde0c39e5..cf5f96340 100644
--- a/CSharpBible/Games/Sokoban_Base/Model/Model.cd
+++ b/CSharpBible/Games/Sokoban_Base/Model/Model.cd
@@ -1,39 +1,17 @@
-
-
+
+
AAABEAAAAAAABAAgAAAAQAAEABAAAAAAAAAAAAAAAAA=
Model\Field.cs
-
-
+
-
-
-
- AAAAEAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- Model\Field.cs
-
-
-
-
-
- AAAAEAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- Model\Field.cs
-
-
-
-
-
- AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- Model\Field.cs
-
-
-
+
AACAACAAAAAAAAAgAAAAAAAAAAEAAAAAAAAAAAAAAgA=
@@ -42,36 +20,38 @@
+
-
+
AAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgA=
Model\Stone.cs
-
+
- AAAQACAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAgA=
+ AAAQACAAAAAAAAAAAAAAAAAAEgAAAAAAAAAAAAAAAgA=
Model\Player.cs
+
-
+
- AACAAAAACAABkAgACACAAAAQAAAAAAABEAAAgABAAAA=
+ AACAAAAACAABEAgACQCAAAAQAAAAAAABEgAAgABAAAA=
Model\Playfield.cs
-
+
-
-
+
+
-
+
AAAAAAAAAAAAAAAAAAEAAAAAAACAAAAAAAAAAAAAAAA=
@@ -81,18 +61,60 @@
- AAAAAAAAAAAAAAAAAAgAAAAAAAAAAAIBAAAAAAAAAAA=
+ AAAAAAAAAAAAAAAAAAAAAAQBAAAAAAIAAAAAAAAAAAA=
Model\LabDefs.cs
-
-
+
+
+
+ AAAAEAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ Model\Floor.cs
+
+
+
+
+
+ AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ Model\Destination.cs
+
+
+
+
+
+ AAAAEAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ Model\Wall.cs
+
+
+
+
+
+ AACAACAAAAAAAAAgAAAAAAAAAAEAAAAAAAAAAAAAAgA=
+ Model\Interfaces\IPlayObject.cs
+
+
+
+
+
+ AAAQAAAAAAAAAAAAAAAAAAAAEgAAAAAAAAAAAAAAAAA=
+ Model\Interfaces\IPlayer.cs
+
+
+
+
+
+ AAABAAAAAAAAAAAgAAAAQAAEAAAAAAAAAAAAAAAAAAA=
+ Model\Interfaces\IField.cs
+
+
+
+
AAAQAAAAAAAAAAgAAAIAAAAAAAAAAAQEkAAAAQAAAAA=
Model\FieldDef.cs
-
+
AACAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAABAAQ=
diff --git a/CSharpBible/Games/Sokoban_Base/Model/PlayObject.cs b/CSharpBible/Games/Sokoban_Base/Model/PlayObject.cs
index acbb43540..c084fd989 100644
--- a/CSharpBible/Games/Sokoban_Base/Model/PlayObject.cs
+++ b/CSharpBible/Games/Sokoban_Base/Model/PlayObject.cs
@@ -11,52 +11,51 @@
//
//
// ***********************************************************************
-using Sokoban.Model;
+using Sokoban.Model.Interfaces;
using System.Drawing;
-namespace Sokoban_Base.Model
+namespace Sokoban.Model;
+
+///
+/// Class PlayObject.
+///
+public abstract class PlayObject : IPlayObject
{
+
///
- /// Class PlayObject.
+ /// Gets or sets the position.
///
- public abstract class PlayObject
- {
-
- ///
- /// Gets or sets the position.
- ///
- /// The position of the player on the playfield
- public Point Position { get; set; }
- ///
- /// Gets or sets the old position.
- ///
- /// The old position.
- public Point OldPosition { get; set; }
+ /// The position of the player on the playfield
+ public Point Position { get; set; }
+ ///
+ /// Gets or sets the old position.
+ ///
+ /// The old position.
+ public Point OldPosition { get; set; }
- ///
- /// Gets or sets the field.
- ///
- /// The field as reference.
- public Field? field { get; set; }
+ ///
+ /// Gets or sets the field.
+ ///
+ /// The field as reference.
+ public IField? field { get; set; }
- ///
- /// Tests if the object can move in the given direction.
- ///
- /// The directon to test
- /// true: if the object can move in the direction
- public abstract bool TestMove(Direction dir);
+ ///
+ /// Tests if the object can move in the given direction.
+ ///
+ /// The directon to test
+ /// true: if the object can move in the direction
+ public abstract bool TestMove(Direction dir);
- ///
- /// Tries to move the object in the given direction.
- ///
- /// The directon to move
- /// true: if the object has moveed in the direction
- public abstract bool TryMove(Direction dir);
+ ///
+ /// Tries to move the object in the given direction.
+ ///
+ /// The directon to move
+ /// true: if the object has moveed in the direction
+ public abstract bool TryMove(Direction dir);
- ///
- /// Initializes a new instance of the class.
- ///
- /// a field.
- public PlayObject(Field? aField) { field = aField; }
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// a field.
+ public PlayObject(IField? aField) { field = aField; }
}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Player.cs b/CSharpBible/Games/Sokoban_Base/Model/Player.cs
index 3cba45ad0..9dbc6e71c 100644
--- a/CSharpBible/Games/Sokoban_Base/Model/Player.cs
+++ b/CSharpBible/Games/Sokoban_Base/Model/Player.cs
@@ -11,84 +11,87 @@
//
//
// ***********************************************************************
-using Sokoban.Model;
+using Sokoban.Model.Interfaces;
using System;
using System.Collections.Generic;
-namespace Sokoban_Base.Model
+namespace Sokoban.Model;
+
+///
+/// Class Player.
+/// Implements the
+///
+///
+public class Player : PlayObject, IPlayer
{
///
- /// Class Player.
- /// Implements the
+ /// Gets or sets the last dir.
///
- ///
- public class Player : PlayObject
- {
- ///
- /// Gets or sets the last dir.
- ///
- /// The last dir.
- public Direction? LastDir { get; set; } = Direction.East;
+ /// The last dir.
+ public Direction? LastDir { get; set; } = Direction.East;
- ///
- /// Initializes a new instance of the class.
- ///
- /// a field.
- public Player(Field? aField) : base(aField)
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// a field.
+ public Player(IField? aField) : base(aField)
+ {
+ }
- ///
- /// Goes the specified a dir.
- ///
- /// a dir.
- /// true if XXXX, false otherwise.
- public bool Go(Direction? aDir) {
- if (aDir == null ) return false;
- else
- return TryMove((Direction)aDir);
- }
+ ///
+ /// Goes the specified a dir.
+ ///
+ /// a dir.
+ /// true if XXXX, false otherwise.
+ public bool Go(Direction? aDir)
+ {
+ if (aDir == null) return false;
+ else
+ return TryMove((Direction)aDir);
+ }
- ///
- /// Tests if the object can move in the given direction.
- ///
- /// The directon to test
- /// true: if the object can move in the direction
- public override bool TestMove(Direction dir)
+ ///
+ /// Tests if the object can move in the given direction.
+ ///
+ /// The directon to test
+ /// true: if the object can move in the direction
+ public override bool TestMove(Direction dir)
+ {
+ var newfield = field?.Parent?[Offsets.DirOffset(dir, Position)];
+ if (newfield == null || !(newfield is Floor f)) return false;
+ else
{
- var newfield = field?.Parent?[Offsets.DirOffset(dir,Position)];
- if (newfield == null || !(newfield is Floor f)) return false;else
- {
- if (f.Item == null ) return true;else
- return f.Item is Stone && f.Item.TestMove(dir);
- }
+ if (f.Item == null) return true;
+ else
+ return f.Item is Stone && f.Item.TestMove(dir);
}
+ }
- ///
- /// Tries to move the object in the given direction.
- ///
- /// The directon to move
- /// true: if the object has moveed in the direction
- public override bool TryMove(Direction dir)
+ ///
+ /// Tries to move the object in the given direction.
+ ///
+ /// The directon to move
+ /// true: if the object has moveed in the direction
+ public override bool TryMove(Direction dir)
+ {
+ var newfield = field?.Parent?[Offsets.DirOffset(dir, Position)];
+ if (newfield == null || !(newfield is Floor f)) return false;
+ else
{
- var newfield = field?.Parent?[Offsets.DirOffset(dir, Position)];
- if (newfield == null || !(newfield is Floor f)) return false;
+ if (f.Item == null) { f.Item = this; LastDir = dir; return true; }
else
- {
- if (f.Item == null) { f.Item = this; LastDir = dir; return true; }
- else
- if (f.Item.TryMove(dir)) { f.Item = this; LastDir = dir; return true; } else return false;
- }
+ if (f.Item.TryMove(dir)) { f.Item = this; LastDir = dir; return true; } else return false;
}
+ }
- ///
- /// Moveables the dirs.
- ///
- /// IEnumerable<Direction>.
- public IEnumerable MoveableDirs() {
- foreach (Direction dir in Enum.GetValues(typeof(Direction)))
- if (TestMove(dir)) yield return dir;
- yield break;
- }
+ ///
+ /// Moveables the dirs.
+ ///
+ /// IEnumerable<Direction>.
+ public IEnumerable MoveableDirs()
+ {
+ foreach (Direction dir in Enum.GetValues(typeof(Direction)))
+ if (TestMove(dir)) yield return dir;
+ yield break;
}
}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Playfield.cs b/CSharpBible/Games/Sokoban_Base/Model/Playfield.cs
index d6121d3af..d1d72a692 100644
--- a/CSharpBible/Games/Sokoban_Base/Model/Playfield.cs
+++ b/CSharpBible/Games/Sokoban_Base/Model/Playfield.cs
@@ -11,230 +11,233 @@
//
//
// ***********************************************************************
-using Sokoban.Model;
+using Sokoban.Model.Interfaces;
using System;
using System.Collections.Generic;
using System.Drawing;
+using System.Linq;
-namespace Sokoban_Base.Model
+namespace Sokoban.Model;
+
+///
+/// Struct MoveField
+///
+public struct MoveField
{
///
- /// Struct MoveField
+ /// The p
///
- public struct MoveField
- {
- ///
- /// The p
- ///
- public Point p;
- ///
- /// The n definition
- ///
- public FieldDef nDef;
- }
+ public Point p;
+ ///
+ /// The n definition
+ ///
+ public FieldDef nDef;
+}
+///
+/// Struct Move
+///
+public struct Move
+{
///
- /// Struct Move
+ /// The d
///
- public struct Move
- {
- ///
- /// The d
- ///
- public Direction d;
- ///
- /// The mf
- ///
- public MoveField[] mf;
- }
+ public Direction d;
+ ///
+ /// The mf
+ ///
+ public MoveField[] mf;
+}
+///
+/// Class Playfield.
+///
+public class Playfield : IPlayfield
+{
///
- /// Class Playfield.
+ /// Gets or sets the with the specified p.
///
- public class Playfield
+ /// The p.
+ /// System.Nullable<Field>.
+ public IField? this[Point p]
{
- ///
- /// Gets or sets the with the specified p.
- ///
- /// The p.
- /// System.Nullable<Field>.
- public Field? this[Point p]
+ get => fields.TryGetValue((p.X, p.Y), out var v) ? v : null; set
{
- get => fields.TryGetValue((p.X, p.Y), out var v) ? v : null; set
- {
- if (value != null) fields[(p.X, p.Y)] = value;
- }
+ if (value != null) fields[(p.X, p.Y)] = value;
}
+ }
- ///
- /// vs the field.
- ///
- /// The p.
- /// The moves.
- /// FieldDef.
- public FieldDef VField(Point p,List moves)
- {
- for (var i = moves.Count - 1; i >= 0; i--)
- foreach (var m in moves[i].mf)
- if (m.p == p) return m.nDef;
- return this[p]?.fieldDef ?? FieldDef.Empty;
- }
+ ///
+ /// vs the field.
+ ///
+ /// The p.
+ /// The moves.
+ /// FieldDef.
+ public FieldDef VField(Point p, IList moves)
+ {
+ for (var i = moves.Count - 1; i >= 0; i--)
+ foreach (var m in moves[i].mf)
+ if (m.p == p) return m.nDef;
+ return this[p]?.fieldDef ?? FieldDef.Empty;
+ }
- ///
- /// The fields
- ///
- Dictionary<(int,int),Field> fields = new Dictionary<(int, int), Field>();
- ///
- /// The field size
- ///
- public Size fieldSize;
- ///
- /// The stones
- ///
- public List Stones=new() { };
- ///
- /// The fields
- ///
- private List _fields = new() { };
- ///
- /// The player
- ///
- public Player? player;
+ ///
+ /// The fields
+ ///
+ private Dictionary<(int, int), IField> fields = [];
+ ///
+ /// The field size
+ ///
+ public Size fieldSize { get; private set; } = Size.Empty;
+ ///
+ /// The stones
+ ///
+ public IList Stones { get; private set; } = [];
+ ///
+ /// The fields
+ ///
+ private List _fields = [];
+ ///
+ /// The player
+ ///
+ public IPlayer? player { get; private set; }
- ///
- /// Gets the stones in dest.
- ///
- /// The stones in dest.
- public int StonesInDest { get => Stones.FindAll((s) => s.field is Destination).Count; }
- ///
- /// Gets a value indicating whether [game solved].
- ///
- /// true if [game solved]; otherwise, false.
- public bool GameSolved { get => Stones.Count==StonesInDest; }
+ ///
+ /// Gets the stones in dest.
+ ///
+ /// The stones in dest.
+ public int StonesInDest { get => Stones.Where((s) => s.field is Destination).Count(); }
+ ///
+ /// Gets a value indicating whether [game solved].
+ ///
+ /// true if [game solved]; otherwise, false.
+ public bool GameSolved { get => Stones.Count == StonesInDest; }
- ///
- /// Setups the specified sf definition.
- ///
- /// The sf definition.
- public void Setup(string[] SFDef)
+ ///
+ /// Setups the specified sf definition.
+ ///
+ /// The sf definition.
+ public void Setup(string[] SFDef)
+ {
+ Clear();
+ var fs = fieldSize;
+ fs.Height = SFDef.Length;
+ var lnr = 0;
+ foreach (var line in SFDef)
{
- Clear();
- fieldSize.Height = SFDef.Length;
- var lnr = 0;
- foreach(var line in SFDef)
+ fs.Width = Math.Max(fs.Width, line.Length);
+ for (int x = 0; x < line.Length; x++)
{
- fieldSize.Width = Math.Max(fieldSize.Width, line.Length);
- for (int x = 0;x < line.Length; x++)
- {
- CreateField(FieldDefs.SDef[line[x]],x,lnr);
- }
- lnr++;
+ CreateField(FieldDefs.SDef[line[x]], x, lnr);
}
+ lnr++;
}
+ fieldSize = fs;
+ }
- ///
- /// Setups the specified sf definition.
- ///
- /// The sf definition.
- public void Setup((FieldDef[],Size) SFDef)
+ ///
+ /// Setups the specified sf definition.
+ ///
+ /// The sf definition.
+ public void Setup((FieldDef[], Size) SFDef)
+ {
+ Clear();
+ fieldSize = SFDef.Item2;
+ var lnr = 0;
+ foreach (var fd in SFDef.Item1)
{
- Clear();
- fieldSize = SFDef.Item2;
- var lnr = 0;
- foreach (var fd in SFDef.Item1)
- {
- CreateField(fd, lnr % fieldSize.Width, lnr/fieldSize.Width);
- lnr++;
- }
+ CreateField(fd, lnr % fieldSize.Width, lnr / fieldSize.Width);
+ lnr++;
}
+ }
- ///
- /// Creates the field.
- ///
- /// The field definition.
- /// The x.
- /// The y.
- /// System.Nullable<Field>.
- private Field? CreateField(FieldDef fieldDef, int x, int y)
+ ///
+ /// Creates the field.
+ ///
+ /// The field definition.
+ /// The x.
+ /// The y.
+ /// System.Nullable<Field>.
+ private Field? CreateField(FieldDef fieldDef, int x, int y)
+ {
+ Field? result = null;
+ switch (fieldDef)
+ {
+ case FieldDef.Empty:
+ break;
+ case FieldDef.Wall:
+ result = new Wall(new Point(x, y), this);
+ break;
+ case FieldDef.Floor:
+ if ((x > 0 || y > 0) && this[Offsets.DirOffset(Direction.North, new Point(x, y))] != null && this[Offsets.DirOffset(Direction.West, new Point(x, y))] != null)
+ result = new Floor(new Point(x, y), this);
+ break;
+ case FieldDef.Player:
+ case FieldDef.Stone:
+ result = new Floor(new Point(x, y), this);
+ break;
+ case FieldDef.Destination:
+ case FieldDef.PlayerOverDest:
+ case FieldDef.StoneInDest:
+ result = new Destination(new Point(x, y), this);
+ break;
+ }
+ if (result != null)
{
- Field? result = null;
+ _fields.Add(result);
+ fields[(x, y)] = result;
switch (fieldDef)
{
- case FieldDef.Empty:
- break;
- case FieldDef.Wall:
- result = new Wall(new Point(x, y),this) ;
- break;
- case FieldDef.Floor:
- if ((x>0 || y>0) && this[Offsets.DirOffset(Direction.North,new Point(x, y))]!=null && this[Offsets.DirOffset(Direction.West, new Point(x, y))] != null)
- result = new Floor(new Point(x, y), this);
- break;
case FieldDef.Player:
- case FieldDef.Stone:
- result = new Floor(new Point(x, y), this) ;
- break;
- case FieldDef.Destination:
case FieldDef.PlayerOverDest:
- case FieldDef.StoneInDest:
- result = new Destination(new Point(x, y), this);
+ result.Item = CreatePLayer();
break;
- }
- if (result != null)
- {
- _fields.Add(result);
- fields[(x, y)] = result;
- switch (fieldDef)
- {
- case FieldDef.Player:
- case FieldDef.PlayerOverDest:
- result.Item = CreatePLayer();
- break;
- case FieldDef.Stone:
- case FieldDef.StoneInDest:
- result.Item = CreateStone();
- break;
- }
+ case FieldDef.Stone:
+ case FieldDef.StoneInDest:
+ result.Item = CreateStone();
+ break;
}
- return result;
}
+ return result;
+ }
- ///
- /// Creates the stone.
- ///
- /// Stone.
- private Stone CreateStone()
- {
- Stone result = new Stone(null);
- Stones.Add(result);
- return result;
- }
+ ///
+ /// Creates the stone.
+ ///
+ /// Stone.
+ private IPlayObject CreateStone()
+ {
+ Stone result = new Stone(null);
+ Stones.Add(result);
+ return result;
+ }
- ///
- /// Creates the p layer.
- ///
- /// Player.
- private Player CreatePLayer()
- {
- if (player != null) return player;
- player = new Player(null);
- return player;
- }
+ ///
+ /// Creates the p layer.
+ ///
+ /// Player.
+ private IPlayObject CreatePLayer()
+ {
+ if (player != null) return player;
+ player = new Player(null);
+ return player;
+ }
- ///
- /// Clears this instance.
- ///
- public void Clear()
+ ///
+ /// Clears this instance.
+ ///
+ public void Clear()
+ {
+ fields.Clear();
+ foreach (var f in _fields)
{
- fields.Clear();
- foreach (var f in _fields)
- {
- f.Item = null;
- };
- _fields.Clear();
- Stones.Clear();
- player = null;
- fieldSize = Size.Empty;
+ f.Item = null;
}
+ ;
+ _fields.Clear();
+ Stones.Clear();
+ player = null;
+ fieldSize = Size.Empty;
}
}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Stone.cs b/CSharpBible/Games/Sokoban_Base/Model/Stone.cs
index 5a0c65c60..eaee4ce86 100644
--- a/CSharpBible/Games/Sokoban_Base/Model/Stone.cs
+++ b/CSharpBible/Games/Sokoban_Base/Model/Stone.cs
@@ -13,54 +13,53 @@
// ***********************************************************************
using Sokoban.Model;
-namespace Sokoban_Base.Model
+namespace Sokoban.Model;
+
+///
+/// Class Stone.
+/// Implements the
+///
+///
+public class Stone : PlayObject
{
///
- /// Class Stone.
- /// Implements the
+ /// Initializes a new instance of the class.
///
- ///
- public class Stone : PlayObject
+ /// a field.
+ public Stone(Field? aField) : base(aField)
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// a field.
- public Stone(Field? aField) : base(aField)
- {
- }
+ }
- ///
- /// Tests if the object can move in the given direction.
- ///
- /// The directon to test
- /// true: if the object can move in the direction
- public override bool TestMove(Direction dir)
+ ///
+ /// Tests if the object can move in the given direction.
+ ///
+ /// The directon to test
+ /// true: if the object can move in the direction
+ public override bool TestMove(Direction dir)
+ {
+ var result = false;
+ var dest = field?.Parent?[Offsets.DirOffset(dir, Position)];
+ if (dest is Floor s && s.Item == null)
{
- var result = false;
- var dest = field?.Parent?[Offsets.DirOffset(dir, Position)];
- if (dest is Floor s && s.Item == null)
- {
- result = true;
- }
- return result;
+ result = true;
}
+ return result;
+ }
- ///
- /// Tries to move the object in the given direction.
- ///
- /// The directon to move
- /// true: if the object has moveed in the direction
- public override bool TryMove(Direction dir)
+ ///
+ /// Tries to move the object in the given direction.
+ ///
+ /// The directon to move
+ /// true: if the object has moveed in the direction
+ public override bool TryMove(Direction dir)
+ {
+ var newfield = field?.Parent?[Offsets.DirOffset(dir, Position)];
+ if (newfield == null || !(newfield is Floor f)) return false;
+ else
{
- var newfield = field?.Parent?[Offsets.DirOffset(dir, Position)];
- if (newfield == null || !(newfield is Floor f)) return false;
+ if (f.Item == null) { f.Item = this; return true; }
else
- {
- if (f.Item == null) { f.Item = this; return true; }
- else
- return false;
- }
+ return false;
}
}
}
diff --git a/CSharpBible/Games/Sokoban_Base/Model/Wall.cs b/CSharpBible/Games/Sokoban_Base/Model/Wall.cs
new file mode 100644
index 000000000..4d0d2b28c
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/Model/Wall.cs
@@ -0,0 +1,52 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 07-09-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Sokoban.Model.Interfaces;
+using System;
+using System.Drawing;
+
+namespace Sokoban.Model;
+
+///
+/// Class Wall.
+/// Implements the
+///
+///
+public class Wall : Field
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The position.
+ /// The parent play object.
+ public Wall(Point position, Playfield? parentPlayObject) : base(position, parentPlayObject)
+ {
+ }
+
+ ///
+ /// Gets the field definition.
+ ///
+ /// FieldDef.
+ protected override FieldDef GetFieldDef() => FieldDef.Wall;
+ ///
+ /// Sets the item.
+ ///
+ /// The value.
+ ///
+ protected override void SetItem(IPlayObject? value)
+ {
+ // a Wall cannot contain an object
+ if (value != null)
+ throw new ArgumentException();
+ }
+}
diff --git a/CSharpBible/Games/Sokoban_Base/Version.svn b/CSharpBible/Games/Sokoban_Base/Version.svn
index 636191c25..f9575806f 100644
--- a/CSharpBible/Games/Sokoban_Base/Version.svn
+++ b/CSharpBible/Games/Sokoban_Base/Version.svn
@@ -1,5 +1,5 @@
[6]Repository UUID: 885f4a47-7d4f-460f-85f6-059ca52e3f0c
-[7]Revision: 1429
+[7]Revision: 1452
[8]Node Kind: directory
[9]Schedule: normal
[10]Last Changed Author: jc99
diff --git a/CSharpBible/Games/Sokoban_Base/ViewModels/Game.cs b/CSharpBible/Games/Sokoban_Base/ViewModels/Game.cs
deleted file mode 100644
index 45c36e9bb..000000000
--- a/CSharpBible/Games/Sokoban_Base/ViewModels/Game.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-// ***********************************************************************
-// Assembly : Sokoban_Base
-// Author : Mir
-// Created : 08-04-2022
-//
-// Last Modified By : Mir
-// Last Modified On : 09-09-2022
-// ***********************************************************************
-//
-// Copyright JC-Soft 2022
-//
-//
-// ***********************************************************************
-using Sokoban.Model;
-using Sokoban_Base.Model;
-using Sokoban.Properties;
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-
-namespace Sokoban_Base.ViewModels
-{
- ///
- /// Class Game.
- ///
- public static class Game {
- ///
- /// The vis show
- ///
- private static Action? _visShow;
- ///
- /// The vis update
- ///
- public static Action? visUpdate;
- ///
- /// The vis get user action
- ///
- public static Func? visGetUserAction;
- ///
- /// The vis set message
- ///
- public static Action? visSetMessage;
-
- ///
- /// Gets or sets the vis show.
- ///
- /// The vis show.
- public static Action? visShow { get => _visShow; set => _visShow = value; }
-
- ///
- /// The u action
- ///
- private static UserAction? uAction = null;
-
- ///
- /// The playfield
- ///
- public static Playfield playfield = new Playfield();
-
- ///
- /// Gets the player.
- ///
- /// The player.
- public static Player? player=>playfield.player;
- ///
- /// Gets the size of the pf.
- ///
- /// The size of the pf.
- public static Size PFSize=>playfield.fieldSize;
- ///
- /// Gets the stones in dest.
- ///
- /// The stones in dest.
- public static int StonesInDest =>playfield.StonesInDest;
- ///
- /// Gets the stones.
- ///
- /// The stones.
- public static IEnumerable Stones => playfield.Stones;
- ///
- /// Gets a value indicating whether [game solved].
- ///
- /// true if [game solved]; otherwise, false.
- public static bool GameSolved =>playfield.GameSolved;
-
- ///
- /// Gets the level.
- ///
- /// The level.
- public static int level { get; private set; }
-
- ///
- /// Initializes this instance.
- ///
- public static void Init() {
- level = 0;
- }
-
- ///
- /// Runs this instance.
- ///
- /// System.Nullable<UserAction>.
- public static UserAction? Run() {
- playfield.Setup(LabDefs.SLevels[level]);
-
- visShow?.Invoke(uAction);
-
- while (uAction != UserAction.Quit && !playfield.GameSolved && uAction != UserAction.Restart) {
-
- uAction = visGetUserAction?.Invoke(uAction);
- if (uAction != null && (int?)uAction < typeof(Direction).GetEnumValues().Length)
- if (!playfield.player?.Go((Direction)uAction) ?? false) {
- visSetMessage?.Invoke(string.Format(Resource1.CannotMoveMsg, (Direction)uAction));
- }
- else { visSetMessage?.Invoke(string.Format(Resource1.OK, (Direction)uAction)); }
- if (uAction == UserAction.Help) {
- visShow?.Invoke(uAction);
- uAction = visGetUserAction?.Invoke(uAction);
- visShow?.Invoke(uAction);
- }
- else
- visUpdate?.Invoke();
- }
- if (playfield.GameSolved)
- visSetMessage?.Invoke(string.Format(Resource1.GameSuccess, ""));
- else if (uAction == UserAction.Quit)
- visSetMessage?.Invoke(string.Format(Resource1.EndMessage, ""));
- else if (uAction == UserAction.Restart)
- visSetMessage?.Invoke(string.Format(Resource1.RestartMessage, ""));
-
- visShow?.Invoke(uAction);
-
- if (uAction != UserAction.Restart)
- level++;
- if (uAction != UserAction.Quit)
- uAction = visGetUserAction?.Invoke(uAction);
- return uAction;
- }
-
- ///
- /// Cleanups this instance.
- ///
- public static void Cleanup() {
- playfield.Clear();
- }
-
- ///
- /// Gets the tile.
- ///
- /// The p.
- /// TileDef.
- static public TileDef GetTile(Point p)
- {
- TileDef result = TileDef.Empty;
- var f = playfield[p]?.fieldDef;
- if (f <= FieldDef.StoneInDest && f != FieldDef.Wall && f != FieldDef.Player)
- result = (TileDef)f;
- else if (f == FieldDef.Player && playfield[p]?.Item is Player pl)
- {
- result = (pl.LastDir) switch
- {
- Direction.East => TileDef.Player_E,
- Direction.South => TileDef.Player_S,
- Direction.West => TileDef.Player_W,
- _ => TileDef.Player
- };
- }
- else if (f == FieldDef.Wall)
- {
- int w = 0;
- result = TileDef.Wall;
- w += ((playfield[Offsets.DirOffset(Direction.North, p)] is Wall) ? 1 : 0);
- w += ((playfield[Offsets.DirOffset(Direction.West, p)] is Wall) ? 2 : 0);
- w += ((playfield[Offsets.DirOffset(Direction.South, p)] is Wall) ? 4 : 0);
- w += ((playfield[Offsets.DirOffset(Direction.East, p)] is Wall) ? 8 : 0);
- if (w > 0)
- result = (TileDef)(w - 1 + (int)TileDef.Wall_N);
- }
- return result;
- }
-
- ///
- /// Gets the old position.
- ///
- /// The p.
- /// Point.
- public static Point GetOldPos(Point p)
- {
- Point result = new(p.X,p.Y);
- if (playfield[p] is Floor f && f.Item !=null)
- result = f.Item.OldPosition;
- return result;
- }
- }
-}
diff --git a/CSharpBible/Games/Sokoban_Base/ViewModels/IGame.cs b/CSharpBible/Games/Sokoban_Base/ViewModels/IGame.cs
new file mode 100644
index 000000000..d174efaeb
--- /dev/null
+++ b/CSharpBible/Games/Sokoban_Base/ViewModels/IGame.cs
@@ -0,0 +1,39 @@
+// ***********************************************************************
+// Assembly : Sokoban_Base
+// Author : Mir
+// Created : 08-04-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 09-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Sokoban.Model.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace Sokoban_Base.ViewModels;
+
+public interface IGame
+{
+ bool GameSolved { get; }
+ int level { get; }
+ Size PFSize { get; }
+ IPlayer? player { get; }
+ IEnumerable Stones { get; }
+ int StonesInDest { get; }
+ Action? visShow { get; set; }
+ Action? visUpdate { get; set; }
+ Func? visGetUserAction { get; set; }
+ Action? visSetMessage { get; set; }
+
+ void Cleanup();
+ Point GetOldPos(Point p);
+ TileDef GetTile(Point p);
+ void Init();
+ UserAction? Run();
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/Sokoban_BaseTests/Sokoban_BaseTests.csproj b/CSharpBible/Games/Sokoban_BaseTests/Sokoban_BaseTests.csproj
index 639fcfd23..df9be803c 100644
--- a/CSharpBible/Games/Sokoban_BaseTests/Sokoban_BaseTests.csproj
+++ b/CSharpBible/Games/Sokoban_BaseTests/Sokoban_BaseTests.csproj
@@ -14,8 +14,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Games/Sudoku_Base/Sudoku_Base.csproj b/CSharpBible/Games/Sudoku_Base/Sudoku_Base.csproj
index ec08dd491..cd0c840bd 100644
--- a/CSharpBible/Games/Sudoku_Base/Sudoku_Base.csproj
+++ b/CSharpBible/Games/Sudoku_Base/Sudoku_Base.csproj
@@ -2,12 +2,18 @@
Library
- net481;net48;net472;net462;net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net481;net48;net472;net462;net8.0-windows
true
enable
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
diff --git a/CSharpBible/Games/Sudoku_BaseTests/Models/SudokuFieldTests.cs b/CSharpBible/Games/Sudoku_BaseTests/Models/SudokuFieldTests.cs
index d645a8f70..81500f9e9 100644
--- a/CSharpBible/Games/Sudoku_BaseTests/Models/SudokuFieldTests.cs
+++ b/CSharpBible/Games/Sudoku_BaseTests/Models/SudokuFieldTests.cs
@@ -27,12 +27,12 @@ public void SetUpTest()
{
Assert.IsNotNull(testModel);
Assert.IsInstanceOfType(testModel, typeof(SudokuField));
- Assert.AreEqual(null, testModel.Value);
- Assert.AreEqual(false, testModel.IsPredefined);
- Assert.AreEqual(0, testModel.PossibleValues.Count);
+ Assert.IsNull(testModel.Value);
+ Assert.IsFalse(testModel.IsPredefined);
+ Assert.IsEmpty(testModel.PossibleValues);
}
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(new int[] { 1, 2, 3, 4, 5 }, 5)]
[DataRow(new int[] { 1 }, 1)]
[DataRow(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 9)]
@@ -43,12 +43,12 @@ public void AddPossibleValueTest(int[] aiAct, int iCnt)
foreach (var iAct in aiAct)
{
testModel.AddPossibleValue(iAct);
- Assert.IsTrue(testModel.PossibleValues.Contains(iAct));
+ Assert.Contains(iAct, testModel.PossibleValues);
}
- Assert.AreEqual(iCnt, testModel.PossibleValues.Count);
+ Assert.HasCount(iCnt, testModel.PossibleValues);
}
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(new int[] { 1, 2, 3, 4, 5 }, 1)]
[DataRow(new int[] { 1, 2, 3, 4, 5 }, 3)]
[DataRow(new int[] { 1, 2, 3, 4, 5 }, 5)]
@@ -70,7 +70,7 @@ public void RemovePossibleValueTest(int[] aiAct, int iAct)
// Act
testModel.RemovePossibleValue(iAct);
// Assert
- Assert.IsFalse(testModel.PossibleValues.Contains(iAct));
+ Assert.DoesNotContain(iAct, testModel.PossibleValues);
}
[TestMethod()]
@@ -85,7 +85,7 @@ public void ReadFromStreamTest(byte[] bytes, SudokuField field)
AssertAreEqual(field, testModel, []);
}
- [DataTestMethod()]
+ [TestMethod()]
[DynamicData(nameof(SudokuFieldTestData))]
public void WriteToStreamTest(byte[] bytes, SudokuField field)
{
@@ -98,7 +98,7 @@ public void WriteToStreamTest(byte[] bytes, SudokuField field)
CollectionAssert.AreEqual(bytes, stream.ToArray());
}
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(new[] { 3, 5 }, null, false, new[] { 1, 3, 7 })]
[DataRow(new[] { 0, 0 }, 5, true, new int[] { })]
[DataRow(new[] { 0, 0 }, 5, true, new int[] { 0, 1, 2 })]
@@ -128,8 +128,8 @@ public void ClearTest(int[] aiPos, int? value, bool isPredefined, int[] pValues)
// Act
field.Clear();
// Assert
- Assert.AreEqual(null,field.Value);
- Assert.AreEqual(false, field.IsPredefined);
- Assert.AreEqual(0, field.PossibleValues.Count);
+ Assert.IsNull(field.Value);
+ Assert.IsFalse(field.IsPredefined);
+ Assert.IsEmpty(field.PossibleValues);
}
}
\ No newline at end of file
diff --git a/CSharpBible/Games/Sudoku_BaseTests/Models/SudokuModelTests.cs b/CSharpBible/Games/Sudoku_BaseTests/Models/SudokuModelTests.cs
index 9524b72e4..c545ed3f0 100644
--- a/CSharpBible/Games/Sudoku_BaseTests/Models/SudokuModelTests.cs
+++ b/CSharpBible/Games/Sudoku_BaseTests/Models/SudokuModelTests.cs
@@ -127,7 +127,7 @@ public void SetupTest()
Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
}
- [DataTestMethod()]
+ [TestMethod()]
[DynamicData(nameof(SudokuModelTestData))]
public void WriteToStreamTest(byte[] bytes, SudokuField[] fields)
{
@@ -150,7 +150,7 @@ public void WriteToStreamTest(byte[] bytes, SudokuField[] fields)
CollectionAssert.AreEqual(bytes, stream.ToArray());
}
- [DataTestMethod()]
+ [TestMethod()]
[DynamicData(nameof(SudokuModelTestData))]
public void ReadFromStreamTest(byte[] bytes, SudokuField[] fields)
{
@@ -168,7 +168,7 @@ public void ReadFromStreamTest(byte[] bytes, SudokuField[] fields)
}
}
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(".\\Resources\\123.sudoku", new[] { 0,0,1, 0,1,-1, 0,2,-1, 0,3,-1, 0,4,9, 0,5,2, 0,6,-1, 0,7,-1, 0,8,-1,
1,0,-1, 1,1,9, 1,2,5, 1,3,-1, 1,4,-1, 1,5,-1, 1,6,-1, 1,7,-1, 1,8,3,
2,0,3, 2,1,-1, 2,2,8, 2,3,1, 2,4,-1, 2,5,-1, 2,6,2, 2,7,-1, 2,8,-1,
diff --git a/CSharpBible/Games/Sudoku_BaseTests/Models/UndoInformationTests.cs b/CSharpBible/Games/Sudoku_BaseTests/Models/UndoInformationTests.cs
index 22709a635..34887cefd 100644
--- a/CSharpBible/Games/Sudoku_BaseTests/Models/UndoInformationTests.cs
+++ b/CSharpBible/Games/Sudoku_BaseTests/Models/UndoInformationTests.cs
@@ -8,7 +8,7 @@ namespace Sudoku_Base.Models.Tests;
[TestClass()]
public class UndoInformationTests
{
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(new int[] { 1, 2 }, 5, true, new int[] { 1, 2, 3, 4, 5 }, null)]
[DataRow(new int[] { 1, 2 }, 5, true, new int[] { 1, 2, 3, 4, 5 }, true)]
[DataRow(new int[] { 1, 2 }, 5, true, new int[] { 1, 2, 3, 4, 5 }, new[] {3,5,7 })]
@@ -23,7 +23,7 @@ public void UndoInformationTest(int[] aiPos, int? value, bool isPredefined, int[
Assert.IsNotNull(testModel);
Assert.IsTrue(testModel.Field.TryGetTarget(out var f2));
AssertAreEqual(field, f2, []);
- Assert.AreEqual(1, testModel.list.Count);
+ Assert.HasCount(1, testModel.list);
Assert.AreEqual((ov, null), testModel.list[0]);
}
@@ -117,7 +117,7 @@ public void UndoTest(int[] aiPos, int? value, bool isPredefined, int[] pValues,
}
}
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(new int[] { 1, 2 }, 5, true, new int[] { 1, 2, 3, 4, 5 }, 5,null)]
[DataRow(new int[] { 1, 2 }, 5, true, new int[] { 1, 2, 3, 4, 5 }, null,5)]
[DataRow(new int[] { 1, 2 }, 5, true, new int[] { 1, 2, 3, 4, 5 }, true,false)]
@@ -132,7 +132,7 @@ public void TryUpdateNewValueTest(int[] aiPos, int? value, bool isPredefined, in
// Act
testModel.TryUpdateNewValue(nv);
// Assert
- Assert.AreEqual(1, testModel.list.Count);
+ Assert.HasCount(1, testModel.list);
Assert.AreEqual((ov, nv is string?null:nv), testModel.list[0]);
}
}
\ No newline at end of file
diff --git a/CSharpBible/Games/Sudoku_BaseTests/Sudoku_BaseTests.csproj b/CSharpBible/Games/Sudoku_BaseTests/Sudoku_BaseTests.csproj
index 19d4c6c2d..0839be8f5 100644
--- a/CSharpBible/Games/Sudoku_BaseTests/Sudoku_BaseTests.csproj
+++ b/CSharpBible/Games/Sudoku_BaseTests/Sudoku_BaseTests.csproj
@@ -1,21 +1,27 @@
- net481;net48;net472;net462;net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net481;net48;net472;net462;net8.0-windows
true
false
AnyCPU;x86
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/CSharpBible/Games/Tetris_BaseTests/Tetris_BaseTests.csproj b/CSharpBible/Games/Tetris_BaseTests/Tetris_BaseTests.csproj
index 957c25325..6e73243e1 100644
--- a/CSharpBible/Games/Tetris_BaseTests/Tetris_BaseTests.csproj
+++ b/CSharpBible/Games/Tetris_BaseTests/Tetris_BaseTests.csproj
@@ -15,8 +15,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Games/Treppen.Base/HeightLabyrinth.cs b/CSharpBible/Games/Treppen.Base/HeightLabyrinth.cs
new file mode 100644
index 000000000..9b33180c1
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Base/HeightLabyrinth.cs
@@ -0,0 +1,407 @@
+using System;
+using System.Drawing;
+using MathLibrary.TwoDim;
+using static MathLibrary.TwoDim.Directions2D;
+
+namespace Treppen.Base;
+
+///
+/// Height-based labyrinth engine ported from Pascal THeightLaby.
+///
+public sealed class HeightLabyrinth : IHeightLabyrinth
+{
+ /*
+ Pseudocode/Plan:
+ - Ziel: Das Seed-Pattern (Signatur) aus Pascal (FPrest2) analog mittels SetLData in das Raster _z schreiben.
+ • Das Pattern besteht aus Ziffern und '.'-Zeichen; in Pascal wird jedes Zeichen in eine Zahl umgewandelt:
+ val = Ord(char) - Ord('0') + Offset. Für '.' (ASCII 46) mit Offset=2 ergibt das 0 => "unbesetzt".
+ • Die Indizierung in Pascal liest Sdat rückwärts:
+ index = (xm*ym) - y*xm - x, wobei Strings 1-basiert sind.
+ In C# (0-basiert) wird daraus: idx0 = index - 1 = (xm*ym) - y*xm - x - 1.
+ • Schleifenbereiche: x in [0..xm-1], y in [0..ym-1]; schreiben nach _z[x,y].
+ • Sicherheitsgrenzen: Nur schreiben, wenn (x < _dimension.Width && y < _dimension.Height).
+ - Implementierung:
+ • private const string FPrest2 = "4465452.
+ 7575331.
+ 6566211.
+ 21326510
+ 30213422
+ 43312434";
+ • private void SetLData(string sdat, int xm, int ym, int offset)
+ - prüfe sdat.Length >= xm*ym
+ - für x=0..xm-1
+ für y=0..ym-1
+ idx0 = (xm*ym) - y*xm - x - 1
+ val = (sdat[idx0] - '0') + offset
+ wenn InBounds(x,y) dann _z[x,y] = val
+ • In Generate():
+ - Nach Array.Clear(_z, 0, _z.Length) SetLData(FPrest2, 8, 6, 2) aufrufen.
+ • Rest der Logik bleibt unverändert.
+ */
+
+ private Rectangle _dimension;
+ private int[,] _z;
+ private readonly Random _rnd = new();
+
+ // Seed-Pattern (Signatur) wie im Pascal-Original
+ // private const string FPrest2 = "4465452.7575331.6566211.213265103021342243312434";
+ private readonly uint[] FPrest3 = [0x7645452E,
+ 0x5746331E,
+ 0x6655211E,
+ 0x44125510,
+ 0x35213422
+ ,0x32312434];
+
+ public event Action? UpdateCell;
+
+ public Rectangle Dimension
+ {
+ get => _dimension;
+ set { _dimension = value; _z = new int[value.Width, value.Height]; }
+ }
+
+ public int this[int x, int y] => InBounds(x, y) ? _z[x, y] : 0;
+ private bool InBounds(int x, int y) => x >= 0 && y >= 0 && x < _dimension.Width && y < _dimension.Height;
+
+ public int BaseLevel(int x, int y) => (int)Math.Truncate((x / 1.3 + y / 1.3) + 1);
+
+ // Mappt die kompakten Seed-Daten in das Raster, wie in Pascal SetLData
+ private void SetLData(string sdat, int xm, int ym, int offset)
+ {
+ if (string.IsNullOrEmpty(sdat)) return;
+ int total = xm * ym;
+ if (sdat.Length < total) return;
+
+ for (int x = 0; x < xm; x++)
+ {
+ for (int y = 0; y < ym; y++)
+ {
+ int idx0 = total - (y * xm) - x - 1; // 0-basierte Entsprechung zu Pascal
+ char ch = sdat[idx0];
+ int val = (ch - '0') + offset; // '.' -> -2 + 2 => 0
+ if (InBounds(x, y))
+ _z[x, y] = val;
+ }
+ }
+ }
+
+ private void SetLData2(uint[] data, int xm, int ym, int offset)
+ {
+ if (data.Length < ym - 1) return;
+
+ for (int x = 0; x < xm; x++)
+ {
+ for (int y = 0; y < ym; y++)
+ {
+ var nibble = (int)((data[ym-y-1] >> 4 * x) & 0x0F);
+ int val = (nibble == 14 ? 0 : nibble + offset); // 14='.'→0
+ if (InBounds(x, y))
+ _z[x, y] = val;
+ }
+ }
+ }
+
+ public void Generate()
+ {
+ Array.Clear(_z, 0, _z.Length);
+
+ // Seed-Pattern (Signatur) initial aufbringen
+ SetLData2(FPrest3, 8, 6, 2);
+
+ var start = new Point(1, Math.Min(6, Math.Max(0, _dimension.Height - 1)));
+ if (!InBounds(start.X, start.Y)) return;
+
+ _z[start.X, start.Y] = BaseLevel(start.X, start.Y) - 1;
+
+ var fifo = new Point[_dimension.Width * _dimension.Height];
+ int push = 0, pop = 0;
+ var act = start; var stored = act;
+ int dirCount = 1;
+
+ while (dirCount != 0 || push >= pop)
+ {
+ // swap
+ (act, stored) = (stored, act);
+ UpdateCell?.Invoke(this, act);
+ dirCount = 0;
+ var pos = new Point[4];
+ var hh = new int[4];
+
+ for (int i = 1; i < Dir4.Length; i++)
+ {
+ pos[dirCount] = new Point(Dir4[i].X, Dir4[i].Y);
+ var next = new Point(act.X - pos[dirCount].X, act.Y - pos[dirCount].Y);
+ if (InBounds(next.X, next.Y) &&
+ CalcStepHeight(act, next, out var h) &&
+ Math.Abs(h - BaseLevel(next.X, next.Y) - 1) < 3)
+ {
+ hh[dirCount] = h; dirCount++;
+ }
+ }
+
+ if (dirCount > 0)
+ {
+ var pick = _rnd.Next(dirCount);
+ if (dirCount > 1) { fifo[push++] = act; }
+ var next = new Point(act.X - pos[pick].X, act.Y - pos[pick].Y);
+ act = next; _z[next.X, next.Y] = hh[pick];
+ }
+ else if (push >= pop)
+ {
+ act = fifo[pop++];
+ }
+ }
+
+ // restliche 0-Zellen heuristisch füllen
+ for (int x = (_dimension.Width - 1) | 1; x >= 0; x--)
+ for (int y = (_dimension.Height - 1) | 1; y >= 0; y--)
+ {
+ var pp = new Point(x ^ 1, y ^ 1);
+ if (!InBounds(pp.X, pp.Y) || _z[pp.X, pp.Y] != 0) continue;
+
+ bool first = true; int zm = 0, cn = 0, zx = 0, cx = 0;
+
+ for (int i = 1; i < Dir4.Length; i++)
+ {
+ var n = new Point(pp.X + Dir4[i].X, pp.Y + Dir4[i].Y);
+ var zz = InBounds(n.X, n.Y) ? _z[n.X, n.Y] : 0;
+
+ if (zz > 0 && (first || zz <= zm)) { if (zz < zm) cn = 0; zm = zz; if (cn < 2) cn++; }
+ if (zz > 0 && (first || zz >= zx)) { if (zz > zx) cx = 0; zx = zz; if (cx < 2) cx++; first = false; }
+ }
+
+ if (zm > 0)
+ _z[pp.X, pp.Y] = (cx == 1 && zx - zm < 6) ? zx + cx : zm - cn;
+ else
+ _z[pp.X, pp.Y] = BaseLevel(pp.X, pp.Y);
+ }
+ }
+
+ private bool CalcStepHeight(Point act, Point next, out int height)
+ {
+ height = 0;
+ var ph = this[act.X, act.Y];
+ if (ph == 0 || this[next.X, next.Y] != 0) return false;
+
+ bool canm1 = true, canz = true, canp1 = true;
+ IntPoint dd = default;
+
+ for (int i = 1; i < Dir4.Length; i++)
+ {
+ var t = new Point(next.X + Dir4[i].X, next.Y + Dir4[i].Y);
+ if (!(t == act))
+ {
+ var lb = this[t.X, t.Y];
+ canm1 &= (lb == 0) || (lb < ph - 2) || (lb > ph);
+ canz &= (lb == 0) || (lb < ph - 1) || (lb > ph + 1);
+ canp1 &= (lb == 0) || (lb < ph) || (lb > ph + 2);
+ }
+ else dd = Dir4[i];
+ }
+
+ var dr = new IntPoint(-dd.Y, dd.X);
+
+ var left = new Point(next.X - dr.X, next.Y - dr.Y);
+ if (InBounds(left.X, left.Y) && this[left.X, left.Y] == 0)
+ {
+ var fl = new Point(left.X + dd.X, left.Y + dd.Y);
+ var lb = InBounds(fl.X, fl.Y) ? this[fl.X, fl.Y] : 0;
+ canm1 &= (lb == 0) || (lb != ph - 1);
+ canz &= (lb == 0) || (lb != ph);
+ canp1 &= (lb == 0) || (lb != ph + 1);
+
+ var bl = new Point(left.X - dd.X, left.Y - dd.Y);
+ lb = InBounds(bl.X, bl.Y) ? this[bl.X, bl.Y] : 0;
+ canm1 &= (lb == 0) || (lb != ph - 1);
+ canz &= (lb == 0) || (lb != ph);
+ canp1 &= (lb == 0) || (lb != ph + 1);
+ }
+
+ var right = new Point(next.X + dr.X, next.Y + dr.Y);
+ if (InBounds(right.X, right.Y) && this[right.X, right.Y] == 0)
+ {
+ var fr = new Point(right.X + dd.X, right.Y + dd.Y);
+ var lb = InBounds(fr.X, fr.Y) ? this[fr.X, fr.Y] : 0;
+ canm1 &= (lb == 0) || (lb != ph - 1);
+ canz &= (lb == 0) || (lb != ph);
+ canp1 &= (lb == 0) || (lb != ph + 1);
+
+ var br = new Point(right.X - dd.X, right.Y - dd.Y);
+ lb = InBounds(br.X, br.Y) ? this[br.X, br.Y] : 0;
+ canm1 &= (lb == 0) || (lb != ph - 1);
+ canz &= (lb == 0) || (lb != ph);
+ canp1 &= (lb == 0) || (lb != ph + 1);
+ }
+
+ var blv = BaseLevel(next.X, next.Y);
+ if (!(canm1 || canz || canp1)) return false;
+
+ if (canp1 && (blv > ph || (!canz && (!canm1 || blv == ph)))) { height = ph + 1; return true; }
+ if (canm1 && (blv < ph || (!canz && (!canp1 || blv == ph)))) { height = ph - 1; return true; }
+ if (canz) { height = ph; return true; }
+
+ return false;
+ }
+
+ public bool LoadFromStream(Stream stream)
+ {
+ if (stream is null) throw new ArgumentNullException(nameof(stream));
+ try
+ {
+ long startPos = stream.CanSeek ? stream.Position : 0;
+
+ // Erkennung Binary-Header (UTF-16 little endian char[] von BinaryWriter.Write(char[]))
+ Span hdr = stackalloc byte[3];
+ int r = stream.Read(hdr);
+ bool isBinary = false;
+ if (r == hdr.Length)
+ {
+ Span magic = stackalloc byte[]
+ {
+ (byte)'H',(byte)'L',(byte)'B'
+ };
+ isBinary = hdr.SequenceEqual(magic);
+ }
+
+ if (stream.CanSeek)
+ stream.Position = startPos;
+
+ if (isBinary)
+ {
+ var br = new BinaryReader(stream, System.Text.Encoding.UTF8, leaveOpen: true);
+ // Header überspringen (8 chars)
+ var headerChars = br.ReadChars(8);
+ var header = new string(headerChars);
+ if (header != "HLB1.00\0") return false;
+
+ int w = br.ReadInt32();
+ int h = br.ReadInt32();
+ if (w <= 0 || h <= 0) return false;
+
+ Dimension = new Rectangle(0, 0, w, h);
+
+ for (int y = 0; y < h; y++)
+ {
+ for (int x = 0; x < w; x++)
+ {
+ if (!InBounds(x, y)) return false;
+ _z[x, y] = br.ReadInt32();
+ }
+ }
+ return true;
+ }
+ else
+ {
+ // JSON laden
+ using var doc = System.Text.Json.JsonDocument.Parse(stream);
+ var root = doc.RootElement;
+
+ if (!root.TryGetProperty("version", out var sVersion) ||
+ !root.TryGetProperty("width", out var wProp) ||
+ !root.TryGetProperty("height", out var hProp) ||
+ !root.TryGetProperty("data", out var dProp))
+ return false;
+
+ int w = wProp.GetInt32();
+ int h = hProp.GetInt32();
+ if (w <= 0 || h <= 0) return false;
+
+ Dimension = new Rectangle(0, 0, w, h);
+
+ if (dProp.ValueKind != System.Text.Json.JsonValueKind.Array || dProp.GetArrayLength() != h)
+ return false;
+
+ int y = 0;
+ foreach (var rowEl in dProp.EnumerateArray())
+ {
+ if (rowEl.ValueKind != System.Text.Json.JsonValueKind.Array || rowEl.GetArrayLength() != w)
+ return false;
+
+ int x = 0;
+ foreach (var cellEl in rowEl.EnumerateArray())
+ {
+ _z[x, y] = cellEl.GetInt32();
+ x++;
+ }
+ y++;
+ }
+ return true;
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ /*
+ Pseudocode/Plan:
+ - Ziel: Stream-Inhalt als binär (xBinary=true) oder JSON (xBinary=false) speichern.
+ - Vorbedingungen: _z ist initialisiert; Dimension gibt Breite/Höhe vor.
+ - Binärformat:
+ • Verwende BinaryWriter (leaveOpen=true).
+ • Schreibe Header: Int32 Width, Int32 Height.
+ • Schleife über y=0..Height-1, x=0..Width-1 (stabile Reihenfolge) und schreibe Int32 _z[x,y].
+ - JSON-Format:
+ • Erzeuge DTO mit:
+ { width: int, height: int, data: int[][] }
+ • Konvertiere _z (int[,]) zu int[height][width] (Zeilen = y, Spalten = x).
+ • Verwende System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(dto) und schreibe in Stream.
+ • JsonSerializerOptions: WriteIndented=false für kompakten Output.
+ - Ressourcen:
+ • Writer nicht disposen, leaveOpen verwenden; Stream bleibt Eigentum des Aufrufers.
+ */
+
+ public void SaveToStream(Stream stream, bool xBinary = false)
+ {
+ if (stream is null) throw new ArgumentNullException(nameof(stream));
+ int width = _dimension.Width;
+ int height = _dimension.Height;
+
+ if (width <= 0 || height <= 0 || _z is null || _z.Length == 0)
+ throw new InvalidOperationException("Leeres Labyrinth kann nicht gespeichert werden.");
+
+ if (xBinary)
+ {
+ using var bw = new BinaryWriter(stream, System.Text.Encoding.UTF8, leaveOpen: true);
+ bw.Write([(byte)'H',(byte)'L', (byte)'B', (byte)'1', (byte)'.', (byte)'0', (byte)'0', (byte)0]); // Header
+ bw.Write(width);
+ bw.Write(height);
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ bw.Write(_z[x, y]);
+ }
+ }
+ bw.Flush();
+ return;
+ }
+
+ // JSON-Ausgabe
+ var data = new int[height][];
+ for (int y = 0; y < height; y++)
+ {
+ var row = new int[width];
+ for (int x = 0; x < width; x++)
+ {
+ row[x] = _z[x, y];
+ }
+ data[y] = row;
+ }
+
+ var dto = new
+ {
+ version="1.00",
+ width,
+ height,
+ data
+ };
+
+ var json = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(dto, new System.Text.Json.JsonSerializerOptions
+ {
+ WriteIndented = false
+ });
+ stream.Write(json, 0, json.Length);
+ }
+}
diff --git a/CSharpBible/Games/Treppen.Base/IHeightLabyrinth.cs b/CSharpBible/Games/Treppen.Base/IHeightLabyrinth.cs
new file mode 100644
index 000000000..1cec9fc1c
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Base/IHeightLabyrinth.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Drawing;
+using MathLibrary.TwoDim;
+
+namespace Treppen.Base;
+
+///
+/// Contract for height labyrinth engine to enable DI and testing.
+///
+public interface IHeightLabyrinth
+{
+ Rectangle Dimension { get; set; }
+ int this[int x, int y] { get; }
+ int BaseLevel(int x, int y);
+ void Generate();
+
+ event Action? UpdateCell;
+
+ bool LoadFromStream(System.IO.Stream stream);
+ void SaveToStream(System.IO.Stream stream, bool xBinary=false);
+}
diff --git a/CSharpBible/Games/Treppen.Base/Treppen.Base.csproj b/CSharpBible/Games/Treppen.Base/Treppen.Base.csproj
new file mode 100644
index 000000000..dc5b59561
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Base/Treppen.Base.csproj
@@ -0,0 +1,26 @@
+
+
+
+
+ net8.0
+ enable
+ enable
+ true
+ Treppen.Base
+
+
+
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Treppen.BaseTests/HeightLabyrinthTests.cs b/CSharpBible/Games/Treppen.BaseTests/HeightLabyrinthTests.cs
new file mode 100644
index 000000000..185f27710
--- /dev/null
+++ b/CSharpBible/Games/Treppen.BaseTests/HeightLabyrinthTests.cs
@@ -0,0 +1,58 @@
+using System.Drawing;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Treppen.Base;
+
+namespace Treppen.BaseTests;
+
+[TestClass]
+public class HeightLabyrinthTests
+{
+ [TestMethod]
+ public void Create_Default_IsEmpty()
+ {
+ var hl = new HeightLabyrinth { Dimension = new Rectangle(0, 0, 3, 3) };
+ for (int x = 0; x < 3; x++) for (int y = 0; y < 3; y++) Assert.AreEqual(0, hl[x, y]);
+ }
+
+ [TestMethod]
+ public void BaseLevel_Is_DiagonalGradient()
+ {
+ var hl = new HeightLabyrinth { Dimension = new Rectangle(0, 0, 3, 3) };
+ Assert.AreEqual(1, hl.BaseLevel(0, 0));
+ Assert.IsTrue(hl.BaseLevel(2, 2) > hl.BaseLevel(0, 0));
+ }
+
+ [TestMethod]
+ public void Generate_Fills_All_Cells()
+ {
+ var hl = new HeightLabyrinth { Dimension = new Rectangle(0, 0, 8, 8) };
+ hl.Generate();
+ for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) Assert.AreNotEqual(0, hl[x, y]);
+ }
+
+ [TestMethod]
+ public void Generate_Raises_UpdateCell_Event()
+ {
+ var hl = new HeightLabyrinth { Dimension = new Rectangle(0, 0, 8, 8) };
+ bool eventRaised = false;
+ hl.UpdateCell += (s, p) => { eventRaised = true; };
+ hl.Generate();
+ Assert.IsTrue(eventRaised);
+ }
+
+ [TestMethod]
+ [DataRow(false)]
+ [DataRow(true)]
+ public void Save_And_Load_Maintains_Data(bool xBin)
+ {
+ var hl = new HeightLabyrinth { Dimension = new Rectangle(0, 0, 5, 5) };
+ hl.Generate();
+ using var ms = new System.IO.MemoryStream();
+ hl.SaveToStream(ms,xBin);
+ ms.Position = 0;
+ var hl2 = new HeightLabyrinth { Dimension = new Rectangle(0, 0, 5, 5) };
+ bool loaded = hl2.LoadFromStream(ms);
+ Assert.IsTrue(loaded);
+ for (int x = 0; x < 5; x++) for (int y = 0; y < 5; y++) Assert.AreEqual(hl[x, y], hl2[x, y]);
+ }
+}
diff --git a/CSharpBible/Games/Treppen.BaseTests/Treppen.BaseTests.csproj b/CSharpBible/Games/Treppen.BaseTests/Treppen.BaseTests.csproj
new file mode 100644
index 000000000..c528f6d28
--- /dev/null
+++ b/CSharpBible/Games/Treppen.BaseTests/Treppen.BaseTests.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+ net8.0;net9.0
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Treppen.Export/Models/PrintOptions.cs b/CSharpBible/Games/Treppen.Export/Models/PrintOptions.cs
new file mode 100644
index 000000000..891637a78
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Export/Models/PrintOptions.cs
@@ -0,0 +1,15 @@
+namespace Treppen.Export.Models;
+
+public sealed class PrintOptions
+{
+ public int Dpi { get; init; } = 300;
+ public int MarginLeft { get; init; } = 10;
+ public int MarginTop { get; init; } = 10;
+ public int MarginRight { get; init; } = 10;
+ public int MarginBottom { get; init; } = 10;
+
+ public int CellSize { get; init; } = 8;
+ public string? Title { get; init; }
+ public string ForegroundColor { get; init; } = "#000000";
+ public string BackgroundColor { get; init; } = "#FFFFFF";
+}
diff --git a/CSharpBible/Games/Treppen.Export/Services/Drawing/DrawCommandRenderer.cs b/CSharpBible/Games/Treppen.Export/Services/Drawing/DrawCommandRenderer.cs
new file mode 100644
index 000000000..c6dfb1440
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Export/Services/Drawing/DrawCommandRenderer.cs
@@ -0,0 +1,15 @@
+using System.Windows.Media;
+using Treppen.Export.Services.Interfaces;
+
+namespace Treppen.Export.Services.Drawing;
+
+public static class DrawCommandRenderer
+{
+ public static void Render(this IEnumerable commands, DrawingContext dc)
+ {
+ foreach (var cmd in commands)
+ {
+ cmd.Render(dc);
+ }
+ }
+}
diff --git a/CSharpBible/Games/Treppen.Export/Services/Interfaces/IDrawCommand.cs b/CSharpBible/Games/Treppen.Export/Services/Interfaces/IDrawCommand.cs
new file mode 100644
index 000000000..612353f1e
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Export/Services/Interfaces/IDrawCommand.cs
@@ -0,0 +1,6 @@
+namespace Treppen.Export.Services.Interfaces;
+
+public interface IDrawCommand
+{
+ void Render(object dc);
+}
diff --git a/CSharpBible/Games/Treppen.Export/Services/Interfaces/IDrawCommandFactory.cs b/CSharpBible/Games/Treppen.Export/Services/Interfaces/IDrawCommandFactory.cs
new file mode 100644
index 000000000..ca0d34f90
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Export/Services/Interfaces/IDrawCommandFactory.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+using System.Windows.Media;
+
+namespace Treppen.Export.Services.Interfaces;
+
+public interface IDrawCommandFactory
+{
+ IDrawCommand newPolygonCommand(Point[] points, Color fill, Color? outline, double v);
+ IDrawCommand newPolyLineCommand(Point[] rampPoints, Color color, double v1, bool v2);
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/Treppen.Export/Services/Interfaces/ILabyrinth3dDrawer.cs b/CSharpBible/Games/Treppen.Export/Services/Interfaces/ILabyrinth3dDrawer.cs
new file mode 100644
index 000000000..7ad1638f6
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Export/Services/Interfaces/ILabyrinth3dDrawer.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+using Treppen.Base;
+
+namespace Treppen.Export.Services.Interfaces;
+
+public interface ILabyrinth3dDrawer
+{
+ // Liefert abstrakte Zeichenkommandos anstatt direkt zu zeichnen
+ IReadOnlyList Build(IHeightLabyrinth labyrinth, Size printableSize, IDrawCommandFactory dcFactory);
+}
diff --git a/CSharpBible/Games/Treppen.Export/Services/Interfaces/IPrintRenderer.cs b/CSharpBible/Games/Treppen.Export/Services/Interfaces/IPrintRenderer.cs
new file mode 100644
index 000000000..dd24ed2c7
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Export/Services/Interfaces/IPrintRenderer.cs
@@ -0,0 +1,10 @@
+using System.IO;
+using Treppen.Base;
+using Treppen.Export.Models;
+
+namespace Treppen.Export.Services.Interfaces;
+
+public interface IPrintRenderer
+{
+ Task RenderAsync(IHeightLabyrinth labyrinth, PrintOptions options, Stream output);
+}
diff --git a/CSharpBible/Games/Treppen.Export/Treppen.Export.csproj b/CSharpBible/Games/Treppen.Export/Treppen.Export.csproj
new file mode 100644
index 000000000..29718a737
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Export/Treppen.Export.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+ net8.0-windows
+ enable
+ enable
+ true
+ Treppen.Export
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/Games/Treppen.Print/PrintService.cs b/CSharpBible/Games/Treppen.Print/PrintService.cs
new file mode 100644
index 000000000..06f8ea0ea
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Print/PrintService.cs
@@ -0,0 +1,18 @@
+using Treppen.Base;
+using Treppen.Export.Models;
+using Treppen.Export.Services.Interfaces;
+
+namespace Treppen.Print;
+
+public sealed class PrintService
+{
+ private readonly IPrintRenderer _renderer;
+
+ public PrintService(IPrintRenderer renderer)
+ {
+ _renderer = renderer;
+ }
+
+ public Task ExportAsync(IHeightLabyrinth labyrinth, PrintOptions options, Stream output)
+ => _renderer.RenderAsync(labyrinth, options, output);
+}
diff --git a/CSharpBible/Games/Treppen.Print/Rendering/ObjPrintRenderer.cs b/CSharpBible/Games/Treppen.Print/Rendering/ObjPrintRenderer.cs
new file mode 100644
index 000000000..fd5d35f1e
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Print/Rendering/ObjPrintRenderer.cs
@@ -0,0 +1,91 @@
+using System.Globalization;
+using System.Text;
+using Treppen.Base;
+using Treppen.Print.Services;
+using Treppen.Export.Models;
+using Treppen.Export.Services.Interfaces;
+
+namespace Treppen.Print.Rendering;
+
+[PrintExporter("Wavefront OBJ", ".obj")]
+public sealed class ObjPrintRenderer : IPrintRenderer
+{
+ public async Task RenderAsync(IHeightLabyrinth labyrinth, PrintOptions options, Stream output)
+ {
+ ArgumentNullException.ThrowIfNull(labyrinth);
+ ArgumentNullException.ThrowIfNull(options);
+ ArgumentNullException.ThrowIfNull(output);
+
+ var dim = labyrinth.Dimension;
+ int w = dim.Width;
+ int h = dim.Height;
+ if (w <= 0 || h <= 0)
+ throw new InvalidOperationException("Leeres Labyrinth kann nicht exportiert werden.");
+
+ // Simple voxel export: each cell height produces stacked unit cubes.
+ // Coordinate system: x to the right, y forward, z up.
+ // Scale cell size to options.CellSize, height to same unit.
+ double cell = Math.Max(1, options.CellSize);
+ var sb = new StringBuilder(1024 * 1024);
+ sb.AppendLine("# Treppen Labyrinth OBJ export");
+ sb.AppendLine("# One unit equals CellSize");
+ int vertexOffset = 1; // OBJ indices are 1-based
+
+ // Helper to append a single cube at (cx, cy, cz) with size cell
+ void AppendCube(double cx, double cy, double height)
+ {
+ // 8 vertices
+ // bottom
+ sb.AppendLine(V(cx - cell / 2, 0, cy - cell / 2)); // v1
+ sb.AppendLine(V(cx + cell / 2, 0, cy - cell / 2)); // v2
+ sb.AppendLine(V(cx + cell / 2, 0 , cy + cell / 2)); // v3
+ sb.AppendLine(V(cx - cell / 2, 0 , cy + cell / 2)); // v4
+ // top
+ sb.AppendLine(V(cx - cell / 2, height, cy - cell / 2)); // v5
+ sb.AppendLine(V(cx + cell / 2, height, cy - cell / 2)); // v6
+ sb.AppendLine(V(cx + cell / 2, height , cy + cell / 2)); // v7
+ sb.AppendLine(V(cx - cell / 2, height , cy + cell / 2)); // v8
+
+ // 6 faces (quads). Indices relative to current vertexOffset
+ int v = vertexOffset;
+ // bottom (1,2,3,4)
+ sb.AppendLine($"f {v} {v + 1} {v + 2} {v + 3}");
+ // top (5,6,7,8)
+ sb.AppendLine($"f {v + 4} {v + 7} {v + 6} {v + 5}");
+ // front (2,6,7,3)
+ sb.AppendLine($"f {v + 1} {v + 5} {v + 6} {v + 2}");
+ // back (1,4,8,5)
+ sb.AppendLine($"f {v} {v + 3} {v + 7} {v + 4}");
+ // left (1,5,6,2)
+ sb.AppendLine($"f {v} {v + 4} {v + 5} {v + 1}");
+ // right (4,3,7,8)
+ sb.AppendLine($"f {v + 3} {v + 2} {v + 6} {v + 7}");
+
+ vertexOffset += 8;
+ }
+
+ for (int y = 0; y < h; y++)
+ {
+ for (int x = 0; x < w; x++)
+ {
+ int heightVal = labyrinth[x, y];
+ if (heightVal <= 0) continue;
+ // center position for the cube
+ double cx = (-x+h/2d) * cell;
+ double cy = (y-h/2d) * cell;
+ double cz = heightVal*cell*0.25d;
+ AppendCube(cy, cx, cz);
+ }
+ }
+
+ var bytes = Encoding.UTF8.GetBytes(sb.ToString());
+ await output.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+ }
+
+ private static string V(double x, double y, double z)
+ {
+ return $"v {Fmt(x)} {Fmt(y)} {Fmt(z)}";
+ }
+
+ private static string Fmt(double d) => d.ToString("0.####", CultureInfo.InvariantCulture);
+}
diff --git a/CSharpBible/Games/Treppen.Print/Services/PrintExporterAttribute.cs b/CSharpBible/Games/Treppen.Print/Services/PrintExporterAttribute.cs
new file mode 100644
index 000000000..d03806b60
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Print/Services/PrintExporterAttribute.cs
@@ -0,0 +1,14 @@
+namespace Treppen.Print.Services;
+
+[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+public sealed class PrintExporterAttribute : Attribute
+{
+ public string Name { get; }
+ public string[] Extensions { get; }
+
+ public PrintExporterAttribute(string name, params string[] extensions)
+ {
+ Name = name;
+ Extensions = extensions.Length == 0 ? new[] { ".svg" } : extensions;
+ }
+}
diff --git a/CSharpBible/Games/Treppen.Print/Services/PrintExporterRegistry.cs b/CSharpBible/Games/Treppen.Print/Services/PrintExporterRegistry.cs
new file mode 100644
index 000000000..6ef8b8b92
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Print/Services/PrintExporterRegistry.cs
@@ -0,0 +1,43 @@
+using System.Reflection;
+
+namespace Treppen.Print.Services;
+
+public static class PrintExporterRegistry
+{
+ private static readonly Dictionary _byExtension = new(StringComparer.OrdinalIgnoreCase);
+ private static readonly Dictionary _byName = new(StringComparer.OrdinalIgnoreCase);
+ private static bool _initialized;
+
+ public static void EnsureInitialized(params Assembly[] assemblies)
+ {
+ if (_initialized) return;
+ var scan = assemblies != null && assemblies.Length > 0 ? assemblies : new[] { Assembly.GetExecutingAssembly() };
+
+ foreach (var asm in scan)
+ {
+ foreach (var t in asm.GetTypes())
+ {
+ var attr = t.GetCustomAttribute();
+ if (attr == null) continue;
+ _byName[attr.Name] = t;
+ foreach (var ext in attr.Extensions)
+ {
+ _byExtension[ext.StartsWith('.') ? ext : "." + ext] = t;
+ }
+ }
+ }
+ _initialized = true;
+ }
+
+ public static Type? GetByExtension(string extension)
+ {
+ EnsureInitialized();
+ return _byExtension.TryGetValue(extension, out var t) ? t : null;
+ }
+
+ public static Type? GetByName(string name)
+ {
+ EnsureInitialized();
+ return _byName.TryGetValue(name, out var t) ? t : null;
+ }
+}
diff --git a/CSharpBible/Games/Treppen.Print/Services/PrintRenderer.cs b/CSharpBible/Games/Treppen.Print/Services/PrintRenderer.cs
new file mode 100644
index 000000000..c419cb628
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Print/Services/PrintRenderer.cs
@@ -0,0 +1,41 @@
+using BaseLib.Helper;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Treppen.Base;
+using Treppen.Export.Models;
+using Treppen.Export.Services.Drawing;
+using Treppen.Export.Services.Interfaces;
+
+namespace Treppen.Print.Services;
+
+public class PrintRenderer : IPrintRenderer
+{
+ public Task RenderAsync(IHeightLabyrinth labyrinth, PrintOptions options, Stream output)
+ {
+ var threeDVisualSrv = IoC.GetRequiredService();
+ var printDialog = new PrintDialog();
+ if (printDialog.ShowDialog() != true)
+ {
+ return Task.CompletedTask;
+ }
+
+ DrawingVisual? threeDVisual = null;
+
+ if (threeDVisualSrv is not null && labyrinth is not null)
+ {
+ threeDVisual = new DrawingVisual();
+ using (var dc = threeDVisual.RenderOpen())
+ {
+ printDialog.PrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;
+ var printableSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
+ // Baue Kommandos und rendere in den WPF-DrawingContext
+ threeDVisualSrv.Build(labyrinth, printableSize, IoC.GetKeyedRequiredService("dc")).Render(dc);
+ }
+
+ printDialog.PrintVisual(threeDVisual, "Labyrinth 3D");
+
+ }
+ return Task.CompletedTask;
+ }
+}
diff --git a/CSharpBible/Games/Treppen.Print/Treppen.Print.csproj b/CSharpBible/Games/Treppen.Print/Treppen.Print.csproj
new file mode 100644
index 000000000..9e6ab78ca
--- /dev/null
+++ b/CSharpBible/Games/Treppen.Print/Treppen.Print.csproj
@@ -0,0 +1,32 @@
+
+
+
+
+ net8.0-windows
+ preview
+ enable
+ enable
+ Treppen.Print
+ Treppen.Print
+ Treppen
+ Print/Export services for Treppen framework
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Treppen.WPF/App.xaml b/CSharpBible/Games/Treppen.WPF/App.xaml
new file mode 100644
index 000000000..7a8b62eda
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/Games/Treppen.WPF/App.xaml.cs b/CSharpBible/Games/Treppen.WPF/App.xaml.cs
new file mode 100644
index 000000000..e547e0597
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/App.xaml.cs
@@ -0,0 +1,16 @@
+using System.Windows;
+
+namespace Treppen.WPF
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ base.OnStartup(e);
+ ViewModelLocator.Init();
+ }
+ }
+}
diff --git a/CSharpBible/Games/Treppen.WPF/AssemblyInfo.cs b/CSharpBible/Games/Treppen.WPF/AssemblyInfo.cs
new file mode 100644
index 000000000..b0ec82757
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/Games/Treppen.WPF/Controls/Labyrinth3DView.cs b/CSharpBible/Games/Treppen.WPF/Controls/Labyrinth3DView.cs
new file mode 100644
index 000000000..5602d2b42
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/Controls/Labyrinth3DView.cs
@@ -0,0 +1,75 @@
+using BaseLib.Helper;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Treppen.Base;
+using Treppen.Export.Services.Interfaces;
+using Treppen.Export.Services.Drawing;
+
+namespace Treppen.WPF.Controls;
+
+public class Labyrinth3DView : Canvas
+{
+ public static readonly DependencyProperty LabyrinthProperty =
+ DependencyProperty.Register(nameof(Labyrinth), typeof(IHeightLabyrinth), typeof(Labyrinth3DView),
+ new PropertyMetadata(null, OnLabyrinthChanged));
+
+ public IHeightLabyrinth Labyrinth
+ {
+ get { return (IHeightLabyrinth)GetValue(LabyrinthProperty); }
+ set { SetValue(LabyrinthProperty, value); }
+ }
+
+ private IHeightLabyrinth? _currentLabyrinth; // track subscription
+
+ private static void OnLabyrinthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is Labyrinth3DView view)
+ {
+ if (e.NewValue is IHeightLabyrinth lab)
+ {
+ view._currentLabyrinth = lab;
+ view.DrawLabyrinth(lab); // initial draw
+ }
+ else
+ {
+ view._currentLabyrinth = null;
+ view.Children.Clear();
+ }
+ }
+ }
+
+ private void DrawLabyrinth(IHeightLabyrinth labyrinth)
+ {
+ Children.Clear();
+ if (labyrinth == null || labyrinth.Dimension.Width == 0 || labyrinth.Dimension.Height == 0)
+ return;
+
+ // Einen gltigen DrawingContext ber DrawingGroup.Open() erhalten
+ var drawingGroup = new DrawingGroup();
+ using (DrawingContext dc = drawingGroup.Open())
+ {
+ IoC.GetRequiredService()
+ .Build(labyrinth, new Size(ActualWidth, ActualHeight),IoC.GetKeyedRequiredService("dc")).Render(dc);
+ }
+
+ // Das Ergebnis als Bild auf dem Canvas anzeigen
+ var img = new Image
+ {
+ Source = new DrawingImage(drawingGroup),
+ Width = ActualWidth,
+ Height = ActualHeight
+ };
+ Children.Add(img);
+ }
+
+
+ protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
+ {
+ base.OnRenderSizeChanged(sizeInfo);
+ if (_currentLabyrinth != null)
+ {
+ DrawLabyrinth(_currentLabyrinth);
+ }
+ }
+}
diff --git a/CSharpBible/Games/Treppen.WPF/MainWindow.xaml b/CSharpBible/Games/Treppen.WPF/MainWindow.xaml
new file mode 100644
index 000000000..09d88ccbf
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/MainWindow.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Treppen.WPF/MainWindow.xaml.cs b/CSharpBible/Games/Treppen.WPF/MainWindow.xaml.cs
new file mode 100644
index 000000000..920ecac77
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/MainWindow.xaml.cs
@@ -0,0 +1,15 @@
+using System.Windows;
+
+namespace Treppen.WPF
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Games/Treppen.WPF/Printing/Svg3DPrintRenderer.cs b/CSharpBible/Games/Treppen.WPF/Printing/Svg3DPrintRenderer.cs
new file mode 100644
index 000000000..ae8e1abfe
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/Printing/Svg3DPrintRenderer.cs
@@ -0,0 +1,92 @@
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Windows;
+using System.Windows.Media;
+using Treppen.Base;
+using Treppen.WPF.Services.Drawing;
+using Treppen.Export.Models;
+using Treppen.Export.Services.Interfaces;
+using Treppen.Print.Services;
+
+namespace Treppen.WPF.Printing;
+
+[PrintExporter("SVG", ".svg")]
+public sealed class Svg3DPrintRenderer : IPrintRenderer
+{
+ public async Task RenderAsync(IHeightLabyrinth labyrinth, PrintOptions options, Stream output)
+ {
+ ArgumentNullException.ThrowIfNull(labyrinth);
+ ArgumentNullException.ThrowIfNull(options);
+ ArgumentNullException.ThrowIfNull(output);
+
+ var dim = labyrinth.Dimension;
+ int wCells = dim.Width;
+ int hCells = dim.Height;
+ if (wCells <= 0 || hCells <= 0)
+ throw new InvalidOperationException("Leeres Labyrinth kann nicht gedruckt werden.");
+
+ var drawer = BaseLib.Helper.IoC.GetRequiredService();
+ var factory = new SvgDrawCommandFactory();
+ var drawWidth = Math.Max(1, options.CellSize) * wCells;
+ var drawHeight = Math.Max(1, options.CellSize) * hCells;
+ var commands = drawer.Build(labyrinth, new Size(drawWidth, drawHeight), factory);
+
+ var allPoints = commands.SelectMany(c => c switch
+ {
+ SvgPolygonCommand p => p.Points,
+ SvgPolyLineCommand l => l.Points,
+ _ => Array.Empty()
+ }).ToArray();
+
+ double minX = allPoints.Length > 0 ? allPoints.Min(p => p.X) : 0;
+ double minY = allPoints.Length > 0 ? allPoints.Min(p => p.Y) : 0;
+ double maxX = allPoints.Length > 0 ? allPoints.Max(p => p.X) : drawWidth;
+ double maxY = allPoints.Length > 0 ? allPoints.Max(p => p.Y) : drawHeight;
+
+ double width = (maxX - minX) + options.MarginLeft + options.MarginRight;
+ double height = (maxY - minY) + options.MarginTop + options.MarginBottom;
+
+ var sb = new StringBuilder(128 * 1024);
+ sb.Append("\n");
+ sb.Append($"");
+
+ var utf8 = Encoding.UTF8.GetBytes(sb.ToString());
+ await output.WriteAsync(utf8, 0, utf8.Length).ConfigureAwait(false);
+ }
+
+ private static string Escape(string text)
+ => text.Replace("&", "&").Replace("<", "<").Replace(">", ">")
+ .Replace("\"", """).Replace("'", "'");
+
+ private static string ToSvgColor(Color color)
+ => $"rgb({color.R},{color.G},{color.B})";
+
+ private static string Fmt(double d) => d.ToString("0.###", CultureInfo.InvariantCulture);
+}
diff --git a/CSharpBible/Games/Treppen.WPF/Services/Drawing/DrawCommands.cs b/CSharpBible/Games/Treppen.WPF/Services/Drawing/DrawCommands.cs
new file mode 100644
index 000000000..354e01b85
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/Services/Drawing/DrawCommands.cs
@@ -0,0 +1,79 @@
+using System.Windows;
+using System.Windows.Media;
+using Treppen.Export.Services.Interfaces;
+
+namespace Treppen.WPF.Services.Drawing;
+
+public class DrawContextDrawingFactory : IDrawCommandFactory
+{
+ public IDrawCommand newPolygonCommand(Point[] points, Color fill, Color? outline, double v) => new PolygonCommand(points, fill, outline, v);
+ public IDrawCommand newPolyLineCommand(Point[] rampPoints, Color color, double v1, bool v2) => new PolyLineCommand(rampPoints, color, v1, v2);
+}
+
+public sealed class PolygonCommand : IDrawCommand
+{
+ public PolygonCommand(Point[] points, Color? fill, Color? stroke, double strokeThickness)
+ {
+ Points = points;
+ Fill = fill;
+ Stroke = stroke;
+ StrokeThickness = strokeThickness;
+ }
+ public Point[] Points { get; }
+ public Color? Fill { get; }
+ public Color? Stroke { get; }
+ public double StrokeThickness { get; }
+
+ public void Render(object dc)
+ {
+ if (Points.Length == 0) return;
+ var geometry = new StreamGeometry();
+ using (var ctx = geometry.Open())
+ {
+ ctx.BeginFigure(Points[0], true, true);
+ for (int i = 1; i < Points.Length; i++)
+ {
+ ctx.LineTo(Points[i], true, false);
+ }
+ }
+ geometry.Freeze();
+ Brush? fillBrush = Fill.HasValue ? new SolidColorBrush(Fill.Value) : null;
+ Brush? strokeBrush = Stroke.HasValue ? new SolidColorBrush(Stroke.Value) : null;
+ if (fillBrush is SolidColorBrush fb && !fb.IsFrozen) fb.Freeze();
+ if (strokeBrush is SolidColorBrush sb && !sb.IsFrozen) sb.Freeze();
+ Pen? pen = strokeBrush != null && StrokeThickness > 0 ? new Pen(strokeBrush, StrokeThickness) : null;
+ ((DrawingContext)dc)?.DrawGeometry(fillBrush, pen, geometry);
+ }
+}
+
+public sealed class PolyLineCommand : IDrawCommand
+{
+ public PolyLineCommand(Point[] points, Color stroke, double strokeThickness, bool drawPoints)
+ {
+ Points = points;
+ Stroke = stroke;
+ StrokeThickness = strokeThickness;
+ DrawPoints = drawPoints;
+ }
+ public Point[] Points { get; }
+ public Color Stroke { get; }
+ public double StrokeThickness { get; }
+ public bool DrawPoints { get; }
+
+ public void Render(object dc)
+ {
+ if (Points.Length == 0) return;
+ var strokeBrush = new SolidColorBrush(Stroke);
+ if (!strokeBrush.IsFrozen) strokeBrush.Freeze();
+ var pen = new Pen(strokeBrush, StrokeThickness);
+ ((DrawingContext)dc)?.DrawEllipse(strokeBrush, null, Points[0], StrokeThickness, StrokeThickness);
+ for (int i = 1; i < Points.Length; i++)
+ {
+ ((DrawingContext)dc)?.DrawLine(pen, Points[i - 1], Points[i]);
+ if (DrawPoints)
+ {
+ ((DrawingContext)dc)?.DrawEllipse(strokeBrush, null, Points[i], StrokeThickness, StrokeThickness);
+ }
+ }
+ }
+}
diff --git a/CSharpBible/Games/Treppen.WPF/Services/Drawing/SvgDrawCommands.cs b/CSharpBible/Games/Treppen.WPF/Services/Drawing/SvgDrawCommands.cs
new file mode 100644
index 000000000..a645d5f56
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/Services/Drawing/SvgDrawCommands.cs
@@ -0,0 +1,49 @@
+using System.Windows;
+using System.Windows.Media;
+using Treppen.Export.Services.Interfaces;
+
+namespace Treppen.WPF.Services.Drawing;
+
+// SVG specific draw commands captured from the 3D drawer
+public sealed class SvgPolygonCommand : IDrawCommand
+{
+ public SvgPolygonCommand(Point[] points, Color? fill, Color? stroke, double strokeThickness)
+ {
+ Points = points;
+ Fill = fill;
+ Stroke = stroke;
+ StrokeThickness = strokeThickness;
+ }
+ public Point[] Points { get; }
+ public Color? Fill { get; }
+ public Color? Stroke { get; }
+ public double StrokeThickness { get; }
+
+ public void Render(object dc) { /* no-op for SVG */ }
+}
+
+public sealed class SvgPolyLineCommand : IDrawCommand
+{
+ public SvgPolyLineCommand(Point[] points, Color stroke, double strokeThickness, bool drawPoints)
+ {
+ Points = points;
+ Stroke = stroke;
+ StrokeThickness = strokeThickness;
+ DrawPoints = drawPoints;
+ }
+ public Point[] Points { get; }
+ public Color Stroke { get; }
+ public double StrokeThickness { get; }
+ public bool DrawPoints { get; }
+
+ public void Render(object dc) { /* no-op for SVG */ }
+}
+
+public sealed class SvgDrawCommandFactory : IDrawCommandFactory
+{
+ public IDrawCommand newPolygonCommand(Point[] points, Color fill, Color? outline, double strokeThickness)
+ => new SvgPolygonCommand(points, fill, outline, strokeThickness);
+
+ public IDrawCommand newPolyLineCommand(Point[] points, Color color, double strokeThickness, bool drawPoints)
+ => new SvgPolyLineCommand(points, color, strokeThickness, drawPoints);
+}
diff --git a/CSharpBible/Games/Treppen.WPF/Services/DrawingService.cs b/CSharpBible/Games/Treppen.WPF/Services/DrawingService.cs
new file mode 100644
index 000000000..7324915ab
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/Services/DrawingService.cs
@@ -0,0 +1,82 @@
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Treppen.Base;
+using Treppen.WPF.Services.Interfaces;
+
+namespace Treppen.WPF.Services;
+
+public class DrawingService : IDrawingService
+{
+ public BitmapSource CreateLabyrinthPreview(IHeightLabyrinth labyrinth)
+ {
+ int width = labyrinth.Dimension.Width;
+ int height = labyrinth.Dimension.Height;
+ var wb = new WriteableBitmap(width*5, height*5, 96, 96, PixelFormats.Bgra32, null);
+ var pixels = new Int32[width*5 * height*5];
+
+ int min = int.MaxValue;
+ int max = int.MinValue;
+
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ var h = labyrinth[x, y];
+ if (h < min) min = h;
+ if (h > max) max = h;
+ }
+ }
+
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ int lab_x = width - x - 1;
+ int lab_y = height - y - 1;
+
+ var h = labyrinth[lab_x, lab_y];
+ int color = ((byte)(h * 128 / max )*(1<<16)+ (byte)(h * 128 / max)*(1<<8)+ (byte)(h * 128 / max+64))+(int)(0xff<<24);
+ // Flle ein 5x5 Pixel groes Quadrat
+ for (int dy = 0; dy < 5; dy++)
+ {
+ for (int dx = 0; dx < 5; dx++)
+ {
+ pixels[((y * 5) + dy) * (width * 5) + ((x * 5) + dx)] = color;
+ }
+ }
+
+
+ // Prfe linken Nachbarn
+ if (lab_x < width - 1)
+ {
+ var h_right = labyrinth[lab_x + 1, lab_y];
+ if (Math.Abs(h - h_right) <= 1)
+ {
+ // Zeichne eine horizontale weie Linie am unteren Rand des 5x5 Quadrats
+ for (int dx = 0; dx <= 5; dx++)
+ {
+ pixels[((y * 5) + 2) * (width * 5) + (x * 5) - dx + 2] = -1;
+ }
+ }
+ }
+
+ // Prfe unteren Nachbarn
+ if (lab_y < height-1 )
+ {
+ var h_down = labyrinth[lab_x, lab_y + 1];
+ if (Math.Abs(h - h_down) <= 1)
+ {
+ // Zeichne eine vertikale weie Linie am rechten Rand des 5x5 Quadrats
+ for (int dy = 0; dy <= 5; dy++)
+ {
+ pixels[((y * 5) - dy + 2) * (width * 5) + (x * 5) + 2] = -1;
+ }
+ }
+ }
+ }
+ }
+
+ wb.WritePixels(new System.Windows.Int32Rect(0, 0, width*5, height*5), pixels, width*5*sizeof(int) , 0);
+ return wb;
+ }
+}
diff --git a/CSharpBible/Games/Treppen.WPF/Services/Interfaces/IDrawingService.cs b/CSharpBible/Games/Treppen.WPF/Services/Interfaces/IDrawingService.cs
new file mode 100644
index 000000000..a8c704f24
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/Services/Interfaces/IDrawingService.cs
@@ -0,0 +1,9 @@
+using System.Windows.Media.Imaging;
+using Treppen.Base;
+
+namespace Treppen.WPF.Services.Interfaces;
+
+public interface IDrawingService
+{
+ BitmapSource CreateLabyrinthPreview(IHeightLabyrinth labyrinth);
+}
diff --git a/CSharpBible/Games/Treppen.WPF/Services/Labyrinth3dDrawer.cs b/CSharpBible/Games/Treppen.WPF/Services/Labyrinth3dDrawer.cs
new file mode 100644
index 000000000..5ee9772a4
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/Services/Labyrinth3dDrawer.cs
@@ -0,0 +1,157 @@
+using System.Windows;
+using System.Windows.Media;
+using Treppen.Base;
+using Treppen.Export.Services.Interfaces;
+
+namespace Treppen.WPF.Services;
+
+public class Labyrinth3dDrawer : ILabyrinth3dDrawer
+{
+
+ private double ViewZ = 5000d;
+ private double tileWidth;
+ private double tileHeight;
+ private double stepHeight;
+ private double offsetX;
+ private IDrawCommandFactory _factory;
+
+ public double offsetY { get; private set; }
+
+ public IReadOnlyList Build(IHeightLabyrinth labyrinth, Size printableSize, IDrawCommandFactory dcFactory)
+ {
+ var result = new List();
+ _factory = dcFactory;
+ double labHW = labyrinth.Dimension.Width + labyrinth.Dimension.Height;
+ if (printableSize.Width < double.Epsilon) return result;
+
+ ViewZ = -printableSize.Width * 2d;
+
+ tileWidth = printableSize.Width / (labHW + 4) * 1.4;
+ tileHeight = tileWidth;
+ stepHeight = tileHeight / 5d;
+
+ offsetX = printableSize.Width / 2;
+ offsetY = printableSize.Height / 2.5;
+
+ for (int i = 0; i < labHW + 4; i++)
+ {
+ for (int y = labyrinth.Dimension.Height - 1; y >= 0; y--)
+ {
+ for (int x = labyrinth.Dimension.Width - 1; x >= 0; x--)
+ {
+ if (x == 0 || y == 0 || i + 6 > labyrinth.BaseLevel(x, y))
+ {
+ int h = labyrinth[x, y];
+ if (h < 0 || i > h)
+ {
+ continue;
+ }
+
+ bool drawTop = i == h;
+ bool drawLeft = y == 0 || labyrinth[x, y - 1] < i;
+ bool drawRight = x == 0 || labyrinth[x - 1, y] < i;
+
+ int hDiag = (x < labyrinth.Dimension.Width && y > 0) ? labyrinth[x + 1, y - 1] : -1;
+ int hLeft = (x < labyrinth.Dimension.Width) ? labyrinth[x + 1, y] : -1;
+
+ BuildCube(result, -x + labyrinth.Dimension.Width / 2d, y - labyrinth.Dimension.Height / 2d, -i + (labHW + 4) / 2d,
+ drawTop, drawLeft, drawRight, i, hDiag, hLeft);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private Point ToScreen(double x, double y, double z)
+ {
+ (x, y) = RotateTransform(x, y, -Math.PI * 0.22);
+ (y, z) = RotateTransform(y, z, Math.PI / 3);
+ return z > ViewZ ? new Point(offsetX + x * ViewZ / (ViewZ - z), offsetY - y * ViewZ / (ViewZ - z)) : new Point();
+ }
+
+ private (double x, double y) RotateTransform(double x, double y, double v)
+ {
+ return (x * Math.Cos(v) - y * Math.Sin(v), x * Math.Sin(v) + y * Math.Cos(v));
+ }
+
+ private void BuildCube(List list,
+ double x,
+ double y,
+ double z,
+ bool drawTop,
+ bool drawLeft,
+ bool drawRight,
+ int currentHeight,
+ int hDiag,
+ int hLeft)
+ {
+ if (drawLeft)
+ {
+ list.Add(_factory.newPolygonCommand(
+ [
+ ToScreen((x - 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x + 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x + 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z + 0.5) * stepHeight),
+ ToScreen((x - 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z + 0.5) * stepHeight)
+ ], Colors.LightGray, Colors.White, 0.5));
+
+ // Ramp diagonally (top-left to right) if higher/equal
+ if (hDiag >= currentHeight)
+ {
+ double s = (hDiag - currentHeight) * 0.1d;
+ double s2 = (hDiag - currentHeight + 1) * 0.1d;
+ var rampPoints = new[]
+ {
+ ToScreen((x - 0.5d + s) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x - 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x - 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z + 0.5) * stepHeight),
+ ToScreen((x - 0.5d + s2) * tileWidth, (y - 0.5) * tileHeight, (z + 0.5) * stepHeight)
+ };
+ list.Add(_factory.newPolygonCommand(rampPoints, Colors.DarkGray, null, 0));
+ list.Add(_factory.newPolyLineCommand(rampPoints, Colors.Gray, 0.5, true));
+ list.Add(_factory.newPolyLineCommand(
+ [
+ ToScreen((x - 0.5d + s) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x - 0.5d + s2) * tileWidth, (y - 0.5) * tileHeight, (z + 0.5) * stepHeight)
+ ], Colors.LightGray, 0.5, true));
+ }
+ }
+
+ if (drawRight)
+ {
+ list.Add(_factory.newPolygonCommand(
+ [
+ ToScreen((x + 0.5d) * tileWidth, (y + 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x + 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x + 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z + 0.5) * stepHeight),
+ ToScreen((x + 0.5d) * tileWidth, (y + 0.5) * tileHeight, (z + 0.5) * stepHeight)
+ ], Colors.DarkGray, Colors.Gray, 0.5));
+ }
+
+ if (drawTop)
+ {
+ list.Add(_factory.newPolygonCommand(
+ [
+ ToScreen((x - 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x + 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x + 0.5d) * tileWidth, (y + 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x - 0.5d) * tileWidth, (y + 0.5) * tileHeight, (z - 0.5) * stepHeight)
+ ], Colors.White, Colors.LightGray, 0.5));
+
+ // Schrge Flche zur hheren Zelle rechts (Logik x-1 -> hLeft)
+ if (hLeft > currentHeight)
+ {
+ double s = (hLeft - currentHeight) * 0.1d;
+ list.Add(_factory.newPolygonCommand(
+ [
+ ToScreen((x - 0.5d) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x - 0.5d) * tileWidth, (y + 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x - 0.5d + s) * tileWidth, (y + 0.5) * tileHeight, (z - 0.5) * stepHeight),
+ ToScreen((x - 0.5d + s) * tileWidth, (y - 0.5) * tileHeight, (z - 0.5) * stepHeight)
+ ], Colors.DarkGray, Colors.LightGray, 0.5));
+ }
+ }
+
+ }
+}
diff --git a/CSharpBible/Games/Treppen.WPF/Treppen.WPF.csproj b/CSharpBible/Games/Treppen.WPF/Treppen.WPF.csproj
new file mode 100644
index 000000000..309ea60bb
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/Treppen.WPF.csproj
@@ -0,0 +1,23 @@
+
+
+
+ WinExe
+ net9.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Games/Treppen.WPF/ViewModelLocator.cs b/CSharpBible/Games/Treppen.WPF/ViewModelLocator.cs
new file mode 100644
index 000000000..1db3696b5
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/ViewModelLocator.cs
@@ -0,0 +1,32 @@
+using BaseLib.Helper;
+using Microsoft.Extensions.DependencyInjection;
+using Treppen.Base;
+using Treppen.Export.Services.Interfaces;
+using Treppen.Print.Services;
+using Treppen.WPF.Services;
+using Treppen.WPF.Services.Drawing;
+using Treppen.WPF.Services.Interfaces;
+using Treppen.WPF.ViewModels;
+
+namespace Treppen.WPF;
+
+public class ViewModelLocator
+{
+ private static ServiceProvider _provider;
+
+ public static void Init()
+ {
+ var services = new ServiceCollection();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddKeyedSingleton("dc");
+ services.AddKeyedSingleton("prn");
+
+ _provider = services.BuildServiceProvider();
+ IoC.Configure(_provider);
+ }
+
+ public MainWindowViewModel MainWindowViewModel => _provider.GetRequiredService();
+}
diff --git a/CSharpBible/Games/Treppen.WPF/ViewModels/MainWindowViewModel.cs b/CSharpBible/Games/Treppen.WPF/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..645df20f6
--- /dev/null
+++ b/CSharpBible/Games/Treppen.WPF/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,118 @@
+using BaseLib.Helper;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using System.IO;
+using System.Windows;
+using System.Windows.Media.Imaging;
+using Treppen.Base;
+using Treppen.Export.Models;
+using Treppen.Export.Services.Interfaces;
+using Treppen.Print.Services;
+using Treppen.WPF.Printing;
+using Treppen.WPF.Services.Interfaces;
+
+namespace Treppen.WPF.ViewModels;
+
+public partial class MainWindowViewModel : ObservableObject
+{
+ [ObservableProperty]
+ private int _size = 21;
+
+ [ObservableProperty]
+ private BitmapSource? _previewImage;
+
+ [ObservableProperty]
+ private IHeightLabyrinth _labyrinth;
+
+ private readonly IDrawingService _drawingService;
+
+ public MainWindowViewModel(IHeightLabyrinth labyrinth, IDrawingService drawingService)
+ {
+ _labyrinth = labyrinth;
+ _drawingService = drawingService;
+ Generate();
+ }
+
+ [RelayCommand]
+ private void Generate()
+ {
+ var _l = Labyrinth;
+ Labyrinth = null; // Clear reference to allow GC of previous labyrinth
+ _l.Dimension = new System.Drawing.Rectangle(0, 0, Size, Size);
+ _l.Generate();
+ PreviewImage = _drawingService.CreateLabyrinthPreview(_l);
+ Labyrinth=_l; // Notify that the labyrinth has changed
+ }
+
+ [RelayCommand]
+ private void Draw()
+ {
+ var _l = Labyrinth;
+ Labyrinth = null; // Clear reference to allow GC of previous labyrinth
+ Labyrinth = _l; // Notify that the labyrinth has changed
+ }
+
+ [RelayCommand]
+ private void Print()
+ {
+ IoC.GetKeyedRequiredService("prn").RenderAsync(Labyrinth,null,null).GetAwaiter().GetResult();
+ }
+
+ [RelayCommand]
+ private void SaveAs()
+ {
+ var sfd = new Microsoft.Win32.SaveFileDialog
+ {
+ Title = "Labyrinth exportieren",
+ Filter = BuildFilter(),
+ AddExtension = true,
+ OverwritePrompt = true
+ };
+ if (sfd.ShowDialog() != true) return;
+
+ var fileExt = System.IO.Path.GetExtension(sfd.FileName);
+ // Registriere alle Exporter (Reihenfolge: WPF-3D-SVG zuletzt, damit es .svg berschreibt)
+ PrintExporterRegistry.EnsureInitialized(
+ typeof(Treppen.Print.Rendering.ObjPrintRenderer).Assembly,
+ typeof(Svg3DPrintRenderer).Assembly);
+
+ var type = PrintExporterRegistry.GetByExtension(fileExt);
+ if (type == null)
+ {
+ MessageBox.Show($"Kein Exporter fr '{fileExt}' gefunden.", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+ if (Activator.CreateInstance(type) is not IPrintRenderer renderer)
+ {
+ MessageBox.Show($"Exporter '{type.Name}' konnte nicht erstellt werden.", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ using var fs = File.Create(sfd.FileName);
+ var options = new PrintOptions
+ {
+ Title = "Treppen Labyrinth",
+ CellSize = 40,
+ MarginLeft = 10,
+ MarginTop = 10,
+ MarginRight = 10,
+ MarginBottom = 10
+ };
+ renderer.RenderAsync(Labyrinth, options, fs).GetAwaiter().GetResult();
+ }
+
+ private static string BuildFilter()
+ {
+ // Registriere alle Exporter (WPF-3D-SVG zuletzt)
+ PrintExporterRegistry.EnsureInitialized(
+ typeof(Treppen.Print.Rendering.ObjPrintRenderer).Assembly,
+ typeof(Svg3DPrintRenderer).Assembly);
+
+ var svgType = PrintExporterRegistry.GetByName("SVG");
+ var objType = PrintExporterRegistry.GetByName("Wavefront OBJ");
+ string svgPart = svgType != null ? "SVG (*.svg)|*.svg" : string.Empty;
+ string objPart = objType != null ? "Wavefront OBJ (*.obj)|*.obj" : string.Empty;
+ string all = string.Join("|", new[] { svgPart, objPart }.Where(s => !string.IsNullOrEmpty(s)));
+ return string.IsNullOrEmpty(all) ? "Alle Dateien (*.*)|*.*" : all;
+ }
+}
diff --git a/CSharpBible/Games/VTileEdit/VTileEdit.csproj b/CSharpBible/Games/VTileEdit/VTileEdit.csproj
index 0e003986a..526511621 100644
--- a/CSharpBible/Games/VTileEdit/VTileEdit.csproj
+++ b/CSharpBible/Games/VTileEdit/VTileEdit.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/CSharpBible/Games/VTileEdit/ViewModels/IVTEViewModel.cs b/CSharpBible/Games/VTileEdit/ViewModels/IVTEViewModel.cs
index 079ea974e..e6635813c 100644
--- a/CSharpBible/Games/VTileEdit/ViewModels/IVTEViewModel.cs
+++ b/CSharpBible/Games/VTileEdit/ViewModels/IVTEViewModel.cs
@@ -19,4 +19,7 @@ public interface IVTEViewModel : INotifyPropertyChanged
FullColor[] CurrentColors { get; set; }
Func DoNewTileDialog { get; set; }
Size TileSize { get; }
+
+ void SaveToPath(string path);
+ void UpdateCurrentTile(string[] lines, FullColor[] colors);
}
\ No newline at end of file
diff --git a/CSharpBible/Games/VTileEdit/ViewModels/VTEViewModel.cs b/CSharpBible/Games/VTileEdit/ViewModels/VTEViewModel.cs
index a87a968a3..e89989ae9 100644
--- a/CSharpBible/Games/VTileEdit/ViewModels/VTEViewModel.cs
+++ b/CSharpBible/Games/VTileEdit/ViewModels/VTEViewModel.cs
@@ -34,6 +34,8 @@ public VTEViewModel(IVTEModel model)
public partial FullColor[] CurrentColors { get; set; } = Array.Empty();
public Func DoNewTileDialog { get; set; }
+ public Size TileSize => _model.TileSize;
+
public void LoadFromPath(string path)
{
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
diff --git a/CSharpBible/Games/VTileEdit/Views/VTEVisual.cs b/CSharpBible/Games/VTileEdit/Views/VTEVisual.cs
index 1054acdb5..8492dba9c 100644
--- a/CSharpBible/Games/VTileEdit/Views/VTEVisual.cs
+++ b/CSharpBible/Games/VTileEdit/Views/VTEVisual.cs
@@ -3,6 +3,7 @@
using System.Drawing;
using VTileEdit.ViewModels;
using VTileEdit.Models;
+using Microsoft.Win32;
namespace VTileEdit.Views;
@@ -187,7 +188,7 @@ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
var t = new System.Threading.Thread(() =>
{
- using var ofd = new System.Windows.Forms.OpenFileDialog
+ var ofd = new OpenFileDialog
{
Title = "Datei öffnen",
Filter = "Tile-Dateien (*.tdf;*.tdt;*.tdj;*.tdx)|*.tdf;*.tdt;*.tdj;*.tdx|Alle Dateien (*.*)|*.*",
@@ -200,7 +201,7 @@ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
};
var result = ofd.ShowDialog();
- if (result == System.Windows.Forms.DialogResult.OK)
+ if (result == true)
{
chosenPath = ofd.FileName;
}
diff --git a/CSharpBible/Games/VTileEditTests/VTileEditTests.csproj b/CSharpBible/Games/VTileEditTests/VTileEditTests.csproj
index 7c75a276b..152634f2f 100644
--- a/CSharpBible/Games/VTileEditTests/VTileEditTests.csproj
+++ b/CSharpBible/Games/VTileEditTests/VTileEditTests.csproj
@@ -10,8 +10,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Games/VectorGfx/App.xaml b/CSharpBible/Games/VectorGfx/App.xaml
index 4e09d700c..9a9cf0ef1 100644
--- a/CSharpBible/Games/VectorGfx/App.xaml
+++ b/CSharpBible/Games/VectorGfx/App.xaml
@@ -1,9 +1,8 @@
-
-
-
-
+
+
diff --git a/CSharpBible/Games/VectorGfx/VectorGfx.csproj b/CSharpBible/Games/VectorGfx/VectorGfx.csproj
index e47a37126..28391a76e 100644
--- a/CSharpBible/Games/VectorGfx/VectorGfx.csproj
+++ b/CSharpBible/Games/VectorGfx/VectorGfx.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/CSharpBible/Games/VectorGfx2/App.xaml b/CSharpBible/Games/VectorGfx2/App.xaml
index 15c38576a..63c1ca799 100644
--- a/CSharpBible/Games/VectorGfx2/App.xaml
+++ b/CSharpBible/Games/VectorGfx2/App.xaml
@@ -1,9 +1,8 @@
-
-
-
-
+
+
diff --git a/CSharpBible/Games/VectorGfx2/VectorGfx2.csproj b/CSharpBible/Games/VectorGfx2/VectorGfx2.csproj
index e47a37126..28391a76e 100644
--- a/CSharpBible/Games/VectorGfx2/VectorGfx2.csproj
+++ b/CSharpBible/Games/VectorGfx2/VectorGfx2.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/CSharpBible/Games/VectorGfx2/ViewModels/VfxDisplayViewModel.cs b/CSharpBible/Games/VectorGfx2/ViewModels/VfxDisplayViewModel.cs
index 3abeb5866..86f554d67 100644
--- a/CSharpBible/Games/VectorGfx2/ViewModels/VfxDisplayViewModel.cs
+++ b/CSharpBible/Games/VectorGfx2/ViewModels/VfxDisplayViewModel.cs
@@ -3,10 +3,10 @@
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using MVVM.ViewModel;
-using System.Timers;
using System.Windows;
using CommunityToolkit.Mvvm.Input;
using System.Windows.Media;
+using System.Windows.Threading;
namespace VectorGfx2.ViewModels;
@@ -18,8 +18,6 @@ public partial class VfxDisplayViewModel : BaseViewModelCT
[ObservableProperty]
private string _dataText;
- private Timer timer;
-
private double dTime;
[ObservableProperty]
@@ -47,9 +45,10 @@ public VfxDisplayViewModel()
Pnts=[new(-5,-10), new(5, -10), new(10, 0), new(0, 10), new(-10, 0)]},
};
dTime = 0;
- timer = new Timer(40);
- timer.Elapsed += Update;
- timer.Start();
+ var dt = new DispatcherTimer(DispatcherPriority.Background);
+ dt.Interval = TimeSpan.FromMilliseconds(40);
+ dt.Tick += (s, e) => UpdateOnUi();
+ dt.Start();
var rnd = new Random();
for (int i = 0; i < 12; i++)
{
@@ -58,7 +57,7 @@ public VfxDisplayViewModel()
}
}
- private void Update(object? sender, ElapsedEventArgs e)
+ private void UpdateOnUi()
{
dTime += 0.04;
var _l = VisObjects.ToList();
@@ -70,16 +69,12 @@ private void Update(object? sender, ElapsedEventArgs e)
}
Pnts2.Clear();
-
Pnts2.Add(new Point(10 + Math.Sin(dTime) * 10, 10 + Math.Cos(dTime) * 10));
- Pnts2.Add(new Point(10 + Math.Sin(dTime+1) * 10, 10 + Math.Cos(dTime+1) * 10));
- Pnts2.Add(new Point(10 + Math.Sin(dTime+2) * 10, 10 + Math.Cos(dTime+2) * 10));
-
+ Pnts2.Add(new Point(10 + Math.Sin(dTime + 1) * 10, 10 + Math.Cos(dTime + 1) * 10));
+ Pnts2.Add(new Point(10 + Math.Sin(dTime + 2) * 10, 10 + Math.Cos(dTime + 2) * 10));
- //VisObjects = _l;
OnPropertyChanged(nameof(VisObjects));
OnPropertyChanged(nameof(Pnts2));
-
}
[RelayCommand]
diff --git a/CSharpBible/Games/Werner_Flaschbier/Werner_Flaschbier_Console.csproj b/CSharpBible/Games/Werner_Flaschbier/Werner_Flaschbier_Console.csproj
index 482d7daf8..8dacd61e2 100644
--- a/CSharpBible/Games/Werner_Flaschbier/Werner_Flaschbier_Console.csproj
+++ b/CSharpBible/Games/Werner_Flaschbier/Werner_Flaschbier_Console.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/CSharpBible/Games/Werner_Flaschbier_BaseTests/Werner_Flaschbier_BaseTests.csproj b/CSharpBible/Games/Werner_Flaschbier_BaseTests/Werner_Flaschbier_BaseTests.csproj
index f3fcbef78..d10cddf58 100644
--- a/CSharpBible/Games/Werner_Flaschbier_BaseTests/Werner_Flaschbier_BaseTests.csproj
+++ b/CSharpBible/Games/Werner_Flaschbier_BaseTests/Werner_Flaschbier_BaseTests.csproj
@@ -13,8 +13,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Games/Werner_Flaschbier_BaseTests/Werner_Flaschbier_ConsoleTests.csproj b/CSharpBible/Games/Werner_Flaschbier_BaseTests/Werner_Flaschbier_ConsoleTests.csproj
index dbd5ac3e8..7b7a151c3 100644
--- a/CSharpBible/Games/Werner_Flaschbier_BaseTests/Werner_Flaschbier_ConsoleTests.csproj
+++ b/CSharpBible/Games/Werner_Flaschbier_BaseTests/Werner_Flaschbier_ConsoleTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Graphics/All_Graphics/All_Graphics_net.csproj b/CSharpBible/Graphics/All_Graphics/All_Graphics_net.csproj
index 2f0930546..2d83aba59 100644
--- a/CSharpBible/Graphics/All_Graphics/All_Graphics_net.csproj
+++ b/CSharpBible/Graphics/All_Graphics/All_Graphics_net.csproj
@@ -28,8 +28,8 @@
-
-
+
+
diff --git a/CSharpBible/Graphics/Graphics.sln b/CSharpBible/Graphics/Graphics.sln
index 3fc9fa51d..7c4db5d16 100644
--- a/CSharpBible/Graphics/Graphics.sln
+++ b/CSharpBible/Graphics/Graphics.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11222.15 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projektmappenelemente", "Projektmappenelemente", "{658BD492-33FF-4995-AAE7-4F6894E92F0C}"
ProjectSection(SolutionItems) = preProject
@@ -98,10 +98,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WPF-Samples", "WPF-Samples"
..\WPFSamples_2\WPF_Samples_net.props = ..\WPFSamples_2\WPF_Samples_net.props
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Geometry", "..\..\WPF-Samples\Graphics\Geometry\Geometry.csproj", "{87466DDE-BDEF-4531-9BC3-17CFFDEF092D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageView", "..\..\WPF-Samples\Graphics\ImageView\ImageView.csproj", "{6D6156A7-7D25-44F2-9F8B-3BA2C150F3CC}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPF_Geometry", "..\WPFSamples_2\WPF_Geometry\WPF_Geometry.csproj", "{B4D693A6-63AF-48A9-89CC-55BE89AE8FCC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseLib", "..\Libraries\BaseLib\BaseLib.csproj", "{A77A2D02-C709-46C3-AC71-DD39B4BE1BB8}"
@@ -116,6 +112,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Graphics", "Graphics", "{02
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrimeDisc", "PrimeDisc\PrimeDisc.csproj", "{0444BD50-EA3E-48E3-9EBF-1A04C2254432}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HilpertColorMap", "HilpertColorMap\HilpertColorMap.csproj", "{1F252B08-7E3C-41A9-8C28-E6A5F8A7BD71}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenX.Base", "ScreenX.Base\ScreenX.Base.csproj", "{9E4A987B-74C2-FFE1-78F7-716DEF2F613E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenX.BaseTests", "ScreenX.BaseTests\ScreenX.BaseTests.csproj", "{FAAA3EDE-52A8-704C-8810-B1060F2C38F9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -262,14 +264,6 @@ Global
{B1FCD6F4-2DBF-4AC7-AD62-64E830E83764}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1FCD6F4-2DBF-4AC7-AD62-64E830E83764}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1FCD6F4-2DBF-4AC7-AD62-64E830E83764}.Release|Any CPU.Build.0 = Release|Any CPU
- {87466DDE-BDEF-4531-9BC3-17CFFDEF092D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {87466DDE-BDEF-4531-9BC3-17CFFDEF092D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {87466DDE-BDEF-4531-9BC3-17CFFDEF092D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {87466DDE-BDEF-4531-9BC3-17CFFDEF092D}.Release|Any CPU.Build.0 = Release|Any CPU
- {6D6156A7-7D25-44F2-9F8B-3BA2C150F3CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6D6156A7-7D25-44F2-9F8B-3BA2C150F3CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6D6156A7-7D25-44F2-9F8B-3BA2C150F3CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6D6156A7-7D25-44F2-9F8B-3BA2C150F3CC}.Release|Any CPU.Build.0 = Release|Any CPU
{B4D693A6-63AF-48A9-89CC-55BE89AE8FCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B4D693A6-63AF-48A9-89CC-55BE89AE8FCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B4D693A6-63AF-48A9-89CC-55BE89AE8FCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -294,6 +288,18 @@ Global
{0444BD50-EA3E-48E3-9EBF-1A04C2254432}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0444BD50-EA3E-48E3-9EBF-1A04C2254432}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0444BD50-EA3E-48E3-9EBF-1A04C2254432}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F252B08-7E3C-41A9-8C28-E6A5F8A7BD71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F252B08-7E3C-41A9-8C28-E6A5F8A7BD71}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F252B08-7E3C-41A9-8C28-E6A5F8A7BD71}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1F252B08-7E3C-41A9-8C28-E6A5F8A7BD71}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -304,14 +310,15 @@ Global
{E79A022E-5BD3-4539-830A-FA991EC793C1} = {2D82C624-6758-46A6-B7BE-B985F6145CC4}
{BB49E15E-986C-4FB9-8376-66C64D86EF86} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
{CB7C3DBF-3E01-41A1-A5E0-B2EF13FDEE4E} = {2D82C624-6758-46A6-B7BE-B985F6145CC4}
- {87466DDE-BDEF-4531-9BC3-17CFFDEF092D} = {ED69D2D6-14A4-4EC3-AA1A-6C094F21F4C0}
- {6D6156A7-7D25-44F2-9F8B-3BA2C150F3CC} = {ED69D2D6-14A4-4EC3-AA1A-6C094F21F4C0}
{B4D693A6-63AF-48A9-89CC-55BE89AE8FCC} = {ED69D2D6-14A4-4EC3-AA1A-6C094F21F4C0}
{A77A2D02-C709-46C3-AC71-DD39B4BE1BB8} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
{BA9C8374-D44A-4102-B709-B4D2DA4020CF} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
{27DAC5E1-BE1F-4B3B-91B3-4CF40A61A59F} = {2D82C624-6758-46A6-B7BE-B985F6145CC4}
{DA21C175-58F7-4701-B0B1-E0AA5EB624FA} = {ED69D2D6-14A4-4EC3-AA1A-6C094F21F4C0}
{0444BD50-EA3E-48E3-9EBF-1A04C2254432} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {1F252B08-7E3C-41A9-8C28-E6A5F8A7BD71} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {9E4A987B-74C2-FFE1-78F7-716DEF2F613E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {FAAA3EDE-52A8-704C-8810-B1060F2C38F9} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1671921B-67F7-474B-A77C-6D6A7F6DF943}
diff --git a/CSharpBible/Graphics/HilpertColorMap/HilpertColorMap.csproj b/CSharpBible/Graphics/HilpertColorMap/HilpertColorMap.csproj
new file mode 100644
index 000000000..facd910ce
--- /dev/null
+++ b/CSharpBible/Graphics/HilpertColorMap/HilpertColorMap.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
diff --git a/CSharpBible/Graphics/HilpertColorMap/Models/ColorHelpers.cs b/CSharpBible/Graphics/HilpertColorMap/Models/ColorHelpers.cs
new file mode 100644
index 000000000..19e2cd9a5
--- /dev/null
+++ b/CSharpBible/Graphics/HilpertColorMap/Models/ColorHelpers.cs
@@ -0,0 +1,45 @@
+// Sie müssen das NuGet-Paket "System.Drawing.Common" zu Ihrem Projekt hinzufügen, um Bitmap und andere Typen aus System.Drawing zu verwenden.
+// Beispiel (in Visual Studio): Rechtsklick auf das Projekt > NuGet-Pakete verwalten > System.Drawing.Common suchen und installieren.
+
+public static class ColorHelpers
+{
+
+ // Hilfsfunktion zur Konvertierung von HSV zu RGB (C# hat keine direkte eingebaute HSV-Unterstützung in System.Drawing)
+ ///
+ /// Converts a color from HSV (hue, saturation, value) color space to an equivalent RGB color represented as a
+ /// System.Drawing.Color object.
+ ///
+ /// If any parameter is outside its valid range, the resulting color may not be meaningful. The
+ /// alpha channel of the returned color is always set to 255 (fully opaque).
+ /// The hue component of the color, in degrees. Valid values are from 0 to 360.
+ /// The saturation component of the color, as a value between 0 and 1. A value of 0 produces a shade of gray, and 1
+ /// produces the most vivid color.
+ /// The value (brightness) component of the color, as a value between 0 and 1. A value of 0 results in black, and 1
+ /// results in the brightest color.
+ /// A Color structure representing the equivalent RGB color with full opacity (alpha = 255).
+ public static Color ColorFromHSV(double hue, double saturation, double value)
+ {
+ int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
+ double f = hue / 60 - Math.Floor(hue / 60);
+
+ value = value * 255;
+ int v = Convert.ToInt32(value);
+ int p = Convert.ToInt32(value * (1 - saturation));
+ int q = Convert.ToInt32(value * (1 - f * saturation));
+ int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
+
+ if (hi == 0)
+ return Color.FromArgb(255, v, t, p);
+ if (hi == 1)
+ return Color.FromArgb(255, q, v, p);
+ if (hi == 2)
+ return Color.FromArgb(255, p, v, t);
+ if (hi == 3)
+ return Color.FromArgb(255, p, q, v);
+ if (hi == 4)
+ return Color.FromArgb(255, t, p, v);
+ if (hi == 5)
+ return Color.FromArgb(255, v, p, q);
+ return Color.FromArgb(255, 0, 0, 0); // Fehlerfarbe
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Graphics/HilpertColorMap/Models/HilpertColorMap.cs b/CSharpBible/Graphics/HilpertColorMap/Models/HilpertColorMap.cs
new file mode 100644
index 000000000..f3a40499e
--- /dev/null
+++ b/CSharpBible/Graphics/HilpertColorMap/Models/HilpertColorMap.cs
@@ -0,0 +1,51 @@
+// Sie müssen das NuGet-Paket "System.Drawing.Common" zu Ihrem Projekt hinzufügen, um Bitmap und andere Typen aus System.Drawing zu verwenden.
+// Beispiel (in Visual Studio): Rechtsklick auf das Projekt > NuGet-Pakete verwalten > System.Drawing.Common suchen und installieren.
+
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+
+// Stellen Sie sicher, dass Ihr Projekt einen Verweis auf das NuGet-Paket "System.Drawing.Common" hat.
+// In Visual Studio: Rechtsklick auf das Projekt > NuGet-Pakete verwalten > System.Drawing.Common suchen und installieren.
+
+class HilpertColorMap
+{
+ public static Bitmap Map3DTo2DColor(int sizeExponent, int i, int j)
+ {
+ int n = (int)Math.Pow(2, sizeExponent);
+ int totalPixels = n * n;
+ Bitmap img = new Bitmap(n, n, PixelFormat.Format24bppRgb);
+
+ for (int x = 0; x < n; x++)
+ {
+ for (int y = 0; y < n; y++)
+ {
+ int distance = HilpertMath.Xy2d(n, x, y);
+ double normDist = (double)distance / (totalPixels - 1);
+ var r = HilpertMath.GetCubeCoordinateFromDistance(normDist, i);
+ // HSV Werte zuweisen
+ // Hue: 0 bis 360 Grad
+ // Saturation: 1.0 (voll)
+ // Value (Helligkeit): 1.0 (voll)
+ var (hue, saturation, value) = j switch
+ {
+ 1 => (r.X, r.Z, r.Y),
+ 2 => (r.Y, r.X, r.Z),
+ 3 => (r.Z, r.Y, r.X),
+ 4 => (r.X, r.Y, r.Z),
+ 5 => (r.Y, r.Z, r.X),
+ _ => (r.Z, r.X, r.Y),
+ };
+ /*Color pixelColor = Color.FromArgb(
+ (int)(hue * 255),
+ (int)(saturation * 255),
+ (int)(value * 255)
+ );*/
+ Color pixelColor = ColorHelpers.ColorFromHSV(hue * 360.0, saturation, value);
+ img.SetPixel(x, y, pixelColor);
+ }
+ }
+ return img;
+ }
+}
diff --git a/CSharpBible/Graphics/HilpertColorMap/Models/HilpertMath.cs b/CSharpBible/Graphics/HilpertColorMap/Models/HilpertMath.cs
new file mode 100644
index 000000000..37c07e6d4
--- /dev/null
+++ b/CSharpBible/Graphics/HilpertColorMap/Models/HilpertMath.cs
@@ -0,0 +1,145 @@
+// Sie müssen das NuGet-Paket "System.Drawing.Common" zu Ihrem Projekt hinzufügen, um Bitmap und andere Typen aus System.Drawing zu verwenden.
+// Beispiel (in Visual Studio): Rechtsklick auf das Projekt > NuGet-Pakete verwalten > System.Drawing.Common suchen und installieren.
+
+internal static class HilpertMath
+{
+ // Die Hilfsfunktion zur Rotation und Spiegelung, übersetzt aus Python
+ private static (int, int) Rot(int n, int x, int y, bool rx, bool ry)
+ {
+ if (!ry)
+ {
+ if (rx)
+ {
+ (x, y) = (n - 1 - x, n - 1 - y);
+ }
+ // Tausche x und y
+ (x, y) = (y, x);
+ }
+ return (x, y);
+ }
+
+ // Koordinate (x, y) zu Distanz (d) auf der Hilbert-Kurve abbilden
+ public static int Xy2d(int n, int x, int y)
+ {
+ int d = 0;
+ int s = n / 2;
+ while (s > 0)
+ {
+ bool rx = (x & s) > 0;
+ bool ry = (y & s) > 0;
+ d += s * s * ((3 * (rx ? 1 : 0)) ^ (ry ? 1 : 0));
+ (x, y) = Rot(s, x, y, rx, ry);
+ s /= 2;
+ }
+ return d;
+ }
+
+ ///
+ /// Wandelt eine gegebene Distanz (0.0 - 1.0) in eine normalisierte 3D-Koordinate (x,y,z) im Einheitswürfel um.
+ /// Verwendet dazu eine Morton-/Z-Order-Abbildung (Bit-Interleaving) für gleichmäßige Raumabdeckung.
+ ///
+ /// Distanz entlang der linearen Sequenz (0.0 bis 1.0).
+ /// Auflösungsexponent (n = 2^order Punkte pro Achse).
+ /// Tuple (x,y,z) mit Werten im Bereich 0.0 - 1.0.
+ public static (double X, double Y, double Z) GetCubeCoordinateFromDistance(double distance, int order)
+ {
+ if (order < 1 || order > 20)
+ {
+ throw new ArgumentOutOfRangeException(nameof(order), "order muss zwischen 1 und 20 liegen.");
+ }
+
+ distance = Math.Clamp(distance, 0d, 1d);
+
+ int n = 1 << order; // Punkte pro Achse
+ long totalPoints = 1L << (3 * order); // Gesamtanzahl entlang der 3D-Hilbert-Kurve
+ double scaledIndex = distance * (totalPoints - 1);
+ long index = (long)Math.Floor(scaledIndex);
+ double frac = scaledIndex - index;
+
+ var (xi, yi, zi) = HilbertIndexToCoord3D(order, index);
+ var (xi1, yi1, zi1) = HilbertIndexToCoord3D(order, index + 1L);
+
+ double denom = n - 1d;
+ return ((xi * (1 - frac) + xi1 * frac) / denom,
+ (yi * (1 - frac) + yi1 * frac) / denom,
+ (zi * (1 - frac) + zi1 * frac) / denom);
+ }
+
+ // 3D-Hilbert: Index -> (x,y,z) nach Skilling.
+ // Diese Version stellt sicher, dass aufeinanderfolgende Indizes nur in genau einer Dimension um 1 differieren.
+ // Pseudocode:
+ // 1. Transponiere den Hilbert-Index h in D=3 Bitspalten: x[0..2] (je 'order' Bits).
+ // Für Bit-Ebene b (0..order-1): hole 3 Bits aus h und schreibe sie in das b-te Bit jeder Achse.
+ // 2. Gray-Decode der transponierten Form:
+ // t = x[D-1] >> 1
+ // Für i von D-1 bis 1: x[i] ^= x[i-1]
+ // x[0] ^= t
+ // 3. Entwirre Rotationen/Reflexionen gemäß Skilling:
+ // Für q = 2; q < 1< Bitspalten
+ // Wir lesen die Bits in Gruppen zu je nDim (höherwertige Gruppen zuerst).
+ for (int bit = 0; bit < order; bit++)
+ {
+ int group = 0;
+ // Hole die nDim Bits für diese Ebene (von oben nach unten).
+ for (int d = 0; d < nDim; d++)
+ {
+ long shift = (long)bit * nDim + (nDim - 1 - d);
+ group |= (int)(((index >> (byte)shift) & 1) << d);
+ }
+
+ // Schreibe Bits in die jeweiligen Achsen (bit-Position 'bit').
+ for (int d = 0; d < nDim; d++)
+ {
+ int b = (group >> d) & 1;
+ if (b == 1)
+ {
+ x[d] |= (1 << bit);
+ }
+ }
+ }
+
+ // Schritt 2: Gray-Decode
+ int tGray = x[nDim - 1] >> 1;
+ for (int i = nDim - 1; i > 0; i--)
+ {
+ x[i] ^= x[i - 1];
+ }
+ x[0] ^= tGray;
+
+ // Schritt 3: Rotationen/Reflexionen rückgängig machen
+ for (int q = 2; q < (1 << order); q <<= 1)
+ {
+ int p = q - 1;
+ for (int i = nDim - 1; i >= 0; i--)
+ {
+ if ((x[i] & q) != 0)
+ {
+ x[0] ^= p; // reflektiere
+ }
+ else
+ {
+ int t = (x[0] ^ x[i]) & p; // rotiere
+ x[0] ^= t;
+ x[i] ^= t;
+ }
+ }
+ }
+
+ return (x[0], x[1], x[2]);
+ }
+
+}
\ No newline at end of file
diff --git a/CSharpBible/Graphics/HilpertColorMap/Program.cs b/CSharpBible/Graphics/HilpertColorMap/Program.cs
new file mode 100644
index 000000000..df0f1b3e2
--- /dev/null
+++ b/CSharpBible/Graphics/HilpertColorMap/Program.cs
@@ -0,0 +1,59 @@
+using System.Drawing.Imaging;
+
+///
+/// Einstiegspunkt der Anwendung zur Erstellung und Speicherung von farbcodierten Hilbert-Farbkarten.
+///
+///
+/// - Zielplattform: .NET 8, C# 12
+/// - Erfordert das NuGet-Paket System.Drawing.Common, um und verwandte Typen verwenden zu können.
+/// - Bilder werden im Benutzer-Bilderordner unter HilbertColorMaps gespeichert.
+/// - Für verschiedene Parameterkombinationen werden PNG-Dateien erzeugt.
+///
+public static class Program
+{
+ ///
+ /// Hauptmethode der Anwendung. Generiert Hilbert-Farbkarten-Bitmaps mit unterschiedlichen Parametern
+ /// und speichert diese als PNG-Dateien im Bilderordner des Benutzers.
+ ///
+ ///
+ /// Optionale Kommandozeilenargumente (derzeit ungenutzt).
+ ///
+ ///
+ /// Die Bildgröße wird als Potenz von 2 festgelegt (2^exponent).
+ /// Die Schleifen i und j bestimmen verschiedene Konfigurationen für die Farbkartenerzeugung.
+ ///
+ ///
+ /// Ausnahmen, die beim Speichern der Bilddateien auftreten können, werden abgefangen und protokolliert.
+ ///
+ static void Main(string[] args)
+ {
+ // Kantenlänge des Bildes wählen (2 hoch 10 = 1024 Pixel)
+ int exponent = 11;
+
+ // Zielverzeichnis vorbereiten
+ var PictureDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "HilbertColorMaps");
+ Directory.CreateDirectory(PictureDir);
+
+ // Parameterkombinationen durchlaufen und Bilder erzeugen
+ for (int i = 1; i < 7; i++)
+ {
+ for (int j = 1; j < 7; j++)
+ {
+ Bitmap resultImage = HilpertColorMap.Map3DTo2DColor(exponent, i, j);
+ int size = (int)Math.Pow(2, exponent);
+ string filename = $"Hilbert_Color_Map_HSB_{size}_{j}{" abcdefg"[i]}.png";
+
+ // Bild speichern
+ try
+ {
+ resultImage.Save(Path.Combine(PictureDir, filename), ImageFormat.Png);
+ Console.WriteLine($"Bild wurde als '{filename}' gespeichert.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Fehler beim Speichern des Bildes: {ex.Message}");
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Graphics/MVVM_Converter_CTDrawGrid2/Model/Model.cs b/CSharpBible/Graphics/MVVM_Converter_CTDrawGrid2/Model/Model.cs
index f6a171332..71a19474c 100644
--- a/CSharpBible/Graphics/MVVM_Converter_CTDrawGrid2/Model/Model.cs
+++ b/CSharpBible/Graphics/MVVM_Converter_CTDrawGrid2/Model/Model.cs
@@ -15,6 +15,7 @@
using Sokoban_Base.Model;
using System.Drawing;
using BaseLib.Helper;
+using Sokoban.Model;
namespace MVVM_Converter_CTDrawGrid2.Model
{
diff --git a/CSharpBible/Graphics/MVVM_Converter_CTDrawGrid2/ViewModels/PlotFrameViewModel.cs b/CSharpBible/Graphics/MVVM_Converter_CTDrawGrid2/ViewModels/PlotFrameViewModel.cs
index c69dd21b5..c0c1d5e55 100644
--- a/CSharpBible/Graphics/MVVM_Converter_CTDrawGrid2/ViewModels/PlotFrameViewModel.cs
+++ b/CSharpBible/Graphics/MVVM_Converter_CTDrawGrid2/ViewModels/PlotFrameViewModel.cs
@@ -14,8 +14,8 @@
using MVVM.ViewModel;
using System.Drawing;
using MVVM_Converter_CTDrawGrid2.Model;
-using Sokoban_Base.Model;
using CommunityToolkit.Mvvm.ComponentModel;
+using Sokoban.Model;
namespace MVVM_Converter_CTDrawGrid2.ViewModel
{
diff --git a/CSharpBible/Graphics/MVVM_Converter_DrawGrid3_NonLin/Model/Model.cs b/CSharpBible/Graphics/MVVM_Converter_DrawGrid3_NonLin/Model/Model.cs
index 449159464..a125effa4 100644
--- a/CSharpBible/Graphics/MVVM_Converter_DrawGrid3_NonLin/Model/Model.cs
+++ b/CSharpBible/Graphics/MVVM_Converter_DrawGrid3_NonLin/Model/Model.cs
@@ -15,6 +15,7 @@
using Sokoban_Base.Model;
using System.Drawing;
using BaseLib.Helper;
+using Sokoban.Model;
namespace MVVM_Converter_DrawGrid3_NonLin.Model
{
diff --git a/CSharpBible/Graphics/MVVM_Converter_ImgGrid2/Model/Model.cs b/CSharpBible/Graphics/MVVM_Converter_ImgGrid2/Model/Model.cs
index bbb0cc827..f1a405fbc 100644
--- a/CSharpBible/Graphics/MVVM_Converter_ImgGrid2/Model/Model.cs
+++ b/CSharpBible/Graphics/MVVM_Converter_ImgGrid2/Model/Model.cs
@@ -15,6 +15,7 @@
using System;
using Sokoban_Base.Model;
using System.Drawing;
+using Sokoban.Model;
namespace MVVM_Converter_ImgGrid2.Model
{
diff --git a/CSharpBible/Graphics/MVVM_Converter_ImgGrid2/ViewModel/PlotFrameViewModel.cs b/CSharpBible/Graphics/MVVM_Converter_ImgGrid2/ViewModel/PlotFrameViewModel.cs
index f96f0e5c2..f7540dd29 100644
--- a/CSharpBible/Graphics/MVVM_Converter_ImgGrid2/ViewModel/PlotFrameViewModel.cs
+++ b/CSharpBible/Graphics/MVVM_Converter_ImgGrid2/ViewModel/PlotFrameViewModel.cs
@@ -14,7 +14,7 @@
using MVVM.ViewModel;
using System.Drawing;
using MVVM_Converter_ImgGrid2.Model;
-using Sokoban_Base.Model;
+using Sokoban.Model;
namespace MVVM_Converter_ImgGrid2.ViewModel
{
diff --git a/CSharpBible/Graphics/MVVM_ImageHandling/MVVM_ImageHandling.csproj b/CSharpBible/Graphics/MVVM_ImageHandling/MVVM_ImageHandling.csproj
index dba4faa67..01199d57a 100644
--- a/CSharpBible/Graphics/MVVM_ImageHandling/MVVM_ImageHandling.csproj
+++ b/CSharpBible/Graphics/MVVM_ImageHandling/MVVM_ImageHandling.csproj
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/CSharpBible/Graphics/MVVM_ImageHandling/MVVM_ImageHandling_net.csproj b/CSharpBible/Graphics/MVVM_ImageHandling/MVVM_ImageHandling_net.csproj
index 48a734ec6..3a1f4c62d 100644
--- a/CSharpBible/Graphics/MVVM_ImageHandling/MVVM_ImageHandling_net.csproj
+++ b/CSharpBible/Graphics/MVVM_ImageHandling/MVVM_ImageHandling_net.csproj
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/CSharpBible/Graphics/MVVM_ImageHandlingTests/MVVM_ImageHandlingTests.csproj b/CSharpBible/Graphics/MVVM_ImageHandlingTests/MVVM_ImageHandlingTests.csproj
index d07daee22..68455ea2a 100644
--- a/CSharpBible/Graphics/MVVM_ImageHandlingTests/MVVM_ImageHandlingTests.csproj
+++ b/CSharpBible/Graphics/MVVM_ImageHandlingTests/MVVM_ImageHandlingTests.csproj
@@ -9,8 +9,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Graphics/MVVM_ImageHandlingTests/MVVM_ImageHandling_netTests.csproj b/CSharpBible/Graphics/MVVM_ImageHandlingTests/MVVM_ImageHandling_netTests.csproj
index 8222033c0..019cb426a 100644
--- a/CSharpBible/Graphics/MVVM_ImageHandlingTests/MVVM_ImageHandling_netTests.csproj
+++ b/CSharpBible/Graphics/MVVM_ImageHandlingTests/MVVM_ImageHandling_netTests.csproj
@@ -8,9 +8,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Graphics/PermutationTests/PermutationTests.csproj b/CSharpBible/Graphics/PermutationTests/PermutationTests.csproj
index 23841877d..f4eb8a176 100644
--- a/CSharpBible/Graphics/PermutationTests/PermutationTests.csproj
+++ b/CSharpBible/Graphics/PermutationTests/PermutationTests.csproj
@@ -13,8 +13,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Graphics/PrimeDisc/App.xaml b/CSharpBible/Graphics/PrimeDisc/App.xaml
index abbefe3e6..55f036633 100644
--- a/CSharpBible/Graphics/PrimeDisc/App.xaml
+++ b/CSharpBible/Graphics/PrimeDisc/App.xaml
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PrimeDisc"
- StartupUri="Views/MainWindow.xaml">
+ >
diff --git a/CSharpBible/Graphics/PrimeDisc/App.xaml.cs b/CSharpBible/Graphics/PrimeDisc/App.xaml.cs
index 3c79b2f54..0a7950353 100644
--- a/CSharpBible/Graphics/PrimeDisc/App.xaml.cs
+++ b/CSharpBible/Graphics/PrimeDisc/App.xaml.cs
@@ -1,14 +1,42 @@
-using System.Configuration;
-using System.Data;
-using System.Windows;
+using System.Windows;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using PrimePlotter.Services;
+using PrimePlotter.ViewModels;
+using PrimePlotter.Views;
+using PrimePlotter.Services.Interfaces;
namespace PrimeDisc
{
- ///
- /// Interaction logic for App.xaml
- ///
public partial class App : Application
{
- }
+ private IHost? _host;
+
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ base.OnStartup(e);
+ _host = Host.CreateDefaultBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ })
+ .Build();
+ var window = _host.Services.GetRequiredService();
+ window.Show();
+ }
+
+ protected override async void OnExit(ExitEventArgs e)
+ {
+ if (_host != null)
+ {
+ await _host.StopAsync();
+ _host.Dispose();
+ }
+ base.OnExit(e);
+ }
+ }
}
diff --git a/CSharpBible/Graphics/PrimeDisc/Infrastructure/BindableBase.cs b/CSharpBible/Graphics/PrimeDisc/Infrastructure/BindableBase.cs
deleted file mode 100644
index e17f7a8e9..000000000
--- a/CSharpBible/Graphics/PrimeDisc/Infrastructure/BindableBase.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Infrastructure/BindableBase.cs
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-
-namespace PrimePlotter.Infrastructure;
-
-public abstract class BindableBase : INotifyPropertyChanged
-{
- public event PropertyChangedEventHandler? PropertyChanged;
-
- protected bool SetProperty(ref T storage, T value, [CallerMemberName] string? name = null)
- {
- if (Equals(storage, value)) return false;
- storage = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
- return true;
- }
-
- protected void Raise([CallerMemberName] string? name = null) =>
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
-}
diff --git a/CSharpBible/Graphics/PrimeDisc/Infrastructure/RelayCommand.cs b/CSharpBible/Graphics/PrimeDisc/Infrastructure/RelayCommand.cs
deleted file mode 100644
index d566a4045..000000000
--- a/CSharpBible/Graphics/PrimeDisc/Infrastructure/RelayCommand.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Infrastructure/RelayCommand.cs
-using System;
-using System.Windows.Input;
-
-namespace PrimePlotter.Infrastructure;
-
-public class RelayCommand : ICommand
-{
- private readonly Action _execute;
- private readonly Func? _canExecute;
- public RelayCommand(Action execute, Func? canExecute = null)
- {
- _execute = execute;
- _canExecute = canExecute;
- }
- public bool CanExecute(object? parameter) => _canExecute?.Invoke() ?? true;
- public void Execute(object? parameter) => _execute();
- public event EventHandler? CanExecuteChanged;
- public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
-}
diff --git a/CSharpBible/Graphics/PrimeDisc/PrimeDisc.csproj b/CSharpBible/Graphics/PrimeDisc/PrimeDisc.csproj
index b1c5a7628..bbe898edb 100644
--- a/CSharpBible/Graphics/PrimeDisc/PrimeDisc.csproj
+++ b/CSharpBible/Graphics/PrimeDisc/PrimeDisc.csproj
@@ -8,6 +8,14 @@
true
PrimePlotter
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Graphics/PrimeDisc/Services/Interfaces/IPlotService.cs b/CSharpBible/Graphics/PrimeDisc/Services/Interfaces/IPlotService.cs
new file mode 100644
index 000000000..0a4eab0e6
--- /dev/null
+++ b/CSharpBible/Graphics/PrimeDisc/Services/Interfaces/IPlotService.cs
@@ -0,0 +1,86 @@
+namespace PrimePlotter.Services.Interfaces;
+
+///
+/// Stellt Plot-Funktionalitt fr Primzahl-Punktwolken bereit, inklusive optionaler
+/// Downsampling-Strategien zur Reduzierung der Anzahl zu rendernder Punkte bei groen Datenmengen.
+///
+///
+/// Diese Schnittstelle kapselt die Berechnung von Bildschirm-/Canvas-Koordinaten aus einer
+/// bergebenen Folge von Primzahlen. Implementierungen knnen verschiedene Projektions- oder
+/// Layout-Verfahren (z. B. Polar-, Spiral-, Gitter- oder benutzerdefinierte Anordnungen) nutzen.
+///
+/// Der Fokus der Methode liegt auf Performance bei sehr groen
+/// Primlisten: Durch einen scaleFactor kann eine grobe Rasterung / Downsampling durchgefhrt
+/// werden, um die resultierende Punktmenge zu reduzieren (z. B. fr Vorschau- oder Echtzeitdarstellung).
+///
+///
+/// Rckgabewert: Ein flaches eindimensionales Array von Ganzzahlen, das die berechneten Punkte
+/// als interleavte X-/Y-Koordinaten enthlt: Index 0 = X0, Index 1 = Y0, Index 2 = X1, Index 3 = Y1, usw.
+/// Dieses Format vermeidet Objekt-Overhead (z. B. von Tupeln oder Strukturen) und ist GC-freundlich.
+///
+///
+/// Thread-Sicherheit: Instanzen mssen nicht zwingend thread-sicher sein; parallele Nutzung sollte
+/// nur erfolgen, wenn die konkrete Implementierung dies explizit dokumentiert.
+///
+///
+public interface IPlotService
+{
+ ///
+ /// Berechnet Bildschirm-/Raster-Koordinaten fr eine Menge von Primzahlen und fhrt dabei
+ /// ein Downsampling anhand eines Skalierungsfaktors durch, um die resultierende Punktmenge
+ /// zu verringern oder zu verdichten.
+ ///
+ ///
+ /// Sortierte oder unsortierte Liste von Primzahlen, die geplottet werden sollen. Die Interpretation
+ /// (z. B. Reihenfolge fr eine Spiral-/Sequenzprojektion) liegt bei der Implementierung. Muss
+ /// nicht leer sein; bei leerer Liste wird ein leeres Array zurckgegeben.
+ ///
+ ///
+ /// Zielbreite der Zeichenflche (in Pixeln). Muss > 0 sein. Wird zur Begrenzung / Normalisierung
+ /// berechneter X-Koordinaten genutzt.
+ ///
+ ///
+ /// Zielhhe der Zeichenflche (in Pixeln). Muss > 0 sein. Wird zur Begrenzung / Normalisierung
+ /// berechneter Y-Koordinaten genutzt.
+ ///
+ ///
+ /// Downsampling- bzw. Verdichtungsfaktor. Grere Werte reduzieren typischerweise die Anzahl
+ /// der zurckgegebenen Punkte, indem z. B. nur jede n-te Primzahl oder aggregierte Cluster
+ /// bercksichtigt werden. Muss > 0 sein; Werte von 1 bedeuten kein Downsampling.
+ ///
+ ///
+ /// Optionaler Fortschritts-Callback (0.0100.0 oder 0.01.0 je nach Implementierungskonvention),
+ /// ber den Zwischenschritte gemeldet werden knnen (z. B. bei sehr groen Eingaben). Kann null sein.
+ ///
+ ///
+ /// zur vorzeitigen Unterbrechung der Berechnung. Wird regelmig
+ /// geprft; bei Auslsung sollte eine geworfen werden.
+ ///
+ ///
+ /// Eindimensionales int-Array mit interleavten X-/Y-Koordinaten der resultierenden (ggf. reduzierten)
+ /// Punktmenge. Die Lnge ist immer gerade (2 * Anzahl Punkte). Kann leer sein, aber niemals null.
+ ///
+ ///
+ /// Wird ausgelst, wenn , oder
+ /// ungltige (nicht positive) Werte besitzen.
+ ///
+ ///
+ /// Wenn die Operation ber abgebrochen wurde.
+ ///
+ ///
+ /// Performance-Hinweise:
+ ///
+ /// - Implementierungen sollten soweit mglich auf Allocation-Minimierung achten.
+ /// - Zwischenpuffer knnen aus Array-Pools bezogen und nach Nutzung zurckgegeben werden.
+ /// - Bei sehr groen Eingaben empfiehlt sich eine Vektorisierung oder Parallelisierung,
+ /// solange deterministische Reihenfolgen nicht erforderlich sind.
+ ///
+ ///
+ int[] PlotPointsDownsampled(
+ IReadOnlyList primes,
+ int width,
+ int height,
+ int scaleFactor,
+ IProgress? progress = null,
+ CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/CSharpBible/Graphics/PrimeDisc/Services/Interfaces/IPrimeService.cs b/CSharpBible/Graphics/PrimeDisc/Services/Interfaces/IPrimeService.cs
new file mode 100644
index 000000000..2a628a862
--- /dev/null
+++ b/CSharpBible/Graphics/PrimeDisc/Services/Interfaces/IPrimeService.cs
@@ -0,0 +1,71 @@
+namespace PrimePlotter.Services.Interfaces;
+
+///
+/// Stellt Dienste zur Ermittlung von Primzahlen bereit.
+///
+///
+/// Diese Schnittstelle abstrahiert die Generierung der ersten n Primzahlen und untersttzt:
+///
+/// - Fortschrittsberichte ber (Prozentwert 0.0100.0).
+/// - Kooperative Unterbrechung mittels .
+/// - Deterministische, geordnete Ausgabe (streng aufsteigende Primzahlen).
+///
+/// Implementierungen sollten effizient sein (z. B. optimiertes Sieb oder inkrementelle Prfung) und Ressourcen schonend arbeiten.
+///
+public interface IPrimeService
+{
+ ///
+ /// Ermittelt die ersten Primzahlen in aufsteigender Reihenfolge.
+ ///
+ /// Anzahl der gewnschten Primzahlen. Muss grer oder gleich 0 sein. Bei 0 wird eine leere Liste zurckgegeben.
+ ///
+ /// Optionaler Fortschritts-Callback. bergibt einen Prozentwert (0.0 bis 100.0), der den bereits berechneten Anteil widerspiegelt.
+ /// Der letzte Aufruf sollte 100.0 melden, sofern nicht vorher abgebrochen wurde.
+ ///
+ ///
+ /// Optionaler zur kooperativen Unterbrechung. Bei Abbruch sollte eine
+ /// ausgelst oder (alternativ) eine teilweise Ergebnisliste zurckgegeben werden
+ /// dies muss in der Implementierungsdokumentation eindeutig festgehalten werden.
+ ///
+ ///
+ /// Eine neue mit genau Primzahlen (beginnend bei 2), sortiert in streng aufsteigender Reihenfolge.
+ ///
+ /// ist negativ.
+ /// Der Vorgang wurde ber abgebrochen (falls Implementierung dies so handhabt).
+ ///
+ /// Leistungsaspekte:
+ ///
+ /// - Fr groe ist ein segmentiertes Sieb typischerweise Speicher-effizienter als ein einfaches Sieb.
+ /// - Fortschritt kann auf Basis der Anzahl gefundener Primzahlen oder des abgearbeiteten Bereiches berechnet werden.
+ ///
+ /// Thread-Sicherheit:
+ ///
+ /// - Die Methode selbst sollte keine geteilten mutierbaren Zustnde voraussetzen. Parallelisierung ist optional.
+ ///
+ /// Fehlerverhalten:
+ ///
+ /// - Bei ungltigen Parametern (z. B. < 0) wird eine Ausnahme ausgelst.
+ /// - Bei Abbruch ber erfolgt entweder oder Teilergebnis Implementierungsspezifik.
+ ///
+ ///
+ ///
+ ///
+ /// IPrimeService service = GetPrimeService();
+ /// var primes = service.FirstNPrimes(10);
+ /// // Ergebnis: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
+ ///
+ ///
+ /// var progress = new Progress<double>(p => Console.WriteLine($"Fortschritt: {p:F2}%"));
+ /// using var cts = new CancellationTokenSource();
+ /// try
+ /// {
+ /// var primes = service.FirstNPrimes(100_000, progress, cts.Token);
+ /// }
+ /// catch (OperationCanceledException)
+ /// {
+ /// Console.WriteLine("Berechnung abgebrochen.");
+ /// }
+ ///
+ ///
+ List FirstNPrimes(int n, IProgress? progress = null, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/CSharpBible/Graphics/PrimeDisc/Services/PlotService.cs b/CSharpBible/Graphics/PrimeDisc/Services/PlotService.cs
index 407dd1723..89115c397 100644
--- a/CSharpBible/Graphics/PrimeDisc/Services/PlotService.cs
+++ b/CSharpBible/Graphics/PrimeDisc/Services/PlotService.cs
@@ -1,140 +1,232 @@
-// Services/PlotService.cs
-using System;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace PrimePlotter.Services
+using PrimePlotter.Services.Interfaces;
+
+namespace PrimePlotter.Services;
+
+///
+/// Statische Helferklasse zum Rendern einer Primzahlen-Spirale auf einen ARGB-Pixelpuffer.
+///
+///
+/// Darstellung:
+/// Die Primzahlen werden auf einer modifizierten Spirale (ähnlich einer Fermat-/Archimedes-Spirale)
+/// positioniert, wobei für jede Primzahl p gilt:
+///
+/// r = sqrt(p)
+/// theta = PI * sqrt(p)
+/// x = cx + scale * r * cos(theta)
+/// y = cy - scale * r * sin(theta) (negatives Vorzeichen für Bildschirm-Koordinaten nach unten)
+///
+/// Der Radius wächst also proportional zur Quadratwurzel der Primzahl. Dadurch entsteht eine relativ
+/// gleichmäßige, aber dennoch strukturierte Verteilung der Punkte. Die Farb-Codierung ist aktuell
+/// monochrom (Schwarz/Weiß bzw. Graustufen beim Downsampling).
+///
+/// Thread-Sicherheit:
+/// Die Klasse ist rein funktional (keine Felder / kein Zustand) und damit threadsicher.
+///
+/// Performance-Hinweise:
+/// Die Routinen sind O(n) bezüglich der Anzahl der Primzahlen. Für sehr große Mengen empfiehlt sich,
+/// das Downsampling-Verfahren zu benutzen, da es visuelle Dichte besser wiedergibt.
+///
+///
+/// Einfache Verwendung:
+///
+/// var primes = new List<int> {2,3,5,7,11,13,17,19};
+/// int[] argb = PlotService.PlotPoints(primes, 800, 800);
+/// // argb enthält jetzt 800*800 Pixel in ARGB-Form
+///
+/// Mit Fortschritt und Abbruch:
+///
+/// var cts = new CancellationTokenSource();
+/// var progress = new Progress<double>(p => Console.WriteLine($"{p:F1}%"));
+/// int[] argb = PlotService.PlotPoints(primes, 1920, 1080, progress, cts.Token);
+///
+///
+public class PlotService : IPlotService
{
- public static class PlotService
+ ///
+ /// Rendert eine Punktdarstellung der Primzahlen auf einen ARGB-Puffer (Schwarz = Hintergrund, Weiß = Primzahlpunkt).
+ ///
+ /// Aufsteigende Liste von Primzahlen (die letzte bestimmt den maximalen Radius).
+ /// Breite des Zielbildes in Pixeln (> 0).
+ /// Höhe des Zielbildes in Pixeln (> 0).
+ ///
+ /// Optionaler Fortschritts-Reporter (Wertebereich: zuerst 50..100%). Die Initialisierung der Skalen (0..50%) kann extern erfolgen.
+ ///
+ /// Abbruch-Token zur frühzeitigen Unterbrechung der Berechnung.
+ ///
+ /// Ein eindimensionales int-Array der Länge width * height im ARGB-Format (0xAARRGGBB), zeilenweise von oben nach unten.
+ ///
+ /// Geworfen, wenn der Abbruch via ausgelöst wurde.
+ ///
+ /// Skalierungslogik:
+ /// Der größte Radius wird aus der Quadratwurzel der letzten Primzahl bestimmt und so normiert,
+ /// dass die Darstellung nahezu (96%) das kleinere Seitenmaß ausfüllt.
+ ///
+ /// Fortschritt:
+ /// Alle ~16384 Punkte (Bitmaske 0x3FFF) wird ein neuer Prozentsatz berechnet. Startwert ist 50%, Endwert 100%.
+ ///
+ ///
+ ///
+ /// var primes = PrimeGenerator.UpTo(1_000_000);
+ /// int[] pixels = PlotService.PlotPoints(primes, 1024, 1024);
+ ///
+ ///
+ public int[] PlotPoints(
+ IReadOnlyList primes,
+ int width, int height,
+ IProgress? progress = null,
+ CancellationToken ct = default)
{
- // Liefert ARGB-Int-Puffer (schwarz=0xFF000000, weiß=0xFFFFFFFF)
- public static int[] PlotPoints(
- IReadOnlyList primes,
- int width, int height,
- IProgress? progress = null,
- CancellationToken ct = default)
+ int w = width, h = height;
+ var pixels = new int[w * h];
+ int argbBlack = unchecked((int)0xFF000000);
+ int argbWhite = unchecked((int)0xFFFFFFFF);
+ for (int i = 0; i < pixels.Length; i++) pixels[i] = argbBlack;
+
+ if (primes.Count == 0) return pixels;
+
+ // Skalierung so, dass r_max (sqrt letzter p) knapp ins Bild passt
+ double rMax = Math.Sqrt(primes[^1]);
+ double margin = 0.96; // 4% Rand
+ double scale = margin * 0.5 * Math.Min(w, h) / Math.Max(1e-9, rMax);
+ double cx = (w - 1) / 2.0;
+ double cy = (h - 1) / 2.0;
+
+ int lastPct = 50;
+ for (int i = 0; i < primes.Count; i++)
{
- int w = width, h = height;
- var pixels = new int[w * h];
- int argbBlack = unchecked((int)0xFF000000);
- int argbWhite = unchecked((int)0xFFFFFFFF);
- for (int i = 0; i < pixels.Length; i++) pixels[i] = argbBlack;
-
- if (primes.Count == 0) return pixels;
-
- // Skalierung so, dass r_max (sqrt letzter p) knapp ins Bild passt
- double rMax = Math.Sqrt(primes[^1]);
- double margin = 0.96; // 4% Rand
- double scale = margin * 0.5 * Math.Min(w, h) / Math.Max(1e-9, rMax);
- double cx = (w - 1) / 2.0;
- double cy = (h - 1) / 2.0;
-
- int lastPct = 50;
- for (int i = 0; i < primes.Count; i++)
- {
- ct.ThrowIfCancellationRequested();
- double p = primes[i];
-
- // r = sqrt(p), theta = (pi)*sqrt(p)
- double r = Math.Sqrt(p);
- double theta = Math.PI * r;
+ ct.ThrowIfCancellationRequested();
+ double p = primes[i];
- double x = cx + scale * r * Math.Cos(theta);
- double y = cy - scale * r * Math.Sin(theta);
+ // r = sqrt(p), theta = (pi)*sqrt(p)
+ double r = Math.Sqrt(p);
+ double theta = Math.PI * r;
- int ix = (int)Math.Round(x);
- int iy = (int)Math.Round(y);
+ double x = cx + scale * r * Math.Cos(theta);
+ double y = cy - scale * r * Math.Sin(theta);
- if ((uint)ix < (uint)w && (uint)iy < (uint)h)
- {
- int idx = iy * w + ix;
- pixels[idx] = argbWhite; // einfache Punktdarstellung
- }
+ int ix = (int)Math.Round(x);
+ int iy = (int)Math.Round(y);
- // Fortschritt von 50%..100%
- if ((i & 0x3FFF) == 0 || i == primes.Count - 1) // alle ~16384 Punkte
- {
- int pct = 50 + (int)Math.Round(50.0 * (i + 1) / primes.Count);
- if (pct != lastPct) { progress?.Report(pct); lastPct = pct; }
- }
+ if ((uint)ix < (uint)w && (uint)iy < (uint)h)
+ {
+ int idx = iy * w + ix;
+ pixels[idx] = argbWhite; // einfache Punktdarstellung
}
- progress?.Report(100.0);
- return pixels;
+ // Fortschritt von 50%..100%
+ if ((i & 0x3FFF) == 0 || i == primes.Count - 1) // alle ~16384 Punkte
+ {
+ int pct = 50 + (int)Math.Round(50.0 * (i + 1) / primes.Count);
+ if (pct != lastPct) { progress?.Report(pct); lastPct = pct; }
+ }
}
- public static int[] PlotPointsDownsampled(
- IReadOnlyList primes,
- int width, int height,
- int scaleFactor,
- IProgress? progress = null,
- CancellationToken ct = default)
- {
- int bigW = width * scaleFactor;
- int bigH = height * scaleFactor;
- var bigBuffer = new float[bigW * bigH]; // Zählpuffer statt Farbe
+ progress?.Report(100.0);
+ return pixels;
+ }
- if (primes.Count == 0) return new int[width * height];
+ ///
+ /// Rendert eine glättete (Downsampling) Primzahl-Spirale mit bilinear verteilter Punktgewichtung zur Erzeugung von Graustufen.
+ ///
+ /// Aufsteigende Liste von Primzahlen (leer => vollständig schwarzes Bild).
+ /// Zielbreite des resultierenden Puffers.
+ /// Zielhöhe des resultierenden Puffers.
+ ///
+ /// Downsampling-Faktor (>= 1). Intern wird ein width * scaleFactor x height * scaleFactor großer Float-Puffer aufgebaut.
+ ///
+ /// Optionaler Fortschritts-Reporter (Werte 50..100%).
+ /// Abbruch-Token zur Unterbrechung.
+ ///
+ /// ARGB-Pixelpuffer in der Zielgröße. Jeder Kanal (R,G,B) enthält die identische Intensität (Graustufen),
+ /// Alpha ist stets 0xFF.
+ ///
+ /// Abbruch während der Berechnung.
+ ///
+ /// Algorithmus:
+ /// 1. Erster Pass: Für jede Primzahl wird ihre Position im vergrößerten Puffer berechnet und
+ /// bilinear auf bis zu vier Nachbarzellen verteilt (weiche Verteilung).
+ /// 2. Zweiter Pass: Aggregation der ^2 Subpixel in einen Zielpixel
+ /// mittels einfacher Summation und Clamping auf 0..255 nach Multiplikation mit 255.
+ ///
+ /// Intensitätsberechnung:
+ /// Die Summe der Gewichte wird auf den Bereich [0..255] skaliert. Für hohe Punktdichten kann eine
+ /// alternative Normalisierung oder logarithmische Skalierung sinnvoll sein (Erweiterungspotenzial).
+ ///
+ ///
+ ///
+ /// var primes = PrimeGenerator.UpTo(500_000);
+ /// int[] smooth = PlotService.PlotPointsDownsampled(primes, 800, 800, scaleFactor: 4);
+ ///
+ ///
+ public int[] PlotPointsDownsampled(
+ IReadOnlyList primes,
+ int width, int height,
+ int scaleFactor,
+ IProgress? progress = null,
+ CancellationToken ct = default)
+ {
+ int bigW = width * scaleFactor;
+ int bigH = height * scaleFactor;
+ var bigBuffer = new float[bigW * bigH]; // Zählpuffer statt Farbe
- double rMax = Math.Sqrt(primes[^1]);
- double margin = 0.96;
- double scale = margin * 0.5 * Math.Min(bigW, bigH) / Math.Max(1e-9, rMax);
- double cx = (bigW - 1) / 2.0;
- double cy = (bigH - 1) / 2.0;
+ if (primes.Count == 0) return new int[width * height];
- for (int i = 0; i < primes.Count; i++)
- {
- ct.ThrowIfCancellationRequested();
- double p = primes[i];
- double r = Math.Sqrt(p);
- double theta = Math.PI * r;
-
-
- double fx = cx + scale * r * Math.Cos(theta);
- double fy = cy - scale * r * Math.Sin(theta);
-
- // Bilineare Interpolation der Position (fx,fy) im bigBuffer
- int x0 = (int)Math.Floor(fx);
- int y0 = (int)Math.Floor(fy);
- int x1 = x0 + 1;
- int y1 = y0 + 1;
- double dx = fx - x0;
- double dy = fy - y0;
- if ((uint)x0 < (uint)bigW && (uint)y0 < (uint)bigH)
- bigBuffer[y0 * bigW + x0] += (float)((1 - dx) * (1 - dy));
- if ((uint)x1 < (uint)bigW && (uint)y0 < (uint)bigH)
- bigBuffer[y0 * bigW + x1] += (float)(dx * (1 - dy));
- if ((uint)x0 < (uint)bigW && (uint)y1 < (uint)bigH)
- bigBuffer[y1 * bigW + x0] += (float)((1 - dx) * dy);
- if ((uint)x1 < (uint)bigW && (uint)y1 < (uint)bigH)
- bigBuffer[y1 * bigW + x1] += (float)(dx * dy);
-
- if ((i & 0x3FFF) == 0)
- progress?.Report(50.0 + 50.0 * (i + 1) / primes.Count);
- }
+ double rMax = Math.Sqrt(primes[^1]);
+ double margin = 0.96;
+ double scale = margin * 0.5 * Math.Min(bigW, bigH) / Math.Max(1e-9, rMax);
+ double cx = (bigW - 1) / 2.0;
+ double cy = (bigH - 1) / 2.0;
- // Downsampling: pro Pixel der Zielgröße Mittelwert bilden
- var smallBuffer = new int[width * height];
- for (int y = 0; y < height; y++)
+ for (int i = 0; i < primes.Count; i++)
+ {
+ ct.ThrowIfCancellationRequested();
+ double p = primes[i];
+ double r = Math.Sqrt(p);
+ double theta = Math.PI * r;
+
+ double fx = cx + scale * r * Math.Cos(theta);
+ double fy = cy - scale * r * Math.Sin(theta);
+
+ // Bilineare Interpolation der Position (fx,fy) im bigBuffer
+ int x0 = (int)Math.Floor(fx);
+ int y0 = (int)Math.Floor(fy);
+ int x1 = x0 + 1;
+ int y1 = y0 + 1;
+ double dx = fx - x0;
+ double dy = fy - y0;
+ if ((uint)x0 < (uint)bigW && (uint)y0 < (uint)bigH)
+ bigBuffer[y0 * bigW + x0] += (float)((1 - dx) * (1 - dy));
+ if ((uint)x1 < (uint)bigW && (uint)y0 < (uint)bigH)
+ bigBuffer[y0 * bigW + x1] += (float)(dx * (1 - dy));
+ if ((uint)x0 < (uint)bigW && (uint)y1 < (uint)bigH)
+ bigBuffer[y1 * bigW + x0] += (float)((1 - dx) * dy);
+ if ((uint)x1 < (uint)bigW && (uint)y1 < (uint)bigH)
+ bigBuffer[y1 * bigW + x1] += (float)(dx * dy);
+
+ if ((i & 0x3FFF) == 0)
+ progress?.Report(50.0 + 50.0 * (i + 1) / primes.Count);
+ }
+
+ // Downsampling: pro Pixel der Zielgröße Mittelwert / Summe bilden
+ var smallBuffer = new int[width * height];
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
{
- for (int x = 0; x < width; x++)
+ float sum = 0;
+ for (int dy = 0; dy < scaleFactor; dy++)
{
- float sum = 0;
- for (int dy = 0; dy < scaleFactor; dy++)
+ for (int dx = 0; dx < scaleFactor; dx++)
{
- for (int dx = 0; dx < scaleFactor; dx++)
- {
- sum += bigBuffer[(y * scaleFactor + dy) * bigW + (x * scaleFactor + dx)];
- }
+ sum += bigBuffer[(y * scaleFactor + dy) * bigW + (x * scaleFactor + dx)];
}
- // Maximalwert finden oder mit Faktor skalieren
- int intensity = Math.Min(255, (int)Math.Round(sum*255)); // hier einfach clamp
- smallBuffer[y * width + x] = unchecked((int)(0xFF000000 | (intensity << 16) | (intensity << 8) | intensity));
}
+ // Maximalwert finden oder mit Faktor skalieren
+ int intensity = Math.Min(255, (int)Math.Round(sum * 255)); // hier einfach clamp
+ smallBuffer[y * width + x] = unchecked((int)(0xFF000000 | (intensity << 16) | (intensity << 8) | intensity));
}
-
- return smallBuffer;
}
+ return smallBuffer;
}
}
diff --git a/CSharpBible/Graphics/PrimeDisc/Services/PrimeService.cs b/CSharpBible/Graphics/PrimeDisc/Services/PrimeService.cs
index 29a5fc9ca..167cf49c2 100644
--- a/CSharpBible/Graphics/PrimeDisc/Services/PrimeService.cs
+++ b/CSharpBible/Graphics/PrimeDisc/Services/PrimeService.cs
@@ -1,12 +1,78 @@
// Services/PrimeService.cs
+using PrimePlotter.Services.Interfaces;
using System;
using System.Collections.Generic;
namespace PrimePlotter.Services;
-public static class PrimeService
+public class PrimeService : IPrimeService
{
- public static List FirstNPrimes(int n, IProgress? progress = null, System.Threading.CancellationToken ct = default)
+ ///
+ /// Ermittelt die ersten Primzahlen in aufsteigender Reihenfolge.
+ ///
+ /// Anzahl der gewünschten Primzahlen (falls kleiner oder gleich 0: leere Liste).
+ ///
+ /// Optionaler Fortschritts-Reporter (0.0–50.0). Die Methode meldet:
+ /// - Groben Fortschritt (0–40%) während der Eliminierung von Vielfachen im Sieb (äußere p-Schleife).
+ /// - Verfeinerung (40–50%) während des Sammelns der Primzahlen aus dem fertigen Sieb.
+ /// Ein Endwert von 50.0 signalisiert Abschluss. Werte >50% sind hier absichtlich nicht genutzt, um ggf.
+ /// nachgelagerte Verarbeitung (Visualisierung etc.) separat abzubilden.
+ ///
+ ///
+ /// Optionales Cancellation Token zur vorzeitigen Unterbrechung. Bei Auslösung wird
+ /// geworfen, sobald der nächste Abbruchpunkt erreicht ist.
+ ///
+ /// Liste der ersten Primzahlen (leer, wenn <= 0).
+ ///
+ ///
+ /// Algorithmus:
+ /// 1. Behandlung kleiner Grenzfälle (n ≤ 6) durch direkte Rückgabe einer vordefinierten Liste.
+ /// 2. Abschätzung einer oberen Schranke für die n-te Primzahl nach Dusart zur Bestimmung der Siebgröße.
+ /// 3. Initialisierung eines booleschen Arrays (true = potentiell prim).
+ /// 4. Klassisches Sieb des Eratosthenes: Streichen der Vielfachen jedes gefundenen Primkandidaten p ab p².
+ /// 5. Sammeln der ersten n Primzahlen durch lineares Durchlaufen des Siebs.
+ ///
+ ///
+ /// Komplexität:
+ /// - Zeit: O(L log log L) für das Sieben, wobei L die berechnete obere Schranke ist.
+ /// - Speicher: O(L) für das boolesche Sieb.
+ /// Die Dusart-Schätzung führt zu einer leichten Überdimensionierung, reduziert aber die Gefahr,
+ /// das Sieb zu klein zu wählen und neu allokieren zu müssen.
+ ///
+ ///
+ /// Thread-Sicherheit: Die Methode erzeugt ausschließlich lokale Datenstrukturen und ist daher
+ /// reentrant und nebenläufig sicher (sofern selbst thread-sicher
+ /// implementiert ist).
+ ///
+ ///
+ /// Fortschritt: Der Fortschritt endet bewusst bei 50%, um weiteren nachgelagerten Verarbeitungsschritten
+ /// (z.B. grafische Darstellung) eigenen Fortschrittsspielraum zu lassen.
+ ///
+ ///
+ ///
+ /// Falls das übergebene während der Berechnung abgebrochen wird.
+ ///
+ ///
+ ///
+ /// // Einfache Nutzung:
+ /// var primes = PrimeService.FirstNPrimes(10);
+ /// // Ergebnis: [2,3,5,7,11,13,17,19,23,29]
+ ///
+ /// // Mit Fortschritts-Reporting und Abbruch:
+ /// var cts = new System.Threading.CancellationTokenSource();
+ /// var progress = new Progress<double>(p => Console.WriteLine($"Fortschritt: {p:F1}%"));
+ /// try
+ /// {
+ /// var firstThousand = PrimeService.FirstNPrimes(1000, progress, cts.Token);
+ /// }
+ /// catch (OperationCanceledException)
+ /// {
+ /// Console.WriteLine("Berechnung abgebrochen.");
+ /// }
+ ///
+ ///
+ ///
+ public List FirstNPrimes(int n, IProgress? progress = null, System.Threading.CancellationToken ct = default)
{
if (n <= 0) return new List();
if (n <= 6) // kleine Grenzfälle direkt
diff --git a/CSharpBible/Graphics/PrimeDisc/ViewModels/MainViewModel.cs b/CSharpBible/Graphics/PrimeDisc/ViewModels/MainViewModel.cs
index 73a306f4e..50a5556f8 100644
--- a/CSharpBible/Graphics/PrimeDisc/ViewModels/MainViewModel.cs
+++ b/CSharpBible/Graphics/PrimeDisc/ViewModels/MainViewModel.cs
@@ -1,117 +1,205 @@
-// ViewModels/MainViewModel.cs
-using PrimePlotter.Infrastructure;
-using PrimePlotter.Services;
-using System;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using PrimePlotter.Services.Interfaces;
using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace PrimePlotter.ViewModels;
-public class MainViewModel : BindableBase
+///
+/// ViewModel zur steuernden Koordination der Primzahlberechnung und anschließenden
+/// Visualisierung als Punktraster / Bitmap innerhalb einer WPF-Oberfläche.
+///
+///
+/// Kernaufgaben:
+///
+/// - Ermittlung der ersten Primzahlen über (CPU-lastig).
+/// - Umrechnung der Primzahlen in Bildkoordinaten über .
+/// - Erzeugung eines zur Anzeige sowie Persistierung als PNG-Datei.
+/// - Bereitstellung von Fortschritt, Statusmeldungen und Abbruchfunktion für die UI.
+///
+/// Threading-Modell:
+/// Die rechenintensiven Teile laufen auf einem Hintergrundthread (Task.Run), UI-gebundene Updates (Properties, Bitmap-Erstellung)
+/// werden über den UI- marshalled.
+/// Abbruch:
+/// Ein laufender Vorgang kann über mittels abgebrochen werden.
+/// Performance:
+/// Zur Minimierung von UI-Blockaden werden nur finale Schritte (Bitmap-Erstellung / Property-Updates) im UI-Thread ausgeführt.
+/// Arrays (ARGB -> BGRA) werden einmalig konvertiert.
+///
+public partial class MainViewModel : ObservableObject
{
- private int _imageWidth = 2000;
- private int _imageHeight = 2000;
- private int _primeCount = 200_000;
- private double _progress;
- private string _status = "Bereit.";
- private ImageSource? _bitmap;
+ private readonly IPrimeService _primeService;
+ private readonly IPlotService _plotService;
+
+ ///
+ /// Zielbreite des zu erzeugenden Bitmaps in Pixeln.
+ /// Muss > 0 sein. Hohe Werte erhöhen Speicherbedarf und Rechenzeit beim Plotten.
+ ///
+ [ObservableProperty]
+ private int imageWidth = 2000;
+
+ ///
+ /// Zielhöhe des zu erzeugenden Bitmaps in Pixeln.
+ /// Muss > 0 sein. Hohe Werte erhöhen Speicherbedarf und Rechenzeit beim Plotten.
+ ///
+ [ObservableProperty]
+ private int imageHeight = 2000;
+
+ ///
+ /// Anzahl der zu berechnenden Primzahlen (Startend bei 2).
+ /// Größere Werte erhöhen exponentiell die Gesamtdauer bei nicht optimalen Sieb-Implementierungen.
+ ///
+ [ObservableProperty]
+ private int primeCount = 200_000;
+
+ ///
+ /// Fortschritt des aktuellen Arbeitsablaufs (0.0–100.0).
+ /// Wird über einen -Callback aktualisiert.
+ ///
+ [ObservableProperty]
+ private double progress;
+
+ ///
+ /// Menschlich lesbare Statusanzeige für die UI (z. B. "Berechne Primzahlen...", "Fertig.", "Abgebrochen.").
+ ///
+ [ObservableProperty]
+ private string status = "Bereit.";
+
+ ///
+ /// Ergebnis-Bitmap der geplotteten Primzahlen (BGRA-Format) zur Bindung im UI.
+ /// Kann null sein, solange kein Plot abgeschlossen wurde.
+ ///
+ [ObservableProperty]
+ private ImageSource? bitmap;
+
+ ///
+ /// Interner Ausführungszustand zur Steuerung der Command-Aktivierbarkeit.
+ /// True während einer laufenden Berechnung / Plot-Operation.
+ ///
private bool _isRunning;
- private CancellationTokenSource? _cts;
-
- public int ImageWidth { get => _imageWidth; set => SetProperty(ref _imageWidth, value); }
- public int ImageHeight { get => _imageHeight; set => SetProperty(ref _imageHeight, value); }
- public int PrimeCount { get => _primeCount; set => SetProperty(ref _primeCount, value); }
-
- public double Progress { get => _progress; private set => SetProperty(ref _progress, value); }
- public string Status { get => _status; private set => SetProperty(ref _status, value); }
- public ImageSource? Bitmap { get => _bitmap; private set => SetProperty(ref _bitmap, value); }
-
- public RelayCommand StartCommand { get; }
- public RelayCommand CancelCommand { get; }
+ ///
+ /// Aktiver für den kooperativen Abbruch langlaufender Operationen.
+ /// Wird bei Start neu erzeugt und nach Abschluss verworfen.
+ ///
+ private CancellationTokenSource? _cts;
- public MainViewModel()
+ ///
+ /// Erstellt eine neue Instanz des ViewModels mit abhängigen Diensten zur Primzahlberechnung und Punktprojektion.
+ ///
+ /// Dienst zur effizienten Ermittlung der ersten N Primzahlen.
+ /// Dienst zur Umrechnung / Projektion der Primzahlen in 2D-Koordinaten.
+ public MainViewModel(IPrimeService primeService, IPlotService plotService)
{
- StartCommand = new RelayCommand(Start, () => !_isRunning);
- CancelCommand = new RelayCommand(Cancel, () => _isRunning);
+ _primeService = primeService;
+ _plotService = plotService;
}
+ ///
+ /// Startet den end-to-end Prozess: Primzahlen berechnen, Punkte plotten, Bitmap erzeugen und abspeichern.
+ ///
+ ///
+ /// Ablauf:
+ ///
+ /// - Validierung des Ausführungszustands (kein paralleler Start).
+ /// - Initialisierung von Fortschritt und Status.
+ /// - Berechnung der Primzahlen über .
+ /// - Plotten / Projektion der Werte über .
+ /// - Erzeugung eines WriteableBitmap und Speicherung als PNG.
+ ///
+ /// Fehlerbehandlung:
+ /// führt zu Status "Abgebrochen.".
+ /// Allgemeine Ausnahmen werden mit Meldung "Fehler: ..." angezeigt.
+ /// UI-Thread:
+ /// Bitmap-Zuweisung sowie Status- und Command-Aktualisierung erfolgen über den UI-Scheduler.
+ ///
+ [RelayCommand(CanExecute = nameof(CanStart))]
private void Start()
{
if (_isRunning) return;
_isRunning = true;
- StartCommand.RaiseCanExecuteChanged();
- CancelCommand.RaiseCanExecuteChanged();
-
+ OnPropertyChanged(nameof(StartCommand));
+ OnPropertyChanged(nameof(CancelCommand));
_cts = new CancellationTokenSource();
Progress = 0;
Status = "Starte...";
-
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
- var progress = new Progress(p => Progress = p);
-
+ var progressReporter = new Progress(p => Progress = p);
Task.Run(async () =>
{
try
{
- // 1) Primzahlen
Status = "Berechne Primzahlen...";
- var primes = PrimeService.FirstNPrimes(PrimeCount, progress, _cts.Token);
-
- // 2) Plotten in Puffer
+ var primes = _primeService.FirstNPrimes(PrimeCount, progressReporter, _cts.Token);
Status = "Zeichne Punkte...";
- var pixels = PlotService.PlotPointsDownsampled(primes, ImageWidth, ImageHeight, 1, progress, _cts.Token);
-
- // 3) Bitmap auf UI erzeugen und füllen
+ var pixels = _plotService.PlotPointsDownsampled(primes, ImageWidth, ImageHeight, 1, progressReporter, _cts.Token);
await Task.Factory.StartNew(() =>
{
var wb = new WriteableBitmap(ImageWidth, ImageHeight, 96, 96, PixelFormats.Bgra32, null);
var rect = new System.Windows.Int32Rect(0, 0, ImageWidth, ImageHeight);
int stride = ImageWidth * 4;
- // pixels ist ARGB, WPF erwartet BGRA — Kanäle tauschen:
var bgra = ArgbToBgra(pixels);
wb.WritePixels(rect, bgra, stride, 0);
Bitmap = wb;
-
- // 4) Automatisch speichern
string path = SaveToPictures(wb);
Status = $"Fertig. Gespeichert unter: {path}";
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
catch (OperationCanceledException)
{
- await Task.Factory.StartNew(() =>
- {
- Status = "Abgebrochen.";
- }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
+ await Task.Factory.StartNew(() => Status = "Abgebrochen.", CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
catch (Exception ex)
{
- await Task.Factory.StartNew(() =>
- {
- Status = "Fehler: " + ex.Message;
- }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
+ await Task.Factory.StartNew(() => Status = "Fehler: " + ex.Message, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
finally
{
_isRunning = false;
await Task.Factory.StartNew(() =>
{
- StartCommand.RaiseCanExecuteChanged();
- CancelCommand.RaiseCanExecuteChanged();
+ StartCommand.NotifyCanExecuteChanged();
+ CancelCommand.NotifyCanExecuteChanged();
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
});
}
+ ///
+ /// Prüft, ob der Startvorgang aktuell ausgeführt werden darf (kein laufender Prozess).
+ ///
+ /// true, wenn keine Berechnung aktiv ist; sonst false.
+ private bool CanStart() => !_isRunning;
+
+ ///
+ /// Löst einen kooperativen Abbruch des aktuellen Prozesses aus (falls vorhanden).
+ ///
+ ///
+ /// Der Abbruch wird erst wirksam, wenn die Dienste regelmäßig den prüfen
+ /// und eine auslösen.
+ ///
+ [RelayCommand(CanExecute = nameof(CanCancel))]
private void Cancel() => _cts?.Cancel();
+ ///
+ /// Prüft, ob ein laufender Prozess abgebrochen werden kann.
+ ///
+ /// true, wenn gerade eine Berechnung läuft; sonst false.
+ private bool CanCancel() => _isRunning;
+
+ ///
+ /// Konvertiert ein int-Array im ARGB-Format (0xAARRGGBB) in ein Byte-Array im BGRA-Format
+ /// passend für .
+ ///
+ /// Quellfarben (pro Eintrag ein Pixel).
+ /// Byte-Array mit 4 Einträgen pro Pixel (B, G, R, A).
+ ///
+ /// Performance: Linearer Durchlauf O(n). Keine zusätzlichen Allokationen außer Zielarray.
+ ///
private static byte[] ArgbToBgra(int[] argb)
{
- // ARGB int -> BGRA bytes
var bytes = new byte[argb.Length * 4];
int bi = 0;
for (int i = 0; i < argb.Length; i++)
@@ -129,12 +217,20 @@ private static byte[] ArgbToBgra(int[] argb)
return bytes;
}
+ ///
+ /// Speichert ein als PNG-Datei im Bilder-Ordner des aktuellen Benutzers.
+ ///
+ /// Das zu speichernde Bitmap.
+ /// Vollständiger Dateipfad der gespeicherten PNG-Datei.
+ ///
+ /// Dateiname enthält Zeitstempel zur Kollisionsvermeidung.
+ /// Wirft bei IO-Fehlern entsprechende Ausnahmen (, ).
+ ///
private static string SaveToPictures(BitmapSource bmp)
{
string dir = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string file = $"PrimesPlot_{DateTime.Now:yyyyMMdd_HHmmss}.png";
string path = Path.Combine(dir, file);
-
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
using var fs = File.Open(path, FileMode.Create, FileAccess.Write);
diff --git a/CSharpBible/Graphics/PrimeDisc/Views/MainWindow.xaml b/CSharpBible/Graphics/PrimeDisc/Views/MainWindow.xaml
index d626e9070..8386eb96f 100644
--- a/CSharpBible/Graphics/PrimeDisc/Views/MainWindow.xaml
+++ b/CSharpBible/Graphics/PrimeDisc/Views/MainWindow.xaml
@@ -2,13 +2,16 @@
-
+
-
+
diff --git a/CSharpBible/Graphics/PrimeDisc/Views/MainWindow.xaml.cs b/CSharpBible/Graphics/PrimeDisc/Views/MainWindow.xaml.cs
index 75168bee5..54b70ec5e 100644
--- a/CSharpBible/Graphics/PrimeDisc/Views/MainWindow.xaml.cs
+++ b/CSharpBible/Graphics/PrimeDisc/Views/MainWindow.xaml.cs
@@ -1,13 +1,5 @@
-using System.Text;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
+using System.Windows;
+using PrimePlotter.ViewModels;
namespace PrimePlotter.Views;
@@ -16,8 +8,9 @@ namespace PrimePlotter.Views;
///
public partial class MainWindow : Window
{
- public MainWindow()
+ public MainWindow(MainViewModel vm)
{
InitializeComponent();
+ DataContext = vm;
}
}
\ No newline at end of file
diff --git a/CSharpBible/Graphics/ScreenX.Base/Color32.cs b/CSharpBible/Graphics/ScreenX.Base/Color32.cs
new file mode 100644
index 000000000..1ca2198ee
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.Base/Color32.cs
@@ -0,0 +1,10 @@
+namespace ScreenX.Base;
+
+public static class Color32
+{
+ // ARGB32 packing helpers
+ public static uint FromArgb(byte a, byte r, byte g, byte b)
+ => (uint)(a <<24 | r <<16 | g <<8 | b);
+ public static uint FromRgb(byte r, byte g, byte b)
+ => FromArgb(255, r, g, b);
+}
diff --git a/CSharpBible/Graphics/ScreenX.Base/ExPoint.cs b/CSharpBible/Graphics/ScreenX.Base/ExPoint.cs
new file mode 100644
index 000000000..af611f536
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.Base/ExPoint.cs
@@ -0,0 +1,11 @@
+namespace ScreenX.Base;
+
+public readonly struct ExPoint
+{
+ public readonly double X;
+ public readonly double Y;
+ public ExPoint(double x, double y)
+ {
+ X = x; Y = y;
+ }
+}
diff --git a/CSharpBible/Graphics/ScreenX.Base/ExRect.cs b/CSharpBible/Graphics/ScreenX.Base/ExRect.cs
new file mode 100644
index 000000000..f0b4b245f
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.Base/ExRect.cs
@@ -0,0 +1,10 @@
+namespace ScreenX.Base;
+
+public readonly struct ExRect
+{
+ public readonly double X1, Y1, X2, Y2;
+ public ExRect(double x1, double y1, double x2, double y2)
+ {
+ X1 = x1; Y1 = y1; X2 = x2; Y2 = y2;
+ }
+}
diff --git a/CSharpBible/Graphics/ScreenX.Base/README.md b/CSharpBible/Graphics/ScreenX.Base/README.md
new file mode 100644
index 000000000..010b1cdb9
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.Base/README.md
@@ -0,0 +1,5 @@
+# ScreenX.Base
+
+Kernfunktionen, Delegates und Renderer-Service fr ScreenX.
+
+Siehe `docs/ScreenX/ScreenX.Base.md`.
diff --git a/CSharpBible/Graphics/ScreenX.Base/Rendering.cs b/CSharpBible/Graphics/ScreenX.Base/Rendering.cs
new file mode 100644
index 000000000..6a0409f35
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.Base/Rendering.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace ScreenX.Base;
+
+public delegate ExPoint DFunction(ExPoint p, ExPoint p0, ref bool shouldBreak);
+public delegate uint CFunction(ExPoint p);
+
+public readonly record struct RenderOptions(
+ int Width,
+ int Height,
+ ExRect Source,
+ IReadOnlyList Functions,
+ CFunction Colorizer);
+
+public readonly record struct RenderResult(
+ int Width,
+ int Height,
+ uint[] Pixels);
+
+public interface IRendererService
+{
+ RenderResult Render(RenderOptions options, CancellationToken ct = default);
+}
+
+public sealed class RendererService : IRendererService
+{
+ public RenderResult Render(RenderOptions options, CancellationToken ct = default)
+ {
+ if (options.Width <= 0 || options.Height <= 0)
+ throw new ArgumentOutOfRangeException("Invalid dimensions");
+ var w = options.Width;
+ var h = options.Height;
+ var pixels = new uint[w * h];
+ var sx1 = options.Source.X1;
+ var sy1 = options.Source.Y1;
+ var dx = options.Source.X2 - options.Source.X1;
+ var dy = options.Source.Y2 - options.Source.Y1;
+ for (int x = 0; x < w; x++)
+ {
+ double vx = sx1 + dx * (x / (double)w);
+ for (int y = 0; y < h; y++)
+ {
+ ct.ThrowIfCancellationRequested();
+ double vy = sy1 + dy * (y / (double)h);
+ var p0 = new ExPoint(vx, vy);
+ var p = p0;
+ bool brk = false;
+ if (options.Functions != null)
+ {
+ for (int i = 0; i < options.Functions.Count; i++)
+ {
+ p = options.Functions[i](p, p0, ref brk);
+ if (brk) break;
+ }
+ }
+ var col = options.Colorizer is null ? 0u : options.Colorizer(p);
+ pixels[y * w + x] = col;
+ }
+ }
+ return new RenderResult(w, h, pixels);
+ }
+}
diff --git a/CSharpBible/Graphics/ScreenX.Base/ScreenX.Base.csproj b/CSharpBible/Graphics/ScreenX.Base/ScreenX.Base.csproj
new file mode 100644
index 000000000..a8cc33175
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.Base/ScreenX.Base.csproj
@@ -0,0 +1,11 @@
+
+
+
+ net8.0;net9.0
+ enable
+ enable
+
+
+
+
+
diff --git a/CSharpBible/Graphics/ScreenX.Base/Transformations.cs b/CSharpBible/Graphics/ScreenX.Base/Transformations.cs
new file mode 100644
index 000000000..d33ecbaa9
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.Base/Transformations.cs
@@ -0,0 +1,180 @@
+using System;
+
+namespace ScreenX.Base;
+
+public static class Transformations
+{
+ static double PQLength(in ExPoint p) => p.X * p.X + p.Y * p.Y;
+ static double PLength(in ExPoint p) => Math.Sqrt(PQLength(p));
+ static double ArcSinXp(in ExPoint p)
+ {
+ if (p.X > 0) return Math.Atan(p.Y / p.X);
+ if (p.X < 0)
+ {
+ if (p.Y >= 0) return Math.Atan(p.Y / p.X) + Math.PI;
+ else return Math.Atan(p.Y / p.X) - Math.PI;
+ }
+ if (p.Y > 0) return 0.5 * Math.PI;
+ if (p.Y < 0) return -0.5 * Math.PI;
+ return 0;
+ }
+
+ public static ExPoint Null(ExPoint p, ExPoint p0, ref bool brk) => p;
+
+ public static ExPoint Strflucht(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ if (p.Y == 0) return new ExPoint(0, 0);
+ if (p.Y > 0) return new ExPoint(p.X * 4 / p.Y, 12 - (32 / p.Y));
+ else return new ExPoint(p.X * 4 / p.Y, -12 - (32 / p.Y));
+ }
+
+ public static ExPoint Ballon(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ if (p.X == 0 && p.Y == 0) return new ExPoint(0, 0);
+ var force = 1 - 0.3 / (PQLength(p) / 100 + 0.3);
+ return new ExPoint(p.X * force, p.Y * force);
+ }
+
+ public static ExPoint Sauger(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ if (p.X == 0 && p.Y == 0) return new ExPoint(0, 0);
+ var force = 1 + 0.3 / (PQLength(p) / 100 + 0.1);
+ return new ExPoint(p.X * force, p.Y * force);
+ }
+
+ public static ExPoint Tunnel(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ var y = PLength(p);
+ if (y == 0) return new ExPoint(0, 0);
+ var x = ArcSinXp(p) / Math.PI;
+ if (y > 0) return new ExPoint(x * 16, 10 - 32 / y);
+ else return new ExPoint(x * 16, 0);
+ }
+
+ public static ExPoint Schnecke2(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ var y = PLength(p);
+ if (y == 0) return new ExPoint(0, 0);
+ var x = ArcSinXp(p);
+ return new ExPoint(x * 32 / Math.PI, y + (x * 4 / Math.PI) - 6);
+ }
+
+ public static ExPoint Strudel(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ var y = PLength(p);
+ if (y == 0) return new ExPoint(0, 0);
+ var x = ArcSinXp(p);
+ var ang = x + Math.PI - Math.PI * Math.Pow((y - 1) / y, 2);
+ return new ExPoint(Math.Sin(ang) * y, Math.Cos(ang) * y);
+ }
+
+ public static ExPoint Strudel2(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ const double rm = 12;
+ var r = PLength(p);
+ double y;
+ if (Math.Abs(r) < rm) y = (1 + Math.Cos(r * Math.PI / rm)) * Math.PI * 0.25;
+ else y = 0;
+ if (r == 0) return new ExPoint(0, 0);
+ var ny = Math.Cos(y) * p.Y - Math.Sin(y) * p.X;
+ var nx = Math.Sin(y) * p.Y + Math.Cos(y) * p.X;
+ return new ExPoint(nx, ny);
+ }
+
+ public static ExPoint Wobble2(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ var y = PLength(p);
+ return new ExPoint(p.X + Math.Sin(p.Y * Math.PI / 3) * 0.2 * y,
+ p.Y + Math.Cos(p.X * Math.PI / 3) * 0.2 * y);
+ }
+
+ public static ExPoint Wobble3(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ const double rm = 12;
+ var r = PLength(p);
+ double x;
+ if (Math.Abs(r) < rm) x = (1 + Math.Cos(r * Math.PI / rm)) * 10;
+ else x = 0;
+ return new ExPoint(p.X + Math.Sin(p.Y * Math.PI / 3) * 0.2 * x,
+ p.Y + Math.Cos(p.X * Math.PI / 3) * 0.2 * x);
+ }
+
+ public static ExPoint JuliaStep(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ if ((p.X * p.X * p.Y * p.Y) > 8000) return p;
+ return new ExPoint(Math.Pow(p.X * 0.2 - 0.7, 2) * 5 - Math.Pow(p.Y * 0.2 + 0.3, 2) * 5,
+ (p.Y * 0.2 + 0.3) * (p.X * 0.2 - 0.7));
+ }
+
+ public static ExPoint MandelbrStep(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ const double fakt = 0.1;
+ const double mfakt = 1 / fakt;
+ var lsqx = p.X * p.X;
+ var lsqy = p.Y * p.Y;
+ if (lsqx + 3 * p.X + lsqy > 6 * mfakt * mfakt)
+ {
+ brk = true;
+ return p;
+ }
+ return new ExPoint((lsqx - lsqy) * fakt + p0.X, 2 * p.Y * p.X * fakt + p0.Y);
+ }
+
+ public static ExPoint MandelbrStepN(ExPoint p, ExPoint p0, ref bool brk, int maxRec)
+ {
+ var res = p;
+ var k = 1;
+ while (k < maxRec && !brk)
+ {
+ res = MandelbrStep(res, p0, ref brk);
+ k++;
+ }
+ return res;
+ }
+
+ public static ExPoint MandelbrFull(ExPoint p, ExPoint p0, ref bool brk, int maxRec)
+ {
+ const double fakt = 0.1;
+ const double mfakt = 1 / fakt;
+ int lc = 0;
+ double lsqx = 0, lsqy = 0;
+ var p1 = new ExPoint(0, 0);
+ while ((lsqx + 3 * p1.X + lsqy < 6 * mfakt * mfakt) && (lc < maxRec))
+ {
+ p1 = new ExPoint((lsqx - lsqy) * fakt + p0.X, 2 * p1.Y * p1.X * fakt + p0.Y);
+ lsqx = p1.X * p1.X;
+ lsqy = p1.Y * p1.Y;
+ lc++;
+ }
+ if (lc < maxRec)
+ {
+ var dlc = 1.0 / maxRec;
+ return new ExPoint(Math.Sin(lc * 0.1) * (maxRec - lc) * dlc,
+ Math.Cos(lc * 0.1) * (maxRec - lc) * dlc);
+ }
+ return p1;
+ }
+
+ public static ExPoint Rotat(ExPoint p, double r)
+ => new ExPoint(Math.Sin(r) * p.Y + Math.Cos(r) * p.X,
+ -Math.Sin(r) * p.X + Math.Cos(r) * p.Y);
+
+ public static ExPoint Kugel(ExPoint p, ExPoint p0, ref bool brk)
+ {
+ const double kippDeg = -30;
+ const double drehDeg = -40;
+ const double degrPart = 0.005555555555555556; //1/180
+ const double rm = 12;
+ var r = PLength(p);
+ if (Math.Abs(r) > rm) return p;
+ p = Rotat(p, Math.PI * kippDeg * degrPart);
+ var zm = Math.Sqrt(rm * rm - p.Y * p.Y);
+ double z = Math.Abs(zm) >= Math.Abs(p.X) ? Math.Sqrt(zm * zm - p.X * p.X) : 0;
+ var p2 = Rotat(new ExPoint(p.Y, z), Math.PI * drehDeg * degrPart);
+ var p3 = new ExPoint(ArcSinXp(new ExPoint(p.X, p2.Y)), ArcSinXp(new ExPoint(p2.X, Math.Sqrt(p2.Y * p2.Y + p.X * p.X))));
+ return new ExPoint(10 - 4 * p3.X / Math.PI * (int)(rm * 0.5),
+ p3.X / Math.PI * 4 - 4 * p3.Y / Math.PI * (int)(rm * 0.5) + 12);
+ }
+
+
+}
diff --git a/CSharpBible/Graphics/ScreenX.BaseTests/RendererServiceTests.cs b/CSharpBible/Graphics/ScreenX.BaseTests/RendererServiceTests.cs
new file mode 100644
index 000000000..b1ce44eb6
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.BaseTests/RendererServiceTests.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using ScreenX.Base;
+
+namespace ScreenX.BaseTests;
+
+[TestClass]
+public class RendererServiceTests
+{
+ [TestMethod]
+ public void IdentityFunction_ProducesExpectedGradient()
+ {
+ var opts = new RenderOptions(
+ Width: 4,
+ Height: 4,
+ Source: new ExRect(0, 0, 1, 1),
+ Functions: new List { (ExPoint p, ExPoint p0, ref bool brk) => p },
+ Colorizer: p => Color32.FromRgb((byte)(p.X * 255), (byte)(p.Y * 255), 0));
+
+ var sut = new RendererService();
+ var res = sut.Render(opts);
+ Assert.AreEqual(16, res.Pixels.Length);
+ Assert.AreEqual(4, res.Width);
+ Assert.AreEqual(4, res.Height);
+ // spot-check a few pixels
+ uint c00 = res.Pixels[0]; // x=0,y=0
+ uint c33 = res.Pixels[3 + 3 * 4];
+ Assert.AreNotEqual(c00, c33);
+ }
+
+ [TestMethod]
+ public void FunctionChain_ComposesCorrectly()
+ {
+ DFunction f1 = (ExPoint p, ExPoint p0, ref bool brk) => new ExPoint(p.X + 1, p.Y);
+ DFunction f2 = (ExPoint p, ExPoint p0, ref bool brk) => new ExPoint(p.X, p.Y + 2);
+ var opts = new RenderOptions(
+ 2, 2,
+ new ExRect(0, 0, 0, 0),
+ new List { f1, f2 },
+ p => Color32.FromRgb((byte)p.X, (byte)p.Y, 0));
+ var sut = new RendererService();
+ var res = sut.Render(opts);
+ foreach (var c in res.Pixels)
+ {
+ Assert.AreNotEqual(0u, c);
+ }
+ }
+}
diff --git a/CSharpBible/Graphics/ScreenX.BaseTests/ScreenX.BaseTests.csproj b/CSharpBible/Graphics/ScreenX.BaseTests/ScreenX.BaseTests.csproj
new file mode 100644
index 000000000..a2fda0607
--- /dev/null
+++ b/CSharpBible/Graphics/ScreenX.BaseTests/ScreenX.BaseTests.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net8.0;net9.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Graphics/TitleGen/TitleGen.csproj b/CSharpBible/Graphics/TitleGen/TitleGen.csproj
index 2d7b0f07f..89b7df19c 100644
--- a/CSharpBible/Graphics/TitleGen/TitleGen.csproj
+++ b/CSharpBible/Graphics/TitleGen/TitleGen.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/CSharpBible/Libraries/BaseLib.Show/BaseLib.Show.csproj b/CSharpBible/Libraries/BaseLib.Show/BaseLib.Show.csproj
new file mode 100644
index 000000000..85ec48768
--- /dev/null
+++ b/CSharpBible/Libraries/BaseLib.Show/BaseLib.Show.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/Libraries/BaseLib.Show/Program.cs b/CSharpBible/Libraries/BaseLib.Show/Program.cs
new file mode 100644
index 000000000..d1c4de830
--- /dev/null
+++ b/CSharpBible/Libraries/BaseLib.Show/Program.cs
@@ -0,0 +1,614 @@
+using BaseLib.Helper;
+using BaseLib.Interfaces;
+using BaseLib.Models;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace BaseLib.Show;
+
+///
+/// Ausstellungs-Software für die BaseLib Bibliothek
+/// Demonstriert alle wichtigen Funktionen und deren Anwendung
+///
+internal class Program
+{
+ private static readonly IConsole _console = new ConsoleProxy();
+
+ static void Main(string[] args)
+ {
+ _console.Title = "BaseLib Showcase - Demonstration aller Funktionen";
+
+ ShowWelcome();
+
+ bool running = true;
+ while (running)
+ {
+ ShowMainMenu();
+ var key = _console.ReadKey();
+ _console.WriteLine("");
+ _console.WriteLine("");
+
+ running = key?.KeyChar switch
+ {
+ '1' => RunDemo(DemoStringUtils),
+ '2' => RunDemo(DemoMathUtilities),
+ '3' => RunDemo(DemoByteeUtils),
+ '4' => RunDemo(DemoObjectHelper),
+ '5' => RunDemo(DemoListHelper),
+ '6' => RunDemo(DemoTypeUtils),
+ '7' => RunDemo(DemoConsoleProxy),
+ '8' => RunDemo(DemoIoC),
+ '9' => RunDemo(DemoSysTimeAndRandom),
+ '0' => RunAllDemos(),
+ 'q' or 'Q' => false,
+ _ => true
+ };
+ }
+
+ ShowGoodbye();
+ }
+
+ static bool RunDemo(Action demo)
+ {
+ demo();
+ WaitForKey();
+ return true;
+ }
+
+ static bool RunAllDemos()
+ {
+ DemoStringUtils();
+ DemoMathUtilities();
+ DemoByteeUtils();
+ DemoObjectHelper();
+ DemoListHelper();
+ DemoTypeUtils();
+ DemoConsoleProxy();
+ DemoIoC();
+ DemoSysTimeAndRandom();
+ WaitForKey();
+ return true;
+ }
+
+ static void ShowWelcome()
+ {
+ _console.ForegroundColor = ConsoleColor.Cyan;
+ _console.WriteLine("╔════════════════════════════════════════════════════════════════╗");
+ _console.WriteLine("║ ║");
+ _console.WriteLine("║ B A S E L I B S H O W C A S E ║");
+ _console.WriteLine("║ ║");
+ _console.WriteLine("║ Demonstriert alle Funktionen der BaseLib Bibliothek ║");
+ _console.WriteLine("║ ║");
+ _console.WriteLine("╚════════════════════════════════════════════════════════════════╝");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ _console.WriteLine("");
+ }
+
+ static void ShowMainMenu()
+ {
+ _console.ForegroundColor = ConsoleColor.Yellow;
+ _console.WriteLine("══════════════════════════════════════════════════════════════════");
+ _console.WriteLine(" HAUPTMENÜ");
+ _console.WriteLine("══════════════════════════════════════════════════════════════════");
+ _console.ForegroundColor = ConsoleColor.White;
+ _console.WriteLine("");
+ _console.WriteLine(" [1] StringUtils - String-Hilfsmethoden");
+ _console.WriteLine(" [2] MathUtilities - Mathematische Funktionen (PT1, Mean, Median)");
+ _console.WriteLine(" [3] ByteUtils - Bit-Operationen");
+ _console.WriteLine(" [4] ObjectHelper - Typ-Konvertierungen");
+ _console.WriteLine(" [5] ListHelper - Listen-Operationen");
+ _console.WriteLine(" [6] TypeUtils - Typ-Utilities");
+ _console.WriteLine(" [7] ConsoleProxy - Console-Abstraktion");
+ _console.WriteLine(" [8] IoC - Dependency Injection");
+ _console.WriteLine(" [9] SysTime/CRandom - Zeit und Zufall");
+ _console.WriteLine("");
+ _console.WriteLine(" [0] Alle Demos ausführen");
+ _console.WriteLine(" [Q] Beenden");
+ _console.WriteLine("");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ _console.Write("Ihre Wahl: ");
+ }
+
+ static void ShowGoodbye()
+ {
+ _console.WriteLine("");
+ _console.ForegroundColor = ConsoleColor.Green;
+ _console.WriteLine("Vielen Dank für die Verwendung des BaseLib Showcases!");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ }
+
+ static void WaitForKey()
+ {
+ _console.WriteLine("");
+ _console.ForegroundColor = ConsoleColor.DarkGray;
+ _console.Write("Drücken Sie eine beliebige Taste, um fortzufahren...");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ _console.ReadKey();
+ _console.WriteLine("");
+ _console.Clear();
+ ShowWelcome();
+ }
+
+ static void PrintHeader(string title)
+ {
+ _console.ForegroundColor = ConsoleColor.Green;
+ _console.WriteLine($"══════════════════════════════════════════════════════════════════");
+ _console.WriteLine($" {title}");
+ _console.WriteLine($"══════════════════════════════════════════════════════════════════");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ _console.WriteLine("");
+ }
+
+ static void PrintSubHeader(string subtitle)
+ {
+ _console.ForegroundColor = ConsoleColor.Cyan;
+ _console.WriteLine($" ── {subtitle} ──");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ }
+
+ static void PrintResult(string description, object? result)
+ {
+ _console.ForegroundColor = ConsoleColor.White;
+ _console.Write($" {description}: ");
+ _console.ForegroundColor = ConsoleColor.Yellow;
+ _console.WriteLine($"{result}");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ }
+
+ #region StringUtils Demo
+ static void DemoStringUtils()
+ {
+ PrintHeader("StringUtils - String-Hilfsmethoden");
+
+ // Quote / UnQuote
+ PrintSubHeader("Quote / UnQuote");
+ string original = "Zeile1\nZeile2\tTabulator";
+ string quoted = original.Quote();
+ string unquoted = quoted.UnQuote();
+ PrintResult("Original", $"\"{original.Replace("\n", "↵").Replace("\t", "→")}\"");
+ PrintResult("Quoted", $"\"{quoted}\"");
+ PrintResult("UnQuoted", $"\"{unquoted.Replace("\n", "↵").Replace("\t", "→")}\"");
+ _console.WriteLine("");
+
+ // Format
+ PrintSubHeader("Format Extension");
+ string template = "Hallo {0}, heute ist {1}!";
+ PrintResult("Template", template);
+ PrintResult("Formatiert", template.Format("Welt", DateTime.Now.ToString("dddd")));
+ _console.WriteLine("");
+
+ // SFirst / SRest
+ PrintSubHeader("SFirst / SRest (String-Splitting)");
+ string sentence = "Dies ist ein Beispielsatz";
+ PrintResult("Original", sentence);
+ PrintResult("SFirst (erstes Wort)", sentence.SFirst());
+ PrintResult("SRest (Rest)", sentence.SRest());
+ _console.WriteLine("");
+
+ // ToNormal
+ PrintSubHeader("ToNormal (Namensformatierung)");
+ string[] names = ["pETER", "MÜLLER", "schmidt", "McGregor"];
+ foreach (var name in names)
+ {
+ PrintResult($"'{name}'", name.ToNormal());
+ }
+ _console.WriteLine("");
+
+ // Left / Right
+ PrintSubHeader("Left / Right (Substring-Funktionen)");
+ string text = "BaseLib Showcase";
+ PrintResult("Original", text);
+ PrintResult("Left(7)", text.Left(7));
+ PrintResult("Right(8)", text.Right(8));
+ PrintResult("Left(-8)", text.Left(-8));
+ PrintResult("Right(-7)", text.Right(-7));
+ _console.WriteLine("");
+
+ // QuotedSplit
+ PrintSubHeader("QuotedSplit (CSV-Parsing)");
+ string csv = "Name,\"Beschreibung, mit Komma\",Wert";
+ PrintResult("CSV-Zeile", csv);
+ var parts = csv.QuotedSplit();
+ for (int i = 0; i < parts.Count; i++)
+ {
+ PrintResult($" Teil {i + 1}", parts[i]);
+ }
+ _console.WriteLine("");
+
+ // IsValidIdentifyer
+ PrintSubHeader("IsValidIdentifyer");
+ string[] identifiers = ["MyVariable", "123Invalid", "_underscore", "Valid_Name"];
+ foreach (var id in identifiers)
+ {
+ PrintResult($"'{id}'", id.IsValidIdentifyer() ? "✓ gültig" : "✗ ungültig");
+ }
+ _console.WriteLine("");
+
+ // PadTab
+ PrintSubHeader("PadTab (Tab-Ausrichtung)");
+ string tabText = "A\tB\tC";
+ PrintResult("Original", tabText.Replace("\t", "→"));
+ PrintResult("PadTab", tabText.PadTab());
+ _console.WriteLine("");
+
+ // ContainsAny / StartswithAny / EndswithAny
+ PrintSubHeader("ContainsAny / StartswithAny / EndswithAny");
+ string testStr = "HelloWorld.txt";
+ PrintResult("String", testStr);
+ PrintResult("ContainsAny('llo','xyz')", testStr.ContainsAny("llo", "xyz"));
+ PrintResult("StartswithAny('Hi','Hello')", testStr.StartswithAny("Hi", "Hello"));
+ PrintResult("EndswithAny('.cs','.txt')", testStr.EndswithAny(".cs", ".txt"));
+ }
+ #endregion
+
+ #region MathUtilities Demo
+ static void DemoMathUtilities()
+ {
+ PrintHeader("MathUtilities - Mathematische Filter und Funktionen");
+
+ // PT1 Filter
+ PrintSubHeader("PT1 Filter (Tiefpass-Verhalten)");
+ double actValue = 0;
+ _console.WriteLine(" Simuliere Sprung von 0 auf 100:");
+ for (int i = 0; i < 10; i++)
+ {
+ double newValue = 100.0;
+ newValue.PT1(ref actValue, 0.3);
+ PrintResult($" Schritt {i + 1}", $"{actValue:F2}");
+ }
+ _console.WriteLine("");
+
+ // Mean (Gleitender Mittelwert)
+ PrintSubHeader("Mean (Gleitender Mittelwert)");
+ double[] meanBuffer = new double[5];
+ int meanIdx = 0;
+ double[] values = [10, 20, 30, 40, 50, 60, 70];
+ _console.WriteLine(" Puffergröße: 5");
+ foreach (var v in values)
+ {
+ double mean = v.Mean(meanBuffer, ref meanIdx);
+ PrintResult($" Wert {v:F0}", $"Mittelwert = {mean:F2}");
+ }
+ _console.WriteLine("");
+
+ // Median
+ PrintSubHeader("Median Filter");
+ double[] medianBuffer = new double[5];
+ int medianIdx = 0;
+ double[] noisyValues = [10, 100, 20, 15, 25, 200, 30];
+ _console.WriteLine(" Puffergröße: 5 (entfernt Ausreißer)");
+ foreach (var v in noisyValues)
+ {
+ double median = v.Median(medianBuffer, ref medianIdx);
+ PrintResult($" Wert {v:F0}", $"Median = {median:F2}");
+ }
+ }
+ #endregion
+
+ #region ByteUtils Demo
+ static void DemoByteeUtils()
+ {
+ PrintHeader("ByteUtils - Bit-Operationen");
+
+ // BitMask
+ PrintSubHeader("BitMask32 / BitMask64");
+ for (int i = 0; i < 8; i++)
+ {
+ PrintResult($"Bit {i}", $"Maske = {i.BitMask32():X8} (binär: {Convert.ToString(i.BitMask32(), 2).PadLeft(8, '0')})");
+ }
+ _console.WriteLine("");
+
+ // SetBit / ClearBit / GetBit
+ PrintSubHeader("SetBit / ClearBit / GetBit / SwitchBit");
+ int bitArray = 0;
+ PrintResult("Start", $"{bitArray:X8}");
+ bitArray = bitArray.SetBit(0);
+ PrintResult("SetBit(0)", $"{bitArray:X8}");
+ bitArray = bitArray.SetBit(3);
+ PrintResult("SetBit(3)", $"{bitArray:X8}");
+ bitArray = bitArray.SetBit(7);
+ PrintResult("SetBit(7)", $"{bitArray:X8}");
+ PrintResult("GetBit(3)", bitArray.GetBit(3));
+ PrintResult("GetBit(4)", bitArray.GetBit(4));
+ bitArray = bitArray.ClearBit(3);
+ PrintResult("ClearBit(3)", $"{bitArray:X8}");
+ bitArray = bitArray.SwitchBit(0);
+ PrintResult("SwitchBit(0)", $"{bitArray:X8}");
+ bitArray = bitArray.SwitchBit(0);
+ PrintResult("SwitchBit(0)", $"{bitArray:X8}");
+ _console.WriteLine("");
+
+ // BitCount
+ PrintSubHeader("BitCount (Anzahl gesetzter Bits)");
+ long[] testValues = [0b1010101, 0xFF, 0xFFFF, 0x12345678];
+ foreach (var val in testValues)
+ {
+ PrintResult($"0x{val:X}", $"{val.BitCount()} Bits gesetzt");
+ }
+ }
+ #endregion
+
+ #region ObjectHelper Demo
+ static void DemoObjectHelper()
+ {
+ PrintHeader("ObjectHelper - Typ-Konvertierungen");
+
+ // AsInt
+ PrintSubHeader("AsInt (zu Integer konvertieren)");
+ object[] intTests = ["42", 3.14, "abc", null!, 100L];
+ foreach (var test in intTests)
+ {
+ PrintResult($"'{test ?? "null"}' ({test?.GetType().Name ?? "null"})", test.AsInt(-1));
+ }
+ _console.WriteLine("");
+
+ // AsDouble
+ PrintSubHeader("AsDouble (zu Double konvertieren)");
+ object[] doubleTests = ["3.14", 42, "1.5e10", "ungültig"];
+ foreach (var test in doubleTests)
+ {
+ PrintResult($"'{test}'", test.AsDouble());
+ }
+ _console.WriteLine("");
+
+ // AsBool
+ PrintSubHeader("AsBool (zu Boolean konvertieren)");
+ object[] boolTests = ["true", "false", 1, 0, "1", 'T'];
+ foreach (var test in boolTests)
+ {
+ PrintResult($"'{test}'", test.AsBool());
+ }
+ _console.WriteLine("");
+
+ // AsDate
+ PrintSubHeader("AsDate (zu DateTime konvertieren)");
+ object[] dateTests = ["2024-01-15", 20240115, "15.01.2024"];
+ foreach (var test in dateTests)
+ {
+ var result = test.AsDate();
+ PrintResult($"'{test}'", result == default ? "ungültig" : result.ToString("dd.MM.yyyy"));
+ }
+ _console.WriteLine("");
+
+ // AsEnum
+ PrintSubHeader("AsEnum (zu Enum konvertieren)");
+ PrintResult("'Red' als ConsoleColor", "Red".AsEnum());
+ PrintResult("1 als ConsoleColor", 1.AsEnum());
+ PrintResult("'DarkBlue' als ConsoleColor", "DarkBlue".AsEnum());
+ _console.WriteLine("");
+
+ // AsString
+ PrintSubHeader("AsString (zu String konvertieren)");
+ object[] stringTests = [123, 3.14, DateTime.Now, null!];
+ foreach (var test in stringTests)
+ {
+ PrintResult($"{test?.GetType().Name ?? "null"}", (test?.AsString() ?? "(null)"));
+ }
+ }
+ #endregion
+
+ #region ListHelper Demo
+ static void DemoListHelper()
+ {
+ PrintHeader("ListHelper - Listen-Operationen");
+
+ // Swap
+ PrintSubHeader("Swap (Elemente tauschen)");
+ List list1 = ["A", "B", "C", "D", "E"];
+ PrintResult("Original", string.Join(", ", list1));
+ list1.Swap(1, 3);
+ PrintResult("Swap(1, 3)", string.Join(", ", list1));
+ _console.WriteLine("");
+
+ // MoveItem
+ PrintSubHeader("MoveItem (Element verschieben)");
+ List list2 = ["A", "B", "C", "D", "E"];
+ PrintResult("Original", string.Join(", ", list2));
+ list2.MoveItem(0, 3);
+ PrintResult("MoveItem(0, 3)", string.Join(", ", list2));
+ list2.MoveItem(4, 1);
+ PrintResult("MoveItem(4, 1)", string.Join(", ", list2));
+ _console.WriteLine("");
+
+ // To (Range erstellen)
+ PrintSubHeader("To (Bereich erstellen)");
+ var range1 = 1.To(10);
+ PrintResult("1.To(10)", string.Join(", ", range1));
+ var range2 = 5.To(8);
+ PrintResult("5.To(8)", string.Join(", ", range2));
+ }
+ #endregion
+
+ #region TypeUtils Demo
+ static void DemoTypeUtils()
+ {
+ PrintHeader("TypeUtils - Typ-Utilities");
+
+ // TC (TypeCode)
+ PrintSubHeader("TC (TypeCode ermitteln)");
+ Type[] types = [typeof(int), typeof(string), typeof(double), typeof(bool), typeof(DateTime)];
+ foreach (var type in types)
+ {
+ PrintResult(type.Name, type.TC());
+ }
+ _console.WriteLine("");
+
+ // ToType (String zu Type)
+ PrintSubHeader("ToType (String zu Type konvertieren)");
+ string[] typeNames = ["Int32", "String", "Double", "Boolean"];
+ foreach (var name in typeNames)
+ {
+ var type = name.ToType();
+ PrintResult(name, type.FullName);
+ }
+ _console.WriteLine("");
+
+ // IsBetweenIncl / IsBetweenExcl
+ PrintSubHeader("IsBetweenIncl / IsBetweenExcl");
+ int testValue = 5;
+ PrintResult($"{testValue}.IsBetweenIncl(1, 5)", testValue.IsBetweenIncl(1, 5));
+ PrintResult($"{testValue}.IsBetweenExcl(1, 5)", testValue.IsBetweenExcl(1, 5));
+ PrintResult($"{testValue}.IsBetweenIncl(5, 10)", testValue.IsBetweenIncl(5, 10));
+ PrintResult($"{testValue}.IsBetweenExcl(5, 10)", testValue.IsBetweenExcl(5, 10));
+ _console.WriteLine("");
+
+ // CheckLimit
+ PrintSubHeader("CheckLimit");
+ PrintResult("5.CheckLimit(1, 10)", 5.CheckLimit(1, 10));
+ PrintResult("15.CheckLimit(1, 10)", 15.CheckLimit(1, 10));
+ PrintResult("0.CheckLimit(1, 10)", 0.CheckLimit(1, 10));
+ _console.WriteLine("");
+
+ // Get (Type-basierte Konvertierung)
+ PrintSubHeader("Get (Type-basierte Konvertierung)");
+ PrintResult("typeof(int).Get(\"42\")", typeof(int).Get("42"));
+ PrintResult("typeof(double).Get(\"3.14\")", typeof(double).Get("3.14"));
+ PrintResult("typeof(bool).Get(\"true\")", typeof(bool).Get("true"));
+ }
+ #endregion
+
+ #region ConsoleProxy Demo
+ static void DemoConsoleProxy()
+ {
+ PrintHeader("ConsoleProxy - Console-Abstraktion");
+
+ PrintSubHeader("Aktuelle Console-Eigenschaften");
+ PrintResult("WindowWidth", _console.WindowWidth);
+ PrintResult("WindowHeight", _console.WindowHeight);
+ PrintResult("Title", _console.Title);
+ PrintResult("IsOutputRedirected", _console.IsOutputRedirected);
+ PrintResult("LargestWindowHeight", _console.LargestWindowHeight);
+ _console.WriteLine("");
+
+ PrintSubHeader("Cursor-Position");
+ var pos = _console.GetCursorPosition();
+ PrintResult("Aktuelle Position", $"Left={pos.Left}, Top={pos.Top}");
+ _console.WriteLine("");
+
+ PrintSubHeader("Farben Demo");
+ ConsoleColor[] colors = [ConsoleColor.Red, ConsoleColor.Green, ConsoleColor.Blue,
+ ConsoleColor.Yellow, ConsoleColor.Magenta, ConsoleColor.Cyan];
+ _console.Write(" Farbpalette: ");
+ foreach (var color in colors)
+ {
+ _console.ForegroundColor = color;
+ _console.Write("■ ");
+ }
+ _console.ForegroundColor = ConsoleColor.Gray;
+ _console.WriteLine("");
+ _console.WriteLine("");
+
+ PrintSubHeader("Verwendungszweck");
+ _console.WriteLine(" Die ConsoleProxy-Klasse ermöglicht:");
+ _console.WriteLine(" • Abstraktion der Console für Unit-Tests");
+ _console.WriteLine(" • Dependency Injection der Console");
+ _console.WriteLine(" • Mockbares Console-Interface (IConsole)");
+ }
+ #endregion
+
+ #region IoC Demo
+ static void DemoIoC()
+ {
+ PrintHeader("IoC - Dependency Injection Container");
+
+ PrintSubHeader("ServiceCollection konfigurieren");
+ _console.WriteLine(" Code-Beispiel:");
+ _console.ForegroundColor = ConsoleColor.DarkGray;
+ _console.WriteLine(" var builder = new ServiceCollection();");
+ _console.WriteLine(" builder.AddSingleton();");
+ _console.WriteLine(" builder.AddTransient();");
+ _console.WriteLine(" IoC.Configure(builder.BuildServiceProvider());");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ _console.WriteLine("");
+
+ // Tatsächliche Konfiguration
+ var builder = new ServiceCollection();
+ builder.AddSingleton();
+ builder.AddTransient();
+ builder.AddSingleton();
+ IoC.Configure(builder.BuildServiceProvider());
+
+ PrintSubHeader("Services abrufen");
+ var console = IoC.GetRequiredService();
+ PrintResult("GetRequiredService()", console.GetType().Name);
+
+ var random = IoC.GetRequiredService();
+ PrintResult("GetRequiredService()", random.GetType().Name);
+
+ var sysTime = IoC.GetService();
+ PrintResult("GetService()", sysTime?.GetType().Name ?? "null");
+
+ var notRegistered = IoC.GetService();
+ PrintResult("GetService() (nicht registriert)", notRegistered?.GetType().Name ?? "null");
+ _console.WriteLine("");
+
+ PrintSubHeader("Scopes");
+ _console.WriteLine(" IoC unterstützt auch Scopes für zeitlich begrenzte Dienste:");
+ _console.ForegroundColor = ConsoleColor.DarkGray;
+ _console.WriteLine(" var scope = IoC.GetNewScope();");
+ _console.WriteLine(" // Arbeiten mit scope-spezifischen Services");
+ _console.WriteLine(" IoC.SetCurrentScope(scope);");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ }
+ #endregion
+
+ #region SysTime and CRandom Demo
+ static void DemoSysTimeAndRandom()
+ {
+ PrintHeader("SysTime & CRandom - Zeit und Zufallszahlen");
+
+ // SysTime
+ PrintSubHeader("SysTime (Systemzeit-Abstraktion)");
+ var sysTime = new SysTime();
+ PrintResult("Now", sysTime.Now.ToString("dd.MM.yyyy HH:mm:ss"));
+ PrintResult("Today", sysTime.Today.ToString("dd.MM.yyyy"));
+ _console.WriteLine("");
+
+ _console.WriteLine(" Testbare Zeit (Mock):");
+ _console.ForegroundColor = ConsoleColor.DarkGray;
+ _console.WriteLine(" SysTime.GetNow = () => new DateTime(2020, 1, 1);");
+ _console.ForegroundColor = ConsoleColor.Gray;
+ var originalGetNow = SysTime.GetNow;
+ SysTime.GetNow = () => new DateTime(2020, 1, 1, 12, 0, 0);
+ PrintResult("Now (gemockt)", sysTime.Now.ToString("dd.MM.yyyy HH:mm:ss"));
+ SysTime.GetNow = originalGetNow; // Zurücksetzen
+ PrintResult("Now (wiederhergestellt)", sysTime.Now.ToString("dd.MM.yyyy HH:mm:ss"));
+ _console.WriteLine("");
+
+ // CRandom
+ PrintSubHeader("CRandom (Zufallszahlen-Generator)");
+ var random = new CRandom();
+
+ _console.WriteLine(" Zufällige Integer:");
+ _console.Write(" ");
+ for (int i = 0; i < 10; i++)
+ {
+ _console.Write($"{random.Next(1, 100),4}");
+ }
+ _console.WriteLine("");
+ _console.WriteLine("");
+
+ _console.WriteLine(" Zufällige Doubles (0.0 - 1.0):");
+ _console.Write(" ");
+ for (int i = 0; i < 5; i++)
+ {
+ _console.Write($"{random.NextDouble():F3} ");
+ }
+ _console.WriteLine("");
+ _console.WriteLine("");
+
+ _console.WriteLine(" Reproduzierbare Sequenz (Seed = 42):");
+ random.Seed(42);
+ _console.Write(" Sequenz 1: ");
+ for (int i = 0; i < 5; i++)
+ _console.Write($"{random.Next(1, 100),4}");
+ _console.WriteLine("");
+
+ random.Seed(42);
+ _console.Write(" Sequenz 2: ");
+ for (int i = 0; i < 5; i++)
+ _console.Write($"{random.Next(1, 100),4}");
+ _console.WriteLine("");
+ _console.WriteLine(" → Beide Sequenzen sind identisch!");
+ }
+ #endregion
+}
diff --git a/CSharpBible/Libraries/BaseLib/BaseLib.csproj b/CSharpBible/Libraries/BaseLib/BaseLib.csproj
index 4f9be44f9..41c6e14a9 100644
--- a/CSharpBible/Libraries/BaseLib/BaseLib.csproj
+++ b/CSharpBible/Libraries/BaseLib/BaseLib.csproj
@@ -2,17 +2,23 @@
Library
- net6.0;net7.0;net8.0;net9.0;net481;net48;net472;net462
+ net6.0;net7.0;net8.0;net481;net48;net472;net462
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
False
4772c317-55ff-4251-b766-1c41dfb672e5
True
False
- ..\sgLib.snk
-
+ ..\sgLib.snk
+
@@ -23,7 +29,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/CSharpBible/Libraries/BaseLib/Helper/ListHelper.cs b/CSharpBible/Libraries/BaseLib/Helper/ListHelper.cs
index 6a274e268..432f0175c 100644
--- a/CSharpBible/Libraries/BaseLib/Helper/ListHelper.cs
+++ b/CSharpBible/Libraries/BaseLib/Helper/ListHelper.cs
@@ -9,26 +9,72 @@
//
// Copyright © JC-Soft 2023
//
-//
+//
+// Provides extension methods for working with generic lists and arrays,
+// including item manipulation, swapping, and range generation utilities.
+//
// ***********************************************************************
using System;
using System.Collections.Generic;
///
-/// The Helper namespace.
+/// The Helper namespace contains utility classes and extension methods
+/// that provide common functionality across the BaseLib library.
///
namespace BaseLib.Helper;
///
-/// Class ListHelper.
+/// Provides static extension methods for manipulating generic lists and generating arrays.
+/// This class contains helper methods for common list operations such as moving items,
+/// swapping elements, and creating ranges of values.
///
+///
+/// All methods in this class are implemented as extension methods, allowing them to be
+/// called directly on instances or value types.
+///
+///
+///
+/// // Moving an item in a list
+/// var list = new List<string> { "A", "B", "C", "D" };
+/// list.MoveItem(0, 3); // Result: { "B", "C", "A", "D" }
+///
+/// // Swapping items
+/// list.Swap(0, 2); // Swaps items at index 0 and 2
+///
+/// // Creating a range
+/// int[] range = 1.To(5); // Result: { 1, 2, 3, 4, 5 }
+///
+///
public static class ListHelper
{
- /// Moves the item from source to target.
- /// The generic type of the list
- /// The list to change.
- /// The index of the source.
- /// The index of the destination.
+ ///
+ /// Moves an item from a source index to a destination index within the list.
+ ///
+ /// The type of elements in the list.
+ /// The list in which to move the item.
+ /// The zero-based index of the item to move.
+ /// The zero-based destination index where the item should be placed.
+ ///
+ ///
+ /// If the source and destination indices are equal, no operation is performed.
+ ///
+ ///
+ /// The method handles the index shift that occurs when removing an item before
+ /// the destination index. If an exception occurs during insertion, the item
+ /// is restored to its original position before re-throwing the exception.
+ ///
+ ///
+ ///
+ /// Thrown when or is outside
+ /// the valid range of indices for the list.
+ ///
+ ///
+ ///
+ /// var list = new List<int> { 1, 2, 3, 4, 5 };
+ /// list.MoveItem(0, 4); // Moves element at index 0 to index 4
+ /// // Result: { 2, 3, 4, 1, 5 }
+ ///
+ ///
public static void MoveItem(this IList list, int iSrc, int iDst)
{
if (iSrc == iDst) return;
@@ -46,18 +92,59 @@ public static void MoveItem(this IList list, int iSrc, int iDst)
}
///
- /// Exchanges the items.
+ /// Exchanges (swaps) the positions of two items in the list.
///
- /// The generic type of the list
- /// The list to change.
- /// The index of the first item.
- /// The index of the second item.
+ /// The type of elements in the list.
+ /// The list containing the items to swap.
+ /// The zero-based index of the first item to swap.
+ /// The zero-based index of the second item to swap.
+ ///
+ /// If both indices are equal, no operation is performed.
+ /// Uses tuple deconstruction for efficient in-place swapping.
+ ///
+ ///
+ /// Thrown when or is outside
+ /// the valid range of indices for the list.
+ ///
+ ///
+ ///
+ /// var list = new List<string> { "A", "B", "C" };
+ /// list.Swap(0, 2); // Swaps "A" and "C"
+ /// // Result: { "C", "B", "A" }
+ ///
+ ///
public static void Swap(this IList list, int iItm1, int iItm2)
{
if (iItm1 == iItm2) return;
- (list[iItm1], list[iItm2]) = (list[iItm2],list[iItm1]);
+ (list[iItm1], list[iItm2]) = (list[iItm2], list[iItm1]);
}
+ ///
+ /// Creates an array containing a range of values from to inclusive.
+ ///
+ ///
+ /// A value type that implements and can be converted to and from .
+ ///
+ ///
+ /// The instance used as an extension method anchor. This parameter is not used.
+ ///
+ /// The starting value of the range (inclusive).
+ /// The ending value of the range (inclusive).
+ ///
+ /// An array of type containing all values from
+ /// to inclusive. Returns an empty array if
+ /// is greater than .
+ ///
+ ///
+ /// This method uses internally, so it works best
+ /// with numeric types that can be represented as integers.
+ ///
+ ///
+ ///
+ /// int[] range = typeof(int).Range(1, 5); // Result: { 1, 2, 3, 4, 5 }
+ /// int[] empty = typeof(int).Range(5, 1); // Result: empty array
+ ///
+ ///
public static T[] Range(this Type _, T v1, T v2) where T : struct, IComparable
{
if (v1.CompareTo(v2) > 0)
@@ -67,6 +154,37 @@ public static T[] Range(this Type _, T v1, T v2) where T : struct, IComparabl
result[i] = (T)Convert.ChangeType(Convert.ToInt32(v1) + i, typeof(T));
return result;
}
+
+ ///
+ /// Creates an array containing a range of values from the current value to the specified end value inclusive.
+ ///
+ ///
+ /// A value type that implements and can be converted to and from .
+ ///
+ /// The starting value of the range (inclusive).
+ /// The ending value of the range (inclusive).
+ ///
+ /// An array of type containing all values from
+ /// to inclusive. Returns an empty array if
+ /// is greater than .
+ ///
+ ///
+ ///
+ /// This extension method provides a fluent syntax for creating ranges directly from a value.
+ ///
+ ///
+ /// This method uses internally, so it works best
+ /// with numeric types that can be represented as integers (e.g., ,
+ /// , ).
+ ///
+ ///
+ ///
+ ///
+ /// int[] range = 1.To(5); // Result: { 1, 2, 3, 4, 5 }
+ /// byte[] bytes = ((byte)0).To((byte)3); // Result: { 0, 1, 2, 3 }
+ /// int[] empty = 10.To(5); // Result: empty array
+ ///
+ ///
public static T[] To(this T v1, T v2) where T : struct, IComparable
{
if (v1.CompareTo(v2) > 0)
@@ -76,5 +194,4 @@ public static T[] To(this T v1, T v2) where T : struct, IComparable
result[i] = (T)Convert.ChangeType(Convert.ToInt32(v1) + i, typeof(T));
return result;
}
-
}
diff --git a/CSharpBible/Libraries/BaseLib/Helper/ObjectHelper.cs b/CSharpBible/Libraries/BaseLib/Helper/ObjectHelper.cs
index 04832da4d..686e531de 100644
--- a/CSharpBible/Libraries/BaseLib/Helper/ObjectHelper.cs
+++ b/CSharpBible/Libraries/BaseLib/Helper/ObjectHelper.cs
@@ -1,4 +1,16 @@
-
+// ***********************************************************************
+// Assembly : BaseLib
+// Author : Mir
+// Created : 03-27-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 03-27-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2025
+//
+//
+// ***********************************************************************
using BaseLib.Interfaces;
using System;
using System.Collections.Generic;
@@ -9,8 +21,54 @@
namespace BaseLib.Helper;
+///
+/// Provides extension methods for safe type conversion of instances to various primitive and common types.
+///
+///
+///
+/// This static class contains a collection of extension methods that facilitate the conversion of objects
+/// to specific types such as , , , ,
+/// , , and .
+///
+///
+/// Each conversion method handles various input types including:
+///
+/// - Direct type matches (returns the value as-is)
+/// - String parsing with appropriate format providers
+/// - interface implementations for recursive value extraction
+/// - and values (returns default)
+/// - implementations for standard .NET type conversions
+///
+///
+///
public static class ObjectHelper
{
+ ///
+ /// Converts an object to a 32-bit signed integer.
+ ///
+ /// The object to convert. Can be .
+ /// The default value to return when conversion fails. Defaults to 0.
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already an , returns it directly.
+ /// - If is a , performs an unchecked cast to .
+ /// - If is a , attempts to parse it as an integer.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If implements , uses with .
+ ///
+ ///
+ ///
+ ///
+ /// int result1 = "42".AsInt(); // Returns 42
+ /// int result2 = ((object)null).AsInt(); // Returns 0
+ /// int result3 = "invalid".AsInt(-1); // Returns -1
+ ///
+ ///
public static int AsInt(this object? obj, int def = default) => obj switch
{
int i => i,
@@ -23,6 +81,31 @@ public static class ObjectHelper
_ => def
};
+ ///
+ /// Converts an object to a 64-bit signed integer.
+ ///
+ /// The object to convert. Can be .
+ /// The default value to return when conversion fails. Defaults to 0.
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If is a , performs an unchecked cast to .
+ /// - If is a , attempts to parse it as a long integer.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If implements , uses with .
+ ///
+ ///
+ ///
+ ///
+ /// long result1 = "9223372036854775807".AsLong(); // Returns long.MaxValue
+ /// long result2 = ((object)null).AsLong(); // Returns 0
+ ///
+ ///
public static long AsLong(this object? obj, int def = default) => obj switch
{
long i => i,
@@ -35,7 +118,34 @@ public static class ObjectHelper
_ => def
};
-
+ ///
+ /// Converts an object to an enumeration value of the specified type.
+ ///
+ /// The enumeration type to convert to. Must be a value type and an .
+ /// The object to convert. Can be .
+ ///
+ /// The converted enumeration value of type , or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already of type , returns it directly.
+ /// - If is an within the valid enum range, converts it to the enum value.
+ /// - If is a , attempts to parse it as an enum name.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If is a , performs an unchecked cast and converts to enum.
+ /// - If implements , converts to first, then to enum.
+ ///
+ ///
+ ///
+ ///
+ /// enum Color { Red, Green, Blue }
+ /// Color result1 = "Green".AsEnum<Color>(); // Returns Color.Green
+ /// Color result2 = 1.AsEnum<Color>(); // Returns Color.Green
+ /// Color result3 = "Invalid".AsEnum<Color>(); // Returns Color.Red (default)
+ ///
+ ///
public static T AsEnum(this object? obj) where T : struct, Enum => obj switch
{
T t => t,
@@ -51,6 +161,37 @@ string s when Enum.TryParse(s, out var t) => t,
_ => default
};
+ ///
+ /// Converts an object to a value.
+ ///
+ /// The object to convert. Can be .
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If is an in the format YYYYMMDD (e.g., 20231225), parses it as a date.
+ /// - If is an that is 0 or greater than 10000000, returns .
+ /// - If implements , recursively converts its .
+ /// - If is a without dots, attempts to parse using .
+ /// - If is a with dots, attempts to parse using .
+ /// - If is a representing a numeric date (YYYYMMDD), parses accordingly.
+ /// - If is a , treats it as ticks for .
+ /// - If is a , converts from OLE Automation date format.
+ /// - If is a , creates a date with year = char value + 1900.
+ /// - If is , returns .
+ /// - If implements , converts from OLE Automation date format.
+ ///
+ ///
+ ///
+ ///
+ /// DateTime result1 = "2023-12-25".AsDate(); // Returns December 25, 2023
+ /// DateTime result2 = 20231225.AsDate(); // Returns December 25, 2023
+ /// DateTime result3 = ((object)null).AsDate(); // Returns DateTime.MinValue
+ ///
+ ///
public static DateTime AsDate(this object? obj) => obj switch
{
DateTime dt => dt,
@@ -69,6 +210,35 @@ string s when DateTime.TryParse(s, CultureInfo.CurrentUICulture, DateTimeStyles.
_ => default,
};
+ ///
+ /// Converts an object to a double-precision floating-point number.
+ ///
+ /// The object to convert. Can be .
+ ///
+ /// The culture-specific formatting information to use for parsing strings.
+ /// If , is used.
+ ///
+ ///
+ /// The converted value, or 0.0 if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If is a , attempts to parse it using and the specified culture.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns (0.0).
+ /// - If is a , returns its numeric value.
+ /// - If implements , uses .
+ ///
+ ///
+ ///
+ ///
+ /// double result1 = "3.14".AsDouble(); // Returns 3.14
+ /// double result2 = "3,14".AsDouble(CultureInfo.GetCultureInfo("de-DE")); // Returns 3.14
+ /// double result3 = ((object)null).AsDouble(); // Returns 0.0
+ ///
+ ///
public static double AsDouble(this object? obj, CultureInfo? culture = null) => obj switch
{
double d => d,
@@ -82,6 +252,33 @@ string s when double.TryParse(s, NumberStyles.Float, culture ?? CultureInfo.Inva
_ => default
};
+ ///
+ /// Converts an object to a Boolean value.
+ ///
+ /// The object to convert. Can be .
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If is a , attempts to parse it using .
+ /// - If is a equal to "1", returns ; otherwise .
+ /// - If is a , returns for '1', 'T', or 't'.
+ /// - If implements , returns if not equal to 0.
+ ///
+ ///
+ ///
+ ///
+ /// bool result1 = "true".AsBool(); // Returns true
+ /// bool result2 = "1".AsBool(); // Returns true
+ /// bool result3 = 'T'.AsBool(); // Returns true
+ /// bool result4 = ((object)null).AsBool(); // Returns false
+ ///
+ ///
public static bool AsBool(this object? obj) => obj switch
{
bool x => x,
@@ -95,6 +292,32 @@ string s when bool.TryParse(s, out var b) => b,
_ => default
};
+ ///
+ /// Converts an object to a value.
+ ///
+ /// The object to convert. Can be .
+ ///
+ /// The converted value, or if conversion is not possible.
+ ///
+ ///
+ /// The conversion is performed using the following priority:
+ ///
+ /// - If is already a , returns it directly.
+ /// - If implements , recursively converts its .
+ /// - If is or , returns .
+ /// - If is a , attempts to parse it as a GUID.
+ /// - If is a representing an integer, creates a GUID with that integer as the first component.
+ /// - If is a , creates a GUID with that value as the first component.
+ /// - If implements , converts to and creates a GUID with that as the first component.
+ ///
+ ///
+ ///
+ ///
+ /// Guid result1 = "550e8400-e29b-41d4-a716-446655440000".AsGUID(); // Returns the parsed GUID
+ /// Guid result2 = 42.AsGUID(); // Returns GUID with first component = 42
+ /// Guid result3 = ((object)null).AsGUID(); // Returns Guid.Empty
+ ///
+ ///
public static Guid AsGUID(this object? obj) => obj switch
{
Guid g => g,
@@ -109,22 +332,137 @@ string s when Guid.TryParse(s, out var g) => g,
_ => default
};
+ ///
+ /// Executes an action on an object and returns a specified value.
+ ///
+ /// The type of the return value.
+ /// The type of the object on which the action is performed.
+ /// The object on which to perform the action.
+ /// The action to perform on .
+ /// The value to return after the action is executed.
+ /// The value after executing the action.
+ ///
+ /// This method is useful for performing side effects on an object while returning
+ /// a value in a fluent or expression-based context.
+ ///
+ ///
+ ///
+ /// var list = new List<int>();
+ /// int count = list.SetRet(l => l.Add(42), 1); // Adds 42 to list, returns 1
+ ///
+ ///
public static T SetRet(this T2 obj, Action action, T v)
{
action(obj);
return v;
}
}
+
+///
+/// Provides extension methods for dictionary operations with 1-based indexing.
+///
+///
+/// This static class contains helper methods for
+/// that use 1-based indexing internally while accepting 0-based index parameters.
+/// This is useful for compatibility with legacy VB6-style control arrays.
+///
public static class ObjectHelper2
{
+ ///
+ /// Sets a value in the dictionary at a 1-based internal index corresponding to the given 0-based index.
+ ///
+ /// The type of the values in the dictionary.
+ /// The dictionary to modify.
+ /// The value to set.
+ /// The 0-based index. Internally stored at + 1.
+ ///
+ /// This method stores the value at key ( + 1) to maintain
+ /// compatibility with 1-based indexing systems.
+ ///
+ ///
+ ///
+ /// var dic = new Dictionary<int, string>();
+ /// dic.SetIndex("Hello", 0); // Stores "Hello" at key 1
+ ///
+ ///
public static void SetIndex(this Dictionary dic, T value, int index) => dic[index + 1] = value;
+
+ ///
+ /// Gets the 0-based index of a value in the dictionary.
+ ///
+ /// The type of the values in the dictionary.
+ /// The dictionary to search.
+ /// The value to find.
+ ///
+ /// The 0-based index of the value (internal key - 1), or -1 if the value is not found.
+ ///
+ ///
+ /// This method searches for the value using
+ /// and returns the corresponding 0-based index.
+ ///
+ ///
+ ///
+ /// var dic = new Dictionary<int, string> { { 1, "Hello" }, { 2, "World" } };
+ /// int index = dic.GetIndex("World"); // Returns 1 (key 2 - 1)
+ ///
+ ///
public static int GetIndex(this Dictionary dic, T value) => dic.Where((itm) => itm.Value?.Equals(value) ?? false)
.FirstOrDefault().Key - 1;
}
+
+///
+/// Represents a generic control array that uses 0-based external indexing with 1-based internal storage.
+///
+/// The type of elements in the control array.
+///
+///
+/// This class extends to provide functionality
+/// similar to Visual Basic 6 control arrays. The external indexer uses 0-based indexing,
+/// but values are stored internally with 1-based keys.
+///
+///
+/// The class implements to support designer initialization
+/// scenarios, though the implementation is empty.
+///
+///
+///
+///
+/// var controls = new ControlArray<Button>();
+/// controls[1] = new Button(); // Stored at key 2
+/// var button = controls[0]; // Retrieves from key 1
+///
+///
public class ControlArray : Dictionary, ISupportInitialize
{
+ ///
+ /// Signals the object that initialization is starting.
+ ///
+ ///
+ /// This method is part of the interface
+ /// and is intentionally left empty as no special initialization logic is required.
+ ///
public void BeginInit() { }
+
+ ///
+ /// Signals the object that initialization is complete.
+ ///
+ ///
+ /// This method is part of the interface
+ /// and is intentionally left empty as no special finalization logic is required.
+ ///
public void EndInit() { }
+ ///
+ /// Gets the element at the specified 0-based index.
+ ///
+ /// The 0-based index of the element to retrieve.
+ /// The element stored at the internal key ( + 1).
+ ///
+ /// Thrown when no element exists at the specified index.
+ ///
+ ///
+ /// This indexer provides 0-based access while internally using 1-based keys,
+ /// maintaining compatibility with VB6-style control arrays.
+ ///
public new T this[int i] => base[i+1];
}
diff --git a/CSharpBible/Libraries/BaseLib/Helper/StringUtils.cs b/CSharpBible/Libraries/BaseLib/Helper/StringUtils.cs
index 835b7c8c1..649a371c9 100644
--- a/CSharpBible/Libraries/BaseLib/Helper/StringUtils.cs
+++ b/CSharpBible/Libraries/BaseLib/Helper/StringUtils.cs
@@ -9,7 +9,10 @@
//
// Copyright JC-Soft 2023
//
-//
+//
+// Provides a collection of utility extension methods for string manipulation,
+// including quoting/unquoting, splitting, formatting, validation, and substring operations.
+//
// ***********************************************************************
using System.Collections.Generic;
using System;
@@ -18,25 +21,96 @@
namespace BaseLib.Helper;
-/// A static class with useful string-routines.
+///
+/// A static class providing a comprehensive set of utility extension methods for string manipulation.
+///
+///
+///
+/// This class contains various helper methods for common string operations such as:
+///
+///
+/// - Quoting and unquoting strings (escaping/unescaping special characters)
+/// - String formatting with parameters
+/// - Splitting strings by separators with support for quoted sections
+/// - Tab padding and normalization
+/// - Substring extraction (Left, Right)
+/// - Identifier validation
+/// - Pattern matching (StartswithAny, EndswithAny, ContainsAny)
+///
+///
+/// All methods are implemented as extension methods on or related types,
+/// allowing for fluent method chaining syntax.
+///
+///
+///
+///
+/// // Quoting a multi-line string
+/// string quoted = "Hello\nWorld".Quote(); // Returns "Hello\\nWorld"
+///
+/// // Getting the first part of a string
+/// string first = "Hello World".SFirst(); // Returns "Hello"
+///
+/// // Checking if a string is a valid identifier
+/// bool valid = "MyVariable".IsValidIdentifyer(); // Returns true
+///
+///
public static class StringUtils
{
- public const string AlphaUpper="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- public const string AlphaLower="abcdefghijklmnopqrstuvwxyz";
- public const string Alpha=AlphaUpper+AlphaLower;
- public const string Numeric="0123456789";
- public const string AlphaNumeric=Alpha+Numeric;
+ ///
+ /// Contains all uppercase letters of the English alphabet (A-Z).
+ ///
+ /// The string "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
+ public const string AlphaUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ ///
+ /// Contains all lowercase letters of the English alphabet (a-z).
+ ///
+ /// The string "abcdefghijklmnopqrstuvwxyz".
+ public const string AlphaLower = "abcdefghijklmnopqrstuvwxyz";
///
- ///
- /// Makes the specified string quotable.
-> by escaping special Characters (Linefeed, NewLine, Tab ...)
- ///
- ///
- ///
+ /// Contains all letters of the English alphabet, both uppercase and lowercase.
///
- /// the string .
- /// Quoted/escaped string
- /// Does the opposite of
In other words: Puts a given text into one line of text.
+ /// The concatenation of and .
+ public const string Alpha = AlphaUpper + AlphaLower;
+
+ ///
+ /// Contains all numeric digits (0-9).
+ ///
+ /// The string "0123456789".
+ public const string Numeric = "0123456789";
+
+ ///
+ /// Contains all alphanumeric characters (letters and digits).
+ ///
+ /// The concatenation of and .
+ public const string AlphaNumeric = Alpha + Numeric;
+
+ ///
+ /// Escapes special characters in a string to make it suitable for single-line representation.
+ ///
+ /// The input string to quote. Can be null.
+ ///
+ /// A string with special characters escaped:
+ ///
+ /// - Backslash (\) becomes \\
+ /// - Tab (\t) becomes \t
+ /// - Carriage return (\r) becomes \r
+ /// - Line feed (\n) becomes \n
+ ///
+ /// Returns an empty string if the input is null or if an exception occurs.
+ ///
+ ///
+ /// This method is the inverse of .
+ /// It converts a multi-line text into a single-line representation by escaping control characters.
+ ///
+ ///
+ ///
+ /// string input = "Line1\nLine2\tTabbed";
+ /// string quoted = input.Quote(); // Returns "Line1\\nLine2\\tTabbed"
+ ///
+ ///
+ ///
public static string Quote(this string? aStr)
{
try
@@ -49,11 +123,38 @@ public static string Quote(this string? aStr)
}
catch { return ""; }
}
- /// Un-quotes the given string.
by un-escaping special characters (Linefeed, Newline, Tab ...)
- /// a string.
- /// the unquoted/un-escaped string
- /// Does the opposite of
In other words: Takes a given line of text and extracts the (original) text.
- public static string UnQuote(this string? aStr)
+
+ ///
+ /// Unescapes special character sequences in a string, restoring the original control characters.
+ ///
+ /// The input string to unquote. Can be null.
+ ///
+ /// A string with escape sequences converted back to their original characters:
+ ///
+ /// - \\ becomes backslash (\)
+ /// - \t becomes tab character
+ /// - \r becomes carriage return
+ /// - \n becomes line feed
+ ///
+ /// Returns an empty string if the input is null.
+ ///
+ ///
+ ///
+ /// This method is the inverse of .
+ /// It converts a single-line escaped representation back to its original multi-line form.
+ ///
+ ///
+ /// The method uses a temporary placeholder character (U+0001) to handle nested backslashes correctly.
+ ///
+ ///
+ ///
+ ///
+ /// string escaped = "Line1\\nLine2\\tTabbed";
+ /// string unquoted = escaped.UnQuote(); // Returns "Line1\nLine2\tTabbed"
+ ///
+ ///
+ ///
+ public static string UnQuote(this string? aStr)
=> (aStr ?? "")
.Replace("\\\\", "\\\u0001")
.Replace("\\t", "\t")
@@ -62,22 +163,50 @@ public static string UnQuote(this string? aStr)
.Replace("\\\u0001", "\\");
///
- /// Formats the specified string with par.
+ /// Formats a string using the specified parameters, similar to .
///
- /// a string.
- /// The par.
- /// System.String.
- ///
+ /// The format string containing placeholders like {0}, {1}, etc.
+ /// An array of objects to format into the string.
+ /// The formatted string with placeholders replaced by the corresponding parameter values.
+ ///
+ /// This is a convenience extension method that wraps ,
+ /// allowing for a more fluent syntax when formatting strings.
+ ///
+ ///
+ ///
+ /// string template = "Hello, {0}! You have {1} messages.";
+ /// string result = template.Format("John", 5); // Returns "Hello, John! You have 5 messages."
+ ///
+ ///
+ ///
+ /// Thrown when the format string is invalid or when there are more placeholders than parameters.
+ ///
public static string Format(this string aStr, params object[] par)
=> string.Format(aStr, par);
///
- /// Gets the first part of the string separated by the separator.
+ /// Extracts the first part of a string before the specified separator.
///
- /// The s.
- /// The sep.
- /// System.String.
- ///
+ /// The input string to split.
+ /// The separator string to search for. Defaults to a single space.
+ ///
+ /// The portion of the string before the first occurrence of the separator.
+ /// If the separator is not found, returns the entire original string.
+ ///
+ ///
+ /// This method is useful for parsing delimited strings when only the first element is needed.
+ /// For the remaining part after the separator, use .
+ ///
+ ///
+ ///
+ /// string input = "key=value";
+ /// string key = input.SFirst("="); // Returns "key"
+ ///
+ /// string words = "Hello World Today";
+ /// string firstWord = words.SFirst(); // Returns "Hello"
+ ///
+ ///
+ ///
public static string SFirst(this string s, string sep = " ")
{
if (!s.Contains(sep)) return s;
@@ -85,24 +214,61 @@ public static string SFirst(this string s, string sep = " ")
}
///
- /// Gets the other part of the string separated by the separator.
+ /// Extracts the remaining part of a string after the first occurrence of the specified separator.
///
- /// The s.
- /// The sep.
- /// System.String.
- ///
+ /// The input string to split.
+ /// The separator string to search for. Defaults to a single space.
+ ///
+ /// The portion of the string after the first occurrence of the separator.
+ /// If the separator is not found, returns an empty string.
+ ///
+ ///
+ /// This method complements for parsing delimited strings.
+ /// Together, they can be used to iterate through delimited elements.
+ ///
+ ///
+ ///
+ /// string input = "key=value=extra";
+ /// string rest = input.SRest("="); // Returns "value=extra"
+ ///
+ /// string words = "Hello World Today";
+ /// string remaining = words.SRest(); // Returns "World Today"
+ ///
+ ///
+ ///
public static string SRest(this string s, string sep = " ")
{
if (!s.Contains(sep)) return "";
return s.Substring(s.IndexOf(sep) + 1);
}
-
- /// Pads the tab of the string with spaces.
- /// The string.
- /// The offset from the start of the line.
- /// System.String.
- ///
+ ///
+ /// Replaces tab characters in a string with the appropriate number of spaces to align to tab stops.
+ ///
+ /// The input string containing tab characters.
+ ///
+ /// The offset from the start of the line to use for tab stop calculation.
+ /// Defaults to 0.
+ ///
+ ///
+ /// A string with all tab characters replaced by spaces, aligned to 8-character tab stops.
+ ///
+ ///
+ ///
+ /// Tab stops are calculated at every 8th character position, taking into account the current
+ /// position in the line plus the specified offset.
+ ///
+ ///
+ /// This is useful for converting tab-formatted text to fixed-width spacing for display
+ /// in environments that don't support tab characters.
+ ///
+ ///
+ ///
+ ///
+ /// string input = "Name\tAge\tCity";
+ /// string padded = input.PadTab(); // Returns "Name Age City" (aligned to 8-char tabs)
+ ///
+ ///
public static string PadTab(this string s, int offs = 0)
{
var _s = s.Split('\t');
@@ -119,19 +285,61 @@ public static string PadTab(this string s, int offs = 0)
static int TabLen(int l, int o) => l + o + (8 - (l + o) % 8) - o;
}
- /// Converts to "Normal" case. (first letter to upper- rest to lowercase)
- /// The string.
- /// System.String.
- /// e.G: pEtEr will be converted to Peter.
+ ///
+ /// Converts a string to "normal" case, where the first letter is uppercase and all subsequent letters are lowercase.
+ ///
+ /// The input string to convert.
+ ///
+ /// The string with the first character in uppercase and all remaining characters in lowercase.
+ /// Returns the original string unchanged if it is null or empty.
+ ///
+ ///
+ /// This method is useful for normalizing names or titles that may have inconsistent casing.
+ /// Only the first character is capitalized; all other characters are converted to lowercase.
+ ///
+ ///
+ ///
+ /// string name = "pEtEr";
+ /// string normalized = name.ToNormal(); // Returns "Peter"
+ ///
+ /// string allCaps = "HELLO";
+ /// string result = allCaps.ToNormal(); // Returns "Hello"
+ ///
+ ///
public static string ToNormal(this string s)
=> string.IsNullOrEmpty(s) ? s : s.Substring(0, 1).ToUpper() + s.Remove(0, 1).ToLower();
- /// Does a quoted split of the given string.
- /// The data.
- /// The separator.
- /// The quotation mark.
- /// List<System.String>.
- ///
+ ///
+ /// Splits a string by a separator while respecting quoted sections that may contain the separator.
+ ///
+ /// The input string to split.
+ /// The separator string to split by. Defaults to comma (",").
+ /// The quotation mark string that defines quoted sections. Defaults to double quote (").
+ ///
+ /// A of strings representing the split elements.
+ /// Quoted sections are preserved as single elements even if they contain the separator.
+ /// Leading and trailing whitespace is trimmed from each element.
+ ///
+ ///
+ ///
+ /// This method is particularly useful for parsing CSV data where fields may contain commas
+ /// enclosed in quotes. The quote marks themselves are removed from the resulting elements.
+ ///
+ ///
+ /// If a quoted section is not properly closed, the remaining content is added as a final element.
+ ///
+ ///
+ ///
+ ///
+ /// string csv = "John,\"Doe, Jr.\",25";
+ /// List<string> fields = csv.QuotedSplit();
+ /// // Returns: ["John", "Doe, Jr.", "25"]
+ ///
+ /// string data = "name='John Smith';age='30'";
+ /// List<string> parts = data.QuotedSplit(";", "'");
+ /// // Returns: ["name=John Smith", "age=30"]
+ ///
+ ///
public static List QuotedSplit(this string Data, string Separator = ",", string QuoteMark = "\"")
{
var arPreSplit = Data.Split(new string[] { Separator }, StringSplitOptions.None);
@@ -170,14 +378,59 @@ public static List QuotedSplit(this string Data, string Separator = ",",
return result;
}
- public static bool EndswithAny(this string s,params string[] strings)
+ ///
+ /// Determines whether the string ends with any of the specified suffixes.
+ ///
+ /// The string to check.
+ /// An array of suffix strings to test against.
+ ///
+ /// true if the string ends with any of the specified suffixes as a complete word boundary;
+ /// otherwise, false.
+ ///
+ ///
+ ///
+ /// This method checks for word-boundary endings by prepending a space to both the input string
+ /// and each suffix before comparison. This ensures that "test" does not match "contest" when
+ /// checking for "test" as an ending.
+ ///
+ ///
+ ///
+ ///
+ /// bool result1 = "Hello World".EndswithAny("World", "Test"); // Returns true
+ /// bool result2 = "contest".EndswithAny("test"); // Returns false (word boundary check)
+ ///
+ ///
+ ///
+ ///
+ public static bool EndswithAny(this string s, params string[] strings)
{
foreach (var item in strings)
- if ((" "+s).EndsWith(" "+item))
+ if ((" " + s).EndsWith(" " + item))
return true;
return false;
}
+ ///
+ /// Determines whether the string contains any of the specified substrings.
+ ///
+ /// The string to search in.
+ /// An array of substrings to search for.
+ ///
+ /// true if the string contains any of the specified substrings;
+ /// otherwise, false.
+ ///
+ ///
+ /// The search is case-sensitive. The method returns true as soon as
+ /// the first matching substring is found.
+ ///
+ ///
+ ///
+ /// bool hasKeyword = "The quick brown fox".ContainsAny("cat", "dog", "fox"); // Returns true
+ /// bool noMatch = "Hello World".ContainsAny("xyz", "123"); // Returns false
+ ///
+ ///
+ ///
+ ///
public static bool ContainsAny(this string s, params string[] strings)
{
foreach (var item in strings)
@@ -185,69 +438,193 @@ public static bool ContainsAny(this string s, params string[] strings)
return true;
return false;
}
+
+ ///
+ /// Determines whether the string starts with any of the specified prefixes.
+ ///
+ /// The string to check.
+ /// An array of prefix strings to test against.
+ ///
+ /// true if the string starts with any of the specified prefixes as a complete word boundary;
+ /// otherwise, false.
+ ///
+ ///
+ ///
+ /// This method checks for word-boundary beginnings by appending a space to both the input string
+ /// and each prefix before comparison. This ensures that "test" does not match "testing" when
+ /// checking for "test" as a beginning.
+ ///
+ ///
+ ///
+ ///
+ /// bool result1 = "Hello World".StartswithAny("Hello", "Hi"); // Returns true
+ /// bool result2 = "testing".StartswithAny("test"); // Returns false (word boundary check)
+ ///
+ ///
+ ///
+ ///
public static bool StartswithAny(this string s, params string[] strings)
{
foreach (var item in strings)
- if ((s+" ").StartsWith(item+" "))
+ if ((s + " ").StartsWith(item + " "))
return true;
return false;
}
-
+
///
- /// Prft, ob der angegebene String ein gltiger Bezeichner (Identifier) ist.
- /// Ein gltiger Bezeichner beginnt mit einem Grobuchstaben (A-Z) und enthlt nur Buchstaben, Ziffern oder Unterstriche.
- /// Leere oder nur aus Leerzeichen bestehende Strings sind ungltig.
+ /// Determines whether the specified string is a valid identifier according to specific naming rules.
///
- /// Der zu prfende String.
- /// True, wenn der String ein gltiger Bezeichner ist, sonst false.
+ /// The string to validate. Can be null.
+ ///
+ /// true if the string is a valid identifier; otherwise, false.
+ ///
+ ///
+ ///
+ /// A valid identifier must meet the following criteria:
+ ///
+ ///
+ /// - Cannot be null, empty, or contain only whitespace
+ /// - Must begin with an uppercase letter (A-Z)
+ /// - Can only contain letters (A-Z, a-z), digits (0-9), or underscores (_)
+ ///
+ ///
+ /// Note: The validation is case-insensitive for subsequent characters, but the first character
+ /// must be an alphabetic character (converted to uppercase for checking).
+ ///
+ ///
+ ///
+ ///
+ /// bool valid1 = "MyVariable".IsValidIdentifyer(); // Returns true
+ /// bool valid2 = "my_variable_123".IsValidIdentifyer(); // Returns true
+ /// bool invalid1 = "123Variable".IsValidIdentifyer(); // Returns false (starts with digit)
+ /// bool invalid2 = "my-variable".IsValidIdentifyer(); // Returns false (contains hyphen)
+ /// bool invalid3 = "".IsValidIdentifyer(); // Returns false (empty)
+ ///
+ ///
public static bool IsValidIdentifyer(this string? s)
{
if (string.IsNullOrWhiteSpace(s)) return false;
var _s = s!.ToUpper();
if (!AlphaUpper.Contains(_s[0])) return false;
foreach (var c in _s)
- if (!(AlphaNumeric+"_").Contains(c)) return false;
+ if (!(AlphaNumeric + "_").Contains(c)) return false;
return true;
}
///
- /// Gibt die ersten iCnt Zeichen des Strings zurck.
- /// Bei positivem iCnt werden die ersten iCnt Zeichen geliefert.
- /// Bei negativem iCnt werden alle bis auf die letzten |iCnt| Zeichen geliefert.
- /// Ist iCnt grer als die Lnge des Strings, wird der gesamte String zurckgegeben.
+ /// Returns the leftmost characters of a string up to the specified count.
///
- /// Der Eingabestring.
- /// Anzahl der gewnschten Zeichen (positiv: von links, negativ: bis auf die letzten |iCnt| Zeichen).
- /// Der entsprechend gekrzte String.
+ /// The input string.
+ ///
+ /// The number of characters to return.
+ ///
+ /// - Positive value: Returns the first characters from the left
+ /// - Negative value: Returns all characters except the last || characters
+ ///
+ ///
+ ///
+ /// A substring containing the specified number of leftmost characters.
+ /// If exceeds the string length, the entire string is returned.
+ /// If the result would be negative length, an empty string is returned.
+ ///
+ ///
+ /// This method provides Python-like string slicing behavior for the beginning of strings.
+ ///
+ ///
+ ///
+ /// string text = "Hello World";
+ ///
+ /// // Positive count - get first N characters
+ /// string first5 = text.Left(5); // Returns "Hello"
+ /// string first20 = text.Left(20); // Returns "Hello World" (entire string)
+ ///
+ /// // Negative count - exclude last N characters
+ /// string allButLast3 = text.Left(-3); // Returns "Hello Wo" (excludes "rld")
+ ///
+ ///
+ ///
public static string Left(this string data, int iCnt)
=> iCnt >= 0
? data.Substring(0, Math.Min(data.Length, iCnt))
: data.Substring(0, Math.Max(0, data.Length + iCnt));
///
- /// Gibt die letzten iCnt Zeichen des Strings zurck.
- /// Bei positivem iCnt werden die letzten iCnt Zeichen geliefert.
- /// Bei negativem iCnt werden die ersten |iCnt| Zeichen entfernt und der Rest geliefert.
- /// Ist iCnt grer als die Lnge des Strings, wird der gesamte String zurckgegeben.
+ /// Returns the rightmost characters of a string up to the specified count.
///
- /// Der Eingabestring.
- /// Anzahl der gewnschten Zeichen (positiv: von rechts, negativ: ab dem |iCnt|-ten Zeichen von links).
- /// Der entsprechend gekrzte String.
+ /// The input string.
+ ///
+ /// The number of characters to return.
+ ///
+ /// - Positive value: Returns the last characters from the right
+ /// - Negative value: Returns all characters starting from the ||th position
+ ///
+ ///
+ ///
+ /// A substring containing the specified number of rightmost characters.
+ /// If exceeds the string length, the entire string is returned.
+ ///
+ ///
+ /// This method provides Python-like string slicing behavior for the end of strings.
+ ///
+ ///
+ ///
+ /// string text = "Hello World";
+ ///
+ /// // Positive count - get last N characters
+ /// string last5 = text.Right(5); // Returns "World"
+ /// string last20 = text.Right(20); // Returns "Hello World" (entire string)
+ ///
+ /// // Negative count - skip first N characters
+ /// string skipFirst3 = text.Right(-3); // Returns "lo World" (skips "Hel")
+ ///
+ ///
+ ///
public static string Right(this string data, int iCnt)
=> iCnt >= 0
? data.Substring(Math.Max(0, data.Length - iCnt))
: data.Substring(Math.Min(data.Length, -iCnt));
///
- /// Gibt eine String-Reprsentation des Objekts zurck.
- /// Falls das Objekt ein String ist, wird es direkt zurckgegeben.
- /// Falls das Objekt das Interface IHasValue implementiert, wird dessen Value als String zurckgegeben.
- /// Bei null wird ein leerer String geliefert, ansonsten wird ToString() verwendet.
+ /// Converts an object to its string representation with special handling for certain types.
///
- /// The data.
- /// The format.(optional)
- /// System.String.
- public static string AsString(this object? data,string? format =null)
+ /// The object to convert. Can be null.
+ ///
+ /// An optional format string. Currently unused but reserved for future implementation.
+ ///
+ ///
+ /// A string representation of the object according to the following rules:
+ ///
+ /// - If is a , it is returned directly
+ /// - If implements , the Value property's string representation is returned
+ /// - If is null, an empty string is returned
+ /// - Otherwise, is called, with null results converted to empty string
+ ///
+ ///
+ ///
+ ///
+ /// This method provides a null-safe way to convert objects to strings, with special handling
+ /// for value wrapper types that implement .
+ ///
+ ///
+ /// The parameter is included for API consistency but is not
+ /// currently used in the implementation.
+ ///
+ ///
+ ///
+ ///
+ /// // String passthrough
+ /// string s = "Hello".AsString(); // Returns "Hello"
+ ///
+ /// // Null handling
+ /// object? nullObj = null;
+ /// string empty = nullObj.AsString(); // Returns ""
+ ///
+ /// // Object conversion
+ /// int number = 42;
+ /// string numStr = number.AsString(); // Returns "42"
+ ///
+ ///
+ public static string AsString(this object? data, string? format = null)
=> data switch
{
string s => s,
@@ -255,16 +632,49 @@ public static string AsString(this object? data,string? format =null)
null => "",
object o => o.ToString() ?? "",
};
-
+
///
- /// Copies the elements of the given string array into the specified list of strings, starting at the given offset.
- /// If the target list is null, a new list with sufficient capacity is created.
- /// Only elements that fit within the bounds of the target list are copied.
+ /// Copies elements from a string array into a target list of strings at a specified offset.
///
/// The source array of strings to copy from.
- /// The target list to copy into. If null, a new list is created.
- /// The zero-based index in the target list at which copying begins. Can be negative.
- /// The target list with the copied elements.
+ ///
+ /// The target list to copy into. If null, a new array is created with
+ /// sufficient capacity to hold all source elements considering the offset.
+ ///
+ ///
+ /// The zero-based index in the target list at which copying begins.
+ /// Can be negative, which will skip the first || elements of the source array.
+ ///
+ ///
+ /// The target list with the copied elements. If was null,
+ /// returns the newly created array.
+ ///
+ ///
+ ///
+ /// Only elements that fit within the bounds of the target list are copied. Elements that would
+ /// fall outside the target list boundaries (either negative indices or beyond the list count)
+ /// are silently ignored.
+ ///
+ ///
+ /// When is null, a new string array is created with size
+ /// Math.Max(0, asData.Length + offs).
+ ///
+ ///
+ ///
+ ///
+ /// // Copy into a new array
+ /// string[] source = { "a", "b", "c" };
+ /// IList<string> result = source.IntoString(); // Creates ["a", "b", "c"]
+ ///
+ /// // Copy with offset into existing array
+ /// string[] target = new string[5];
+ /// source.IntoString(target, 1); // target becomes [null, "a", "b", "c", null]
+ ///
+ /// // Copy with negative offset (skips first element of source)
+ /// string[] target2 = new string[2];
+ /// source.IntoString(target2, -1); // target2 becomes ["b", "c"]
+ ///
+ ///
public static IList IntoString(this string[] asData, IList? asKont = null, int offs = 0)
{
asKont ??= new string[Math.Max(0, asData.Length + offs)];
@@ -273,5 +683,4 @@ public static IList IntoString(this string[] asData, IList? asKo
asKont[i + offs] = asData[i];
return asKont;
}
-
}
diff --git a/CSharpBible/Libraries/BaseLib/Interfaces/IConsole.cs b/CSharpBible/Libraries/BaseLib/Interfaces/IConsole.cs
index 30d2bbb15..708334cb3 100644
--- a/CSharpBible/Libraries/BaseLib/Interfaces/IConsole.cs
+++ b/CSharpBible/Libraries/BaseLib/Interfaces/IConsole.cs
@@ -15,24 +15,148 @@
namespace BaseLib.Interfaces;
+///
+/// Defines an abstraction layer for console operations, enabling dependency injection
+/// and testability for console-based applications.
+///
+///
+/// This interface provides a comprehensive set of console operations including:
+///
+/// - Color management for foreground and background
+/// - Window sizing and positioning
+/// - Input/output operations
+/// - Cursor positioning
+/// - Audio feedback capabilities
+///
+/// Implementations of this interface can wrap for production use
+/// or provide mock implementations for unit testing purposes.
+///
public interface IConsole
{
+ ///
+ /// Gets or sets the foreground color of the console output.
+ ///
+ ///
+ /// A value representing the text color.
+ ///
ConsoleColor ForegroundColor { get; set; }
+
+ ///
+ /// Gets or sets the background color of the console output.
+ ///
+ ///
+ /// A value representing the background color behind the text.
+ ///
ConsoleColor BackgroundColor { get; set; }
+
+ ///
+ /// Gets a value indicating whether the console output stream has been redirected.
+ ///
+ ///
+ /// true if the output is redirected to a file or another stream; otherwise, false.
+ ///
bool IsOutputRedirected { get; }
+
+ ///
+ /// Gets a value indicating whether a key press is available in the input stream.
+ ///
+ ///
+ /// true if a key press is available to be read; otherwise, false.
+ ///
bool KeyAvailable { get; }
+
+ ///
+ /// Gets the largest possible number of console window rows based on the current font and display resolution.
+ ///
+ ///
+ /// The maximum number of rows that can be displayed in the console window.
+ ///
int LargestWindowHeight { get; }
+
+ ///
+ /// Gets or sets the title to display in the console window's title bar.
+ ///
+ ///
+ /// A string representing the console window title.
+ ///
string Title { get; set; }
+
+ ///
+ /// Gets or sets the height of the console window area in rows.
+ ///
+ ///
+ /// The number of rows visible in the console window.
+ ///
int WindowHeight { get; set; }
+
+ ///
+ /// Gets or sets the width of the console window area in columns.
+ ///
+ ///
+ /// The number of columns visible in the console window.
+ ///
int WindowWidth { get; set; }
+ ///
+ /// Plays a beep sound through the console speaker.
+ ///
+ /// The frequency of the beep sound in hertz (Hz). Valid range is typically 37 to 32767 Hz.
+ /// The duration of the beep sound in milliseconds.
void Beep(int freq, int len);
+
+ ///
+ /// Clears the console buffer and corresponding console window of all displayed information.
+ ///
void Clear();
+
+ ///
+ /// Gets the current position of the cursor within the console buffer.
+ ///
+ ///
+ /// A tuple containing the Left (column) and Top (row) coordinates of the cursor position,
+ /// where (0, 0) represents the top-left corner of the console buffer.
+ ///
(int Left, int Top) GetCursorPosition();
+
+ ///
+ /// Obtains the next character or function key pressed by the user.
+ ///
+ ///
+ /// A object describing the key pressed, including the character
+ /// and any modifier keys (Alt, Shift, Control); or null if no key is available.
+ ///
ConsoleKeyInfo? ReadKey();
+
+ ///
+ /// Reads the next line of characters from the standard input stream.
+ ///
+ ///
+ /// The next line of characters from the input stream, or an empty string if no input is available.
+ ///
string ReadLine();
+
+ ///
+ /// Sets the position of the cursor within the console buffer.
+ ///
+ /// The column position of the cursor, where 0 is the leftmost column.
+ /// The row position of the cursor, where 0 is the topmost row.
void SetCursorPosition(int left, int top);
+
+ ///
+ /// Writes the specified Unicode character to the standard output stream.
+ ///
+ /// The character to write to the console.
void Write(char ch);
+
+ ///
+ /// Writes the specified string value to the standard output stream.
+ ///
+ /// The string to write. Can be null, in which case nothing is written.
void Write(string? st);
+
+ ///
+ /// Writes the specified string value, followed by the current line terminator, to the standard output stream.
+ ///
+ /// The string to write. Can be null or empty. Defaults to an empty string.
void WriteLine(string? st = "");
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/BaseLib/Interfaces/INotifyPropertyChangedAdv.cs b/CSharpBible/Libraries/BaseLib/Interfaces/INotifyPropertyChangedAdv.cs
index bdef8bc64..3feb59321 100644
--- a/CSharpBible/Libraries/BaseLib/Interfaces/INotifyPropertyChangedAdv.cs
+++ b/CSharpBible/Libraries/BaseLib/Interfaces/INotifyPropertyChangedAdv.cs
@@ -116,7 +116,7 @@ public PropertyChangedAdvEventArgs(string propertyName,object? oldVal,object? ne
#if !NET6_0_OR_GREATER
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
#endif
- public delegate void PropertyChangedAdvEventHandler(object sender, PropertyChangedAdvEventArgs e);
+ public delegate void PropertyChangedAdvEventHandler(object? sender, PropertyChangedAdvEventArgs e);
//
// Zusammenfassung:
diff --git a/CSharpBible/Libraries/BaseLibTests/BaseLibTests.csproj b/CSharpBible/Libraries/BaseLibTests/BaseLibTests.csproj
index dde0c2806..07cf5b979 100644
--- a/CSharpBible/Libraries/BaseLibTests/BaseLibTests.csproj
+++ b/CSharpBible/Libraries/BaseLibTests/BaseLibTests.csproj
@@ -1,13 +1,20 @@
- net8.0;net9.0;net10.0;net481;net48;net472;net462
+ net8.0;net481;net48;net472;net462
false
+ true
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Libraries/BaseLibTests/Helper/FileUtilsTests.cs b/CSharpBible/Libraries/BaseLibTests/Helper/FileUtilsTests.cs
index d0c7eada2..276f504f4 100644
--- a/CSharpBible/Libraries/BaseLibTests/Helper/FileUtilsTests.cs
+++ b/CSharpBible/Libraries/BaseLibTests/Helper/FileUtilsTests.cs
@@ -159,7 +159,7 @@ public void SaveFileTest1(int iPreMode, string sFileName, bool _)
PrepareFiles(ePreMode, Path.Combine(sLocalTestPath, sFileName));
sUserData = "Test 123";
- Assert.IsTrue(FileUtils.SaveFile(SaveTestFile, Path.Combine(sLocalTestPath, sFileName), this));
+ Assert.IsTrue(FileUtils.SaveFile(SaveTestFile!, Path.Combine(sLocalTestPath, sFileName), this));
Assert.IsTrue(File.Exists(Path.Combine(sLocalTestPath, sFileName)));
}
@@ -196,7 +196,7 @@ public void SaveFileTest2(int iPreMode, string sFileName, bool xExp)
PrepareFiles(ePreMode, Path.Combine(sLocalTestPath, sFileName));
sUserData = "Test 123";
eException = new FieldAccessException("Dummy Dummy");
- Assert.ThrowsExactly(() => FileUtils.SaveFile(SaveTestFile, Path.Combine(sLocalTestPath, sFileName), this));
+ Assert.ThrowsExactly(() => FileUtils.SaveFile(SaveTestFile!, Path.Combine(sLocalTestPath, sFileName), this));
Assert.AreEqual(xExp, File.Exists(Path.Combine(sLocalTestPath, sFileName)));
}
@@ -214,7 +214,7 @@ public void SaveFileTest4()
var sFileName = "test.nix";
try
{
- Assert.ThrowsExactly(()=>FileUtils.SaveFile(SaveTestFile2, Path.Combine(sLocalTestPath, sFileName), this));
+ Assert.ThrowsExactly(()=>FileUtils.SaveFile(SaveTestFile2!, Path.Combine(sLocalTestPath, sFileName), this));
Assert.IsFalse(File.Exists(Path.Combine(sLocalTestPath, sFileName)));
}
finally
diff --git a/CSharpBible/Libraries/BaseLibTests/Helper/ListHelperTests.cs b/CSharpBible/Libraries/BaseLibTests/Helper/ListHelperTests.cs
new file mode 100644
index 000000000..7f49e6103
--- /dev/null
+++ b/CSharpBible/Libraries/BaseLibTests/Helper/ListHelperTests.cs
@@ -0,0 +1,67 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Linq;
+using static BaseLib.Helper.TestHelper;
+
+namespace BaseLib.Helper.Tests;
+
+[TestClass()]
+public class ListHelperTests
+{
+ [TestMethod()]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 2, new object[] { 0, 1, 2, 3, 4 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 4, 0, new object[] { 4, 0, 1, 2, 3 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 1, 3, new object[] { 0, 2, 1, 3, 4 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 5, new object[] { 0, 1, 3, 4, 2 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 1, 6, new object[] { 0, 1, 2, 3, 4 })]
+ public void MoveItemTest(object[] aoSrc, int iSrc, int iDst, object[] asExp)
+ {
+ var aoResult = aoSrc.ToList();
+ if (iDst > asExp.Length)
+ Assert.ThrowsExactly(() => aoResult.MoveItem(iSrc, iDst));
+ else
+ aoResult.MoveItem(iSrc, iDst);
+ AssertAreEqual(asExp, aoResult.ToArray());
+ }
+
+ [TestMethod()]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 2, 2, new object[] { 0, 1, 2, 3, 4 })]
+ [DataRow(new object[] { 0, 1, 2, 3, 4 }, 4, 0, new object[] { 4, 1, 2, 3, 0 })]
+ public void SwapTest(object[] aoSrc, int iSrc, int iDst, object[] asExp)
+ {
+ aoSrc.Swap(iSrc, iDst);
+ AssertAreEqual(asExp, aoSrc);
+ }
+
+ [TestMethod()]
+ [DataRow(0, -1, new int[] { })]
+ [DataRow(2, 2, new[] { 2 })]
+ [DataRow(0, 4, new[] { 0, 1, 2, 3, 4 })]
+ public void RangeTest(int iSrc, int iDst, int[] aiExp)
+ {
+ AssertAreEqual(aiExp, typeof(int).Range(iSrc, iDst));
+ }
+
+ [TestMethod()]
+ [DataRow('2', '6', new char[] { '2', '3', '4', '5', '6' })]
+ public void RangeTest2(char iSrc, char iDst, char[] aoExp)
+ {
+ AssertAreEqual(aoExp, typeof(object).Range(iSrc, iDst));
+ }
+
+ [TestMethod()]
+ [DataRow((byte)0, (byte)4, new byte[] { 0, 1, 2, 3, 4 })]
+ public void RangeTest3(byte iSrc, byte iDst, byte[] aoExp)
+ {
+ AssertAreEqual(aoExp, typeof(object).Range(iSrc, iDst));
+ }
+
+ [TestMethod()]
+ [DataRow(0, -1, new int[] { })]
+ [DataRow(2, 2, new[] { 2 })]
+ [DataRow(0, 4, new[] { 0, 1, 2, 3, 4 })]
+ public void ToTest(int iSrc, int iDst, int[] aoExp)
+ {
+ AssertAreEqual(aoExp, iSrc.To(iDst));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/BaseLibTests/Helper/ObjectHelper2Tests.cs b/CSharpBible/Libraries/BaseLibTests/Helper/ObjectHelper2Tests.cs
new file mode 100644
index 000000000..333e3b3f7
--- /dev/null
+++ b/CSharpBible/Libraries/BaseLibTests/Helper/ObjectHelper2Tests.cs
@@ -0,0 +1,45 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.Generic;
+
+namespace BaseLib.Helper.Tests;
+
+[TestClass()]
+public class ObjectHelper2Tests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Fügen Sie ggf. den „erforderlichen“ Modifizierer hinzu, oder deklarieren Sie den Modifizierer als NULL-Werte zulassend.
+ private ControlArray _testClass;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Fügen Sie ggf. den „erforderlichen“ Modifizierer hinzu, oder deklarieren Sie den Modifizierer als NULL-Werte zulassend.
+
+ [TestInitialize]
+ public void Init()
+ {
+ _testClass = new ControlArray();
+ }
+
+ [TestMethod()]
+ public void SetIndexTest()
+ {
+ // Act
+ _testClass.SetIndex(8, 4);
+ _testClass.SetIndex(2, 1);
+ // Assert
+ Assert.HasCount(2, _testClass);
+ Assert.AreEqual(8, _testClass[4]);
+ Assert.AreEqual(2, _testClass[1]);
+ }
+
+ [TestMethod()]
+ public void GetIndexTest()
+ {
+ // Arrange
+ var d = (Dictionary)_testClass;
+ d[5] = 8;
+ d[1] = null!;
+ d[2] = 2;
+
+ // Assert
+ Assert.AreEqual(4, _testClass.GetIndex(8));
+ Assert.AreEqual(1, _testClass.GetIndex(2));
+ Assert.AreEqual(-1, _testClass.GetIndex(1));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/BaseLibTests/Helper/ObjectHelperTests.cs b/CSharpBible/Libraries/BaseLibTests/Helper/ObjectHelperTests.cs
new file mode 100644
index 000000000..1357942ca
--- /dev/null
+++ b/CSharpBible/Libraries/BaseLibTests/Helper/ObjectHelperTests.cs
@@ -0,0 +1,278 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using BaseLib.Interfaces;
+using NSubstitute;
+
+namespace BaseLib.Helper.Tests;
+
+[TestClass()]
+public class ObjectHelperTests
+{
+ [TestMethod()]
+ [DataRow(null, -1)]
+ [DataRow(1, 1)]
+ [DataRow(int.MaxValue, int.MaxValue)]
+ [DataRow(int.MinValue, int.MinValue)]
+ [DataRow(uint.MaxValue-1, -2)]
+ [DataRow(2u, 2)]
+ [DataRow("3", 3)]
+ [DataRow("4.5", -1)]
+ [DataRow(5.1f, 5)]
+ [DataRow(6L, 6)]
+ [DataRow("DBNull", -1)]
+ [DataRow("IHasValue", 0)]
+ [DataRow("_", -1)]
+ public void AsIntTest(object? obj,int iExp)
+ {
+ if (obj is string s)
+ obj = obj switch
+ {
+ "DBNull" => DBNull.Value,
+ "IHasValue" => Substitute.For(),
+ "_" => new object(),
+ _ => obj
+ };
+ // Act
+ var result = ObjectHelper.AsInt(obj,-1);
+
+ // Assert
+ Assert.AreEqual(iExp, result);
+
+ }
+
+ [TestMethod()]
+ [DataRow(null, -1)]
+ [DataRow(1, 1)]
+ [DataRow(long.MaxValue, long.MaxValue)]
+ [DataRow(long.MinValue, long.MinValue)]
+ [DataRow(ulong.MaxValue - 1, -2)]
+ [DataRow(2u, 2)]
+ [DataRow("3", 3)]
+ [DataRow("4.5", -1)]
+ [DataRow(5.1f, 5)]
+ [DataRow(6L, 6)]
+ [DataRow("DBNull", -1)]
+ [DataRow("IHasValue", 0)]
+ [DataRow("_", -1)]
+ public void AsLongTest(object? obj, long lExp)
+ {
+ if (obj is string s)
+ obj = obj switch
+ {
+ "DBNull" => DBNull.Value,
+ "IHasValue" => Substitute.For(),
+ "_" => new object(),
+ _ => obj
+ };
+ // Act
+ var result = ObjectHelper.AsLong(obj, -1);
+
+ // Assert
+ Assert.AreEqual(lExp, result);
+ }
+
+ public enum TestEnum
+ {
+ Default = 0,
+ Value1 = 1,
+ Value2 = 2
+ }
+
+ [TestMethod()]
+ [DataRow(null, TestEnum.Default)]
+ [DataRow("Value1", TestEnum.Value1)]
+ [DataRow("Value2", TestEnum.Value2)]
+ [DataRow("InvalidValue", TestEnum.Default)]
+ [DataRow("TestEnum.Value1", TestEnum.Value1)]
+ [DataRow("IHasValue", TestEnum.Default)]
+ [DataRow("DBNull", TestEnum.Default)]
+ [DataRow(1, TestEnum.Value1)]
+ [DataRow(2, TestEnum.Value2)]
+ [DataRow(2u, TestEnum.Value2)]
+ [DataRow(0, TestEnum.Default)]
+ [DataRow(99, (TestEnum)99)]
+ [DataRow(-1, (TestEnum)(int)-1)]
+ [DataRow("_", TestEnum.Default)]
+ public void AsEnumTest(object? obj, TestEnum expected)
+ {
+ if (obj is string s)
+ obj = obj switch
+ {
+ "DBNull" => DBNull.Value,
+ "IHasValue" => Substitute.For(),
+ "TestEnum.Value1" => TestEnum.Value1,
+
+ "_" => new object(),
+ _ => obj
+ };
+ // Act
+ var result = ObjectHelper.AsEnum(obj);
+
+ // Assert
+ Assert.AreEqual(expected, result);
+ }
+
+ [TestMethod()]
+ [DataRow(null, "0001-01-01T00:00:00")]
+ [DataRow(20231040, "0001-01-01T00:00:00")]
+ [DataRow(20230010, "0001-01-01T00:00:00")]
+ [DataRow(0, "0001-01-01T00:00:00")]
+ [DataRow(20231010, "2023-10-10T00:00:00")]
+ [DataRow(1000u, "1902-09-26T00:00:00")]
+ [DataRow(1000, "1902-09-26T00:00:00")]
+ [DataRow('c', "1999-01-01T00:00:00")]
+ [DataRow("10-10-2000", "2000-10-10T00:00:00")]
+ [DataRow("2023-10-10", "2023-10-10T00:00:00")]
+ [DataRow("10/10/2023", "2023-10-10T00:00:00")]
+ [DataRow("10-10-2023", "2023-10-10T00:00:00")]
+ [DataRow("2023-10-10T10:10:10", "2023-10-10T10:10:10")]
+ [DataRow("InvalidDate", "0001-01-01T00:00:00")]
+ [DataRow(638000000000000000L, "2022-09-28T22:13:20")]
+ [DataRow("DBNull", "0001-01-01T00:00:00")]
+ [DataRow("IHasValue", "0001-01-01T00:00:00")]
+ [DataRow("_", "0001-01-01T00:00:00")]
+ public void AsDateTest(object? obj, string expected)
+ {
+ if (obj is string s)
+ obj = s switch
+ {
+ "DBNull" => DBNull.Value,
+ "IHasValue" => Substitute.For(),
+ "_" => new object(),
+ _ when s.Contains("2000") && DateTime.TryParse(s, out var dt) => dt,
+ _ => obj
+ };
+ // Act
+ var result = ObjectHelper.AsDate(obj);
+
+ // Assert
+ Assert.AreEqual(DateTime.Parse(expected), result);
+ }
+
+ [TestMethod()]
+ [DataRow(null, 0)]
+ [DataRow(1, 1)]
+ [DataRow(long.MaxValue, long.MaxValue)]
+ [DataRow(long.MinValue, long.MinValue)]
+ [DataRow(ulong.MaxValue , ulong.MaxValue)]
+ [DataRow(2u, 2)]
+ [DataRow("3", 3)]
+ [DataRow("Dog", 0)]
+ [DataRow("4.5", 4.5)]
+ [DataRow(5.1f, 5.1f)]
+ [DataRow(double.MinValue, double.MinValue)]
+ [DataRow(double.MaxValue, double.MaxValue)]
+ [DataRow(double.NaN, double.NaN)]
+ [DataRow(6L, 6)]
+ [DataRow('7', 48+7)]
+ [DataRow("DBNull", 0)]
+ [DataRow("IHasValue", 0)]
+ [DataRow("_", 0)]
+ public void AsDoubleTest(object? obj, double dExp)
+ {
+ if (obj is string s)
+ obj = s switch
+ {
+ "DBNull" => DBNull.Value,
+ "IHasValue" => Substitute.For(),
+ "_" => new object(),
+ _ => obj
+ };
+ // Act
+ var result = ObjectHelper.AsDouble(obj);
+
+ // Assert
+ Assert.AreEqual(dExp, result);
+ }
+
+ [TestMethod()]
+ [DataRow(null, false)]
+ [DataRow(true, true)]
+ [DataRow(false, false)]
+ [DataRow(1, true)]
+ [DataRow(0, false)]
+ [DataRow("true", true)]
+ [DataRow("false", false)]
+ [DataRow("True", true)]
+ [DataRow("False", false)]
+ [DataRow("yes", false)]
+ [DataRow("no", false)]
+ [DataRow("1", true)]
+ [DataRow("0", false)]
+ [DataRow('1', true)]
+ [DataRow('T', true)]
+ [DataRow('t', true)]
+ [DataRow('0', false)]
+ [DataRow("DBNull", false)]
+ [DataRow("IHasValue", false)]
+ [DataRow("_", false)]
+ public void AsBoolTest(object? obj, bool bExp)
+ {
+ if (obj is string s)
+ obj = obj switch
+ {
+ "DBNull" => DBNull.Value,
+ "IHasValue" => Substitute.For(),
+ "_" => new object(),
+ _ => obj
+ };
+ // Act
+ var result = ObjectHelper.AsBool(obj);
+
+ // Assert
+ Assert.AreEqual(bExp, result);
+ }
+
+ [TestMethod()]
+ [DataRow(null, "00000000-0000-0000-0000-000000000000")]
+ [DataRow("d3c4a1b2-3f4e-5d6c-7a8b-9c0d1e2f3a4b", "d3c4a1b2-3f4e-5d6c-7a8b-9c0d1e2f3a4b")]
+ [DataRow("invalid-guid", "00000000-0000-0000-0000-000000000000")]
+ [DataRow("00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000")]
+ [DataRow("123456789", "075bcd15-0000-0000-0000-000000000000")]
+ [DataRow(123456789L, "075bcd15-0000-0000-0000-000000000000")]
+ [DataRow(123456789u, "075bcd15-0000-0000-0000-000000000000")]
+ [DataRow("IHasValue", "00000000-0000-0000-0000-000000000000")]
+ [DataRow("DBNull", "00000000-0000-0000-0000-000000000000")]
+ [DataRow("EmptyGUID", "00000000-0000-0000-0000-000000000000")]
+ [DataRow("_", "00000000-0000-0000-0000-000000000000")]
+ public void AsGUIDTest(object? obj, string expected)
+ {
+ if (obj is string s)
+ obj = obj switch
+ {
+ "DBNull" => DBNull.Value,
+ "IHasValue" => Substitute.For(),
+ "EmptyGUID" => Guid.Empty,
+ "_" => new object(),
+ _ => obj
+ };
+
+ // Act
+ var result = ObjectHelper.AsGUID(obj);
+
+ // Assert
+ Assert.AreEqual(Guid.Parse(expected), result);
+ }
+
+ [TestMethod()]
+ [DataRow(null, false)]
+ [DataRow(1, true)]
+ [DataRow("test", true)]
+ [DataRow("", false)]
+ [DataRow(0, false)]
+ public void SetRetTest(object? input, bool expected)
+ {
+ // Arrange
+ bool actionCalled = false;
+ object? _o = null;
+ Action action = (obj) => { actionCalled = true; _o = obj; };
+
+ // Act
+ var result = ObjectHelper.SetRet(input, action, expected);
+
+ // Assert
+ Assert.AreEqual(expected, result);
+ Assert.AreEqual(input, _o);
+ Assert.IsTrue(actionCalled);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/BaseLibTests/Helper/StringUtilsTests.cs b/CSharpBible/Libraries/BaseLibTests/Helper/StringUtilsTests.cs
index ba2f59670..becb3fffc 100644
--- a/CSharpBible/Libraries/BaseLibTests/Helper/StringUtilsTests.cs
+++ b/CSharpBible/Libraries/BaseLibTests/Helper/StringUtilsTests.cs
@@ -326,4 +326,38 @@ public void IsValidIdentifyerTest(string sAct, bool xExp)
{
Assert.AreEqual(xExp,sAct.IsValidIdentifyer());
}
+
+ [TestMethod()]
+ [DataRow("This is a test",14, "This is a test")]
+ [DataRow("This is a test",0, "")]
+ [DataRow("This is a test",-1, "This is a tes")]
+ [DataRow("This is a test",1, "T")]
+ [DataRow("This is a test",20, "This is a test")]
+ public void LeftTest(string sAct,int iAct, string sExp)
+ {
+ Assert.AreEqual(sExp, sAct.Left(iAct));
+ }
+
+ [TestMethod()]
+ [DataRow("This is a test",14, "This is a test")]
+ [DataRow("This is a test",0, "")]
+ [DataRow("This is a test",-1, "his is a test")]
+ [DataRow("This is a test",1, "t")]
+ [DataRow("This is a test",20, "This is a test")]
+ public void RightTest(string sAct,int iAct, string sExp)
+ {
+ Assert.AreEqual(sExp, sAct.Right(iAct));
+ }
+
+ [TestMethod()]
+ [DataRow(null, "")]
+ [DataRow("", "")]
+ [DataRow(true, "True")]
+ [DataRow(false, "False")]
+ [DataRow(1234, "1234")]
+
+ public void AsStringTest(object? oAct,string sExp)
+ {
+ Assert.AreEqual(sExp, oAct.AsString());
+ }
}
diff --git a/CSharpBible/Libraries/BaseLibTests/Interfaces/IParentedObjectTests.cs b/CSharpBible/Libraries/BaseLibTests/Interfaces/IParentedObjectTests.cs
index 85fc8e19c..27d2e40c9 100644
--- a/CSharpBible/Libraries/BaseLibTests/Interfaces/IParentedObjectTests.cs
+++ b/CSharpBible/Libraries/BaseLibTests/Interfaces/IParentedObjectTests.cs
@@ -8,7 +8,7 @@ namespace BaseLib_netTests.Interfaces
class TestIPClass : IParentedObject>
{
private List? _parent=null;
- public string Name { get; set; }
+ public string? Name { get; set; }
public List? GetParent()
=> _parent;
@@ -28,7 +28,7 @@ public override string ToString()
[TestClass]
public class IParentedObjectTests
{
- private IParentedObject>[] _child;
+ private IParentedObject>[]? _child;
private readonly List _par1=new();
private readonly List _par2=new();
@@ -45,7 +45,7 @@ public void Init()
#if NET5_0_OR_GREATER
[TestMethod]
public void ParentTest() {
- Assert.IsNull(_child[0].Parent);
+ Assert.IsNull(_child![0].Parent);
Assert.IsEmpty(_par1);
Assert.IsEmpty(_par2);
_child[0].Parent = _par1;
@@ -64,7 +64,7 @@ public void ParentTest() {
[TestMethod]
public void SetParentTest()
{
- Assert.IsNull(_child[0].GetParent());
+ Assert.IsNull(_child![0].GetParent());
Assert.IsEmpty(_par1);
Assert.IsEmpty(_par2);
_child[0].SetParent(_par1);
diff --git a/CSharpBible/Libraries/BaseLibTests/Model/CRandomTests.cs b/CSharpBible/Libraries/BaseLibTests/Model/CRandomTests.cs
new file mode 100644
index 000000000..61baf4b03
--- /dev/null
+++ b/CSharpBible/Libraries/BaseLibTests/Model/CRandomTests.cs
@@ -0,0 +1,82 @@
+using BaseLib.Models.Interfaces;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using static BaseLib.Helper.TestHelper;
+
+namespace BaseLib.Models.Tests;
+
+[TestClass()]
+public class CRandomTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ private CRandom _testClass;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ _testClass = new CRandom();
+ _testClass.Seed(0);
+ }
+
+ [TestMethod()]
+ public void CRandomTest()
+ {
+ Assert.IsNotNull(_testClass);
+ Assert.IsInstanceOfType(_testClass, typeof(IRandom));
+ Assert.IsInstanceOfType(_testClass, typeof(CRandom));
+ }
+
+ [TestMethod()]
+ [DataRow(0, 1, new[] { 0, 0, 0, 0, 0 })]
+ [DataRow(0, 2, new[] { 1, 1, 1, 1, 0 })]
+
+ public void NextTest(int imin, int iMax,int[] asExp)
+ {
+ var aoResult = new int[asExp.Length];
+ for (int i = 0; i < asExp.Length; i++)
+ {
+ aoResult[i] = _testClass.Next(imin, iMax);
+ }
+ AssertAreEqual(asExp, aoResult);
+ }
+
+ [TestMethod()]
+ [DataRow(new[] { 0.7262432699679598, 0.8173253595909687, 0.7680226893946634,0.5581611914365372, 0.2060331540210327 })]
+ public void NextDoubleTest(double[] adExp)
+ {
+ var adResult = new double[adExp.Length];
+ for (int i = 0; i < adExp.Length; i++)
+ {
+ adResult[i] = _testClass.NextDouble();
+ }
+ AssertAreEqual(adExp, adResult);
+ }
+
+ [TestMethod()]
+ [DataRow(new[] { 1559595546, 1755192844, 1649316166, 1198642031, 442452829 })]
+ public void NextIntTest(int[] asExp)
+ {
+ var aoResult = new int[asExp.Length];
+ for (int i = 0; i < asExp.Length; i++)
+ {
+ aoResult[i] = _testClass.NextInt();
+ }
+ AssertAreEqual(asExp, aoResult);
+ }
+
+ [TestMethod()]
+ [DataRow(0, new[] { 1559595546, 1755192844, 1649316166, 1198642031, 442452829 })]
+ [DataRow(1, new[] { 534011718, 237820880, 1002897798, 1657007234, 1412011072 })]
+ [DataRow(int.MaxValue, new[] { 1559595546, 1755192844, 1649316172, 1198642031, 442452829 })]
+
+ public void SeedTest(int iSeed, int[] asExp)
+ {
+ _testClass.Seed(iSeed);
+ var aoResult = new int[asExp.Length];
+ for (int i = 0; i < asExp.Length; i++)
+ {
+ aoResult[i] = _testClass.NextInt();
+ }
+ AssertAreEqual(asExp, aoResult);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/BaseLibTests/Model/ConsoleProxyTests.cs b/CSharpBible/Libraries/BaseLibTests/Model/ConsoleProxyTests.cs
new file mode 100644
index 000000000..b769e5b8f
--- /dev/null
+++ b/CSharpBible/Libraries/BaseLibTests/Model/ConsoleProxyTests.cs
@@ -0,0 +1,186 @@
+using BaseLib.Models;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace BaseLib.Models.Tests;
+
+[TestClass]
+public class ConsoleProxyTests
+{
+ private ConsoleProxy _sut = null!;
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ _sut = new ConsoleProxy();
+ }
+
+ [TestMethod]
+ public void Constructor_ShouldCreateInstance()
+ {
+ // Assert
+ Assert.IsNotNull(_sut);
+ }
+
+ [TestMethod]
+ public void ForegroundColor_Get_ShouldReturnConsoleColor()
+ {
+ // Act
+ var result = _sut.ForegroundColor;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(ConsoleColor));
+ }
+
+ [TestMethod]
+ public void ForegroundColor_Set_ShouldNotThrow()
+ {
+ // Arrange
+ var originalColor = _sut.ForegroundColor;
+
+ // Act & Assert
+ _sut.ForegroundColor = ConsoleColor.Red;
+ _sut.ForegroundColor = originalColor;
+ }
+
+ [TestMethod]
+ public void BackgroundColor_Get_ShouldReturnConsoleColor()
+ {
+ // Act
+ var result = _sut.BackgroundColor;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(ConsoleColor));
+ }
+
+ [TestMethod]
+ public void BackgroundColor_Set_ShouldNotThrow()
+ {
+ // Arrange
+ var originalColor = _sut.BackgroundColor;
+
+ // Act & Assert
+ _sut.BackgroundColor = ConsoleColor.Blue;
+ _sut.BackgroundColor = originalColor;
+ }
+
+ [TestMethod]
+ public void IsOutputRedirected_ShouldReturnBool()
+ {
+ // Act
+ var result = _sut.IsOutputRedirected;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(bool));
+ }
+
+ [TestMethod]
+ public void KeyAvailable_ShouldReturnBool()
+ {
+ // Act
+ var result = _sut.KeyAvailable;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(bool));
+ }
+
+ [TestMethod]
+ public void LargestWindowHeight_ShouldReturnInt()
+ {
+ // Act
+ var result = _sut.LargestWindowHeight;
+
+ // Assert
+ Assert.IsInstanceOfType(result, typeof(int));
+ }
+
+ [TestMethod]
+ public void Title_Get_ShouldReturnString()
+ {
+ // Act
+ var result = _sut.Title;
+
+ // Assert
+ Assert.IsNotNull(result);
+ }
+
+ [TestMethod]
+ public void Title_Set_ShouldNotThrow()
+ {
+ // Arrange
+ var originalTitle = _sut.Title;
+
+ // Act & Assert
+ _sut.Title = "TestTitle";
+ _sut.Title = originalTitle;
+ }
+
+ [TestMethod]
+ public void GetCursorPosition_ShouldReturnTuple()
+ {
+ // Act
+ var result = _sut.GetCursorPosition;
+
+ // Assert
+ Assert.IsNotNull(result);
+ }
+
+ [TestMethod]
+ public void SetCursorPosition_ShouldNotThrow()
+ {
+ // Arrange
+
+ // Act
+ var result = _sut.SetCursorPosition;
+
+ //Assert
+ Assert.IsNotNull(result);
+ }
+
+ [TestMethod]
+ public void Write_Char_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.Write('X');
+ }
+
+ [TestMethod]
+ public void Write_String_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.Write("Test");
+ }
+
+ [TestMethod]
+ public void Write_NullString_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.Write((string?)null);
+ }
+
+ [TestMethod]
+ public void WriteLine_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.WriteLine("TestLine");
+ }
+
+ [TestMethod]
+ public void WriteLine_Null_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ _sut.WriteLine(null);
+ }
+
+ [TestMethod]
+ public void Clear_ShouldNotThrow()
+ {
+ // Act & Assert - keine Exception erwartet
+ var m= _sut.Clear;
+ //Assert
+ Assert.IsNotNull(m);
+ }
+
+ // Beep und ReadKey/ReadLine werden nicht getestet, da sie
+ // auf Benutzereingaben warten oder Audio abspielen
+}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/BaseLibTests/Model/NotificationObjectAdvTests.cs b/CSharpBible/Libraries/BaseLibTests/Model/NotificationObjectAdvTests.cs
index c7770dbdc..88353ec55 100644
--- a/CSharpBible/Libraries/BaseLibTests/Model/NotificationObjectAdvTests.cs
+++ b/CSharpBible/Libraries/BaseLibTests/Model/NotificationObjectAdvTests.cs
@@ -19,7 +19,7 @@ public enum eValidReact
ArgumetException,
}
- private string _testString;
+ private string _testString = "";
private int _testInt;
private float _testFloat;
private double _testDouble;
@@ -95,7 +95,7 @@ private void Clear()
DebugResult = "";
}
- private void OnPropertyChanged(object sender, PropertyChangedAdvEventArgs e)
+ private void OnPropertyChanged(object? sender, PropertyChangedAdvEventArgs e)
=> DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName)?.GetValue(sender)}, o:{e.OldVal}, n:{e.NewVal}{Environment.NewLine}";
[TestInitialize]
diff --git a/CSharpBible/Libraries/BaseLibTests/Model/SysTimeTests.cs b/CSharpBible/Libraries/BaseLibTests/Model/SysTimeTests.cs
new file mode 100644
index 000000000..abb53320a
--- /dev/null
+++ b/CSharpBible/Libraries/BaseLibTests/Model/SysTimeTests.cs
@@ -0,0 +1,81 @@
+using BaseLib.Models;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace BaseLib.Models.Tests;
+
+[TestClass]
+public class SysTimeTests
+{
+ private SysTime _sut = null!;
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ _sut = new SysTime();
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ SysTime.GetNow = () => DateTime.Now;
+ }
+
+ [TestMethod]
+ public void Now_ReturnsCurrentDateTime()
+ {
+ // Arrange
+ var expectedDate = new DateTime(2024, 6, 15, 10, 30, 45);
+ SysTime.GetNow = () => expectedDate;
+
+ // Act
+ var result = _sut.Now;
+
+ // Assert
+ Assert.AreEqual(expectedDate, result);
+ }
+
+ [TestMethod]
+ public void Today_ReturnsDatePartOnly()
+ {
+ // Arrange
+ var dateWithTime = new DateTime(2024, 6, 15, 10, 30, 45);
+ var expectedDate = new DateTime(2024, 6, 15);
+ SysTime.GetNow = () => dateWithTime;
+
+ // Act
+ var result = _sut.Today;
+
+ // Assert
+ Assert.AreEqual(expectedDate, result);
+ }
+
+ [TestMethod]
+ public void GetNow_DefaultValue_ReturnsDateTimeNow()
+ {
+ // Arrange
+ SysTime.GetNow = () => DateTime.Now;
+
+ // Act
+ var before = DateTime.Now;
+ var result = _sut.Now;
+ var after = DateTime.Now;
+
+ // Assert
+ Assert.IsTrue(result >= before && result <= after);
+ }
+
+ [TestMethod]
+ public void GetNow_CanBeOverridden()
+ {
+ // Arrange
+ var customDate = new DateTime(2000, 1, 1);
+ SysTime.GetNow = () => customDate;
+
+ // Act
+ var result = SysTime.GetNow();
+
+ // Assert
+ Assert.AreEqual(customDate, result);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/CommonDialogs/Class1.cs b/CSharpBible/Libraries/CommonDialogs/Class1.cs
deleted file mode 100644
index 88307b3c6..000000000
--- a/CSharpBible/Libraries/CommonDialogs/Class1.cs
+++ /dev/null
@@ -1,235 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace CommonDialogs
-{
- public class TaskDialog : Component
- {
- private Microsoft.WindowsAPICodePack.Dialogs.TaskDialog _td = new Microsoft.WindowsAPICodePack.Dialogs.TaskDialog();
-
- //
- // Zusammenfassung:
- // Creates a basic TaskDialog window
- public TaskDialog()
- {
-
- }
-
- //
- // Zusammenfassung:
- // TaskDialog Finalizer
- ~TaskDialog()
- {
- _td.Dispose();
- }
-
- //
- // Zusammenfassung:
- // Indicates whether this feature is supported on the current platform.
- public static bool IsPlatformSupported { get => Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.IsPlatformSupported; }
- /*
- // Zusammenfassung:
- // Gets or sets the progress bar on the taskdialog. ProgressBar a visual representation
- // of the progress of a long running operation.
- public TaskDialogProgressBar ProgressBar { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the startup location.
- public TaskDialogStartupLocation StartupLocation { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the expansion mode for this dialog.
- public TaskDialogExpandedDetailsLocation ExpansionMode { get; set; }
- /*/
- // Zusammenfassung:
- // Gets or sets a value that indicates if the footer checkbox is checked.
- public bool? FooterCheckBoxChecked { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that determines if hyperlinks are enabled.
- public bool HyperlinksEnabled { get; set; }
- /*
- // Zusammenfassung:
- // Gets a value that contains the TaskDialog controls.
- public DialogControlCollection Controls { get; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the standard buttons.
- public TaskDialogStandardButtons StandardButtons { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the footer icon.
- public TaskDialogStandardIcon FooterIcon { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the TaskDialog main icon.
- public TaskDialogStandardIcon Icon { get; set; }
- /*/
- // Zusammenfassung:
- // Gets or sets a value that determines if Cancelable is set.
- public bool Cancelable { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the collapsed control text.
- public string DetailsCollapsedLabel { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the expanded control text.
- public string DetailsExpandedLabel { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the expanded text in the details section.
- public string DetailsExpandedText { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that determines if the details section is expanded.
- public bool DetailsExpanded { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the footer text.
- public string FooterText { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the caption text.
- public string Caption { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the instruction text.
- public string InstructionText { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the message text.
- public string Text { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the owner window's handle.
- public IntPtr OwnerWindowHandle { get; set; }
- //
- // Zusammenfassung:
- // Gets or sets a value that contains the footer check box text.
- public string FooterCheckBoxText { get; set; }
-
-
- //
- // Zusammenfassung:
- // Occurs when a user clicks on Help.
- public event EventHandler HelpInvoked { add => _td.HelpInvoked += value; remove => _td.HelpInvoked -= value; }
- //
- // Zusammenfassung:
- // Occurs when the TaskDialog is closing.
- public event EventHandler Closing;
- //
- // Zusammenfassung:
- // Occurs when a user clicks a hyperlink.
- public event EventHandler HyperlinkClick;
- //
- // Zusammenfassung:
- // Occurs when a progress bar changes.
- public event EventHandler Tick;
- //
- // Zusammenfassung:
- // Occurs when the TaskDialog is opened.
- public event EventHandler Opened { add=> _td.Opened+=value; remove => _td.Opened -= value; }
-
- //
- // Zusammenfassung:
- // Creates and shows a task dialog with the specified supporting text, main instruction,
- // and dialog caption.
- //
- // Parameter:
- // text:
- // The supporting text to display.
- //
- // instructionText:
- // The main instruction text to display.
- //
- // caption:
- // The caption for the dialog.
- //
- // Rückgabewerte:
- // The dialog result.
- public static bool Show(string text, string instructionText, string caption)
- => Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.Show(text,instructionText,caption) == Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok;
- //
- // Zusammenfassung:
- // Creates and shows a task dialog with the specified supporting text and main instruction.
- //
- // Parameter:
- // text:
- // The supporting text to display.
- //
- // instructionText:
- // The main instruction text to display.
- //
- // Rückgabewerte:
- // The dialog result.
- public static bool Show(string text, string instructionText)
- => Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.Show(text,instructionText)==Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok;
- //
- // Zusammenfassung:
- // Creates and shows a task dialog with the specified message text.
- //
- // Parameter:
- // text:
- // The text to display.
- //
- // Rückgabewerte:
- // The dialog result.
- public static bool Show(string text)
- => Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.Show(text)==Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok;
- //
- // Zusammenfassung:
- // Close TaskDialog with a given TaskDialogResult
- //
- // Parameter:
- // closingResult:
- // TaskDialogResult to return from the TaskDialog.Show() method
- //
- // Ausnahmen:
- // T:System.InvalidOperationException:
- // if TaskDialog is not showing.
- public void Close(bool result)=>_td.Close(result ? Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok:
- Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Cancel);
- //
- // Zusammenfassung:
- // Close TaskDialog
- //
- // Ausnahmen:
- // T:System.InvalidOperationException:
- // if TaskDialog is not showing.
- public void Close()=>_td.Close();
- //
- // Zusammenfassung:
- // Dispose TaskDialog Resources
-#if NET50_OR_GREATER
- public override void Dispose()=>_td.Dispose();
-#else
- public new void Dispose()=>_td.Dispose();
-#endif
- //
- // Zusammenfassung:
- // Dispose TaskDialog Resources
- //
- // Parameter:
- // disposing:
- // If true, indicates that this is being called via Dispose rather than via the
- // finalizer.
-#if NET50_OR_GREATER
- public override void Dispose(bool disposing) => _td.Dispose(disposing);
-#else
- public new void Dispose(bool disposing) => _td.Dispose(disposing);
-#endif
- //
- // Zusammenfassung:
- // Creates and shows a task dialog.
- //
- // Rückgabewerte:
- // The dialog result.
- public bool? Show()=> _td.Show()==Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok;
-
- }
-}
diff --git a/CSharpBible/Libraries/CommonDialogs/CommonDialogs_net.csproj b/CSharpBible/Libraries/CommonDialogs/CommonDialogs_net.csproj
index fcd0ced70..0c2c0f81f 100644
--- a/CSharpBible/Libraries/CommonDialogs/CommonDialogs_net.csproj
+++ b/CSharpBible/Libraries/CommonDialogs/CommonDialogs_net.csproj
@@ -1,21 +1,25 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
true
true
Library
- AnyCPU;x86
-
+ AnyCPU;x86;x64
+
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
-
+
-
+
diff --git a/CSharpBible/Libraries/CommonDialogs/FileDialogProxy.cs b/CSharpBible/Libraries/CommonDialogs/FileDialogProxy.cs
index 70617c2ec..78008fa4f 100644
--- a/CSharpBible/Libraries/CommonDialogs/FileDialogProxy.cs
+++ b/CSharpBible/Libraries/CommonDialogs/FileDialogProxy.cs
@@ -3,38 +3,138 @@
namespace CommonDialogs;
-public class FileDialogProxy : IFileDialog where T : FileDialog
+///
+/// Represents a generic proxy for file dialogs, wrapping a specific implementation.
+///
+/// The type of the file dialog to wrap. Must be a reference type.
+///
+/// This class adapts the standard WPF (and its subclasses like or )
+/// to the interface. This abstraction facilitates unit testing and dependency injection
+/// by allowing the dialog logic to be mocked or substituted.
+///
+public class FileDialogProxy : IFileDialog where T : class
{
- private T _fileDialog;
+ ///
+ /// The internal reference to the wrapped .
+ ///
+ private FileDialog _fileDialog;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The file dialog instance to wrap.
+ ///
+ /// The provided is cast to .
+ /// Ensure that is compatible with .
+ ///
public FileDialogProxy(T fileDialog)
{
- _fileDialog = fileDialog;
+ _fileDialog = (fileDialog as FileDialog)!;
}
- public T This => _fileDialog;
+ ///
+ /// Gets the underlying file dialog instance.
+ ///
+ /// The wrapped file dialog instance cast to type .
+ public T This => (_fileDialog as T)!;
+ ///
+ /// Gets or sets the full path of the file selected in the file dialog.
+ ///
+ ///
+ /// A containing the full path of the selected file.
+ ///
public string FileName
{
get => _fileDialog.FileName;
set => _fileDialog.FileName = value;
}
+
+ ///
+ /// Gets or sets the filter string that determines what types of files are displayed in the file dialog.
+ ///
+ ///
+ /// A containing the filter options.
+ /// For example: "Text files (*.txt)|*.txt|All files (*.*)|*.*"
+ ///
public string Filter { get => _fileDialog.Filter; set => _fileDialog.Filter = value; }
+
+ ///
+ /// Gets or sets the index of the filter currently selected in the file dialog.
+ ///
+ ///
+ /// The integer index of the selected filter. The first filter is index 1.
+ ///
public int FilterIndex { get => _fileDialog.FilterIndex; set => _fileDialog.FilterIndex = value; }
+
+ ///
+ /// Gets or sets the initial directory displayed by the file dialog.
+ ///
+ ///
+ /// A representing the path to the initial directory.
+ ///
public string InitialDirectory { get => _fileDialog.InitialDirectory; set => _fileDialog.InitialDirectory = value; }
+
+ ///
+ /// Gets or sets a value indicating whether the dialog restores the directory to the previously selected directory before closing.
+ ///
+ ///
+ /// if the dialog restores the current directory to its original value; otherwise, .
+ ///
public bool RestoreDirectory { get => _fileDialog.RestoreDirectory; set => _fileDialog.RestoreDirectory = value; }
+
+ ///
+ /// Gets or sets a value indicating whether the dialog automatically adds an extension to a file name if the user omits the extension.
+ ///
+ ///
+ /// if the dialog adds an extension; otherwise, .
+ ///
public bool AddExtension { get => _fileDialog.AddExtension; set => _fileDialog.AddExtension = value; }
+
+ ///
+ /// Gets or sets a value indicating whether the dialog checks if the specified file exists.
+ ///
+ ///
+ /// if the dialog checks that the file exists; otherwise, .
+ ///
public bool CheckFileExists { get => _fileDialog.CheckFileExists; set => _fileDialog.CheckFileExists = value; }
+
+ ///
+ /// Gets or sets the default file name extension.
+ ///
+ ///
+ /// A representing the default extension (without the leading dot).
+ ///
public string DefaultExt { get => _fileDialog.DefaultExt; set => _fileDialog.DefaultExt = value; }
+
+ ///
+ /// Gets or sets the title of the file dialog.
+ ///
+ ///
+ /// A containing the text displayed in the title bar of the dialog.
+ ///
public string Title { get => _fileDialog.Title; set => _fileDialog.Title = value; }
+ ///
+ /// Displays the file dialog.
+ ///
+ ///
+ /// if the user clicks OK; if the user clicks Cancel; otherwise .
+ ///
public bool? ShowDialog()
{
return _fileDialog.ShowDialog();
}
+ ///
+ /// Displays the file dialog with the specified owner.
+ ///
+ /// The window that owns this dialog. It is expected to be a .
+ ///
+ /// if the user clicks OK; if the user clicks Cancel; otherwise .
+ ///
public bool? ShowDialog(object owner)
{
return _fileDialog.ShowDialog(owner as System.Windows.Window);
}
-
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/CommonDialogs/Interfaces/IOpenFileDialog.cs b/CSharpBible/Libraries/CommonDialogs/Interfaces/IOpenFileDialog.cs
index df553a743..69c87af40 100644
--- a/CSharpBible/Libraries/CommonDialogs/Interfaces/IOpenFileDialog.cs
+++ b/CSharpBible/Libraries/CommonDialogs/Interfaces/IOpenFileDialog.cs
@@ -2,13 +2,9 @@
public interface IOpenFileDialog: IFileDialog
{
- string Filter { get; set; }
- string InitialDirectory { get; set; }
- string Title { get; set; }
bool Multiselect { get; set; }
string[] FileNames { get; }
string SafeFileName { get; }
string[] SafeFileNames { get; }
- string DefaultExt { get; set; }
string FileNameExtension { get; }
}
diff --git a/CSharpBible/Libraries/CommonDialogs/OpenFileDialogProxy.cs b/CSharpBible/Libraries/CommonDialogs/OpenFileDialogProxy.cs
index a5fed2a5e..811ffe148 100644
--- a/CSharpBible/Libraries/CommonDialogs/OpenFileDialogProxy.cs
+++ b/CSharpBible/Libraries/CommonDialogs/OpenFileDialogProxy.cs
@@ -3,48 +3,73 @@
namespace CommonDialogs;
+///
+/// Represents a proxy class for the .
+///
+/// This class wraps the standard WPF to implement the interface,
+/// allowing for dependency injection and easier unit testing of view models that require file opening functionality.
+///
+///
+///
+///
public class OpenFileDialogProxy : FileDialogProxy, IOpenFileDialog
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// This constructor creates a new underlying instance.
+ ///
+ ///
public OpenFileDialogProxy()
: base(new OpenFileDialog())
{
}
- public string Filter
- {
- get => This.Filter;
- set => This.Filter = value;
- }
-
- public string InitialDirectory
- {
- get => This.InitialDirectory;
- set => This.InitialDirectory = value;
- }
-
- public string Title
- {
- get => This.Title;
- set => This.Title = value;
- }
-
+ ///
+ /// Gets or sets a value indicating whether the dialog box allows multiple files to be selected.
+ ///
+ ///
+ /// true if the dialog box allows multiple files to be selected together or concurrently; otherwise, false.
+ /// The default value is false.
+ ///
public bool Multiselect
{
get => This.Multiselect;
set => This.Multiselect = value;
}
+ ///
+ /// Gets an array that contains one file name for each selected file.
+ ///
+ ///
+ /// An array of containing the full paths of the selected files.
+ /// If only one file is selected, the array contains a single element.
+ ///
public string[] FileNames => This.FileNames;
+ ///
+ /// Gets the file name and extension for the selected file. The file name does not include the path.
+ ///
+ ///
+ /// A containing the name and extension of the selected file.
+ /// The file name does not include the path. If no file is selected, an empty string is returned.
+ ///
public string SafeFileName => This.SafeFileName;
+ ///
+ /// Gets an array that contains one safe file name for each selected file.
+ ///
+ ///
+ /// An array of containing the names and extensions of the selected files.
+ /// The file names do not include the path.
+ ///
public string[] SafeFileNames => This.SafeFileNames;
- public string DefaultExt
- {
- get => This.DefaultExt;
- set => This.DefaultExt = value;
- }
-
+ ///
+ /// Gets the default file name extension.
+ ///
+ ///
+ /// The default file name extension. The returned string includes the leading period (.).
+ ///
public string FileNameExtension => This.DefaultExt;
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/CommonDialogs/SaveAsFileDialogProxy.cs b/CSharpBible/Libraries/CommonDialogs/SaveAsFileDialogProxy.cs
index 1517fe476..74a6af7b6 100644
--- a/CSharpBible/Libraries/CommonDialogs/SaveAsFileDialogProxy.cs
+++ b/CSharpBible/Libraries/CommonDialogs/SaveAsFileDialogProxy.cs
@@ -3,58 +3,48 @@
namespace CommonDialogs;
-public class SaveFileDialogProxy : FileDialogProxy, IFileDialog
+///
+/// Represents a proxy for the class.
+///
+/// This class encapsulates the functionality of the standard WPF
+/// and adapts it to the interface, facilitating dependency injection
+/// and testing within an MVVM architecture.
+///
+///
+///
+///
+public class SaveFileDialogProxy : FileDialogProxy, IFileDialog
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// This constructor instantiates the underlying which is used
+ /// to perform the actual dialog operations.
+ ///
public SaveFileDialogProxy()
: base(new SaveFileDialog())
{
}
- public string Filter
- {
- get => This.Filter;
- set => This.Filter = value;
- }
-
- public int FilterIndex
- {
- get => This.FilterIndex;
- set => This.FilterIndex = value;
- }
-
- public string InitialDirectory
- {
- get => This.InitialDirectory;
- set => This.InitialDirectory = value;
- }
-
- public string Title
- {
- get => This.Title;
- set => This.Title = value;
- }
-
- public string DefaultExt
- {
- get => This.DefaultExt;
- set => This.DefaultExt = value;
- }
-
+ ///
+ /// Gets or sets a value indicating whether the Save As dialog displays a warning if the user specifies a file name that already exists.
+ ///
+ ///
+ /// true if the dialog should prompt the user before overwriting an existing file; otherwise, false.
+ /// The default value is true.
+ ///
public bool OverwritePrompt
{
- get => This.OverwritePrompt;
- set => This.OverwritePrompt = value;
+ get => ((SaveFileDialog)This).OverwritePrompt;
+ set => ((SaveFileDialog)This).OverwritePrompt = value;
}
- public bool AddExtension
- {
- get => This.AddExtension;
- set => This.AddExtension = value;
- }
- public bool RestoreDirectory
- {
- get => This.RestoreDirectory;
- set => This.RestoreDirectory = value;
- }
- public string FileNameExtension => This.DefaultExt;
+ ///
+ /// Gets the default file name extension.
+ ///
+ ///
+ /// The default file name extension string. The returned string does not include the period (.).
+ ///
+ public string FileNameExtension => ((SaveFileDialog)This).DefaultExt;
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/CommonDialogs/TaskDialog.cs b/CSharpBible/Libraries/CommonDialogs/TaskDialog.cs
new file mode 100644
index 000000000..20649ef16
--- /dev/null
+++ b/CSharpBible/Libraries/CommonDialogs/TaskDialog.cs
@@ -0,0 +1,234 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CommonDialogs;
+
+public class TaskDialog : Component
+{
+ private Microsoft.WindowsAPICodePack.Dialogs.TaskDialog _td = new Microsoft.WindowsAPICodePack.Dialogs.TaskDialog();
+
+ //
+ // Zusammenfassung:
+ // Creates a basic TaskDialog window
+ public TaskDialog()
+ {
+
+ }
+
+ //
+ // Zusammenfassung:
+ // TaskDialog Finalizer
+ ~TaskDialog()
+ {
+ _td.Dispose();
+ }
+
+ //
+ // Zusammenfassung:
+ // Indicates whether this feature is supported on the current platform.
+ public static bool IsPlatformSupported { get => Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.IsPlatformSupported; }
+ /*
+ // Zusammenfassung:
+ // Gets or sets the progress bar on the taskdialog. ProgressBar a visual representation
+ // of the progress of a long running operation.
+ public TaskDialogProgressBar ProgressBar { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the startup location.
+ public TaskDialogStartupLocation StartupLocation { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the expansion mode for this dialog.
+ public TaskDialogExpandedDetailsLocation ExpansionMode { get; set; }
+ /*/
+ // Zusammenfassung:
+ // Gets or sets a value that indicates if the footer checkbox is checked.
+ public bool? FooterCheckBoxChecked { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that determines if hyperlinks are enabled.
+ public bool HyperlinksEnabled { get; set; }
+ /*
+ // Zusammenfassung:
+ // Gets a value that contains the TaskDialog controls.
+ public DialogControlCollection Controls { get; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the standard buttons.
+ public TaskDialogStandardButtons StandardButtons { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the footer icon.
+ public TaskDialogStandardIcon FooterIcon { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the TaskDialog main icon.
+ public TaskDialogStandardIcon Icon { get; set; }
+ /*/
+ // Zusammenfassung:
+ // Gets or sets a value that determines if Cancelable is set.
+ public bool Cancelable { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the collapsed control text.
+ public string DetailsCollapsedLabel { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the expanded control text.
+ public string DetailsExpandedLabel { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the expanded text in the details section.
+ public string DetailsExpandedText { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that determines if the details section is expanded.
+ public bool DetailsExpanded { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the footer text.
+ public string FooterText { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the caption text.
+ public string Caption { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the instruction text.
+ public string InstructionText { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the message text.
+ public string Text { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the owner window's handle.
+ public IntPtr OwnerWindowHandle { get; set; }
+ //
+ // Zusammenfassung:
+ // Gets or sets a value that contains the footer check box text.
+ public string FooterCheckBoxText { get; set; }
+
+
+ //
+ // Zusammenfassung:
+ // Occurs when a user clicks on Help.
+ public event EventHandler HelpInvoked { add => _td.HelpInvoked += value; remove => _td.HelpInvoked -= value; }
+ //
+ // Zusammenfassung:
+ // Occurs when the TaskDialog is closing.
+ public event EventHandler Closing;
+ //
+ // Zusammenfassung:
+ // Occurs when a user clicks a hyperlink.
+ public event EventHandler HyperlinkClick;
+ //
+ // Zusammenfassung:
+ // Occurs when a progress bar changes.
+ public event EventHandler Tick;
+ //
+ // Zusammenfassung:
+ // Occurs when the TaskDialog is opened.
+ public event EventHandler Opened { add=> _td.Opened+=value; remove => _td.Opened -= value; }
+
+ //
+ // Zusammenfassung:
+ // Creates and shows a task dialog with the specified supporting text, main instruction,
+ // and dialog caption.
+ //
+ // Parameter:
+ // text:
+ // The supporting text to display.
+ //
+ // instructionText:
+ // The main instruction text to display.
+ //
+ // caption:
+ // The caption for the dialog.
+ //
+ // Rückgabewerte:
+ // The dialog result.
+ public static bool Show(string text, string instructionText, string caption)
+ => Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.Show(text,instructionText,caption) == Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok;
+ //
+ // Zusammenfassung:
+ // Creates and shows a task dialog with the specified supporting text and main instruction.
+ //
+ // Parameter:
+ // text:
+ // The supporting text to display.
+ //
+ // instructionText:
+ // The main instruction text to display.
+ //
+ // Rückgabewerte:
+ // The dialog result.
+ public static bool Show(string text, string instructionText)
+ => Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.Show(text,instructionText)==Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok;
+ //
+ // Zusammenfassung:
+ // Creates and shows a task dialog with the specified message text.
+ //
+ // Parameter:
+ // text:
+ // The text to display.
+ //
+ // Rückgabewerte:
+ // The dialog result.
+ public static bool Show(string text)
+ => Microsoft.WindowsAPICodePack.Dialogs.TaskDialog.Show(text)==Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok;
+ //
+ // Zusammenfassung:
+ // Close TaskDialog with a given TaskDialogResult
+ //
+ // Parameter:
+ // closingResult:
+ // TaskDialogResult to return from the TaskDialog.Show() method
+ //
+ // Ausnahmen:
+ // T:System.InvalidOperationException:
+ // if TaskDialog is not showing.
+ public void Close(bool result)=>_td.Close(result ? Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok:
+ Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Cancel);
+ //
+ // Zusammenfassung:
+ // Close TaskDialog
+ //
+ // Ausnahmen:
+ // T:System.InvalidOperationException:
+ // if TaskDialog is not showing.
+ public void Close()=>_td.Close();
+ //
+ // Zusammenfassung:
+ // Dispose TaskDialog Resources
+#if NET50_OR_GREATER
+ public override void Dispose()=>_td.Dispose();
+#else
+ public new void Dispose()=>_td.Dispose();
+#endif
+ //
+ // Zusammenfassung:
+ // Dispose TaskDialog Resources
+ //
+ // Parameter:
+ // disposing:
+ // If true, indicates that this is being called via Dispose rather than via the
+ // finalizer.
+#if NET50_OR_GREATER
+ public override void Dispose(bool disposing) => _td.Dispose(disposing);
+#else
+ public new void Dispose(bool disposing) => _td.Dispose(disposing);
+#endif
+ //
+ // Zusammenfassung:
+ // Creates and shows a task dialog.
+ //
+ // Rückgabewerte:
+ // The dialog result.
+ public bool? Show()=> _td.Show()==Microsoft.WindowsAPICodePack.Dialogs.TaskDialogResult.Ok;
+
+}
diff --git a/CSharpBible/Libraries/ConsoleDisplay/ConsoleDisplay.csproj b/CSharpBible/Libraries/ConsoleDisplay/ConsoleDisplay.csproj
index efdb0f4ae..df8500c00 100644
--- a/CSharpBible/Libraries/ConsoleDisplay/ConsoleDisplay.csproj
+++ b/CSharpBible/Libraries/ConsoleDisplay/ConsoleDisplay.csproj
@@ -15,7 +15,6 @@
-
diff --git a/CSharpBible/Libraries/ConsoleDisplay/View/Display.cs b/CSharpBible/Libraries/ConsoleDisplay/View/Display.cs
index 8ab238cce..b1dddd2d1 100644
--- a/CSharpBible/Libraries/ConsoleDisplay/View/Display.cs
+++ b/CSharpBible/Libraries/ConsoleDisplay/View/Display.cs
@@ -16,204 +16,202 @@
using System;
using System.Drawing;
-namespace ConsoleDisplay.View
+namespace ConsoleDisplay.View;
+
+
+///
+/// Class Display.
+/// To use the console as an graphic display, Graphic is done with half-blocks and setting the fore- and background color.
+///
+public class Display
{
+ ///
+ /// The color map
+ ///
+ static ConsoleColor[]? colorMap = null;
+ ///
+ /// My console
+ ///
+ static public IConsole myConsole = new ConsoleProxy();
///
- /// Class Display.
- /// To use the console as an graphic display, Graphic is done with half-blocks and setting the fore- and background color.
+ /// The h block
+ ///
+ const char hBlock = '▄';
+ ///
+ /// Initializes a new instance of the class.
///
- public class Display
+ /// The x.
+ /// The y.
+ /// The width.
+ /// The height.
+ public Display(int x, int y, int width, int height)
{
- ///
- /// The color map
- ///
- static ConsoleColor[]? colorMap = null;
- ///
- /// My console
- ///
- static public IConsole myConsole = new ConsoleProxy();
-
- ///
- /// The h block
- ///
- const char hBlock = '▄';
- ///
- /// Initializes a new instance of the class.
- ///
- /// The x.
- /// The y.
- /// The width.
- /// The height.
- public Display(int x, int y, int width, int height)
+ Origin = new Point(x, y);
+ dSize = new Size(width, height);
+ ScreenBuffer = new ConsoleColor[dSize.Width * dSize.Height];
+ OutBuffer = new ConsoleColor[dSize.Width * dSize.Height];
+
+ if (colorMap == null)
{
- Origin = new Point(x, y);
- dSize = new Size(width, height);
- ScreenBuffer = new ConsoleColor[dSize.Width * dSize.Height];
- OutBuffer = new ConsoleColor[dSize.Width * dSize.Height];
+ colorMap = new ConsoleColor[64];
- if (colorMap == null)
+ for (var i = 63; i > 0; i--)
{
- colorMap = new ConsoleColor[64];
-
- for (var i = 63; i > 0; i--)
+ var cdm = 0;
+ for (var j = 0; j < 16; j++)
{
- var cdm = 0;
- for (var j = 0; j < 16; j++)
- {
- var cd = Math.Abs(i % 4 - (j == 8 ? 1 : ((j % 8) / 4) * ((j / 8) + 2)))
- + Math.Abs((i / 4) % 4 - (j == 8 ? 1 : (j % 4) / 2 * ((j / 8) + 2)))
- + Math.Abs((i / 16) - (j == 8 ? 1 : (j % 2) * ((j / 8) + 2)));
- if (j == 0 || cd <= cdm)
- {
- colorMap[i] = (ConsoleColor)j;
- cdm = cd;
- }
+ var cd = Math.Abs(i % 4 - (j == 8 ? 1 : ((j % 8) / 4) * ((j / 8) + 2)))
+ + Math.Abs((i / 4) % 4 - (j == 8 ? 1 : (j % 4) / 2 * ((j / 8) + 2)))
+ + Math.Abs((i / 16) - (j == 8 ? 1 : (j % 2) * ((j / 8) + 2)));
+ if (j == 0 || cd <= cdm)
+ {
+ colorMap[i] = (ConsoleColor)j;
+ cdm = cd;
}
}
-
}
+
}
+ }
- ///
- /// Gets the origin of the display.
- ///
- /// The origin.
- public Point Origin { get; private set; }
+ ///
+ /// Gets the origin of the display.
+ ///
+ /// The origin.
+ public Point Origin { get; private set; }
- ///
- /// Clears this instance of the display.
- ///
- public void Clear()
- {
- for (var i = 0; i < ScreenBuffer.Length; i++)
- ScreenBuffer[i] = ConsoleColor.Black;
- }
+ ///
+ /// Clears this instance of the display.
+ ///
+ public void Clear()
+ {
+ for (var i = 0; i < ScreenBuffer.Length; i++)
+ ScreenBuffer[i] = ConsoleColor.Black;
+ }
- ///
- /// Gets the size of the display.
- ///
- /// The size of the d.
- public Size dSize { get; private set; }
-
- ///
- /// Gets the screen buffer.
- ///
- /// The screen buffer.
- public ConsoleColor[] ScreenBuffer { get; private set; }
- ///
- /// Gets or sets the out buffer.
- ///
- /// The out buffer.
- private ConsoleColor[] OutBuffer { get; set; }
- ///
- /// Gets or sets the default color of the outside.
- ///
- /// The default color of the outside.
- public ConsoleColor DefaultOutsideColor { get; set; } = ConsoleColor.Black;
-
- ///
- /// Updates this instance of the display.
- ///
- public void Update()
- {
- var _fgr = myConsole.ForegroundColor;
- var _bgr = myConsole.BackgroundColor;
- for (int y = 0; y < dSize.Height / 2 * 2; y += 2)
- for (int x = 0; x < dSize.Width; x++)
- if (ScreenBuffer[y * dSize.Width + x] != OutBuffer[y * dSize.Width + x] ||
- ScreenBuffer[(y + 1) * dSize.Width + x] != OutBuffer[(y + 1) * dSize.Width + x])
- {
- myConsole.BackgroundColor = ScreenBuffer[(y + 0) * dSize.Width + x];
- myConsole.ForegroundColor = ScreenBuffer[(y + 1) * dSize.Width + x];
- myConsole.SetCursorPosition(Origin.X + x, Origin.Y + y / 2);
- myConsole.Write(hBlock);
- OutBuffer[y * dSize.Width + x] = ScreenBuffer[y * dSize.Width + x];
- OutBuffer[(y + 1) * dSize.Width + x] = ScreenBuffer[(y + 1) * dSize.Width + x];
- }
- if (dSize.Height % 2 == 1)
- {
- var y = (dSize.Height - 1);
- for (int x = 0; x < dSize.Width; x++)
- if (ScreenBuffer[y * dSize.Width + x] != OutBuffer[y * dSize.Width + x])
- {
- myConsole.BackgroundColor = ScreenBuffer[(y + 0) * dSize.Width + x];
- myConsole.ForegroundColor = ConsoleColor.Black;
- myConsole.SetCursorPosition(Origin.X + x, Origin.Y + y / 2);
- myConsole.Write(hBlock);
- OutBuffer[y * dSize.Width + x] = ScreenBuffer[y * dSize.Width + x];
- }
- }
- myConsole.ForegroundColor = _fgr;
- myConsole.BackgroundColor = _bgr;
- }
+ ///
+ /// Gets the size of the display.
+ ///
+ /// The size of the d.
+ public Size dSize { get; private set; }
- ///
- /// Puts the pixel.
- ///
- /// The x.
- /// The y.
- /// The r.
- /// The g.
- /// The b.
- public void PutPixel(int x, int y, byte r, byte g, byte b) =>
- PutPixel(x, y, colorMap?[r / 64 + (g / 64) * 4 + (b / 64) * 16] ?? ConsoleColor.Black);
-
- ///
- /// Puts the pixel on the display.
- ///
- /// The x.
- /// The y.
- /// The c.
- public void PutPixel(int x, int y, ConsoleColor c)
- {
- if (x >= 0 && x < dSize.Width && y >= 0 && y < dSize.Height)
- ScreenBuffer[x + y * dSize.Width] = c;
- }
+ ///
+ /// Gets the screen buffer.
+ ///
+ /// The screen buffer.
+ public ConsoleColor[] ScreenBuffer { get; private set; }
+ ///
+ /// Gets or sets the out buffer.
+ ///
+ /// The out buffer.
+ private ConsoleColor[] OutBuffer { get; set; }
+ ///
+ /// Gets or sets the default color of the outside.
+ ///
+ /// The default color of the outside.
+ public ConsoleColor DefaultOutsideColor { get; set; } = ConsoleColor.Black;
- ///
- /// Puts the line on the display.
- ///
- /// The x1.
- /// The y1.
- /// The x2.
- /// The y2.
- /// The c.
- public void PutLine(int x1, int y1, int x2, int y2, ConsoleColor c)
+ ///
+ /// Updates this instance of the display.
+ ///
+ public void Update()
+ {
+ var _fgr = myConsole.ForegroundColor;
+ var _bgr = myConsole.BackgroundColor;
+ for (int y = 0; y < dSize.Height / 2 * 2; y += 2)
+ for (int x = 0; x < dSize.Width; x++)
+ if (ScreenBuffer[y * dSize.Width + x] != OutBuffer[y * dSize.Width + x] ||
+ ScreenBuffer[(y + 1) * dSize.Width + x] != OutBuffer[(y + 1) * dSize.Width + x])
+ {
+ myConsole.BackgroundColor = ScreenBuffer[(y + 0) * dSize.Width + x];
+ myConsole.ForegroundColor = ScreenBuffer[(y + 1) * dSize.Width + x];
+ myConsole.SetCursorPosition(Origin.X + x, Origin.Y + y / 2);
+ myConsole.Write(hBlock);
+ OutBuffer[y * dSize.Width + x] = ScreenBuffer[y * dSize.Width + x];
+ OutBuffer[(y + 1) * dSize.Width + x] = ScreenBuffer[(y + 1) * dSize.Width + x];
+ }
+ if (dSize.Height % 2 == 1)
{
- var mx = Math.Max(Math.Abs(x1 - x2), Math.Abs(y1 - y2));
- for (var i = 0; i <= mx; i++)
- PutPixel(x1 + (x2 - x1) * i / mx, y1 + (y2 - y1) * i / mx, c);
+ var y = (dSize.Height - 1);
+ for (int x = 0; x < dSize.Width; x++)
+ if (ScreenBuffer[y * dSize.Width + x] != OutBuffer[y * dSize.Width + x])
+ {
+ myConsole.BackgroundColor = ScreenBuffer[(y + 0) * dSize.Width + x];
+ myConsole.ForegroundColor = ConsoleColor.Black;
+ myConsole.SetCursorPosition(Origin.X + x, Origin.Y + y / 2);
+ myConsole.Write(hBlock);
+ OutBuffer[y * dSize.Width + x] = ScreenBuffer[y * dSize.Width + x];
+ }
}
+ myConsole.ForegroundColor = _fgr;
+ myConsole.BackgroundColor = _bgr;
+ }
- ///
- /// Returns a that represents this instance.
- ///
- /// A that represents this instance.
- public override string ToString()
- {
- string result = this.GetType().ToString();
- result += ".";
- result += $"({dSize.Width};{dSize.Height}),";
- for (var i = 0; i < ScreenBuffer.Length; i++)
- result += $"{(byte)ScreenBuffer[i]:X}";
- return result;
- }
+ ///
+ /// Puts the pixel.
+ ///
+ /// The x.
+ /// The y.
+ /// The r.
+ /// The g.
+ /// The b.
+ public void PutPixel(int x, int y, byte r, byte g, byte b) =>
+ PutPixel(x, y, colorMap?[r / 64 + (g / 64) * 4 + (b / 64) * 16] ?? ConsoleColor.Black);
- ///
- /// Gets the pixel.
- ///
- /// The x.
- /// The y.
- /// ConsoleColor.
- public ConsoleColor GetPixel(int x, int y)
- {
- if (x >= 0 && x < dSize.Width && y >= 0 && y < dSize.Height)
- return ScreenBuffer[x + y * dSize.Width];
- else
- return DefaultOutsideColor;
- }
+ ///
+ /// Puts the pixel on the display.
+ ///
+ /// The x.
+ /// The y.
+ /// The c.
+ public void PutPixel(int x, int y, ConsoleColor c)
+ {
+ if (x >= 0 && x < dSize.Width && y >= 0 && y < dSize.Height)
+ ScreenBuffer[x + y * dSize.Width] = c;
+ }
+
+ ///
+ /// Puts the line on the display.
+ ///
+ /// The x1.
+ /// The y1.
+ /// The x2.
+ /// The y2.
+ /// The c.
+ public void PutLine(int x1, int y1, int x2, int y2, ConsoleColor c)
+ {
+ var mx = Math.Max(Math.Abs(x1 - x2), Math.Abs(y1 - y2));
+ for (var i = 0; i <= mx; i++)
+ PutPixel(x1 + (x2 - x1) * i / mx, y1 + (y2 - y1) * i / mx, c);
}
+ ///
+ /// Returns a that represents this instance.
+ ///
+ /// A that represents this instance.
+ public override string ToString()
+ {
+ string result = this.GetType().ToString();
+ result += ".";
+ result += $"({dSize.Width};{dSize.Height}),";
+ for (var i = 0; i < ScreenBuffer.Length; i++)
+ result += $"{(byte)ScreenBuffer[i]:X}";
+ return result;
+ }
+
+ ///
+ /// Gets the pixel.
+ ///
+ /// The x.
+ /// The y.
+ /// ConsoleColor.
+ public ConsoleColor GetPixel(int x, int y)
+ {
+ if (x >= 0 && x < dSize.Width && y >= 0 && y < dSize.Height)
+ return ScreenBuffer[x + y * dSize.Width];
+ else
+ return DefaultOutsideColor;
+ }
}
diff --git a/CSharpBible/Libraries/ConsoleDisplay/View/MyConsole.cs b/CSharpBible/Libraries/ConsoleDisplay/View/MyConsole.cs
index f95d6d52a..95054f561 100644
--- a/CSharpBible/Libraries/ConsoleDisplay/View/MyConsole.cs
+++ b/CSharpBible/Libraries/ConsoleDisplay/View/MyConsole.cs
@@ -16,252 +16,252 @@
using System.Linq;
using System.Reflection;
-namespace ConsoleDisplay.View
-{
+namespace ConsoleDisplay.View;
+
+///
+/// Class MyConsole.
+/// Implements the
+///
+///
+[Obsolete("Use BaseLib.ConsoleProxy")]
+public class MyConsole : IConsole
+{
///
- /// Class MyConsole.
- /// Implements the
+ /// Gets or sets the color of the foreground.
///
- ///
- public class MyConsole : IConsole
- {
- ///
- /// Gets or sets the color of the foreground.
- ///
- /// The color of the foreground.
- protected PropertyInfo? foregroundColor { get; set; }
- = typeof(Console).GetProperty(nameof(ForegroundColor));
- ///
- /// Gets or sets the color of the background.
- ///
- /// The color of the background.
- protected PropertyInfo? backgroundColor { get; set; }
- = typeof(Console).GetProperty(nameof(BackgroundColor));
- ///
- /// Gets or sets the key available.
- ///
- /// The key available.
- protected PropertyInfo? keyAvailable { get; set; }
- = typeof(Console).GetProperty(nameof(KeyAvailable));
- ///
- /// Gets or sets the height of the window.
- ///
- /// The height of the window.
- protected PropertyInfo? windowHeight { get; set; }
- = typeof(Console).GetProperty(nameof(WindowHeight));
- ///
- /// Gets or sets the width of the window.
- ///
- /// The width of the window.
- protected PropertyInfo? windowWidth { get; set; }
- = typeof(Console).GetProperty(nameof(WindowWidth));
- ///
- /// Gets or sets the height of the largest window.
- ///
- /// The height of the largest window.
- protected PropertyInfo? largestWindowHeight { get; set; }
- = typeof(Console).GetProperty(nameof(LargestWindowHeight));
- ///
- /// Gets if the Output is redirected.
- ///
- /// TRUE if the Output is redirected
- protected PropertyInfo? isOutputRedirected { get; set; }
- = typeof(Console).GetProperty(nameof(IsOutputRedirected));
+ /// The color of the foreground.
+ protected PropertyInfo? foregroundColor { get; set; }
+ = typeof(Console).GetProperty(nameof(ForegroundColor));
+ ///
+ /// Gets or sets the color of the background.
+ ///
+ /// The color of the background.
+ protected PropertyInfo? backgroundColor { get; set; }
+ = typeof(Console).GetProperty(nameof(BackgroundColor));
+ ///
+ /// Gets or sets the key available.
+ ///
+ /// The key available.
+ protected PropertyInfo? keyAvailable { get; set; }
+ = typeof(Console).GetProperty(nameof(KeyAvailable));
+ ///
+ /// Gets or sets the height of the window.
+ ///
+ /// The height of the window.
+ protected PropertyInfo? windowHeight { get; set; }
+ = typeof(Console).GetProperty(nameof(WindowHeight));
+ ///
+ /// Gets or sets the width of the window.
+ ///
+ /// The width of the window.
+ protected PropertyInfo? windowWidth { get; set; }
+ = typeof(Console).GetProperty(nameof(WindowWidth));
+ ///
+ /// Gets or sets the height of the largest window.
+ ///
+ /// The height of the largest window.
+ protected PropertyInfo? largestWindowHeight { get; set; }
+ = typeof(Console).GetProperty(nameof(LargestWindowHeight));
+ ///
+ /// Gets if the Output is redirected.
+ ///
+ /// TRUE if the Output is redirected
+ protected PropertyInfo? isOutputRedirected { get; set; }
+ = typeof(Console).GetProperty(nameof(IsOutputRedirected));
- ///
- /// Gets or sets the title of the window.
- ///
- /// The height of the largest window.
- protected PropertyInfo? title { get; set; }
- = typeof(Console).GetProperty(nameof(Title));
+ ///
+ /// Gets or sets the title of the window.
+ ///
+ /// The height of the largest window.
+ protected PropertyInfo? title { get; set; }
+ = typeof(Console).GetProperty(nameof(Title));
- ///
- /// Gets or sets the clear.
- ///
- /// The clear.
- protected MethodInfo? clear { get; set; }
- = typeof(Console).GetMember(nameof(Clear))?.First(
- (o) => true) as MethodInfo;
- ///
- /// Gets or sets the read key.
- ///
- /// The read key.
- protected MethodInfo? readKey { get; set; }
- = typeof(Console).GetMember(nameof(ReadKey))?.First(
- (o) => true) as MethodInfo;
- ///
- /// Gets or sets the write ch.
- ///
- /// The write ch.
- protected MethodInfo? write_ch { get; set; }
- = typeof(Console).GetMember(nameof(Console.Write))?.First(
- (o) => (o as MethodInfo)?.GetParameters()?[0].ParameterType==typeof(char) ) as MethodInfo;
- ///
- /// Gets or sets the write st.
- ///
- /// The write st.
- protected MethodInfo? write_st { get; set; }
- = typeof(Console).GetMember(nameof(Console.Write))?.First(
- (o) => o is MethodInfo m
- && m.GetParameters().Length==1
- && m.GetParameters()?[0].ParameterType == typeof(string)) as MethodInfo;
- ///
- /// Gets or sets the write st.
- ///
- /// The write st.
- protected MethodInfo? read_st { get; set; }
- = typeof(Console).GetMember(nameof(Console.ReadLine))?.First(
- (o) => o is MethodInfo m
- && (m.GetParameters().Length == 0)
- && m.ReturnType == typeof(string)) as MethodInfo;
+ ///
+ /// Gets or sets the clear.
+ ///
+ /// The clear.
+ protected MethodInfo? clear { get; set; }
+ = typeof(Console).GetMember(nameof(Clear))?.First(
+ (o) => true) as MethodInfo;
+ ///
+ /// Gets or sets the read key.
+ ///
+ /// The read key.
+ protected MethodInfo? readKey { get; set; }
+ = typeof(Console).GetMember(nameof(ReadKey))?.First(
+ (o) => true) as MethodInfo;
+ ///
+ /// Gets or sets the write ch.
+ ///
+ /// The write ch.
+ protected MethodInfo? write_ch { get; set; }
+ = typeof(Console).GetMember(nameof(Console.Write))?.First(
+ (o) => (o as MethodInfo)?.GetParameters()?[0].ParameterType==typeof(char) ) as MethodInfo;
+ ///
+ /// Gets or sets the write st.
+ ///
+ /// The write st.
+ protected MethodInfo? write_st { get; set; }
+ = typeof(Console).GetMember(nameof(Console.Write))?.First(
+ (o) => o is MethodInfo m
+ && m.GetParameters().Length==1
+ && m.GetParameters()?[0].ParameterType == typeof(string)) as MethodInfo;
+ ///
+ /// Gets or sets the write st.
+ ///
+ /// The write st.
+ protected MethodInfo? read_st { get; set; }
+ = typeof(Console).GetMember(nameof(Console.ReadLine))?.First(
+ (o) => o is MethodInfo m
+ && (m.GetParameters().Length == 0)
+ && m.ReturnType == typeof(string)) as MethodInfo;
- ///
- /// Gets or sets the set cursor position.
- ///
- /// The set cursor position.
- protected MethodInfo? setCursorPos { get; set; }
- = typeof(Console).GetMember(nameof(SetCursorPosition))?.First((o) => true) as MethodInfo;
+ ///
+ /// Gets or sets the set cursor position.
+ ///
+ /// The set cursor position.
+ protected MethodInfo? setCursorPos { get; set; }
+ = typeof(Console).GetMember(nameof(SetCursorPosition))?.First((o) => true) as MethodInfo;
- ///
- /// Gets or sets the beep int.
- ///
- /// The beep int.
- protected MethodInfo? beep_int { get; set; }
- = typeof(Console).GetMember(nameof(Beep))?.First(
- (o) => (o as MethodInfo)?.GetParameters().Length == 2) as MethodInfo;
- ///
- /// Gets or sets the get cursor position.
- ///
- /// The get cursor position.
- protected MethodInfo? getCursorPos { get; set; }
+ ///
+ /// Gets or sets the beep int.
+ ///
+ /// The beep int.
+ protected MethodInfo? beep_int { get; set; }
+ = typeof(Console).GetMember(nameof(Beep))?.First(
+ (o) => (o as MethodInfo)?.GetParameters().Length == 2) as MethodInfo;
+ ///
+ /// Gets or sets the get cursor position.
+ ///
+ /// The get cursor position.
+ protected MethodInfo? getCursorPos { get; set; }
#if NET6_0_OR_GREATER
= typeof(Console).GetMember(nameof(Console.GetCursorPosition))?.First((o) => true) as MethodInfo;
#else
= typeof(MyConsole).GetMethod(nameof(_GetCursorPosition));
- public static (int Left, int Top) _GetCursorPosition() => (Console.CursorLeft, Console.CursorTop);
+ public static (int Left, int Top) _GetCursorPosition() => (Console.CursorLeft, Console.CursorTop);
#endif
- ///
- /// The instance
- ///
- protected object? instance = null;
+ ///
+ /// The instance
+ ///
+ protected object? instance = null;
- ///
- /// Gets or sets the color of the foreground.
- ///
- /// The color of the foreground.
- public override ConsoleColor ForegroundColor
- {
- get => (ConsoleColor)(foregroundColor?.GetValue(instance) ?? ConsoleColor.Gray);
- set => foregroundColor?.SetValue(instance, value);
- }
+ ///
+ /// Gets or sets the color of the foreground.
+ ///
+ /// The color of the foreground.
+ public ConsoleColor ForegroundColor
+ {
+ get => (ConsoleColor)(foregroundColor?.GetValue(instance) ?? ConsoleColor.Gray);
+ set => foregroundColor?.SetValue(instance, value);
+ }
- ///
- /// Gets or sets the color of the background.
- ///
- /// The color of the background.
- public override ConsoleColor BackgroundColor
- {
- get => (ConsoleColor)(backgroundColor?.GetValue(instance) ?? ConsoleColor.Gray);
- set => backgroundColor?.SetValue(instance, value);
- }
+ ///
+ /// Gets or sets the color of the background.
+ ///
+ /// The color of the background.
+ public ConsoleColor BackgroundColor
+ {
+ get => (ConsoleColor)(backgroundColor?.GetValue(instance) ?? ConsoleColor.Gray);
+ set => backgroundColor?.SetValue(instance, value);
+ }
- ///
- /// Gets or sets the height of the window.
- ///
- /// The height of the window.
- public override int WindowHeight
- {
- get => (int)((instance != null ? windowHeight?.GetValue(instance):null) ?? 0);
- set => windowHeight?.SetValue(instance, value);
- }
+ ///
+ /// Gets or sets the height of the window.
+ ///
+ /// The height of the window.
+ public int WindowHeight
+ {
+ get => (int)((instance != null ? windowHeight?.GetValue(instance):null) ?? 0);
+ set => windowHeight?.SetValue(instance, value);
+ }
- ///
- /// Gets a value indicating whether [key available].
- ///
- /// true if [key available]; otherwise, false.
- public override bool KeyAvailable {
- get => (bool)(keyAvailable?.GetValue(instance) ?? false);
- }
- ///
- /// Gets the height of the largest window.
- ///
- /// The height of the largest window.
- public override int LargestWindowHeight {
- get =>(int) (largestWindowHeight?.GetValue(instance) ?? 0);
- }
- ///
- /// Gets or sets the width of the window.
- ///
- /// The width of the window.
- public override int WindowWidth {
- get => (int)((instance!=null?windowWidth?.GetValue(instance):null) ?? 0);
- set => windowWidth?.SetValue(instance, value); }
+ ///
+ /// Gets a value indicating whether [key available].
+ ///
+ /// true if [key available]; otherwise, false.
+ public bool KeyAvailable {
+ get => (bool)(keyAvailable?.GetValue(instance) ?? false);
+ }
+ ///
+ /// Gets the height of the largest window.
+ ///
+ /// The height of the largest window.
+ public int LargestWindowHeight {
+ get =>(int) (largestWindowHeight?.GetValue(instance) ?? 0);
+ }
+ ///
+ /// Gets or sets the width of the window.
+ ///
+ /// The width of the window.
+ public int WindowWidth {
+ get => (int)((instance!=null?windowWidth?.GetValue(instance):null) ?? 0);
+ set => windowWidth?.SetValue(instance, value); }
- ///
- /// Gets or sets the title of the window.
- ///
- /// The width of the window.
- public override string Title {
- get => (string)(title?.GetValue(instance) ??"");
- set => title?.SetValue(instance, value); }
+ ///
+ /// Gets or sets the title of the window.
+ ///
+ /// The width of the window.
+ public string Title {
+ get => (string)(title?.GetValue(instance) ??"");
+ set => title?.SetValue(instance, value); }
- public override bool IsOutputRedirected
- => (bool)(isOutputRedirected?.GetValue(instance) ?? false);
+ public bool IsOutputRedirected
+ => (bool)(isOutputRedirected?.GetValue(instance) ?? false);
- ///
- /// Clears this instance.
- ///
- public override void Clear() => clear?.Invoke(instance, new object[] { });
- ///
- /// Writes the specified ch.
- ///
- /// The ch.
- public override void Write(char ch) => write_ch?.Invoke(instance, new object[] { ch });
- ///
- /// Writes the specified st.
- ///
- /// The st.
- public override void Write(string? st) => write_st?.Invoke(instance, new object[] { st ?? "" });
- ///
- /// Writes the line.
- ///
- /// The st.
- public override void WriteLine(string? st="") => write_st?.Invoke(instance, new object[] { st+"\r\n" });
+ ///
+ /// Clears this instance.
+ ///
+ public void Clear() => clear?.Invoke(instance, new object[] { });
+ ///
+ /// Writes the specified ch.
+ ///
+ /// The ch.
+ public void Write(char ch) => write_ch?.Invoke(instance, new object[] { ch });
+ ///
+ /// Writes the specified st.
+ ///
+ /// The st.
+ public void Write(string? st) => write_st?.Invoke(instance, new object[] { st ?? "" });
+ ///
+ /// Writes the line.
+ ///
+ /// The st.
+ public void WriteLine(string? st="") => write_st?.Invoke(instance, new object[] { st+"\r\n" });
- ///
- /// Writes the line.
- ///
- /// The st.
- public override string ReadLine() =>
- (string)(read_st?.Invoke(instance, new object[] {}) ?? "");
+ ///
+ /// Writes the line.
+ ///
+ /// The st.
+ public string ReadLine() =>
+ (string)(read_st?.Invoke(instance, new object[] {}) ?? "");
- ///
- /// Sets the cursor position.
- ///
- /// The left.
- /// The top.
- public override void SetCursorPosition(int left, int top) => setCursorPos?.Invoke(instance, new object[] { left, top });
+ ///
+ /// Sets the cursor position.
+ ///
+ /// The left.
+ /// The top.
+ public void SetCursorPosition(int left, int top) => setCursorPos?.Invoke(instance, new object[] { left, top });
- ///
- /// Reads the key.
- ///
- /// System.Nullable<ConsoleKeyInfo>.
- public override ConsoleKeyInfo? ReadKey() => (ConsoleKeyInfo?)readKey?.Invoke(instance, new object[] { });
+ ///
+ /// Reads the key.
+ ///
+ /// System.Nullable<ConsoleKeyInfo>.
+ public ConsoleKeyInfo? ReadKey() => (ConsoleKeyInfo?)readKey?.Invoke(instance, new object[] { });
- ///
- /// Gets the cursor position.
- ///
- /// System.ValueTuple<System.Int32, System.Int32>.
- public override (int Left, int Top) GetCursorPosition() => ((int Left, int Top)?)getCursorPos?.Invoke(instance, new object[] { }) ?? (0,0);
+ ///
+ /// Gets the cursor position.
+ ///
+ /// System.ValueTuple<System.Int32, System.Int32>.
+ public (int Left, int Top) GetCursorPosition() => ((int Left, int Top)?)getCursorPos?.Invoke(instance, new object[] { }) ?? (0,0);
- ///
- /// Beeps the specified freq.
- ///
- /// The freq.
- /// The dur.
- public override void Beep(int freq,int dur) => beep_int?.Invoke(instance, new object[] { freq, dur });
+ ///
+ /// Beeps the specified freq.
+ ///
+ /// The freq.
+ /// The dur.
+ public void Beep(int freq,int dur) => beep_int?.Invoke(instance, new object[] { freq, dur });
- }
}
diff --git a/CSharpBible/Libraries/ConsoleDisplayTests/ConsoleDisplayTests.csproj b/CSharpBible/Libraries/ConsoleDisplayTests/ConsoleDisplayTests.csproj
index 6d2e1461a..f1c3ced99 100644
--- a/CSharpBible/Libraries/ConsoleDisplayTests/ConsoleDisplayTests.csproj
+++ b/CSharpBible/Libraries/ConsoleDisplayTests/ConsoleDisplayTests.csproj
@@ -17,7 +17,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Libraries/ConsoleLib/ConsoleLib.csproj b/CSharpBible/Libraries/ConsoleLib/ConsoleLib.csproj
index 173541e37..35dbf2f99 100644
--- a/CSharpBible/Libraries/ConsoleLib/ConsoleLib.csproj
+++ b/CSharpBible/Libraries/ConsoleLib/ConsoleLib.csproj
@@ -1,11 +1,17 @@
- net481;net48;net472;net462;net6.0-Windows;net7.0-Windows;net8.0-Windows;net9.0-Windows
+ net481;net48;net472;net462;net6.0-Windows;net7.0-Windows;net8.0-Windows
True
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
diff --git a/CSharpBible/Libraries/ExtendedConsole/ExtendedConsole.csproj b/CSharpBible/Libraries/ExtendedConsole/ExtendedConsole.csproj
index 6da1cc4e2..e9dfecbab 100644
--- a/CSharpBible/Libraries/ExtendedConsole/ExtendedConsole.csproj
+++ b/CSharpBible/Libraries/ExtendedConsole/ExtendedConsole.csproj
@@ -1,12 +1,18 @@
- net462;net472;net481;net48;net6.0-Windows;net7.0-Windows;net8.0-Windows;net9.0-Windows
+ net462;net472;net481;net48;net6.0-Windows;net7.0-Windows;net8.0-Windows
enable
enable
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
diff --git a/CSharpBible/Libraries/GenInterfaces/GenInterfaces.csproj b/CSharpBible/Libraries/GenInterfaces/GenInterfaces.csproj
index 93d9e60e7..af3c8d3bf 100644
--- a/CSharpBible/Libraries/GenInterfaces/GenInterfaces.csproj
+++ b/CSharpBible/Libraries/GenInterfaces/GenInterfaces.csproj
@@ -13,6 +13,6 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/CSharpBible/Libraries/Libraries_net.props b/CSharpBible/Libraries/Libraries_net.props
index 80db63fcd..7fae629d2 100644
--- a/CSharpBible/Libraries/Libraries_net.props
+++ b/CSharpBible/Libraries/Libraries_net.props
@@ -8,6 +8,7 @@
enable
disable
true
+ true
JC-Soft
Joe Care
Copyright © JC-Soft 2025
diff --git a/CSharpBible/Libraries/MVVM_BaseLib/MVVM_BaseLib.csproj b/CSharpBible/Libraries/MVVM_BaseLib/MVVM_BaseLib.csproj
index bc18ca14f..dcab58b26 100644
--- a/CSharpBible/Libraries/MVVM_BaseLib/MVVM_BaseLib.csproj
+++ b/CSharpBible/Libraries/MVVM_BaseLib/MVVM_BaseLib.csproj
@@ -2,12 +2,18 @@
Library
- net481;net48;net472;net462;net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
- AnyCPU;x86
+ net481;net48;net472;net462;net8.0-windows
+ AnyCPU;x86;x64
true
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
False
4772c317-55ff-4251-b766-1c41dfb672e5
@@ -26,7 +32,7 @@
-
+
diff --git a/CSharpBible/Libraries/MVVM_BaseLib/View/Extension/IIoC.cs b/CSharpBible/Libraries/MVVM_BaseLib/View/Extension/IIoC.cs
index fd610d783..63277e898 100644
--- a/CSharpBible/Libraries/MVVM_BaseLib/View/Extension/IIoC.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLib/View/Extension/IIoC.cs
@@ -13,11 +13,23 @@
// ***********************************************************************
using System;
-namespace MVVM.View.Extension;
+namespace MVVM.Views.Extension;
+///
+/// Defines a contract for Inversion of Control (IoC) container integration with XAML markup extensions.
+///
public interface IIoC
{
+ ///
+ /// Gets or sets the type to be resolved from the IoC container.
+ ///
+ /// The type to resolve.
Type Type { get; set; }
+ ///
+ /// Provides the resolved value from the IoC container.
+ ///
+ /// The service provider that can provide services for the markup extension.
+ /// The resolved object instance, or null if the type cannot be resolved.
object? ProvideValue(IServiceProvider serviceProvider);
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLib/View/Extension/IoC2.cs b/CSharpBible/Libraries/MVVM_BaseLib/View/Extension/IoC2.cs
index e81cde672..7ad7314bd 100644
--- a/CSharpBible/Libraries/MVVM_BaseLib/View/Extension/IoC2.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLib/View/Extension/IoC2.cs
@@ -18,7 +18,7 @@
///
/// The Extension namespace.
///
-namespace MVVM.View.Extension;
+namespace MVVM.Views.Extension;
///
/// Class IoC.
diff --git a/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/Bool2VisibilityConverter.cs b/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/Bool2VisibilityConverter.cs
index 18eb03efd..3738b48b9 100644
--- a/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/Bool2VisibilityConverter.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/Bool2VisibilityConverter.cs
@@ -1,12 +1,12 @@
// ***********************************************************************
-// Assembly : MVVM.View.Converters
+// Assembly : MVVM.Views.Converters
// Author : Mir
// Created : 07-03-2022
//
// Last Modified By : Mir
// Last Modified On : 07-04-2022
// ***********************************************************************
-//
+//
// Copyright (c) JC-Soft. All rights reserved.
//
//
@@ -16,7 +16,7 @@
using System.Windows;
using System.Windows.Data;
-namespace MVVM.View.ValueConverter;
+namespace MVVM.Views.ValueConverter;
///
/// Class Bool2VisibilityConverter.
diff --git a/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/DoubleValueConverter.cs b/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/DoubleValueConverter.cs
index 0ea2d7e6e..1a07aa921 100644
--- a/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/DoubleValueConverter.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/DoubleValueConverter.cs
@@ -1,5 +1,5 @@
// ***********************************************************************
-// Assembly : MVVM.View.Converter
+// Assembly : MVVM.Views.Converter
// Author : Mir
// Created : 07-03-2022
//
@@ -15,7 +15,7 @@
using System.Globalization;
using System.Windows.Data;
-namespace MVVM.View.ValueConverter;
+namespace MVVM.Views.ValueConverter;
///
/// Class CurrencyValueConverter.
diff --git a/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/Xaml2ElementConverter.cs b/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/Xaml2ElementConverter.cs
index 6b95baf7f..2f760ad9a 100644
--- a/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/Xaml2ElementConverter.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLib/View/ValueConverter/Xaml2ElementConverter.cs
@@ -4,7 +4,7 @@
using System.Windows.Data;
using System.Windows.Markup;
-namespace MVVM.View.ValueConverter;
+namespace MVVM.Views.ValueConverter;
public class Xaml2ElementConverter : IValueConverter
{
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/MVVM_BaseLibTests.csproj b/CSharpBible/Libraries/MVVM_BaseLibTests/MVVM_BaseLibTests.csproj
index 95ddc4599..fcc3cce3b 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/MVVM_BaseLibTests.csproj
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/MVVM_BaseLibTests.csproj
@@ -1,26 +1,24 @@
- net481;net48;net472;net462;net8.0-windows;net9.0-windows
+ net481;net48;net472;net462;net8.0-windows
true
false
+ true
AnyCPU;x86
-
-
-
-
-
-
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseTestViewModel.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseTestViewModel.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseTestViewModel.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseTestViewModel.cs
index e852bac6f..a84d34384 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseTestViewModel.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseTestViewModel.cs
@@ -1,148 +1,148 @@
-// ***********************************************************************
-// Assembly : MVVM_BaseLibTests
-// Author : Mir
-// Created : 05-19-2023
-//
-// Last Modified By : Mir
-// Last Modified On : 01-26-2024
-// ***********************************************************************
-//
-// Copyright © JC-Soft 2023
-//
-//
-// ***********************************************************************
-using BaseLib.Helper;
-using CommunityToolkit.Mvvm.Input;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-
-///
-/// The ViewModel namespace.
-///
-namespace MVVM.ViewModel;
-
-///
-/// Class BaseTestViewModel.
-/// Implements the
-///
-///
-///
-public abstract class BaseTestViewModel : BaseTestViewModel where T : class, INotifyPropertyChanged, new()
-{
- ///
- /// The get model
- ///
- protected Func GetModel = () => new T();
-#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
- ///
- /// The test model with events
- ///
- protected T testModel;
- ///
- /// The test model2 without events
- ///
- protected T testModel2;
-#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
-
- protected virtual Dictionary GetDefaultData() => new();
-
- ///
- /// Initializes the test-models for this instance.
- ///
- [TestInitialize]
- public virtual void Init()
- {
- testModel = GetModel();
- testModel2 = GetModel();
- if (testModel is INotifyPropertyChanged inpc)
- inpc.PropertyChanged += OnVMPropertyChanged;
- if (testModel is INotifyPropertyChanging inpcg)
- inpcg.PropertyChanging += OnVMPropertyChanging;
- if (testModel is INotifyDataErrorInfo indei)
- indei.ErrorsChanged += OnVMErrorsChanged;
- foreach (var p in typeof(T).GetProperties())
- if (p.CanRead && p.GetValue(testModel) is IRelayCommand irc)
- irc.CanExecuteChanged += OnCanExChanged;
- }
-
- protected static IEnumerable TestModelProperies => typeof(T).GetProperties().Select(o => new object[] { o.Name, o.PropertyType.TC(), o.CanRead, o.CanWrite });
-
- [TestMethod]
- [DynamicData(nameof(TestModelProperies))]
- public virtual void TestModelProperiesTest(string sPropName, TypeCode tPropType, bool xCanRead, bool xCanWrite)
- {
- var p = typeof(T).GetProperty(sPropName);
- Assert.IsNotNull(p);
- Assert.AreEqual(xCanRead, p!.CanRead);
- Assert.AreEqual(xCanWrite, p!.CanWrite);
- if (xCanRead && GetDefaultData()?.TryGetValue(sPropName, out var oDefVal) == true)
- Assert.AreEqual(oDefVal, testModel.GetProp(sPropName));
- }
-}
-
-///
-/// Class BaseTestViewModel.
-/// Implements the
-///
-///
-public class BaseTestViewModel
-{
- ///
- /// The debug log
- ///
- private string _debugLog = "";
-
- ///
- /// Gets the debug log.
- ///
- /// The debug log.
- protected string DebugLog => _debugLog;
-
- ///
- /// Does the log.
- ///
- /// The v.
- protected void DoLog(string v)
- => _debugLog += $"{v}{Environment.NewLine}";
-
- ///
- /// Clears the log.
- ///
- protected void ClearLog() => _debugLog = "";
-
- ///
- /// Handles the event of the command.
- ///
- /// The sender.
- /// The instance containing the event data.
- protected virtual void OnCanExChanged(object? sender, EventArgs e)
- => DoLog($"CanExChanged({sender})={(sender as IRelayCommand)?.CanExecute(null)}");
-
- ///
- /// Handles the event of the model.
- ///
- /// The sender.
- /// The instance containing the event data.
- protected virtual void OnVMPropertyChanged(object? sender, PropertyChangedEventArgs e)
- => DoLog($"PropChg({sender},{e.PropertyName})={sender?.GetProp(e.PropertyName ?? "")}");
-
- ///
- /// Handles the event of the model.
- ///
- /// The sender.
- /// The instance containing the event data.
- protected virtual void OnVMPropertyChanging(object? sender, PropertyChangingEventArgs e)
- => DoLog($"PropChgn({sender},{e.PropertyName})={sender?.GetProp(e.PropertyName ?? "")}");
-
- ///
- /// Handles the event of the model.
- ///
- /// The sender.
- /// The instance containing the event data.
- protected virtual void OnVMErrorsChanged(object? sender, DataErrorsChangedEventArgs e)
- => DoLog($"ErrorsChanged({sender},{e.PropertyName})={string.Join(",", ((List)(sender as INotifyDataErrorInfo)!.GetErrors(e.PropertyName)).ConvertAll(o => o.ErrorMessage))}");
-}
+// ***********************************************************************
+// Assembly : MVVM_BaseLibTests
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 01-26-2024
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using BaseLib.Helper;
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+
+///
+/// The ViewModel namespace.
+///
+namespace MVVM.ViewModel;
+
+///
+/// Class BaseTestViewModel.
+/// Implements the
+///
+///
+///
+public abstract class BaseTestViewModel : BaseTestViewModel where T : class, INotifyPropertyChanged, new()
+{
+ ///
+ /// The get model
+ ///
+ protected Func GetModel = () => new T();
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model with events
+ ///
+ protected T testModel;
+ ///
+ /// The test model2 without events
+ ///
+ protected T testModel2;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ protected virtual Dictionary GetDefaultData() => new();
+
+ ///
+ /// Initializes the test-models for this instance.
+ ///
+ [TestInitialize]
+ public virtual void Init()
+ {
+ testModel = GetModel();
+ testModel2 = GetModel();
+ if (testModel is INotifyPropertyChanged inpc)
+ inpc.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging inpcg)
+ inpcg.PropertyChanging += OnVMPropertyChanging;
+ if (testModel is INotifyDataErrorInfo indei)
+ indei.ErrorsChanged += OnVMErrorsChanged;
+ foreach (var p in typeof(T).GetProperties())
+ if (p.CanRead && p.GetValue(testModel) is IRelayCommand irc)
+ irc.CanExecuteChanged += OnCanExChanged;
+ }
+
+ protected static IEnumerable TestModelProperies => typeof(T).GetProperties().Select(o => new object[] { o.Name, o.PropertyType.TC(), o.CanRead, o.CanWrite });
+
+ [TestMethod]
+ [DynamicData(nameof(TestModelProperies))]
+ public virtual void TestModelProperiesTest(string sPropName, TypeCode tPropType, bool xCanRead, bool xCanWrite)
+ {
+ var p = typeof(T).GetProperty(sPropName);
+ Assert.IsNotNull(p);
+ Assert.AreEqual(xCanRead, p!.CanRead);
+ Assert.AreEqual(xCanWrite, p!.CanWrite);
+ if (xCanRead && GetDefaultData()?.TryGetValue(sPropName, out var oDefVal) == true)
+ Assert.AreEqual(oDefVal, testModel.GetProp(sPropName));
+ }
+}
+
+///
+/// Class BaseTestViewModel.
+/// Implements the
+///
+///
+public class BaseTestViewModel
+{
+ ///
+ /// The debug log
+ ///
+ private string _debugLog = "";
+
+ ///
+ /// Gets the debug log.
+ ///
+ /// The debug log.
+ protected string DebugLog => _debugLog;
+
+ ///
+ /// Does the log.
+ ///
+ /// The v.
+ protected void DoLog(string v)
+ => _debugLog += $"{v}{Environment.NewLine}";
+
+ ///
+ /// Clears the log.
+ ///
+ protected void ClearLog() => _debugLog = "";
+
+ ///
+ /// Handles the event of the command.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ protected virtual void OnCanExChanged(object? sender, EventArgs e)
+ => DoLog($"CanExChanged({sender})={(sender as IRelayCommand)?.CanExecute(null)}");
+
+ ///
+ /// Handles the event of the model.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ protected virtual void OnVMPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ => DoLog($"PropChg({sender},{e.PropertyName})={sender?.GetProp(e.PropertyName ?? "")}");
+
+ ///
+ /// Handles the event of the model.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ protected virtual void OnVMPropertyChanging(object? sender, PropertyChangingEventArgs e)
+ => DoLog($"PropChgn({sender},{e.PropertyName})={sender?.GetProp(e.PropertyName ?? "")}");
+
+ ///
+ /// Handles the event of the model.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ protected virtual void OnVMErrorsChanged(object? sender, DataErrorsChangedEventArgs e)
+ => DoLog($"ErrorsChanged({sender},{e.PropertyName})={string.Join(",", ((List)(sender as INotifyDataErrorInfo)!.GetErrors(e.PropertyName)).ConvertAll(o => o.ErrorMessage))}");
+}
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseTestViewModelTest.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseTestViewModelTest.cs
similarity index 96%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseTestViewModelTest.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseTestViewModelTest.cs
index 72b4151d1..6950bd57f 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseTestViewModelTest.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseTestViewModelTest.cs
@@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
+using System.Globalization;
namespace MVVM_BaseLibTests.ViewModel;
@@ -37,6 +38,7 @@ public class BaseTestViewModelTest : BaseTestViewModel
[TestInitialize]
public void Init()
{
+ CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
testModel = new TestVM();
testModel.PropertyChanged += OnVMPropertyChanged;
testModel.PropertyChanging += OnVMPropertyChanging;
@@ -108,6 +110,12 @@ public void StringPropTest(string sAct, string[] asExp)
[TestClass]
public class BaseTestViewModelTest_T : BaseTestViewModel
{
+ [TestInitialize]
+ public override void Init()
+ {
+ CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
+ base.Init();
+ }
[TestMethod]
[DataRow(0, 0, new[] { "" })]
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseViewModelCTTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseViewModelCTTests.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseViewModelCTTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseViewModelCTTests.cs
index 801c65caa..065a2e331 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseViewModelCTTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseViewModelCTTests.cs
@@ -1,163 +1,163 @@
-using CommunityToolkit.Mvvm.Input;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.ComponentModel;
-
-namespace MVVM.ViewModel.Tests;
-
-[TestClass()]
-public class BaseViewModelCTTests : BaseViewModelCT
-{
- private int property1=3;
- private int property2=5;
- private string DebugResult="";
-
- [TestInitialize()]
- public void Init()
- {
- CommandCanExecuteBindingClear();
- KnownParams.Clear();
- PropertyChanged -= OnPropertyChanged;
- PropertyChanged += OnPropertyChanged;
- doSomething = new DelegateCommand((i) => DebugResult += $"doSomething({i})",(i)=>Prop1IsGreaterThen1Prop2());
- doSomething.CanExecuteChanged += OnCanExChanged;
- }
-
- private void OnCanExChanged(object? sender, EventArgs e)
- => DebugResult += $"OnCanExChanged: o:{sender}{Environment.NewLine}";
-
- private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
- => DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName??"")?.GetValue(sender)}{Environment.NewLine}";
-
- public int Property1 { get => property1; set => SetProperty(ref property1 , value); }
- public int Property2 { get => property2; set => SetProperty(ref property2, value); }
- public int Property3 { get => property1 + property2; set => Property1 = value -Property2; }
- public int Property4 { get => property2*property2; set => Property2 = (int)Math.Sqrt(value); }
-
- public bool Prop1IsGreaterThen1Prop2() => property1 > property2;
- public bool Prop2IsGreater(int i) => i > property2;
- public bool IsGreater(int i,int i2) => i > i2;
-
- public IRelayCommand? doSomething { get; set; }
-
- [TestMethod()]
- [DataRow("0 - 1 => 2",1,2,new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:7
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property1:2
-" })]
- [DataRow("1 - 1 => 6", 1, 6, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:11
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property1:6
-" })]
- [DataRow("2 - 2 => 2", 2, 2, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:5
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property4:4
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property2:2
-" })]
- [DataRow("3 - 2 => 6", 2, 6, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:9
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property4:36
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property2:6
-" })]
- [DataRow("4 - 3 => 9", 3, 9, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:9
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property1:4
-" })]
- [DataRow("5 - 3 => 7", 3, 7, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:7
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property1:2
-" })]
- [DataRow("6 - 4 => 9", 4, 9, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:6
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property4:9
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property2:3
-" })]
- [DataRow("7 - 4 => 16", 4, 16, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:7
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property4:16
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property2:4
-" })]
- public void BaseViewModelTest(string name, int i,int iVal, string[] aExp )
- {
- AddPropertyDependency(nameof(Property3), nameof(Property1));
- AddPropertyDependency(nameof(Property3), nameof(Property2));
- AddPropertyDependency(nameof(Property4), nameof(Property2));
- AddPropertyDependency(nameof(Prop1IsGreaterThen1Prop2), nameof(Property1));
- AddPropertyDependency(nameof(Prop1IsGreaterThen1Prop2), nameof(Property2));
- AddPropertyDependency(nameof(Prop2IsGreater), nameof(Property2));
- AddPropertyDependency(nameof(doSomething), nameof(Prop1IsGreaterThen1Prop2));
- AddPropertyDependency(nameof(IsGreater), nameof(Property2));
- AddPropertyDependency("XX", nameof(Property2));
- AppendKnownParams(1, nameof(Prop2IsGreater));
- AppendKnownParams(5, nameof(Prop2IsGreater));
- AppendKnownParams(7, nameof(Prop2IsGreater));
- AppendKnownParams(7, "");
- switch (i)
- {
- case 1:Property1 = iVal; break;
- case 2: Property2 = iVal; break;
- case 3: Property3 = iVal; break;
- case 4: Property4 = iVal; break;
- }
- Assert.AreEqual( aExp[0], DebugResult, name);
-
- }
-
- [TestMethod]
- public void RemovePropertyDependencyTest()
- {
- Assert.ThrowsExactly(() => RemovePropertyDependency("1", "3"));
- }
-
- [TestMethod]
- [DataRow(1,false)]
- [DataRow(2, false)]
- [DataRow(0, false)]
- public void FuncProxyTest (int dVal,bool xExp)
- {
- Assert.AreEqual (xExp,FuncProxy(dVal, Prop2IsGreater));
- }
-
- [TestMethod]
- [DataRow(1, true)]
- [DataRow(2, true)]
- [DataRow(0.5, false)]
- public void FuncProxy2Test(double dVal, bool xExp)
- {
- Assert.AreEqual(xExp, FuncProxy(dVal, LocalFunc));
- }
-
- private bool LocalFunc(double arg)
- {
- return arg == Math.Floor(arg);
- }
-
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.ComponentModel;
+
+namespace MVVM.ViewModel.Tests;
+
+[TestClass()]
+public class BaseViewModelCTTests : BaseViewModelCT
+{
+ private int property1=3;
+ private int property2=5;
+ private string DebugResult="";
+
+ [TestInitialize()]
+ public void Init()
+ {
+ CommandCanExecuteBindingClear();
+ KnownParams.Clear();
+ PropertyChanged -= OnPropertyChanged;
+ PropertyChanged += OnPropertyChanged;
+ doSomething = new DelegateCommand((i) => DebugResult += $"doSomething({i})",(i)=>Prop1IsGreaterThen1Prop2());
+ doSomething.CanExecuteChanged += OnCanExChanged;
+ }
+
+ private void OnCanExChanged(object? sender, EventArgs e)
+ => DebugResult += $"OnCanExChanged: o:{sender}{Environment.NewLine}";
+
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ => DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName??"")?.GetValue(sender)}{Environment.NewLine}";
+
+ public int Property1 { get => property1; set => SetProperty(ref property1 , value); }
+ public int Property2 { get => property2; set => SetProperty(ref property2, value); }
+ public int Property3 { get => property1 + property2; set => Property1 = value -Property2; }
+ public int Property4 { get => property2*property2; set => Property2 = (int)Math.Sqrt(value); }
+
+ public bool Prop1IsGreaterThen1Prop2() => property1 > property2;
+ public bool Prop2IsGreater(int i) => i > property2;
+ public bool IsGreater(int i,int i2) => i > i2;
+
+ public IRelayCommand? doSomething { get; set; }
+
+ [TestMethod()]
+ [DataRow("0 - 1 => 2",1,2,new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:7
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property1:2
+" })]
+ [DataRow("1 - 1 => 6", 1, 6, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:11
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property1:6
+" })]
+ [DataRow("2 - 2 => 2", 2, 2, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:5
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property4:4
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property2:2
+" })]
+ [DataRow("3 - 2 => 6", 2, 6, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:9
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property4:36
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property2:6
+" })]
+ [DataRow("4 - 3 => 9", 3, 9, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:9
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property1:4
+" })]
+ [DataRow("5 - 3 => 7", 3, 7, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:7
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property1:2
+" })]
+ [DataRow("6 - 4 => 9", 4, 9, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:6
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property4:9
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property2:3
+" })]
+ [DataRow("7 - 4 => 16", 4, 16, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property3:7
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property4:16
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelCTTests, p:Property2:4
+" })]
+ public void BaseViewModelTest(string name, int i,int iVal, string[] aExp )
+ {
+ AddPropertyDependency(nameof(Property3), nameof(Property1));
+ AddPropertyDependency(nameof(Property3), nameof(Property2));
+ AddPropertyDependency(nameof(Property4), nameof(Property2));
+ AddPropertyDependency(nameof(Prop1IsGreaterThen1Prop2), nameof(Property1));
+ AddPropertyDependency(nameof(Prop1IsGreaterThen1Prop2), nameof(Property2));
+ AddPropertyDependency(nameof(Prop2IsGreater), nameof(Property2));
+ AddPropertyDependency(nameof(doSomething), nameof(Prop1IsGreaterThen1Prop2));
+ AddPropertyDependency(nameof(IsGreater), nameof(Property2));
+ AddPropertyDependency("XX", nameof(Property2));
+ AppendKnownParams(1, nameof(Prop2IsGreater));
+ AppendKnownParams(5, nameof(Prop2IsGreater));
+ AppendKnownParams(7, nameof(Prop2IsGreater));
+ AppendKnownParams(7, "");
+ switch (i)
+ {
+ case 1:Property1 = iVal; break;
+ case 2: Property2 = iVal; break;
+ case 3: Property3 = iVal; break;
+ case 4: Property4 = iVal; break;
+ }
+ Assert.AreEqual( aExp[0], DebugResult, name);
+
+ }
+
+ [TestMethod]
+ public void RemovePropertyDependencyTest()
+ {
+ Assert.ThrowsExactly(() => RemovePropertyDependency("1", "3"));
+ }
+
+ [TestMethod]
+ [DataRow(1,false)]
+ [DataRow(2, false)]
+ [DataRow(0, false)]
+ public void FuncProxyTest (int dVal,bool xExp)
+ {
+ Assert.AreEqual (xExp,FuncProxy(dVal, Prop2IsGreater));
+ }
+
+ [TestMethod]
+ [DataRow(1, true)]
+ [DataRow(2, true)]
+ [DataRow(0.5, false)]
+ public void FuncProxy2Test(double dVal, bool xExp)
+ {
+ Assert.AreEqual(xExp, FuncProxy(dVal, LocalFunc));
+ }
+
+ private bool LocalFunc(double arg)
+ {
+ return arg == Math.Floor(arg);
+ }
+
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseViewModelTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseViewModelTests.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseViewModelTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseViewModelTests.cs
index 70034671f..2e19f4475 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BaseViewModelTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BaseViewModelTests.cs
@@ -1,170 +1,170 @@
-using CommunityToolkit.Mvvm.Input;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.ComponentModel;
-
-namespace MVVM.ViewModel.Tests;
-
-[TestClass()]
-public class BaseViewModelTests : BaseViewModel
-{
- private int property1=3;
- private int property2=5;
- private string DebugResult="";
-
- [TestInitialize()]
- public void Init()
- {
- CommandCanExecuteBindingClear();
- KnownParams.Clear();
- PropertyChanged -= OnPropertyChanged;
- PropertyChanged += OnPropertyChanged;
- doSomething = new DelegateCommand((i) => DebugResult += $"doSomething({i})",(i)=>Prop1IsGreaterThen1Prop2());
- doSomething.CanExecuteChanged += OnCanExChanged;
- }
-
- private void OnCanExChanged(object? sender, EventArgs e)
- => DebugResult += $"OnCanExChanged: o:{sender}{Environment.NewLine}";
-
- private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
- => DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName??"")?.GetValue(sender)}{Environment.NewLine}";
-
- public int Property1 { get => property1; set => SetProperty(ref property1 , value); }
- public int Property2 { get => property2; set => SetProperty(ref property2, value); }
- public int Property3 { get => property1 + property2; set => Property1 = value -Property2; }
- public int Property4 { get => property2*property2; set => Property2 = (int)Math.Sqrt(value); }
-
- public bool Prop1IsGreaterThen1Prop2() => property1 > property2;
- public bool Prop2IsGreater(int i) => i > property2;
- public bool IsGreater(int i,int i2) => i > i2;
-
- public IRelayCommand? doSomething { get; set; }
-
- [TestMethod()]
- [DataRow("0 - 1 => 2",1,2,new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:7
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property1:2
-" })]
- [DataRow("1 - 1 => 6", 1, 6, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:11
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property1:6
-" })]
- [DataRow("2 - 2 => 2", 2, 2, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:5
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property4:4
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property2:2
-" })]
- [DataRow("3 - 2 => 6", 2, 6, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:9
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property4:36
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property2:6
-" })]
- [DataRow("4 - 3 => 9", 3, 9, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:9
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property1:4
-" })]
- [DataRow("5 - 3 => 7", 3, 7, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:7
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property1:2
-" })]
- [DataRow("6 - 4 => 9", 4, 9, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:6
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property4:9
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property2:3
-" })]
- [DataRow("7 - 4 => 16", 4, 16, new string[] {
- @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:7
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property4:16
-OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
-OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property2:4
-" })]
- public void BaseViewModelTest(string name, int i,int iVal, string[] aExp )
- {
- AddPropertyDependency(nameof(Property3), nameof(Property1));
- AddPropertyDependency(nameof(Property3), nameof(Property2));
- AddPropertyDependency(nameof(Property4), nameof(Property2));
- AddPropertyDependency(nameof(Prop1IsGreaterThen1Prop2), nameof(Property1));
- AddPropertyDependency(nameof(Prop1IsGreaterThen1Prop2), nameof(Property2));
- AddPropertyDependency(nameof(Prop2IsGreater), nameof(Property2));
- AddPropertyDependency(nameof(doSomething), nameof(Prop1IsGreaterThen1Prop2));
- AddPropertyDependency(nameof(IsGreater), nameof(Property2));
- AddPropertyDependency("XX", nameof(Property2));
- AppendKnownParams(1, nameof(Prop2IsGreater));
- AppendKnownParams(5, nameof(Prop2IsGreater));
- AppendKnownParams(7, nameof(Prop2IsGreater));
- AppendKnownParams(7, "");
- switch (i)
- {
- case 1:Property1 = iVal; break;
- case 2: Property2 = iVal; break;
- case 3: Property3 = iVal; break;
- case 4: Property4 = iVal; break;
- }
- Assert.AreEqual( aExp[0], DebugResult, name);
-
- }
-
- [TestMethod]
- public void RemovePropertyDependencyTest()
- {
- Assert.ThrowsExactly(() => RemovePropertyDependency("1", "3"));
- }
-
- [TestMethod]
- [DataRow(1,false)]
- [DataRow(2, false)]
- [DataRow(0, false)]
- public void FuncProxyTest (int dVal,bool xExp)
- {
- Assert.AreEqual (xExp,FuncProxy(dVal, Prop2IsGreater));
- }
-
- [TestMethod]
- [DataRow(1, true)]
- [DataRow(2, true)]
- [DataRow(0.5, false)]
- public void FuncProxy2Test(double dVal, bool xExp)
- {
- Assert.AreEqual(xExp, FuncProxy(dVal, LocalFunc));
- }
-
- [TestMethod]
- [DataRow(1,1, false)]
- [DataRow(3,2, true)]
- [DataRow(0,1, false)]
- public void IsGreaterTest(int dVal1,int dVal2, bool xExp)
- {
- Assert.AreEqual(xExp, IsGreater(dVal1,dVal2));
- }
- private bool LocalFunc(double arg)
- {
- return arg == Math.Floor(arg);
- }
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.ComponentModel;
+
+namespace MVVM.ViewModel.Tests;
+
+[TestClass()]
+public class BaseViewModelTests : BaseViewModel
+{
+ private int property1=3;
+ private int property2=5;
+ private string DebugResult="";
+
+ [TestInitialize()]
+ public void Init()
+ {
+ CommandCanExecuteBindingClear();
+ KnownParams.Clear();
+ PropertyChanged -= OnPropertyChanged;
+ PropertyChanged += OnPropertyChanged;
+ doSomething = new DelegateCommand((i) => DebugResult += $"doSomething({i})",(i)=>Prop1IsGreaterThen1Prop2());
+ doSomething.CanExecuteChanged += OnCanExChanged;
+ }
+
+ private void OnCanExChanged(object? sender, EventArgs e)
+ => DebugResult += $"OnCanExChanged: o:{sender}{Environment.NewLine}";
+
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ => DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName??"")?.GetValue(sender)}{Environment.NewLine}";
+
+ public int Property1 { get => property1; set => SetProperty(ref property1 , value); }
+ public int Property2 { get => property2; set => SetProperty(ref property2, value); }
+ public int Property3 { get => property1 + property2; set => Property1 = value -Property2; }
+ public int Property4 { get => property2*property2; set => Property2 = (int)Math.Sqrt(value); }
+
+ public bool Prop1IsGreaterThen1Prop2() => property1 > property2;
+ public bool Prop2IsGreater(int i) => i > property2;
+ public bool IsGreater(int i,int i2) => i > i2;
+
+ public IRelayCommand? doSomething { get; set; }
+
+ [TestMethod()]
+ [DataRow("0 - 1 => 2",1,2,new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:7
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property1:2
+" })]
+ [DataRow("1 - 1 => 6", 1, 6, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:11
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property1:6
+" })]
+ [DataRow("2 - 2 => 2", 2, 2, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:5
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property4:4
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property2:2
+" })]
+ [DataRow("3 - 2 => 6", 2, 6, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:9
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property4:36
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property2:6
+" })]
+ [DataRow("4 - 3 => 9", 3, 9, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:9
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property1:4
+" })]
+ [DataRow("5 - 3 => 7", 3, 7, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:7
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property1:2
+" })]
+ [DataRow("6 - 4 => 9", 4, 9, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:6
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property4:9
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property2:3
+" })]
+ [DataRow("7 - 4 => 16", 4, 16, new string[] {
+ @"OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property3:7
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property4:16
+OnCanExChanged: o:MVVM.ViewModel.DelegateCommand`1[System.Int32]
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop1IsGreaterThen1Prop2:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Prop2IsGreater:
+OnPropChanged: o:MVVM.ViewModel.Tests.BaseViewModelTests, p:Property2:4
+" })]
+ public void BaseViewModelTest(string name, int i,int iVal, string[] aExp )
+ {
+ AddPropertyDependency(nameof(Property3), nameof(Property1));
+ AddPropertyDependency(nameof(Property3), nameof(Property2));
+ AddPropertyDependency(nameof(Property4), nameof(Property2));
+ AddPropertyDependency(nameof(Prop1IsGreaterThen1Prop2), nameof(Property1));
+ AddPropertyDependency(nameof(Prop1IsGreaterThen1Prop2), nameof(Property2));
+ AddPropertyDependency(nameof(Prop2IsGreater), nameof(Property2));
+ AddPropertyDependency(nameof(doSomething), nameof(Prop1IsGreaterThen1Prop2));
+ AddPropertyDependency(nameof(IsGreater), nameof(Property2));
+ AddPropertyDependency("XX", nameof(Property2));
+ AppendKnownParams(1, nameof(Prop2IsGreater));
+ AppendKnownParams(5, nameof(Prop2IsGreater));
+ AppendKnownParams(7, nameof(Prop2IsGreater));
+ AppendKnownParams(7, "");
+ switch (i)
+ {
+ case 1:Property1 = iVal; break;
+ case 2: Property2 = iVal; break;
+ case 3: Property3 = iVal; break;
+ case 4: Property4 = iVal; break;
+ }
+ Assert.AreEqual( aExp[0], DebugResult, name);
+
+ }
+
+ [TestMethod]
+ public void RemovePropertyDependencyTest()
+ {
+ Assert.ThrowsExactly(() => RemovePropertyDependency("1", "3"));
+ }
+
+ [TestMethod]
+ [DataRow(1,false)]
+ [DataRow(2, false)]
+ [DataRow(0, false)]
+ public void FuncProxyTest (int dVal,bool xExp)
+ {
+ Assert.AreEqual (xExp,FuncProxy(dVal, Prop2IsGreater));
+ }
+
+ [TestMethod]
+ [DataRow(1, true)]
+ [DataRow(2, true)]
+ [DataRow(0.5, false)]
+ public void FuncProxy2Test(double dVal, bool xExp)
+ {
+ Assert.AreEqual(xExp, FuncProxy(dVal, LocalFunc));
+ }
+
+ [TestMethod]
+ [DataRow(1,1, false)]
+ [DataRow(3,2, true)]
+ [DataRow(0,1, false)]
+ public void IsGreaterTest(int dVal1,int dVal2, bool xExp)
+ {
+ Assert.AreEqual(xExp, IsGreater(dVal1,dVal2));
+ }
+ private bool LocalFunc(double arg)
+ {
+ return arg == Math.Floor(arg);
+ }
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BindableCollectionTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BindableCollectionTests.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BindableCollectionTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BindableCollectionTests.cs
index d4ffe8ce5..a88eda4fc 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/BindableCollectionTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/BindableCollectionTests.cs
@@ -1,118 +1,118 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-
-namespace MVVM.ViewModel.Tests;
-
-[TestClass()]
-public class BindableCollectionTests : BaseTestViewModel
-{
-#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
- private BindableCollection _testClass;
-#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
-
- [TestInitialize]
- public void Init()
- {
- _testClass = new BindableCollection();
- _testClass.CollectionChanged += OnCollectionChanged;
- ClearLog();
-
- }
-
- private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
- {
- DoLog($"OnCollectionChanged({sender},{e.Action})");
- }
-
- [TestMethod()]
- public void BindableCollectionTest()
- {
- Assert.IsNotNull(_testClass);
- Assert.IsInstanceOfType(_testClass, typeof(BindableCollection));
- Assert.IsInstanceOfType(_testClass, typeof(System.Collections.ObjectModel.ObservableCollection));
- Assert.IsInstanceOfType(_testClass, typeof(ObservableCollection));
- Assert.AreSame(_testClass, _testClass as ObservableCollection);
- }
-
- [TestMethod()]
- public void BindableCollectionTest1()
- {
- var testClass = new BindableCollection(new[] { "1", "2", "3" });
- Assert.IsNotNull(testClass);
- Assert.IsInstanceOfType(testClass, typeof(BindableCollection));
- Assert.IsInstanceOfType(testClass, typeof(System.Collections.ObjectModel.ObservableCollection));
- Assert.IsInstanceOfType(testClass, typeof(ObservableCollection));
- }
-
- [TestMethod()]
- public void NotifyOnCollectionChangedTest()
- {
- _testClass.Add("5");
- Assert.HasCount(1, _testClass);
- Assert.AreEqual("5", _testClass[0]);
- Assert.AreEqual("OnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Add)\r\n", DebugLog);
- }
-
- [TestMethod()]
- public void NotifyOfPropertyChangeTest()
- {
- _testClass.Add("5");
- _testClass.NotifyOfPropertyChange("Test");
- Assert.HasCount(1, _testClass);
- Assert.AreEqual("5", _testClass[0]);
- Assert.AreEqual("OnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Add)\r\n", DebugLog);
- }
-
- [TestMethod()]
- public void SetItemTest()
- {
- _testClass.Add("5");
- _testClass[0] = "7";
- Assert.HasCount(1, _testClass);
- Assert.AreEqual("7", _testClass[0]);
- Assert.AreEqual("OnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Add)\r\nOnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Replace)\r\n", DebugLog);
- }
-
- [TestMethod()]
- public void RemoveItemTest()
- {
- _testClass.Add("5");
- _testClass.RemoveAt(0);
- Assert.IsEmpty(_testClass);
- Assert.AreEqual("OnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Add)\r\nOnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Remove)\r\n", DebugLog);
- }
-
- [TestMethod()]
- public void AddRangeTest()
- {
- _testClass.AddRange(new[] { "5", "7" });
- Assert.HasCount(2, _testClass);
- Assert.AreEqual("5", _testClass[0]);
- Assert.AreEqual("7", _testClass[1]);
- }
-
- [TestMethod()]
- public void ClearTest()
- {
- _testClass.AddRange(new[] { "5", "7" });
- _testClass.Clear();
- Assert.IsEmpty(_testClass);
- }
-
- [TestMethod()]
- public void RefreshTest()
- {
- _testClass.AddRange(new[] { "5", "7" });
- _testClass.Refresh();
- Assert.HasCount(2, _testClass);
- }
-
- [TestMethod()]
- public void RemoveRangeTest()
- {
- _testClass.AddRange(new[] { "5", "7", "1" });
- _testClass.RemoveRange(new[] { "5", "1", "0" });
- Assert.HasCount(1, _testClass);
- }
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+
+namespace MVVM.ViewModel.Tests;
+
+[TestClass()]
+public class BindableCollectionTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ private BindableCollection _testClass;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ _testClass = new BindableCollection();
+ _testClass.CollectionChanged += OnCollectionChanged;
+ ClearLog();
+
+ }
+
+ private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ DoLog($"OnCollectionChanged({sender},{e.Action})");
+ }
+
+ [TestMethod()]
+ public void BindableCollectionTest()
+ {
+ Assert.IsNotNull(_testClass);
+ Assert.IsInstanceOfType(_testClass, typeof(BindableCollection));
+ Assert.IsInstanceOfType(_testClass, typeof(System.Collections.ObjectModel.ObservableCollection));
+ Assert.IsInstanceOfType(_testClass, typeof(ObservableCollection));
+ Assert.AreSame(_testClass, _testClass as ObservableCollection);
+ }
+
+ [TestMethod()]
+ public void BindableCollectionTest1()
+ {
+ var testClass = new BindableCollection(new[] { "1", "2", "3" });
+ Assert.IsNotNull(testClass);
+ Assert.IsInstanceOfType(testClass, typeof(BindableCollection));
+ Assert.IsInstanceOfType(testClass, typeof(System.Collections.ObjectModel.ObservableCollection));
+ Assert.IsInstanceOfType(testClass, typeof(ObservableCollection));
+ }
+
+ [TestMethod()]
+ public void NotifyOnCollectionChangedTest()
+ {
+ _testClass.Add("5");
+ Assert.HasCount(1, _testClass);
+ Assert.AreEqual("5", _testClass[0]);
+ Assert.AreEqual("OnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Add)\r\n", DebugLog);
+ }
+
+ [TestMethod()]
+ public void NotifyOfPropertyChangeTest()
+ {
+ _testClass.Add("5");
+ _testClass.NotifyOfPropertyChange("Test");
+ Assert.HasCount(1, _testClass);
+ Assert.AreEqual("5", _testClass[0]);
+ Assert.AreEqual("OnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Add)\r\n", DebugLog);
+ }
+
+ [TestMethod()]
+ public void SetItemTest()
+ {
+ _testClass.Add("5");
+ _testClass[0] = "7";
+ Assert.HasCount(1, _testClass);
+ Assert.AreEqual("7", _testClass[0]);
+ Assert.AreEqual("OnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Add)\r\nOnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Replace)\r\n", DebugLog);
+ }
+
+ [TestMethod()]
+ public void RemoveItemTest()
+ {
+ _testClass.Add("5");
+ _testClass.RemoveAt(0);
+ Assert.IsEmpty(_testClass);
+ Assert.AreEqual("OnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Add)\r\nOnCollectionChanged(MVVM.ViewModel.BindableCollection`1[System.String],Remove)\r\n", DebugLog);
+ }
+
+ [TestMethod()]
+ public void AddRangeTest()
+ {
+ _testClass.AddRange(new[] { "5", "7" });
+ Assert.HasCount(2, _testClass);
+ Assert.AreEqual("5", _testClass[0]);
+ Assert.AreEqual("7", _testClass[1]);
+ }
+
+ [TestMethod()]
+ public void ClearTest()
+ {
+ _testClass.AddRange(new[] { "5", "7" });
+ _testClass.Clear();
+ Assert.IsEmpty(_testClass);
+ }
+
+ [TestMethod()]
+ public void RefreshTest()
+ {
+ _testClass.AddRange(new[] { "5", "7" });
+ _testClass.Refresh();
+ Assert.HasCount(2, _testClass);
+ }
+
+ [TestMethod()]
+ public void RemoveRangeTest()
+ {
+ _testClass.AddRange(new[] { "5", "7", "1" });
+ _testClass.RemoveRange(new[] { "5", "1", "0" });
+ Assert.HasCount(1, _testClass);
+ }
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DebugLog.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DebugLog.cs
similarity index 96%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DebugLog.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DebugLog.cs
index 334e02a40..9b3e590c4 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DebugLog.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DebugLog.cs
@@ -1,33 +1,33 @@
-// ***********************************************************************
-// Assembly : MVVM_36_ComToolKtSavesWork_netTests
-// Author : Mir
-// Created : 05-14-2023
-//
-// Last Modified By : Mir
-// Last Modified On : 05-14-2023
-// ***********************************************************************
-//
-// Copyright © JC-Soft 2023
-//
-//
-// ***********************************************************************
-using System;
-
-namespace MVVM.ViewModel.Tests;
-
-public class DebugLog : IDebugLog
-{
- private string _debugLog="";
-
- string IDebugLog.DebugLog => _debugLog;
-
- public void ClearLog()
- {
- _debugLog="";
- }
-
- public void DoLog(string message)
- {
- _debugLog+=$"{message}{Environment.NewLine}";
- }
+// ***********************************************************************
+// Assembly : MVVM_36_ComToolKtSavesWork_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using System;
+
+namespace MVVM.ViewModel.Tests;
+
+public class DebugLog : IDebugLog
+{
+ private string _debugLog="";
+
+ string IDebugLog.DebugLog => _debugLog;
+
+ public void ClearLog()
+ {
+ _debugLog="";
+ }
+
+ public void DoLog(string message)
+ {
+ _debugLog+=$"{message}{Environment.NewLine}";
+ }
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DebugLogTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DebugLogTests.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DebugLogTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DebugLogTests.cs
index 1778ea231..38bb434c2 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DebugLogTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DebugLogTests.cs
@@ -1,56 +1,56 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace MVVM.ViewModel.Tests;
-
-[TestClass]
-public class DebugLogTests
-{
-#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
- DebugLog testClass;
-#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
-
- [TestInitialize]
- public void Init() {
- testClass = new();
- }
-
- [TestMethod]
- public void SetupTest()
- {
- Assert.IsNotNull(testClass);
- Assert.IsInstanceOfType(testClass,typeof(DebugLog));
- Assert.IsInstanceOfType(testClass,typeof(IDebugLog));
- Assert.AreEqual("", (testClass as IDebugLog).DebugLog);
- }
-
- [TestMethod]
- public void DebugLogTest()
- {
- Assert.AreEqual("", (testClass as IDebugLog).DebugLog);
- testClass.DoLog("123");
- Assert.AreEqual("123\r\n", (testClass as IDebugLog).DebugLog);
- }
-
- [TestMethod]
- public void ClearLogTest()
- {
- testClass.ClearLog();
- Assert.AreEqual("", (testClass as IDebugLog).DebugLog);
- testClass.DoLog("123");
- Assert.AreEqual("123\r\n", (testClass as IDebugLog).DebugLog);
- testClass.ClearLog();
- Assert.AreEqual("", (testClass as IDebugLog).DebugLog);
- }
-
- [TestMethod]
- [DataRow(new[] { "" }, new[] { "\r\n" })]
- [DataRow(new[] { "","" }, new[] { "\r\n\r\n" })]
- [DataRow(new[] { "Peter" }, new[] { "Peter\r\n" })]
- [DataRow(new[] { "Peter","Haase" }, new[] { "Peter\r\nHaase\r\n" })]
- public void DoLogTest(string[] asVal, string[] asExp)
- {
- foreach (var line in asVal)
- testClass.DoLog(line);
- Assert.AreEqual(asExp[0], (testClass as IDebugLog).DebugLog);
- }
-}
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace MVVM.ViewModel.Tests;
+
+[TestClass]
+public class DebugLogTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ DebugLog testClass;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init() {
+ testClass = new();
+ }
+
+ [TestMethod]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testClass);
+ Assert.IsInstanceOfType(testClass,typeof(DebugLog));
+ Assert.IsInstanceOfType(testClass,typeof(IDebugLog));
+ Assert.AreEqual("", (testClass as IDebugLog).DebugLog);
+ }
+
+ [TestMethod]
+ public void DebugLogTest()
+ {
+ Assert.AreEqual("", (testClass as IDebugLog).DebugLog);
+ testClass.DoLog("123");
+ Assert.AreEqual("123\r\n", (testClass as IDebugLog).DebugLog);
+ }
+
+ [TestMethod]
+ public void ClearLogTest()
+ {
+ testClass.ClearLog();
+ Assert.AreEqual("", (testClass as IDebugLog).DebugLog);
+ testClass.DoLog("123");
+ Assert.AreEqual("123\r\n", (testClass as IDebugLog).DebugLog);
+ testClass.ClearLog();
+ Assert.AreEqual("", (testClass as IDebugLog).DebugLog);
+ }
+
+ [TestMethod]
+ [DataRow(new[] { "" }, new[] { "\r\n" })]
+ [DataRow(new[] { "","" }, new[] { "\r\n\r\n" })]
+ [DataRow(new[] { "Peter" }, new[] { "Peter\r\n" })]
+ [DataRow(new[] { "Peter","Haase" }, new[] { "Peter\r\nHaase\r\n" })]
+ public void DoLogTest(string[] asVal, string[] asExp)
+ {
+ foreach (var line in asVal)
+ testClass.DoLog(line);
+ Assert.AreEqual(asExp[0], (testClass as IDebugLog).DebugLog);
+ }
+}
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DelegateCommandTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DelegateCommandTests.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DelegateCommandTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DelegateCommandTests.cs
index a5f7f0d0a..f570fdf95 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/DelegateCommandTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/DelegateCommandTests.cs
@@ -1,102 +1,102 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-
-namespace MVVM.ViewModel.Tests;
-
-[TestClass()]
-public class DelegateCommandTests : BaseTestViewModel
-{
- DelegateCommand _testCommand1 =null!;
- DelegateCommand _testCommand3 = null!;
- DelegateCommand _testCommand2 = null!;
-
- [TestInitialize()]
- public void Init()
- {
- _testCommand1 = new((o) => DoLog($"Execute({nameof(_testCommand1)},{o})"));
- _testCommand3 = new((o) => DoLog($"Execute({nameof(_testCommand3)},{o})"),canCmd3);
- _testCommand2 = new(
- (o) => DoLog($"Execute({nameof(_testCommand2)},{o})"),
- canCmd2);
- _testCommand1.CanExecuteChanged += OnCanExecuteChanged;
- _testCommand2.CanExecuteChanged += OnCanExecuteChanged;
-// _testCommand3.CanExecuteChanged += OnCanExecuteChanged;
- }
-
- private void OnCanExecuteChanged(object? sender, EventArgs e)
- => DoLog($"CanExecuteChanged({sender})");
-
- private bool canCmd2(TypeCode? o)
- => o is not null and not (TypeCode.Object or TypeCode.Empty or TypeCode.DBNull);
-
- private bool canCmd3(object? o)
- => o is not null and not (TypeCode.Object or TypeCode.Empty or TypeCode.DBNull);
-
-
- [TestMethod()]
- [DataRow(0, null, false)]
- [DataRow(1,TypeCode.Boolean,true)]
- [DataRow(1, TypeCode.Empty, true)]
- [DataRow(1, TypeCode.String, true)]
- [DataRow(1, null, true)]
- [DataRow(2, TypeCode.Boolean, true)]
- [DataRow(2, TypeCode.Empty, false)]
- [DataRow(2, TypeCode.String, true)]
- [DataRow(2, null, false)]
- [DataRow(3, TypeCode.Boolean, true)]
- [DataRow(3, TypeCode.Empty, false)]
- [DataRow(3, TypeCode.String, true)]
- [DataRow(3, null, false)]
- public void CanExecuteTest(int iVal,TypeCode? tC,bool xExp)
- {
- var f = iVal switch {
- 1 => _testCommand1.CanExecute,
- 2 => _testCommand2.CanExecute,
- 3 => _testCommand3.CanExecute,
- _ => (Func)(o => false) };
- Assert.AreEqual(xExp,f(tC));
- }
-
- [TestMethod()]
- [DataRow(0, null, new[] { ""})]
- [DataRow(1, TypeCode.Boolean, new[] { "Execute(_testCommand1,Boolean)\r\n" })]
- [DataRow(1, TypeCode.Empty, new[] { "Execute(_testCommand1,Empty)\r\n" })]
- [DataRow(1, TypeCode.String, new[] { "Execute(_testCommand1,String)\r\n" })]
- [DataRow(1, null, new[] { "Execute(_testCommand1,)\r\n" })]
- [DataRow(2, TypeCode.Boolean, new[] { "Execute(_testCommand2,Boolean)\r\n" })]
- [DataRow(2, TypeCode.Empty, new[] { "Execute(_testCommand2,Empty)\r\n" })]
- [DataRow(2, TypeCode.String, new[] { "Execute(_testCommand2,String)\r\n" })]
- [DataRow(2, null, new[] { "Execute(_testCommand2,)\r\n" })]
- [DataRow(3, TypeCode.Boolean, new[] { "Execute(_testCommand3,Boolean)\r\n" })]
- [DataRow(3, TypeCode.Empty, new[] { "Execute(_testCommand3,Empty)\r\n" })]
- [DataRow(3, TypeCode.String, new[] { "Execute(_testCommand3,String)\r\n" })]
- [DataRow(3, null, new[] { "Execute(_testCommand3,)\r\n" })]
- public void ExecuteTest(int iVal, TypeCode? tC, string[] asExp)
- {
- (iVal switch
- {
- 1 => _testCommand1.Execute,
- 2 => _testCommand2.Execute,
- 3 => _testCommand3.Execute,
- _ => (Action)(o => { })
- }).Invoke(tC);
- Assert.AreEqual(asExp[0], DebugLog);
- }
-
- [TestMethod()]
- [DataRow(0, new[] { "" })]
- [DataRow(1, new[] { "CanExecuteChanged(MVVM.ViewModel.DelegateCommand)\r\n" })]
- [DataRow(2, new[] { "CanExecuteChanged(MVVM.ViewModel.DelegateCommand`1[System.Nullable`1[System.TypeCode]])\r\n" })]
- [DataRow(3, new[] { "" })]
- public void NotifyCanExecuteChangedTest(int iVal, string[] asExp)
- {
- (iVal switch
- {
- 1 => _testCommand1.NotifyCanExecuteChanged,
- 2 => _testCommand2.NotifyCanExecuteChanged,
- 3 => _testCommand3.NotifyCanExecuteChanged,
- _ => (Action)(() => { })
- }).Invoke();
- Assert.AreEqual(asExp[0],DebugLog);
- }
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace MVVM.ViewModel.Tests;
+
+[TestClass()]
+public class DelegateCommandTests : BaseTestViewModel
+{
+ DelegateCommand _testCommand1 =null!;
+ DelegateCommand _testCommand3 = null!;
+ DelegateCommand _testCommand2 = null!;
+
+ [TestInitialize()]
+ public void Init()
+ {
+ _testCommand1 = new((o) => DoLog($"Execute({nameof(_testCommand1)},{o})"));
+ _testCommand3 = new((o) => DoLog($"Execute({nameof(_testCommand3)},{o})"),canCmd3);
+ _testCommand2 = new(
+ (o) => DoLog($"Execute({nameof(_testCommand2)},{o})"),
+ canCmd2);
+ _testCommand1.CanExecuteChanged += OnCanExecuteChanged;
+ _testCommand2.CanExecuteChanged += OnCanExecuteChanged;
+// _testCommand3.CanExecuteChanged += OnCanExecuteChanged;
+ }
+
+ private void OnCanExecuteChanged(object? sender, EventArgs e)
+ => DoLog($"CanExecuteChanged({sender})");
+
+ private bool canCmd2(TypeCode? o)
+ => o is not null and not (TypeCode.Object or TypeCode.Empty or TypeCode.DBNull);
+
+ private bool canCmd3(object? o)
+ => o is not null and not (TypeCode.Object or TypeCode.Empty or TypeCode.DBNull);
+
+
+ [TestMethod()]
+ [DataRow(0, null, false)]
+ [DataRow(1,TypeCode.Boolean,true)]
+ [DataRow(1, TypeCode.Empty, true)]
+ [DataRow(1, TypeCode.String, true)]
+ [DataRow(1, null, true)]
+ [DataRow(2, TypeCode.Boolean, true)]
+ [DataRow(2, TypeCode.Empty, false)]
+ [DataRow(2, TypeCode.String, true)]
+ [DataRow(2, null, false)]
+ [DataRow(3, TypeCode.Boolean, true)]
+ [DataRow(3, TypeCode.Empty, false)]
+ [DataRow(3, TypeCode.String, true)]
+ [DataRow(3, null, false)]
+ public void CanExecuteTest(int iVal,TypeCode? tC,bool xExp)
+ {
+ var f = iVal switch {
+ 1 => _testCommand1.CanExecute,
+ 2 => _testCommand2.CanExecute,
+ 3 => _testCommand3.CanExecute,
+ _ => (Func)(o => false) };
+ Assert.AreEqual(xExp,f(tC));
+ }
+
+ [TestMethod()]
+ [DataRow(0, null, new[] { ""})]
+ [DataRow(1, TypeCode.Boolean, new[] { "Execute(_testCommand1,Boolean)\r\n" })]
+ [DataRow(1, TypeCode.Empty, new[] { "Execute(_testCommand1,Empty)\r\n" })]
+ [DataRow(1, TypeCode.String, new[] { "Execute(_testCommand1,String)\r\n" })]
+ [DataRow(1, null, new[] { "Execute(_testCommand1,)\r\n" })]
+ [DataRow(2, TypeCode.Boolean, new[] { "Execute(_testCommand2,Boolean)\r\n" })]
+ [DataRow(2, TypeCode.Empty, new[] { "Execute(_testCommand2,Empty)\r\n" })]
+ [DataRow(2, TypeCode.String, new[] { "Execute(_testCommand2,String)\r\n" })]
+ [DataRow(2, null, new[] { "Execute(_testCommand2,)\r\n" })]
+ [DataRow(3, TypeCode.Boolean, new[] { "Execute(_testCommand3,Boolean)\r\n" })]
+ [DataRow(3, TypeCode.Empty, new[] { "Execute(_testCommand3,Empty)\r\n" })]
+ [DataRow(3, TypeCode.String, new[] { "Execute(_testCommand3,String)\r\n" })]
+ [DataRow(3, null, new[] { "Execute(_testCommand3,)\r\n" })]
+ public void ExecuteTest(int iVal, TypeCode? tC, string[] asExp)
+ {
+ (iVal switch
+ {
+ 1 => _testCommand1.Execute,
+ 2 => _testCommand2.Execute,
+ 3 => _testCommand3.Execute,
+ _ => (Action)(o => { })
+ }).Invoke(tC);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ [TestMethod()]
+ [DataRow(0, new[] { "" })]
+ [DataRow(1, new[] { "CanExecuteChanged(MVVM.ViewModel.DelegateCommand)\r\n" })]
+ [DataRow(2, new[] { "CanExecuteChanged(MVVM.ViewModel.DelegateCommand`1[System.Nullable`1[System.TypeCode]])\r\n" })]
+ [DataRow(3, new[] { "" })]
+ public void NotifyCanExecuteChangedTest(int iVal, string[] asExp)
+ {
+ (iVal switch
+ {
+ 1 => _testCommand1.NotifyCanExecuteChanged,
+ 2 => _testCommand2.NotifyCanExecuteChanged,
+ 3 => _testCommand3.NotifyCanExecuteChanged,
+ _ => (Action)(() => { })
+ }).Invoke();
+ Assert.AreEqual(asExp[0],DebugLog);
+ }
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/GetResult.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/GetResult.cs
similarity index 96%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/GetResult.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/GetResult.cs
index 001a39346..139e0b66f 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/GetResult.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/GetResult.cs
@@ -1,41 +1,41 @@
-// ***********************************************************************
-// Assembly : MVVM_36_ComToolKtSavesWork_netTests
-// Author : Mir
-// Created : 05-14-2023
-//
-// Last Modified By : Mir
-// Last Modified On : 05-14-2023
-// ***********************************************************************
-//
-// Copyright © JC-Soft 2023
-//
-//
-// ***********************************************************************
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-
-namespace MVVM.ViewModel.Tests;
-
-public class GetResult : IGetResult
-{
- private readonly Dictionary> _Dic=new();
-
- public int Count => _Dic.Count;
- public object? Get(object[] objects, [CallerMemberName] string proc = "")
- {
- if (_Dic.TryGetValue(proc??"", out var f))
- return f(objects);
- else
- return null;
- }
-
- public void Register(string proc, Func fResultFct)
- {
- if (_Dic.ContainsKey(proc))
- _Dic[proc] = fResultFct;
- else
- _Dic.Add(proc, fResultFct);
- }
-
+// ***********************************************************************
+// Assembly : MVVM_36_ComToolKtSavesWork_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace MVVM.ViewModel.Tests;
+
+public class GetResult : IGetResult
+{
+ private readonly Dictionary> _Dic=new();
+
+ public int Count => _Dic.Count;
+ public object? Get(object[] objects, [CallerMemberName] string proc = "")
+ {
+ if (_Dic.TryGetValue(proc??"", out var f))
+ return f(objects);
+ else
+ return null;
+ }
+
+ public void Register(string proc, Func fResultFct)
+ {
+ if (_Dic.ContainsKey(proc))
+ _Dic[proc] = fResultFct;
+ else
+ _Dic.Add(proc, fResultFct);
+ }
+
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/GetResultTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/GetResultTests.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/GetResultTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/GetResultTests.cs
index b8a1f5003..2c224b858 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/GetResultTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/GetResultTests.cs
@@ -1,72 +1,72 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace MVVM.ViewModel.Tests;
-
-[TestClass]
-public class GetResultTests
-{
-#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
- GetResult testClass;
-#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
-
- [TestInitialize]
- public void Init() {
- testClass = new();
- }
-
- [TestMethod]
- public void SetupTest() {
- Assert.IsNotNull(testClass);
- Assert.IsInstanceOfType(testClass,typeof(GetResult));
- Assert.IsInstanceOfType(testClass,typeof(IGetResult));
- Assert.AreEqual(0,testClass.Count);
- }
-
- [TestMethod]
- public void RegisterTest()
- {
- testClass.Register("Test1",(o)=>$"Test1({string.Join(", ",o)})");
- Assert.AreEqual(1, testClass.Count);
- testClass.Register("Test2", (o) => $"Test2({string.Join(", ", o)})");
- Assert.AreEqual(2, testClass.Count);
- testClass.Register("Test3", (o) => null);
- Assert.AreEqual(3, testClass.Count);
- testClass.Register("Test3", (o) => $"Test3({string.Join(", ", o)})");
- Assert.AreEqual(3, testClass.Count);
- }
-
- [TestMethod]
- [DataRow("Test1", new[] {"Hello" }, "Test1(Hello)")]
- [DataRow("Test1", new[] { "Hello", "World" }, "Test1(Hello, World)")]
- [DataRow("Test1", new[] { "Hello", "World","!" }, "Test1(Hello, World, !)")]
- public void Test1(string _, object[] param, string? sExp) {
- Assert.IsNull(testClass.Get(param));
- RegisterTest();
- Assert.AreEqual(sExp,testClass.Get(param));
- Assert.AreEqual(3, testClass.Count);
- }
-
- [TestMethod]
- [DataRow("Test3", new[] { "Hello" }, "Test3(Hello)")]
- [DataRow("Test2", new[] { "Hello", "World" }, "Test2(Hello, World)")]
- [DataRow("Test1", new[] { "Hello", "new", "World" }, "Test1(Hello, new, World)")]
- public void GetTest(string name, object[] param, string? sExp)
- {
- Assert.IsNull(testClass.Get(param,name));
- RegisterTest();
- Assert.AreEqual(sExp, testClass.Get(param,name));
- Assert.AreEqual(3, testClass.Count);
- }
-
- [TestMethod]
- [DataRow(null, new[] { "Hello" }, null)]
- [DataRow("Test0", new[] { "Hello", "World" }, null)]
- [DataRow("", new[] { "Hello", "new", "World" }, null)]
- public void GetTest2(string name, object[] param, string? sExp)
- {
- Assert.IsNull(testClass.Get(param, name));
- RegisterTest();
- Assert.AreEqual(sExp, testClass.Get(param,name));
- Assert.AreEqual(3, testClass.Count);
- }
-}
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace MVVM.ViewModel.Tests;
+
+[TestClass]
+public class GetResultTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ GetResult testClass;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init() {
+ testClass = new();
+ }
+
+ [TestMethod]
+ public void SetupTest() {
+ Assert.IsNotNull(testClass);
+ Assert.IsInstanceOfType(testClass,typeof(GetResult));
+ Assert.IsInstanceOfType(testClass,typeof(IGetResult));
+ Assert.AreEqual(0,testClass.Count);
+ }
+
+ [TestMethod]
+ public void RegisterTest()
+ {
+ testClass.Register("Test1",(o)=>$"Test1({string.Join(", ",o)})");
+ Assert.AreEqual(1, testClass.Count);
+ testClass.Register("Test2", (o) => $"Test2({string.Join(", ", o)})");
+ Assert.AreEqual(2, testClass.Count);
+ testClass.Register("Test3", (o) => null);
+ Assert.AreEqual(3, testClass.Count);
+ testClass.Register("Test3", (o) => $"Test3({string.Join(", ", o)})");
+ Assert.AreEqual(3, testClass.Count);
+ }
+
+ [TestMethod]
+ [DataRow("Test1", new[] {"Hello" }, "Test1(Hello)")]
+ [DataRow("Test1", new[] { "Hello", "World" }, "Test1(Hello, World)")]
+ [DataRow("Test1", new[] { "Hello", "World","!" }, "Test1(Hello, World, !)")]
+ public void Test1(string _, object[] param, string? sExp) {
+ Assert.IsNull(testClass.Get(param));
+ RegisterTest();
+ Assert.AreEqual(sExp,testClass.Get(param));
+ Assert.AreEqual(3, testClass.Count);
+ }
+
+ [TestMethod]
+ [DataRow("Test3", new[] { "Hello" }, "Test3(Hello)")]
+ [DataRow("Test2", new[] { "Hello", "World" }, "Test2(Hello, World)")]
+ [DataRow("Test1", new[] { "Hello", "new", "World" }, "Test1(Hello, new, World)")]
+ public void GetTest(string name, object[] param, string? sExp)
+ {
+ Assert.IsNull(testClass.Get(param,name));
+ RegisterTest();
+ Assert.AreEqual(sExp, testClass.Get(param,name));
+ Assert.AreEqual(3, testClass.Count);
+ }
+
+ [TestMethod]
+ [DataRow(null, new[] { "Hello" }, null)]
+ [DataRow("Test0", new[] { "Hello", "World" }, null)]
+ [DataRow("", new[] { "Hello", "new", "World" }, null)]
+ public void GetTest2(string name, object[] param, string? sExp)
+ {
+ Assert.IsNull(testClass.Get(param, name));
+ RegisterTest();
+ Assert.AreEqual(sExp, testClass.Get(param,name));
+ Assert.AreEqual(3, testClass.Count);
+ }
+}
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/IDebugLog.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/IDebugLog.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/IDebugLog.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/IDebugLog.cs
index 4ecf24ce8..4fa3629b5 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/IDebugLog.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/IDebugLog.cs
@@ -1,21 +1,21 @@
-// ***********************************************************************
-// Assembly : MVVM_36_ComToolKtSavesWork_netTests
-// Author : Mir
-// Created : 05-14-2023
-//
-// Last Modified By : Mir
-// Last Modified On : 05-14-2023
-// ***********************************************************************
-//
-// Copyright © JC-Soft 2023
-//
-//
-// ***********************************************************************
-namespace MVVM.ViewModel.Tests;
-
-public interface IDebugLog
-{
- void DoLog(string message);
- void ClearLog();
- string DebugLog { get; }
+// ***********************************************************************
+// Assembly : MVVM_36_ComToolKtSavesWork_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+namespace MVVM.ViewModel.Tests;
+
+public interface IDebugLog
+{
+ void DoLog(string message);
+ void ClearLog();
+ string DebugLog { get; }
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/IGetResult.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/IGetResult.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/IGetResult.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/IGetResult.cs
index 1fe987a94..c8542f12a 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/IGetResult.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/IGetResult.cs
@@ -1,23 +1,23 @@
-// ***********************************************************************
-// Assembly : MVVM_36_ComToolKtSavesWork_netTests
-// Author : Mir
-// Created : 05-14-2023
-//
-// Last Modified By : Mir
-// Last Modified On : 05-14-2023
-// ***********************************************************************
-//
-// Copyright © JC-Soft 2023
-//
-//
-// ***********************************************************************
-using System;
-using System.Runtime.CompilerServices;
-
-namespace MVVM.ViewModel.Tests;
-
-public interface IGetResult
-{
- object? Get( object[] objects, [CallerMemberName] string proc="");
- void Register(string proc, Func fesultFct);
+// ***********************************************************************
+// Assembly : MVVM_36_ComToolKtSavesWork_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using System;
+using System.Runtime.CompilerServices;
+
+namespace MVVM.ViewModel.Tests;
+
+public interface IGetResult
+{
+ object? Get( object[] objects, [CallerMemberName] string proc="");
+ void Register(string proc, Func fesultFct);
}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/NotificationObjectCTTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/NotificationObjectCTTests.cs
similarity index 97%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/NotificationObjectCTTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/NotificationObjectCTTests.cs
index 85092a9ff..8efc88943 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/NotificationObjectCTTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/NotificationObjectCTTests.cs
@@ -1,332 +1,332 @@
-// ***********************************************************************
-// Assembly : MVVM_BaseLibTests
-// Author : Mir
-// Created : 05-22-2023
-//
-// Last Modified By : Mir
-// Last Modified On : 05-23-2023
-// ***********************************************************************
-//
-// Copyright JC-Soft 2023
-//
-//
-// ***********************************************************************
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.ComponentModel;
-using System.Linq;
-
-///
-/// The ViewModel namespace.
-///
-///
-namespace MVVM.ViewModel.Tests;
-
-///
-/// Defines test class PropertyTests.
-///
-[TestClass()]
-public class NotificationObjectCTTests : NotificationObjectCT
-{
- ///
- /// Enum eValidReact
- ///
- ///
- public enum eValidReact
- {
- ///
- /// The ok
- ///
- ///
- OK = 0,
- ///
- /// The nio
- ///
- ///
- NIO,
- ///
- /// The general exception
- ///
- ///
- GeneralException,
- ///
- /// The argument exception
- ///
- ///
- ArgumentException,
- }
-
- ///
- /// The test string
- ///
- ///
- private string _testString="";
- ///
- /// The test int
- ///
- ///
- private int _testInt;
- ///
- /// The test float
- ///
- ///
- private float _testFloat;
- ///
- /// The test double
- ///
- ///
- private double _testDouble;
-
- ///
- /// The value react
- ///
- ///
- private eValidReact valReact=eValidReact.OK;
-
- ///
- /// The debug result
- ///
- ///
- private string DebugResult ="";
-
- ///
- /// Gets or sets the test string.
- ///
- /// The test string.
- ///
- public string TestString { get => _testString;set=>SetProperty(ref _testString,value); }
-
- ///
- /// Gets or sets the test string0a.
- ///
- /// The test string0a.
- ///
- public string TestString0a { get => _testString; set => ExecPropSetter((v) => _testString = v, _testString, value); }
- ///
- /// Gets or sets the test string2a.
- ///
- /// The test string2a.
- ///
- public string TestString2a { get => _testString; set => ExecPropSetter((v) => _testString = v, _testString, value, StringAct); }
- ///
- /// Gets or sets the test string6a.
- ///
- /// The test string6a.
- ///
- public string TestString6a { get => _testString; set
- => ExecPropSetter((v)=>_testString=v, _testString, value, new string[] { nameof(TestString), nameof(TestString) },null, StringAct);}
- ///
- /// Gets or sets the test string7a.
- ///
- /// The test string7a.
- ///
- public string TestString7a { get => _testString; set
- => ExecPropSetter((v)=>_testString=v, _testString, value, new string[] { nameof(TestString), nameof(TestString) }, ValidateString, StringAct);}
-
- ///
- /// Gets or sets the test int.
- ///
- /// The test int.
- ///
- public int TestInt { get => _testInt; set => SetProperty(ref _testInt, value); }
- ///
- /// Gets or sets the test float.
- ///
- /// The test float.
- ///
- public float TestFloat { get => _testFloat; set => SetProperty(ref _testFloat, value); }
- ///
- /// Gets or sets the test double.
- ///
- /// The test double.
- ///
- public double TestDouble { get => _testDouble; set => SetProperty(ref _testDouble, value); }
-
- ///
- /// Strings the act.
- ///
- /// The arg1.
- /// The arg2.
- /// A general exception occurred
- /// Argument ({arg2}) not valid!
- ///
- private void StringAct(string arg1, string arg2)
- {
- DebugResult += $"StrAct: {arg1}; {arg2}{Environment.NewLine}";
- var _=valReact switch
- {
- eValidReact.GeneralException => throw new Exception("A general exception occurred"),
- eValidReact.ArgumentException => throw new ArgumentException($"Argument ({arg2}) not valid!"),
- _ => (object?)null,
- };
- }
-
- ///
- /// Validates the string.
- ///
- /// The arg1.
- /// true if XXXX, false otherwise.
- /// A general exception occurred
- /// Argument ({arg1}) not valid!
- ///
- private bool ValidateString(string arg1)
- {
- DebugResult += $"Validate: {arg1}, React:{valReact}{Environment.NewLine}";
- return valReact switch
- {
- eValidReact.OK => true,
- eValidReact.NIO => false,
- eValidReact.GeneralException => throw new Exception("A general exception occurred"),
- eValidReact.ArgumentException => throw new ArgumentException($"Argument ({arg1}) not valid!"),
- _ => false,
- };
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- public NotificationObjectCTTests()
- {
- }
-
- ///
- /// Clears this instance.
- ///
- ///
- private void Clear()
- {
- _testString = String.Empty;
- _testInt = 0;
- _testFloat = 0f;
- _testDouble = 0d;
- DebugResult = "";
- PropertyChanged -= OnPropertyChanged;
- }
-
- ///
- /// Handles the event.
- ///
- /// The sender.
- /// The instance containing the event data.
- ///
- private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
- => DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName!)?.GetValue(sender)}{Environment.NewLine}";
-
- ///
- /// Initializes this instance.
- ///
- ///
- [TestInitialize]
- public void Init()
- {
- Clear();
- PropertyChanged += OnPropertyChanged;
- }
-
- ///
- /// Defines the test method TestRaise.
- ///
- ///
- [TestMethod()]
- public void TestRaise()
- {
- PropertyChanged -= OnPropertyChanged;
- OnPropertyChanged("Test");
- OnPropertyChanged("Test2");
- Assert.AreEqual("", DebugResult);
- }
-
- ///
- /// Tests the string property.
- ///
- /// The name.
- /// The i ts.
- /// The s value.
- /// The e react.
- /// The s exp.
- /// The s deb exp.
- ///
- [TestMethod]
- [TestProperty("Author","J.C.")]
- [DataRow("00 Empty",0,"",eValidReact.OK,"","")]
- [DataRow("01-Test", 0, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\n")]
- public void TestStringProp(string name,int iTs,string sVal, eValidReact eReact, string sExp,string sDebExp)
- {
- valReact = eReact;
- bool xCh = sVal != _testString;
- bool eRIsEx = eReact == eValidReact.GeneralException || eReact == eValidReact.ArgumentException;
- bool xITsHasVl = new[]{ 1,3,5,7}.Contains(iTs);
- Action Setter = iTs switch
- {
- _ => (s) => TestString = s
- };
- if (xCh && xITsHasVl && eReact == eValidReact.GeneralException)
- Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
- else if (xCh && xITsHasVl && eReact == eValidReact.ArgumentException)
- Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
- else
- Setter(sVal);
-
- Assert.AreEqual(sExp, _testString, $"{name}.Result");
- Assert.AreEqual(sDebExp, DebugResult, $"{name}.DebRes");
- }
-
- ///
- /// Tests the string2 property.
- ///
- /// The name.
- /// The i ts.
- /// The s value.
- /// The e react.
- /// The s exp.
- /// The s deb exp.
- ///
- [TestMethod]
- [TestProperty("Author", "J.C.")]
- [DataRow("00 Empty", 0, "", eValidReact.OK, "", new string[] { "" })]
- [DataRow("01-Test", 0, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString0a:Test\r\n" })]
- [DataRow("20-1 Empty", 2, "", eValidReact.OK, "", new string[] { "" })]
- [DataRow("20-2 Test", 2, "", eValidReact.NIO, "", new string[] { "" })]
- [DataRow("21-1 Test", 2, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString2a:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("21-2 Test2", 2, "Test2", eValidReact.NIO, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString2a:Test2\r\nStrAct: ; Test2\r\n" })]
- [DataRow("21-3 GEx", 2, "Test", eValidReact.GeneralException, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString2a:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("21-4 AEx", 2, "Test2", eValidReact.ArgumentException, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString2a:Test2\r\nStrAct: ; Test2\r\n" })]
- [DataRow("60-1 Empty", 6, "", eValidReact.OK, "", new string[] { "" })]
- [DataRow("60-2 Test ", 6, "", eValidReact.NIO, "", new string[] { "" })]
- [DataRow("61-1 Test ", 6, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString6a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("61-2 Test2", 6, "Test2", eValidReact.NIO, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString6a:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test2\r\nStrAct: ; Test2\r\n" })]
- [DataRow("61-3 GEx ", 6, "Test", eValidReact.GeneralException, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString6a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("70-1 Empty", 7, "", eValidReact.OK, "", new string[] { "" })]
- [DataRow("70-2 Test ", 7, "", eValidReact.NIO, "", new string[] { "" })]
- [DataRow("70-2 Test ", 7, "", eValidReact.GeneralException, "", new string[] { "" })]
- [DataRow("71-1 Empty", 7, "Test", eValidReact.OK, "Test", new string[] { "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString7a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("71-2 Test ", 7, "Test", eValidReact.NIO, "", new string[] { "Validate: Test, React:NIO\r\n" })]
- [DataRow("71-3 GEx ", 7, "Test", eValidReact.GeneralException, "", new string[] { "Validate: Test, React:GeneralException\r\n" })]
- [DataRow("71-4 AEx ", 7, "Test", eValidReact.ArgumentException, "",new string[] { "Validate: Test, React:ArgumentException\r\n" })]
- public void TestString2Prop(string name, int iTs, string sVal, eValidReact eReact, string sExp, string[] sDebExp)
- {
- valReact = eReact;
- bool xCh = sVal != _testString;
- bool eRIsEx = eReact == eValidReact.GeneralException || eReact == eValidReact.ArgumentException;
- bool xITsHasVl = new[] { 1, 3, 5, 7 }.Contains(iTs);
- Action Setter = iTs switch
- {
- 2 => (s) => TestString2a = s,
- 6 => (s) => TestString6a = s,
- 7 => (s) => TestString7a = s,
- _ => (s) => TestString0a = s
- };
-
- if (xCh && xITsHasVl && eReact == eValidReact.GeneralException)
- Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
- else if (xCh && xITsHasVl && eReact == eValidReact.ArgumentException)
- Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
- else
- Setter(sVal);
-
- Assert.AreEqual(sExp, _testString, $"{name}.Result");
- Assert.AreEqual(sDebExp[0], DebugResult, $"{name}.DebRes");
- }
-
-}
+// ***********************************************************************
+// Assembly : MVVM_BaseLibTests
+// Author : Mir
+// Created : 05-22-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-23-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.ComponentModel;
+using System.Linq;
+
+///
+/// The ViewModel namespace.
+///
+///
+namespace MVVM.ViewModel.Tests;
+
+///
+/// Defines test class PropertyTests.
+///
+[TestClass()]
+public class NotificationObjectCTTests : NotificationObjectCT
+{
+ ///
+ /// Enum eValidReact
+ ///
+ ///
+ public enum eValidReact
+ {
+ ///
+ /// The ok
+ ///
+ ///
+ OK = 0,
+ ///
+ /// The nio
+ ///
+ ///
+ NIO,
+ ///
+ /// The general exception
+ ///
+ ///
+ GeneralException,
+ ///
+ /// The argument exception
+ ///
+ ///
+ ArgumentException,
+ }
+
+ ///
+ /// The test string
+ ///
+ ///
+ private string _testString="";
+ ///
+ /// The test int
+ ///
+ ///
+ private int _testInt;
+ ///
+ /// The test float
+ ///
+ ///
+ private float _testFloat;
+ ///
+ /// The test double
+ ///
+ ///
+ private double _testDouble;
+
+ ///
+ /// The value react
+ ///
+ ///
+ private eValidReact valReact=eValidReact.OK;
+
+ ///
+ /// The debug result
+ ///
+ ///
+ private string DebugResult ="";
+
+ ///
+ /// Gets or sets the test string.
+ ///
+ /// The test string.
+ ///
+ public string TestString { get => _testString;set=>SetProperty(ref _testString,value); }
+
+ ///
+ /// Gets or sets the test string0a.
+ ///
+ /// The test string0a.
+ ///
+ public string TestString0a { get => _testString; set => ExecPropSetter((v) => _testString = v, _testString, value); }
+ ///
+ /// Gets or sets the test string2a.
+ ///
+ /// The test string2a.
+ ///
+ public string TestString2a { get => _testString; set => ExecPropSetter((v) => _testString = v, _testString, value, StringAct); }
+ ///
+ /// Gets or sets the test string6a.
+ ///
+ /// The test string6a.
+ ///
+ public string TestString6a { get => _testString; set
+ => ExecPropSetter((v)=>_testString=v, _testString, value, new string[] { nameof(TestString), nameof(TestString) },null, StringAct);}
+ ///
+ /// Gets or sets the test string7a.
+ ///
+ /// The test string7a.
+ ///
+ public string TestString7a { get => _testString; set
+ => ExecPropSetter((v)=>_testString=v, _testString, value, new string[] { nameof(TestString), nameof(TestString) }, ValidateString, StringAct);}
+
+ ///
+ /// Gets or sets the test int.
+ ///
+ /// The test int.
+ ///
+ public int TestInt { get => _testInt; set => SetProperty(ref _testInt, value); }
+ ///
+ /// Gets or sets the test float.
+ ///
+ /// The test float.
+ ///
+ public float TestFloat { get => _testFloat; set => SetProperty(ref _testFloat, value); }
+ ///
+ /// Gets or sets the test double.
+ ///
+ /// The test double.
+ ///
+ public double TestDouble { get => _testDouble; set => SetProperty(ref _testDouble, value); }
+
+ ///
+ /// Strings the act.
+ ///
+ /// The arg1.
+ /// The arg2.
+ /// A general exception occurred
+ /// Argument ({arg2}) not valid!
+ ///
+ private void StringAct(string arg1, string arg2)
+ {
+ DebugResult += $"StrAct: {arg1}; {arg2}{Environment.NewLine}";
+ var _=valReact switch
+ {
+ eValidReact.GeneralException => throw new Exception("A general exception occurred"),
+ eValidReact.ArgumentException => throw new ArgumentException($"Argument ({arg2}) not valid!"),
+ _ => (object?)null,
+ };
+ }
+
+ ///
+ /// Validates the string.
+ ///
+ /// The arg1.
+ /// true if XXXX, false otherwise.
+ /// A general exception occurred
+ /// Argument ({arg1}) not valid!
+ ///
+ private bool ValidateString(string arg1)
+ {
+ DebugResult += $"Validate: {arg1}, React:{valReact}{Environment.NewLine}";
+ return valReact switch
+ {
+ eValidReact.OK => true,
+ eValidReact.NIO => false,
+ eValidReact.GeneralException => throw new Exception("A general exception occurred"),
+ eValidReact.ArgumentException => throw new ArgumentException($"Argument ({arg1}) not valid!"),
+ _ => false,
+ };
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public NotificationObjectCTTests()
+ {
+ }
+
+ ///
+ /// Clears this instance.
+ ///
+ ///
+ private void Clear()
+ {
+ _testString = String.Empty;
+ _testInt = 0;
+ _testFloat = 0f;
+ _testDouble = 0d;
+ DebugResult = "";
+ PropertyChanged -= OnPropertyChanged;
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ ///
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ => DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName!)?.GetValue(sender)}{Environment.NewLine}";
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ Clear();
+ PropertyChanged += OnPropertyChanged;
+ }
+
+ ///
+ /// Defines the test method TestRaise.
+ ///
+ ///
+ [TestMethod()]
+ public void TestRaise()
+ {
+ PropertyChanged -= OnPropertyChanged;
+ OnPropertyChanged("Test");
+ OnPropertyChanged("Test2");
+ Assert.AreEqual("", DebugResult);
+ }
+
+ ///
+ /// Tests the string property.
+ ///
+ /// The name.
+ /// The i ts.
+ /// The s value.
+ /// The e react.
+ /// The s exp.
+ /// The s deb exp.
+ ///
+ [TestMethod]
+ [TestProperty("Author","J.C.")]
+ [DataRow("00 Empty",0,"",eValidReact.OK,"","")]
+ [DataRow("01-Test", 0, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\n")]
+ public void TestStringProp(string name,int iTs,string sVal, eValidReact eReact, string sExp,string sDebExp)
+ {
+ valReact = eReact;
+ bool xCh = sVal != _testString;
+ bool eRIsEx = eReact == eValidReact.GeneralException || eReact == eValidReact.ArgumentException;
+ bool xITsHasVl = new[]{ 1,3,5,7}.Contains(iTs);
+ Action Setter = iTs switch
+ {
+ _ => (s) => TestString = s
+ };
+ if (xCh && xITsHasVl && eReact == eValidReact.GeneralException)
+ Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
+ else if (xCh && xITsHasVl && eReact == eValidReact.ArgumentException)
+ Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
+ else
+ Setter(sVal);
+
+ Assert.AreEqual(sExp, _testString, $"{name}.Result");
+ Assert.AreEqual(sDebExp, DebugResult, $"{name}.DebRes");
+ }
+
+ ///
+ /// Tests the string2 property.
+ ///
+ /// The name.
+ /// The i ts.
+ /// The s value.
+ /// The e react.
+ /// The s exp.
+ /// The s deb exp.
+ ///
+ [TestMethod]
+ [TestProperty("Author", "J.C.")]
+ [DataRow("00 Empty", 0, "", eValidReact.OK, "", new string[] { "" })]
+ [DataRow("01-Test", 0, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString0a:Test\r\n" })]
+ [DataRow("20-1 Empty", 2, "", eValidReact.OK, "", new string[] { "" })]
+ [DataRow("20-2 Test", 2, "", eValidReact.NIO, "", new string[] { "" })]
+ [DataRow("21-1 Test", 2, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString2a:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("21-2 Test2", 2, "Test2", eValidReact.NIO, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString2a:Test2\r\nStrAct: ; Test2\r\n" })]
+ [DataRow("21-3 GEx", 2, "Test", eValidReact.GeneralException, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString2a:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("21-4 AEx", 2, "Test2", eValidReact.ArgumentException, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString2a:Test2\r\nStrAct: ; Test2\r\n" })]
+ [DataRow("60-1 Empty", 6, "", eValidReact.OK, "", new string[] { "" })]
+ [DataRow("60-2 Test ", 6, "", eValidReact.NIO, "", new string[] { "" })]
+ [DataRow("61-1 Test ", 6, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString6a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("61-2 Test2", 6, "Test2", eValidReact.NIO, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString6a:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test2\r\nStrAct: ; Test2\r\n" })]
+ [DataRow("61-3 GEx ", 6, "Test", eValidReact.GeneralException, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString6a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("70-1 Empty", 7, "", eValidReact.OK, "", new string[] { "" })]
+ [DataRow("70-2 Test ", 7, "", eValidReact.NIO, "", new string[] { "" })]
+ [DataRow("70-2 Test ", 7, "", eValidReact.GeneralException, "", new string[] { "" })]
+ [DataRow("71-1 Empty", 7, "Test", eValidReact.OK, "Test", new string[] { "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString7a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectCTTests, p:TestString:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("71-2 Test ", 7, "Test", eValidReact.NIO, "", new string[] { "Validate: Test, React:NIO\r\n" })]
+ [DataRow("71-3 GEx ", 7, "Test", eValidReact.GeneralException, "", new string[] { "Validate: Test, React:GeneralException\r\n" })]
+ [DataRow("71-4 AEx ", 7, "Test", eValidReact.ArgumentException, "",new string[] { "Validate: Test, React:ArgumentException\r\n" })]
+ public void TestString2Prop(string name, int iTs, string sVal, eValidReact eReact, string sExp, string[] sDebExp)
+ {
+ valReact = eReact;
+ bool xCh = sVal != _testString;
+ bool eRIsEx = eReact == eValidReact.GeneralException || eReact == eValidReact.ArgumentException;
+ bool xITsHasVl = new[] { 1, 3, 5, 7 }.Contains(iTs);
+ Action Setter = iTs switch
+ {
+ 2 => (s) => TestString2a = s,
+ 6 => (s) => TestString6a = s,
+ 7 => (s) => TestString7a = s,
+ _ => (s) => TestString0a = s
+ };
+
+ if (xCh && xITsHasVl && eReact == eValidReact.GeneralException)
+ Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
+ else if (xCh && xITsHasVl && eReact == eValidReact.ArgumentException)
+ Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
+ else
+ Setter(sVal);
+
+ Assert.AreEqual(sExp, _testString, $"{name}.Result");
+ Assert.AreEqual(sDebExp[0], DebugResult, $"{name}.DebRes");
+ }
+
+}
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/NotificationObjectTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/NotificationObjectTests.cs
similarity index 98%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/NotificationObjectTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/NotificationObjectTests.cs
index 98bb55058..6b5ec5c7d 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModel/NotificationObjectTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/ViewModels/NotificationObjectTests.cs
@@ -1,237 +1,237 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.ComponentModel;
-using System.Linq;
-
-namespace MVVM.ViewModel.Tests;
-
-///
-/// Defines test class PropertyTests.
-///
-[TestClass()]
-public class NotificationObjectTests : NotificationObject
-{
- public enum eValidReact
- {
- OK=0,
- NIO,
- GeneralException,
- ArgumentException,
- }
-
- private string _testString="";
- private int _testInt;
- private float _testFloat;
- private double _testDouble;
-
- private eValidReact valReact=eValidReact.OK;
-
- private string DebugResult ="";
-
- public string TestString { get => _testString;set=>SetProperty(ref _testString,value); }
- public string TestString1 { get => _testString; set => SetProperty(ref _testString, value, ValidateString); }
- public string TestString2 { get => _testString; set => SetProperty(ref _testString, value, StringAct); }
- public string TestString3 { get => _testString; set => SetProperty(ref _testString, value, ValidateString, StringAct); }
- public string TestString4 { get => _testString; set => SetProperty(ref _testString, value, new string[] {nameof(TestString),nameof(TestString2) }); }
- public string TestString5 { get => _testString; set
- => SetProperty(ref _testString, value, new string[] { nameof(TestString), nameof(TestString1) },ValidateString); }
- public string TestString6 { get => _testString; set
- => SetProperty(ref _testString, value, new string[] { nameof(TestString), nameof(TestString1) },StringAct); }
- public string TestString7 { get => _testString; set
- => SetProperty(ref _testString, value, new string[] { nameof(TestString), nameof(TestString1) }, ValidateString, StringAct); }
-
- public string TestString0a { get => _testString; set => ExecPropSetter((v) => _testString = v, _testString, value); }
- public string TestString2a { get => _testString; set => ExecPropSetter((v) => _testString = v, _testString, value, StringAct); }
- public string TestString6a { get => _testString; set
- => ExecPropSetter((v)=>_testString=v, _testString, value, new string[] { nameof(TestString), nameof(TestString1) },null, StringAct);}
- public string TestString7a { get => _testString; set
- => ExecPropSetter((v)=>_testString=v, _testString, value, new string[] { nameof(TestString), nameof(TestString1) }, ValidateString, StringAct);}
-
- public int TestInt { get => _testInt; set => SetProperty(ref _testInt, value); }
- public float TestFloat { get => _testFloat; set => SetProperty(ref _testFloat, value); }
- public double TestDouble { get => _testDouble; set => SetProperty(ref _testDouble, value); }
-
- private void StringAct(string arg1, string arg2)
- {
- DebugResult += $"StrAct: {arg1}; {arg2}{Environment.NewLine}";
- var _=valReact switch
- {
- eValidReact.GeneralException => throw new Exception("A general exception occurred"),
- eValidReact.ArgumentException => throw new ArgumentException($"Argument ({arg2}) not valid!"),
- _ => (object?)null,
- };
- }
-
- private bool ValidateString(string arg1)
- {
- DebugResult += $"Validate: {arg1}, React:{valReact}{Environment.NewLine}";
- return valReact switch
- {
- eValidReact.OK => true,
- eValidReact.NIO => false,
- eValidReact.GeneralException => throw new Exception("A general exception occurred"),
- eValidReact.ArgumentException => throw new ArgumentException($"Argument ({arg1}) not valid!"),
- _ => false,
- };
- }
-
- public NotificationObjectTests()
- {
- }
-
- private void Clear()
- {
- _testString = String.Empty;
- _testInt = 0;
- _testFloat = 0f;
- _testDouble = 0d;
- DebugResult = "";
- PropertyChanged -= OnPropertyChanged;
- }
-
- private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
- => DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName!)?.GetValue(sender)}{Environment.NewLine}";
-
- [TestInitialize]
- public void Init()
- {
- Clear();
- PropertyChanged += OnPropertyChanged;
- }
-
- [TestMethod()]
- public void TestRaise()
- {
- PropertyChanged -= OnPropertyChanged;
- RaisePropertyChanged("Test");
- RaisePropertyChanged("Test","Test2");
- Assert.AreEqual("", DebugResult);
- }
-
- [TestMethod]
- [TestProperty("Author","J.C.")]
- [DataRow("00 Empty",0,"",eValidReact.OK,"","")]
- [DataRow("01-Test", 0, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\n")]
- [DataRow("10-1 Empty", 1, "", eValidReact.OK, "", "")]
- [DataRow("10-2 Test" , 1, "", eValidReact.NIO, "", "")]
- [DataRow("10-2 Test" , 1, "", eValidReact.GeneralException, "", "")]
- [DataRow("11-1 Empty", 1, "Test", eValidReact.OK, "Test", "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\n")]
- [DataRow("11-2 Test" , 1, "Test", eValidReact.NIO, "", "Validate: Test, React:NIO\r\n")]
- [DataRow("11-3 GEx" , 1, "Test", eValidReact.GeneralException, "", "Validate: Test, React:GeneralException\r\n")]
- [DataRow("11-4 AEx" , 1, "Test", eValidReact.ArgumentException, "", "Validate: Test, React:ArgumentException\r\n")]
- [DataRow("20-1 Empty", 2, "", eValidReact.OK, "", "")]
- [DataRow("20-2 Test", 2, "", eValidReact.NIO, "", "")]
- [DataRow("21-1 Test", 2, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test\r\nStrAct: ; Test\r\n")]
- [DataRow("21-2 Test2", 2, "Test2", eValidReact.NIO, "Test2", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test2\r\nStrAct: ; Test2\r\n")]
- [DataRow("21-3 GEx", 2, "Test", eValidReact.GeneralException, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test\r\nStrAct: ; Test\r\n")]
- [DataRow("21-4 AEx", 2, "Test2", eValidReact.ArgumentException, "Test2", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test2\r\nStrAct: ; Test2\r\n")]
- [DataRow("30-1 Empty", 3, "", eValidReact.OK, "", "")]
- [DataRow("30-2 Test", 3, "", eValidReact.NIO, "", "")]
- [DataRow("30-2 Test", 3, "", eValidReact.GeneralException, "", "")]
- [DataRow("31-1 Empty", 3, "Test", eValidReact.OK, "Test", "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString3:Test\r\nStrAct: ; Test\r\n")]
- [DataRow("31-2 Test", 3, "Test", eValidReact.NIO, "", "Validate: Test, React:NIO\r\n")]
- [DataRow("31-3 GEx", 3, "Test", eValidReact.GeneralException, "", "Validate: Test, React:GeneralException\r\n")]
- [DataRow("31-4 AEx", 3, "Test", eValidReact.ArgumentException, "", "Validate: Test, React:ArgumentException\r\n")]
- [DataRow("32-1 Test2", 3, "Test2", eValidReact.OK, "Test2", "Validate: Test2, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString3:Test2\r\nStrAct: ; Test2\r\n")]
- [DataRow("40-1 Empty", 4, "", eValidReact.OK, "", "")]
- [DataRow("40-2 Test " , 4, "", eValidReact.NIO, "", "")]
- [DataRow("41-1 Test " , 4, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString4:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test\r\n")]
- [DataRow("41-2 Test2", 4, "Test2", eValidReact.NIO, "Test2", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString4:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test2\r\n")]
- [DataRow("50-1 Empty", 5, "", eValidReact.OK, "", "")]
- [DataRow("50-2 Test ", 5, "", eValidReact.NIO, "", "")]
- [DataRow("50-2 Test ", 5, "", eValidReact.GeneralException, "", "")]
- [DataRow("51-1 Empty", 5, "Test", eValidReact.OK, "Test", "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString5:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\n")]
- [DataRow("51-2 Test ", 5, "Test", eValidReact.NIO, "", "Validate: Test, React:NIO\r\n")]
- [DataRow("51-3 GEx ", 5, "Test", eValidReact.GeneralException, "", "Validate: Test, React:GeneralException\r\n")]
- [DataRow("51-4 AEx ", 5, "Test", eValidReact.ArgumentException, "", "Validate: Test, React:ArgumentException\r\n")]
- [DataRow("60-1 Empty", 6, "", eValidReact.OK, "", "")]
- [DataRow("60-2 Test ", 6, "", eValidReact.NIO, "", "")]
- [DataRow("61-1 Test ", 6, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n")]
- [DataRow("61-2 Test2", 6, "Test2", eValidReact.NIO, "Test2", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test2\r\nStrAct: ; Test2\r\n")]
- [DataRow("70-1 Empty", 7, "", eValidReact.OK, "", "")]
- [DataRow("70-2 Test ", 7, "", eValidReact.NIO, "", "")]
- [DataRow("70-2 Test ", 7, "", eValidReact.GeneralException, "", "")]
- [DataRow("71-1 Empty", 7, "Test", eValidReact.OK, "Test", "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString7:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n")]
- [DataRow("71-2 Test ", 7, "Test", eValidReact.NIO, "", "Validate: Test, React:NIO\r\n")]
- [DataRow("71-3 GEx ", 7, "Test", eValidReact.GeneralException, "", "Validate: Test, React:GeneralException\r\n")]
- [DataRow("71-4 AEx ", 7, "Test", eValidReact.ArgumentException, "", "Validate: Test, React:ArgumentException\r\n")]
- public void TestStringProp(string name,int iTs,string sVal, eValidReact eReact, string sExp,string sDebExp)
- {
- valReact = eReact;
- bool xCh = sVal != _testString;
- bool eRIsEx = eReact == eValidReact.GeneralException || eReact == eValidReact.ArgumentException;
- bool xITsHasVl = new[]{ 1,3,5,7}.Contains(iTs);
- Action Setter = iTs switch
- {
- 1 => (s) => TestString1 = s,
- 2 => (s) => TestString2 = s,
- 3 => (s) => TestString3 = s,
- 4 => (s) => TestString4 = s,
- 5 => (s) => TestString5 = s,
- 6 => (s) => TestString6 = s,
- 7 => (s) => TestString7 = s,
- _ => (s) => TestString = s
- };
- if (xCh && xITsHasVl && eReact == eValidReact.GeneralException)
- Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
- else if (xCh && xITsHasVl && eReact == eValidReact.ArgumentException)
- Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
- else
- Setter(sVal);
-
- Assert.AreEqual(sExp, _testString, $"{name}.Result");
- Assert.AreEqual(sDebExp, DebugResult, $"{name}.DebRes");
- }
-
- [TestMethod]
- [TestProperty("Author", "J.C.")]
- [DataRow("00 Empty", 0, "", eValidReact.OK, "", new string[] { "" })]
- [DataRow("01-Test", 0, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString0a:Test\r\n" })]
- [DataRow("20-1 Empty", 2, "", eValidReact.OK, "", new string[] { "" })]
- [DataRow("20-2 Test", 2, "", eValidReact.NIO, "", new string[] { "" })]
- [DataRow("21-1 Test", 2, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2a:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("21-2 Test2", 2, "Test2", eValidReact.NIO, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2a:Test2\r\nStrAct: ; Test2\r\n" })]
- [DataRow("21-3 GEx", 2, "Test", eValidReact.GeneralException, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2a:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("21-4 AEx", 2, "Test2", eValidReact.ArgumentException, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2a:Test2\r\nStrAct: ; Test2\r\n" })]
- [DataRow("60-1 Empty", 6, "", eValidReact.OK, "", new string[] { "" })]
- [DataRow("60-2 Test ", 6, "", eValidReact.NIO, "", new string[] { "" })]
- [DataRow("61-1 Test ", 6, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("61-2 Test2", 6, "Test2", eValidReact.NIO, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6a:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test2\r\nStrAct: ; Test2\r\n" })]
- [DataRow("61-3 GEx ", 6, "Test", eValidReact.GeneralException, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("70-1 Empty", 7, "", eValidReact.OK, "", new string[] { "" })]
- [DataRow("70-2 Test ", 7, "", eValidReact.NIO, "", new string[] { "" })]
- [DataRow("70-2 Test ", 7, "", eValidReact.GeneralException, "", new string[] { "" })]
- [DataRow("71-1 Empty", 7, "Test", eValidReact.OK, "Test", new string[] { "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString7a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n" })]
- [DataRow("71-2 Test ", 7, "Test", eValidReact.NIO, "", new string[] { "Validate: Test, React:NIO\r\n" })]
- [DataRow("71-3 GEx ", 7, "Test", eValidReact.GeneralException, "", new string[] { "Validate: Test, React:GeneralException\r\n" })]
- [DataRow("71-4 AEx ", 7, "Test", eValidReact.ArgumentException, "",new string[] { "Validate: Test, React:ArgumentException\r\n" })]
- public void TestString2Prop(string name, int iTs, string sVal, eValidReact eReact, string sExp, string[] sDebExp)
- {
- valReact = eReact;
- bool xCh = sVal != _testString;
- bool eRIsEx = eReact == eValidReact.GeneralException || eReact == eValidReact.ArgumentException;
- bool xITsHasVl = new[] { 1, 3, 5, 7 }.Contains(iTs);
- Action Setter = iTs switch
- {
- 1 => (s) => TestString1 = s,
- 2 => (s) => TestString2a = s,
- 3 => (s) => TestString3 = s,
- 4 => (s) => TestString4 = s,
- 5 => (s) => TestString5 = s,
- 6 => (s) => TestString6a = s,
- 7 => (s) => TestString7a = s,
- _ => (s) => TestString0a = s
- };
-
- if (xCh && xITsHasVl && eReact == eValidReact.GeneralException)
- Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
- else if (xCh && xITsHasVl && eReact == eValidReact.ArgumentException)
- Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
- else
- Setter(sVal);
-
- Assert.AreEqual(sExp, _testString, $"{name}.Result");
- Assert.AreEqual(sDebExp[0], DebugResult, $"{name}.DebRes");
- }
-
-}
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.ComponentModel;
+using System.Linq;
+
+namespace MVVM.ViewModel.Tests;
+
+///
+/// Defines test class PropertyTests.
+///
+[TestClass()]
+public class NotificationObjectTests : NotificationObject
+{
+ public enum eValidReact
+ {
+ OK=0,
+ NIO,
+ GeneralException,
+ ArgumentException,
+ }
+
+ private string _testString="";
+ private int _testInt;
+ private float _testFloat;
+ private double _testDouble;
+
+ private eValidReact valReact=eValidReact.OK;
+
+ private string DebugResult ="";
+
+ public string TestString { get => _testString;set=>SetProperty(ref _testString,value); }
+ public string TestString1 { get => _testString; set => SetProperty(ref _testString, value, ValidateString); }
+ public string TestString2 { get => _testString; set => SetProperty(ref _testString, value, StringAct); }
+ public string TestString3 { get => _testString; set => SetProperty(ref _testString, value, ValidateString, StringAct); }
+ public string TestString4 { get => _testString; set => SetProperty(ref _testString, value, new string[] {nameof(TestString),nameof(TestString2) }); }
+ public string TestString5 { get => _testString; set
+ => SetProperty(ref _testString, value, new string[] { nameof(TestString), nameof(TestString1) },ValidateString); }
+ public string TestString6 { get => _testString; set
+ => SetProperty(ref _testString, value, new string[] { nameof(TestString), nameof(TestString1) },StringAct); }
+ public string TestString7 { get => _testString; set
+ => SetProperty(ref _testString, value, new string[] { nameof(TestString), nameof(TestString1) }, ValidateString, StringAct); }
+
+ public string TestString0a { get => _testString; set => ExecPropSetter((v) => _testString = v, _testString, value); }
+ public string TestString2a { get => _testString; set => ExecPropSetter((v) => _testString = v, _testString, value, StringAct); }
+ public string TestString6a { get => _testString; set
+ => ExecPropSetter((v)=>_testString=v, _testString, value, new string[] { nameof(TestString), nameof(TestString1) },null, StringAct);}
+ public string TestString7a { get => _testString; set
+ => ExecPropSetter((v)=>_testString=v, _testString, value, new string[] { nameof(TestString), nameof(TestString1) }, ValidateString, StringAct);}
+
+ public int TestInt { get => _testInt; set => SetProperty(ref _testInt, value); }
+ public float TestFloat { get => _testFloat; set => SetProperty(ref _testFloat, value); }
+ public double TestDouble { get => _testDouble; set => SetProperty(ref _testDouble, value); }
+
+ private void StringAct(string arg1, string arg2)
+ {
+ DebugResult += $"StrAct: {arg1}; {arg2}{Environment.NewLine}";
+ var _=valReact switch
+ {
+ eValidReact.GeneralException => throw new Exception("A general exception occurred"),
+ eValidReact.ArgumentException => throw new ArgumentException($"Argument ({arg2}) not valid!"),
+ _ => (object?)null,
+ };
+ }
+
+ private bool ValidateString(string arg1)
+ {
+ DebugResult += $"Validate: {arg1}, React:{valReact}{Environment.NewLine}";
+ return valReact switch
+ {
+ eValidReact.OK => true,
+ eValidReact.NIO => false,
+ eValidReact.GeneralException => throw new Exception("A general exception occurred"),
+ eValidReact.ArgumentException => throw new ArgumentException($"Argument ({arg1}) not valid!"),
+ _ => false,
+ };
+ }
+
+ public NotificationObjectTests()
+ {
+ }
+
+ private void Clear()
+ {
+ _testString = String.Empty;
+ _testInt = 0;
+ _testFloat = 0f;
+ _testDouble = 0d;
+ DebugResult = "";
+ PropertyChanged -= OnPropertyChanged;
+ }
+
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ => DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName!)?.GetValue(sender)}{Environment.NewLine}";
+
+ [TestInitialize]
+ public void Init()
+ {
+ Clear();
+ PropertyChanged += OnPropertyChanged;
+ }
+
+ [TestMethod()]
+ public void TestRaise()
+ {
+ PropertyChanged -= OnPropertyChanged;
+ RaisePropertyChanged("Test");
+ RaisePropertyChanged("Test","Test2");
+ Assert.AreEqual("", DebugResult);
+ }
+
+ [TestMethod]
+ [TestProperty("Author","J.C.")]
+ [DataRow("00 Empty",0,"",eValidReact.OK,"","")]
+ [DataRow("01-Test", 0, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\n")]
+ [DataRow("10-1 Empty", 1, "", eValidReact.OK, "", "")]
+ [DataRow("10-2 Test" , 1, "", eValidReact.NIO, "", "")]
+ [DataRow("10-2 Test" , 1, "", eValidReact.GeneralException, "", "")]
+ [DataRow("11-1 Empty", 1, "Test", eValidReact.OK, "Test", "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\n")]
+ [DataRow("11-2 Test" , 1, "Test", eValidReact.NIO, "", "Validate: Test, React:NIO\r\n")]
+ [DataRow("11-3 GEx" , 1, "Test", eValidReact.GeneralException, "", "Validate: Test, React:GeneralException\r\n")]
+ [DataRow("11-4 AEx" , 1, "Test", eValidReact.ArgumentException, "", "Validate: Test, React:ArgumentException\r\n")]
+ [DataRow("20-1 Empty", 2, "", eValidReact.OK, "", "")]
+ [DataRow("20-2 Test", 2, "", eValidReact.NIO, "", "")]
+ [DataRow("21-1 Test", 2, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test\r\nStrAct: ; Test\r\n")]
+ [DataRow("21-2 Test2", 2, "Test2", eValidReact.NIO, "Test2", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test2\r\nStrAct: ; Test2\r\n")]
+ [DataRow("21-3 GEx", 2, "Test", eValidReact.GeneralException, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test\r\nStrAct: ; Test\r\n")]
+ [DataRow("21-4 AEx", 2, "Test2", eValidReact.ArgumentException, "Test2", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test2\r\nStrAct: ; Test2\r\n")]
+ [DataRow("30-1 Empty", 3, "", eValidReact.OK, "", "")]
+ [DataRow("30-2 Test", 3, "", eValidReact.NIO, "", "")]
+ [DataRow("30-2 Test", 3, "", eValidReact.GeneralException, "", "")]
+ [DataRow("31-1 Empty", 3, "Test", eValidReact.OK, "Test", "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString3:Test\r\nStrAct: ; Test\r\n")]
+ [DataRow("31-2 Test", 3, "Test", eValidReact.NIO, "", "Validate: Test, React:NIO\r\n")]
+ [DataRow("31-3 GEx", 3, "Test", eValidReact.GeneralException, "", "Validate: Test, React:GeneralException\r\n")]
+ [DataRow("31-4 AEx", 3, "Test", eValidReact.ArgumentException, "", "Validate: Test, React:ArgumentException\r\n")]
+ [DataRow("32-1 Test2", 3, "Test2", eValidReact.OK, "Test2", "Validate: Test2, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString3:Test2\r\nStrAct: ; Test2\r\n")]
+ [DataRow("40-1 Empty", 4, "", eValidReact.OK, "", "")]
+ [DataRow("40-2 Test " , 4, "", eValidReact.NIO, "", "")]
+ [DataRow("41-1 Test " , 4, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString4:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test\r\n")]
+ [DataRow("41-2 Test2", 4, "Test2", eValidReact.NIO, "Test2", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString4:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2:Test2\r\n")]
+ [DataRow("50-1 Empty", 5, "", eValidReact.OK, "", "")]
+ [DataRow("50-2 Test ", 5, "", eValidReact.NIO, "", "")]
+ [DataRow("50-2 Test ", 5, "", eValidReact.GeneralException, "", "")]
+ [DataRow("51-1 Empty", 5, "Test", eValidReact.OK, "Test", "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString5:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\n")]
+ [DataRow("51-2 Test ", 5, "Test", eValidReact.NIO, "", "Validate: Test, React:NIO\r\n")]
+ [DataRow("51-3 GEx ", 5, "Test", eValidReact.GeneralException, "", "Validate: Test, React:GeneralException\r\n")]
+ [DataRow("51-4 AEx ", 5, "Test", eValidReact.ArgumentException, "", "Validate: Test, React:ArgumentException\r\n")]
+ [DataRow("60-1 Empty", 6, "", eValidReact.OK, "", "")]
+ [DataRow("60-2 Test ", 6, "", eValidReact.NIO, "", "")]
+ [DataRow("61-1 Test ", 6, "Test", eValidReact.OK, "Test", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n")]
+ [DataRow("61-2 Test2", 6, "Test2", eValidReact.NIO, "Test2", "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test2\r\nStrAct: ; Test2\r\n")]
+ [DataRow("70-1 Empty", 7, "", eValidReact.OK, "", "")]
+ [DataRow("70-2 Test ", 7, "", eValidReact.NIO, "", "")]
+ [DataRow("70-2 Test ", 7, "", eValidReact.GeneralException, "", "")]
+ [DataRow("71-1 Empty", 7, "Test", eValidReact.OK, "Test", "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString7:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n")]
+ [DataRow("71-2 Test ", 7, "Test", eValidReact.NIO, "", "Validate: Test, React:NIO\r\n")]
+ [DataRow("71-3 GEx ", 7, "Test", eValidReact.GeneralException, "", "Validate: Test, React:GeneralException\r\n")]
+ [DataRow("71-4 AEx ", 7, "Test", eValidReact.ArgumentException, "", "Validate: Test, React:ArgumentException\r\n")]
+ public void TestStringProp(string name,int iTs,string sVal, eValidReact eReact, string sExp,string sDebExp)
+ {
+ valReact = eReact;
+ bool xCh = sVal != _testString;
+ bool eRIsEx = eReact == eValidReact.GeneralException || eReact == eValidReact.ArgumentException;
+ bool xITsHasVl = new[]{ 1,3,5,7}.Contains(iTs);
+ Action Setter = iTs switch
+ {
+ 1 => (s) => TestString1 = s,
+ 2 => (s) => TestString2 = s,
+ 3 => (s) => TestString3 = s,
+ 4 => (s) => TestString4 = s,
+ 5 => (s) => TestString5 = s,
+ 6 => (s) => TestString6 = s,
+ 7 => (s) => TestString7 = s,
+ _ => (s) => TestString = s
+ };
+ if (xCh && xITsHasVl && eReact == eValidReact.GeneralException)
+ Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
+ else if (xCh && xITsHasVl && eReact == eValidReact.ArgumentException)
+ Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
+ else
+ Setter(sVal);
+
+ Assert.AreEqual(sExp, _testString, $"{name}.Result");
+ Assert.AreEqual(sDebExp, DebugResult, $"{name}.DebRes");
+ }
+
+ [TestMethod]
+ [TestProperty("Author", "J.C.")]
+ [DataRow("00 Empty", 0, "", eValidReact.OK, "", new string[] { "" })]
+ [DataRow("01-Test", 0, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString0a:Test\r\n" })]
+ [DataRow("20-1 Empty", 2, "", eValidReact.OK, "", new string[] { "" })]
+ [DataRow("20-2 Test", 2, "", eValidReact.NIO, "", new string[] { "" })]
+ [DataRow("21-1 Test", 2, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2a:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("21-2 Test2", 2, "Test2", eValidReact.NIO, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2a:Test2\r\nStrAct: ; Test2\r\n" })]
+ [DataRow("21-3 GEx", 2, "Test", eValidReact.GeneralException, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2a:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("21-4 AEx", 2, "Test2", eValidReact.ArgumentException, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString2a:Test2\r\nStrAct: ; Test2\r\n" })]
+ [DataRow("60-1 Empty", 6, "", eValidReact.OK, "", new string[] { "" })]
+ [DataRow("60-2 Test ", 6, "", eValidReact.NIO, "", new string[] { "" })]
+ [DataRow("61-1 Test ", 6, "Test", eValidReact.OK, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("61-2 Test2", 6, "Test2", eValidReact.NIO, "Test2", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6a:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test2\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test2\r\nStrAct: ; Test2\r\n" })]
+ [DataRow("61-3 GEx ", 6, "Test", eValidReact.GeneralException, "Test", new string[] { "OnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString6a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("70-1 Empty", 7, "", eValidReact.OK, "", new string[] { "" })]
+ [DataRow("70-2 Test ", 7, "", eValidReact.NIO, "", new string[] { "" })]
+ [DataRow("70-2 Test ", 7, "", eValidReact.GeneralException, "", new string[] { "" })]
+ [DataRow("71-1 Empty", 7, "Test", eValidReact.OK, "Test", new string[] { "Validate: Test, React:OK\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString7a:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString:Test\r\nOnPropChanged: o:MVVM.ViewModel.Tests.NotificationObjectTests, p:TestString1:Test\r\nStrAct: ; Test\r\n" })]
+ [DataRow("71-2 Test ", 7, "Test", eValidReact.NIO, "", new string[] { "Validate: Test, React:NIO\r\n" })]
+ [DataRow("71-3 GEx ", 7, "Test", eValidReact.GeneralException, "", new string[] { "Validate: Test, React:GeneralException\r\n" })]
+ [DataRow("71-4 AEx ", 7, "Test", eValidReact.ArgumentException, "",new string[] { "Validate: Test, React:ArgumentException\r\n" })]
+ public void TestString2Prop(string name, int iTs, string sVal, eValidReact eReact, string sExp, string[] sDebExp)
+ {
+ valReact = eReact;
+ bool xCh = sVal != _testString;
+ bool eRIsEx = eReact == eValidReact.GeneralException || eReact == eValidReact.ArgumentException;
+ bool xITsHasVl = new[] { 1, 3, 5, 7 }.Contains(iTs);
+ Action Setter = iTs switch
+ {
+ 1 => (s) => TestString1 = s,
+ 2 => (s) => TestString2a = s,
+ 3 => (s) => TestString3 = s,
+ 4 => (s) => TestString4 = s,
+ 5 => (s) => TestString5 = s,
+ 6 => (s) => TestString6a = s,
+ 7 => (s) => TestString7a = s,
+ _ => (s) => TestString0a = s
+ };
+
+ if (xCh && xITsHasVl && eReact == eValidReact.GeneralException)
+ Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
+ else if (xCh && xITsHasVl && eReact == eValidReact.ArgumentException)
+ Assert.ThrowsExactly(() => Setter(sVal), $"{name}.T{iTs}");
+ else
+ Setter(sVal);
+
+ Assert.AreEqual(sExp, _testString, $"{name}.Result");
+ Assert.AreEqual(sDebExp[0], DebugResult, $"{name}.DebRes");
+ }
+
+}
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/View/Extension/IoC2Tests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/Views/Extension/IoC2Tests.cs
similarity index 93%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/View/Extension/IoC2Tests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/Views/Extension/IoC2Tests.cs
index e5ed52fe6..4b1833abd 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/View/Extension/IoC2Tests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/Views/Extension/IoC2Tests.cs
@@ -3,7 +3,7 @@
using System;
using BaseLib.Helper;
-namespace MVVM.View.Extension.Tests;
+namespace MVVM.Views.Extension.Tests;
[TestClass()]
public class IoC2Tests : BaseTestViewModel
@@ -20,7 +20,7 @@ private object GetReqSrv(Type arg)
public void Init()
{
_grsOld = IoC.GetReqSrv;
- Assert.ThrowsExactly(() => _grsOld?.Invoke(null));
+ Assert.ThrowsExactly(() => _grsOld?.Invoke(null!));
IoC.GetReqSrv = GetReqSrv;
}
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/Views/ValueConverter/Bool2VisibilityConverterTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/Views/ValueConverter/Bool2VisibilityConverterTests.cs
new file mode 100644
index 000000000..62ffe6d86
--- /dev/null
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/Views/ValueConverter/Bool2VisibilityConverterTests.cs
@@ -0,0 +1,69 @@
+// ***********************************************************************
+// Assembly : MVVM.Views.ConvertersTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
+using System.Windows;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM.Views.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class Bool2VisibilityConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ Bool2VisibilityConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(true, Visibility.Visible)]
+ [DataRow(false, Visibility.Hidden)]
+ [DataRow(null, null)]
+ public void ConvertTest(object? value, Visibility expected)
+ {
+ var result = testConv.Convert(value, typeof(string), null!, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod]
+ [DataRow(true, Visibility.Visible)]
+ [DataRow(false, Visibility.Hidden)]
+ [DataRow(false, Visibility.Collapsed)]
+ [DataRow(false, null)]
+ [DataRow(false, "Hallo")]
+ public void ConvertBackTest(object? value, object expected)
+ {
+ var result = testConv.ConvertBack(expected, typeof(string), null!, CultureInfo.InvariantCulture);
+ Assert.AreEqual(value, result);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/Libraries/MVVM_BaseLibTests/View/ValueConverter/DoubleValueConverterTests.cs b/CSharpBible/Libraries/MVVM_BaseLibTests/Views/ValueConverter/DoubleValueConverterTests.cs
similarity index 98%
rename from CSharpBible/Libraries/MVVM_BaseLibTests/View/ValueConverter/DoubleValueConverterTests.cs
rename to CSharpBible/Libraries/MVVM_BaseLibTests/Views/ValueConverter/DoubleValueConverterTests.cs
index 9767c7636..1a3c3b625 100644
--- a/CSharpBible/Libraries/MVVM_BaseLibTests/View/ValueConverter/DoubleValueConverterTests.cs
+++ b/CSharpBible/Libraries/MVVM_BaseLibTests/Views/ValueConverter/DoubleValueConverterTests.cs
@@ -1,7 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Globalization;
-namespace MVVM.View.ValueConverter.Tests;
+namespace MVVM.Views.ValueConverter.Tests;
[TestClass()]
public class DoubleValueConverterTests
diff --git a/CSharpBible/Libraries/MathLIbrary/MathLibrary.csproj b/CSharpBible/Libraries/MathLIbrary/MathLibrary.csproj
index 505390051..20465d211 100644
--- a/CSharpBible/Libraries/MathLIbrary/MathLibrary.csproj
+++ b/CSharpBible/Libraries/MathLIbrary/MathLibrary.csproj
@@ -1,7 +1,7 @@
- net481;net48;net472;net462;net6.0;net7.0;net8.0;net9.0
+ net481;net48;net472;net462;net8.0
True
diff --git a/CSharpBible/Libraries/MathLIbrary/MathLibrary_net.csproj b/CSharpBible/Libraries/MathLIbrary/MathLibrary_net.csproj
deleted file mode 100644
index 78d8245f6..000000000
--- a/CSharpBible/Libraries/MathLIbrary/MathLibrary_net.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- net481;net48;net472;net462;net6.0;net7.0;net8.0;net9.0
- True
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
diff --git a/CSharpBible/Libraries/MathLIbrary/RenderImage/Angle.cs b/CSharpBible/Libraries/MathLIbrary/RenderImage/Angle.cs
new file mode 100644
index 000000000..d1d16ed09
--- /dev/null
+++ b/CSharpBible/Libraries/MathLIbrary/RenderImage/Angle.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Globalization;
+
+namespace MathLibrary.RenderImage
+{
+ public struct Angle
+ {
+ public double Value;
+
+ public double AsGrad
+ {
+ readonly get => Value * 180.0 / Math.PI;
+ set => Value = value / 180.0 * Math.PI;
+ }
+
+ public static Angle Normalize(Angle w)
+ {
+ var twoPi = 2.0 * Math.PI;
+ var v = w.Value - Math.Floor((w.Value + Math.PI) / twoPi) * twoPi;
+ return new Angle { Value = v };
+ }
+
+ public Angle Normalize() => Normalize(this);
+
+ public Angle Sum(Angle w) => Normalize(new Angle { Value = Value + w.Value });
+ public Angle Add(Angle w) { this = Sum(w); return this; }
+ public Angle Diff(Angle w) => Normalize(new Angle { Value = Value - w.Value });
+ public Angle Subt(Angle w) { this = Diff(w); return this; }
+
+ public override readonly string ToString() => string.Format(CultureInfo.InvariantCulture, "{0:F}", AsGrad);
+
+ public static implicit operator Angle(double v) => new() { Value = v };
+ public static implicit operator double(Angle a) => a.Value;
+
+ public static readonly Angle Zero = new() { Value = 0.0 };
+ }
+}
diff --git a/CSharpBible/Libraries/MathLIbrary/RenderImage/TFTriple.cs b/CSharpBible/Libraries/MathLIbrary/RenderImage/TFTriple.cs
new file mode 100644
index 000000000..d14f1e49f
--- /dev/null
+++ b/CSharpBible/Libraries/MathLIbrary/RenderImage/TFTriple.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Globalization;
+
+namespace MathLibrary.RenderImage
+{
+ public struct TFTriple
+ {
+ public double X;
+ public double Y;
+ public double Z;
+
+ public double this[int idx]
+ {
+ readonly get => idx switch { 0 => X, 1 => Y, 2 => Z, _ => throw new ArgumentOutOfRangeException(nameof(idx)) };
+ set
+ {
+ switch (idx)
+ {
+ case 0: X = value; break;
+ case 1: Y = value; break;
+ case 2: Z = value; break;
+ default: throw new ArgumentOutOfRangeException(nameof(idx));
+ }
+ }
+ }
+
+ public override string ToString() => string.Format(CultureInfo.InvariantCulture, "< {0:F}; {1:F}; {2:F} >", X, Y, Z);
+
+ public void Init(double aX, double aY, double aZ)
+ {
+ X = aX; Y = aY; Z = aZ;
+ }
+
+ public void InitDirLen(double len, double dirZ, double dirX)
+ {
+ X = Math.Cos(dirZ) * len;
+ Y = Math.Sin(dirZ) * len * Math.Cos(dirX);
+ Z = Math.Sin(dirZ) * len * Math.Sin(dirX);
+ }
+
+ public void InitTuple(in TFTuple tuple, int plane = 0)
+ {
+ switch (plane)
+ {
+ case 0: Init(tuple.X, tuple.Y, 0); break;
+ case 1: Init(0, tuple.X, tuple.Y); break;
+ case 2: Init(tuple.Y, 0, tuple.X); break;
+ default: throw new ArgumentOutOfRangeException(nameof(plane));
+ }
+ }
+
+ public TFTriple Add(in TFTriple sum) => new() { X = X + sum.X, Y = Y + sum.Y, Z = Z + sum.Z };
+ public TFTriple AddTo(in TFTriple sum) { X += sum.X; Y += sum.Y; Z += sum.Z; return this; }
+ public TFTriple Subt(in TFTriple dmin) => new() { X = X - dmin.X, Y = Y - dmin.Y, Z = Z - dmin.Z };
+ public TFTriple SubtTo(in TFTriple dmin) { X -= dmin.X; Y -= dmin.Y; Z -= dmin.Z; return this; }
+
+ public double Mul(in TFTriple fak) => X * fak.X + Y * fak.Y + Z * fak.Z;
+ public TFTriple Mul(double fak) => new() { X = X * fak, Y = Y * fak, Z = Z * fak };
+ public TFTriple MulTo(double fak) { X *= fak; Y *= fak; Z *= fak; return this; }
+ public TFTriple Divide(double divs) => new() { X = X / divs, Y = Y / divs, Z = Z / divs };
+
+ public TFTriple XMul(in TFTriple fak) => new()
+ {
+ X = Y * fak.Z - Z * fak.Y,
+ Y = Z * fak.X - X * fak.Z,
+ Z = X * fak.Y - Y * fak.X
+ };
+
+ public bool Equals(in TFTriple probe, double eps = 1e-15) =>
+ Math.Abs(probe.X - X) < eps && Math.Abs(probe.Y - Y) < eps && Math.Abs(probe.Z - Z) < eps;
+
+ public TFTriple Normalize() => this / GLen();
+
+ public static TFTriple Copy(double nx, double ny, double nz) => new() { X = nx, Y = ny, Z = nz };
+ public static TFTriple Copy(in TFTriple vect) => vect;
+ public TFTriple Copy() => this;
+
+ public double GLen() => Math.Sqrt(X * X + Y * Y + Z * Z);
+ public double MLen() => Math.Max(Math.Abs(X), Math.Max(Math.Abs(Y), Math.Abs(Z)));
+
+ public TFTuple GDir()
+ {
+ if (Y == 0.0 && Z == 0.0)
+ {
+ return X >= 0.0 ? TFTuple.Zero : TFTuple.Copy(Math.PI, 0.0);
+ }
+ else
+ {
+ var t0 = new TFTuple { X = X, Y = Math.Sqrt(Y * Y + Z * Z) }.GDir();
+ var t1 = new TFTuple { X = Y, Y = Z }.GDir();
+ return TFTuple.Copy(t0, t1);
+ }
+ }
+
+ public static TFTriple operator +(in TFTriple a, in TFTriple b) => a.Add(b);
+ public static TFTriple operator -(in TFTriple a, in TFTriple b) => a.Subt(b);
+ public static TFTriple operator -(in TFTriple a) => a.Mul(-1);
+ public static double operator *(in TFTriple a, in TFTriple b) => a.Mul(b);
+ public static TFTriple operator *(in TFTriple a, double k) => a.Mul(k);
+ public static TFTriple operator *(double k, in TFTriple a) => a.Mul(k);
+ public static TFTriple operator /(in TFTriple a, double k) => a.Divide(k);
+
+ public static double Abs(TFTriple a) => a.GLen();
+ public static double Sqr(TFTriple a) => a.X * a.X + a.Y * a.Y + a.Z * a.Z;
+
+ public static readonly TFTriple Zero = new() { X = 0.0, Y = 0.0, Z = 0.0 };
+ }
+}
diff --git a/CSharpBible/Libraries/MathLIbrary/RenderImage/TFTuple.cs b/CSharpBible/Libraries/MathLIbrary/RenderImage/TFTuple.cs
new file mode 100644
index 000000000..f12e257e6
--- /dev/null
+++ b/CSharpBible/Libraries/MathLIbrary/RenderImage/TFTuple.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Globalization;
+
+namespace MathLibrary.RenderImage
+{
+ public struct TFTuple
+ {
+ public double X;
+ public double Y;
+
+ public double this[int idx]
+ {
+ readonly get => idx switch { 0 => X, 1 => Y, _ => throw new ArgumentOutOfRangeException(nameof(idx)) };
+ set
+ {
+ switch (idx)
+ {
+ case 0: X = value; break;
+ case 1: Y = value; break;
+ default: throw new ArgumentOutOfRangeException(nameof(idx));
+ }
+ }
+ }
+
+ public override string ToString() => string.Format(CultureInfo.InvariantCulture, "<{0:F};{1:F}>", X, Y);
+
+ public void Init(double aX, double aY)
+ {
+ X = aX; Y = aY;
+ }
+
+ public void InitLenDir(double len, double dir)
+ {
+ X = Math.Cos(dir) * len;
+ Y = Math.Sin(dir) * len;
+ }
+
+ public TFTuple Sum(in TFTuple val) => new() { X = X + val.X, Y = Y + val.Y };
+ public TFTuple Add(in TFTuple val)
+ {
+ this = Sum(val);
+ return this;
+ }
+ public TFTuple Subt(in TFTuple dmin) => new() { X = X - dmin.X, Y = Y - dmin.Y };
+ public TFTuple SubtTo(in TFTuple dmin)
+ {
+ X -= dmin.X; Y -= dmin.Y; return this;
+ }
+
+ public double Mul(in TFTuple fak) => X * fak.X + Y * fak.Y; // dot product
+ public TFTuple Mul(double fak) => new() { X = X * fak, Y = Y * fak };
+ public TFTuple MulTo(double fak) { X *= fak; Y *= fak; return this; }
+ public TFTuple Divide(double divs) => new() { X = X / divs, Y = Y / divs };
+
+ // Complex-like multiplication (VMul)
+ public TFTuple VMul(in TFTuple fak) => new() { X = X * fak.X - Y * fak.Y, Y = X * fak.Y + Y * fak.X };
+
+ public bool Equals(in TFTuple probe, double eps = 1e-15) => Math.Abs(probe.X - X) < eps && Math.Abs(probe.Y - Y) < eps;
+
+ public static TFTuple Copy(double nx, double ny) => new() { X = nx, Y = ny };
+ public static TFTuple Copy(in TFTuple vect) => vect;
+ public TFTuple Copy() => this;
+
+ public double GLen() => Math.Sqrt(X * X + Y * Y);
+ public double MLen() => Math.Max(Math.Abs(X), Math.Abs(Y));
+
+ public double GDir() => Math.Atan2(Y, X);
+
+ public static TFTuple operator +(in TFTuple a, in TFTuple b) => a.Sum(b);
+ public static TFTuple operator -(in TFTuple a, in TFTuple b) => a.Subt(b);
+ public static TFTuple operator -(in TFTuple a) => a.Mul(-1);
+ public static double operator *(in TFTuple a, in TFTuple b) => a.Mul(b);
+ public static TFTuple operator *(in TFTuple a, double k) => a.Mul(k);
+ public static TFTuple operator *(double k, in TFTuple a) => a.Mul(k);
+ public static TFTuple operator /(in TFTuple a, double k) => a.Divide(k);
+
+ public static double Abs(in TFTuple a) => a.GLen();
+ public static double Sqr(in TFTuple a) => a.X * a.X + a.Y * a.Y;
+
+ public static readonly TFTuple Zero = new() { X = 0.0, Y = 0.0 };
+ }
+}
diff --git a/CSharpBible/Libraries/MathLIbrary/TwoDim/Directions2D.cs b/CSharpBible/Libraries/MathLIbrary/TwoDim/Directions2D.cs
new file mode 100644
index 000000000..efcf692b3
--- /dev/null
+++ b/CSharpBible/Libraries/MathLIbrary/TwoDim/Directions2D.cs
@@ -0,0 +1,40 @@
+// Copyright (c) JC-Soft
+// Direction helpers for grid-based movement
+using System;
+using System.Collections.Generic;
+
+namespace MathLibrary.TwoDim;
+
+///
+/// Predefined direction sets (4/8/12) and helpers akin to Pascal dir4/dir8/dir12.
+///
+public static class Directions2D
+{
+ public static readonly IntPoint[] Dir4 =
+ [new(0, 0), new(1, 0), new(0, 1), new(-1, 0), new(0, -1)];
+ public static readonly IntPoint[] Dir8 =
+ [Dir4[0], Dir4[1], new(1, 1), Dir4[2], new(-1, 1), Dir4[3], new(-1, -1), Dir4[4], new(1, -1)];
+ public static readonly IntPoint[] Dir12 =
+ [Dir4[0], new(2, 0), new(2, 1), new(1, 2), new(0, 2), new(-1, 2), new(-2, 1), new(-2, 0), new(-2, -1), new(-1, -2), new(0, -2), new(1, -2), new(2, -1)];
+
+ public static int GetDirNo(IntPoint v)
+ {
+ if (v.X == 0 && v.Y == 0) return 0;
+ var m = v.MLen();
+ if (m == 1) { for (int i = 1; i < Dir8.Length; i++) if (Dir8[i] == v) return i; }
+ else if (m == 2) { for (int i = 1; i < Dir12.Length; i++) if (Dir12[i] == v) return i; }
+ return -1;
+ }
+
+ public static int GetInvDir(int dir, int radius)
+ {
+ if (dir < 1) return dir;
+ return radius switch
+ {
+ 10 => ((dir + 2) % 6) + 1,
+ 15 => GetDirNo(Dir8[dir].Scale(-1)),
+ 22 => GetDirNo(Dir12[dir].Scale(-1)),
+ _ => dir
+ };
+ }
+}
diff --git a/CSharpBible/Libraries/MathLIbrary/TwoDim/IntPoint.cs b/CSharpBible/Libraries/MathLIbrary/TwoDim/IntPoint.cs
new file mode 100644
index 000000000..4dda5f338
--- /dev/null
+++ b/CSharpBible/Libraries/MathLIbrary/TwoDim/IntPoint.cs
@@ -0,0 +1,31 @@
+// Copyright (c) JC-Soft
+// Integer2D point with basic vector math for grid-based algorithms
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace MathLibrary.TwoDim;
+
+///
+/// Immutable integer2D point/vector.
+/// Provides basic vector arithmetic and norms used by labyrinth engine.
+///
+public readonly record struct IntPoint(int X, int Y)
+{
+ public static readonly IntPoint Zero = new(0, 0);
+ public static readonly IntPoint EX = new(1, 0);
+ public static readonly IntPoint EY = new(0, 1);
+
+ public IntPoint Add(IntPoint other) => new(X + other.X, Y + other.Y);
+ public IntPoint Sub(IntPoint other) => new(X - other.X, Y - other.Y);
+ public IntPoint Neg() => new(-X, -Y);
+ public IntPoint Scale(int k) => new(X * k, Y * k);
+
+ /// Manhattan length |x|+|y|
+ public int GLen() => Math.Abs(X) + Math.Abs(Y);
+ /// Max norm max(|x|,|y|)
+ public int MLen() => Math.Max(Math.Abs(X), Math.Abs(Y));
+
+ public int Dot(IntPoint other) => X * other.X + Y * other.Y;
+
+ public override string ToString() => $"<{X},{Y}>";
+}
diff --git a/CSharpBible/Libraries/MathLibraryTests/MathLibraryTests.csproj b/CSharpBible/Libraries/MathLibraryTests/MathLibraryTests.csproj
index a4e006e89..d8b93dba2 100644
--- a/CSharpBible/Libraries/MathLibraryTests/MathLibraryTests.csproj
+++ b/CSharpBible/Libraries/MathLibraryTests/MathLibraryTests.csproj
@@ -1,19 +1,24 @@
- net481;net48;net472;net462;net6.0;net7.0;net8.0;net9.0
+ net481;net48;net472;net462;net8.0
false
true
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/CSharpBible/Libraries/MathLibraryTests/MathLibrary_netTests.csproj b/CSharpBible/Libraries/MathLibraryTests/MathLibrary_netTests.csproj
deleted file mode 100644
index b9c7a32a8..000000000
--- a/CSharpBible/Libraries/MathLibraryTests/MathLibrary_netTests.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- net6.0;net7.0;net8.0;net9.0
- false
- true
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
diff --git a/CSharpBible/Libraries/MathLibraryTests/TwoDim/AGVHandlingTests.cs b/CSharpBible/Libraries/MathLibraryTests/TwoDim/AGVHandlingTests.cs
index f79303eb7..d6cf87766 100644
--- a/CSharpBible/Libraries/MathLibraryTests/TwoDim/AGVHandlingTests.cs
+++ b/CSharpBible/Libraries/MathLibraryTests/TwoDim/AGVHandlingTests.cs
@@ -7,7 +7,7 @@ namespace MathLibrary.TwoDim.Tests;
public class AGVHandlingTests
{
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 1)]
[DataRow(0.5 * Math.PI, -0.25, 0.6366197723675814)]
[DataRow(-0.5 * Math.PI, 0.25, 0.6366197723675814)]
@@ -25,7 +25,7 @@ public void SinX_XTest(double x, double dx, double dExp)
Assert.AreEqual(dExp - dx * _eps, AGVHandling.SinX_X(x - _eps), 1e-8, $"SinX_X({x - _eps}) == {dExp} (-1)");
}
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0, 0, 0, 0)]
[DataRow(0, 0, 0, 1, 0, 0, 0)]
[DataRow(0, 0, 0, 2, 0, 0, 0)]
@@ -56,7 +56,7 @@ public void AGVStateTest(double l, double vr, double r, double t, double xPx, do
Assert.AreEqual(xAng, ang, 1e-8, $"Angle({ang}) == {xAng}");
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(new[] { 1.0d, 0.0d }, 0.0, new[] { 1.0, 0.0 }, new[] { 1.0, 0.0 })]
[DataRow(new[] { 0.0d, -1.0d }, 1.0, new[] { 0.0, 0.0 }, new[] { 1.0, 0.0 })]
public void AGVSteeringTest(double[] afPos, double fActRot, double[] afVel, double[] afExp)
diff --git a/CSharpBible/Libraries/MathLibraryTests/TwoDim/CProcAntennaValuesTests.cs b/CSharpBible/Libraries/MathLibraryTests/TwoDim/CProcAntennaValuesTests.cs
index a100736de..165b96cb0 100644
--- a/CSharpBible/Libraries/MathLibraryTests/TwoDim/CProcAntennaValuesTests.cs
+++ b/CSharpBible/Libraries/MathLibraryTests/TwoDim/CProcAntennaValuesTests.cs
@@ -70,7 +70,7 @@ class DObjComaprer : System.Collections.IComparer
public int Compare(object? x, object? y) => x is double dx && y is double dy ? (Math.Abs(dx - dy) < 1e-8 ? 0 : -1) : x!.Equals(y) ? 0 : -1;
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(new[] {300.0, 0, 280, 0, 260, 0, 240, 0, 220, 0, 200, 0, 180, 0, 160, 0, 140, 0, 120, 0, 100, 0, 80, 0, 60, 0, 40, 0, 20, 0, 0, 0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, false, double.NaN,
new[] {300.0, 0, 280, 0, 260, 0, 240, 0, 220, 0, 200, 0, 180, 0, 160, 0, 140, 0, 120, 0, 100, 0, 80, 0, 60, 0, 40, 0, 20, 0, 0, 0,
@@ -100,7 +100,7 @@ public void HandleStdAntennaValueTest(double[] aDVal, bool xDetect, double fValu
CollectionAssert.AreEqual(adExp, ToDArr(testClass.Debug.aPoints), new DObjComaprer());
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(new[] {300.0, 0, 280, 0, 260, 0, 240, 0, 220, 0, 200, 0, 180, 0, 160, 0, 140, 0, 120, 0, 100, 0, 80, 0, 60, 0, 40, 0, 20, 0, 0, 0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, new[] { 150d, 0d }, new[] { 45d, 0d }, new[] { 1.25d, 0d }, DisplayName = "1 - Default")]
[DataRow(new[] {300.0, 1d, 280, 0, 260, 0, 240, 0, 220, 0, 200, 0, 180, 0, 160, 0, 140, 0, 120, 0, 100, 0, 80, 0, 60, 0, 40, 0, 20, 0, 0, 0,
@@ -129,7 +129,7 @@ public void Calculate3DistinctPointsTest(double[] aDVal, double[] adExp1, double
Assert.AreEqual(adExp3[1], _v3.y, 1e-8, "v3.y");
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(new[] { 150d, 0d, 45d, 0d, 1.25d, 0d }, 0d, 0d, 0.5d, new[] { 150d, 0d, 45d, 0d, 1.25d, 0d }, DisplayName = "0 - No Movement")]
[DataRow(new[] { 150d, 0d, 45d, 0d, 1.25d, 0d }, Math.PI, 1000d, 0d, new[] { 150d, 0d, 45d, 0d, 1.25d, 0d }, DisplayName = "1 - No Time")]
[DataRow(new[] { 150d, 0.0625d, 45d, 0d, 1.25d, 0d }, 0d, 100d, 0.5d, new[] { 100d, 0.0625d, -5d, 0d, -48.75d, 0d }, DisplayName = "2 - only linear")]
@@ -143,7 +143,7 @@ public void CalculateLookAheadTest(double[] aDVal, double fRot, double fTransl,
CollectionAssert.AreEqual(adExp, ToDArr(aPoints), new DObjComaprer());
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(new[] { 150d, 0d, 100d, 0d, 150d, 0d }, new[] { 150d, 0d, 0d, 0d, 0d }, 0d, 0d, double.NaN, DisplayName = "0 - Nothing")]
[DataRow(new[] { 150d, 0d, 100d, 0d, 50d, 0d }, new[] { 0d, 0d, 0d, 1d, 0d }, 0d, 0d, 0d, DisplayName = "1 - Gerade bei y=0")]
[DataRow(new[] { 150d, 10d, 100d, 10d, 50d, 10d }, new[] { 0d, 10d, 0d, 1d, 0d }, 0d, 0d, -10d, DisplayName = "1a - Gerade bei y=10")]
diff --git a/CSharpBible/Libraries/MathLibraryTests/TwoDim/Math2dTests.cs b/CSharpBible/Libraries/MathLibraryTests/TwoDim/Math2dTests.cs
index ead11507a..fa8bcf01b 100644
--- a/CSharpBible/Libraries/MathLibraryTests/TwoDim/Math2dTests.cs
+++ b/CSharpBible/Libraries/MathLibraryTests/TwoDim/Math2dTests.cs
@@ -199,7 +199,7 @@ public void eYTest()
AssertAreEqual(exp, Math2d.eY, $"{exp} = eY");
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(0.0, 0.0)]
[DataRow(1.0, 0.0)]
[DataRow(0.0, 1.0)]
@@ -214,7 +214,7 @@ public void VecTest3(double x, double y)
///
/// The x.
/// The y.
- [DataTestMethod]
+ [TestMethod]
[DataRow(0.0, 0.0)]
[DataRow(1.0, 0.0)]
[DataRow(0.0, 1.0)]
@@ -233,7 +233,7 @@ public void VecTest4(double x, double y)
/// The v2y.
/// The expx.
/// The expy.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0, 0, 0)]
[DataRow(0, 1, 0, 0, 0, 1)]
[DataRow(1, 0, 0, 0, 1, 0)]
@@ -264,7 +264,7 @@ public void AddTest(double v1x, double v1y, double v2x, double v2y, double expx,
/// The v2y.
/// The expx.
/// The expy.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0, 0, 0)]
[DataRow(0, 1, 0, 0, 0, 1)]
[DataRow(1, 0, 0, 0, 1, 0)]
@@ -292,7 +292,7 @@ public void SubtractTest(double v1x, double v1y, double v2x, double v2y, double
/// The angle.
/// The middle.
/// The exp.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0)]
[DataRow(0, Double.NaN, 0)]
[DataRow(Math.PI, 0, -Math.PI)]
@@ -322,7 +322,7 @@ public void WinkelNormTest(Double angle, Double middle, Double exp)
/// The v y.
/// The exp x.
/// The exp y.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0)]
[DataRow(0, 1, -1, 0)]
@@ -353,7 +353,7 @@ public void Rot90Test(double v_x, double v_y, double exp_x, double exp_y)
/// The v y.
/// The exp x.
/// The exp y.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0)]
[DataRow(0, 1, 0, -1)]
@@ -386,7 +386,7 @@ public void NegateTest(double v_x, double v_y, double exp_x, double exp_y)
/// if set to true [exp].
/// The exlength.
/// The exangle.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, false, 0, 0)]
[DataRow(1, 0, true, 1, 0)]
@@ -424,7 +424,7 @@ public void TryLengthAngleTest(double v_x, double v_y, bool exp, double exlength
/// The exv y.
/// The length.
/// The angle.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0)]
[DataRow(1, 0, 1, 0)]
@@ -480,7 +480,7 @@ public void MultTest(double v_x, double v_y, double s, double exp_x, double exp_
/// The v2 x.
/// The v2 y.
/// The exp.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0, 0)]
[DataRow(0, 1, 1, 0, 0)]
@@ -544,7 +544,7 @@ public void MultTest1(double v1_x, double v1_y, double v2_x, double v2_y, double
/// The v2 y.
/// The exp x.
/// The exp y.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0, 0, 0)]
[DataRow(0, 1, 1, 0, 0, 1)]
@@ -608,7 +608,7 @@ public void CMultTest(double v1_x, double v1_y, double v2_x, double v2_y, double
/// The s.
/// The v x.
/// The v y.
- [DataTestMethod()]
+ [TestMethod()]
[DynamicData(nameof(VectorMultiplyTestData))]
public void DivTest(double exp_x, double exp_y, double s, double v_x, double v_y)
{
@@ -627,7 +627,7 @@ public void DivTest(double exp_x, double exp_y, double s, double v_x, double v_y
/// The w.
/// The exp x.
/// The exp y.
- [DataTestMethod()]
+ [TestMethod()]
// Null
[DataRow(0, 0, 0, 0, 0)]
[DataRow(0, 0, 1, 0, 0)]
@@ -666,7 +666,7 @@ public void RotateTest(double v_x, double v_y, double w, double exp_x, double ex
/// The w.
/// The exp x.
/// The exp y.
- [DataTestMethod()]
+ [TestMethod()]
// Null
[DataRow(0, 0, 0, 0, 0)]
[DataRow(0, 0, 1, 0, 0)]
@@ -697,7 +697,7 @@ public void Rotate2Test(double v_x, double v_y, double w, double exp_x, double e
AssertAreEqual(v, exp.Rotate(-w), $"{v} = {exp}.Rotate({-w})");
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("00", 0, new double[] { 1, 0 }, new double[] { 1, 0 })]
[DataRow("01", 1, new double[] { 1, 0 }, new double[] { 1, 0 })]
[DataRow("02", 2, new double[] { 1, 0 }, new double[] { 0, -1 })]
@@ -721,7 +721,7 @@ public void DoTest(string name, int f, double[] dv, double[] dexp)
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("00", 0, new double[] { 1, 0 }, new double[] { 1, 0 })]
[DataRow("01", 1, new double[] { 1, 0 }, new double[] { 1, 0 })]
[DataRow("02", 2, new double[] { 1, 0 }, new double[] { 0, -1 })]
@@ -745,7 +745,7 @@ public void DoTest2(string name, int f, double[] dv, double[] dexp)
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("00", new[] { 1d, 0d }, new[] { 1d, 0d }, true, 0d)]
[DataRow("01", new double[] { 1, 0 }, new double[] { 1, 0 }, true, 0d)]
[DataRow("02", new double[] { 1, 0 }, new double[] { 0, -1 }, true, 4.71238898038469d)]
@@ -765,7 +765,7 @@ public void TryWinkel2VecTest(string name, double[] dv1, double[] dv2, bool xExp
Assert.AreEqual(fExp == 0 ? 0d : 2 * Math.PI - fExp, wnkl2, $"{name} W2");
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("00", new double[] { 1, 0 }, new double[] { 1, 0 }, true, 0d)]
[DataRow("01", new double[] { 1, 0 }, new double[] { 1, 0 }, true, 0d)]
[DataRow("02", new double[] { 1, 0 }, new double[] { 0, -1 }, true, 4.71238898038469d)]
@@ -784,7 +784,7 @@ public void TryWinkel2VecTest2(string name, double[] dv1, double[] dv2, bool xEx
Assert.AreEqual(fExp == 0 ? 0d : 2 * Math.PI - fExp, wnkl2, $"{name} W2");
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("00", new double[] { 1, 0 }, new double[] { 1, 0 }, new double[] { 1, 0 }, new double[] { 0, 0 }, 0d)]
[DataRow("01", new double[] { 1, 0 }, new double[] { 0, 0 }, new double[] { 1, 0 }, new double[] { 0, 0 }, 0d)]
[DataRow("02", new double[] { 1, 0 }, new double[] { 1, 0 }, new double[] { 0, 0 }, new double[] { 0, 0 }, 0d)]
diff --git a/CSharpBible/Libraries/MathLibraryTests/TwoDim/StTrackSegTests.cs b/CSharpBible/Libraries/MathLibraryTests/TwoDim/StTrackSegTests.cs
index a156a57dc..cb474d37d 100644
--- a/CSharpBible/Libraries/MathLibraryTests/TwoDim/StTrackSegTests.cs
+++ b/CSharpBible/Libraries/MathLibraryTests/TwoDim/StTrackSegTests.cs
@@ -16,7 +16,7 @@ public void StTrackSegTests1()
Assert.AreEqual(double.NaN, st.lrRadius);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(new[] { 1d, 2d }, new[] { 3d, 4d },5d)]
[DataRow(new[] { 6d, 5d }, new[] { 4d, 3d },2d)]
public void StTrackSegTests2(double[] adAct1, double[] adAct2,double fAct3)
diff --git a/CSharpBible/Libraries/MathLibraryTests/TwoDim/VectorTests.cs b/CSharpBible/Libraries/MathLibraryTests/TwoDim/VectorTests.cs
index 64f06cc4d..7ae5c347c 100644
--- a/CSharpBible/Libraries/MathLibraryTests/TwoDim/VectorTests.cs
+++ b/CSharpBible/Libraries/MathLibraryTests/TwoDim/VectorTests.cs
@@ -20,7 +20,7 @@ public void VectorInitTest1()
Assert.AreEqual(0, _v.y);
}
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, "( 0, 0)")]
[DataRow(0, 1, "( 0, 1)")]
[DataRow(1, 0, "( 1, 0)")]
@@ -41,7 +41,7 @@ public void VectorInitTest2(double x,double y,string _)
/// The x.
/// The y.
/// The exp.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, "( 0, 0)")]
[DataRow(0, 1, "( 0, 1)")]
[DataRow(1, 0, "( 1, 0)")]
@@ -59,7 +59,7 @@ public void ToStringTest2(Double x, Double y, string exp)
/// Gets or sets the vector as a tuple.
///
/// The vector as a tuple.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, "( 0, 0)")]
[DataRow(0, 1, "( 0, 1)")]
[DataRow(1, 0, "( 1, 0)")]
@@ -76,7 +76,7 @@ public void ValueTupleGetTest(Double x, Double y, string _)
/// Gets or sets the vector as a tuple.
///
/// The vector as a tuple.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, "( 0, 0)")]
[DataRow(0, 1, "( 0, 1)")]
[DataRow(1, 0, "( 1, 0)")]
@@ -96,7 +96,7 @@ public void ValueTupleSetTest(Double x, Double y, string _)
/// Gets or sets the vector as a tuple.
///
/// The vector as a tuple.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, "( 0, 0)")]
[DataRow(0, 1, "( 0, 1)")]
[DataRow(1, 0, "( 1, 0)")]
@@ -113,7 +113,7 @@ public void ComplexGetTest(Double x, Double y, string _)
/// Gets or sets the vector as a tuple.
///
/// The vector as a tuple.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, "( 0, 0)")]
[DataRow(0, 1, "( 0, 1)")]
[DataRow(1, 0, "( 1, 0)")]
@@ -132,7 +132,7 @@ public void ComplexSetTest(Double x, Double y, string _)
/// Gets or sets the vector as a tuple.
///
/// The vector as a tuple.
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0, 0, true)]
[DataRow(0, 1, 0, 0, false)]
[DataRow(1, 0, 0, 0, false)]
@@ -159,7 +159,7 @@ public void EqualsTest(Double x, Double y, Double x2, Double y2, bool xExp)
Assert.AreEqual(xExp, v2.Equals(v1), $"({x2},{y2}).Equals(({x},{y}))");
}
- [DataTestMethod()]
+ [TestMethod()]
[DataRow(0, 0, 0)]
[DataRow(0, 1, -4194304)]
[DataRow(1, 0, 1072693248)]
diff --git a/CSharpBible/MVVM_Tutorial/.info b/CSharpBible/MVVM_Tutorial/.info
new file mode 100644
index 000000000..5949d29a6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/.info
@@ -0,0 +1 @@
+Folder for MVVM-Tutorial
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibrary/DataAccess.cs b/CSharpBible/MVVM_Tutorial/DemoLibrary/DataAccess.cs
new file mode 100644
index 000000000..e524e4dcf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/DemoLibrary/DataAccess.cs
@@ -0,0 +1,160 @@
+// ***********************************************************************
+// Assembly : DemoLibrary
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-17-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using DemoLibrary.Models;
+using System;
+using System.Collections.Generic;
+
+namespace DemoLibrary
+{
+ ///
+ /// Class DataAccess.
+ ///
+ public static class DataAccess
+ {
+ ///
+ /// The random
+ ///
+ public static Func GetNext { get; set; } = (mn, mx) => (_rnd ??= new Random()).Next(mn, mx);
+ ///
+ /// The street addresses
+ ///
+ static readonly string[] streetAddresses = { "101 State Street", "425 Oak Avenue", "7 Wallace Way", "928 Ecclesia Place", "123 Winbur House", "543 Venture Drive", "29 Main Avenue" };
+ ///
+ /// The cities
+ ///
+ static readonly string[] cities = { "Springfield", "Wilshire", "Alexandria", "Franklin", "Clinton", "Fairview", "Boulder", "Denver", "Evangeline", "Georgetown", "Halifax", "Iconia", "Madison" };
+ ///
+ /// The states
+ ///
+ static readonly string[] states = { "AZ", "CA", "DL", "FL", "GA", "IL", "OK", "PA", "TX", "VA", "WA", "WI" };
+ ///
+ /// The zip codes
+ ///
+ static readonly string[] zipCodes = { "14121", "08904", "84732", "23410", "60095", "90210", "10456", "60618", "00926", "08701", "90280", "92335", "79936" };
+
+ ///
+ /// The first names
+ ///
+ static readonly string[] firstNames = { "Andrew", "Bob", "Carla", "Dany", "Earl", "Frank", "Georgina", "Henry", "Inez", "John", "Karl", "Lenny", "Monique", "Norbert", "Oscar", "Paula", "Quentin", "Richard", "Steve", "Urban", "Victor", "Walter", "Xavier", "Yvonne", "Zaharias" };
+ ///
+ /// The last names
+ ///
+ static readonly string[] lastNames = { "Smith", "Jones", "Garcia", "Hernandez", "Miller", "Santiago", "Thomas", "Lee", "Taylor", "Widmark" };
+ ///
+ /// The last names
+ ///
+ static readonly string[] titles = { "", "", "", "", "", "Dr.", "Prof.", "Prof. Dr.", "Dr. med.", "Dipl.Ing.", "M.D.", "M.D." };
+ ///
+ /// The alive statuses
+ ///
+ static readonly bool[] aliveStatuses = { true, false };
+ ///
+ /// The low end date
+ ///
+ static readonly DateTime lowEndDate = new(1943, 1, 1);
+ ///
+ /// The days from low date
+ ///
+ static readonly int daysFromLowDate = (DateTime.Today - lowEndDate).Days;
+ private static Random? _rnd;
+
+ // public DataAccess() {
+ // daysFromLowDate = (DateTime.Today - lowEndDate).Days;
+ // }
+
+ ///
+ /// Gets the people.
+ ///
+ /// The total.
+ /// List<PersonModel>.
+ static public List GetPeople(int total = 10)
+ {
+ var output = new List();
+ for (int i = 0; i < total; i++)
+ {
+ output.Add(GetPerson(i + 1));
+ }
+ return output;
+ }
+
+ ///
+ /// Gets the person.
+ ///
+ /// The identifier.
+ /// PersonModel.
+ static public PersonModel GetPerson(int id = 1)
+ {
+ var output = new PersonModel
+ {
+ PersonId = id,
+ FirstNames = GetRandomItem(firstNames),
+ LastName = GetRandomItem(lastNames),
+ IsAlive = GetRandomItem(aliveStatuses),
+ Title = GetRandomItem(titles),
+ DateOfBirth = GetRandomDate(),
+ AccountBalance = ((decimal)GetNext(1, 1000000) / 100),
+ };
+ output.Age = GetAgeInYears(output.DateOfBirth);
+
+ int addressCount = GetNext(1, 5);
+ for (int i = 0; i < addressCount; i++)
+ {
+ output.Addresses.Add(GetAddress((id - 1) * 5 + i + 1));
+ }
+ output.PrimaryAddress = output.Addresses[GetNext(0, addressCount - 1)];
+ return output;
+ }
+
+ ///
+ /// Gets the address.
+ ///
+ /// The identifier.
+ /// AddressModel.
+ static private AddressModel GetAddress(int id = 1)
+ {
+ var output = new AddressModel
+ {
+ AddressId = id,
+ StreetAddress = GetRandomItem(streetAddresses),
+ City = GetRandomItem(cities),
+ State = GetRandomItem(states),
+ ZipCode = GetRandomItem(zipCodes)
+ };
+ return output;
+ }
+
+ ///
+ /// Gets the random item.
+ ///
+ ///
+ /// The items.
+ /// T.
+ static private T GetRandomItem(T[] Items) => Items[GetNext(0, Items.Length)];
+ ///
+ /// Gets the random date.
+ ///
+ /// DateTime.
+ static private DateTime GetRandomDate() => lowEndDate.AddDays(GetNext(0, daysFromLowDate));
+ ///
+ /// Gets the age in years.
+ ///
+ /// The birthday.
+ /// System.Int32.
+ static private int GetAgeInYears(DateTime birthday)
+ {
+ int age = DateTime.Now.Year - birthday.Year;
+ return DateTime.Now < birthday.AddYears(age) ? age - 1 : age;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibrary/DemoLibrary.csproj b/CSharpBible/MVVM_Tutorial/DemoLibrary/DemoLibrary.csproj
new file mode 100644
index 000000000..afd183f21
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/DemoLibrary/DemoLibrary.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Library
+ net6.0;net7.0;net8.0;net481;net48;net472;net462
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/AddressModel.cs b/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/AddressModel.cs
new file mode 100644
index 000000000..85c46dc29
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/AddressModel.cs
@@ -0,0 +1,69 @@
+// ***********************************************************************
+// Assembly : DemoLibrary
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-17-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+
+namespace DemoLibrary.Models
+{
+ ///
+ /// Class AddressModel.
+ ///
+ public class AddressModel {
+ ///
+ /// Gets or sets the address identifier.
+ ///
+ /// The address identifier.
+ public int AddressId { get; set; }
+ ///
+ /// Gets or sets the additional line.
+ ///
+ /// The additional line.
+ public string AdditionalLine { get; set; } = "";
+ ///
+ /// Gets or sets the street address.
+ ///
+ /// The street address.
+ public string StreetAddress { get; set; } = "";
+ ///
+ /// Gets or sets the city.
+ ///
+ /// The city.
+ public string City { get; set; } = "";
+ ///
+ /// Gets or sets the state.
+ ///
+ /// The state.
+ public string State { get; set; } = "";
+ ///
+ /// Gets or sets the zip code.
+ ///
+ /// The zip code.
+ public string ZipCode { get; set; } = "";
+ ///
+ /// Gets or sets the country.
+ ///
+ /// The country.
+ public string Country { get; set; } = "";
+
+ ///
+ /// Gets the full address.
+ ///
+ /// The full address.
+ public string FullAddress => $"{StreetAddress}, {City}, {State} {ZipCode}, {Country}".Trim(new char[]{ ' ', ',' });
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ /// A that represents this instance.
+ public override string ToString() => FullAddress;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/Models.cd b/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/Models.cd
new file mode 100644
index 000000000..a63aaa03e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/Models.cd
@@ -0,0 +1,24 @@
+
+
+
+
+
+ AQBAAAAAABAAAMAEAEAAAIAIAAAQAAAAAAAAAAAAAAg=
+ Models\PersonModel.cs
+
+
+
+
+
+
+
+
+
+
+
+ AAAAAAAAAAAAAQAMAAAAAEAAAAIAAAABAAQABAAAABA=
+ Models\AddressModel.cs
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/PersonModel.cs b/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/PersonModel.cs
new file mode 100644
index 000000000..78d9ea9f0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/DemoLibrary/Models/PersonModel.cs
@@ -0,0 +1,79 @@
+// ***********************************************************************
+// Assembly : DemoLibrary
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Collections.Generic;
+
+namespace DemoLibrary.Models
+{
+ ///
+ /// Class PersonModel.
+ ///
+ public class PersonModel {
+ ///
+ /// Gets or sets the person identifier.
+ ///
+ /// The person identifier.
+ public int PersonId { get; set; }
+ ///
+ /// Gets or sets the title.
+ ///
+ /// The title.
+ public string Title { get; set; } = "";
+ ///
+ /// Gets or sets the first names.
+ ///
+ /// The first names.
+ public string FirstNames { get; set; } = "";
+ ///
+ /// Gets or sets the last name.
+ ///
+ /// The last name.
+ public string LastName { get; set; } = "";
+ ///
+ /// Gets or sets the age.
+ ///
+ /// The age.
+ public int Age { get; set; }
+ ///
+ /// Gets or sets the date of birth.
+ ///
+ /// The date of birth.
+ public DateTime DateOfBirth { get; set; }
+ ///
+ /// Gets or sets a value indicating whether this instance is alive.
+ ///
+ /// true if this instance is alive; otherwise, false.
+ public bool IsAlive { get; set; }
+ ///
+ /// Gets or sets the account balance.
+ ///
+ /// The account balance.
+ public decimal AccountBalance { get; set; }
+ ///
+ /// The addresses
+ ///
+ public List Addresses = new();
+ ///
+ /// Gets or sets the primary address.
+ ///
+ /// The primary address.
+ public AddressModel? PrimaryAddress { get; set; }
+ ///
+ /// Gets the full name.
+ ///
+ /// The full name.
+ public string FullName => $"{Title} {FirstNames} {LastName}".TrimStart(' ');
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibraryTests/DataAccessTests.cs b/CSharpBible/MVVM_Tutorial/DemoLibraryTests/DataAccessTests.cs
new file mode 100644
index 000000000..0031cfb15
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/DemoLibraryTests/DataAccessTests.cs
@@ -0,0 +1,59 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace DemoLibrary.Tests
+{
+ [TestClass()]
+ public class DataAccessTests
+ {
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Fügen Sie ggf. den „erforderlichen“ Modifizierer hinzu, oder deklarieren Sie den Modifizierer als NULL-Werte zulassend.
+ private Random _rnd;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Fügen Sie ggf. den „erforderlichen“ Modifizierer hinzu, oder deklarieren Sie den Modifizierer als NULL-Werte zulassend.
+
+ [TestInitialize]
+ public void Init()
+ {
+ _rnd = new Random(0);
+ _ = DataAccess.GetNext(0, 5);
+ DataAccess.GetNext = (mn, mx) => _rnd.Next(mn, mx);
+ }
+
+ [TestMethod()]
+ [DataRow(0, "Prof. Steve Taylor")]
+ [DataRow(1, "Dr. med. Richard Santiago")]
+ [DataRow(2, "M.D. Andrew Hernandez")]
+ [DataRow(3, "Dr. med. Dany Garcia")]
+ [DataRow(4, "M.D. Paula Garcia")]
+ [DataRow(5, "Quentin Lee")]
+ [DataRow(6, "Dipl.Ing. Quentin Miller")]
+ [DataRow(7, "Quentin Smith")]
+ [DataRow(8, "M.D. Henry Lee")]
+ [DataRow(9, "Dr. med. Karl Widmark")]
+ public void GetPeopleTest(int iNr, string sExp)
+ {
+ var peoples = DataAccess.GetPeople();
+ Assert.HasCount(10, peoples);
+ for (int i = 0; i < peoples.Count; i++)
+ Assert.AreEqual(sExp, peoples[iNr].FullName, $"p[{iNr}].FullName");
+ }
+
+ [TestMethod()]
+ [DataRow(0, "Prof. Steve Taylor")]
+ [DataRow(1, "Dipl.Ing. Georgina Jones")]
+ [DataRow(2, "M.D. Urban Miller")]
+ [DataRow(3, "Henry Thomas")]
+ [DataRow(4, "Victor Widmark")]
+ [DataRow(5, "Prof. Dr. Inez Garcia")]
+ [DataRow(6, "M.D. Walter Santiago")]
+ [DataRow(7, "John Taylor")]
+ [DataRow(8, "Xavier Jones")]
+ [DataRow(9, "Dr. Karl Miller")]
+ [DataRow(10, "Dr. med. Yvonne Lee")]
+ [DataRow(11, "M.D. Lenny Smith")]
+ public void GetPersonTest(int iVal, string sExp)
+ {
+ _rnd = new Random(iVal);
+ Assert.AreEqual(sExp, DataAccess.GetPerson().FullName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibraryTests/DemoLibraryTests.csproj b/CSharpBible/MVVM_Tutorial/DemoLibraryTests/DemoLibraryTests.csproj
index 137e342b8..c39723ade 100644
--- a/CSharpBible/MVVM_Tutorial/DemoLibraryTests/DemoLibraryTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/DemoLibraryTests/DemoLibraryTests.csproj
@@ -2,20 +2,26 @@
Library
- net6.0;net7.0;net8.0;net9.0;net481;net48;net472;net462
+ net8.0;net481;net48;net472;net462
false
-
+
+ $(TargetFrameworks);net9.0
+
+
+ $(TargetFrameworks);net10.0
+
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibraryTests/Models/AddressModelTests.cs b/CSharpBible/MVVM_Tutorial/DemoLibraryTests/Models/AddressModelTests.cs
new file mode 100644
index 000000000..6a87e38f6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/DemoLibraryTests/Models/AddressModelTests.cs
@@ -0,0 +1,70 @@
+// ***********************************************************************
+// Assembly : DemoLibraryTests
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-26-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using DemoLibrary.Models;
+
+namespace DemoLibraryTests.Models
+{
+ ///
+ /// Defines test class AddressModelTests.
+ ///
+ [TestClass()]
+ public class AddressModelTests {
+ ///
+ /// The c exp to string1
+ ///
+ private readonly string cExpToString1="1, 4, 2 3, 5";
+
+ ///
+ /// Converts to stringtest.
+ ///
+ /// a street.
+ /// a city.
+ /// a state.
+ /// a zip.
+ /// a.
+ /// The exp to string.
+ [TestMethod()]
+ [DataRow("1", "2", "3", "4", "5", "1, 2, 3 4, 5")]
+ [DataRow("123 test street","Los Angelos","WI","90210","", "123 test street, Los Angelos, WI 90210")]
+ [DataRow("321 ocean drive", "Santa Monica", "CA", "90901", "USA", "321 ocean drive, Santa Monica, CA 90901, USA")]
+ public void ToStringTest(string aStreet, string aCity,string aState, string aZip, string aCountry,string ExpToString) {
+ var model = new AddressModel() {
+ StreetAddress = aStreet,
+ AdditionalLine="",
+ State = aState,
+ ZipCode = aZip,
+ City = aCity,
+ Country = aCountry,
+ };
+ Assert.AreEqual(ExpToString,model.ToString());
+ }
+
+ ///
+ /// Defines the test method ToStringTest1.
+ ///
+ [TestMethod()]
+ public void ToStringTest1() {
+ var model = new AddressModel()
+ {
+ StreetAddress = "1",
+ State = "2",
+ ZipCode = "3",
+ City = "4",
+ Country = "5"
+ };
+ Assert.AreEqual(cExpToString1, model.ToString());
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/DemoLibraryTests/Models/PersonModelTests.cs b/CSharpBible/MVVM_Tutorial/DemoLibraryTests/Models/PersonModelTests.cs
new file mode 100644
index 000000000..471ec9497
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/DemoLibraryTests/Models/PersonModelTests.cs
@@ -0,0 +1,57 @@
+// ***********************************************************************
+// Assembly : DemoLibraryTests
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-19-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using DemoLibrary.Models;
+
+namespace DemoLibraryTests.Models
+{
+
+ ///
+ /// Defines test class PersonModelTests.
+ ///
+ [TestClass]
+ public class PersonModelTests {
+
+ ///
+ /// Defines the test method TestPerson.
+ ///
+ [TestMethod()]
+ public void TestPerson() {
+ PersonModel model = new();
+ Assert.IsNotNull(model);
+ }
+
+ ///
+ /// Tests the person fullName.
+ ///
+ /// a first names.
+ /// a last name.
+ /// a title.
+ /// The exp fullName.
+ [TestMethod()]
+ [DataRow("","","","")]
+ [DataRow("", "1", "", "1")]
+ [DataRow("1", "2", "", "1 2")]
+ [DataRow("1", "2", "3", "3 1 2")]
+ [DataRow("Peter", "Mustermann", "Dr.", "Dr. Peter Mustermann")]
+ public void TestPersonFullName(string aFirstNames, string aLastName, string aTitle, string expFullName) {
+ PersonModel model = new() {
+ FirstNames = aFirstNames,
+ LastName = aLastName,
+ PrimaryAddress=null,
+ Title= aTitle };
+ Assert.AreEqual(expFullName, model.FullName);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut1/App.xaml b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/App.xaml
new file mode 100644
index 000000000..bbec7a2f2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut1/App.xaml.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/App.xaml.cs
new file mode 100644
index 000000000..a9a4741d6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/App.xaml.cs
@@ -0,0 +1,24 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut1
+// Author : Mir
+// Created : 06-16-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-09-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ItemsControlTut1
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut1/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/AssemblyInfo.cs
new file mode 100644
index 000000000..2ecc86240
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut1
+// Author : Mir
+// Created : 06-16-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-09-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut1/ItemsControlTut1.csproj b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/ItemsControlTut1.csproj
new file mode 100644
index 000000000..34b415588
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/ItemsControlTut1.csproj
@@ -0,0 +1,40 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+
+
+
+
+
+
+
+ True
+ basic
+
+
+
+ False
+ full
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut1/View/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/View/MainWindow.xaml
new file mode 100644
index 000000000..ad74bc8e3
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/View/MainWindow.xaml
@@ -0,0 +1,19 @@
+
+
+
+ ItemsControl Item #1
+ ItemsControl Item #2
+ ItemsControl Item #3
+ ItemsControl Item #4
+ ItemsControl Item #5
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut1/View/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/View/MainWindow.xaml.cs
new file mode 100644
index 000000000..cb3c934ef
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/View/MainWindow.xaml.cs
@@ -0,0 +1,31 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut1
+// Author : Mir
+// Created : 06-16-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-09-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ItemsControlTut1.View
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut1/ViewModels/.info b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/ViewModels/.info
new file mode 100644
index 000000000..5949d29a6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut1/ViewModels/.info
@@ -0,0 +1 @@
+Folder for MVVM-Tutorial
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut2/App.xaml b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/App.xaml
new file mode 100644
index 000000000..9a3ed1faa
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut2/App.xaml.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/App.xaml.cs
new file mode 100644
index 000000000..9592540f4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/App.xaml.cs
@@ -0,0 +1,24 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut2
+// Author : Mir
+// Created : 06-16-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ItemsControlTut2
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut2/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/AssemblyInfo.cs
new file mode 100644
index 000000000..669988a40
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut2
+// Author : Mir
+// Created : 06-16-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-09-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut2/ItemsControlTut2.csproj b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/ItemsControlTut2.csproj
new file mode 100644
index 000000000..af53e1b4f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/ItemsControlTut2.csproj
@@ -0,0 +1,42 @@
+
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+ False
+ full
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut2/View/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/View/MainWindow.xaml
new file mode 100644
index 000000000..5355697bc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/View/MainWindow.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut2/View/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/View/MainWindow.xaml.cs
new file mode 100644
index 000000000..a754b5762
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/View/MainWindow.xaml.cs
@@ -0,0 +1,56 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut2
+// Author : Mir
+// Created : 06-16-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Collections.Generic;
+using System.Windows;
+
+namespace ItemsControlTut2.View
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ List items = new List();
+ items.Add(new TodoItem() { Title = "Complete this WPF tutorial", Completion = 45 });
+ items.Add(new TodoItem() { Title = "Learn C#", Completion = 80 });
+ items.Add(new TodoItem() { Title = "Wash the car", Completion = 0 });
+
+ icTodoList.ItemsSource = items;
+
+ }
+ }
+
+ ///
+ /// Class TodoItem.
+ ///
+ public class TodoItem {
+ ///
+ /// Gets or sets the title.
+ ///
+ /// The title.
+ public string Title { get; set; }
+ ///
+ /// Gets or sets the completion.
+ ///
+ /// The completion.
+ public int Completion { get; set; }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut2/ViewModels/.info b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/ViewModels/.info
new file mode 100644
index 000000000..5949d29a6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut2/ViewModels/.info
@@ -0,0 +1 @@
+Folder for MVVM-Tutorial
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/App.xaml b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/App.xaml
new file mode 100644
index 000000000..23b75d456
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/App.xaml.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/App.xaml.cs
new file mode 100644
index 000000000..c5a944109
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/App.xaml.cs
@@ -0,0 +1,24 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_net
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ItemsControlTut3
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/AssemblyInfo.cs
new file mode 100644
index 000000000..6af98ad8e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_net
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ItemsControlTut3.csproj b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ItemsControlTut3.csproj
new file mode 100644
index 000000000..63127f793
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ItemsControlTut3.csproj
@@ -0,0 +1,35 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+ False
+ full
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ItemsControlTut3_net.csproj b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ItemsControlTut3_net.csproj
new file mode 100644
index 000000000..3d07a1fdb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ItemsControlTut3_net.csproj
@@ -0,0 +1,36 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+ False
+ full
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/View/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/View/MainWindow.xaml
new file mode 100644
index 000000000..c921f5c55
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/View/MainWindow.xaml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/View/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/View/MainWindow.xaml.cs
new file mode 100644
index 000000000..add065065
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/View/MainWindow.xaml.cs
@@ -0,0 +1,32 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_net
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ItemsControlTut3.View
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..1fba83fa5
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,147 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_net
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System;
+using System.Collections.ObjectModel;
+
+namespace ItemsControlTut3.ViewModel
+{
+ ///
+ /// Class TodoItem.
+ /// Extends the
+ ///
+ ///
+ public class TodoItem : NotificationObject
+ {
+ #region Properties
+ #region private properties
+ ///
+ /// Storage for completion
+ ///
+ private int _completion = 0;
+ ///
+ /// Storage for title
+ ///
+ private string _Title = "";
+ #endregion
+ ///
+ /// Gets or sets the title.
+ ///
+ /// The title of the task.
+ public string Title { get => _Title; set => SetProperty(ref _Title, value); }
+ ///
+ /// Gets or sets the completion [%].
+ ///
+ /// The completion of the task [%].
+ public int Completion { get => _completion; set => SetProperty(ref _completion, value); }
+ ///
+ /// Gets or sets the do-Command.
+ ///
+ /// The do.
+ public DelegateCommand Do { get; set; }
+
+ ///
+ /// Gets or sets the Step-Command.
+ ///
+ /// The do.
+ public DelegateCommand Step { get; set; }
+
+ ///
+ /// Gets the this.
+ ///
+ /// The this.
+ public object This => this;
+ #endregion
+
+ }
+
+ ///
+ /// Class MainWindowViewModel.
+ /// Implements the
+ ///
+ ///
+ public class MainWindowViewModel: BaseViewModel
+ {
+ #region Properties
+ #region private properties
+ private string _newItem="";
+ #endregion
+
+ ///
+ /// Gets or sets the todo list.
+ ///
+ /// The todo list.
+ public ObservableCollection TodoList { get ; set; }
+ ///
+ /// Gets or sets the add item command.
+ ///
+ /// The add item command.
+ public DelegateCommand AddCommand { get; set; }
+ /// Title of the new item.
+ /// The title of the new item.
+ public string NewItem { get=>_newItem; set=>SetProperty(ref _newItem,value); }
+ #endregion
+ #region Methods
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+ TodoList = new ObservableCollection();
+ TodoList.Add(new TodoItem() { Title = "Complete this WPF tutorial", Completion = 50,Do= new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+ TodoList.Add(new TodoItem() { Title = "Learn C#", Completion = 90, Do = new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+ TodoList.Add(new TodoItem() { Title = "Wash the car", Completion = 10, Do = new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+
+ AddCommand = new DelegateCommand((o)=> { AddTodo(NewItem, 0); NewItem = ""; },(o)=>!String.IsNullOrEmpty(NewItem));
+ AddPropertyDependency(nameof(AddCommand),nameof(NewItem));
+ }
+
+ ///
+ /// Executes the "Step" action.
+ ///
+ /// The object.
+ private void StepAction(object? obj)
+ {
+ if (obj is TodoItem todo)
+ {
+ if (todo.Completion < 90)
+ todo.Completion += 10;
+ else
+ todo.Completion = 100;
+ }
+ }
+
+ ///
+ /// Executes the "Do" action.
+ ///
+ /// The object.
+ private void DoAction(object? obj)
+ {
+ if (obj is TodoItem todo)
+ {
+ todo.Completion = 100;
+ }
+ }
+
+ /// Adds an item to the the todo-List.
+ /// The title.
+ /// The completion-percentage.
+ public void AddTodo(string sTitle, int iCompl)
+ {
+ TodoList.Add(new TodoItem() { Title = sTitle, Completion = iCompl, Do = new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+ }
+ #endregion
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ViewModels/ViewModel.cd b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ViewModels/ViewModel.cd
new file mode 100644
index 000000000..d62132f33
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3/ViewModels/ViewModel.cd
@@ -0,0 +1,21 @@
+
+
+
+
+
+ AAAAAAAAAAAIAEAAAAAAAAACCIIAAAAAIAAAAAAAAAA=
+ ViewModel\MainWindowViewModel.cs
+
+
+
+
+
+ AAAAAAAAAAAAAAACAAAAAAAAAAgIAAACACAAAAAAAAI=
+ ViewModel\MainWindowViewModel.cs
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/ItemsControlTut3_netTests.csproj b/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/ItemsControlTut3_netTests.csproj
index 2a331f6b2..6fc2a9c8c 100644
--- a/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/ItemsControlTut3_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/ItemsControlTut3_netTests.csproj
@@ -2,12 +2,18 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
@@ -15,8 +21,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/MainWindowTests.cs
new file mode 100644
index 000000000..98123cb34
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/MainWindowTests.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_netTests
+// Author : Mir
+// Created : 05-13-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-13-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace ItemsControlTut3.View.Tests
+{
+ ///
+ /// Defines test class MainWindowTests.
+ ///
+ ///
+ [TestClass()]
+ public class MainWindowTests
+ {
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..af97cebf8
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut3_netTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,226 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+
+namespace ItemsControlTut3.ViewModel.Tests
+{
+ public class BaseTest
+ {
+ ///
+ /// The debug result
+ ///
+ ///
+ protected string DebugResult = "";
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ ///
+ protected void OnItemPropChanged(object? sender, PropertyChangedEventArgs e)
+ => DoLog($"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName)?.GetValue(sender)}");
+
+ protected void DoLog(string line)
+ => DebugResult += $"{line}{Environment.NewLine}";
+
+ protected void ClearResults()
+ => DebugResult = "";
+
+ protected void TestToDoItems(ETestAction eTest, object o, TodoItem testToDoItem)
+ {
+ switch (eTest)
+ {
+ case ETestAction.SetTitle:
+ testToDoItem.Title = o as string ?? "";
+ break;
+ case ETestAction.SetCompletion:
+ testToDoItem.Completion = (int)o;
+ Assert.AreEqual((int)o, testToDoItem.Completion);
+ break;
+ case ETestAction.DoStep:
+ testToDoItem.Step.Execute(testToDoItem);
+ Assert.AreEqual((int)o, testToDoItem.Completion);
+ break;
+ case ETestAction.DoStep2:
+ testToDoItem.Step.Execute(testToDoItem);
+ testToDoItem.Step.Execute(testToDoItem);
+ Assert.AreEqual((int)o, testToDoItem.Completion);
+ break;
+ case ETestAction.DoDo:
+ testToDoItem.Do.Execute(testToDoItem);
+ Assert.AreEqual((int)o, testToDoItem.Completion);
+ break;
+ }
+ }
+
+ public enum ETestAction
+ {
+ SetTitle,
+ SetCompletion,
+ DoStep,
+ DoStep2,
+ DoDo,
+ }
+ }
+
+ [TestClass()]
+ public class TodoItemTests: BaseTest
+ {
+ private TodoItem TestToDoItem;
+
+ private int ciStep = 10;
+
+ [TestInitialize()]
+ public void Init()
+ {
+ TestToDoItem = new TodoItem() { Title = "TestItem", Completion = 10, Do = new DelegateCommand(DoCommand), Step = new DelegateCommand(StepCommand) };
+ TestToDoItem.PropertyChanged += OnItemPropChanged;
+ ClearResults();
+ }
+
+
+ private void StepCommand(object? obj)
+ {
+ DoLog($"StepCommand: o:{obj}");
+ if (obj is TodoItem todo)
+ todo.Completion += ciStep;
+ }
+
+ private void DoCommand(object? obj)
+ {
+ DoLog($"DoCommand: o:{obj}");
+ if (obj is TodoItem todo)
+ todo.Completion = 100;
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(TestToDoItem);
+ Assert.IsInstanceOfType(TestToDoItem,typeof(TodoItem));
+ Assert.AreEqual(TestToDoItem, TestToDoItem.This);
+ }
+
+ [TestMethod]
+ [DataRow("Title",ETestAction.SetTitle,"ABC",new string[] { "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title:ABC\r\n" })]
+ [DataRow("Title1", ETestAction.SetTitle, "TestItem", new string[] { "" })]
+ [DataRow("Title2", ETestAction.SetTitle, "TestItem2", new string[] { "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title:TestItem2\r\n" })]
+ [DataRow("Completion", ETestAction.SetCompletion, 57, new string[] { "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:57\r\n" })]
+ [DataRow("Completion", ETestAction.SetCompletion, 10, new string[] { "" })]
+ [DataRow("Completion", ETestAction.SetCompletion, 20, new string[] { "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:20\r\n" })]
+ [DataRow("DoStep", ETestAction.DoStep, 20, new string[] { "StepCommand: o:ItemsControlTut3.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:20\r\n" })]
+ [DataRow("DoStep2", ETestAction.DoStep2, 30, new string[] { "StepCommand: o:ItemsControlTut3.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:20\r\nStepCommand: o:ItemsControlTut3.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:30\r\n" })]
+ [DataRow("DoDo", ETestAction.DoDo, 100, new string[] { "DoCommand: o:ItemsControlTut3.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:100\r\n" })]
+ public void TodoItemTest(string name,ETestAction eTest,object o,string[] sExp)
+ {
+ TestToDoItems(eTest, o, TestToDoItem);
+ Assert.AreEqual(sExp[0], DebugResult);
+ }
+
+ [TestMethod]
+ [DataRow("Title", ETestAction.SetTitle, "ABC", new string[] { "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title:ABC\r\n" })]
+ [DataRow("Title1", ETestAction.SetTitle, "TestItem", new string[] { "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title:TestItem\r\n" })]
+ [DataRow("Title2", ETestAction.SetTitle, "TestItem2", new string[] { "" })]
+ [DataRow("Completion", ETestAction.SetCompletion, 57, new string[] { "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:57\r\n" })]
+ [DataRow("Completion", ETestAction.SetCompletion, 10, new string[] { "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:10\r\n" })]
+ [DataRow("Completion", ETestAction.SetCompletion, 20, new string[] { "" })]
+ [DataRow("DoStep", ETestAction.DoStep, 30, new string[] { "StepCommand: o:ItemsControlTut3.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:30\r\n" })]
+ [DataRow("DoStep2", ETestAction.DoStep2, 40, new string[] { "StepCommand: o:ItemsControlTut3.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:30\r\nStepCommand: o:ItemsControlTut3.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:40\r\n" })]
+ [DataRow("DoDo", ETestAction.DoDo, 100, new string[] { "DoCommand: o:ItemsControlTut3.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion:100\r\n" })]
+ public void TodoItemTest2(string name, ETestAction eTest, object o, string[] sExp)
+ {
+ var testToDoItem = new TodoItem() { Title = "TestItem2", Completion = 20, Do = new DelegateCommand(DoCommand), Step = new DelegateCommand(StepCommand) };
+ testToDoItem.PropertyChanged += OnItemPropChanged;
+ TestToDoItems(eTest, o, testToDoItem);
+ Assert.AreEqual(sExp[0], DebugResult);
+ }
+ }
+
+ [TestClass()]
+ public class MainWindowViewModelTests:BaseTest
+ {
+ private MainWindowViewModel TestMainWindowViewModel;
+ private string DebugResult="";
+ private readonly string cAddTodoExp= "OnCollectionChanged: o:System.Collections.ObjectModel.ObservableCollection`1[ItemsControlTut3.ViewModel.TodoItem], p:System.Collections.Specialized.SingleItemReadOnlyList, io:-1, in:3\r\n";
+
+ private void OnItemPropChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ DebugResult += $"OnPropChanged: o:{sender}, p:{e.PropertyName}{Environment.NewLine}";
+ }
+ private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ DebugResult += $"OnCollectionChanged: o:{sender}, p:{e.NewItems}, io:{e.OldStartingIndex}, in:{e.NewStartingIndex}{Environment.NewLine}";
+ }
+
+ [TestInitialize()]
+ public void Init()
+ {
+ TestMainWindowViewModel = new MainWindowViewModel();
+ TestMainWindowViewModel.PropertyChanged += OnItemPropChanged;
+ TestMainWindowViewModel.TodoList.CollectionChanged += OnCollectionChanged;
+ foreach (var item in TestMainWindowViewModel.TodoList)
+ item.PropertyChanged += OnItemPropChanged;
+ DebugResult = "";
+ }
+
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(TestMainWindowViewModel);
+ Assert.IsInstanceOfType(TestMainWindowViewModel, typeof(MainWindowViewModel));
+ Assert.IsNotNull(TestMainWindowViewModel.TodoList);
+ Assert.IsInstanceOfType(TestMainWindowViewModel.TodoList,typeof(ObservableCollection));
+ foreach(var item in TestMainWindowViewModel.TodoList)
+ Assert.IsInstanceOfType(item, typeof(TodoItem));
+ }
+
+ [TestMethod]
+ [DataRow("Title-0", 0, ETestAction.SetTitle, "ABC", new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title\r\n"})]
+ [DataRow("Title-1", 1, ETestAction.SetTitle, "ABC", new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title\r\n"})]
+ [DataRow("Title-2", 2, ETestAction.SetTitle, "ABC", new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title\r\n"})]
+ [DataRow("Title1-0",0, ETestAction.SetTitle, "Wash the car", new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title\r\n"})]
+ [DataRow("Title1-1",1, ETestAction.SetTitle, "Wash the car", new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title\r\n"})]
+ [DataRow("Title1-2",2, ETestAction.SetTitle, "Wash the car", new string[]{ ""})]
+ [DataRow("Title2-0",0, ETestAction.SetTitle, "Learn C#", new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title\r\n"})]
+ [DataRow("Title2-1",1, ETestAction.SetTitle, "Learn C#", new string[]{ ""})]
+ [DataRow("Title2-2",2, ETestAction.SetTitle, "Learn C#", new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Title\r\n"})]
+ [DataRow("Completion-0-57", 0, ETestAction.SetCompletion, 57, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("Completion-0-10", 0, ETestAction.SetCompletion, 10, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("Completion-0-50", 0, ETestAction.SetCompletion, 50, new string[]{ ""})]
+ [DataRow("Completion-0-90", 0, ETestAction.SetCompletion, 90, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("Completion-1-57", 1, ETestAction.SetCompletion, 57, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("Completion-1-10", 1, ETestAction.SetCompletion, 10, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("Completion-1-50", 1, ETestAction.SetCompletion, 50, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("Completion-1-90", 1, ETestAction.SetCompletion, 90, new string[]{ ""})]
+ [DataRow("Completion-2-57", 2, ETestAction.SetCompletion, 57, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("Completion-2-10", 2, ETestAction.SetCompletion, 10, new string[]{ ""})]
+ [DataRow("Completion-2-50", 2, ETestAction.SetCompletion, 50, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("Completion-2-90", 2, ETestAction.SetCompletion, 90, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoStep" , 0, ETestAction.DoStep, 60, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoStep2", 0, ETestAction.DoStep2, 70, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoDo" , 0, ETestAction.DoDo, 100, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoStep" , 1, ETestAction.DoStep, 100, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoStep2", 1, ETestAction.DoStep2, 100, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoDo" , 1, ETestAction.DoDo, 100, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoStep" , 2, ETestAction.DoStep, 20, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoStep2", 2, ETestAction.DoStep2, 30, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\nOnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ [DataRow("DoDo" , 2, ETestAction.DoDo, 100, new string[]{ "OnPropChanged: o:ItemsControlTut3.ViewModel.TodoItem, p:Completion\r\n"})]
+ public void TodoItemTest(string name,int iEl, ETestAction eTest, object o, string[] sExp)
+ {
+ TestToDoItems(eTest, o, TestMainWindowViewModel.TodoList[iEl]);
+ Assert.AreEqual(sExp[0], DebugResult);
+ }
+
+ [TestMethod()]
+ public void MainWindowViewModelTest()
+ {
+ TestMainWindowViewModel.AddTodo("Drive Home", 20);
+ Assert.AreEqual(cAddTodoExp, DebugResult);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/App.xaml b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/App.xaml
new file mode 100644
index 000000000..3c8eacdb0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/App.xaml.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/App.xaml.cs
new file mode 100644
index 000000000..24528a322
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/App.xaml.cs
@@ -0,0 +1,24 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_net
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ItemsControlTut4
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/AssemblyInfo.cs
new file mode 100644
index 000000000..6af98ad8e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_net
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ItemsControlTut4.csproj b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ItemsControlTut4.csproj
new file mode 100644
index 000000000..1db0df670
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ItemsControlTut4.csproj
@@ -0,0 +1,34 @@
+
+
+
+ WinExe
+ net481;net48
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+ False
+ full
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ItemsControlTut4_net.csproj b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ItemsControlTut4_net.csproj
new file mode 100644
index 000000000..fbdec1d1f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ItemsControlTut4_net.csproj
@@ -0,0 +1,37 @@
+
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+ False
+ full
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/View/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/View/MainWindow.xaml
new file mode 100644
index 000000000..f514f3c95
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/View/MainWindow.xaml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/View/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/View/MainWindow.xaml.cs
new file mode 100644
index 000000000..441822107
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/View/MainWindow.xaml.cs
@@ -0,0 +1,32 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_net
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ItemsControlTut4.View
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..6e1bf9f6e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,148 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut3_net
+// Author : Mir
+// Created : 08-14-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System;
+using System.Collections.ObjectModel;
+
+namespace ItemsControlTut4.ViewModel
+{
+ ///
+ /// Class TodoItem.
+ /// Extends the
+ ///
+ ///
+ public class TodoItem : NotificationObject
+ {
+
+ #region Properties
+ #region private properties
+ ///
+ /// Storage for completion
+ ///
+ private int _completion = 0;
+ ///
+ /// Storage for title
+ ///
+ private string _Title = "";
+ #endregion
+ ///
+ /// Gets or sets the title.
+ ///
+ /// The title of the task.
+ public string Title { get => _Title; set => SetProperty(ref _Title, value); }
+ ///
+ /// Gets or sets the completion.
+ ///
+ /// The completion of the task [%].
+ public int Completion { get => _completion; set => SetProperty(ref _completion, value); }
+ ///
+ /// Gets or sets the do-Command.
+ ///
+ /// The commang-delegate for "Do".
+ public DelegateCommand Do { get; set; }
+
+ ///
+ /// Gets or sets the Step-Command.
+ ///
+ /// The command-delegate for the "Step".
+ public DelegateCommand Step { get; set; }
+
+ ///
+ /// Gets the this.
+ ///
+ /// The this.
+ public object This => this;
+ #endregion
+
+ }
+
+ ///
+ /// Class MainWindowViewModel.
+ /// Implements the
+ ///
+ ///
+ public class MainWindowViewModel: BaseViewModel
+ {
+ #region Properties
+ #region private properties
+ private string _newItem = "";
+ #endregion
+
+ ///
+ /// Gets or sets the todo list.
+ ///
+ /// The todo list.
+ public ObservableCollection TodoList { get ; set; }
+ /// Gets or sets the add command.
+ /// The add command.
+ public DelegateCommand AddCommand { get; set; }
+ /// Title of the new item.
+ /// The title of the new item.
+ public string NewItem { get => _newItem; set => SetProperty(ref _newItem, value); }
+ #endregion
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+ TodoList = new ObservableCollection();
+ TodoList.Add(new TodoItem() { Title = "Complete this WPF tutorial", Completion = 50,Do= new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+ TodoList.Add(new TodoItem() { Title = "Learn C#", Completion = 90, Do = new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+ TodoList.Add(new TodoItem() { Title = "Wash the car", Completion = 10, Do = new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+ TodoList.Add(new TodoItem() { Title = "Drive Home", Completion = 20, Do = new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+
+ AddCommand = new DelegateCommand((o) => { AddTodo(NewItem, 0); NewItem = ""; }, (o) => !String.IsNullOrEmpty(NewItem));
+ AddPropertyDependency(nameof(AddCommand), nameof(NewItem));
+ }
+
+ ///
+ /// Executes the "Step" action.
+ ///
+ /// Increases the completion by 10%
+ /// The object.
+ private void StepAction(object? obj)
+ {
+ if (obj is TodoItem todo)
+ {
+ if (todo.Completion < 90)
+ todo.Completion += 10;
+ else
+ todo.Completion = 100;
+ }
+ }
+
+ ///
+ /// Executes the "Do" action.
+ ///
+ /// Puts the completion of the task to 100%
+ /// The object.
+ private void DoAction(object? obj)
+ {
+ if (obj is TodoItem todo)
+ {
+ todo.Completion = 100;
+ }
+ }
+
+ /// Adds an item to the the todo-List.
+ /// The title.
+ /// The completion-percentage.
+ public void AddTodo(string sTitle, int iCompl)
+ {
+ TodoList.Add(new TodoItem() { Title = sTitle, Completion = iCompl, Do = new DelegateCommand(DoAction), Step = new DelegateCommand(StepAction) });
+ }
+ #endregion
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ViewModels/ViewModel.cd b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ViewModels/ViewModel.cd
new file mode 100644
index 000000000..e20b27bfd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4/ViewModels/ViewModel.cd
@@ -0,0 +1,21 @@
+
+
+
+
+
+ AAAAAAAAAAAIAEAAAAAAAAACCIIAAAAAIAAAAAAAAAA=
+ ViewModel\MainWindowViewModel.cs
+
+
+
+
+
+ AAAAAAAAAAAAAAACAAAAAAAAAAgIAAACACAAAAAAAAI=
+ ViewModel\MainWindowViewModel.cs
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/ItemsControlTut4_netTests.csproj b/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/ItemsControlTut4_netTests.csproj
index fd27264dc..65b2ce0fe 100644
--- a/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/ItemsControlTut4_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/ItemsControlTut4_netTests.csproj
@@ -1,12 +1,18 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
@@ -14,8 +20,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/MainWindowTests.cs
new file mode 100644
index 000000000..4018331b0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/MainWindowTests.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut4_netTests
+// Author : Mir
+// Created : 05-13-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-13-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace ItemsControlTut4.View.Tests
+{
+ ///
+ /// Defines test class MainWindowTests.
+ ///
+ ///
+ [TestClass()]
+ public class MainWindowTests
+ {
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..cca620635
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ItemsControlTut4_netTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,377 @@
+// ***********************************************************************
+// Assembly : ItemsControlTut4_netTests
+// Author : Mir
+// Created : 10-21-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 10-21-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace ItemsControlTut4.ViewModel.Tests
+{
+ ///
+ /// Class BaseTest.
+ ///
+ ///
+ public class BaseTest
+ {
+ ///
+ /// The debug result
+ ///
+ ///
+ protected string DebugResult = "";
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ ///
+ protected void OnItemPropChanged(object? sender, PropertyChangedEventArgs e)
+ => DoLog($"OnPropChanged: o:{sender}, p:{e.PropertyName}:{sender?.GetType().GetProperty(e.PropertyName)?.GetValue(sender)}");
+
+ protected void DoLog(string line)
+ => DebugResult += $"{line}{Environment.NewLine}";
+
+ protected void ClearResults()
+ => DebugResult = "";
+
+ ///
+ /// Tests to do items.
+ ///
+ /// The enum test.
+ /// The object.
+ /// The test to do item.
+ ///
+ protected void TestToDoItems(ETestAction eTest, object o, TodoItem testToDoItem)
+ {
+ switch (eTest)
+ {
+ case ETestAction.SetTitle:
+ testToDoItem.Title = o as string ?? "";
+ break;
+ case ETestAction.SetCompletion:
+ testToDoItem.Completion = (int)o;
+ Assert.AreEqual((int?)o, testToDoItem.Completion);
+ break;
+ case ETestAction.DoStep:
+ testToDoItem.Step.Execute(testToDoItem);
+ Assert.AreEqual((int?)o, testToDoItem.Completion);
+ break;
+ case ETestAction.DoStep2:
+ testToDoItem.Step.Execute(testToDoItem);
+ testToDoItem.Step.Execute(testToDoItem);
+ Assert.AreEqual((int?)o, testToDoItem.Completion);
+ break;
+ case ETestAction.DoDo:
+ testToDoItem.Do.Execute(testToDoItem);
+ Assert.AreEqual((int?)o, testToDoItem.Completion);
+ break;
+ }
+ }
+
+
+
+ ///
+ /// Enum ETestAction
+ ///
+ ///
+ public enum ETestAction
+ {
+ ///
+ /// The set title
+ ///
+ ///
+ SetTitle,
+ ///
+ /// The set completion
+ ///
+ ///
+ SetCompletion,
+ ///
+ /// The do step
+ ///
+ ///
+ DoStep,
+ ///
+ /// The do step2
+ ///
+ ///
+ DoStep2,
+ ///
+ /// The do do
+ ///
+ ///
+ DoDo,
+ }
+ }
+
+ ///
+ /// Defines test class TodoItemTests.
+ /// Implements the
+ ///
+ ///
+ ///
+ [TestClass()]
+ public class TodoItemTests : BaseTest
+ {
+ ///
+ /// The test to do item
+ ///
+ ///
+ private TodoItem TestToDoItem;
+
+ ///
+ /// The ci step
+ ///
+ ///
+ private int ciStep = 10;
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize()]
+ public void Init()
+ {
+ TestToDoItem = new TodoItem() { Title = "TestItem", Completion = 10, Do = new DelegateCommand(DoCommand), Step = new DelegateCommand(StepCommand) };
+ TestToDoItem.PropertyChanged += OnItemPropChanged;
+ ClearResults();
+ }
+
+ ///
+ /// Steps the command.
+ ///
+ /// The object.
+ ///
+ private void StepCommand(object? obj)
+ {
+ DoLog($"StepCommand: o:{obj}");
+ if (obj is TodoItem todo)
+ todo.Completion += ciStep;
+ }
+
+ ///
+ /// Does the command.
+ ///
+ /// The object.
+ ///
+ private void DoCommand(object? obj)
+ {
+ DoLog($"DoCommand: o:{obj}");
+ if (obj is TodoItem todo)
+ todo.Completion = 100;
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(TestToDoItem);
+ Assert.IsInstanceOfType(TestToDoItem, typeof(TodoItem));
+ Assert.AreEqual(TestToDoItem, TestToDoItem.This);
+ }
+
+ ///
+ /// Todoes the item test.
+ ///
+ /// The name.
+ /// The e test.
+ /// The o.
+ /// The s exp.
+ ///
+ [TestMethod]
+ [DataRow("Title", ETestAction.SetTitle, "ABC", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:ABC\r\n"})]
+ [DataRow("Title1", ETestAction.SetTitle, "TestItem", new string[]{ ""})]
+ [DataRow("Title2", ETestAction.SetTitle, "TestItem2", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:TestItem2\r\n"})]
+ [DataRow("Completion", ETestAction.SetCompletion, 57, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:57\r\n"})]
+ [DataRow("Completion", ETestAction.SetCompletion, 10, new string[]{ ""})]
+ [DataRow("Completion", ETestAction.SetCompletion, 20, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:20\r\n"})]
+ [DataRow("DoStep", ETestAction.DoStep, 20, new string[]{ "StepCommand: o:ItemsControlTut4.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:20\r\n"})]
+ [DataRow("DoStep2", ETestAction.DoStep2, 30, new string[]{ "StepCommand: o:ItemsControlTut4.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:20\r\nStepCommand: o:ItemsControlTut4.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:30\r\n"})]
+ [DataRow("DoDo", ETestAction.DoDo, 100, new string[]{ "DoCommand: o:ItemsControlTut4.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:100\r\n"})]
+ public void TodoItemTest(string name, ETestAction eTest, object o, string[] sExp)
+ {
+ TestToDoItems(eTest, o, TestToDoItem);
+ Assert.AreEqual(sExp[0], DebugResult);
+ }
+
+ ///
+ /// Todoes the item test2.
+ ///
+ /// The name.
+ /// The e test.
+ /// The o.
+ /// The s exp.
+ ///
+ [TestMethod]
+ [DataRow("Title", ETestAction.SetTitle, "ABC", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:ABC\r\n"})]
+ [DataRow("Title1", ETestAction.SetTitle, "TestItem", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:TestItem\r\n"})]
+ [DataRow("Title2", ETestAction.SetTitle, "TestItem2", new string[]{ ""})]
+ [DataRow("Completion", ETestAction.SetCompletion, 57, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:57\r\n"})]
+ [DataRow("Completion", ETestAction.SetCompletion, 10, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:10\r\n"})]
+ [DataRow("Completion", ETestAction.SetCompletion, 20, new string[]{ ""})]
+ [DataRow("DoStep", ETestAction.DoStep, 30, new string[]{ "StepCommand: o:ItemsControlTut4.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:30\r\n"})]
+ [DataRow("DoStep2", ETestAction.DoStep2, 40, new string[]{ "StepCommand: o:ItemsControlTut4.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:30\r\nStepCommand: o:ItemsControlTut4.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:40\r\n"})]
+ [DataRow("DoDo", ETestAction.DoDo, 100, new string[]{ "DoCommand: o:ItemsControlTut4.ViewModel.TodoItem\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:100\r\n"})]
+ public void TodoItemTest2(string name, ETestAction eTest, object o, string[] sExp)
+ {
+ var testToDoItem = new TodoItem() { Title = "TestItem2", Completion = 20, Do = new DelegateCommand(DoCommand), Step = new DelegateCommand(StepCommand) };
+ testToDoItem.PropertyChanged += OnItemPropChanged;
+ TestToDoItems(eTest, o, testToDoItem);
+ Assert.AreEqual(sExp[0], DebugResult);
+ }
+ }
+
+ ///
+ /// Defines test class MainWindowViewModelTests.
+ /// Implements the
+ ///
+ ///
+ ///
+ [TestClass()]
+ public class MainWindowViewModelTests : BaseTest
+ {
+ ///
+ /// The test main window view model
+ ///
+ ///
+ private MainWindowViewModel testModel;
+
+ ///
+ /// The c add todo exp
+ ///
+ ///
+ private readonly string cAddTodoExp = "OnCollectionChanged: o:System.Collections.ObjectModel.ObservableCollection`1[ItemsControlTut4.ViewModel.TodoItem], p:System.Collections.Specialized.SingleItemReadOnlyList, io:-1, in:4\r\n";
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ ///
+ private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ DebugResult += $"OnCollectionChanged: o:{sender}, p:{e.NewItems}, io:{e.OldStartingIndex}, in:{e.NewStartingIndex}{Environment.NewLine}";
+ }
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize()]
+ public void Init()
+ {
+ testModel = new MainWindowViewModel();
+ testModel.PropertyChanged += OnItemPropChanged;
+ testModel.TodoList.CollectionChanged += OnCollectionChanged;
+ foreach (var item in testModel.TodoList)
+ item.PropertyChanged += OnItemPropChanged;
+ DebugResult = "";
+ }
+
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsNotNull(testModel.TodoList);
+ Assert.IsInstanceOfType(testModel.TodoList, typeof(ObservableCollection));
+ foreach (var item in testModel.TodoList)
+ Assert.IsInstanceOfType(item, typeof(TodoItem));
+ }
+
+ ///
+ /// Todoes the item test.
+ ///
+ /// The name.
+ /// The i el.
+ /// The e test.
+ /// The o.
+ /// The s exp.
+ ///
+ [TestMethod]
+ [DataRow("Title-0", 0, ETestAction.SetTitle, "ABC", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:ABC\r\n"})]
+ [DataRow("Title-1", 1, ETestAction.SetTitle, "ABC", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:ABC\r\n" })]
+ [DataRow("Title-2", 2, ETestAction.SetTitle, "ABC", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:ABC\r\n" })]
+ [DataRow("Title1-0", 0, ETestAction.SetTitle, "Wash the car", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:Wash the car\r\n"})]
+ [DataRow("Title1-1", 1, ETestAction.SetTitle, "Wash the car", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:Wash the car\r\n" })]
+ [DataRow("Title1-2", 2, ETestAction.SetTitle, "Wash the car", new string[]{ ""})]
+ [DataRow("Title2-0", 0, ETestAction.SetTitle, "Learn C#", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:Learn C#\r\n"})]
+ [DataRow("Title2-1", 1, ETestAction.SetTitle, "Learn C#", new string[]{ ""})]
+ [DataRow("Title2-2", 2, ETestAction.SetTitle, "Learn C#", new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Title:Learn C#\r\n" })]
+ [DataRow("Completion-0-57", 0, ETestAction.SetCompletion, 57, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:57\r\n"})]
+ [DataRow("Completion-0-10", 0, ETestAction.SetCompletion, 10, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:10\r\n"})]
+ [DataRow("Completion-0-50", 0, ETestAction.SetCompletion, 50, new string[]{ ""})]
+ [DataRow("Completion-0-90", 0, ETestAction.SetCompletion, 90, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:90\r\n"})]
+ [DataRow("Completion-1-57", 1, ETestAction.SetCompletion, 57, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:57\r\n"})]
+ [DataRow("Completion-1-10", 1, ETestAction.SetCompletion, 10, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:10\r\n"})]
+ [DataRow("Completion-1-50", 1, ETestAction.SetCompletion, 50, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:50\r\n"})]
+ [DataRow("Completion-1-90", 1, ETestAction.SetCompletion, 90, new string[]{ ""})]
+ [DataRow("Completion-2-57", 2, ETestAction.SetCompletion, 57, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:57\r\n"})]
+ [DataRow("Completion-2-10", 2, ETestAction.SetCompletion, 10, new string[]{ ""})]
+ [DataRow("Completion-2-50", 2, ETestAction.SetCompletion, 50, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:50\r\n"})]
+ [DataRow("Completion-2-90", 2, ETestAction.SetCompletion, 90, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:90\r\n"})]
+ [DataRow("DoStep", 0, ETestAction.DoStep, 60, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:60\r\n"})]
+ [DataRow("DoStep2", 0, ETestAction.DoStep2, 70, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:60\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:70\r\n"})]
+ [DataRow("DoDo", 0, ETestAction.DoDo, 100, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:100\r\n"})]
+ [DataRow("DoStep", 1, ETestAction.DoStep, 100, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:100\r\n"})]
+ [DataRow("DoStep2", 1, ETestAction.DoStep2, 100, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:100\r\n"})]
+ [DataRow("DoDo", 1, ETestAction.DoDo, 100, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:100\r\n"})]
+ [DataRow("DoStep", 2, ETestAction.DoStep, 20, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:20\r\n"})]
+ [DataRow("DoStep2", 2, ETestAction.DoStep2, 30, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:20\r\nOnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:30\r\n"})]
+ [DataRow("DoDo", 2, ETestAction.DoDo, 100, new string[]{ "OnPropChanged: o:ItemsControlTut4.ViewModel.TodoItem, p:Completion:100\r\n"})]
+ public void TodoItemTest(string name, int iEl, ETestAction eTest, object o, string[] sExp)
+ {
+ TestToDoItems(eTest, o, testModel.TodoList[iEl]);
+ Assert.AreEqual(sExp[0], DebugResult);
+ }
+
+ ///
+ /// Defines the test method MainWindowViewModelTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowViewModelTest()
+ {
+ testModel.AddTodo("Clean house", 30);
+ Assert.AreEqual(cAddTodoExp, DebugResult);
+ }
+
+ [TestMethod]
+ [DataRow(null,new string[] { "OnPropChanged: o:ItemsControlTut4.ViewModel.MainWindowViewModel, p:NewItem:\r\n" })]
+ [DataRow("", new string[] { "" })]
+ [DataRow("Get this Job done !", new string[] { "OnPropChanged: o:ItemsControlTut4.ViewModel.MainWindowViewModel, p:NewItem:Get this Job done !\r\n" })]
+ public void NewItemTest(string? sVal, string[] sExp)
+ {
+ Assert.AreEqual("", testModel.NewItem);
+ testModel.NewItem = sVal;
+ Assert.AreEqual(sVal, testModel.NewItem);
+ Assert.AreEqual(sExp[0], DebugResult);
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/App.config b/CSharpBible/MVVM_Tutorial/ListBinding/App.config
new file mode 100644
index 000000000..51cf15f82
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/App.config
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <?xml version="1.0" encoding="utf-8"?>
+<ArrayOfPerson xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <Person>
+ <LastName>Care</LastName>
+ <FirstName>Joe</FirstName>
+ <Title>Dr,</Title>
+ <Id>1</Id>
+ </Person>
+</ArrayOfPerson>
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/App.xaml b/CSharpBible/MVVM_Tutorial/ListBinding/App.xaml
new file mode 100644
index 000000000..03d2a0b3d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/App.xaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/App.xaml.cs b/CSharpBible/MVVM_Tutorial/ListBinding/App.xaml.cs
new file mode 100644
index 000000000..005a3af90
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/App.xaml.cs
@@ -0,0 +1,24 @@
+// ***********************************************************************
+// Assembly : ListBinding
+// Author : Mir
+// Created : 12-23-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 12-23-2021
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ListBinding
+{
+ ///
+ /// Interaktionslogik für "App.xaml"
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/ListBinding.csproj b/CSharpBible/MVVM_Tutorial/ListBinding/ListBinding.csproj
new file mode 100644
index 000000000..ca7054128
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/ListBinding.csproj
@@ -0,0 +1,48 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/ListBinding_net.csproj b/CSharpBible/MVVM_Tutorial/ListBinding/ListBinding_net.csproj
new file mode 100644
index 000000000..7fbb1be48
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/ListBinding_net.csproj
@@ -0,0 +1,62 @@
+
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+ Designer
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/Model/IPersons.cs b/CSharpBible/MVVM_Tutorial/ListBinding/Model/IPersons.cs
new file mode 100644
index 000000000..681b747fe
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/Model/IPersons.cs
@@ -0,0 +1,9 @@
+using System.Collections.ObjectModel;
+
+namespace ListBinding.Model
+{
+ public interface IPersons
+ {
+ ObservableCollection Persons { get; }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/Model/Person.cs b/CSharpBible/MVVM_Tutorial/ListBinding/Model/Person.cs
new file mode 100644
index 000000000..70f15fd31
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/Model/Person.cs
@@ -0,0 +1,133 @@
+// ***********************************************************************
+// Assembly : ListBinding
+// Author : Mir
+// Created : 06-22-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-20-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System;
+using System.Runtime.Serialization;
+
+namespace ListBinding.Model
+{
+ ///
+ /// Class Person.
+ /// Implements the
+ /// Implements the
+ ///
+ ///
+ ///
+ [Serializable]
+ public class Person : NotificationObject//, ISafeSerializationData
+ {
+ #region Properties
+ #region private properties
+ ///
+ /// The identifier
+ ///
+ private int _id;
+ ///
+ /// The first name
+ ///
+ private string _firstName="";
+ ///
+ /// The last name
+ ///
+ private string _lastName="";
+ ///
+ /// The title
+ ///
+ private string _title = "";
+ #endregion
+ ///
+ /// Gets or sets the identifier.
+ ///
+ /// The identifier.
+ public int Id
+ {
+ get => _id; set => SetProperty(ref _id, value);
+ }
+ ///
+ /// Gets or sets the first name.
+ ///
+ /// The first name.
+ public string FirstName
+ {
+ get => _firstName; set => SetProperty(ref _firstName, value,new string[] { nameof(FullName) });
+ }
+ ///
+ /// Gets or sets the last name.
+ ///
+ /// The last name.
+ public string LastName
+ {
+ get => _lastName; set => SetProperty(ref _lastName, value, new string[] { nameof(FullName) });
+ }
+
+ ///
+ /// Gets or sets the title.
+ ///
+ /// The title.
+ public string Title
+ {
+ get => _title; set => SetProperty(ref _title, value, new string[] { nameof(FullName) });
+ }
+
+ ///
+ /// Gets the full name.
+ ///
+ /// The full name.
+ public string FullName => $"{_lastName}{(IsEmpty?"":", ")}{_firstName}{(string.IsNullOrEmpty(Title) ? "" : ", ")}{Title}";
+
+ ///
+ /// Gets a value indicating whether this instance is empty.
+ ///
+ /// true if this instance is empty; otherwise, false.
+ public bool IsEmpty => string.IsNullOrWhiteSpace(_firstName) && string.IsNullOrEmpty(_lastName);
+ #endregion
+
+ #region Methods
+ /// Initializes a new instance of the class.
+ ///
+ /// The last name.
+ /// The first name.
+ /// The title.
+ public Person(string lastName, string firstName, string title="")
+ {
+ (FirstName, LastName, Title) = (firstName, lastName, title);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The full name.
+ public Person(string fullName)
+ : this(fullName.Split(',')[0].Trim(),
+ fullName.Split(',').Length > 1 ? fullName.Split(',')[1].Trim():"",
+ fullName.Split(',').Length > 2 ? fullName.Split(',')[2].Trim():"")
+ {}
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Person() { }
+
+ ///
+ ///
+ /// Returns a that represents this instance.
+ ///
+ /// A that represents this instance.
+ public override string ToString()
+ {
+ return $"{Id}, {FullName}";
+ }
+ #endregion
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/Model/Persons.cs b/CSharpBible/MVVM_Tutorial/ListBinding/Model/Persons.cs
new file mode 100644
index 000000000..727dc0064
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/Model/Persons.cs
@@ -0,0 +1,68 @@
+using ListBinding.Properties;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace ListBinding.Model
+{
+ public class Persons : IPersons, IEnumerable
+ {
+ ///
+ /// The persons
+ ///
+ private ObservableCollection persons = new ObservableCollection();
+
+ public Persons()
+ {
+
+ if (string.IsNullOrEmpty(Settings.Default.Data))
+ persons = new ObservableCollection();
+ else
+ try
+ {
+ // Construct an instance of the XmlSerializer with the type
+ // of object that is being deserialized.
+ var mySerializer = new XmlSerializer(typeof(ObservableCollection));
+ // To read the file, create a FileStream.
+ using var myFileStream = new MemoryStream();
+ byte[] b;
+ myFileStream.Write(b = Encoding.UTF8.GetPreamble(), 0, b.Length);
+ myFileStream.Write(b = Encoding.UTF8.GetBytes(Settings.Default.Data), 0, b.Length);
+ myFileStream.Position = 0;
+ // Call the Deserialize method and cast to the object type.
+ persons = (ObservableCollection)mySerializer.Deserialize(myFileStream);
+ }
+ catch { persons = new ObservableCollection(); }
+ }
+ class Utf8StringWriter : StringWriter
+ {
+ public override Encoding Encoding => Encoding.UTF8;
+ }
+
+ ~Persons()
+ {
+ // Insert code to set properties and fields of the object.
+ XmlSerializer mySerializer = new(typeof(ObservableCollection));
+ // To write to a file, create a StreamWriter object.
+ StringWriter myWriter = new Utf8StringWriter();
+ XmlTextWriter xmlTextWriter = new XmlTextWriter(myWriter);
+ xmlTextWriter.Formatting = Formatting.Indented;
+ xmlTextWriter.Indentation = 2;
+ mySerializer.Serialize(xmlTextWriter, persons);
+ myWriter.Close();
+ Settings.Default.Data = myWriter.ToString();
+ Settings.Default.Save();
+ }
+ ObservableCollection IPersons.Persons => persons;
+
+ public IEnumerator GetEnumerator()
+ => persons.GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator()
+ => persons.GetEnumerator();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..3bfb7882e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace ListBinding.Properties {
+ using System;
+
+
+ ///
+ /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+ ///
+ // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+ // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+ // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+ // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ListBinding.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..0ce1562cd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Settings.Designer.cs
@@ -0,0 +1,46 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace ListBinding.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+ public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute(@"
+
+
+ Care
+ Joe
+ Dr,
+ 1
+
+")]
+ public string Data {
+ get {
+ return ((string)(this["Data"]));
+ }
+ set {
+ this["Data"] = value;
+ }
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Settings.settings
new file mode 100644
index 000000000..ded0e17ed
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/Properties/Settings.settings
@@ -0,0 +1,17 @@
+
+
+
+
+
+ <?xml version="1.0" encoding="utf-8"?>
+<ArrayOfPerson xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <Person>
+ <LastName>Care</LastName>
+ <FirstName>Joe</FirstName>
+ <Title>Dr,</Title>
+ <Id>1</Id>
+ </Person>
+</ArrayOfPerson>
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/View/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/ListBinding/View/MainWindow.xaml
new file mode 100644
index 000000000..bff7091a6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/View/MainWindow.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/View/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/ListBinding/View/MainWindow.xaml.cs
new file mode 100644
index 000000000..1aedbd78f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/View/MainWindow.xaml.cs
@@ -0,0 +1,31 @@
+// ***********************************************************************
+// Assembly : ListBinding
+// Author : Mir
+// Created : 12-24-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 12-24-2021
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace ListBinding.View
+{
+ ///
+ /// Interaktionslogik für MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/View/PersonView.xaml b/CSharpBible/MVVM_Tutorial/ListBinding/View/PersonView.xaml
new file mode 100644
index 000000000..5a8e0a3ac
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/View/PersonView.xaml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/View/PersonView.xaml.cs b/CSharpBible/MVVM_Tutorial/ListBinding/View/PersonView.xaml.cs
new file mode 100644
index 000000000..47fe81679
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/View/PersonView.xaml.cs
@@ -0,0 +1,42 @@
+// ***********************************************************************
+// Assembly : ListBinding
+// Author : Mir
+// Created : 12-23-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 12-24-2021
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using ListBinding.ViewModel;
+using System;
+using System.Windows;
+
+namespace ListBinding.View
+{
+ ///
+ /// Interaktionslogik für PersonView.xaml
+ ///
+ public partial class PersonView : Window
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PersonView()
+ {
+ InitializeComponent();
+ ((PersonViewViewModel)DataContext).MissingData += (object? sender, EventArgs e)=>ShowError();
+ }
+
+ ///
+ /// Shows the error.
+ ///
+ public void ShowError()
+ {
+ MessageBox.Show("Bitte einen Vornamen oder Nachnamen eingeben.");
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/ListBinding/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..2f5ddfdee
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,26 @@
+// ***********************************************************************
+// Assembly : ListBinding
+// Author : Mir
+// Created : 12-23-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 12-24-2021
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace ListBinding.ViewModel
+{
+ ///
+ /// Class MainWindowViewModel.
+ /// Implements the
+ ///
+ ///
+ internal class MainWindowViewModel : BaseViewModel
+ {
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBinding/ViewModels/PersonViewViewModel.cs b/CSharpBible/MVVM_Tutorial/ListBinding/ViewModels/PersonViewViewModel.cs
new file mode 100644
index 000000000..fb25c4504
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBinding/ViewModels/PersonViewViewModel.cs
@@ -0,0 +1,92 @@
+// ***********************************************************************
+// Assembly : ListBinding
+// Author : Mir
+// Created : 12-24-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 08-14-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using ListBinding.Model;
+using MVVM.ViewModel;
+using System;
+using System.Collections.ObjectModel;
+
+namespace ListBinding.ViewModel
+{
+ ///
+ /// Class PersonViewViewModel.
+ /// Implements the
+ ///
+ ///
+ public class PersonViewViewModel : BaseViewModel
+ {
+ ///
+ /// The new person
+ ///
+ private Person newPerson = new Person();
+
+ ///
+ /// Occurs when [missing data].
+ ///
+ public event EventHandler? MissingData;
+
+ ///
+ /// Creates new person.
+ ///
+ /// The new person.
+ public Person NewPerson
+ {
+ get => newPerson;
+ set
+ {
+ if (value == newPerson) return;
+ newPerson = value;
+ RaisePropertyChanged();
+ btnAddPerson.NotifyCanExecuteChanged();
+ }
+ }
+
+ ///
+ /// Gets or sets the persons.
+ ///
+ /// The persons.
+ public ObservableCollection Persons { get => _persons.Persons; }
+
+ private IPersons _persons;
+
+ ///
+ /// Gets or sets the BTN add person.
+ ///
+ /// The BTN add person.
+ public DelegateCommand btnAddPerson { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PersonViewViewModel() : this(new Persons()) { }
+ public PersonViewViewModel(IPersons persons)
+ {
+ _persons = persons;
+ this.btnAddPerson = new DelegateCommand(
+ (o) =>
+ {
+ if (newPerson == null || newPerson.FullName == "")
+ MissingData?.Invoke(this, new EventArgs());
+ else
+ {
+ _persons.Persons.Add(NewPerson);
+ NewPerson.Id = _persons.Persons.IndexOf(newPerson)+1;
+ NewPerson = new Person();
+ }
+ },
+ (o)=>newPerson?.Id==0
+ );
+
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBindingTests/ListBindingTests.csproj b/CSharpBible/MVVM_Tutorial/ListBindingTests/ListBindingTests.csproj
index bb39b5c88..c1bde3495 100644
--- a/CSharpBible/MVVM_Tutorial/ListBindingTests/ListBindingTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/ListBindingTests/ListBindingTests.csproj
@@ -26,8 +26,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/ListBindingTests/ListBinding_netTests.csproj b/CSharpBible/MVVM_Tutorial/ListBindingTests/ListBinding_netTests.csproj
index 67febf751..edc0b6fef 100644
--- a/CSharpBible/MVVM_Tutorial/ListBindingTests/ListBinding_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/ListBindingTests/ListBinding_netTests.csproj
@@ -1,11 +1,17 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
@@ -18,8 +24,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/ListBindingTests/Model/PersonTests.cs b/CSharpBible/MVVM_Tutorial/ListBindingTests/Model/PersonTests.cs
new file mode 100644
index 000000000..114204731
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBindingTests/Model/PersonTests.cs
@@ -0,0 +1,323 @@
+// ***********************************************************************
+// Assembly : ListBindingTests
+// Author : Mir
+// Created : 06-22-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-13-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Text;
+using System.Xml.Serialization;
+
+namespace ListBinding.Model.Tests
+{
+ ///
+ /// Defines test class PersonTests.
+ ///
+ [TestClass()]
+ public class PersonTests
+ {
+ private Person? TestPerson1,TestPerson2;
+ private string DebugResult = "";
+
+ [TestInitialize]
+ public void Init()
+ {
+ TestPerson1 = new Person()
+ {
+ LastName = "2",
+ FirstName = "1",
+ Title = "3",
+ Id = -1
+ };
+ TestPerson1.PropertyChanged += OnPropertyChanged;
+ TestPerson2 = new Person("Mustermann", "Max", "Dr.");
+ TestPerson2.PropertyChanged += OnPropertyChanged;
+ ClearResults();
+ }
+
+ private void ClearResults()
+ {
+ DebugResult = "";
+ }
+
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ DebugResult += $"PropChg: {sender}, P:{e.PropertyName}, V:{sender?.GetType().GetProperty(e.PropertyName)?.GetValue(sender)}{Environment.NewLine}";
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(TestPerson1);
+ Assert.IsNotNull(TestPerson1);
+ Assert.IsInstanceOfType(TestPerson1,typeof(Person));
+ Assert.IsInstanceOfType(TestPerson1,typeof(Person));
+ }
+
+ ///
+ /// Defines the test method PersonTest.
+ ///
+ [TestMethod()]
+ public void PersonTest()
+ {
+ var person = new Person()
+ {
+ LastName = "2",
+ FirstName = "1",
+ Title = "3",
+ Id = -1
+ };
+ Assert.IsNotNull(person);
+
+ }
+
+ ///
+ /// Persons the test2.
+ ///
+ /// a identifier.
+ /// a firstname.
+ /// a lastname.
+ /// a title.
+ /// The exp fullname.
+ [TestMethod()]
+ [DataRow(0, "", "", "", "")]
+ [DataRow(1, "1", "2", "3", "2, 1, 3")]
+ [DataRow(2, "1", "", "", ", 1")]
+ [DataRow(3, "", "2", "", "2, ")]
+ [DataRow(4, "", "", "3", ", 3")]
+ [DataRow(5, "1", "2", "", "2, 1")]
+ [DataRow(6, "", "2", "3", "2, , 3")]
+ [DataRow(7, "1", "", "3", ", 1, 3")]
+ public void PersonTest2(int aId, string aFirstname, string aLastname, string aTitle, string ExpFullname)
+ {
+ var person = new Person(aLastname, aFirstname, aTitle)
+ {
+ Id = aId
+ };
+ Assert.AreEqual(ExpFullname, person.FullName);
+ }
+
+ ///
+ /// Persons the test3.
+ ///
+ /// a identifier.
+ /// The exp firstname.
+ /// The exp lastname.
+ /// The exp title.
+ /// a fullname.
+ [TestMethod()]
+ [DataRow(0, "", "", "", "")]
+ [DataRow(1, "1", "2", "3", "2, 1, 3")]
+ [DataRow(2, "1", "", "", ", 1")]
+ [DataRow(3, "", "2", "", "2, ")]
+ [DataRow(4, "", "", "3", ", , 3")] //!!
+ [DataRow(5, "1", "2", "", "2, 1")]
+ [DataRow(6, "", "2", "3", "2, , 3")]
+ [DataRow(7, "1", "", "3", ", 1, 3")]
+ public void PersonTest3(int aId, string ExpFirstname, string ExpLastname, string ExpTitle, string aFullname)
+ {
+ var person = new Person(aFullname)
+ {
+ Id = aId
+ };
+ Assert.AreEqual(ExpLastname, person.LastName, "Lastname");
+ Assert.AreEqual(ExpFirstname, person.FirstName, "Firstname");
+ Assert.AreEqual(ExpTitle, person.Title,"Title");
+ }
+
+ ///
+ /// Persons the full name.
+ ///
+ /// a identifier.
+ /// a firstname.
+ /// a lastname.
+ /// a title.
+ /// The exp fullname.
+ [TestMethod()]
+ [DataRow(0, "", "", "", "")]
+ [DataRow(1, "1", "2", "3", "2, 1, 3")]
+ [DataRow(2, "1", "", "", ", 1")]
+ [DataRow(3, "", "2", "", "2, ")]
+ [DataRow(4, "", "", "3", ", 3")]
+ [DataRow(5, "1", "2", "", "2, 1")]
+ [DataRow(6, "", "2", "3", "2, , 3")]
+ [DataRow(7, "1", "", "3", ", 1, 3")]
+ public void PersonFullName(int aId, string aFirstname, string aLastname, string aTitle, string ExpFullname)
+ {
+ var person = new Person()
+ {
+ Id = aId,
+ FirstName = aFirstname,
+ LastName = aLastname,
+ Title = aTitle
+ };
+ Assert.AreEqual(ExpFullname, person.FullName);
+ }
+
+ ///
+ /// Gets the object data test.
+ ///
+ /// a identifier.
+ /// a firstname.
+ /// a lastname.
+ /// a title.
+ /// The exp fullname.
+ [TestMethod()]
+ [DataRow(0, "", "", "", new[] { " 0\r\n \r\n \r\n \r\n" })]
+ [DataRow(1, "1", "2", "3", new[] { " 1\r\n 1\r\n 2\r\n 3\r\n" })]
+ [DataRow(2, "1", "", "", new[] { " 2\r\n 1\r\n \r\n \r\n" })]
+ [DataRow(3, "", "2", "", new[] { " 3\r\n \r\n 2\r\n \r\n" })]
+ [DataRow(4, "", "", "3", new[] { " 4\r\n \r\n \r\n 3\r\n" })]
+ [DataRow(5, "1", "2", "", new[] { " 5\r\n 1\r\n 2\r\n \r\n" })]
+ [DataRow(6, "", "2", "3", new[] { " 6\r\n \r\n 2\r\n 3\r\n" })]
+ [DataRow(7, "1", "", "3", new[] { " 7\r\n 1\r\n \r\n 3\r\n" })]
+ [DataRow(8, "Joe", "Care", "Prof. Dr.", new[] { " 8\r\n Joe\r\n Care\r\n Prof. Dr.\r\n" })]
+ public void GetObjectDataTest(int aId, string aFirstname, string aLastname, string aTitle, string[] ExpFullname)
+ {
+ const string cXMLHeader =
+#if NET5_0_OR_GREATER
+ "\r\n\r\n";
+#else
+ "\r\n\r\n";
+#endif
+ var person = new Person(aLastname, aFirstname, aTitle) { Id = aId };
+ var formatter = new XmlSerializer(typeof(Person));
+ string actual = "";
+ using (var stream = new MemoryStream())
+ {
+ formatter.Serialize(stream, person);
+ stream.Seek(0L, SeekOrigin.Begin);
+ var b = new byte[stream.Length];
+ stream.Read(b, 0, b.Length);
+ actual = Encoding.UTF8.GetString(b);
+ }
+ Assert.AreEqual(cXMLHeader+ExpFullname[0], actual);
+ }
+
+ ///
+ /// Converts to stringtest.
+ ///
+ /// a identifier.
+ /// a firstname.
+ /// a lastname.
+ /// a title.
+ /// The exp fullname.
+ [TestMethod()]
+ [DataRow(0, "", "", "", "0, ")]
+ [DataRow(1, "1", "2", "3", "1, 2, 1, 3")]
+ [DataRow(2, "1", "", "", "2, , 1")]
+ [DataRow(3, "", "2", "", "3, 2, ")]
+ [DataRow(4, "", "", "3", "4, , 3")]
+ [DataRow(5, "1", "2", "", "5, 2, 1")]
+ [DataRow(6, "", "2", "3", "6, 2, , 3")]
+ [DataRow(7, "1", "", "3", "7, , 1, 3")]
+ public void ToStringTest(int aId, string aFirstname, string aLastname, string aTitle, string ExpFullname)
+ {
+ var person = new Person()
+ {
+ Id = aId,
+ FirstName = aFirstname,
+ LastName = aLastname,
+ Title = aTitle
+ };
+ Assert.AreEqual(ExpFullname, person.ToString());
+ }
+
+ ///
+ /// Gets the object data test.
+ ///
+ /// a identifier.
+ /// a firstname.
+ /// a lastname.
+ /// a title.
+ /// The exp fullname.
+ [TestMethod()]
+ [DataRow(0, "", "", "", new[] { "\r\n\r\n 0\r\n \r\n \r\n \r\n" })]
+ [DataRow(1, "1", "2", "3", new[] { "\r\n\r\n 1\r\n 1\r\n 2\r\n 3\r\n" })]
+ [DataRow(2, "1", "", "", new[] { "\r\n\r\n 2\r\n 1\r\n \r\n \r\n" })]
+ [DataRow(3, "", "2", "", new[] { "\r\n\r\n 3\r\n \r\n 2\r\n \r\n" })]
+ [DataRow(4, "", "", "3", new[] { "\r\n\r\n 4\r\n \r\n \r\n 3\r\n" })]
+ [DataRow(5, "1", "2", "", new[] { "\r\n\r\n 5\r\n 1\r\n 2\r\n \r\n" })]
+ [DataRow(6, "", "2", "3", new[] { "\r\n\r\n 6\r\n \r\n 2\r\n 3\r\n" })]
+ [DataRow(7, "1", "", "3", new[] { "\r\n\r\n 7\r\n 1\r\n \r\n 3\r\n" })]
+ [DataRow(8, "Joe", "Care", "Prof. Dr.", new[] { "\r\n\r\n 8\r\n Joe\r\n Care\r\n Prof. Dr.\r\n" })]
+ public void CompleteDeserializationTest(int aId, string aFirstname, string aLastname, string aTitle, string[] ExpFullname)
+ {
+ object? p=default;
+ var formatter = new XmlSerializer(typeof(Person));
+ using (var stream = new MemoryStream())
+ {
+ var b = Encoding.UTF8.GetBytes(ExpFullname[0]);
+ stream.Write(b, 0, b.Length);
+ stream.Seek(0L, SeekOrigin.Begin);
+ p = formatter.Deserialize(stream);
+ }
+ Assert.IsInstanceOfType(p, typeof(Person));
+ if (p is Person pp)
+ {
+ Assert.AreEqual(aId, pp.Id,$"{DisplayNameAttribute.Default.DisplayName}.ID");
+ Assert.AreEqual(aFirstname, pp.FirstName, $"{DisplayNameAttribute.Default.DisplayName}.FirstName");
+ Assert.AreEqual(aLastname, pp.LastName, $"{DisplayNameAttribute.Default.DisplayName}.LastName");
+ Assert.AreEqual(aTitle, pp.Title, $"{DisplayNameAttribute.Default.DisplayName}.Title");
+ }
+ }
+
+ ///
+ /// test.
+ ///
+ /// a identifier.
+ /// a firstname.
+ /// a lastname.
+ /// a title.
+ /// The exp fullname.
+ [TestMethod()]
+ [DataRow(null, null, null, null, new string[] { "", "" })]
+ [DataRow(-1, null, null, null,new string[] { "", "PropChg: -1, Mustermann, Max, Dr., P:Id, V:-1\r\n" })]
+ [DataRow(0, null, null, null, new string[] { "PropChg: 0, 2, 1, 3, P:Id, V:0\r\n", "" })]
+ [DataRow(1, "1", "2", "3", new string[] { "PropChg: 1, 2, 1, 3, P:Id, V:1\r\n", "PropChg: 1, Mustermann, Max, Dr., P:Id, V:1\r\nPropChg: 1, Mustermann, 1, Dr., P:FirstName, V:1\r\nPropChg: 1, Mustermann, 1, Dr., P:FullName, V:Mustermann, 1, Dr.\r\nPropChg: 1, 2, 1, Dr., P:LastName, V:2\r\nPropChg: 1, 2, 1, Dr., P:FullName, V:2, 1, Dr.\r\nPropChg: 1, 2, 1, 3, P:Title, V:3\r\nPropChg: 1, 2, 1, 3, P:FullName, V:2, 1, 3\r\n" })]
+ [DataRow(11, "Max", "Mustermann", "Dr.", new string[] { "PropChg: 11, 2, 1, 3, P:Id, V:11\r\nPropChg: 11, 2, Max, 3, P:FirstName, V:Max\r\nPropChg: 11, 2, Max, 3, P:FullName, V:2, Max, 3\r\nPropChg: 11, Mustermann, Max, 3, P:LastName, V:Mustermann\r\nPropChg: 11, Mustermann, Max, 3, P:FullName, V:Mustermann, Max, 3\r\nPropChg: 11, Mustermann, Max, Dr., P:Title, V:Dr.\r\nPropChg: 11, Mustermann, Max, Dr., P:FullName, V:Mustermann, Max, Dr.\r\n", "PropChg: 11, Mustermann, Max, Dr., P:Id, V:11\r\n" })]
+ [DataRow(2, "1", null, null, new string[] { "PropChg: 2, 2, 1, 3, P:Id, V:2\r\n", "PropChg: 2, Mustermann, Max, Dr., P:Id, V:2\r\nPropChg: 2, Mustermann, 1, Dr., P:FirstName, V:1\r\nPropChg: 2, Mustermann, 1, Dr., P:FullName, V:Mustermann, 1, Dr.\r\n" })]
+ [DataRow(12, "Max", null, null, new string[] { "PropChg: 12, 2, 1, 3, P:Id, V:12\r\nPropChg: 12, 2, Max, 3, P:FirstName, V:Max\r\nPropChg: 12, 2, Max, 3, P:FullName, V:2, Max, 3\r\n", "PropChg: 12, Mustermann, Max, Dr., P:Id, V:12\r\n" })]
+ [DataRow(3, null, "2", null, new string[] { "PropChg: 3, 2, 1, 3, P:Id, V:3\r\n", "PropChg: 3, Mustermann, Max, Dr., P:Id, V:3\r\nPropChg: 3, 2, Max, Dr., P:LastName, V:2\r\nPropChg: 3, 2, Max, Dr., P:FullName, V:2, Max, Dr.\r\n" })]
+ [DataRow(13, null, "Mustermann", null, new string[] { "PropChg: 13, 2, 1, 3, P:Id, V:13\r\nPropChg: 13, Mustermann, 1, 3, P:LastName, V:Mustermann\r\nPropChg: 13, Mustermann, 1, 3, P:FullName, V:Mustermann, 1, 3\r\n", "PropChg: 13, Mustermann, Max, Dr., P:Id, V:13\r\n" })]
+ [DataRow(4, null, null, "3", new string[] { "PropChg: 4, 2, 1, 3, P:Id, V:4\r\n", "PropChg: 4, Mustermann, Max, Dr., P:Id, V:4\r\nPropChg: 4, Mustermann, Max, 3, P:Title, V:3\r\nPropChg: 4, Mustermann, Max, 3, P:FullName, V:Mustermann, Max, 3\r\n" })]
+ [DataRow(14, null, null, "Dr.", new string[] { "PropChg: 14, 2, 1, 3, P:Id, V:14\r\nPropChg: 14, 2, 1, Dr., P:Title, V:Dr.\r\nPropChg: 14, 2, 1, Dr., P:FullName, V:2, 1, Dr.\r\n", "PropChg: 14, Mustermann, Max, Dr., P:Id, V:14\r\n" })]
+ [DataRow(5, "1", "2", null, new string[] { "PropChg: 5, 2, 1, 3, P:Id, V:5\r\n", "PropChg: 5, Mustermann, Max, Dr., P:Id, V:5\r\nPropChg: 5, Mustermann, 1, Dr., P:FirstName, V:1\r\nPropChg: 5, Mustermann, 1, Dr., P:FullName, V:Mustermann, 1, Dr.\r\nPropChg: 5, 2, 1, Dr., P:LastName, V:2\r\nPropChg: 5, 2, 1, Dr., P:FullName, V:2, 1, Dr.\r\n" })]
+ [DataRow(15, "Max", "Mustermann", null, new string[] { "PropChg: 15, 2, 1, 3, P:Id, V:15\r\nPropChg: 15, 2, Max, 3, P:FirstName, V:Max\r\nPropChg: 15, 2, Max, 3, P:FullName, V:2, Max, 3\r\nPropChg: 15, Mustermann, Max, 3, P:LastName, V:Mustermann\r\nPropChg: 15, Mustermann, Max, 3, P:FullName, V:Mustermann, Max, 3\r\n", "PropChg: 15, Mustermann, Max, Dr., P:Id, V:15\r\n" })]
+ [DataRow(6, null, "2", "3", new string[] { "PropChg: 6, 2, 1, 3, P:Id, V:6\r\n", "PropChg: 6, Mustermann, Max, Dr., P:Id, V:6\r\nPropChg: 6, 2, Max, Dr., P:LastName, V:2\r\nPropChg: 6, 2, Max, Dr., P:FullName, V:2, Max, Dr.\r\nPropChg: 6, 2, Max, 3, P:Title, V:3\r\nPropChg: 6, 2, Max, 3, P:FullName, V:2, Max, 3\r\n" })]
+ [DataRow(16, null, "Mustermann", "Dr.", new string[] { "PropChg: 16, 2, 1, 3, P:Id, V:16\r\nPropChg: 16, Mustermann, 1, 3, P:LastName, V:Mustermann\r\nPropChg: 16, Mustermann, 1, 3, P:FullName, V:Mustermann, 1, 3\r\nPropChg: 16, Mustermann, 1, Dr., P:Title, V:Dr.\r\nPropChg: 16, Mustermann, 1, Dr., P:FullName, V:Mustermann, 1, Dr.\r\n", "PropChg: 16, Mustermann, Max, Dr., P:Id, V:16\r\n" })]
+ [DataRow(7, "1", null, "3", new string[] { "PropChg: 7, 2, 1, 3, P:Id, V:7\r\n", "PropChg: 7, Mustermann, Max, Dr., P:Id, V:7\r\nPropChg: 7, Mustermann, 1, Dr., P:FirstName, V:1\r\nPropChg: 7, Mustermann, 1, Dr., P:FullName, V:Mustermann, 1, Dr.\r\nPropChg: 7, Mustermann, 1, 3, P:Title, V:3\r\nPropChg: 7, Mustermann, 1, 3, P:FullName, V:Mustermann, 1, 3\r\n" })]
+ [DataRow(17, "Max", null, "Dr.", new string[] { "PropChg: 17, 2, 1, 3, P:Id, V:17\r\nPropChg: 17, 2, Max, 3, P:FirstName, V:Max\r\nPropChg: 17, 2, Max, 3, P:FullName, V:2, Max, 3\r\nPropChg: 17, 2, Max, Dr., P:Title, V:Dr.\r\nPropChg: 17, 2, Max, Dr., P:FullName, V:2, Max, Dr.\r\n", "PropChg: 17, Mustermann, Max, Dr., P:Id, V:17\r\n" })]
+ public void PropertyChangeTest(int? aId, string aFirstname, string aLastname, string aTitle, string[] ExpRes)
+ {
+ SetData(TestPerson1);
+ Assert.AreEqual(ExpRes[0], DebugResult,$"{nameof(TestPerson1)}");
+ ClearResults();
+ SetData(TestPerson2);
+ Assert.AreEqual(ExpRes[1], DebugResult, $"{nameof(TestPerson2)}");
+
+ void SetData(Person? p)
+ {
+ if (p == null) return;
+ if (aId != null) p.Id = aId ?? 0; // Soll die ID nderbar sein ?
+ if (aFirstname != null) p.FirstName = aFirstname;
+ if (aLastname != null) p.LastName = aLastname;
+ if (aTitle != null) p.Title = aTitle;
+ }
+ }
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/ListBindingTests/Properties/.info b/CSharpBible/MVVM_Tutorial/ListBindingTests/Properties/.info
new file mode 100644
index 000000000..e69de29bb
diff --git a/CSharpBible/MVVM_Tutorial/ListBindingTests/View/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/ListBindingTests/View/MainWindowTests.cs
new file mode 100644
index 000000000..36c63a0da
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBindingTests/View/MainWindowTests.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : ListBinding_netTests
+// Author : Mir
+// Created : 05-13-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-13-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace ListBinding.View.Tests
+{
+ ///
+ /// Defines test class MainWindowTests.
+ ///
+ ///
+ [TestClass()]
+ public class MainWindowTests
+ {
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ListBindingTests/View/PersonViewTests.cs b/CSharpBible/MVVM_Tutorial/ListBindingTests/View/PersonViewTests.cs
new file mode 100644
index 000000000..f55a0f913
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBindingTests/View/PersonViewTests.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : ListBinding_netTests
+// Author : Mir
+// Created : 05-13-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-13-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace ListBinding.View.Tests
+{
+ ///
+ /// Defines test class PersonViewTests.
+ ///
+ ///
+ [TestClass()]
+ public class PersonViewTests
+ {
+ ///
+ /// Defines the test method PersonViewTests.
+ ///
+ ///
+ [TestMethod()]
+ public void PersonViewTest()
+ {
+ PersonView? testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(PersonView));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ListBindingTests/ViewModels/PersonViewViewModelTests.cs b/CSharpBible/MVVM_Tutorial/ListBindingTests/ViewModels/PersonViewViewModelTests.cs
new file mode 100644
index 000000000..30b6ce41f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBindingTests/ViewModels/PersonViewViewModelTests.cs
@@ -0,0 +1,37 @@
+// ***********************************************************************
+// Assembly : ListBindingTests
+// Author : Mir
+// Created : 06-17-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 06-17-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace ListBinding.ViewModel.Tests
+{
+ ///
+ /// Defines test class PersonViewViewModelTests.
+ ///
+ [TestClass()]
+ public class PersonViewViewModelTests
+ {
+ ///
+ /// Defines the test method PersonViewViewModelTest.
+ ///
+ [TestMethod()]
+ public void PersonViewViewModelTest()
+ {
+ var model = new PersonViewViewModel();
+ Assert.IsNotNull(model.NewPerson);
+ Assert.IsNotNull(model.Persons);
+ Assert.HasCount(1, model.Persons);
+ Assert.IsTrue(model.NewPerson.IsEmpty);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/ListBindingTests/packages.config b/CSharpBible/MVVM_Tutorial/ListBindingTests/packages.config
new file mode 100644
index 000000000..e49cfc4c0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/ListBindingTests/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Empty.sln b/CSharpBible/MVVM_Tutorial/MVVM_00_Empty.sln
new file mode 100644
index 000000000..9297e85dd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Empty.sln
@@ -0,0 +1,45 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projektmappenelemente", "Projektmappenelemente", "{658BD492-33FF-4995-AAE7-4F6894E92F0C}"
+ ProjectSection(SolutionItems) = preProject
+ MVVM_Tutorial.props = MVVM_Tutorial.props
+ MVVM_Tutorial_net.props = MVVM_Tutorial_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLib", "..\Libraries\MVVM_BaseLib\MVVM_BaseLib.csproj", "{890CF504-3814-443B-9EE6-E8BCACF68203}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{AF041458-CF94-4D6D-BB0F-8BC50F4F099C}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Libraries\Libraries_net.props = ..\Libraries\Libraries_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLibTests", "..\Libraries\MVVM_BaseLibTests\MVVM_BaseLibTests.csproj", "{66EB60F2-523D-47C8-95EA-C7E10759E239}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.Build.0 = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {66EB60F2-523D-47C8-95EA-C7E10759E239} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate.sln b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate.sln
new file mode 100644
index 000000000..f94f3c44a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate.sln
@@ -0,0 +1,67 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projektmappenelemente", "Projektmappenelemente", "{658BD492-33FF-4995-AAE7-4F6894E92F0C}"
+ ProjectSection(SolutionItems) = preProject
+ MVVM_Tutorial.props = MVVM_Tutorial.props
+ MVVM_Tutorial_net.props = MVVM_Tutorial_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLib", "..\Libraries\MVVM_BaseLib\MVVM_BaseLib.csproj", "{890CF504-3814-443B-9EE6-E8BCACF68203}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{AF041458-CF94-4D6D-BB0F-8BC50F4F099C}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Libraries\Libraries_net.props = ..\Libraries\Libraries_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLibTests", "..\Libraries\MVVM_BaseLibTests\MVVM_BaseLibTests.csproj", "{66EB60F2-523D-47C8-95EA-C7E10759E239}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_00_IoCTemplate", "MVVM_00_IoCTemplate\MVVM_00_IoCTemplate.csproj", "{16A3E367-674C-4959-8B31-7681B0E8258C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_00_IoCTemplate_net", "MVVM_00_IoCTemplate\MVVM_00_IoCTemplate_net.csproj", "{D6BFC652-822A-4E7D-9846-7F529C584BAC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_00_IoCTemplate_netTests", "MVVM_00_IoCTemplateTests\MVVM_00_IoCTemplate_netTests.csproj", "{A7844F45-74D9-4A81-9C27-D0E17ED0F851}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_00_IoCTemplateTests", "MVVM_00_IoCTemplateTests\MVVM_00_IoCTemplateTests.csproj", "{7FF12BBB-AA2E-48C1-B283-0665570902F8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.Build.0 = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.Build.0 = Release|Any CPU
+ {16A3E367-674C-4959-8B31-7681B0E8258C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {16A3E367-674C-4959-8B31-7681B0E8258C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {16A3E367-674C-4959-8B31-7681B0E8258C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {16A3E367-674C-4959-8B31-7681B0E8258C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6BFC652-822A-4E7D-9846-7F529C584BAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6BFC652-822A-4E7D-9846-7F529C584BAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6BFC652-822A-4E7D-9846-7F529C584BAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6BFC652-822A-4E7D-9846-7F529C584BAC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7844F45-74D9-4A81-9C27-D0E17ED0F851}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7844F45-74D9-4A81-9C27-D0E17ED0F851}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7844F45-74D9-4A81-9C27-D0E17ED0F851}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A7844F45-74D9-4A81-9C27-D0E17ED0F851}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7FF12BBB-AA2E-48C1-B283-0665570902F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FF12BBB-AA2E-48C1-B283-0665570902F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FF12BBB-AA2E-48C1-B283-0665570902F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FF12BBB-AA2E-48C1-B283-0665570902F8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {66EB60F2-523D-47C8-95EA-C7E10759E239} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ EndGlobalSection
+EndGlobal
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/App.xaml
new file mode 100644
index 000000000..5368ab7d6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/App.xaml.cs
new file mode 100644
index 000000000..3d92e0ddc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/App.xaml.cs
@@ -0,0 +1,43 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using BaseLib.Helper;
+using BaseLib.Models;
+using BaseLib.Models.Interfaces;
+using Microsoft.Extensions.DependencyInjection;
+using MVVM_00_IoCTemplate.Models;
+using System.Windows;
+
+namespace MVVM_00_IoCTemplate
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ var sc = new ServiceCollection()
+ .AddTransient()
+ .AddTransient()
+ .AddSingleton();
+
+ var bc = sc.BuildServiceProvider();
+
+ IoC.Configure(bc);
+ }
+ }
+}
+namespace MVVM_00_IoCTemplate.Models { }
+namespace MVVM_00_IoCTemplate.ValueConverter { }
+namespace MVVM_00_IoCTemplate.Services { }
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/AssemblyInfo.cs
new file mode 100644
index 000000000..f60efc019
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MVVM_00_IoCTemplate.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MVVM_00_IoCTemplate.csproj
index 9e243068e..e6405f28b 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MVVM_00_IoCTemplate.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MVVM_00_IoCTemplate.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MVVM_00_IoCTemplate_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MVVM_00_IoCTemplate_net.csproj
index 780795add..adbbea3c3 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MVVM_00_IoCTemplate_net.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MVVM_00_IoCTemplate_net.csproj
@@ -2,13 +2,19 @@
WinExe
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
-
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MainWindow.xaml
new file mode 100644
index 000000000..940bfcbab
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MainWindow.xaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MainWindow.xaml.cs
new file mode 100644
index 000000000..a9ca00a74
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_00_IoCTemplate;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/.info b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/.info
new file mode 100644
index 000000000..62f215ed0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/.info
@@ -0,0 +1 @@
+Folder for Models
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/ITemplateModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/ITemplateModel.cs
new file mode 100644
index 000000000..4903cd190
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/ITemplateModel.cs
@@ -0,0 +1,40 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using System;
+using System.ComponentModel;
+
+///
+/// The Models namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.Models;
+
+///
+/// Interface ITemplateModel
+///
+///
+public interface ITemplateModel
+{
+ ///
+ /// Gets the now.
+ ///
+ /// The now.
+ ///
+ DateTime Now { get; }
+ ///
+ /// Occurs when [property changed].
+ ///
+ ///
+ event PropertyChangedEventHandler PropertyChanged;
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/SimpleLog.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/SimpleLog.cs
new file mode 100644
index 000000000..2849190af
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/SimpleLog.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using BaseLib.Models.Interfaces;
+using System;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace MVVM_00_IoCTemplate;
+
+public class SimpleLog(ISysTime sysTime):ILog
+{
+ public static Action LogAction { get; set; } = (message) => Debug.WriteLine(message);
+ ISysTime _sysTime { get; } = sysTime;
+
+ public void Log(string message)
+ => LogAction($"{_sysTime.Now.ToString(CultureInfo.InvariantCulture)}: Msg: {message}");
+ public void Log(string message, Exception exception)
+ => LogAction($"{_sysTime.Now.ToString(CultureInfo.InvariantCulture)}: Err: {message}, {exception}");
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/TemplateModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/TemplateModel.cs
new file mode 100644
index 000000000..5de3464db
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Models/TemplateModel.cs
@@ -0,0 +1,83 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using BaseLib.Models.Interfaces;
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Timers;
+
+///
+/// The Models namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.Models;
+
+///
+/// Class TemplateModel.
+/// Implements the
+/// Implements the
+///
+///
+///
+///
+public partial class TemplateModel :ObservableObject, ITemplateModel
+{
+ private const string csApplStart = "Application startet";
+#if !NET5_0_OR_GREATER
+ private const string csApplEnded = "Application ended";
+#endif
+ #region Properties
+ ///
+ /// The timer
+ ///
+ ///
+ private readonly Timer _timer;
+ private readonly ISysTime _systime;
+ private readonly ILog _log;
+
+ ///
+ /// Gets the now.
+ ///
+ /// The now.
+ ///
+ public DateTime Now { get => _systime.Now; }
+ #endregion
+
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public TemplateModel(ISysTime sysTime,ILog log)
+ {
+ _systime = sysTime;
+ _log = log;
+ _log.Log(csApplStart);
+ _timer = new(250d);
+ _timer.Elapsed += (s, e) => OnPropertyChanged(nameof(Now));
+ _timer.Start();
+ }
+
+#if !NET5_0_OR_GREATER
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~TemplateModel()
+ {
+ _timer.Stop();
+ _log.Log(csApplEnded);
+ return;
+ }
+#endif
+ #endregion
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..866163b4b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Resources.Designer.cs
@@ -0,0 +1,118 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_00_IoCTemplate.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_00_IoCTemplate.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM-Template using Community-Toolkit ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_00_IoCTemplate.Views"
+ /// xmlns:p="clr-namespace:MVVM_00_IoCTemplate.Properties"
+ /// xmlns:vc="clr-namespace:MVVM_00_IoCTemplate.ValueConverter"
+ /// xmlns:mvvm="clr-namespace: [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string TemplateView {
+ get {
+ return ResourceManager.GetString("TemplateView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_00_IoCTemplate
+ ///// Author : Mir
+ ///// Created : 08-11-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 08-24-2022
+ ///// ***********************************************************************
+ ///// <copyright file="MainWindowViewModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2022
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ************************************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string TemplateViewModel {
+ get {
+ return ResourceManager.GetString("TemplateViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die CT-Template-View ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Resources.resx
new file mode 100644
index 000000000..9225fb660
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Resources.resx
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ MVVM-Template using Community-Toolkit
+
+
+
+ ..\Views\TemplateView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\TemplateViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ CT-Template-View
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..5a5a3b949
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_00_IoCTemplate.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Settings.settings
new file mode 100644
index 000000000..049245f40
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ValueConverters/.info b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ValueConverters/.info
new file mode 100644
index 000000000..b4bc464df
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ValueConverters/.info
@@ -0,0 +1 @@
+Folder for ValueConverters
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ValueConverters/DateTimeValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ValueConverters/DateTimeValueConverter.cs
new file mode 100644
index 000000000..d108fbc66
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ValueConverters/DateTimeValueConverter.cs
@@ -0,0 +1,62 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_00_IoCTemplate.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class DateTimeValueConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is DateTime dt)
+ if (parameter is string spar)
+ return dt.ToString(spar);
+ else
+ return dt.ToString(culture);
+ else
+ return value?.ToString() ?? "";
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string sval && DateTime.TryParse(sval.Trim(),culture, DateTimeStyles.AssumeLocal, out var dt))
+ return dt;
+ else
+ return DateTime.MinValue;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/.info b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/.info
new file mode 100644
index 000000000..a3ed848fb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/.info
@@ -0,0 +1 @@
+Folder for ViewModels
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..5db05248f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_00_IoCTemplate.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModelCT
+{
+ #region Properties
+ #endregion
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+
+#if !NET5_0_OR_GREATER
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~MainWindowViewModel()
+ {
+ return;
+ }
+#endif
+ #endregion
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/TemplateViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/TemplateViewModel.cs
new file mode 100644
index 000000000..96b74a9ed
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/ViewModels/TemplateViewModel.cs
@@ -0,0 +1,55 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using BaseLib.Helper;
+using MVVM.ViewModel;
+using MVVM_00_IoCTemplate.Models;
+using System;
+using System.ComponentModel;
+
+namespace MVVM_00_IoCTemplate.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public partial class TemplateViewModel : BaseViewModelCT
+{
+ #region Properties
+ private readonly ITemplateModel _model;
+
+ public DateTime Now => _model.Now;
+ #endregion
+
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TemplateViewModel():this(IoC.GetRequiredService())
+ {
+ }
+
+ public TemplateViewModel(ITemplateModel model)
+ {
+ _model = model;
+ _model.PropertyChanged += OnMPropertyChanged;
+ }
+
+ private void OnMPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ OnPropertyChanged(e.PropertyName);
+ }
+
+ #endregion
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/.info
new file mode 100644
index 000000000..65a00d680
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/.info
@@ -0,0 +1 @@
+Folder for Views
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/TemplateView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/TemplateView.xaml
new file mode 100644
index 000000000..caa70bcd9
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/TemplateView.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/TemplateView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/TemplateView.xaml.cs
new file mode 100644
index 000000000..64bb98527
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplate/Views/TemplateView.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_00_IoCTemplate.Views;
+
+///
+/// Interaktionslogik für TemplateView.xaml
+///
+public partial class TemplateView : Page
+{
+ public TemplateView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/AppTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/AppTests.cs
new file mode 100644
index 000000000..82388550f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/AppTests.cs
@@ -0,0 +1,64 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using BaseLib.Helper;
+using MVVM_00_IoCTemplate.Models;
+using NSubstitute;
+using System;
+using BaseLib.Models.Interfaces;
+
+namespace MVVM_00_IoCTemplate.Tests;
+
+internal class TestApp : App
+{
+ public void DoStartUp()
+ {
+ OnStartup(null);
+ }
+}
+[TestClass()]
+public class AppTests
+{
+ static TestApp app = new();
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ private Func _gsold;
+ private Func _grsold;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ private ISysTime? _sysTime;
+ private ILog? _log;
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ _gsold = IoC.GetSrv;
+ _grsold = IoC.GetReqSrv;
+ IoC.GetReqSrv = (t) =>t switch {
+ _ when t == typeof(ILog) => _log ??= Substitute.For(),
+ _ when t == typeof(ISysTime) => _sysTime ??= Substitute.For(),
+ _ when t == typeof(ITemplateModel) => new TemplateModel(IoC.GetRequiredService(),IoC.GetRequiredService()),
+ _ => throw new ArgumentException() };
+ }
+
+ [TestCleanup]
+ public void CleanUp()
+ {
+ IoC.GetSrv = _gsold;
+ IoC.GetReqSrv = _grsold;
+ }
+
+ [TestMethod]
+ public void AppTest()
+ {
+ Assert.IsNotNull(app);
+ }
+
+ [TestMethod]
+ public void AppTest2()
+ {
+ app.DoStartUp();
+ Assert.IsNotNull(IoC.GetReqSrv(typeof(ITemplateModel)));
+ Assert.IsNull(IoC.GetSrv(typeof(App)));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/MVVM_00_IoCTemplateTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/MVVM_00_IoCTemplateTests.csproj
index 83e2131a6..a422005a2 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/MVVM_00_IoCTemplateTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/MVVM_00_IoCTemplateTests.csproj
@@ -12,8 +12,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/MVVM_00_IoCTemplate_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/MVVM_00_IoCTemplate_netTests.csproj
index e055df9e0..c3c583eb2 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/MVVM_00_IoCTemplate_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/MVVM_00_IoCTemplate_netTests.csproj
@@ -1,16 +1,22 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
false
true
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Models/SimpleLogTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Models/SimpleLogTests.cs
new file mode 100644
index 000000000..42bb30075
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Models/SimpleLogTests.cs
@@ -0,0 +1,65 @@
+using BaseLib.Models.Interfaces;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using NSubstitute;
+using System;
+using System.Linq;
+
+namespace MVVM_00_IoCTemplate.Models.Tests;
+
+[TestClass]
+public class SimpleLogTests:BaseTestViewModel
+{
+ private Action _gsOld;
+ private ISysTime? _sysTime;
+ private SimpleLog simpleLog;
+
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ _gsOld= SimpleLog.LogAction;
+ _gsOld("Test message");
+ SimpleLog.LogAction = DoLog;
+ _sysTime = Substitute.For();
+ _sysTime.Now.Returns(new DateTime(2022, 08, 24, 12, 0, 0));
+ simpleLog = new SimpleLog(_sysTime);
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ SimpleLog.LogAction = _gsOld;
+ }
+
+ [TestMethod]
+ [DataRow("Test message",new[] { "08/24/2022 12:00:00: Msg: Test message\r\n" })]
+ [DataRow(null, new[] { "08/24/2022 12:00:00: Msg: \r\n" })]
+ [DataRow("Some other test", new[] { "08/24/2022 12:00:00: Msg: Some other test\r\n" })]
+
+ public void LogTest(string message, string[] asExp)
+ {
+
+ // Act
+ simpleLog.Log(message);
+
+ // Assert
+ Assert.AreEqual(1, _sysTime.ReceivedCalls().Count());
+ Assert.AreEqual(asExp[0],DebugLog);
+ }
+
+ [TestMethod]
+ public void LogTest1()
+ {
+ // Arrange
+ var message = "Test message";
+ var exception = new Exception("Test exception");
+
+ // Act
+ simpleLog.Log(message, exception);
+
+ // Assert
+ Assert.AreEqual(1, _sysTime.ReceivedCalls().Count());
+ Assert.AreEqual("08/24/2022 12:00:00: Err: Test message, System.Exception: Test exception\r\n", DebugLog);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Models/TemplateModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Models/TemplateModelTests.cs
new file mode 100644
index 000000000..89b3235c8
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Models/TemplateModelTests.cs
@@ -0,0 +1,72 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplateTests
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using CommunityToolkit.Mvvm.ComponentModel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using NSubstitute;
+using System.ComponentModel;
+using BaseLib.Models.Interfaces;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.Models.Tests;
+
+///
+/// Defines test class TemplateModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class TemplateModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ TemplateModel testModel;
+ ISysTime _sysTime;
+ ILog _log;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ _sysTime = Substitute.For();
+ _log = Substitute.For();
+ testModel = new(_sysTime,_log);
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(TemplateModel));
+ Assert.IsInstanceOfType(testModel, typeof(ObservableObject));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..b89223048
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Properties/SettingsTests.cs
@@ -0,0 +1,70 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.Properties.Tests;
+
+///
+/// Defines test class SettingsTests.
+///
+///
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test item
+ ///
+ ///
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ ///
+ /// Defines the test method DefaultInstanceTest.
+ ///
+ ///
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ValueConverters/DateTimeValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ValueConverters/DateTimeValueConverterTests.cs
new file mode 100644
index 000000000..047bb09f2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ValueConverters/DateTimeValueConverterTests.cs
@@ -0,0 +1,72 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplateTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class DateTimeValueConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ readonly DateTimeValueConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow("2023-01-01 22:00",null, "01/01/2023 23:00:00")]
+ [DataRow("2023-05-02 12:30","MM/dd/yyyy", "05.02.2023")]
+ [DataRow("Hallo",null, "Hallo")]
+ [DataRow(null,null, "")]
+ public void ConvertTest(object? value, string? param, string expected)
+ {
+ if (value is string s && DateTime.TryParse(s, CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal,out var dt)) value = dt;
+ var result = testConv.Convert(value, typeof(string), param, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod]
+ [DataRow("2023-01-01 22:30", null, "01/01/2023 22:30:00")]
+ [DataRow("2023-05-02", "MM/dd/yyyy", "05.02.2023")]
+ [DataRow("01.01.0001 00:00:00", null, "Hallo")]
+ [DataRow("01.01.0001 00:00:00", "", 100)]
+ [DataRow("01.01.0001 00:00:00", "", null)]
+ public void ConvertBackTest(object? value, string? param, object? expected)
+ {
+ if (value is string s && DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var dt)) value = dt;
+ var result = testConv.ConvertBack(expected, typeof(DateTime), param, CultureInfo.InvariantCulture);
+ Assert.AreEqual(value, result);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..f836f6b2d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,57 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.ViewModels.Tests;
+
+///
+/// Defines test class MainWindowViewModelTests.
+///
+///
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public override void Init()
+ {
+ base.Init();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ViewModels/TemplateViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ViewModels/TemplateViewModelTests.cs
new file mode 100644
index 000000000..0dfb20031
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/ViewModels/TemplateViewModelTests.cs
@@ -0,0 +1,79 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using System;
+using System.ComponentModel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
+using MVVM.ViewModel;
+using BaseLib.Helper;
+using MVVM_00_IoCTemplate.Models;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.ViewModels.Tests;
+
+///
+/// Defines test class TemplateViewModelTests.
+///
+///
+[TestClass()]
+public class TemplateViewModelTests : BaseTestViewModel
+{
+ /// The model
+ private ITemplateModel? _model;
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public override void Init()
+ {
+ IoC.GetReqSrv = (t) => t switch
+ {
+ Type _t when _t == typeof(ITemplateModel) => _model ??= Substitute.For(),
+ _ => throw new System.NotImplementedException($"No code for {t}")
+ };
+ base.Init();
+ _model.Now.Returns(new DateTime(2022, 08, 24, 12, 0, 0));
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(TemplateViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ Assert.IsNotNull(_model);
+ }
+
+ [TestMethod()]
+ [DataRow(nameof(ITemplateModel.Now), new[] { "PropChg(MVVM_00_IoCTemplate.ViewModels.TemplateViewModel,Now)=24.08.2022 12:00:00\r\n" })]
+ [DataRow("HasErrors", new[] { "PropChg(MVVM_00_IoCTemplate.ViewModels.TemplateViewModel,HasErrors)=False\r\n" })]
+ [DataRow("Dummy", new[] { "PropChg(MVVM_00_IoCTemplate.ViewModels.TemplateViewModel,Dummy)=\r\n" })]
+ public void PropertyChangedTest(string prop, string[] asExp)
+ {
+ _model.PropertyChanged += Raise.Event(_model, new PropertyChangedEventArgs(prop));
+ if (prop == nameof(ITemplateModel.Now))
+ _ = _model.Received(2).Now;
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..a98708c76
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Views/MainWindowTests.cs
@@ -0,0 +1,61 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+///
+[TestClass()]
+public class MainWindowTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ MainWindow testView;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Views/TemplateViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Views/TemplateViewTests.cs
new file mode 100644
index 000000000..cc0d2805d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_IoCTemplateTests/Views/TemplateViewTests.cs
@@ -0,0 +1,61 @@
+// ***********************************************************************
+// Assembly : MVVM_00_IoCTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_IoCTemplate.Views.Tests;
+
+///
+/// Defines test class TemplateViewTests.
+///
+///
+[TestClass()]
+public class TemplateViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ TemplateView testView;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(TemplateView));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/App.xaml
new file mode 100644
index 000000000..878fee64b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/App.xaml.cs
new file mode 100644
index 000000000..e93f5beaa
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/App.xaml.cs
@@ -0,0 +1,27 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_00_Template
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
+namespace MVVM_00_Template.Models { }
+namespace MVVM_00_Template.ValueConverter { }
+namespace MVVM_00_Template.Services { }
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MVVM_00_Template.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MVVM_00_Template.csproj
new file mode 100644
index 000000000..ef7ad2102
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MVVM_00_Template.csproj
@@ -0,0 +1,37 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MVVM_00_Template_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MVVM_00_Template_net.csproj
new file mode 100644
index 000000000..6e385d906
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MVVM_00_Template_net.csproj
@@ -0,0 +1,43 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MainWindow.xaml
new file mode 100644
index 000000000..937296c3b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MainWindow.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MainWindow.xaml.cs
new file mode 100644
index 000000000..5d8960208
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_00_Template;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Models/.info b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Models/.info
new file mode 100644
index 000000000..973554c8d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Models/.info
@@ -0,0 +1 @@
+Folder for Models
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..24b35ef03
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Resources.Designer.cs
@@ -0,0 +1,118 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_00_Template.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_00_Template.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die a Template for MVVM-Examples ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_00_Template.Views"
+ /// xmlns:p="clr-namespace:MVVM_00_Template.Properties"
+ /// xmlns:vc="clr-namespace:MVVM_00_Template.ValueConverter"
+ /// xmlns:mvvm="clr-namespace:MVVM_00_T [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string TemplateView {
+ get {
+ return ResourceManager.GetString("TemplateView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_00_Template
+ ///// Author : Mir
+ ///// Created : 08-11-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 08-24-2022
+ ///// ***********************************************************************
+ ///// <copyright file="MainWindowViewModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2022
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ***************************************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string TemplateViewModel {
+ get {
+ return ResourceManager.GetString("TemplateViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Template-View ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Resources.resx
new file mode 100644
index 000000000..13cb75366
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Resources.resx
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ a Template for MVVM-Examples
+
+
+
+ ..\Views\TemplateView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\TemplateViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ Template-View
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..b458ebdf0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_00_Template.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Settings.settings
new file mode 100644
index 000000000..049245f40
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ValueConverters/.info b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ValueConverters/.info
new file mode 100644
index 000000000..b52875a9b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ValueConverters/.info
@@ -0,0 +1 @@
+Folder for ValueConverters
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ValueConverters/DateTimeValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ValueConverters/DateTimeValueConverter.cs
new file mode 100644
index 000000000..b2b398dc9
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ValueConverters/DateTimeValueConverter.cs
@@ -0,0 +1,62 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_00_Template.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class DateTimeValueConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is DateTime dt)
+ if (parameter is string spar)
+ return dt.ToString(spar);
+ else
+ return dt.ToString(culture);
+ else
+ return value?.ToString() ?? "";
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string sval && DateTime.TryParse(sval.Trim(),culture, DateTimeStyles.AssumeLocal, out var dt))
+ return dt;
+ else
+ return DateTime.MinValue;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/.info b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/.info
new file mode 100644
index 000000000..519986dc2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/.info
@@ -0,0 +1 @@
+Folder for ViewModels
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..fbd5b9612
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_00_Template.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModel
+{
+ #region Properties
+ #endregion
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+
+#if !NET5_0_OR_GREATER
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~MainWindowViewModel()
+ {
+ return;
+ }
+#endif
+ #endregion
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/TemplateViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/TemplateViewModel.cs
new file mode 100644
index 000000000..83dab094d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/ViewModels/TemplateViewModel.cs
@@ -0,0 +1,54 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System;
+using System.Timers;
+
+namespace MVVM_00_Template.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class TemplateViewModel : BaseViewModel
+{
+ #region Properties
+ private readonly Timer _timer;
+ public static Func GetNow { get; set; } = () => DateTime.Now;
+ public DateTime Now { get => GetNow(); }
+ #endregion
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TemplateViewModel()
+ {
+ _timer = new Timer() { Interval = 250};
+ _timer.Elapsed += (s, e) => RaisePropertyChanged(nameof(Now));
+ _timer.Start();
+ }
+
+#if !NET5_0_OR_GREATER
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~TemplateViewModel()
+ {
+ _timer.Stop();
+ return;
+ }
+#endif
+ #endregion
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/.info
new file mode 100644
index 000000000..0791c329c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/.info
@@ -0,0 +1 @@
+Folder for Views
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/TemplateView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/TemplateView.xaml
new file mode 100644
index 000000000..561edd2b5
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/TemplateView.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/TemplateView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/TemplateView.xaml.cs
new file mode 100644
index 000000000..8772b0de4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_Template/Views/TemplateView.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_00_Template.Views;
+
+///
+/// Interaktionslogik für TemplateView.xaml
+///
+public partial class TemplateView : Page
+{
+ public TemplateView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/MVVM_00_TemplateTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/MVVM_00_TemplateTests.csproj
index 5046ace6f..44b502663 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/MVVM_00_TemplateTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/MVVM_00_TemplateTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/MVVM_00_Template_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/MVVM_00_Template_netTests.csproj
index 0b0c45898..940857e1f 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/MVVM_00_Template_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/MVVM_00_Template_netTests.csproj
@@ -1,15 +1,21 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
false
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..f970887af
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Properties/SettingsTests.cs
@@ -0,0 +1,70 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_Template.Properties.Tests;
+
+///
+/// Defines test class SettingsTests.
+///
+///
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test item
+ ///
+ ///
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ ///
+ /// Defines the test method DefaultInstanceTest.
+ ///
+ ///
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ValueConverters/DateTimeValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ValueConverters/DateTimeValueConverterTests.cs
new file mode 100644
index 000000000..065d48409
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ValueConverters/DateTimeValueConverterTests.cs
@@ -0,0 +1,72 @@
+// ***********************************************************************
+// Assembly : MVVM_00_TemplateTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_Template.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class DateTimeValueConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ readonly DateTimeValueConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow("2023-01-01 22:00",null, "01/01/2023 23:00:00")]
+ [DataRow("2023-05-02 12:30","MM/dd/yyyy", "05.02.2023")]
+ [DataRow("Hallo",null, "Hallo")]
+ [DataRow(null,null, "")]
+ public void ConvertTest(object? value, string? param, string expected)
+ {
+ if (value is string s && DateTime.TryParse(s, CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal,out var dt)) value = dt;
+ var result = testConv.Convert(value, typeof(string), param, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod]
+ [DataRow("2023-01-01 22:30", null, "01/01/2023 22:30:00")]
+ [DataRow("2023-05-02", "MM/dd/yyyy", "05.02.2023")]
+ [DataRow("01.01.0001 00:00:00", null, "Hallo")]
+ [DataRow("01.01.0001 00:00:00", "", 100)]
+ [DataRow("01.01.0001 00:00:00", "", null)]
+ public void ConvertBackTest(object? value, string? param, object? expected)
+ {
+ if (value is string s && DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var dt)) value = dt;
+ var result = testConv.ConvertBack(expected, typeof(DateTime), param, CultureInfo.InvariantCulture);
+ Assert.AreEqual(value, result);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..76cd054dc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,65 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_Template.ViewModels.Tests;
+
+///
+/// Defines test class MainWindowViewModelTests.
+///
+///
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchg)
+ npchg.PropertyChanging += OnVMPropertyChanging;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ViewModels/TemplateViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ViewModels/TemplateViewModelTests.cs
new file mode 100644
index 000000000..d8058ed6a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/ViewModels/TemplateViewModelTests.cs
@@ -0,0 +1,65 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_Template.ViewModels.Tests;
+
+///
+/// Defines test class TemplateViewModelTests.
+///
+///
+[TestClass()]
+public class TemplateViewModelTests:BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ TemplateViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(TemplateViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..1a32cedb1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Views/MainWindowTests.cs
@@ -0,0 +1,61 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_Template.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+///
+[TestClass()]
+public class MainWindowTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ MainWindow testView;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Views/TemplateViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Views/TemplateViewTests.cs
new file mode 100644
index 000000000..dbf3f11d6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00_TemplateTests/Views/TemplateViewTests.cs
@@ -0,0 +1,61 @@
+// ***********************************************************************
+// Assembly : MVVM_00_Template_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00_Template.Views.Tests;
+
+///
+/// Defines test class TemplateViewTests.
+///
+///
+[TestClass()]
+public class TemplateViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ TemplateView testView;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(TemplateView));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate.sln b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate.sln
new file mode 100644
index 000000000..2ca200c2d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate.sln
@@ -0,0 +1,67 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projektmappenelemente", "Projektmappenelemente", "{658BD492-33FF-4995-AAE7-4F6894E92F0C}"
+ ProjectSection(SolutionItems) = preProject
+ MVVM_Tutorial.props = MVVM_Tutorial.props
+ MVVM_Tutorial_net.props = MVVM_Tutorial_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLib", "..\Libraries\MVVM_BaseLib\MVVM_BaseLib.csproj", "{890CF504-3814-443B-9EE6-E8BCACF68203}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{AF041458-CF94-4D6D-BB0F-8BC50F4F099C}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Libraries\Libraries_net.props = ..\Libraries\Libraries_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLibTests", "..\Libraries\MVVM_BaseLibTests\MVVM_BaseLibTests.csproj", "{66EB60F2-523D-47C8-95EA-C7E10759E239}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_00a_CTTemplate", "MVVM_00a_CTTemplate\MVVM_00a_CTTemplate.csproj", "{16A3E367-674C-4959-8B31-7681B0E8258C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_00a_CTTemplate_net", "MVVM_00a_CTTemplate\MVVM_00a_CTTemplate_net.csproj", "{D6BFC652-822A-4E7D-9846-7F529C584BAC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_00a_CTTemplate_netTests", "MVVM_00a_CTTemplateTests\MVVM_00a_CTTemplate_netTests.csproj", "{A7844F45-74D9-4A81-9C27-D0E17ED0F851}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_00a_CTTemplateTests", "MVVM_00a_CTTemplateTests\MVVM_00a_CTTemplateTests.csproj", "{7FF12BBB-AA2E-48C1-B283-0665570902F8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.Build.0 = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.Build.0 = Release|Any CPU
+ {16A3E367-674C-4959-8B31-7681B0E8258C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {16A3E367-674C-4959-8B31-7681B0E8258C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {16A3E367-674C-4959-8B31-7681B0E8258C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {16A3E367-674C-4959-8B31-7681B0E8258C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6BFC652-822A-4E7D-9846-7F529C584BAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6BFC652-822A-4E7D-9846-7F529C584BAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6BFC652-822A-4E7D-9846-7F529C584BAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6BFC652-822A-4E7D-9846-7F529C584BAC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7844F45-74D9-4A81-9C27-D0E17ED0F851}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7844F45-74D9-4A81-9C27-D0E17ED0F851}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7844F45-74D9-4A81-9C27-D0E17ED0F851}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A7844F45-74D9-4A81-9C27-D0E17ED0F851}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7FF12BBB-AA2E-48C1-B283-0665570902F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FF12BBB-AA2E-48C1-B283-0665570902F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FF12BBB-AA2E-48C1-B283-0665570902F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FF12BBB-AA2E-48C1-B283-0665570902F8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {66EB60F2-523D-47C8-95EA-C7E10759E239} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ EndGlobalSection
+EndGlobal
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/App.xaml
new file mode 100644
index 000000000..084fdb47e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/App.xaml.cs
new file mode 100644
index 000000000..ed131f8d8
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/App.xaml.cs
@@ -0,0 +1,27 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_00a_CTTemplate
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
+namespace MVVM_00a_CTTemplate.Models { }
+namespace MVVM_00a_CTTemplate.ValueConverter { }
+namespace MVVM_00a_CTTemplate.Services { }
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/AssemblyInfo.cs
new file mode 100644
index 000000000..f60efc019
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MVVM_00a_CTTemplate.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MVVM_00a_CTTemplate.csproj
new file mode 100644
index 000000000..22f01783f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MVVM_00a_CTTemplate.csproj
@@ -0,0 +1,37 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MVVM_00a_CTTemplate_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MVVM_00a_CTTemplate_net.csproj
new file mode 100644
index 000000000..6e385d906
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MVVM_00a_CTTemplate_net.csproj
@@ -0,0 +1,43 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MainWindow.xaml
new file mode 100644
index 000000000..82596ca56
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MainWindow.xaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MainWindow.xaml.cs
new file mode 100644
index 000000000..e4abc353f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_00a_CTTemplate;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/.info b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/.info
new file mode 100644
index 000000000..62f215ed0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/.info
@@ -0,0 +1 @@
+Folder for Models
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/ITemplateModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/ITemplateModel.cs
new file mode 100644
index 000000000..b43c615be
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/ITemplateModel.cs
@@ -0,0 +1,40 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using System;
+using System.ComponentModel;
+
+///
+/// The Models namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.Models;
+
+///
+/// Interface ITemplateModel
+///
+///
+public interface ITemplateModel
+{
+ ///
+ /// Gets the now.
+ ///
+ /// The now.
+ ///
+ DateTime Now { get; }
+ ///
+ /// Occurs when [property changed].
+ ///
+ ///
+ event PropertyChangedEventHandler PropertyChanged;
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/TemplateModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/TemplateModel.cs
new file mode 100644
index 000000000..81cf7d603
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Models/TemplateModel.cs
@@ -0,0 +1,77 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Timers;
+
+///
+/// The Models namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.Models;
+
+///
+/// Class TemplateModel.
+/// Implements the
+/// Implements the
+///
+///
+///
+///
+public partial class TemplateModel :ObservableObject, ITemplateModel
+{
+ #region Properties
+ ///
+ /// The timer
+ ///
+ ///
+ private readonly Timer _timer;
+ ///
+ /// Gets or sets the get now.
+ ///
+ /// The get now.
+ ///
+ public static Func GetNow { get; set; } = () => DateTime.Now;
+ ///
+ /// Gets the now.
+ ///
+ /// The now.
+ ///
+ public DateTime Now { get => GetNow(); }
+ #endregion
+
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public TemplateModel()
+ {
+ _timer = new(250d);
+ _timer.Elapsed += (s, e) => OnPropertyChanged(nameof(Now));
+ _timer.Start();
+ }
+
+#if !NET5_0_OR_GREATER
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~TemplateModel()
+ {
+ _timer.Stop();
+ return;
+ }
+#endif
+ #endregion
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..735e9b340
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Resources.Designer.cs
@@ -0,0 +1,118 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_00a_CTTemplate.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_00a_CTTemplate.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM-Template using Community-Toolkit ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_00a_CTTemplate.Views"
+ /// xmlns:p="clr-namespace:MVVM_00a_CTTemplate.Properties"
+ /// xmlns:vc="clr-namespace:MVVM_00a_CTTemplate.ValueConverter"
+ /// xmlns:mvvm="clr-namespace: [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string TemplateView {
+ get {
+ return ResourceManager.GetString("TemplateView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_00a_CTTemplate
+ ///// Author : Mir
+ ///// Created : 08-11-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 08-24-2022
+ ///// ***********************************************************************
+ ///// <copyright file="MainWindowViewModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2022
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ************************************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string TemplateViewModel {
+ get {
+ return ResourceManager.GetString("TemplateViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die CT-Template-View ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Resources.resx
new file mode 100644
index 000000000..65438674b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Resources.resx
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ MVVM-Template using Community-Toolkit
+
+
+
+ ..\Views\TemplateView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\TemplateViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ CT-Template-View
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..c9775d595
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_00a_CTTemplate.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Settings.settings
new file mode 100644
index 000000000..049245f40
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ValueConverters/.info b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ValueConverters/.info
new file mode 100644
index 000000000..b4bc464df
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ValueConverters/.info
@@ -0,0 +1 @@
+Folder for ValueConverters
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ValueConverters/DateTimeValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ValueConverters/DateTimeValueConverter.cs
new file mode 100644
index 000000000..11631532f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ValueConverters/DateTimeValueConverter.cs
@@ -0,0 +1,62 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_00a_CTTemplate.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class DateTimeValueConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is DateTime dt)
+ if (parameter is string spar)
+ return dt.ToString(spar);
+ else
+ return dt.ToString(culture);
+ else
+ return value?.ToString() ?? "";
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string sval && DateTime.TryParse(sval.Trim(),culture, DateTimeStyles.AssumeLocal, out var dt))
+ return dt;
+ else
+ return DateTime.MinValue;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/.info b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/.info
new file mode 100644
index 000000000..a3ed848fb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/.info
@@ -0,0 +1 @@
+Folder for ViewModels
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..17a53b863
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_00a_CTTemplate.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModelCT
+{
+ #region Properties
+ #endregion
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+
+#if !NET5_0_OR_GREATER
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~MainWindowViewModel()
+ {
+ return;
+ }
+#endif
+ #endregion
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/TemplateViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/TemplateViewModel.cs
new file mode 100644
index 000000000..750867671
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/ViewModels/TemplateViewModel.cs
@@ -0,0 +1,56 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using MVVM_00a_CTTemplate.Models;
+using System;
+using System.ComponentModel;
+
+namespace MVVM_00a_CTTemplate.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public partial class TemplateViewModel : BaseViewModelCT
+{
+ #region Properties
+ public static Func GetModel { get; set; } = () => new TemplateModel();
+
+ private readonly ITemplateModel _model;
+
+ public DateTime Now => _model.Now;
+ #endregion
+
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TemplateViewModel():this(GetModel())
+ {
+ }
+
+ public TemplateViewModel(ITemplateModel model)
+ {
+ _model = model;
+ _model.PropertyChanged += OnMPropertyChanged;
+ }
+
+ private void OnMPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ OnPropertyChanged(e.PropertyName);
+ }
+
+ #endregion
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/.info
new file mode 100644
index 000000000..65a00d680
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/.info
@@ -0,0 +1 @@
+Folder for Views
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/TemplateView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/TemplateView.xaml
new file mode 100644
index 000000000..b4c4b5487
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/TemplateView.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/TemplateView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/TemplateView.xaml.cs
new file mode 100644
index 000000000..2998912c7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplate/Views/TemplateView.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_00a_CTTemplate.Views;
+
+///
+/// Interaktionslogik für TemplateView.xaml
+///
+public partial class TemplateView : Page
+{
+ public TemplateView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/AppTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/AppTests.cs
new file mode 100644
index 000000000..1e23cb741
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/AppTests.cs
@@ -0,0 +1,56 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using BaseLib.Helper;
+using MVVM_00a_CTTemplate.Models;
+using System;
+
+namespace MVVM_00a_CTTemplate.Tests;
+
+internal class TestApp : App
+{
+ public void DoStartUp()
+ {
+ OnStartup(null);
+ }
+}
+[TestClass()]
+public class AppTests
+{
+ static TestApp app = new();
+ private Func _gsold;
+ private Func _grsold;
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ _gsold = IoC.GetSrv;
+ _grsold = IoC.GetReqSrv;
+ IoC.GetReqSrv = (t) =>t switch {
+ _ when t == typeof(ITemplateModel) => new TemplateModel(),
+ _ => throw new ArgumentException() };
+ }
+
+ [TestCleanup]
+ public void CleanUp()
+ {
+ IoC.GetSrv = _gsold;
+ IoC.GetReqSrv = _grsold;
+ }
+
+ [TestMethod]
+ public void AppTest()
+ {
+ Assert.IsNotNull(app);
+ }
+
+ [TestMethod]
+ public void AppTest2()
+ {
+ app.DoStartUp();
+ Assert.IsNotNull(IoC.GetReqSrv(typeof(ITemplateModel)));
+ Assert.IsNull(IoC.GetSrv(typeof(App)));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/MVVM_00a_CTTemplateTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/MVVM_00a_CTTemplateTests.csproj
index 622a473c0..7e33304e8 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/MVVM_00a_CTTemplateTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/MVVM_00a_CTTemplateTests.csproj
@@ -9,8 +9,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/MVVM_00a_CTTemplate_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/MVVM_00a_CTTemplate_netTests.csproj
index 2b0fad7f5..2ccc59cab 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/MVVM_00a_CTTemplate_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/MVVM_00a_CTTemplate_netTests.csproj
@@ -1,16 +1,22 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
false
true
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Models/TemplateModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Models/TemplateModelTests.cs
new file mode 100644
index 000000000..8119f8134
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Models/TemplateModelTests.cs
@@ -0,0 +1,66 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplateTests
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using CommunityToolkit.Mvvm.ComponentModel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System.ComponentModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.Models.Tests;
+
+///
+/// Defines test class TemplateModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class TemplateModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ TemplateModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(TemplateModel));
+ Assert.IsInstanceOfType(testModel, typeof(ObservableObject));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..8bf3c3a7b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Properties/SettingsTests.cs
@@ -0,0 +1,70 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.Properties.Tests;
+
+///
+/// Defines test class SettingsTests.
+///
+///
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test item
+ ///
+ ///
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ ///
+ /// Defines the test method DefaultInstanceTest.
+ ///
+ ///
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ValueConverters/DateTimeValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ValueConverters/DateTimeValueConverterTests.cs
new file mode 100644
index 000000000..b0b19fbd6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ValueConverters/DateTimeValueConverterTests.cs
@@ -0,0 +1,72 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplateTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class DateTimeValueConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ readonly DateTimeValueConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow("2023-01-01 22:00",null, "01/01/2023 23:00:00")]
+ [DataRow("2023-05-02 12:30","MM/dd/yyyy", "05.02.2023")]
+ [DataRow("Hallo",null, "Hallo")]
+ [DataRow(null,null, "")]
+ public void ConvertTest(object? value, string? param, string expected)
+ {
+ if (value is string s && DateTime.TryParse(s, CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal,out var dt)) value = dt;
+ var result = testConv.Convert(value, typeof(string), param, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod]
+ [DataRow("2023-01-01 22:30", null, "01/01/2023 22:30:00")]
+ [DataRow("2023-05-02", "MM/dd/yyyy", "05.02.2023")]
+ [DataRow("01.01.0001 00:00:00", null, "Hallo")]
+ [DataRow("01.01.0001 00:00:00", "", 100)]
+ [DataRow("01.01.0001 00:00:00", "", null)]
+ public void ConvertBackTest(object? value, string? param, object? expected)
+ {
+ if (value is string s && DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var dt)) value = dt;
+ var result = testConv.ConvertBack(expected, typeof(DateTime), param, CultureInfo.InvariantCulture);
+ Assert.AreEqual(value, result);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..a05269247
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,65 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.ViewModels.Tests;
+
+///
+/// Defines test class MainWindowViewModelTests.
+///
+///
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchg)
+ npchg.PropertyChanging += OnVMPropertyChanging;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ViewModels/TemplateViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ViewModels/TemplateViewModelTests.cs
new file mode 100644
index 000000000..3dc45ffcd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/ViewModels/TemplateViewModelTests.cs
@@ -0,0 +1,65 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.ViewModels.Tests;
+
+///
+/// Defines test class TemplateViewModelTests.
+///
+///
+[TestClass()]
+public class TemplateViewModelTests:BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ TemplateViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(TemplateViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..865157d11
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Views/MainWindowTests.cs
@@ -0,0 +1,61 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+///
+[TestClass()]
+public class MainWindowTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ MainWindow testView;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Views/TemplateViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Views/TemplateViewTests.cs
new file mode 100644
index 000000000..4e0e32f93
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_00a_CTTemplateTests/Views/TemplateViewTests.cs
@@ -0,0 +1,61 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_00a_CTTemplate.Views.Tests;
+
+///
+/// Defines test class TemplateViewTests.
+///
+///
+[TestClass()]
+public class TemplateViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ TemplateView testView;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(TemplateView));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.config b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.config
new file mode 100644
index 000000000..193aecc67
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.xaml
new file mode 100644
index 000000000..7abadb922
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.xaml.cs
new file mode 100644
index 000000000..597ef4a73
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_03_NotifyChange;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MVVM_03_NotifyChange.csproj b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MVVM_03_NotifyChange.csproj
new file mode 100644
index 000000000..691506e9e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MVVM_03_NotifyChange.csproj
@@ -0,0 +1,47 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MVVM_03_NotifyChange_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MVVM_03_NotifyChange_net.csproj
new file mode 100644
index 000000000..cc1d88249
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MVVM_03_NotifyChange_net.csproj
@@ -0,0 +1,53 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MainWindow.xaml
new file mode 100644
index 000000000..7d4e98fa8
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MainWindow.xaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MainWindow.xaml.cs
new file mode 100644
index 000000000..c5d987949
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_03_NotifyChange;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..f48b52b54
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-24-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("WpfApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("WpfApp")]
+[assembly: AssemblyCopyright("Copyright © JC-Soft 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..629c042dd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_03_NotifyChange.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_03_NotifyChange.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Tutorial #03: How to use NotifyPropertyChange to update information ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Firstname: ähnelt.
+ ///
+ public static string FirstName {
+ get {
+ return ResourceManager.GetString("FirstName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Fullname: ähnelt.
+ ///
+ public static string FullName {
+ get {
+ return ResourceManager.GetString("FullName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Lastname: ähnelt.
+ ///
+ public static string LastName {
+ get {
+ return ResourceManager.GetString("LastName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page x:Class="MVVM_03_NotifyChange.Views.NotifyChangeView"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_03_NotifyChange"
+ /// xmlns:p="clr-namespace:MVVM_03_NotifyChange.Properties"
+ /// xmlns:mvvm="clr-namespace:MVVM_03_NotifyCha [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string NotifyChangeView {
+ get {
+ return ResourceManager.GetString("NotifyChangeView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die using MVVM.ViewModel;
+ ///
+ ///namespace MVVM_03_NotifyChange.ViewModels
+ ///{
+ /// public class NotifyChangeViewModel : BaseViewModel
+ /// {
+ /// #region Properties
+ /// private string _firstname;
+ /// private string _lastname;
+ ///
+ /// public string Firstname { get => _firstname; set => SetProperty(ref _firstname, value); }
+ /// public string Lastname { get => _lastname; set => SetProperty(ref _lastname , value); }
+ /// public string Fullname => $"{Lastname}, {Firstname}";
+ /// #endregion [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string NotifyChangeViewModel {
+ get {
+ return ResourceManager.GetString("NotifyChangeViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM #03 Notify Property Change ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.de.resx b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.de.resx
new file mode 100644
index 000000000..281569f9e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.de.resx
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Vorname:
+
+
+ vollständiger Name:
+
+
+ Nachname:
+
+
+
+ ..\Views\NotifyChangeView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\NotifyChangeViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ MVVM #03 Notify Property Change
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.resx
new file mode 100644
index 000000000..09c9df3ee
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Resources.resx
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Tutorial #03: How to use NotifyPropertyChange to update information
+
+
+ Firstname:
+
+
+ Fullname:
+
+
+ Lastname:
+
+
+
+ ..\Views\NotifyChangeView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\NotifyChangeViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ MVVM #03 Notify Property Change
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..99375619d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_03_NotifyChange.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Settings.settings
new file mode 100644
index 000000000..049245f40
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..ed64ad60d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,32 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_03_NotifyChange.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModel
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/ViewModels/NotifyChangeViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/ViewModels/NotifyChangeViewModel.cs
new file mode 100644
index 000000000..298460d64
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/ViewModels/NotifyChangeViewModel.cs
@@ -0,0 +1,26 @@
+using MVVM.ViewModel;
+
+namespace MVVM_03_NotifyChange.ViewModels;
+
+public class NotifyChangeViewModel : BaseViewModel
+{
+ #region Properties
+ private string _firstname;
+ private string _lastname;
+
+ public string Firstname { get => _firstname; set => SetProperty(ref _firstname, value); }
+ public string Lastname { get => _lastname; set => SetProperty(ref _lastname , value); }
+ public string Fullname => $"{Lastname}, {Firstname}";
+ #endregion
+
+ #region Methods
+ public NotifyChangeViewModel()
+ {
+ _firstname = "Dave";
+ _lastname = "Dev";
+ AddPropertyDependency(nameof(Fullname), nameof(Lastname));
+ AddPropertyDependency(nameof(Fullname), nameof(Firstname));
+ }
+ #endregion
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/.info
new file mode 100644
index 000000000..0791c329c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/.info
@@ -0,0 +1 @@
+Folder for Views
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/NotifyChangeView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/NotifyChangeView.xaml
new file mode 100644
index 000000000..82cea2f49
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/NotifyChangeView.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/NotifyChangeView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/NotifyChangeView.xaml.cs
new file mode 100644
index 000000000..a7893001b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChange/Views/NotifyChangeView.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_03_NotifyChange.Views;
+
+///
+/// Interaktionslogik für DelegateCommandView.xaml
+///
+public partial class NotifyChangeView : Page
+{
+ public NotifyChangeView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/MVVM_03_NotifyChangeTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/MVVM_03_NotifyChangeTests.csproj
index 950bb6d57..c037e16c3 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/MVVM_03_NotifyChangeTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/MVVM_03_NotifyChangeTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/MVVM_03_NotifyChange_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/MVVM_03_NotifyChange_netTests.csproj
index 765901574..220143877 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/MVVM_03_NotifyChange_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/MVVM_03_NotifyChange_netTests.csproj
@@ -1,14 +1,20 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..2b6a873f2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Properties/SettingsTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+namespace MVVM_03_NotifyChange.Properties.Tests;
+
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..9877e1d66
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,65 @@
+// ***********************************************************************
+// Assembly : MVVM_03_NotifyChange_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_03_NotifyChange.ViewModels.Tests;
+
+///
+/// Defines test class MainWindowViewModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class MainWindowViewModelTests: BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/ViewModels/NotifyChangeViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/ViewModels/NotifyChangeViewModelTests.cs
new file mode 100644
index 000000000..dc944f4bb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/ViewModels/NotifyChangeViewModelTests.cs
@@ -0,0 +1,75 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+
+namespace MVVM_03_NotifyChange.ViewModels.Tests;
+
+[TestClass()]
+public class NotifyChangeViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ NotifyChangeViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ ClearLog();
+ }
+
+ [TestMethod]
+ public void NotifyChangeViewModelTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(NotifyChangeViewModel));
+ Assert.AreEqual("Dave", testModel.Firstname);
+ Assert.AreEqual("Dev", testModel.Lastname);
+ Assert.AreEqual("Dev, Dave", testModel.Fullname);
+ Assert.AreEqual("", DebugLog);
+ }
+
+ [TestMethod()]
+ [DataRow("", new string[] { "","Dev, ", "PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Dev, \r\nPropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=\r\n" })]
+ [DataRow("Peter", new string[] { "Peter", "Dev, Peter", "PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Dev, Peter\r\nPropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Peter\r\n" })]
+ [DataRow("Steve\tEugene", new string[] { "Eugene", "Dev, Eugene", @"PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Dev, Steve
+PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Steve
+PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Dev, Eugene
+PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Eugene
+" })]
+ public void FirstnameTest(string name, string[] asExp)
+ {
+ // Arrange
+ var asName=name.Split('\t');
+ // Act
+ foreach (var item in asName)
+ testModel.Firstname = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Firstname,"Firstname");
+ Assert.AreEqual(asExp[1], testModel.Fullname, "Fullname");
+ Assert.AreEqual(asExp[2], DebugLog, "DebugOut");
+ }
+
+ [TestMethod()]
+ [DataRow("", new string[] { "", ", Dave", "PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=, Dave\r\nPropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=\r\n" })]
+ [DataRow("Miller", new string[] { "Miller", "Miller, Dave", "PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Miller, Dave\r\nPropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Miller\r\n" })]
+ [DataRow("Fry\tWebb", new string[] { "Webb", "Webb, Dave", @"PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Fry, Dave
+PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Fry
+PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Webb, Dave
+PropChg(MVVM_03_NotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Webb
+" })]
+ public void LastnameTest(string name, string[] asExp)
+ {
+ // Arrange
+ var asName = name.Split('\t');
+ // Act
+ foreach (var item in asName)
+ testModel.Lastname = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Lastname, "Firstname");
+ Assert.AreEqual(asExp[1], testModel.Fullname, "Fullname");
+ Assert.AreEqual(asExp[2], DebugLog, "DebugOut");
+ }
+
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..4505549cc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Views/MainWindowTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_03_NotifyChange.Views.Tests;
+
+[TestClass()]
+public class MainWindowTests
+{
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Views/NotifyChangeViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Views/NotifyChangeViewTests.cs
new file mode 100644
index 000000000..f7da17080
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03_NotifyChangeTests/Views/NotifyChangeViewTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_03_NotifyChange.Views.Tests;
+
+[TestClass()]
+public class NotifyChangeViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ NotifyChangeView testView;
+ private object vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize()]
+ public void Init()
+ {
+ Thread thread = new(() =>
+ {
+ testView = new();
+ vm = testView.DataContext;
+ });
+ thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ thread.Start();
+ thread.Join(); //Wait for the thread to end
+ }
+
+ [TestMethod()]
+ public void ValidationPageTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsNotNull(vm);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.config b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.config
new file mode 100644
index 000000000..996fd1e81
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.xaml
new file mode 100644
index 000000000..df5329b3f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.xaml.cs
new file mode 100644
index 000000000..7e01c2fbd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_03a_CTNotifyChange;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MVVM_03a_CTNotifyChange.csproj b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MVVM_03a_CTNotifyChange.csproj
new file mode 100644
index 000000000..6725c59b2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MVVM_03a_CTNotifyChange.csproj
@@ -0,0 +1,47 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MVVM_03a_CTNotifyChange_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MVVM_03a_CTNotifyChange_net.csproj
new file mode 100644
index 000000000..77efc8b7b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MVVM_03a_CTNotifyChange_net.csproj
@@ -0,0 +1,53 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MainWindow.xaml
new file mode 100644
index 000000000..dc575da8e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MainWindow.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MainWindow.xaml.cs
new file mode 100644
index 000000000..b72aac490
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_03a_CTNotifyChange;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..ed1846baf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-24-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen ber eine Assembly werden ber die folgenden
+// Attribute gesteuert. ndern Sie diese Attributwerte, um die Informationen zu ndern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("WpfApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("WpfApp")]
+[assembly: AssemblyCopyright("Copyright JC-Soft 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// fr COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen mssen, sollten Sie das ComVisible-Attribut fr diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei bereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwrterbcher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wrterbchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwrterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwrterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen fr eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie knnen alle Werte angeben oder Standardwerte fr die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..fce5a5e2b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// nderungen an dieser Datei knnen falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_03a_CTNotifyChange.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse ber ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufgen oder zu entfernen, bearbeiten Sie die .ResX-Datei und fhren dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurck, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_03a_CTNotifyChange.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// berschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads fr alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Resources.resx
new file mode 100644
index 000000000..ccbd18136
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Resources.resx
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..534c27533
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// nderungen an dieser Datei knnen falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_03a_CTNotifyChange.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Settings.settings
new file mode 100644
index 000000000..a6d407f53
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..2e012ff49
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,32 @@
+// ***********************************************************************
+// Assembly : MVVM_03a_CTNotifyChange
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_03a_CTNotifyChange.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModelCT
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/ViewModels/NotifyChangeViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/ViewModels/NotifyChangeViewModel.cs
new file mode 100644
index 000000000..742f4c113
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/ViewModels/NotifyChangeViewModel.cs
@@ -0,0 +1,28 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using MVVM.ViewModel;
+
+namespace MVVM_03a_CTNotifyChange.ViewModels;
+
+public partial class NotifyChangeViewModel : BaseViewModelCT
+{
+ #region Properties
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(Fullname))]
+ private string _firstname;
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(Fullname))]
+ private string _lastname;
+
+ public string Fullname => $"{Lastname}, {Firstname}";
+ #endregion
+
+ #region Methods
+ public NotifyChangeViewModel()
+ {
+ _firstname = "Dave";
+ _lastname = "Dev";
+ }
+ #endregion
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/.info
new file mode 100644
index 000000000..e6605be2f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/.info
@@ -0,0 +1 @@
+Folder for Views
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/NotifyChangeView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/NotifyChangeView.xaml
new file mode 100644
index 000000000..aa04a76c3
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/NotifyChangeView.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/NotifyChangeView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/NotifyChangeView.xaml.cs
new file mode 100644
index 000000000..531f0553d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChange/Views/NotifyChangeView.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_03a_CTNotifyChange.Views;
+
+///
+/// Interaktionslogik fr DelegateCommandView.xaml
+///
+public partial class NotifyChangeView : Page
+{
+ public NotifyChangeView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/MVVM_03a_CTNotifyChangeTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/MVVM_03a_CTNotifyChangeTests.csproj
index 2018d9727..e01d0c95c 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/MVVM_03a_CTNotifyChangeTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/MVVM_03a_CTNotifyChangeTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/MVVM_03a_CTNotifyChange_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/MVVM_03a_CTNotifyChange_netTests.csproj
index 79c70f000..935558209 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/MVVM_03a_CTNotifyChange_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/MVVM_03a_CTNotifyChange_netTests.csproj
@@ -1,14 +1,20 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..5ae4b2614
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Properties/SettingsTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+namespace MVVM_03a_CTNotifyChange.Properties.Tests;
+
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..fa4e525d3
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,67 @@
+// ***********************************************************************
+// Assembly : MVVM_03a_CTNotifyChange_netTests
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_03a_CTNotifyChange.ViewModels.Tests;
+
+///
+/// Defines test class MainWindowViewModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanging += OnVMPropertyChanging;
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanging));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/ViewModels/NotifyChangeViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/ViewModels/NotifyChangeViewModelTests.cs
new file mode 100644
index 000000000..b4e37ee9d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/ViewModels/NotifyChangeViewModelTests.cs
@@ -0,0 +1,141 @@
+// ***********************************************************************
+// Assembly : MVVM_03a_CTNotifyChange_netTests
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using static BaseLib.Helper.TestHelper;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_03a_CTNotifyChange.ViewModels.Tests;
+
+///
+/// Defines test class NotifyChangeViewModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class NotifyChangeViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ NotifyChangeViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanging += OnVMPropertyChanging;
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method NotifyChangeViewModelTest.
+ ///
+ ///
+ [TestMethod]
+ public void NotifyChangeViewModelTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(NotifyChangeViewModel));
+ Assert.AreEqual("Dave", testModel.Firstname);
+ Assert.AreEqual("Dev", testModel.Lastname);
+ Assert.AreEqual("Dev, Dave", testModel.Fullname);
+ Assert.AreEqual("", DebugLog);
+ }
+
+ ///
+ /// Firstnames the test.
+ ///
+ /// The name.
+ /// As exp.
+ ///
+ [TestMethod()]
+ [DataRow("", new string[] { "","Dev, ", @"PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Dave
+PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Dev,
+" })]
+ [DataRow("Peter", new string[] { "Peter", "Dev, Peter", @"PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Dave
+PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Peter
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Dev, Peter
+" })]
+ [DataRow("Steve\tEugene", new string[] { "Eugene", "Dev, Eugene", @"PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Dave
+PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Steve
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Dev, Steve
+PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Steve
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Firstname)=Eugene
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Dev, Eugene
+" })]
+ public void FirstnameTest(string name, string[] asExp)
+ {
+ // Arrange
+ var asName=name.Split('\t');
+ // Act
+ foreach (var item in asName)
+ testModel.Firstname = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Firstname,"Firstname");
+ Assert.AreEqual(asExp[1], testModel.Fullname, "Fullname");
+ AssertAreEqual(asExp[2], DebugLog, "DebugOut");
+ }
+
+ ///
+ /// Lastnames the test.
+ ///
+ /// The name.
+ /// As exp.
+ ///
+ [TestMethod()]
+ [DataRow("", new string[] { "", ", Dave", @"PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Dev
+PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=, Dave
+" })]
+ [DataRow("Miller", new string[] { "Miller", "Miller, Dave", @"PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Dev
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Miller
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Miller, Dave
+" })]
+ [DataRow("Fry\tWebb", new string[] { "Webb", "Webb, Dave", @"PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Dev
+PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Fry
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Fry, Dave
+PropChgn(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Fry
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Lastname)=Webb
+PropChg(MVVM_03a_CTNotifyChange.ViewModels.NotifyChangeViewModel,Fullname)=Webb, Dave
+" })]
+ public void LastnameTest(string name, string[] asExp)
+ {
+ // Arrange
+ var asName = name.Split('\t');
+ // Act
+ foreach (var item in asName)
+ testModel.Lastname = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Lastname, "Firstname");
+ Assert.AreEqual(asExp[1], testModel.Fullname, "Fullname");
+ AssertAreEqual(asExp[2], DebugLog, "DebugOut");
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..4a2ae6300
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Views/MainWindowTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_03a_CTNotifyChange.Views.Tests;
+
+[TestClass()]
+public class MainWindowTests
+{
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Views/NotifyChangeViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Views/NotifyChangeViewTests.cs
new file mode 100644
index 000000000..cd2b83266
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_03a_CTNotifyChangeTests/Views/NotifyChangeViewTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_03a_CTNotifyChange.Views.Tests;
+
+[TestClass()]
+public class NotifyChangeViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ NotifyChangeView testView;
+ private object vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ [TestInitialize()]
+ public void Init()
+ {
+ Thread thread = new(() =>
+ {
+ testView = new();
+ vm = testView.DataContext;
+ });
+ thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ thread.Start();
+ thread.Join(); //Wait for the thread to end
+ }
+
+ [TestMethod()]
+ public void ValidationPageTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsNotNull(vm);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.config b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.config
new file mode 100644
index 000000000..193aecc67
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.xaml
new file mode 100644
index 000000000..b414b67cd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.xaml.cs
new file mode 100644
index 000000000..dd8902bb8
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_04_DelegateCommand
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_04_DelegateCommand;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MVVM_04_DelegateCommand.csproj b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MVVM_04_DelegateCommand.csproj
new file mode 100644
index 000000000..691506e9e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MVVM_04_DelegateCommand.csproj
@@ -0,0 +1,47 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MVVM_04_DelegateCommand_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MVVM_04_DelegateCommand_net.csproj
new file mode 100644
index 000000000..d2408e65c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MVVM_04_DelegateCommand_net.csproj
@@ -0,0 +1,53 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MainWindow.xaml
new file mode 100644
index 000000000..7c74f6ed2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MainWindow.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MainWindow.xaml.cs
new file mode 100644
index 000000000..40c89d366
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_04_DelegateCommand
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_04_DelegateCommand;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..f48b52b54
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-24-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("WpfApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("WpfApp")]
+[assembly: AssemblyCopyright("Copyright © JC-Soft 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..24ab39889
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_04_DelegateCommand.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_04_DelegateCommand.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Clear ähnelt.
+ ///
+ public static string btnClear {
+ get {
+ return ResourceManager.GetString("btnClear", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page x:Class="MVVM_04_DelegateCommand.Views.DelegateCommandView"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_04_DelegateCommand.Views"
+ /// xmlns:p="clr-namespace:MVVM_04_DelegateCommand.Properties"
+ /// xmlns:mvvm="clr-namespace [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DelegateCommandView {
+ get {
+ return ResourceManager.GetString("DelegateCommandView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die using CommunityToolkit.Mvvm.Input;
+ ///using MVVM.ViewModel;
+ ///
+ ///namespace MVVM_04_DelegateCommand.ViewModels
+ ///{
+ /// public class DelegateCommandViewModel : BaseViewModel
+ /// {
+ /// #region Properties
+ /// private string _firstname;
+ /// private string _lastname;
+ ///
+ /// public RelayCommand<object> ClearCommand { get; set; }
+ /// public string Firstname { get => _firstname; set => SetProperty(ref _firstname, value); }
+ /// public string Lastname { get => _lastname; set => SetProperty(re [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DelegateCommandViewModel {
+ get {
+ return ResourceManager.GetString("DelegateCommandViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Firstname: ähnelt.
+ ///
+ public static string FirstName {
+ get {
+ return ResourceManager.GetString("FirstName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Fullname: ähnelt.
+ ///
+ public static string FullName {
+ get {
+ return ResourceManager.GetString("FullName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Lastname: ähnelt.
+ ///
+ public static string LastName {
+ get {
+ return ResourceManager.GetString("LastName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM #4 Delegate Command ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Resources.resx
new file mode 100644
index 000000000..f06615345
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Resources.resx
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Clear
+
+
+
+ ..\Views\DelegateCommandView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\DelegateCommandViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ Firstname:
+
+
+ Fullname:
+
+
+ Lastname:
+
+
+ MVVM #4 Delegate Command
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..de9739fc6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_04_DelegateCommand.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Settings.settings
new file mode 100644
index 000000000..049245f40
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/ViewModels/DelegateCommandViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/ViewModels/DelegateCommandViewModel.cs
new file mode 100644
index 000000000..a015906cd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/ViewModels/DelegateCommandViewModel.cs
@@ -0,0 +1,33 @@
+using CommunityToolkit.Mvvm.Input;
+using MVVM.ViewModel;
+
+namespace MVVM_04_DelegateCommand.ViewModels;
+
+public class DelegateCommandViewModel : BaseViewModel
+{
+ #region Properties
+ private string _firstname;
+ private string _lastname;
+
+ public RelayCommand ClearCommand { get; set; }
+ public string Firstname { get => _firstname; set => SetProperty(ref _firstname, value); }
+ public string Lastname { get => _lastname; set => SetProperty(ref _lastname , value); }
+ public string Fullname => $"{Lastname}, {Firstname}";
+ #endregion
+
+ #region Methods
+ public DelegateCommandViewModel()
+ {
+ _firstname = "Dave";
+ _lastname = "Dev";
+ AddPropertyDependency(nameof(Fullname), nameof(Lastname));
+ AddPropertyDependency(nameof(Fullname), nameof(Firstname));
+ AddPropertyDependency(nameof(ClearCommand), nameof(Lastname));
+ AddPropertyDependency(nameof(ClearCommand), nameof(Firstname));
+ ClearCommand = new(
+ (o)=> { Firstname = ""; Lastname = ""; },
+ (o) => !string.IsNullOrEmpty(_firstname) || !string.IsNullOrEmpty(_lastname));
+ }
+ #endregion
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..4ac802cb1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,32 @@
+// ***********************************************************************
+// Assembly : MVVM_04_DelegateCommand
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_04_DelegateCommand.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModel
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/.info
new file mode 100644
index 000000000..0791c329c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/.info
@@ -0,0 +1 @@
+Folder for Views
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/DelegateCommandView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/DelegateCommandView.xaml
new file mode 100644
index 000000000..5a2678db0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/DelegateCommandView.xaml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/DelegateCommandView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/DelegateCommandView.xaml.cs
new file mode 100644
index 000000000..0f8b9a8e5
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommand/Views/DelegateCommandView.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_04_DelegateCommand.Views;
+
+///
+/// Interaktionslogik für DelegateCommandView.xaml
+///
+public partial class DelegateCommandView : Page
+{
+ public DelegateCommandView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/MVVM_04_DelegateCommandTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/MVVM_04_DelegateCommandTests.csproj
index 3286c229a..720a135d4 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/MVVM_04_DelegateCommandTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/MVVM_04_DelegateCommandTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/MVVM_04_DelegateCommand_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/MVVM_04_DelegateCommand_netTests.csproj
index 81773d423..966383f98 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/MVVM_04_DelegateCommand_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/MVVM_04_DelegateCommand_netTests.csproj
@@ -1,14 +1,20 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..aadd8552b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Properties/SettingsTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+namespace MVVM_04_DelegateCommand.Properties.Tests;
+
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/ViewModels/DelegateCommandViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/ViewModels/DelegateCommandViewModelTests.cs
new file mode 100644
index 000000000..ccf306d03
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/ViewModels/DelegateCommandViewModelTests.cs
@@ -0,0 +1,100 @@
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using static BaseLib.Helper.TestHelper;
+
+namespace MVVM_04_DelegateCommand.ViewModels.Tests;
+
+[TestClass()]
+public class DelegateCommandViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ DelegateCommandViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ testModel.ClearCommand.CanExecuteChanged += OnCanExChanged;
+ ClearLog();
+ }
+
+ [TestMethod]
+ public void DelegateCommandViewModelTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(DelegateCommandViewModel));
+ Assert.IsNotNull(testModel.ClearCommand);
+ Assert.IsInstanceOfType(testModel.ClearCommand, typeof(RelayCommand));
+ Assert.AreEqual("Dave", testModel.Firstname);
+ Assert.AreEqual("Dev", testModel.Lastname);
+ Assert.AreEqual("Dev, Dave", testModel.Fullname);
+ Assert.AreEqual("", DebugLog);
+ }
+
+ [TestMethod()]
+ [DataRow("", new string[] { "","Dev, ", "PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=Dev, \r\nCanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True\r\nPropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Firstname)=\r\n" })]
+ [DataRow("Peter", new string[] { "Peter", "Dev, Peter", "PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=Dev, Peter\r\nCanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True\r\nPropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Firstname)=Peter\r\n" })]
+ [DataRow("Steve\tEugene", new string[] { "Eugene", "Dev, Eugene", @"PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=Dev, Steve
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Firstname)=Steve
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=Dev, Eugene
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Firstname)=Eugene
+" })]
+ public void FirstnameTest(string name, string[] asExp)
+ {
+ // Arrange
+ var asName=name.Split('\t');
+ // Act
+ foreach (var item in asName)
+ testModel.Firstname = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Firstname,"Firstname");
+ Assert.AreEqual(asExp[1], testModel.Fullname, "Fullname");
+ AssertAreEqual(asExp[2], DebugLog, "DebugOut");
+ }
+
+ [TestMethod()]
+ [DataRow("", new string[] { "", ", Dave", "PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=, Dave\r\nCanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True\r\nPropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Lastname)=\r\n" })]
+ [DataRow("Miller", new string[] { "Miller", "Miller, Dave", "PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=Miller, Dave\r\nCanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True\r\nPropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Lastname)=Miller\r\n" })]
+ [DataRow("Fry\tWebb", new string[] { "Webb", "Webb, Dave", @"PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=Fry, Dave
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Lastname)=Fry
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=Webb, Dave
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Lastname)=Webb
+" })]
+ public void LastnameTest(string name, string[] asExp)
+ {
+ // Arrange
+ var asName = name.Split('\t');
+ // Act
+ foreach (var item in asName)
+ testModel.Lastname = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Lastname, "Firstname");
+ Assert.AreEqual(asExp[1], testModel.Fullname, "Fullname");
+ AssertAreEqual(asExp[2], DebugLog, "DebugOut");
+ }
+
+ [TestMethod]
+ public void ClearCommandExecuteTest()
+ {
+ testModel.ClearCommand.Execute(null);
+ Assert.AreEqual("", testModel.Firstname);
+ Assert.AreEqual("", testModel.Lastname);
+ Assert.AreEqual(", ", testModel.Fullname);
+ AssertAreEqual(@"PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=Dev,
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=True
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Firstname)=
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Fullname)=,
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand`1[System.Object])=False
+PropChg(MVVM_04_DelegateCommand.ViewModels.DelegateCommandViewModel,Lastname)=
+", DebugLog);
+ }
+
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..0796675a7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,30 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+namespace MVVM_04_DelegateCommand.ViewModels.Tests;
+
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ MainWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ ClearLog();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Views/DelegateCommandViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Views/DelegateCommandViewTests.cs
new file mode 100644
index 000000000..055ba3b39
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Views/DelegateCommandViewTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_04_DelegateCommand.Views.Tests;
+
+[TestClass()]
+public class DelegateCommandViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ DelegateCommandView testView;
+ private object vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize()]
+ public void Init()
+ {
+ Thread thread = new(() =>
+ {
+ testView = new();
+ vm = testView.DataContext;
+ });
+ thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ thread.Start();
+ thread.Join(); //Wait for the thread to end
+ }
+
+ [TestMethod()]
+ public void ValidationPageTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsNotNull(vm);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..f0f889cb0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04_DelegateCommandTests/Views/MainWindowTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_04_DelegateCommand.Views.Tests;
+
+[TestClass()]
+public class MainWindowTests
+{
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.config b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.config
new file mode 100644
index 000000000..996fd1e81
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.xaml
new file mode 100644
index 000000000..f6e7327a5
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.xaml.cs
new file mode 100644
index 000000000..4fe27e9d2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_04a_CTRelayCommand
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_04a_CTRelayCommand;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MVVM_04a_CTRelayCommand.csproj b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MVVM_04a_CTRelayCommand.csproj
new file mode 100644
index 000000000..00d3615a7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MVVM_04a_CTRelayCommand.csproj
@@ -0,0 +1,47 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MVVM_04a_CTRelayCommand_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MVVM_04a_CTRelayCommand_net.csproj
new file mode 100644
index 000000000..77efc8b7b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MVVM_04a_CTRelayCommand_net.csproj
@@ -0,0 +1,53 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MainWindow.xaml
new file mode 100644
index 000000000..e8b6638c0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MainWindow.xaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MainWindow.xaml.cs
new file mode 100644
index 000000000..b802b84ae
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_04a_CTRelayCommand
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_04a_CTRelayCommand;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..ed1846baf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-24-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen ber eine Assembly werden ber die folgenden
+// Attribute gesteuert. ndern Sie diese Attributwerte, um die Informationen zu ndern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("WpfApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("WpfApp")]
+[assembly: AssemblyCopyright("Copyright JC-Soft 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// fr COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen mssen, sollten Sie das ComVisible-Attribut fr diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei bereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwrterbcher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wrterbchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwrterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwrterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen fr eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie knnen alle Werte angeben oder Standardwerte fr die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..e892aa397
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Resources.Designer.cs
@@ -0,0 +1,120 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_04a_CTRelayCommand.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_04a_CTRelayCommand.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Example use of RelayCommand with Community-Toolkit ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page x:Class="MVVM_04a_CTRelayCommand.Views.RelayCommandView"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_04a_CTRelayCommand.Views"
+ /// xmlns:mvvm="clr-namespace:MVVM_04a_CTRelayCommand.ViewModels"
+ /// mc:Ignorable="d"
+ /// d [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string RelayCommandView {
+ get {
+ return ResourceManager.GetString("RelayCommandView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die using CommunityToolkit.Mvvm.ComponentModel;
+ ///using CommunityToolkit.Mvvm.Input;
+ ///using MVVM.ViewModel;
+ ///
+ ///namespace MVVM_04a_CTRelayCommand.ViewModels
+ ///{
+ /// public partial class RelayCommandViewModel : BaseViewModelCT
+ /// {
+ /// #region Properties
+ /// [ObservableProperty]
+ /// [NotifyPropertyChangedFor(nameof(Fullname))]
+ /// [NotifyCanExecuteChangedFor(nameof(ClearCommand))]
+ /// private string _firstname="";
+ /// [ObservableProperty]
+ /// [NotifyPropertyChangedFor(nameof(Fu [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string RelayCommandViewModel {
+ get {
+ return ResourceManager.GetString("RelayCommandViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die CT-RelayCommand ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Resources.resx
new file mode 100644
index 000000000..255adc28d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Resources.resx
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Example use of RelayCommand with Community-Toolkit
+
+
+
+ ..\Views\RelayCommandView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
+
+ ..\ViewModels\RelayCommandViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
+
+ CT-RelayCommand
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..cfd33efcf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// nderungen an dieser Datei knnen falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_04a_CTRelayCommand.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Settings.settings
new file mode 100644
index 000000000..a6d407f53
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..b6ee4ef7f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,32 @@
+// ***********************************************************************
+// Assembly : MVVM_04a_CTRelayCommand
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_04a_CTRelayCommand.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModelCT
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/ViewModels/RelayCommandViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/ViewModels/RelayCommandViewModel.cs
new file mode 100644
index 000000000..c1e76279a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/ViewModels/RelayCommandViewModel.cs
@@ -0,0 +1,39 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using MVVM.ViewModel;
+
+namespace MVVM_04a_CTRelayCommand.ViewModels;
+
+public partial class RelayCommandViewModel : BaseViewModelCT
+{
+ #region Properties
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(Fullname))]
+ [NotifyCanExecuteChangedFor(nameof(ClearCommand))]
+ private string _firstname="";
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(Fullname))]
+ [NotifyCanExecuteChangedFor(nameof(ClearCommand))]
+ private string _lastname="";
+
+ private bool _CanClear
+ => !string.IsNullOrEmpty(Firstname)
+ || !string.IsNullOrEmpty(Lastname);
+
+ public string Fullname => $"{Lastname}, {Firstname}";
+ #endregion
+
+ #region Methods
+ public RelayCommandViewModel()
+ {
+ _firstname = "Dave";
+ _lastname = "Dev";
+ }
+
+
+ [RelayCommand(CanExecute = nameof(_CanClear))]
+ private void Clear()
+ => (Firstname, Lastname) = ("", "");
+ #endregion
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/.info
new file mode 100644
index 000000000..e6605be2f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/.info
@@ -0,0 +1 @@
+Folder for Views
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/RelayCommandView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/RelayCommandView.xaml
new file mode 100644
index 000000000..5cf533e5c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/RelayCommandView.xaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/RelayCommandView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/RelayCommandView.xaml.cs
new file mode 100644
index 000000000..6a3a92e6e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommand/Views/RelayCommandView.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_04a_CTRelayCommand.Views;
+
+///
+/// Interaktionslogik fr DelegateCommandView.xaml
+///
+public partial class RelayCommandView : Page
+{
+ public RelayCommandView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/MVVM_04a_CTRelayCommandTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/MVVM_04a_CTRelayCommandTests.csproj
index d0202ae6b..4088eae1c 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/MVVM_04a_CTRelayCommandTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/MVVM_04a_CTRelayCommandTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/MVVM_04a_CTRelayCommand_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/MVVM_04a_CTRelayCommand_netTests.csproj
index 5966b9f38..9919974c4 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/MVVM_04a_CTRelayCommand_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/MVVM_04a_CTRelayCommand_netTests.csproj
@@ -1,14 +1,20 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..96956e845
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Properties/SettingsTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+namespace MVVM_04a_CTRelayCommand.Properties.Tests;
+
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..fff3e8c2e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,66 @@
+// ***********************************************************************
+// Assembly : MVVM_04a_CTRelayCommand_netTests
+// Author : Mir
+// Created : 05-19-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_04a_CTRelayCommand.ViewModels.Tests;
+
+///
+/// Defines test class MainWindowViewModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ testModel.PropertyChanging += OnVMPropertyChanging;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/ViewModels/RelayCommandViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/ViewModels/RelayCommandViewModelTests.cs
new file mode 100644
index 000000000..fe3c710ce
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/ViewModels/RelayCommandViewModelTests.cs
@@ -0,0 +1,123 @@
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using static BaseLib.Helper.TestHelper;
+
+namespace MVVM_04a_CTRelayCommand.ViewModels.Tests;
+
+[TestClass()]
+public class RelayCommandViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ RelayCommandViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ testModel.PropertyChanging += OnVMPropertyChanging;
+ testModel.ClearCommand.CanExecuteChanged += OnCanExChanged;
+ ClearLog();
+ }
+
+ [TestMethod]
+ public void RelayCommandViewModelTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(RelayCommandViewModel));
+ Assert.IsNotNull(testModel.ClearCommand);
+ Assert.IsInstanceOfType(testModel.ClearCommand, typeof(RelayCommand));
+ Assert.AreEqual("Dave", testModel.Firstname);
+ Assert.AreEqual("Dev", testModel.Lastname);
+ Assert.AreEqual("Dev, Dave", testModel.Fullname);
+ Assert.AreEqual("", DebugLog);
+ }
+
+ [TestMethod()]
+ [DataRow("", new string[] { "","Dev, ", @"PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=Dave
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=Dev,
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+" })]
+ [DataRow("Peter", new string[] { "Peter", "Dev, Peter", @"PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=Dave
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=Peter
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=Dev, Peter
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+" })]
+ [DataRow("Steve\tEugene", new string[] { "Eugene", "Dev, Eugene", @"PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=Dave
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=Steve
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=Dev, Steve
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=Steve
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=Eugene
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=Dev, Eugene
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+" })]
+ public void FirstnameTest(string name, string[] asExp)
+ {
+ // Arrange
+ var asName=name.Split('\t');
+ // Act
+ foreach (var item in asName)
+ testModel.Firstname = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Firstname,"Firstname");
+ Assert.AreEqual(asExp[1], testModel.Fullname, "Fullname");
+ AssertAreEqual(asExp[2], DebugLog, "DebugOut");
+ }
+
+ [TestMethod()]
+ [DataRow("", new string[] { "", ", Dave", @"PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=Dev
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=, Dave
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+" })]
+ [DataRow("Miller", new string[] { "Miller", "Miller, Dave", @"PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=Dev
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=Miller
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=Miller, Dave
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+" })]
+ [DataRow("Fry\tWebb", new string[] { "Webb", "Webb, Dave", @"PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=Dev
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=Fry
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=Fry, Dave
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=Fry
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=Webb
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=Webb, Dave
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+" })]
+ public void LastnameTest(string name, string[] asExp)
+ {
+ // Arrange
+ var asName = name.Split('\t');
+ // Act
+ foreach (var item in asName)
+ testModel.Lastname = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Lastname, "Firstname");
+ Assert.AreEqual(asExp[1], testModel.Fullname, "Fullname");
+ AssertAreEqual(asExp[2], DebugLog, "DebugOut");
+ }
+
+ [TestMethod]
+ public void ClearCommandExecuteTest()
+ {
+ testModel.ClearCommand.Execute(null);
+ Assert.AreEqual("", testModel.Firstname);
+ Assert.AreEqual("", testModel.Lastname);
+ Assert.AreEqual(", ", testModel.Fullname);
+ AssertAreEqual(@"PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=Dave
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Firstname)=
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=Dev,
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=True
+PropChgn(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=Dev
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Lastname)=
+PropChg(MVVM_04a_CTRelayCommand.ViewModels.RelayCommandViewModel,Fullname)=,
+CanExChanged(CommunityToolkit.Mvvm.Input.RelayCommand)=False
+", DebugLog);
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Views/DelegateCommandViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Views/DelegateCommandViewTests.cs
new file mode 100644
index 000000000..7399961cb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Views/DelegateCommandViewTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_04a_CTRelayCommand.Views.Tests;
+
+[TestClass()]
+public class DelegateCommandViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ RelayCommandView testView;
+ private object vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ [TestInitialize()]
+ public void Init()
+ {
+ Thread thread = new(() =>
+ {
+ testView = new();
+ vm = testView.DataContext;
+ });
+ thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ thread.Start();
+ thread.Join(); //Wait for the thread to end
+ }
+
+ [TestMethod()]
+ public void ValidationPageTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsNotNull(vm);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..3d9720d77
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_04a_CTRelayCommandTests/Views/MainWindowTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_04a_CTRelayCommand.Views.Tests;
+
+[TestClass()]
+public class MainWindowTests
+{
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.config b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.config
new file mode 100644
index 000000000..193aecc67
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.xaml
new file mode 100644
index 000000000..ddb8e8ee2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.xaml.cs
new file mode 100644
index 000000000..56606ce97
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_05_CommandParCalculator;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MVVM_05_CommandParCalculator.csproj b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MVVM_05_CommandParCalculator.csproj
new file mode 100644
index 000000000..691506e9e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MVVM_05_CommandParCalculator.csproj
@@ -0,0 +1,47 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MVVM_05_CommandParCalculator_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MVVM_05_CommandParCalculator_net.csproj
new file mode 100644
index 000000000..d2408e65c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MVVM_05_CommandParCalculator_net.csproj
@@ -0,0 +1,53 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MainWindow.xaml
new file mode 100644
index 000000000..5e0950c28
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MainWindow.xaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MainWindow.xaml.cs
new file mode 100644
index 000000000..95b401c31
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_05_CommandParCalculator;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/.info b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/.info
new file mode 100644
index 000000000..e69de29bb
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/CalculatorModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/CalculatorModel.cs
new file mode 100644
index 000000000..8df180e63
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/CalculatorModel.cs
@@ -0,0 +1,348 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System;
+using System.Collections.Generic;
+
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Model;
+
+///
+/// Class CalculatorModel.
+/// Implements the
+/// Implements the
+///
+///
+///
+///
+public class CalculatorModel : NotificationObject, ICalculatorModel
+{
+
+ #region Properties
+ ///
+ /// The unary operation
+ ///
+ ///
+ private static readonly List _unaryOperation = new() {
+ EOperations.Nop,
+ EOperations.Negate,
+ EOperations.Sin,
+ EOperations.Cos,
+ EOperations.Tan,
+ EOperations.Square,
+ EOperations.SquareRt,
+ EOperations.Inverse,
+ EOperations.ExpN,
+ EOperations.LogN,
+ };
+
+ ///
+ /// The operation level
+ ///
+ ///
+ private static readonly Dictionary _OperationLevel = new() {
+ { EOperations.CalcResult, 0 },
+ { EOperations.Add, 1 },
+ { EOperations.Subtract, 1 },
+ { EOperations.Multiply, 2 },
+ { EOperations.Divide, 2 },
+ { EOperations.Power, 3 },
+ { EOperations.SquareRtX, 3 },
+ };
+
+ ///
+ /// The instance
+ ///
+ ///
+ private static CalculatorModel _instance;
+ ///
+ /// The op
+ ///
+ ///
+ private EOperations _op;
+ ///
+ /// The operation
+ ///
+ ///
+ private Func? _Operation;
+ ///
+ /// The accumulator
+ ///
+ ///
+ private double _accumulator = 0d;
+ ///
+ /// The register
+ ///
+ ///
+ private double? _register = null;
+ ///
+ /// The memory
+ ///
+ ///
+ private double? _memory = null;
+ ///
+ /// The stack
+ ///
+ ///
+ private Stack<(double, EOperations, Func)> _stack = new();
+ ///
+ /// The decimal fak
+ ///
+ ///
+ private double _decFak;
+ ///
+ /// The decimal mode
+ ///
+ ///
+ private bool _decMode;
+ ///
+ /// The edit mode
+ ///
+ ///
+ private bool _editMode;
+ ///
+ /// The trig mode
+ ///
+ ///
+ private ETrigMode _trigMode;
+ ///
+ /// The calculate error
+ ///
+ ///
+ private ECalcError _calcError;
+
+ ///
+ /// Gets or sets a value indicating whether [decimal mode].
+ ///
+ /// true if [decimal mode]; otherwise, false.
+ ///
+ public bool DecMode
+ {
+ get => _decMode;
+ set
+ {
+ if (!_decMode && value)
+ _decFak = 1d;
+ _decMode = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the accumulator.
+ ///
+ /// The accumulator.
+ ///
+ public double Accumulator { get => _accumulator; set => SetProperty(ref _accumulator, value); }
+ ///
+ /// Gets or sets the register.
+ ///
+ /// The register.
+ ///
+ public double? Register { get => _register; set => SetProperty(ref _register, value); }
+ ///
+ /// Gets or sets the memory.
+ ///
+ /// The memory.
+ ///
+ public double? Memory { get => _memory; set => SetProperty(ref _memory, value); }
+ ///
+ /// Gets the instance.
+ ///
+ /// The instance.
+ ///
+ public static CalculatorModel Instance => _instance ??= new();
+
+ ///
+ /// Gets the dependencies.
+ ///
+ /// The dependencies.
+ ///
+ public IEnumerable<(string, string)> Dependencies => new[]{
+ (nameof(canOperator),nameof(Accumulator)),
+ (nameof(canOperator),nameof(Register)),
+ };
+
+
+ ///
+ /// Gets the trig mode.
+ ///
+ /// The trig mode.
+ ///
+ public ETrigMode TrigMode => _trigMode;
+
+ ///
+ /// Gets the calculate error.
+ ///
+ /// The calculate error.
+ ///
+ public ECalcError CalcError => _calcError;
+
+ ///
+ /// Gets the size of the stack.
+ ///
+ /// The size of the stack.
+ ///
+ public int StackSize => _stack.Count;
+
+ #endregion
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public CalculatorModel() { }
+
+ ///
+ /// Determines whether this instance can operator the specified e o.
+ ///
+ /// The e o.
+ /// true if this instance can operator the specified e o; otherwise, false.
+ ///
+ public bool canOperator(EOperations eO)
+ {
+ if (eO == EOperations.Nop) return true;
+ if (eO == EOperations.CalcResult) return _Operation != null && _register != null;
+ if (eO > EOperations.CalcResult) return _editMode || _accumulator != 0d;
+ return false;
+ }
+
+ ///
+ /// Determines whether this instance can command the specified e.
+ ///
+ /// The e.
+ /// true if this instance can command the specified e; otherwise, false.
+ ///
+ public bool canCommand(ECommands e)
+ {
+
+ if (e == ECommands.DecMode) return !_decMode;
+ return true;
+ }
+
+
+ ///
+ /// Numbers the command.
+ ///
+ /// The oo.
+ ///
+ public void NumberCmd(ENumbers oo)
+ {
+ if (oo is ENumbers e && (int)e is int iN)
+ {
+ if (!_editMode)
+ {
+ _accumulator = 0d;
+ _editMode = true;
+ _decMode = false;
+ }
+ if (!_decMode)
+ Accumulator = Accumulator * 10d + iN;
+ else
+ {
+ _decFak *= 0.1d;
+ Accumulator = Accumulator + iN * _decFak;
+ }
+ }
+ }
+
+ ///
+ /// Operators the command.
+ ///
+ /// The e o.
+ ///
+ public void OperatorCmd(EOperations eO)
+ {
+
+ _editMode = false;
+
+ if (eO >= EOperations.CalcResult && _register != null && !_unaryOperation.Contains(eO))
+ {
+ if (_Operation != null )
+ Accumulator = _Operation.Invoke(_accumulator);
+
+ Register = null;
+ DecMode = false;
+ }
+ if (eO > EOperations.CalcResult && !_unaryOperation.Contains(eO))
+ { Register = _accumulator; };
+ var op = eO switch
+ {
+
+ EOperations.CalcResult => null,
+ EOperations.Add => (a) => _register!.Value + a,
+ EOperations.Subtract => (a) => _register!.Value - a,
+ EOperations.Multiply => (a) => _register!.Value * a,
+ EOperations.Divide => (a) => _register!.Value / a,
+ EOperations.Power => (a) => Math.Pow(_register!.Value, a),
+ EOperations.Negate => (a) => -a,
+ EOperations.Square => (a) => a * a,
+ EOperations.SquareRt => (a) => Math.Sqrt(a),
+ EOperations.Inverse => (a) => 1 / a,
+ EOperations.Sin => (a) => Math.Sin(a), // Dodo: Factor by Trig-Mode
+ EOperations.Cos => (a) => Math.Cos(a),
+ EOperations.Tan => (a) => Math.Tan(a),
+ EOperations.LogN => (a) => Math.Log(a),
+ EOperations.ExpN => (a) => Math.Exp(a),
+ _ => (Func?)null,
+ };
+ if (_unaryOperation.Contains(eO))
+ Accumulator = op?.Invoke(_accumulator) ?? _accumulator;
+ else
+ {
+ _op = eO;
+ _Operation = op;
+ }
+ }
+
+ ///
+ /// Calculates the command.
+ ///
+ /// The o.
+ ///
+ public void CalcCmd(ECommands o)
+ {
+ switch (o)
+ {
+ case ECommands.Clear: //Clear
+ Accumulator = 0d;
+ _editMode = false;
+ break;
+ case ECommands.ClearAll: //Clear All
+ Accumulator = 0d;
+ Register = 0d;
+ _Operation = null;
+ _editMode = false;
+ break;
+ case ECommands.DecMode: // DecimalSeparator
+ DecMode = true;
+ break;
+ case ECommands.e:
+ Accumulator = Math.E;
+ break;
+ case ECommands.Pi:
+ Accumulator = Math.PI;
+ break;
+ case ECommands.MS: Memory = Accumulator; break;
+ case ECommands.MR: Accumulator = Memory ?? 0d; break;
+ case ECommands.MC: Memory = null; break;
+ case ECommands.Mp: Memory = (Memory ?? 0d) + Accumulator; break;
+ case ECommands.Mm: Memory = (Memory ?? 0d) - Accumulator; break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ECalcError.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ECalcError.cs
new file mode 100644
index 000000000..2068ffd52
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ECalcError.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Model;
+
+///
+/// Enum ECalcError
+///
+///
+public enum ECalcError
+{
+ ///
+ /// The none
+ ///
+ ///
+ None = 0,
+ ///
+ /// The div by zero
+ ///
+ ///
+ DivByZero,
+ ///
+ /// The over flow
+ ///
+ ///
+ OverFlow,
+ ///
+ /// The under flow
+ ///
+ ///
+ UnderFlow,
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ECommands.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ECommands.cs
new file mode 100644
index 000000000..eddad488e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ECommands.cs
@@ -0,0 +1,93 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Model;
+
+///
+/// Enum ECommands
+///
+///
+public enum ECommands
+{
+ ///
+ /// The nop
+ ///
+ ///
+ Nop,
+ ///
+ /// The clear
+ ///
+ ///
+ Clear,
+ ///
+ /// The clear all
+ ///
+ ///
+ ClearAll,
+ ///
+ /// The decimal mode
+ ///
+ ///
+ DecMode,
+ ///
+ /// The pi
+ ///
+ ///
+ Pi,
+ ///
+ /// The e
+ ///
+ ///
+ e,
+ ///
+ /// The bracket open
+ ///
+ ///
+ BracketOpen,
+ ///
+ /// The bracket close
+ ///
+ ///
+ BracketClose,
+ ///
+ /// The ms
+ ///
+ ///
+ MS,
+ ///
+ /// The mr
+ ///
+ ///
+ MR,
+ ///
+ /// The mc
+ ///
+ ///
+ MC,
+ ///
+ /// The mp
+ ///
+ ///
+ Mp,
+ ///
+ /// The mm
+ ///
+ ///
+ Mm,
+
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ENumbers.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ENumbers.cs
new file mode 100644
index 000000000..279b94d1d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ENumbers.cs
@@ -0,0 +1,21 @@
+namespace MVVM_05_CommandParCalculator.Model;
+
+public enum ENumbers:int
+{
+ _0 = 0,
+ _1 = 1,
+ _2 = 2,
+ _3 = 3,
+ _4 = 4,
+ _5 = 5,
+ _6 = 6,
+ _7 = 7,
+ _8 = 8,
+ _9 = 9,
+ _A = 10,
+ _B = 11,
+ _C = 12,
+ _D = 13,
+ _E = 14,
+ _F = 15
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/EOperations.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/EOperations.cs
new file mode 100644
index 000000000..55f54607c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/EOperations.cs
@@ -0,0 +1,121 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Model;
+
+///
+/// Enum EOperations
+///
+///
+public enum EOperations
+{
+ ///
+ /// The nop
+ ///
+ ///
+ Nop,
+ ///
+ /// The calculate result
+ ///
+ ///
+ CalcResult, // =
+ ///
+ /// The add
+ ///
+ ///
+ Add, // +
+ ///
+ /// The subtract
+ ///
+ ///
+ Subtract, // -
+ ///
+ /// The multiply
+ ///
+ ///
+ Multiply,
+ ///
+ /// The divide
+ ///
+ ///
+ Divide,
+ ///
+ /// The negate
+ ///
+ ///
+ Negate,
+ ///
+ /// The power
+ ///
+ ///
+ Power,
+ ///
+ /// The square
+ ///
+ ///
+ Square,
+ ///
+ /// The square root
+ ///
+ ///
+ SquareRt, // √x
+ ///
+ /// The inverse
+ ///
+ ///
+ Inverse, // 1/x
+ ///
+ /// The sin
+ ///
+ ///
+ Sin,
+ ///
+ /// The cos
+ ///
+ ///
+ Cos,
+ ///
+ /// The tan
+ ///
+ ///
+ Tan,
+ ///
+ /// The exp n
+ ///
+ ///
+ ExpN, // eˣ
+ ///
+ /// The log n
+ ///
+ ///
+ LogN,
+ ///
+ /// The exp x
+ ///
+ ///
+ ExpX, // yˣ
+ ///
+ /// The log x
+ ///
+ ///
+ LogX,
+ ///
+ /// The square rt x
+ ///
+ ///
+ SquareRtX,
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ETrigMode.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ETrigMode.cs
new file mode 100644
index 000000000..f214ba5e8
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ETrigMode.cs
@@ -0,0 +1,41 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Model;
+
+///
+/// Enum ETrigMode
+///
+///
+public enum ETrigMode
+{
+ ///
+ /// The grad
+ ///
+ ///
+ Grad,
+ ///
+ /// The RAD
+ ///
+ ///
+ Rad,
+ ///
+ /// The degr
+ ///
+ ///
+ Degr
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ICalculatorModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ICalculatorModel.cs
new file mode 100644
index 000000000..05cfc8034
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Model/ICalculatorModel.cs
@@ -0,0 +1,107 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using System.Collections.Generic;
+using System.ComponentModel;
+
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Model;
+
+///
+/// Interface ICalculatorModel
+/// Extends the
+///
+///
+///
+public interface ICalculatorModel :INotifyPropertyChanged
+{
+
+ ///
+ /// Gets the accumulator.
+ ///
+ /// The accumulator.
+ ///
+ double Accumulator { get; }
+ ///
+ /// Gets the register.
+ ///
+ /// The register.
+ ///
+ double? Register { get; }
+ ///
+ /// Gets the memory.
+ ///
+ /// The memory.
+ ///
+ double? Memory { get; }
+ ///
+ /// Gets the dependencies.
+ ///
+ /// The dependencies.
+ ///
+ IEnumerable<(string Dest,string Src)> Dependencies { get; }
+ ///
+ /// Gets the trig mode.
+ ///
+ /// The trig mode.
+ ///
+ ETrigMode TrigMode { get; }
+ ///
+ /// Gets the calculate error.
+ ///
+ /// The calculate error.
+ ///
+ ECalcError CalcError { get; }
+
+ ///
+ /// Gets the size of the stack.
+ ///
+ /// The size of the stack.
+ ///
+ int StackSize { get; }
+ ///
+ /// Determines whether this instance can command the specified e c.
+ ///
+ /// The e c.
+ /// true if this instance can command the specified e c; otherwise, false.
+ ///
+ bool canCommand(ECommands eC);
+ ///
+ /// Calculates the command.
+ ///
+ /// The o.
+ ///
+ void CalcCmd(ECommands o);
+ ///
+ /// Determines whether this instance can operator the specified e o.
+ ///
+ /// The e o.
+ /// true if this instance can operator the specified e o; otherwise, false.
+ ///
+ bool canOperator(EOperations eO);
+ ///
+ /// Operators the command.
+ ///
+ /// The e o.
+ ///
+ void OperatorCmd(EOperations eO);
+ ///
+ /// Numbers the command.
+ ///
+ /// The o.
+ ///
+ void NumberCmd(ENumbers o);
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..f48b52b54
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-24-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("WpfApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("WpfApp")]
+[assembly: AssemblyCopyright("Copyright © JC-Soft 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..5b45a5690
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Resources.Designer.cs
@@ -0,0 +1,139 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_05_CommandParCalculator.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_05_CommandParCalculator.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_05_CommandParCalculator
+ ///// Author : Mir
+ ///// Created : 05-11-2023
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 05-19-2023
+ ///// ***********************************************************************
+ ///// <copyright file="CalculatorModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2023
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ********************************************* [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CalculatorModel {
+ get {
+ return ResourceManager.GetString("CalculatorModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_05_CommandParCalculator.Views"
+ /// xmlns:model="clr-namespace:MVVM_05_CommandParCalculator.Model"
+ /// xmlns:mvvm="clr-namespace:MVVM_05_CommandParCalculator.ViewModels"
+ /// xml [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CommandParCalculatorView {
+ get {
+ return ResourceManager.GetString("CommandParCalculatorView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_05_CommandParCalculator_net
+ ///// Author : Mir
+ ///// Created : 05-11-2023
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 05-19-2023
+ ///// ***********************************************************************
+ ///// <copyright file="CommandParCalculatorViewModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2023
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// *************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CommandParCalculatorViewModel {
+ get {
+ return ResourceManager.GetString("CommandParCalculatorViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Example-Calculator using CommandParameter ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die CommandParameter ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Resources.resx
new file mode 100644
index 000000000..fdaf7c444
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Resources.resx
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Model\CalculatorModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
+
+ ..\Views\CommandParCalculatorView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\CommandParCalculatorViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ Example-Calculator using CommandParameter
+
+
+ CommandParameter
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..633898726
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_05_CommandParCalculator.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Settings.settings
new file mode 100644
index 000000000..049245f40
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/ViewModels/CommandParCalculatorViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/ViewModels/CommandParCalculatorViewModel.cs
new file mode 100644
index 000000000..20660103e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/ViewModels/CommandParCalculatorViewModel.cs
@@ -0,0 +1,141 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using CommunityToolkit.Mvvm.Input;
+using MVVM.ViewModel;
+using MVVM_05_CommandParCalculator.Model;
+using System;
+using System.ComponentModel;
+
+///
+/// The ViewModels namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.ViewModels;
+
+///
+/// Class CommandParCalculatorViewModel.
+/// Implements the
+///
+///
+///
+public class CommandParCalculatorViewModel : BaseViewModel
+{
+ #region Properties
+ ///
+ /// Gets or sets the get model.
+ ///
+ /// The get model.
+ ///
+ public static Func GetModel { get; set; } = () => CalculatorModel.Instance;
+ ///
+ /// The model
+ ///
+ ///
+ private ICalculatorModel _model;
+ ///
+ /// Gets or sets the number command.
+ ///
+ /// The number command.
+ ///
+ public RelayCommand NumberCommand { get; set; }
+ ///
+ /// Gets or sets the operator command.
+ ///
+ /// The operator command.
+ ///
+ public RelayCommand OperatorCommand { get; set; }
+ ///
+ /// Gets or sets the calculator command.
+ ///
+ /// The calculator command.
+ ///
+ public RelayCommand CalculatorCommand { get; set; }
+
+ ///
+ /// Gets the accumulator.
+ ///
+ /// The accumulator.
+ ///
+ public double Accumulator => _model.Accumulator;
+ ///
+ /// Gets the memory.
+ ///
+ /// The memory.
+ ///
+ public double Memory => _model.Memory ?? double.NaN;
+ ///
+ /// Gets the register.
+ ///
+ /// The register.
+ ///
+ public double Register => _model.Register ?? double.NaN;
+ ///
+ /// Gets the status.
+ ///
+ /// The status.
+ ///
+ public string Status => $"{_model.CalcError} {_model.TrigMode} ";
+ #endregion
+
+ #region Methods
+ ///
+ /// Determines whether this instance can operator the specified e o.
+ ///
+ /// The e o.
+ /// true if this instance can operator the specified e o; otherwise, false.
+ ///
+ public bool canOperator(EOperations eO) => FuncProxy(eO, _model.canOperator);
+ ///
+ /// Determines whether this instance can command the specified e c.
+ ///
+ /// The e c.
+ /// true if this instance can command the specified e c; otherwise, false.
+ ///
+ public bool canCommand(ECommands eC) => FuncProxy(eC, _model.canCommand);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public CommandParCalculatorViewModel()
+ {
+ _model = GetModel();
+ _model.PropertyChanged += OnPropertyChanged;
+ NumberCommand = new(_model.NumberCmd);
+ OperatorCommand = new(_model.OperatorCmd,canOperator);
+ CalculatorCommand = new(_model.CalcCmd, canCommand);
+ foreach (var d in _model.Dependencies)
+ AddPropertyDependency(d.Dest, d.Src);
+ AddPropertyDependency(nameof(OperatorCommand), nameof(canOperator));
+ AddPropertyDependency(nameof(CalculatorCommand), nameof(canCommand));
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ ///
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Accumulator))
+ RaisePropertyChanged(e.PropertyName);
+ if (e.PropertyName == nameof(Memory))
+ RaisePropertyChanged(e.PropertyName);
+ if (e.PropertyName == nameof(Register))
+ RaisePropertyChanged(e.PropertyName);
+ }
+ #endregion
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..b806c95f2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,32 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_05_CommandParCalculator.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModel
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/.info
new file mode 100644
index 000000000..0791c329c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/.info
@@ -0,0 +1 @@
+Folder for Views
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/CommandParCalculatorView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/CommandParCalculatorView.xaml
new file mode 100644
index 000000000..67f51c0c1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/CommandParCalculatorView.xaml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/CommandParCalculatorView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/CommandParCalculatorView.xaml.cs
new file mode 100644
index 000000000..c45971256
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/CommandParCalculatorView.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_05_CommandParCalculator.Views;
+
+///
+/// Interaktionslogik für CommandParCalculatorView.xaml
+///
+public partial class CommandParCalculatorView : Page
+{
+ public CommandParCalculatorView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/Page1.xaml b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/Page1.xaml
new file mode 100644
index 000000000..c8a26b86f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/Page1.xaml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/Page1.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/Page1.xaml.cs
new file mode 100644
index 000000000..74bdf7fd0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/Page1.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_05_CommandParCalculator.Views;
+
+///
+/// Interaktionslogik für Page1.xaml
+///
+public partial class Page1 : Page
+{
+ public Page1()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/ValueConverter/DoubleValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/ValueConverter/DoubleValueConverter.cs
new file mode 100644
index 000000000..56a0ea29a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculator/Views/ValueConverter/DoubleValueConverter.cs
@@ -0,0 +1,75 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_05_CommandParCalculator.Views.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class DoubleValueConverter : IValueConverter
+{
+ public double FixedFactor { get; set; } = 1.0d;
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is double dval && parameter is string spar)
+ return (dval*FixedFactor).ToString(spar,culture);
+ else
+ return value?.ToString() ?? "";
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string sval && parameter is string spar)
+ {
+ if (double.TryParse(sval, NumberStyles.Float, culture, out double dval))
+ return dval / FixedFactor;
+ else
+ if (spar.Contains("{"))
+ return double.NaN; // Todo:
+ else
+ {
+ var pp = spar.LastIndexOf('0');
+ if (double.TryParse(sval.Replace(spar.Substring(pp + 1), "").Trim(),NumberStyles.Float,culture, out var dVal))
+ return dVal / FixedFactor;
+ else
+ return double.NaN;
+ }
+
+ }
+ else
+ return 0d;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/MVVM_05_CommandParCalculatorTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/MVVM_05_CommandParCalculatorTests.csproj
index 74dd70d0b..ed128af49 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/MVVM_05_CommandParCalculatorTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/MVVM_05_CommandParCalculatorTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/MVVM_05_CommandParCalculator_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/MVVM_05_CommandParCalculator_netTests.csproj
index 0292ab95f..de8dda6de 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/MVVM_05_CommandParCalculator_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/MVVM_05_CommandParCalculator_netTests.csproj
@@ -1,14 +1,20 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Model/CalculatorModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Model/CalculatorModelTests.cs
new file mode 100644
index 000000000..fc6367677
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Model/CalculatorModelTests.cs
@@ -0,0 +1,377 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using BaseLib.Helper;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System;
+using System.ComponentModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Model.Tests;
+
+///
+/// Defines test class CalculatorModelTests.
+///
+///
+[TestClass]
+public class CalculatorModelTests
+{
+ ///
+ /// The test model
+ ///
+ ///
+ CalculatorModel testModel;
+
+ ///
+ /// The debug out
+ ///
+ ///
+ public string DebugOut = "";
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnPropertyChanged;
+ DebugOut = "";
+ }
+ ///
+ /// Cleanups this instance.
+ ///
+ ///
+ [TestCleanup]
+ public void Cleanup()
+ {
+ // Nothing to do
+ }
+
+ ///
+ /// Defines the test method SetUpTest.
+ ///
+ ///
+ [TestMethod]
+ public void SetUpTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(NotificationObject));
+ Assert.IsInstanceOfType(testModel, typeof(CalculatorModel));
+ Assert.IsInstanceOfType(CalculatorModel.Instance, typeof(CalculatorModel));
+ Assert.AreEqual(0d, testModel.Accumulator);
+ Assert.IsNull(testModel.Register);
+ Assert.IsNull(testModel.Memory);
+ Assert.IsFalse(testModel.DecMode);
+ Assert.IsFalse(testModel.canOperator(EOperations.CalcResult));
+ Assert.IsFalse(testModel.canOperator(EOperations.Add));
+ Assert.IsTrue(testModel.canCommand(ECommands.DecMode));
+ Assert.IsTrue(testModel.canCommand(ECommands.MR));
+ Assert.AreEqual(ETrigMode.Grad, testModel.TrigMode);
+ Assert.AreEqual(ECalcError.None, testModel.CalcError);
+ Assert.AreEqual(0, testModel.StackSize);
+ Assert.AreEqual("", DebugOut);
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ ///
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ DoLog($"PropChg({sender},{e.PropertyName})={sender.GetProp(e.PropertyName)}");
+ }
+
+ ///
+ /// Does the log.
+ ///
+ /// The v.
+ ///
+ private void DoLog(string v)
+ {
+ DebugOut += $"{v}{Environment.NewLine}";
+ }
+
+ ///
+ /// Numbers the command test.
+ ///
+ /// The name.
+ /// The ae value.
+ /// The d exp.
+ /// As exp.
+ ///
+ [TestMethod]
+ [DataRow("0", new ENumbers[] { ENumbers._0 }, 0d, new string[] { "" })]
+ [DataRow("123", new ENumbers[] { ENumbers._1, ENumbers._2, ENumbers._3 }, 123d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=12
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=123
+" })]
+ [DataRow("987", new ENumbers[] { ENumbers._9, ENumbers._8, ENumbers._7 }, 987d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=9
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=98
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=987
+" })]
+ public void NumberCmdTest(string name, ENumbers[] aeVal, double dExp, string[] asExp)
+ {
+ // Arrange
+
+ // Act
+ foreach (var ae in aeVal)
+ testModel.NumberCmd(ae);
+
+ // Assert
+ Assert.AreEqual(dExp, testModel.Accumulator);
+ Assert.IsTrue(testModel.canOperator(EOperations.Add));
+ Assert.AreEqual(asExp[0], DebugOut);
+ }
+
+ ///
+ /// Numbers the CMD2 test.
+ ///
+ /// The name.
+ /// The ae value.
+ /// The d exp.
+ /// As exp.
+ ///
+ [TestMethod]
+ [DataRow("0", new ENumbers[] { ENumbers._0 }, 0d, new string[] { "" })]
+#if NET5_0_OR_GREATER
+ [DataRow("123", new ENumbers[] { ENumbers._1, ENumbers._2, ENumbers._3 }, 0.123d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,1
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,12000000000000001
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,12300000000000001
+" })]
+#else
+ [DataRow("123", new ENumbers[] { ENumbers._1, ENumbers._2, ENumbers._3 }, 0.123d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,1
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,12
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,123
+" })]
+#endif
+ [DataRow("987", new ENumbers[] { ENumbers._9, ENumbers._8, ENumbers._7 }, 0.987d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,9
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,98
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,987
+" })]
+ public void NumberCmd2Test(string name, ENumbers[] aeVal, double dExp, string[] asExp)
+ {
+ // Arrange
+ testModel.NumberCmd(ENumbers._0);
+ testModel.DecMode = true;
+ // Act
+ foreach (var ae in aeVal)
+ testModel.NumberCmd(ae);
+
+ // Assert
+ Assert.AreEqual(dExp, testModel.Accumulator, 1e-10);
+ Assert.AreEqual(asExp[0], DebugOut);
+ }
+
+ ///
+ /// Operators the command test.
+ ///
+ /// The e op.
+ /// The d val1.
+ /// The d val2.
+ /// The d exp.
+ /// As exp.
+ ///
+ [TestMethod]
+ [DataRow(EOperations.Add, 1d, 2d, 3d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=1
+", @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=
+" })]
+ [DataRow(EOperations.Subtract, 3d, 2d, 1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=3
+", @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=
+" })]
+ [DataRow(EOperations.Multiply, 2d, 3d, 6d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=2
+", @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=6
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=
+" })]
+ [DataRow(EOperations.Divide, 6d, 3d, 2d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=6
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=6
+", @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=
+" })]
+ [DataRow(EOperations.Power, 2d, 3d, 8d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=2
+", @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=8
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Register)=
+" })]
+ public void OperatorCmdTest(EOperations eOp, double dVal1, double dVal2, double dExp, string[] asExp)
+ {
+ // Arrange
+ testModel.Accumulator = dVal1;
+
+ // Act
+ testModel.OperatorCmd(eOp);
+
+ // Assert
+ Assert.AreEqual(dVal1, testModel.Accumulator);
+ Assert.AreEqual(dVal1, testModel.Register);
+ Assert.AreEqual(asExp[0], DebugOut);
+ DebugOut = "";
+
+ // Arrange
+ testModel.Accumulator = dVal2;
+ // Act2
+ testModel.OperatorCmd(EOperations.CalcResult);
+ // Assert2
+ Assert.AreEqual(dExp, testModel.Accumulator);
+ Assert.IsNull(testModel.Register);
+ Assert.AreEqual(asExp[1], DebugOut);
+ }
+
+ ///
+ /// Operators the CMD2 test.
+ ///
+ /// The e op.
+ /// The d val1.
+ /// The .
+ /// The d exp.
+ /// As exp.
+ ///
+ [TestMethod]
+ [DataRow(EOperations.Negate, 1d, 2d, -1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=-1
+", @"" })]
+ [DataRow(EOperations.Square, 3d, 2d, 9d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=9
+", @"" })]
+ [DataRow(EOperations.SquareRt, 4d, 3d, 2d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=4
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=2
+", @"" })]
+ [DataRow(EOperations.Inverse, 4d, 3d, 0.25d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=4
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,25
+", @"" })]
+#if NET5_0_OR_GREATER
+ [DataRow(EOperations.Sin, Math.PI * 0.5d, 3d, 1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1,5707963267948966
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+", @"" })]
+ [DataRow(EOperations.Cos, Math.PI, 3d, -1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=3,141592653589793
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=-1
+", @"" })]
+ [DataRow(EOperations.Tan, Math.PI * 0.25, 0d, 1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,7853981633974483
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,9999999999999999
+", @"" })]
+#else
+ [DataRow(EOperations.Sin, Math.PI * 0.5d, 3d, 1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1,5707963267949
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+", @"" })]
+ [DataRow(EOperations.Cos, Math.PI, 3d, -1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=3,14159265358979
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=-1
+", @"" })]
+ [DataRow(EOperations.Tan, Math.PI * 0.25, 0d, 1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0,785398163397448
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+", @"" })]
+#endif
+ [DataRow(EOperations.ExpN, 0d, 3d, 1d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+", @"" })]
+ [DataRow(EOperations.LogN, 1d, 0d, 0d, new string[] { @"PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05_CommandParCalculator.Model.CalculatorModel,Accumulator)=0
+", @"" })]
+ public void OperatorCmd2Test(EOperations eOp, double dVal1, double _, double dExp, string[] asExp)
+ {
+ // Arrange
+ testModel.Accumulator = dVal1;
+
+ // Act
+ testModel.OperatorCmd(eOp);
+
+ // Assert
+ Assert.AreEqual(dExp, testModel.Accumulator, 1e-10);
+ Assert.IsNull(testModel.Register);
+ Assert.AreEqual(asExp[0], DebugOut);
+
+ }
+
+ ///
+ /// Calculates the command test.
+ ///
+ /// The e command.
+ /// The d val1.
+ /// The d a exp.
+ /// The d m exp.
+ ///
+ [TestMethod]
+ [DataRow(ECommands.Nop, 123d, 123d, 123d)]
+ [DataRow(ECommands.Clear, 123d, 0d, 123d)]
+ [DataRow(ECommands.ClearAll, 123d, 0d, 123d)]
+ [DataRow(ECommands.e, 0d, Math.E, 0d)]
+ [DataRow(ECommands.Pi, 0d, Math.PI, 0d)]
+ [DataRow(ECommands.MS, 123d, 123d, 123d)]
+ [DataRow(ECommands.MR, 123d, 123d, 123d)]
+ [DataRow(ECommands.MC, 123d, 123d, null)]
+ [DataRow(ECommands.Mp, 123d, 123d, 246d)]
+ [DataRow(ECommands.Mm, 123d, 123d, 0d)]
+ public void CalcCmdTest(ECommands eCmd, double dVal1,
+ double dAExp,
+ double? dMExp)
+ {
+ // Arrange
+ testModel.Accumulator = dVal1;
+ testModel.Memory = dVal1;
+
+ // Act
+ testModel.CalcCmd(eCmd);
+
+ // Assert
+ Assert.AreEqual(dAExp, testModel.Accumulator);
+ Assert.AreEqual(dMExp, testModel.Memory);
+ }
+
+ ///
+ /// Defines the test method CalcCmd2Test.
+ ///
+ ///
+ [TestMethod]
+ public void CalcCmd2Test()
+ {
+ testModel.CalcCmd(ECommands.DecMode);
+ Assert.IsTrue(testModel.DecMode);
+ }
+
+ ///
+ /// Defines the test method canOperatorTest.
+ ///
+ /// The e value.
+ /// The e pre.
+ /// The d akk.
+ /// if set to true [x result].
+ ///
+ [TestMethod()]
+ [DataRow((EOperations)(-1), EOperations.Nop, 0d, false)]
+ [DataRow(EOperations.Nop,EOperations.Add,3d,true)]
+ [DataRow(EOperations.CalcResult, EOperations.Add, 3d, true)]
+ [DataRow(EOperations.Subtract, EOperations.Add, 3d, true)]
+ public void canOperatorTest(EOperations eVal, EOperations ePre,double dAkk,bool xResult)
+ {
+ // Arrange
+ testModel.Accumulator = dAkk;
+ testModel.OperatorCmd(ePre);
+ // Act & Assert
+ Assert.AreEqual(xResult,testModel.canOperator(eVal));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Model/TestCalcModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Model/TestCalcModel.cs
new file mode 100644
index 000000000..94866524f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Model/TestCalcModel.cs
@@ -0,0 +1,185 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System;
+using System.Collections.Generic;
+
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Model;
+
+///
+/// Class TestCalcModel.
+/// Implements the
+/// Implements the
+///
+///
+///
+///
+internal class TestCalcModel : NotificationObject, ICalculatorModel
+{
+ ///
+ /// The accumulator
+ ///
+ ///
+ private double _accumulator = 0d;
+ ///
+ /// The register
+ ///
+ ///
+ private double? _register;
+ ///
+ /// The memory
+ ///
+ ///
+ private double? _memory;
+ ///
+ /// The trig mode
+ ///
+ ///
+ private ETrigMode _trigMode;
+ ///
+ /// The calculate error
+ ///
+ ///
+ private ECalcError _calcError;
+ ///
+ /// The stack size
+ ///
+ ///
+ private int _stackSize;
+ ///
+ /// The do log
+ ///
+ ///
+ private Action _doLog;
+
+ ///
+ /// Gets or sets the accumulator.
+ ///
+ /// The accumulator.
+ ///
+ public double Accumulator { get => _accumulator; set => SetProperty(ref _accumulator, value); }
+ ///
+ /// Gets or sets the register.
+ ///
+ /// The register.
+ ///
+ public double? Register { get => _register; set => SetProperty(ref _register, value); }
+ ///
+ /// Gets or sets the memory.
+ ///
+ /// The memory.
+ ///
+ public double? Memory { get => _memory; set => SetProperty(ref _memory, value); }
+ ///
+ /// Gets the dependencies.
+ ///
+ /// The dependencies.
+ ///
+ public IEnumerable<(string Dest, string Src)> Dependencies => new[]{
+ (nameof(canOperator),nameof(Accumulator)),
+ (nameof(canOperator),nameof(Register)),
+ };
+ ///
+ /// Gets or sets the trig mode.
+ ///
+ /// The trig mode.
+ ///
+ public ETrigMode TrigMode { get => _trigMode; set => SetProperty(ref _trigMode, value); }
+ ///
+ /// Gets or sets the calculate error.
+ ///
+ /// The calculate error.
+ ///
+ public ECalcError CalcError { get => _calcError; set => SetProperty(ref _calcError, value); }
+ ///
+ /// Gets or sets the size of the stack.
+ ///
+ /// The size of the stack.
+ ///
+ public int StackSize { get => _stackSize; set => SetProperty(ref _stackSize, value); }
+ ///
+ /// Gets or sets a value indicating whether [x result].
+ ///
+ /// true if [x result]; otherwise, false.
+ ///
+ public bool xResult { get; set; } = false;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The do log.
+ ///
+ public TestCalcModel(Action DoLog)
+ {
+ _doLog = DoLog;
+ }
+
+ ///
+ /// Calculates the command.
+ ///
+ /// The o.
+ ///
+ public void CalcCmd(ECommands o)
+ {
+ _doLog($"CalcCmd({o})");
+ }
+
+ ///
+ /// Determines whether this instance can command the specified e c.
+ ///
+ /// The e c.
+ /// true if this instance can command the specified e c; otherwise, false.
+ ///
+ public bool canCommand(ECommands eC)
+ {
+ _doLog($"canCommand({eC})={xResult}");
+ return xResult;
+ }
+
+ ///
+ /// Determines whether this instance can operator the specified e o.
+ ///
+ /// The e o.
+ /// true if this instance can operator the specified e o; otherwise, false.
+ ///
+ public bool canOperator(EOperations eO)
+ {
+ _doLog($"canOperator({eO}={xResult})");
+ return xResult;
+ }
+
+ ///
+ /// Numbers the command.
+ ///
+ /// The o.
+ ///
+ public void NumberCmd(ENumbers o)
+ {
+ _doLog($"NumberCmd({o})");
+ }
+
+ ///
+ /// Operators the command.
+ ///
+ /// The e o.
+ ///
+ public void OperatorCmd(EOperations eO)
+ {
+ _doLog($"OperatorCmd({eO})");
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..321c45da5
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Properties/SettingsTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+namespace MVVM_05_CommandParCalculator.Properties.Tests;
+
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/ViewModels/CommandParCalculatorViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/ViewModels/CommandParCalculatorViewModelTests.cs
new file mode 100644
index 000000000..44121aba8
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/ViewModels/CommandParCalculatorViewModelTests.cs
@@ -0,0 +1,305 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using MVVM_05_CommandParCalculator.Model;
+using System;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.ViewModels.Tests;
+
+///
+/// Defines test class CommandParCalculatorViewModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class CommandParCalculatorViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ CommandParCalculatorViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// The old model
+ ///
+ ///
+ private Func _oldModel;
+ ///
+ /// The test calculate model
+ ///
+ ///
+ private TestCalcModel testCalcModel;
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ _oldModel = CommandParCalculatorViewModel.GetModel;
+ testCalcModel = new TestCalcModel(DoLog);
+ CommandParCalculatorViewModel.GetModel = () => testCalcModel;
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ Assert.IsFalse(testModel.canOperator(EOperations.CalcResult));
+ Assert.IsFalse(testModel.canOperator(EOperations.Add));
+ Assert.IsFalse(testModel.canCommand(ECommands.DecMode));
+ Assert.IsFalse(testModel.canCommand(ECommands.MR));
+ ClearLog();
+ }
+ ///
+ /// Cleanups this instance.
+ ///
+ ///
+ [TestCleanup]
+ public void Cleanup()
+ {
+ CommandParCalculatorViewModel.GetModel = _oldModel;
+ }
+
+ ///
+ /// Defines the test method CommandParCalculatorViewModelTest.
+ ///
+ ///
+ [TestMethod]
+ public void CommandParCalculatorViewModelTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(CommandParCalculatorViewModel));
+ Assert.AreEqual(0d, testModel.Accumulator);
+ Assert.AreEqual(double.NaN, testModel.Register);
+ Assert.AreEqual(double.NaN, testModel.Memory);
+ Assert.AreEqual("None Grad ", testModel.Status);
+ Assert.IsFalse(testModel.canOperator(EOperations.CalcResult));
+ Assert.IsFalse(testModel.canOperator(EOperations.Add));
+ Assert.IsFalse(testModel.canCommand(ECommands.DecMode));
+ Assert.IsFalse(testModel.canCommand(ECommands.MR));
+ Assert.IsNotNull( testModel.NumberCommand);
+ Assert.IsNotNull( testModel.OperatorCommand);
+ Assert.IsNotNull( testModel.CalculatorCommand);
+ Assert.AreEqual("canOperator(CalcResult=False)\r\ncanOperator(Add=False)\r\ncanCommand(DecMode)=False\r\ncanCommand(MR)=False\r\n", DebugLog);
+ }
+
+ ///
+ /// Accumulators the test.
+ ///
+ /// The name.
+ /// The ad value.
+ /// As exp.
+ ///
+ [TestMethod()]
+ [DataRow("0",new double[] {0d }, new string[] { "0", "" })]
+ [DataRow("25", new double[] { 25d }, new string[] { "25", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=25
+" })]
+#if NET5_0_OR_GREATER
+ [DataRow("PI,E", new double[] { Math.PI,Math.E }, new string[] { "2.718281828459045", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=3,141592653589793
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=2,718281828459045
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN,double.PositiveInfinity,double.Epsilon }, new string[] { "5E-324", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=-1,7976931348623157E+308
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=NaN
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=∞
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=5E-324
+" })]
+#else
+ [DataRow("PI,E", new double[] { Math.PI,Math.E }, new string[] { "2.71828182845905", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=3,14159265358979
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=2,71828182845905
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN,double.PositiveInfinity,double.Epsilon }, new string[] { "4.94065645841247E-324", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=-1,79769313486232E+308
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=NaN
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=∞
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Accumulator)=4,94065645841247E-324
+" })]
+#endif
+ public void AccumulatorTest(string name, double[] adVal, string[] asExp)
+ {
+ // Arrange
+ // Act
+ foreach (var item in adVal)
+ testCalcModel.Accumulator = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Accumulator.ToString(CultureInfo.InvariantCulture), "Accumulator");
+ Assert.AreEqual(asExp[1], DebugLog, "DebugOut");
+ }
+
+ ///
+ /// Registers the test.
+ ///
+ /// The name.
+ /// The ad value.
+ /// As exp.
+ ///
+ [TestMethod()]
+ [DataRow("0", new double[] { 0d }, new string[] { "0", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=0
+" })]
+ [DataRow("25", new double[] { 25d }, new string[] { "25", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=25
+" })]
+#if NET5_0_OR_GREATER
+ [DataRow("PI,E", new double[] { Math.PI, Math.E }, new string[] { "2.718281828459045", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=3,141592653589793
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=2,718281828459045
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN, double.PositiveInfinity, double.Epsilon }, new string[] { "5E-324", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=-1,7976931348623157E+308
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=NaN
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=∞
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=5E-324
+" })]
+#else
+ [DataRow("PI,E", new double[] { Math.PI, Math.E }, new string[] { "2.71828182845905", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=3,14159265358979
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=2,71828182845905
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN, double.PositiveInfinity, double.Epsilon }, new string[] { "4.94065645841247E-324", @"canOperator(CalcResult=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=-1,79769313486232E+308
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=NaN
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=∞
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Register)=4,94065645841247E-324
+" })]
+#endif
+ public void RegisterTest(string name, double[] adVal, string[] asExp)
+ {
+ // Arrange
+ // Act
+ foreach (var item in adVal)
+ testCalcModel.Register = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Register.ToString(CultureInfo.InvariantCulture), "Accumulator");
+ Assert.AreEqual(asExp[1], DebugLog, "DebugOut");
+ }
+
+ ///
+ /// Memories the test.
+ ///
+ /// The name.
+ /// The ad value.
+ /// As exp.
+ ///
+ [TestMethod()]
+ [DataRow("0", new double[] { 0d }, new string[] { "0", "PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=0\r\n" })]
+ [DataRow("25", new double[] { 25d }, new string[] { "25", @"PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=25
+" })]
+#if NET5_0_OR_GREATER
+ [DataRow("PI,E", new double[] { Math.PI, Math.E }, new string[] { "2.718281828459045", @"PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=3,141592653589793
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=2,718281828459045
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN, double.PositiveInfinity, double.Epsilon }, new string[] { "5E-324", @"PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=-1,7976931348623157E+308
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=NaN
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=∞
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=5E-324
+" })]
+#else
+ [DataRow("PI,E", new double[] { Math.PI, Math.E }, new string[] { "2.71828182845905", @"PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=3,14159265358979
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=2,71828182845905
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN, double.PositiveInfinity, double.Epsilon }, new string[] { "4.94065645841247E-324", @"PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=-1,79769313486232E+308
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=NaN
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=∞
+PropChg(MVVM_05_CommandParCalculator.ViewModels.CommandParCalculatorViewModel,Memory)=4,94065645841247E-324
+" })]
+#endif
+ public void MemoryTest(string name, double[] adVal, string[] asExp)
+ {
+ // Arrange
+ // Act
+ foreach (var item in adVal)
+ testCalcModel.Memory = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Memory.ToString(CultureInfo.InvariantCulture), "Accumulator");
+ Assert.AreEqual(asExp[1], DebugLog, "DebugOut");
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..78f64eba5
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,65 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.ViewModels.Tests;
+
+///
+/// Defines test class MainWindowViewModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/CommandParCalculatorViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/CommandParCalculatorViewTests.cs
new file mode 100644
index 000000000..8179e0843
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/CommandParCalculatorViewTests.cs
@@ -0,0 +1,70 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-02-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Views.Tests;
+
+///
+/// Defines test class CommandParCalculatorViewTests.
+///
+///
+[TestClass()]
+public class CommandParCalculatorViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ CommandParCalculatorView testView;
+ ///
+ /// The vm
+ ///
+ ///
+ private object vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize()]
+ public void Init()
+ {
+ Thread thread = new(() =>
+ {
+ testView = new();
+ vm = testView.DataContext;
+ });
+ thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ thread.Start();
+ thread.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method ValidationPageTest.
+ ///
+ ///
+ [TestMethod()]
+ public void ValidationPageTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsNotNull(vm);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..7b34cc495
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/MainWindowTests.cs
@@ -0,0 +1,45 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-02-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+///
+[TestClass()]
+public class MainWindowTests
+{
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/Page1Tests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/Page1Tests.cs
new file mode 100644
index 000000000..c631e95e0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/Page1Tests.cs
@@ -0,0 +1,66 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Views.Tests;
+
+///
+/// Defines test class Page1Tests.
+///
+///
+[TestClass()]
+public class Page1Tests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ Page1 testView;
+ // private object vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize()]
+ public void Init()
+ {
+ Thread thread = new(() =>
+ {
+ testView = new();
+// vm = testView.DataContext;
+ });
+ thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ thread.Start();
+ thread.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method ValidationPageTest.
+ ///
+ ///
+ [TestMethod()]
+ public void ValidationPageTest()
+ {
+ Assert.IsNotNull(testView);
+// Assert.IsNotNull(vm);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/ValueConverter/DoubleValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/ValueConverter/DoubleValueConverterTests.cs
new file mode 100644
index 000000000..62e2c7f9f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05_CommandParCalculatorTests/Views/ValueConverter/DoubleValueConverterTests.cs
@@ -0,0 +1,83 @@
+// ***********************************************************************
+// Assembly : MVVM_05_CommandParCalculator_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05_CommandParCalculator.Views.ValueConverter.Tests;
+
+
+///
+/// Defines test class DoubleValueConverterTests.
+///
+///
+[TestClass]
+public class DoubleValueConverterTests
+{
+ ///
+ /// Converts the test.
+ ///
+ /// The value.
+ /// The parameter.
+ /// The fixed factor.
+ /// The expected result.
+ ///
+ [TestMethod]
+ [DataRow(123.45d, "F2", 2d, "246.90")]
+ [DataRow(123.45d, "C2", 2d, "¤246.90")]
+ [DataRow("Hallo", "C2", 2d, "Hallo")]
+ [DataRow(null, "C2", 2d, "")]
+ public void ConvertTest(object value, string parameter, double fixedFactor, string expectedResult)
+ {
+ // Arrange
+ var converter = new DoubleValueConverter();
+ converter.FixedFactor = fixedFactor;
+
+ // Act
+ var result = converter.Convert(value, typeof(string), parameter, CultureInfo.InvariantCulture);
+
+ // Assert
+ Assert.AreEqual(expectedResult, result);
+ }
+
+ ///
+ /// Converts the back test.
+ ///
+ /// The value.
+ /// The parameter.
+ /// The fixed factor.
+ /// The expected result.
+ ///
+ [TestMethod]
+ [DataRow("246.90", "F2", 2d, 123.45d)]
+ [DataRow("246.90-", "{0}-", 2d, double.NaN)]
+ [DataRow("246.90 ¤", "0.00 ¤", 2d, 123.45d)]
+ [DataRow("246.X ¤", "0.00 ¤", 2d, double.NaN)]
+ [DataRow(null, "0.00 ¤", 2d, 0d)]
+ public void ConvertBackTest(string value, string parameter, double fixedFactor, double expectedResult)
+ {
+ // Arrange
+ var converter = new DoubleValueConverter();
+ converter.FixedFactor = fixedFactor;
+
+ // Act
+ var result = converter.ConvertBack(value, typeof(double), parameter, CultureInfo.InvariantCulture);
+
+ // Assert
+ Assert.AreEqual(expectedResult, result);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.config b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.config
new file mode 100644
index 000000000..996fd1e81
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.xaml
new file mode 100644
index 000000000..cf9155065
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.xaml.cs
new file mode 100644
index 000000000..7ec433dfe
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/App.xaml.cs
@@ -0,0 +1,37 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using BaseLib.Helper;
+using Microsoft.Extensions.DependencyInjection;
+using MVVM_05a_CTCommandParCalc.Model;
+using MVVM_05a_CTCommandParCalc.Model.Interfaces;
+using System.Windows;
+
+namespace MVVM_05a_CTCommandParCalc;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ var sc = new ServiceCollection()
+ .AddTransient();
+
+ var bc = sc.BuildServiceProvider();
+
+ IoC.Configure(bc);
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ECalcError.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ECalcError.cs
new file mode 100644
index 000000000..9530be7d9
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ECalcError.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+///
+/// The Data namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Data;
+
+///
+/// Enum ECalcError
+///
+///
+public enum ECalcError
+{
+ ///
+ /// The none
+ ///
+ ///
+ None = 0,
+ ///
+ /// The div by zero
+ ///
+ ///
+ DivByZero,
+ ///
+ /// The over flow
+ ///
+ ///
+ OverFlow,
+ ///
+ /// The under flow
+ ///
+ ///
+ UnderFlow,
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ECommands.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ECommands.cs
new file mode 100644
index 000000000..f7ae6b328
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ECommands.cs
@@ -0,0 +1,93 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+
+///
+/// The Data namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Data;
+
+///
+/// Enum ECommands
+///
+///
+public enum ECommands
+{
+ ///
+ /// The nop
+ ///
+ ///
+ Nop,
+ ///
+ /// The clear
+ ///
+ ///
+ Clear,
+ ///
+ /// The clear all
+ ///
+ ///
+ ClearAll,
+ ///
+ /// The decimal mode
+ ///
+ ///
+ DecMode,
+ ///
+ /// The pi
+ ///
+ ///
+ Pi,
+ ///
+ /// The e
+ ///
+ ///
+ e,
+ ///
+ /// The bracket open
+ ///
+ ///
+ BracketOpen,
+ ///
+ /// The bracket close
+ ///
+ ///
+ BracketClose,
+ ///
+ /// The ms
+ ///
+ ///
+ MS,
+ ///
+ /// The mr
+ ///
+ ///
+ MR,
+ ///
+ /// The mc
+ ///
+ ///
+ MC,
+ ///
+ /// The mp
+ ///
+ ///
+ Mp,
+ ///
+ /// The mm
+ ///
+ ///
+ Mm,
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ENumbers.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ENumbers.cs
new file mode 100644
index 000000000..1431ad0b6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ENumbers.cs
@@ -0,0 +1,88 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_net
+// Author : Mir
+// Created : 01-16-2025
+//
+// Last Modified By : Mir
+// Last Modified On : 02-18-2024
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+///
+/// The Data namespace.
+///
+namespace MVVM_05a_CTCommandParCalc.Data;
+
+///
+/// Enum ENumbers
+///
+public enum ENumbers:int
+{
+ ///
+ /// The 0
+ ///
+ _0 = 0,
+ ///
+ /// The 1
+ ///
+ _1 = 1,
+ ///
+ /// The 2
+ ///
+ _2 = 2,
+ ///
+ /// The 3
+ ///
+ _3 = 3,
+ ///
+ /// The 4
+ ///
+ _4 = 4,
+ ///
+ /// The 5
+ ///
+ _5 = 5,
+ ///
+ /// The 6
+ ///
+ _6 = 6,
+ ///
+ /// The 7
+ ///
+ _7 = 7,
+ ///
+ /// The 8
+ ///
+ _8 = 8,
+ ///
+ /// The 9
+ ///
+ _9 = 9,
+ ///
+ /// a
+ ///
+ _A = 10,
+ ///
+ /// The b
+ ///
+ _B = 11,
+ ///
+ /// The c
+ ///
+ _C = 12,
+ ///
+ /// The d
+ ///
+ _D = 13,
+ ///
+ /// The e
+ ///
+ _E = 14,
+ ///
+ /// The f
+ ///
+ _F = 15
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/EOperations.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/EOperations.cs
new file mode 100644
index 000000000..f55620adb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/EOperations.cs
@@ -0,0 +1,121 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+///
+/// The Data namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Data;
+
+///
+/// Enum EOperations
+///
+///
+public enum EOperations
+{
+ ///
+ /// The nop
+ ///
+ ///
+ Nop,
+ ///
+ /// The calculate result
+ ///
+ ///
+ CalcResult, // =
+ ///
+ /// The add
+ ///
+ ///
+ Add, // +
+ ///
+ /// The subtract
+ ///
+ ///
+ Subtract, // -
+ ///
+ /// The multiply
+ ///
+ ///
+ Multiply,
+ ///
+ /// The divide
+ ///
+ ///
+ Divide,
+ ///
+ /// The negate
+ ///
+ ///
+ Negate,
+ ///
+ /// The power
+ ///
+ ///
+ Power,
+ ///
+ /// The square
+ ///
+ ///
+ Square,
+ ///
+ /// The square root
+ ///
+ ///
+ SquareRt, // vx
+ ///
+ /// The inverse
+ ///
+ ///
+ Inverse, // 1/x
+ ///
+ /// The sin
+ ///
+ ///
+ Sin,
+ ///
+ /// The cos
+ ///
+ ///
+ Cos,
+ ///
+ /// The tan
+ ///
+ ///
+ Tan,
+ ///
+ /// The exp n
+ ///
+ ///
+ ExpN, // e?
+ ///
+ /// The log n
+ ///
+ ///
+ LogN,
+ ///
+ /// The exp x
+ ///
+ ///
+ ExpX, // y?
+ ///
+ /// The log x
+ ///
+ ///
+ LogX,
+ ///
+ /// The square rt x
+ ///
+ ///
+ SquareRtX,
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ETrigMode.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ETrigMode.cs
new file mode 100644
index 000000000..7deac7f92
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Data/ETrigMode.cs
@@ -0,0 +1,41 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Data;
+
+///
+/// Enum ETrigMode
+///
+///
+public enum ETrigMode
+{
+ ///
+ /// The grad
+ ///
+ ///
+ Grad,
+ ///
+ /// The RAD
+ ///
+ ///
+ Rad,
+ ///
+ /// The degr
+ ///
+ ///
+ Degr
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MVVM_05a_CTCommandParCalc.csproj b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MVVM_05a_CTCommandParCalc.csproj
index 891802cde..837c1e004 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MVVM_05a_CTCommandParCalc.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MVVM_05a_CTCommandParCalc.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MVVM_05a_CTCommandParCalc_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MVVM_05a_CTCommandParCalc_net.csproj
index 2d65b76a3..f6a77785f 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MVVM_05a_CTCommandParCalc_net.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MVVM_05a_CTCommandParCalc_net.csproj
@@ -2,12 +2,18 @@
WinExe
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
@@ -20,7 +26,7 @@
-
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MainWindow.xaml
new file mode 100644
index 000000000..ec0728003
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MainWindow.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MainWindow.xaml.cs
new file mode 100644
index 000000000..3d46904e6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_05a_CTCommandParCalc;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Model/.info b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Model/.info
new file mode 100644
index 000000000..e69de29bb
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Model/CalculatorModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Model/CalculatorModel.cs
new file mode 100644
index 000000000..960aeaee3
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Model/CalculatorModel.cs
@@ -0,0 +1,334 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using CommunityToolkit.Mvvm.ComponentModel;
+using MVVM.ViewModel;
+using MVVM_05a_CTCommandParCalc.Data;
+using MVVM_05a_CTCommandParCalc.Model.Interfaces;
+using System;
+using System.Collections.Generic;
+
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Model;
+
+///
+/// Class CalculatorModel.
+/// Implements the
+/// Implements the
+///
+///
+///
+///
+public partial class CalculatorModel : ObservableObject, ICalculatorModel
+{
+
+ #region Properties
+ ///
+ /// The unary operation
+ ///
+ ///
+ private static readonly List _unaryOperation = new() {
+ EOperations.Nop,
+ EOperations.Negate,
+ EOperations.Sin,
+ EOperations.Cos,
+ EOperations.Tan,
+ EOperations.Square,
+ EOperations.SquareRt,
+ EOperations.Inverse,
+ EOperations.ExpN,
+ EOperations.LogN,
+ };
+
+ ///
+ /// The operation level
+ ///
+ ///
+ private static readonly Dictionary _OperationLevel = new() {
+ { EOperations.CalcResult, 0 },
+ { EOperations.Add, 1 },
+ { EOperations.Subtract, 1 },
+ { EOperations.Multiply, 2 },
+ { EOperations.Divide, 2 },
+ { EOperations.Power, 3 },
+ { EOperations.SquareRtX, 3 },
+ };
+
+ ///
+ /// The instance
+ ///
+ ///
+ private static CalculatorModel _instance;
+ ///
+ /// The op
+ ///
+ ///
+ private EOperations _op;
+ ///
+ /// The operation
+ ///
+ ///
+ private Func? _Operation;
+ ///
+ /// The accumulator
+ ///
+ ///
+ [ObservableProperty]
+ private double _accumulator = 0d;
+ ///
+ /// The register
+ ///
+ ///
+ [ObservableProperty]
+ private double? _register = null;
+ ///
+ /// The memory
+ ///
+ ///
+ [ObservableProperty]
+ private double? _memory = null;
+ ///
+ /// The stack
+ ///
+ ///
+ private Stack<(double, EOperations, Func)> _stack = new();
+ ///
+ /// The decimal fak
+ ///
+ ///
+ private double _decFak;
+ ///
+ /// The decimal mode
+ ///
+ ///
+ private bool _decMode;
+ ///
+ /// The edit mode
+ ///
+ ///
+ private bool _editMode;
+ ///
+ /// The trig mode
+ ///
+ ///
+ private ETrigMode _trigMode;
+ ///
+ /// The calculate error
+ ///
+ ///
+ private ECalcError _calcError;
+
+ ///
+ /// Gets or sets a value indicating whether [decimal mode].
+ ///
+ /// true if [decimal mode]; otherwise, false.
+ ///
+ public bool DecMode
+ {
+ get => _decMode;
+ set
+ {
+ if (!_decMode && value)
+ _decFak = 1d;
+ _decMode = value;
+ }
+ }
+
+ ///
+ /// Gets the instance.
+ ///
+ /// The instance.
+ ///
+ public static CalculatorModel Instance => _instance ??= new();
+
+ ///
+ /// Gets the dependencies.
+ ///
+ /// The dependencies.
+ ///
+ public IEnumerable<(string, string)> Dependencies => new[]{
+ (nameof(canOperator),nameof(Accumulator)),
+ (nameof(canOperator),nameof(Register)),
+ };
+
+ ///
+ /// Gets the trig mode.
+ ///
+ /// The trig mode.
+ ///
+ public ETrigMode TrigMode => _trigMode;
+
+ ///
+ /// Gets the calculate error.
+ ///
+ /// The calculate error.
+ ///
+ public ECalcError CalcError => _calcError;
+
+ ///
+ /// Gets the size of the stack.
+ ///
+ /// The size of the stack.
+ ///
+ public int StackSize => _stack.Count;
+
+ #endregion
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public CalculatorModel() { }
+
+ ///
+ /// Determines whether this instance can operator the specified e o.
+ ///
+ /// The e o.
+ /// true if this instance can operator the specified e o; otherwise, false.
+ ///
+ public bool canOperator(EOperations eO)
+ {
+ if (eO == EOperations.Nop) return true;
+ if (eO == EOperations.CalcResult) return _Operation != null && _register != null;
+ if (eO > EOperations.CalcResult) return _editMode || _accumulator != 0d;
+ return false;
+ }
+
+ ///
+ /// Determines whether this instance can command the specified e.
+ ///
+ /// The e.
+ /// true if this instance can command the specified e; otherwise, false.
+ ///
+ public bool canCommand(ECommands e)
+ {
+ if (e == ECommands.DecMode) return !_decMode;
+ return true;
+ }
+
+
+ ///
+ /// Numbers the command.
+ ///
+ /// The oo.
+ ///
+ public void NumberCmd(ENumbers oo)
+ {
+ if (oo is ENumbers e && (int)e is int iN)
+ {
+ if (!_editMode)
+ {
+ Accumulator = 0d;
+ _editMode = true;
+ _decMode = false;
+ }
+ if (!_decMode)
+ Accumulator = Accumulator * 10d + iN;
+ else
+ {
+ _decFak *= 0.1d;
+ Accumulator = Accumulator + iN * _decFak;
+ }
+ }
+ }
+
+ ///
+ /// Operators the command.
+ ///
+ /// The e o.
+ ///
+ public void OperatorCmd(EOperations eO)
+ {
+
+ _editMode = false;
+
+ if (eO >= EOperations.CalcResult && _register != null && !_unaryOperation.Contains(eO))
+ {
+ if (_Operation != null )
+ Accumulator = _Operation.Invoke(_accumulator);
+
+ Register = null;
+ DecMode = false;
+ }
+ if (eO > EOperations.CalcResult && !_unaryOperation.Contains(eO))
+ { Register = _accumulator; };
+ var op = eO switch
+ {
+
+ EOperations.CalcResult => null,
+ EOperations.Add => (a) => _register!.Value + a,
+ EOperations.Subtract => (a) => _register!.Value - a,
+ EOperations.Multiply => (a) => _register!.Value * a,
+ EOperations.Divide => (a) => _register!.Value / a,
+ EOperations.Power => (a) => Math.Pow(_register!.Value, a),
+ EOperations.Negate => (a) => -a,
+ EOperations.Square => (a) => a * a,
+ EOperations.SquareRt => (a) => Math.Sqrt(a),
+ EOperations.Inverse => (a) => 1 / a,
+ EOperations.Sin => (a) => Math.Sin(a), // Dodo: Factor by Trig-Mode
+ EOperations.Cos => (a) => Math.Cos(a),
+ EOperations.Tan => (a) => Math.Tan(a),
+ EOperations.LogN => (a) => Math.Log(a),
+ EOperations.ExpN => (a) => Math.Exp(a),
+ _ => (Func?)null,
+ };
+ if (_unaryOperation.Contains(eO))
+ Accumulator = op?.Invoke(_accumulator) ?? _accumulator;
+ else
+ {
+ _op = eO;
+ _Operation = op;
+ }
+ }
+
+ ///
+ /// Calculates the command.
+ ///
+ /// The o.
+ ///
+ public void CalcCmd(ECommands o)
+ {
+ switch (o)
+ {
+ case ECommands.Clear: //Clear
+ Accumulator = 0d;
+ _editMode = false;
+ break;
+ case ECommands.ClearAll: //Clear All
+ Accumulator = 0d;
+ Register = 0d;
+ _Operation = null;
+ _editMode = false;
+ break;
+ case ECommands.DecMode: // DecimalSeparator
+ DecMode = true;
+ break;
+ case ECommands.e:
+ Accumulator = Math.E;
+ break;
+ case ECommands.Pi:
+ Accumulator = Math.PI;
+ break;
+ case ECommands.MS: Memory = Accumulator; break;
+ case ECommands.MR: Accumulator = Memory ?? 0d; break;
+ case ECommands.MC: Memory = null; break;
+ case ECommands.Mp: Memory = (Memory ?? 0d) + Accumulator; break;
+ case ECommands.Mm: Memory = (Memory ?? 0d) - Accumulator; break;
+ default:
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Model/Interfaces/ICalculatorModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Model/Interfaces/ICalculatorModel.cs
new file mode 100644
index 000000000..28e4433b1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Model/Interfaces/ICalculatorModel.cs
@@ -0,0 +1,108 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using MVVM_05a_CTCommandParCalc.Data;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Model.Interfaces;
+
+///
+/// Interface ICalculatorModel
+/// Extends the
+///
+///
+///
+public interface ICalculatorModel :INotifyPropertyChanged
+{
+
+ ///
+ /// Gets the accumulator.
+ ///
+ /// The accumulator.
+ ///
+ double Accumulator { get; }
+ ///
+ /// Gets the register.
+ ///
+ /// The register.
+ ///
+ double? Register { get; }
+ ///
+ /// Gets the memory.
+ ///
+ /// The memory.
+ ///
+ double? Memory { get; }
+ ///
+ /// Gets the dependencies.
+ ///
+ /// The dependencies.
+ ///
+ IEnumerable<(string Dest,string Src)> Dependencies { get; }
+ ///
+ /// Gets the trig mode.
+ ///
+ /// The trig mode.
+ ///
+ ETrigMode TrigMode { get; }
+ ///
+ /// Gets the calculate error.
+ ///
+ /// The calculate error.
+ ///
+ ECalcError CalcError { get; }
+
+ ///
+ /// Gets the size of the stack.
+ ///
+ /// The size of the stack.
+ ///
+ int StackSize { get; }
+ ///
+ /// Determines whether this instance can command the specified e c.
+ ///
+ /// The e c.
+ /// true if this instance can command the specified e c; otherwise, false.
+ ///
+ bool canCommand(ECommands eC);
+ ///
+ /// Calculates the command.
+ ///
+ /// The o.
+ ///
+ void CalcCmd(ECommands o);
+ ///
+ /// Determines whether this instance can operator the specified e o.
+ ///
+ /// The e o.
+ /// true if this instance can operator the specified e o; otherwise, false.
+ ///
+ bool canOperator(EOperations eO);
+ ///
+ /// Operators the command.
+ ///
+ /// The e o.
+ ///
+ void OperatorCmd(EOperations eO);
+ ///
+ /// Numbers the command.
+ ///
+ /// The o.
+ ///
+ void NumberCmd(ENumbers o);
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..ed1846baf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : WpfApp
+// Author : Mir
+// Created : 08-24-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen ber eine Assembly werden ber die folgenden
+// Attribute gesteuert. ndern Sie diese Attributwerte, um die Informationen zu ndern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("WpfApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("WpfApp")]
+[assembly: AssemblyCopyright("Copyright JC-Soft 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// fr COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen mssen, sollten Sie das ComVisible-Attribut fr diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei bereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwrterbcher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wrterbchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwrterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwrterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen fr eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie knnen alle Werte angeben oder Standardwerte fr die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..4714eabed
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Resources.Designer.cs
@@ -0,0 +1,139 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_05a_CTCommandParCalc.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_05a_CTCommandParCalc.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_05a_CTCommandParCalc
+ ///// Author : Mir
+ ///// Created : 05-11-2023
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 05-19-2023
+ ///// ***********************************************************************
+ ///// <copyright file="CalculatorModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2023
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ************************************************ [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CalculatorModel {
+ get {
+ return ResourceManager.GetString("CalculatorModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_05a_CTCommandParCalc.Views"
+ /// xmlns:model="clr-namespace:MVVM_05a_CTCommandParCalc.Model"
+ /// xmlns:mvvm="clr-namespace:MVVM_05a_CTCommandParCalc.ViewModels"
+ /// xmlns:ValueC [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CommandParCalculatorView {
+ get {
+ return ResourceManager.GetString("CommandParCalculatorView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_05a_CTCommandParCalc_net
+ ///// Author : Mir
+ ///// Created : 05-11-2023
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 05-19-2023
+ ///// ***********************************************************************
+ ///// <copyright file="CommandParCalculatorViewModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2023
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ****************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CommandParCalculatorViewModel {
+ get {
+ return ResourceManager.GetString("CommandParCalculatorViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Example-Calculator using CommandParameter with Community-Toolkit ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die CT CommandParameter ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Resources.resx
new file mode 100644
index 000000000..99127f6ea
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Resources.resx
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Model\CalculatorModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\Views\CommandParCalculatorView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\CommandParCalculatorViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
+
+ Example-Calculator using CommandParameter with Community-Toolkit
+
+
+ CT CommandParameter
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..45370d9ea
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Settings.Designer.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// nderungen an dieser Datei knnen falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_05a_CTCommandParCalc.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Settings.settings
new file mode 100644
index 000000000..a6d407f53
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/ViewModels/CommandParCalculatorViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/ViewModels/CommandParCalculatorViewModel.cs
new file mode 100644
index 000000000..17670028b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/ViewModels/CommandParCalculatorViewModel.cs
@@ -0,0 +1,126 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_net
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using System.ComponentModel;
+using BaseLib.Helper;
+using CommunityToolkit.Mvvm.Input;
+using MVVM.ViewModel;
+using MVVM_05a_CTCommandParCalc.Data;
+using MVVM_05a_CTCommandParCalc.Model.Interfaces;
+
+///
+/// The ViewModels namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.ViewModels;
+
+///
+/// Class CommandParCalculatorViewModel.
+/// Implements the
+///
+///
+///
+public partial class CommandParCalculatorViewModel : BaseViewModelCT
+{
+ #region Properties
+ ///
+ /// The model
+ ///
+ ///
+ private ICalculatorModel _model;
+
+ ///
+ /// Gets the accumulator.
+ ///
+ /// The accumulator.
+ ///
+ public double Accumulator => _model.Accumulator;
+ ///
+ /// Gets the memory.
+ ///
+ /// The memory.
+ ///
+ public double Memory => _model.Memory ?? double.NaN;
+ ///
+ /// Gets the register.
+ ///
+ /// The register.
+ ///
+ public double Register => _model.Register ?? double.NaN;
+ ///
+ /// Gets the status.
+ ///
+ /// The status.
+ ///
+ public string Status => $"{_model.CalcError} {_model.TrigMode} ";
+ #endregion
+
+ #region Methods
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public CommandParCalculatorViewModel():this(IoC.GetRequiredService())
+ {
+ }
+ public CommandParCalculatorViewModel(ICalculatorModel model)
+ {
+ _model = model;
+ _model.PropertyChanged += OnMPropertyChanged;
+ foreach (var d in _model.Dependencies)
+ AddPropertyDependency(d.Dest, d.Src);
+ AddPropertyDependency(nameof(OperatorCommand), nameof(canOperator));
+ AddPropertyDependency(nameof(CalculatorCommand), nameof(canCommand));
+ }
+ ///
+ /// Determines whether this instance can operator the specified e o.
+ ///
+ /// The e o.
+ /// true if this instance can operator the specified e o; otherwise, false.
+ ///
+ public bool canOperator(EOperations eO) => FuncProxy(eO, _model.canOperator);
+ ///
+ /// Determines whether this instance can command the specified e c.
+ ///
+ /// The e c.
+ /// true if this instance can command the specified e c; otherwise, false.
+ ///
+ public bool canCommand(ECommands eC) => FuncProxy(eC, _model.canCommand);
+
+ [RelayCommand()]
+ private void Number(ENumbers eN) => _model.NumberCmd(eN);
+
+ [RelayCommand(CanExecute = nameof(canOperator))]
+ private void Operator(EOperations eO) => _model.OperatorCmd(eO);
+
+ [RelayCommand(CanExecute = nameof(canCommand))]
+ private void Calculator(ECommands eC) => _model.CalcCmd(eC);
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ ///
+ private void OnMPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Accumulator))
+ OnPropertyChanged(e.PropertyName);
+ if (e.PropertyName == nameof(Memory))
+ OnPropertyChanged(e.PropertyName);
+ if (e.PropertyName == nameof(Register))
+ OnPropertyChanged(e.PropertyName);
+ }
+ #endregion
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..1c6b014ca
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,32 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc
+// Author : Mir
+// Created : 08-11-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-24-2022
+// ***********************************************************************
+//
+// Copyright JC-Soft 2022
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_05a_CTCommandParCalc.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModelCT
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/.info b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/.info
new file mode 100644
index 000000000..e6605be2f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/.info
@@ -0,0 +1 @@
+Folder for Views
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/CommandParCalculatorView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/CommandParCalculatorView.xaml
new file mode 100644
index 000000000..09e3296d6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/CommandParCalculatorView.xaml
@@ -0,0 +1,365 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/CommandParCalculatorView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/CommandParCalculatorView.xaml.cs
new file mode 100644
index 000000000..5d1a6a9f2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/CommandParCalculatorView.xaml.cs
@@ -0,0 +1,20 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace MVVM_05a_CTCommandParCalc.Views;
+
+///
+/// Interaktionslogik fr CommandParCalculatorView.xaml
+///
+public partial class CommandParCalculatorView : Page
+{
+ public CommandParCalculatorView()
+ {
+ InitializeComponent();
+ }
+
+ private void Button_Click(object? sender, RoutedEventArgs e)
+ {
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/Page1.xaml b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/Page1.xaml
new file mode 100644
index 000000000..534bac16d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/Page1.xaml
@@ -0,0 +1,260 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/Page1.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/Page1.xaml.cs
new file mode 100644
index 000000000..ddbc30773
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/Page1.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace MVVM_05a_CTCommandParCalc.Views;
+
+///
+/// Interaktionslogik fr Page1.xaml
+///
+public partial class Page1 : Page
+{
+ public Page1()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/ValueConverter/DoubleValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/ValueConverter/DoubleValueConverter.cs
new file mode 100644
index 000000000..7155bf083
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalc/Views/ValueConverter/DoubleValueConverter.cs
@@ -0,0 +1,75 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_05a_CTCommandParCalc.Views.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class DoubleValueConverter : IValueConverter
+{
+ public double FixedFactor { get; set; } = 1.0d;
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is double dval && parameter is string spar)
+ return (dval*FixedFactor).ToString(spar,culture);
+ else
+ return value?.ToString() ?? "";
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string sval && parameter is string spar)
+ {
+ if (double.TryParse(sval, NumberStyles.Float, culture, out double dval))
+ return dval / FixedFactor;
+ else
+ if (spar.Contains("{"))
+ return double.NaN; // Todo:
+ else
+ {
+ var pp = spar.LastIndexOf('0');
+ if (double.TryParse(sval.Replace(spar.Substring(pp + 1), "").Trim(),NumberStyles.Float,culture, out var dVal))
+ return dVal / FixedFactor;
+ else
+ return double.NaN;
+ }
+
+ }
+ else
+ return 0d;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/MVVM_05a_CTCommandParCalcTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/MVVM_05a_CTCommandParCalcTests.csproj
index 849092317..59211048f 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/MVVM_05a_CTCommandParCalcTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/MVVM_05a_CTCommandParCalcTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/MVVM_05a_CTCommandParCalc_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/MVVM_05a_CTCommandParCalc_netTests.csproj
index 392097cd4..677ee716c 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/MVVM_05a_CTCommandParCalc_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/MVVM_05a_CTCommandParCalc_netTests.csproj
@@ -1,14 +1,20 @@
- net6.0-windows;net7.0-windows;net8.0-windows
+ net8.0-windows
false
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Model/CalculatorModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Model/CalculatorModelTests.cs
new file mode 100644
index 000000000..75efd88f1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Model/CalculatorModelTests.cs
@@ -0,0 +1,380 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using BaseLib.Helper;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM_05a_CTCommandParCalc.Data;
+using MVVM_05a_CTCommandParCalc.Model.Interfaces;
+using System;
+using System.ComponentModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Model.Tests;
+
+///
+/// Defines test class CalculatorModelTests.
+///
+///
+[TestClass]
+public class CalculatorModelTests
+{
+ ///
+ /// The test model
+ ///
+ ///
+ CalculatorModel testModel;
+
+ ///
+ /// The debug out
+ ///
+ ///
+ public string DebugOut = "";
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnPropertyChanged;
+ DebugOut = "";
+ }
+ ///
+ /// Cleanups this instance.
+ ///
+ ///
+ [TestCleanup]
+ public void Cleanup()
+ {
+ // Nothing to do
+ }
+
+ ///
+ /// Defines the test method SetUpTest.
+ ///
+ ///
+ [TestMethod]
+ public void SetUpTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(CalculatorModel));
+ Assert.IsInstanceOfType(testModel, typeof(ObservableObject));
+ Assert.IsInstanceOfType(testModel, typeof(ICalculatorModel));
+ Assert.IsInstanceOfType(CalculatorModel.Instance, typeof(CalculatorModel));
+ Assert.AreEqual(0d, testModel.Accumulator);
+ Assert.IsNull(testModel.Register);
+ Assert.IsNull(testModel.Memory);
+ Assert.IsFalse(testModel.DecMode);
+ Assert.IsFalse(testModel.canOperator(EOperations.CalcResult));
+ Assert.IsFalse(testModel.canOperator(EOperations.Add));
+ Assert.IsTrue(testModel.canCommand(ECommands.DecMode));
+ Assert.IsTrue(testModel.canCommand(ECommands.MR));
+ Assert.AreEqual(ETrigMode.Grad, testModel.TrigMode);
+ Assert.AreEqual(ECalcError.None, testModel.CalcError);
+ Assert.AreEqual(0, testModel.StackSize);
+ Assert.AreEqual("", DebugOut);
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ ///
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ DoLog($"PropChg({sender},{e.PropertyName})={sender.GetProp(e.PropertyName)}");
+ }
+
+ ///
+ /// Does the log.
+ ///
+ /// The v.
+ ///
+ private void DoLog(string v)
+ {
+ DebugOut += $"{v}{Environment.NewLine}";
+ }
+
+ ///
+ /// Numbers the command test.
+ ///
+ /// The name.
+ /// The ae value.
+ /// The d exp.
+ /// As exp.
+ ///
+ [TestMethod]
+ [DataRow("0", new ENumbers[] { ENumbers._0 }, 0d, new string[] { "" })]
+ [DataRow("123", new ENumbers[] { ENumbers._1, ENumbers._2, ENumbers._3 }, 123d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=12
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=123
+" })]
+ [DataRow("987", new ENumbers[] { ENumbers._9, ENumbers._8, ENumbers._7 }, 987d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=9
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=98
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=987
+" })]
+ public void NumberCmdTest(string name, ENumbers[] aeVal, double dExp, string[] asExp)
+ {
+ // Arrange
+
+ // Act
+ foreach (var ae in aeVal)
+ testModel.NumberCmd(ae);
+
+ // Assert
+ Assert.AreEqual(dExp, testModel.Accumulator);
+ Assert.IsTrue(testModel.canOperator(EOperations.Add));
+ Assert.AreEqual(asExp[0], DebugOut);
+ }
+
+ ///
+ /// Numbers the CMD2 test.
+ ///
+ /// The name.
+ /// The ae value.
+ /// The d exp.
+ /// As exp.
+ ///
+ [TestMethod]
+ [DataRow("0", new ENumbers[] { ENumbers._0 }, 0d, new string[] { "" })]
+#if NET5_0_OR_GREATER
+ [DataRow("123", new ENumbers[] { ENumbers._1, ENumbers._2, ENumbers._3 }, 0.123d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,1
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,12000000000000001
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,12300000000000001
+" })]
+#else
+ [DataRow("123", new ENumbers[] { ENumbers._1, ENumbers._2, ENumbers._3 }, 0.123d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,1
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,12
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,123
+" })]
+#endif
+ [DataRow("987", new ENumbers[] { ENumbers._9, ENumbers._8, ENumbers._7 }, 0.987d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,9
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,98
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,987
+" })]
+ public void NumberCmd2Test(string name, ENumbers[] aeVal, double dExp, string[] asExp)
+ {
+ // Arrange
+ testModel.NumberCmd(ENumbers._0);
+ testModel.DecMode = true;
+ // Act
+ foreach (var ae in aeVal)
+ testModel.NumberCmd(ae);
+
+ // Assert
+ Assert.AreEqual(dExp, testModel.Accumulator, 1e-10);
+ Assert.AreEqual(asExp[0], DebugOut);
+ }
+
+ ///
+ /// Operators the command test.
+ ///
+ /// The e op.
+ /// The d val1.
+ /// The d val2.
+ /// The d exp.
+ /// As exp.
+ ///
+ [TestMethod]
+ [DataRow(EOperations.Add, 1d, 2d, 3d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=1
+", @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=
+" })]
+ [DataRow(EOperations.Subtract, 3d, 2d, 1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=3
+", @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=
+" })]
+ [DataRow(EOperations.Multiply, 2d, 3d, 6d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=2
+", @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=6
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=
+" })]
+ [DataRow(EOperations.Divide, 6d, 3d, 2d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=6
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=6
+", @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=
+" })]
+ [DataRow(EOperations.Power, 2d, 3d, 8d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=2
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=2
+", @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=8
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Register)=
+" })]
+ public void OperatorCmdTest(EOperations eOp, double dVal1, double dVal2, double dExp, string[] asExp)
+ {
+ // Arrange
+ testModel.Accumulator = dVal1;
+
+ // Act
+ testModel.OperatorCmd(eOp);
+
+ // Assert
+ Assert.AreEqual(dVal1, testModel.Accumulator);
+ Assert.AreEqual(dVal1, testModel.Register);
+ Assert.AreEqual(asExp[0], DebugOut);
+ DebugOut = "";
+
+ // Arrange
+ testModel.Accumulator = dVal2;
+ // Act2
+ testModel.OperatorCmd(EOperations.CalcResult);
+ // Assert2
+ Assert.AreEqual(dExp, testModel.Accumulator);
+ Assert.IsNull(testModel.Register);
+ Assert.AreEqual(asExp[1], DebugOut);
+ }
+
+ ///
+ /// Operators the CMD2 test.
+ ///
+ /// The e op.
+ /// The d val1.
+ /// The .
+ /// The d exp.
+ /// As exp.
+ ///
+ [TestMethod]
+ [DataRow(EOperations.Negate, 1d, 2d, -1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=-1
+", @"" })]
+ [DataRow(EOperations.Square, 3d, 2d, 9d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=3
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=9
+", @"" })]
+ [DataRow(EOperations.SquareRt, 4d, 3d, 2d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=4
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=2
+", @"" })]
+ [DataRow(EOperations.Inverse, 4d, 3d, 0.25d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=4
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,25
+", @"" })]
+#if NET5_0_OR_GREATER
+ [DataRow(EOperations.Sin, Math.PI * 0.5d, 3d, 1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1,5707963267948966
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+", @"" })]
+ [DataRow(EOperations.Cos, Math.PI, 3d, -1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=3,141592653589793
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=-1
+", @"" })]
+ [DataRow(EOperations.Tan, Math.PI * 0.25, 0d, 1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,7853981633974483
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,9999999999999999
+", @"" })]
+#else
+ [DataRow(EOperations.Sin, Math.PI * 0.5d, 3d, 1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1,5707963267949
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+", @"" })]
+ [DataRow(EOperations.Cos, Math.PI, 3d, -1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=3,14159265358979
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=-1
+", @"" })]
+ [DataRow(EOperations.Tan, Math.PI * 0.25, 0d, 1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0,785398163397448
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+", @"" })]
+#endif
+ [DataRow(EOperations.ExpN, 0d, 3d, 1d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+", @"" })]
+ [DataRow(EOperations.LogN, 1d, 0d, 0d, new string[] { @"PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=1
+PropChg(MVVM_05a_CTCommandParCalc.Model.CalculatorModel,Accumulator)=0
+", @"" })]
+ public void OperatorCmd2Test(EOperations eOp, double dVal1, double _, double dExp, string[] asExp)
+ {
+ // Arrange
+ testModel.Accumulator = dVal1;
+
+ // Act
+ testModel.OperatorCmd(eOp);
+
+ // Assert
+ Assert.AreEqual(dExp, testModel.Accumulator, 1e-10);
+ Assert.IsNull(testModel.Register);
+ Assert.AreEqual(asExp[0], DebugOut);
+
+ }
+
+ ///
+ /// Calculates the command test.
+ ///
+ /// The e command.
+ /// The d val1.
+ /// The d a exp.
+ /// The d m exp.
+ ///
+ [TestMethod]
+ [DataRow(ECommands.Nop, 123d, 123d, 123d)]
+ [DataRow(ECommands.Clear, 123d, 0d, 123d)]
+ [DataRow(ECommands.ClearAll, 123d, 0d, 123d)]
+ [DataRow(ECommands.e, 0d, Math.E, 0d)]
+ [DataRow(ECommands.Pi, 0d, Math.PI, 0d)]
+ [DataRow(ECommands.MS, 123d, 123d, 123d)]
+ [DataRow(ECommands.MR, 123d, 123d, 123d)]
+ [DataRow(ECommands.MC, 123d, 123d, null)]
+ [DataRow(ECommands.Mp, 123d, 123d, 246d)]
+ [DataRow(ECommands.Mm, 123d, 123d, 0d)]
+ public void CalcCmdTest(ECommands eCmd, double dVal1,
+ double dAExp,
+ double? dMExp)
+ {
+ // Arrange
+ testModel.Accumulator = dVal1;
+ testModel.Memory = dVal1;
+
+ // Act
+ testModel.CalcCmd(eCmd);
+
+ // Assert
+ Assert.AreEqual(dAExp, testModel.Accumulator);
+ Assert.AreEqual(dMExp, testModel.Memory);
+ }
+
+ ///
+ /// Defines the test method CalcCmd2Test.
+ ///
+ ///
+ [TestMethod]
+ public void CalcCmd2Test()
+ {
+ testModel.CalcCmd(ECommands.DecMode);
+ Assert.IsTrue(testModel.DecMode);
+ }
+
+ ///
+ /// Defines the test method canOperatorTest.
+ ///
+ /// The e value.
+ /// The e pre.
+ /// The d akk.
+ /// if set to true [x result].
+ ///
+ [TestMethod()]
+ [DataRow((EOperations)(-1), EOperations.Nop, 0d, false)]
+ [DataRow(EOperations.Nop,EOperations.Add,3d,true)]
+ [DataRow(EOperations.CalcResult, EOperations.Add, 3d, true)]
+ [DataRow(EOperations.Subtract, EOperations.Add, 3d, true)]
+ public void canOperatorTest(EOperations eVal, EOperations ePre,double dAkk,bool xResult)
+ {
+ // Arrange
+ testModel.Accumulator = dAkk;
+ testModel.OperatorCmd(ePre);
+ // Act & Assert
+ Assert.AreEqual(xResult,testModel.canOperator(eVal));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Model/TestCalcModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Model/TestCalcModel.cs
new file mode 100644
index 000000000..6f9b31e13
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Model/TestCalcModel.cs
@@ -0,0 +1,187 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using MVVM_05a_CTCommandParCalc.Data;
+using MVVM_05a_CTCommandParCalc.Model.Interfaces;
+using System;
+using System.Collections.Generic;
+
+///
+/// The Model namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Model;
+
+///
+/// Class TestCalcModel.
+/// Implements the
+/// Implements the
+///
+///
+///
+///
+internal class TestCalcModel : NotificationObject, ICalculatorModel
+{
+ ///
+ /// The accumulator
+ ///
+ ///
+ private double _accumulator = 0d;
+ ///
+ /// The register
+ ///
+ ///
+ private double? _register;
+ ///
+ /// The memory
+ ///
+ ///
+ private double? _memory;
+ ///
+ /// The trig mode
+ ///
+ ///
+ private ETrigMode _trigMode;
+ ///
+ /// The calculate error
+ ///
+ ///
+ private ECalcError _calcError;
+ ///
+ /// The stack size
+ ///
+ ///
+ private int _stackSize;
+ ///
+ /// The do log
+ ///
+ ///
+ private Action _doLog;
+
+ ///
+ /// Gets or sets the accumulator.
+ ///
+ /// The accumulator.
+ ///
+ public double Accumulator { get => _accumulator; set => SetProperty(ref _accumulator, value); }
+ ///
+ /// Gets or sets the register.
+ ///
+ /// The register.
+ ///
+ public double? Register { get => _register; set => SetProperty(ref _register, value); }
+ ///
+ /// Gets or sets the memory.
+ ///
+ /// The memory.
+ ///
+ public double? Memory { get => _memory; set => SetProperty(ref _memory, value); }
+ ///
+ /// Gets the dependencies.
+ ///
+ /// The dependencies.
+ ///
+ public IEnumerable<(string Dest, string Src)> Dependencies => new[]{
+ (nameof(canOperator),nameof(Accumulator)),
+ (nameof(canOperator),nameof(Register)),
+ };
+ ///
+ /// Gets or sets the trig mode.
+ ///
+ /// The trig mode.
+ ///
+ public ETrigMode TrigMode { get => _trigMode; set => SetProperty(ref _trigMode, value); }
+ ///
+ /// Gets or sets the calculate error.
+ ///
+ /// The calculate error.
+ ///
+ public ECalcError CalcError { get => _calcError; set => SetProperty(ref _calcError, value); }
+ ///
+ /// Gets or sets the size of the stack.
+ ///
+ /// The size of the stack.
+ ///
+ public int StackSize { get => _stackSize; set => SetProperty(ref _stackSize, value); }
+ ///
+ /// Gets or sets a value indicating whether [x result].
+ ///
+ /// true if [x result]; otherwise, false.
+ ///
+ public bool xResult { get; set; } = false;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The do log.
+ ///
+ public TestCalcModel(Action DoLog)
+ {
+ _doLog = DoLog;
+ }
+
+ ///
+ /// Calculates the command.
+ ///
+ /// The o.
+ ///
+ public void CalcCmd(ECommands o)
+ {
+ _doLog($"CalcCmd({o})");
+ }
+
+ ///
+ /// Determines whether this instance can command the specified e c.
+ ///
+ /// The e c.
+ /// true if this instance can command the specified e c; otherwise, false.
+ ///
+ public bool canCommand(ECommands eC)
+ {
+ _doLog($"canCommand({eC})={xResult}");
+ return xResult;
+ }
+
+ ///
+ /// Determines whether this instance can operator the specified e o.
+ ///
+ /// The e o.
+ /// true if this instance can operator the specified e o; otherwise, false.
+ ///
+ public bool canOperator(EOperations eO)
+ {
+ _doLog($"canOperator({eO}={xResult})");
+ return xResult;
+ }
+
+ ///
+ /// Numbers the command.
+ ///
+ /// The o.
+ ///
+ public void NumberCmd(ENumbers o)
+ {
+ _doLog($"NumberCmd({o})");
+ }
+
+ ///
+ /// Operators the command.
+ ///
+ /// The e o.
+ ///
+ public void OperatorCmd(EOperations eO)
+ {
+ _doLog($"OperatorCmd({eO})");
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..417344d13
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Properties/SettingsTests.cs
@@ -0,0 +1,33 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+namespace MVVM_05a_CTCommandParCalc.Properties.Tests;
+
+[TestClass()]
+public class SettingsTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/ViewModels/CommandParCalculatorViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/ViewModels/CommandParCalculatorViewModelTests.cs
new file mode 100644
index 000000000..cf99243bc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/ViewModels/CommandParCalculatorViewModelTests.cs
@@ -0,0 +1,302 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using BaseLib.Helper;
+using MVVM.ViewModel;
+using MVVM_05a_CTCommandParCalc.Data;
+using MVVM_05a_CTCommandParCalc.Model;
+using MVVM_05a_CTCommandParCalc.Model.Interfaces;
+using System;
+using System.Globalization;
+using static BaseLib.Helper.TestHelper;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.ViewModels.Tests;
+
+///
+/// Defines test class CommandParCalculatorViewModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class CommandParCalculatorViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ CommandParCalculatorViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// The old model
+ ///
+ ///
+ private Func _oldModel;
+ ///
+ /// The test calculate model
+ ///
+ ///
+ private TestCalcModel testCalcModel;
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ IoC.GetReqSrv = (t) => t switch {
+ _ when t == typeof(ICalculatorModel) => testCalcModel,
+ _ => throw new ArgumentException($"No service for {t}")};
+ testCalcModel = new TestCalcModel(DoLog);
+
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ Assert.IsFalse(testModel.canOperator(EOperations.CalcResult));
+ Assert.IsFalse(testModel.canOperator(EOperations.Add));
+ Assert.IsFalse(testModel.canCommand(ECommands.DecMode));
+ Assert.IsFalse(testModel.canCommand(ECommands.MR));
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method CommandParCalculatorViewModelTest.
+ ///
+ ///
+ [TestMethod]
+ public void CommandParCalculatorViewModelTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(CommandParCalculatorViewModel));
+ Assert.AreEqual(0d, testModel.Accumulator);
+ Assert.AreEqual(double.NaN, testModel.Register);
+ Assert.AreEqual(double.NaN, testModel.Memory);
+ Assert.AreEqual("None Grad ", testModel.Status);
+ Assert.IsFalse(testModel.canOperator(EOperations.CalcResult));
+ Assert.IsFalse(testModel.canOperator(EOperations.Add));
+ Assert.IsFalse(testModel.canCommand(ECommands.DecMode));
+ Assert.IsFalse(testModel.canCommand(ECommands.MR));
+ Assert.IsNotNull( testModel.NumberCommand);
+ Assert.IsNotNull( testModel.OperatorCommand);
+ Assert.IsNotNull( testModel.CalculatorCommand);
+ Assert.AreEqual("canOperator(CalcResult=False)\r\ncanOperator(Add=False)\r\ncanCommand(DecMode)=False\r\ncanCommand(MR)=False\r\n", DebugLog);
+ }
+
+ ///
+ /// Accumulators the test.
+ ///
+ /// The name.
+ /// The ad value.
+ /// As exp.
+ ///
+ [TestMethod()]
+ [DataRow("0",new double[] {0d }, new string[] { "0", "" })]
+ [DataRow("25", new double[] { 25d }, new string[] { "25", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=25
+" })]
+#if NET5_0_OR_GREATER
+ [DataRow("PI,E", new double[] { Math.PI,Math.E }, new string[] { "2.718281828459045", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=3,141592653589793
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=2,718281828459045
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN,double.PositiveInfinity,double.Epsilon }, new string[] { "5E-324", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=-1,7976931348623157E+308
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=NaN
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=∞
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=5E-324
+" })]
+#else
+ [DataRow("PI,E", new double[] { Math.PI,Math.E }, new string[] { "2.71828182845905", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=3,14159265358979
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=2,71828182845905
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN,double.PositiveInfinity,double.Epsilon }, new string[] { "4.94065645841247E-324", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=-1,79769313486232E+308
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=NaN
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=∞
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Accumulator)=4,94065645841247E-324
+" })]
+#endif
+ public void AccumulatorTest(string name, double[] adVal, string[] asExp)
+ {
+ // Arrange
+ // Act
+ foreach (var item in adVal)
+ testCalcModel.Accumulator = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Accumulator.ToString(CultureInfo.InvariantCulture), "Accumulator");
+ AssertAreEqual(asExp[1], DebugLog, "DebugOut");
+ }
+
+ ///
+ /// Registers the test.
+ ///
+ /// The name.
+ /// The ad value.
+ /// As exp.
+ ///
+ [TestMethod()]
+ [DataRow("0", new double[] { 0d }, new string[] { "0", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=0
+" })]
+ [DataRow("25", new double[] { 25d }, new string[] { "25", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=25
+" })]
+#if NET5_0_OR_GREATER
+ [DataRow("PI,E", new double[] { Math.PI, Math.E }, new string[] { "2.718281828459045", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=3,141592653589793
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=2,718281828459045
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN, double.PositiveInfinity, double.Epsilon }, new string[] { "5E-324", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=-1,7976931348623157E+308
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=NaN
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=∞
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=5E-324
+" })]
+#else
+ [DataRow("PI,E", new double[] { Math.PI, Math.E }, new string[] { "2.71828182845905", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=3,14159265358979
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=2,71828182845905
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN, double.PositiveInfinity, double.Epsilon }, new string[] { "4.94065645841247E-324", @"canOperator(CalcResult=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,canOperator)=
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=-1,79769313486232E+308
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=NaN
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=∞
+canOperator(CalcResult=False)
+canOperator(Add=False)
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Register)=4,94065645841247E-324
+" })]
+#endif
+ public void RegisterTest(string name, double[] adVal, string[] asExp)
+ {
+ // Arrange
+ // Act
+ foreach (var item in adVal)
+ testCalcModel.Register = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Register.ToString(CultureInfo.InvariantCulture), "Accumulator");
+ AssertAreEqual(asExp[1], DebugLog, "DebugOut");
+ }
+
+ ///
+ /// Memories the test.
+ ///
+ /// The name.
+ /// The ad value.
+ /// As exp.
+ ///
+ [TestMethod()]
+ [DataRow("0", new double[] { 0d }, new string[] { "0", "PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=0\r\n" })]
+ [DataRow("25", new double[] { 25d }, new string[] { "25", @"PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=25
+" })]
+#if NET5_0_OR_GREATER
+ [DataRow("PI,E", new double[] { Math.PI, Math.E }, new string[] { "2.718281828459045", @"PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=3,141592653589793
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=2,718281828459045
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN, double.PositiveInfinity, double.Epsilon }, new string[] { "5E-324", @"PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=-1,7976931348623157E+308
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=NaN
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=∞
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=5E-324
+" })]
+#else
+ [DataRow("PI,E", new double[] { Math.PI, Math.E }, new string[] { "2.71828182845905", @"PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=3,14159265358979
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=2,71828182845905
+" })]
+ [DataRow("Min,NaN,+Inf", new double[] { double.MinValue, double.NaN, double.PositiveInfinity, double.Epsilon }, new string[] { "4.94065645841247E-324", @"PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=-1,79769313486232E+308
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=NaN
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=∞
+PropChg(MVVM_05a_CTCommandParCalc.ViewModels.CommandParCalculatorViewModel,Memory)=4,94065645841247E-324
+" })]
+#endif
+ public void MemoryTest(string name, double[] adVal, string[] asExp)
+ {
+ // Arrange
+ // Act
+ foreach (var item in adVal)
+ testCalcModel.Memory = item;
+ // Assert
+ Assert.AreEqual(asExp[0], testModel.Memory.ToString(CultureInfo.InvariantCulture), "Accumulator");
+ AssertAreEqual(asExp[1], DebugLog, "DebugOut");
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..28aaa509d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,65 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-19-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.ViewModels.Tests;
+
+///
+/// Defines test class MainWindowViewModelTests.
+/// Implements the
+///
+///
+///
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/CommandParCalculatorViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/CommandParCalculatorViewTests.cs
new file mode 100644
index 000000000..b22ce201e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/CommandParCalculatorViewTests.cs
@@ -0,0 +1,70 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-02-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Views.Tests;
+
+///
+/// Defines test class CommandParCalculatorViewTests.
+///
+///
+[TestClass()]
+public class CommandParCalculatorViewTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ CommandParCalculatorView testView;
+ ///
+ /// The vm
+ ///
+ ///
+ private object vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize()]
+ public void Init()
+ {
+ Thread thread = new(() =>
+ {
+ testView = new();
+ vm = testView.DataContext;
+ });
+ thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ thread.Start();
+ thread.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method ValidationPageTest.
+ ///
+ ///
+ [TestMethod()]
+ public void ValidationPageTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsNotNull(vm);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..1b2f7b180
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/MainWindowTests.cs
@@ -0,0 +1,45 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-02-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+///
+[TestClass()]
+public class MainWindowTests
+{
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/Page1Tests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/Page1Tests.cs
new file mode 100644
index 000000000..390142a4b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/Page1Tests.cs
@@ -0,0 +1,66 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Views.Tests;
+
+///
+/// Defines test class Page1Tests.
+///
+///
+[TestClass()]
+public class Page1Tests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ Page1 testView;
+ // private object vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwgen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize()]
+ public void Init()
+ {
+ Thread thread = new(() =>
+ {
+ testView = new();
+// vm = testView.DataContext;
+ });
+ thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ thread.Start();
+ thread.Join(); //Wait for the thread to end
+ }
+
+ ///
+ /// Defines the test method ValidationPageTest.
+ ///
+ ///
+ [TestMethod()]
+ public void ValidationPageTest()
+ {
+ Assert.IsNotNull(testView);
+// Assert.IsNotNull(vm);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/ValueConverter/DoubleValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/ValueConverter/DoubleValueConverterTests.cs
new file mode 100644
index 000000000..1e953cf1b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_05a_CTCommandParCalcTests/Views/ValueConverter/DoubleValueConverterTests.cs
@@ -0,0 +1,83 @@
+// ***********************************************************************
+// Assembly : MVVM_05a_CTCommandParCalc_netTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-08-2023
+// ***********************************************************************
+//
+// Copyright JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_05a_CTCommandParCalc.Views.ValueConverter.Tests;
+
+
+///
+/// Defines test class DoubleValueConverterTests.
+///
+///
+[TestClass]
+public class DoubleValueConverterTests
+{
+ ///
+ /// Converts the test.
+ ///
+ /// The value.
+ /// The parameter.
+ /// The fixed factor.
+ /// The expected result.
+ ///
+ [TestMethod]
+ [DataRow(123.45d, "F2", 2d, "246.90")]
+ [DataRow(123.45d, "C2", 2d, "246.90")]
+ [DataRow("Hallo", "C2", 2d, "Hallo")]
+ [DataRow(null, "C2", 2d, "")]
+ public void ConvertTest(object value, string parameter, double fixedFactor, string expectedResult)
+ {
+ // Arrange
+ var converter = new DoubleValueConverter();
+ converter.FixedFactor = fixedFactor;
+
+ // Act
+ var result = converter.Convert(value, typeof(string), parameter, CultureInfo.InvariantCulture);
+
+ // Assert
+ Assert.AreEqual(expectedResult, result);
+ }
+
+ ///
+ /// Converts the back test.
+ ///
+ /// The value.
+ /// The parameter.
+ /// The fixed factor.
+ /// The expected result.
+ ///
+ [TestMethod]
+ [DataRow("246.90", "F2", 2d, 123.45d)]
+ [DataRow("246.90-", "{0}-", 2d, double.NaN)]
+ [DataRow("246.90 ", "0.00 ", 2d, 123.45d)]
+ [DataRow("246.X ", "0.00 ", 2d, double.NaN)]
+ [DataRow(null, "0.00 ", 2d, 0d)]
+ public void ConvertBackTest(string value, string parameter, double fixedFactor, double expectedResult)
+ {
+ // Arrange
+ var converter = new DoubleValueConverter();
+ converter.FixedFactor = fixedFactor;
+
+ // Act
+ var result = converter.ConvertBack(value, typeof(double), parameter, CultureInfo.InvariantCulture);
+
+ // Assert
+ Assert.AreEqual(expectedResult, result);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converter.sln b/CSharpBible/MVVM_Tutorial/MVVM_06_Converter.sln
new file mode 100644
index 000000000..1e19629e6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converter.sln
@@ -0,0 +1,153 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projektmappenelemente", "Projektmappenelemente", "{658BD492-33FF-4995-AAE7-4F6894E92F0C}"
+ ProjectSection(SolutionItems) = preProject
+ MVVM_Tutorial.props = MVVM_Tutorial.props
+ MVVM_Tutorial_net.props = MVVM_Tutorial_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLib", "..\Libraries\MVVM_BaseLib\MVVM_BaseLib.csproj", "{890CF504-3814-443B-9EE6-E8BCACF68203}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{AF041458-CF94-4D6D-BB0F-8BC50F4F099C}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Libraries\Libraries_net.props = ..\Libraries\Libraries_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLibTests", "..\Libraries\MVVM_BaseLibTests\MVVM_BaseLibTests.csproj", "{66EB60F2-523D-47C8-95EA-C7E10759E239}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_4", "MVVM_06_Converters_4\MVVM_06_Converters_4.csproj", "{8708C25C-CC05-4462-A084-2A153105176C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_4_net", "MVVM_06_Converters_4\MVVM_06_Converters_4_net.csproj", "{BE62D2F8-A91D-45B2-9EC6-76EEA30EDECD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_net", "MVVM_06_Converters\MVVM_06_Converters_net.csproj", "{2F1934A4-8649-46B9-8D98-A091475D2B20}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_2_net", "MVVM_06_Converters_2\MVVM_06_Converters_2_net.csproj", "{A1126275-F1F5-4AFD-80F9-5294E5F705E1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters", "MVVM_06_Converters\MVVM_06_Converters.csproj", "{41E09DE2-0BA3-4EF4-B2A3-643A5308278B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_2", "MVVM_06_Converters_2\MVVM_06_Converters_2.csproj", "{98C1BD14-E946-4D55-BB98-32711557474D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_3_net", "MVVM_06_Converters_3\MVVM_06_Converters_3_net.csproj", "{B8EF677E-C365-40CB-BD34-0C358C700FE7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_3", "MVVM_06_Converters_3\MVVM_06_Converters_3.csproj", "{E4A33646-7999-46B6-BD7C-33E1F877B9F2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathLibrary", "..\Libraries\MathLIbrary\MathLibrary.csproj", "{9D009B76-E498-4569-8B83-C7B6E5C425C8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathLibrary_netTests", "..\Libraries\MathLibraryTests\MathLibrary_netTests.csproj", "{878CD114-8446-4E3C-918B-F58FD1D63EF7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLib_net", "..\Libraries\MVVM_BaseLib\MVVM_BaseLib_net.csproj", "{42BEB9FE-728F-47BF-B114-0E0C21516788}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathLibrary_net", "..\Libraries\MathLIbrary\MathLibrary_net.csproj", "{90EB0B25-1E6A-43B5-BA03-063589B65C25}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathLibraryTests", "..\Libraries\MathLibraryTests\MathLibraryTests.csproj", "{09511EA2-7ED4-4EE4-ABCD-EA1921EEC63F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_4Tests", "MVVM_06_Converters_4Tests\MVVM_06_Converters_4Tests.csproj", "{7D236474-FEFC-4BCA-80CE-2ABC7E841D20}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_4_netTests", "MVVM_06_Converters_4Tests\MVVM_06_Converters_4_netTests.csproj", "{81954E5E-E6B7-4C6D-B500-6E47779E74AC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_3Tests", "MVVM_06_Converters_3Tests\MVVM_06_Converters_3Tests.csproj", "{F1E883C0-E1EB-48F4-80E1-17376C9E4E81}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_06_Converters_3_netTests", "MVVM_06_Converters_3Tests\MVVM_06_Converters_3_netTests.csproj", "{7E18CEBC-FC92-48D3-8929-B6CFD85FC847}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.Build.0 = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8708C25C-CC05-4462-A084-2A153105176C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8708C25C-CC05-4462-A084-2A153105176C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8708C25C-CC05-4462-A084-2A153105176C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8708C25C-CC05-4462-A084-2A153105176C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BE62D2F8-A91D-45B2-9EC6-76EEA30EDECD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE62D2F8-A91D-45B2-9EC6-76EEA30EDECD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BE62D2F8-A91D-45B2-9EC6-76EEA30EDECD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BE62D2F8-A91D-45B2-9EC6-76EEA30EDECD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F1934A4-8649-46B9-8D98-A091475D2B20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F1934A4-8649-46B9-8D98-A091475D2B20}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F1934A4-8649-46B9-8D98-A091475D2B20}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F1934A4-8649-46B9-8D98-A091475D2B20}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A1126275-F1F5-4AFD-80F9-5294E5F705E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1126275-F1F5-4AFD-80F9-5294E5F705E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1126275-F1F5-4AFD-80F9-5294E5F705E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1126275-F1F5-4AFD-80F9-5294E5F705E1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {41E09DE2-0BA3-4EF4-B2A3-643A5308278B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {41E09DE2-0BA3-4EF4-B2A3-643A5308278B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {41E09DE2-0BA3-4EF4-B2A3-643A5308278B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {41E09DE2-0BA3-4EF4-B2A3-643A5308278B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {98C1BD14-E946-4D55-BB98-32711557474D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {98C1BD14-E946-4D55-BB98-32711557474D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {98C1BD14-E946-4D55-BB98-32711557474D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {98C1BD14-E946-4D55-BB98-32711557474D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B8EF677E-C365-40CB-BD34-0C358C700FE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B8EF677E-C365-40CB-BD34-0C358C700FE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B8EF677E-C365-40CB-BD34-0C358C700FE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8EF677E-C365-40CB-BD34-0C358C700FE7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E4A33646-7999-46B6-BD7C-33E1F877B9F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E4A33646-7999-46B6-BD7C-33E1F877B9F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E4A33646-7999-46B6-BD7C-33E1F877B9F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E4A33646-7999-46B6-BD7C-33E1F877B9F2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9D009B76-E498-4569-8B83-C7B6E5C425C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9D009B76-E498-4569-8B83-C7B6E5C425C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9D009B76-E498-4569-8B83-C7B6E5C425C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9D009B76-E498-4569-8B83-C7B6E5C425C8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {878CD114-8446-4E3C-918B-F58FD1D63EF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {878CD114-8446-4E3C-918B-F58FD1D63EF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {878CD114-8446-4E3C-918B-F58FD1D63EF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {878CD114-8446-4E3C-918B-F58FD1D63EF7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {42BEB9FE-728F-47BF-B114-0E0C21516788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {42BEB9FE-728F-47BF-B114-0E0C21516788}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {42BEB9FE-728F-47BF-B114-0E0C21516788}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {42BEB9FE-728F-47BF-B114-0E0C21516788}.Release|Any CPU.Build.0 = Release|Any CPU
+ {90EB0B25-1E6A-43B5-BA03-063589B65C25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {90EB0B25-1E6A-43B5-BA03-063589B65C25}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {90EB0B25-1E6A-43B5-BA03-063589B65C25}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {90EB0B25-1E6A-43B5-BA03-063589B65C25}.Release|Any CPU.Build.0 = Release|Any CPU
+ {09511EA2-7ED4-4EE4-ABCD-EA1921EEC63F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {09511EA2-7ED4-4EE4-ABCD-EA1921EEC63F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {09511EA2-7ED4-4EE4-ABCD-EA1921EEC63F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {09511EA2-7ED4-4EE4-ABCD-EA1921EEC63F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7D236474-FEFC-4BCA-80CE-2ABC7E841D20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7D236474-FEFC-4BCA-80CE-2ABC7E841D20}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7D236474-FEFC-4BCA-80CE-2ABC7E841D20}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D236474-FEFC-4BCA-80CE-2ABC7E841D20}.Release|Any CPU.Build.0 = Release|Any CPU
+ {81954E5E-E6B7-4C6D-B500-6E47779E74AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {81954E5E-E6B7-4C6D-B500-6E47779E74AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {81954E5E-E6B7-4C6D-B500-6E47779E74AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {81954E5E-E6B7-4C6D-B500-6E47779E74AC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F1E883C0-E1EB-48F4-80E1-17376C9E4E81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F1E883C0-E1EB-48F4-80E1-17376C9E4E81}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F1E883C0-E1EB-48F4-80E1-17376C9E4E81}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F1E883C0-E1EB-48F4-80E1-17376C9E4E81}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7E18CEBC-FC92-48D3-8929-B6CFD85FC847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7E18CEBC-FC92-48D3-8929-B6CFD85FC847}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7E18CEBC-FC92-48D3-8929-B6CFD85FC847}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7E18CEBC-FC92-48D3-8929-B6CFD85FC847}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {66EB60F2-523D-47C8-95EA-C7E10759E239} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {9D009B76-E498-4569-8B83-C7B6E5C425C8} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {878CD114-8446-4E3C-918B-F58FD1D63EF7} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {42BEB9FE-728F-47BF-B114-0E0C21516788} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {90EB0B25-1E6A-43B5-BA03-063589B65C25} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {09511EA2-7ED4-4EE4-ABCD-EA1921EEC63F} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {73DECD49-328A-4BEC-A41D-966857352E58}
+ EndGlobalSection
+EndGlobal
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/App.xaml
new file mode 100644
index 000000000..cb5c4f66a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/App.xaml.cs
new file mode 100644
index 000000000..8b6aada01
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters
+// Author : Mir
+// Created : 07-19-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-19-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_06_Converters;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/AssemblyInfo.cs
new file mode 100644
index 000000000..e8057f153
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters
+// Author : Mir
+// Created : 07-01-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-01-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MVVM_06_Converters.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MVVM_06_Converters.csproj
new file mode 100644
index 000000000..a1bc8b6f9
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MVVM_06_Converters.csproj
@@ -0,0 +1,33 @@
+
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Resources.resx
+ True
+ True
+
+
+
+
+ Resources.Designer.cs
+ PublicResXFileCodeGenerator
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MVVM_06_Converters_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MVVM_06_Converters_net.csproj
new file mode 100644
index 000000000..9b0912b0b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MVVM_06_Converters_net.csproj
@@ -0,0 +1,39 @@
+
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MainWindow.xaml
new file mode 100644
index 000000000..b7bd63043
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MainWindow.xaml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MainWindow.xaml.cs
new file mode 100644
index 000000000..41ee41e12
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters
+// Author : Mir
+// Created : 07-01-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_06_Converters;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..60f68d9ed
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/Properties/Resources.Designer.cs
@@ -0,0 +1,138 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_06_Converters.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_06_Converters.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_06_Converters
+ ///// Author : Mir
+ ///// Created : 07-03-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 07-03-2022
+ ///// ***********************************************************************
+ ///// <copyright file="CurrencyValueConverter.cs" company="MVVM_06_Converters">
+ ///// Copyright (c) JC-Soft. All rights reserved.
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ****************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CurrencyValueConverter {
+ get {
+ return ResourceManager.GetString("CurrencyValueConverter", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page x:Class="MVVM_06_Converters.View.CurrencyView"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_06_Converters.View"
+ /// xmlns:mvvm="clr-namespace:MVVM_06_Converters.ViewModels"
+ /// xmlns:vc="clr-namespace:MVVM_06_Converters.Valu [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CurrencyView {
+ get {
+ return ResourceManager.GetString("CurrencyView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_06_Converters
+ ///// Author : Mir
+ ///// Created : 07-01-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 07-01-2022
+ ///// ***********************************************************************
+ ///// <copyright file="CurrencyViewModel.cs" company="MVVM_06_Converters">
+ ///// Copyright (c) JC-Soft. All rights reserved.
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// *********************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CurrencyViewModel {
+ get {
+ return ResourceManager.GetString("CurrencyViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Tutorial #06: Example of a Currency-ValueConverter ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM #06 Converter ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/Properties/Resources.resx
new file mode 100644
index 000000000..1318968cc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/Properties/Resources.resx
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\ValueConverter\CurrencyValueConverter.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\View\CurrencyView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
+
+ ..\ViewModels\CurrencyViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ Tutorial #06: Example of a Currency-ValueConverter
+
+
+ MVVM #06 Converter
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/ValueConverter/CurrencyValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/ValueConverter/CurrencyValueConverter.cs
new file mode 100644
index 000000000..fb7c5e47a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/ValueConverter/CurrencyValueConverter.cs
@@ -0,0 +1,57 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-03-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_06_Converters.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class CurrencyValueConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object? value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is decimal dval)
+ return dval.ToString("0.00€",culture);
+ else
+ return value?.ToString() ?? "";
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/CurrencyView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/CurrencyView.xaml
new file mode 100644
index 000000000..8f79d9530
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/CurrencyView.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/CurrencyView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/CurrencyView.xaml.cs
new file mode 100644
index 000000000..9f10364ae
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/CurrencyView.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters
+// Author : Mir
+// Created : 07-01-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-01-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows.Controls;
+
+namespace MVVM_06_Converters.View;
+
+///
+/// Interaktionslogik für CurrencyView.xaml
+///
+public partial class CurrencyView : Page
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CurrencyView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/Extensions/KeyboardExtensions.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/Extensions/KeyboardExtensions.cs
new file mode 100644
index 000000000..8906499d0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/View/Extensions/KeyboardExtensions.cs
@@ -0,0 +1,71 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters
+// Author : Mir
+// Created : 07-19-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-19-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+using System.Windows.Input;
+
+namespace MVVM_06_Converters.View.Extensions;
+
+///
+/// Class KeyboardExtensions.
+///
+public static class EnterKeyExtensions
+{
+ ///
+ /// The enter key command property
+ ///
+ public static readonly DependencyProperty EnterKeyCommandProperty = DependencyProperty.RegisterAttached(
+ nameof(SetEnterKeyCommand).Replace("Set",""),
+ typeof(ICommand),
+ typeof(EnterKeyExtensions),
+ new PropertyMetadata(
+ (d, e) =>
+ {
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+ if (e.NewValue != null)
+ ((IInputElement)d).KeyDown += OnKeyDown;
+ else
+ ((IInputElement)d).KeyDown -= OnKeyDown;
+ }));
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private static void OnKeyDown(object? sender, KeyEventArgs args)
+ {
+ if (args.Key == Key.Enter)
+ {
+ var command = GetEnterKeyCommand((UIElement)sender);
+
+ if (command?.CanExecute(null) == true)
+ command.Execute(null);
+
+ args.Handled = true;
+ }
+ }
+
+ ///
+ /// Gets the enter key command.
+ ///
+ /// The element.
+ /// ICommand.
+ public static ICommand GetEnterKeyCommand(UIElement element) => (ICommand)element.GetValue(EnterKeyCommandProperty);
+ ///
+ /// Sets the enter key command.
+ ///
+ /// The element.
+ /// The value.
+ public static void SetEnterKeyCommand(UIElement element, ICommand value) => element.SetValue(EnterKeyCommandProperty, value);
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/ViewModels/CurrencyViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/ViewModels/CurrencyViewModel.cs
new file mode 100644
index 000000000..f3bafea14
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters/ViewModels/CurrencyViewModel.cs
@@ -0,0 +1,49 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters
+// Author : Mir
+// Created : 07-01-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-01-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_06_Converters.ViewModels;
+
+///
+/// Class CurrencyViewModel.
+/// Implements the
+///
+///
+public class CurrencyViewModel : BaseViewModel
+{
+ ///
+ /// The value
+ ///
+ private decimal _value;
+ ///
+ /// Gets or sets the value.
+ ///
+ /// The value.
+ public decimal Value { get => _value; set => SetProperty(ref _value, value); }
+
+ public DelegateCommand EnterKey { get; set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CurrencyViewModel()
+ {
+ Value = 10;
+ EnterKey = new DelegateCommand(OnEnterKey);
+ }
+
+ private void OnEnterKey(object? obj)
+ {
+ ;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/MVVM_06_ConvertersTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/MVVM_06_ConvertersTests.csproj
index eb2939119..6d4bb929d 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/MVVM_06_ConvertersTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/MVVM_06_ConvertersTests.csproj
@@ -8,8 +8,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/ValueConverter/CurrencyValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/ValueConverter/CurrencyValueConverterTests.cs
new file mode 100644
index 000000000..a7bb00da0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/ValueConverter/CurrencyValueConverterTests.cs
@@ -0,0 +1,65 @@
+// ***********************************************************************
+// Assembly : MVVM_06_ConvertersTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_06_Converters.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class CurrencyValueConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ CurrencyValueConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(10.5, "10.50€")]
+ [DataRow(0.99, "0.99€")]
+ [DataRow("Hallo", "Hallo")]
+ [DataRow(null, "")]
+ public void ConvertTest(object? value, string expected)
+ {
+ if (value is double d) value = (decimal)d;
+ var result = testConv.Convert(value, typeof(string), null, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod()]
+ public void ConvertBackTest()
+ {
+ Assert.ThrowsExactly(()=> testConv.ConvertBack(null,typeof(object),null,null));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/View/CurrencyViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/View/CurrencyViewTests.cs
new file mode 100644
index 000000000..13a516439
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/View/CurrencyViewTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_06_Converters.View.Tests;
+
+[TestClass]
+public class CurrencyViewTests
+{
+ [TestMethod()]
+ public void CurrencyViewTest()
+ {
+ CurrencyView? testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(CurrencyView));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/View/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/View/MainWindowTests.cs
new file mode 100644
index 000000000..b44b28800
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/View/MainWindowTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_06_Converters.View.Tests;
+
+[TestClass()]
+public class MainWindowTests
+{
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/ViewModels/CurrencyViewViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/ViewModels/CurrencyViewViewModelTests.cs
new file mode 100644
index 000000000..7ba5ddf5d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_ConvertersTests/ViewModels/CurrencyViewViewModelTests.cs
@@ -0,0 +1,40 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System.Collections.Generic;
+
+namespace MVVM_06_Converters.ViewModels.Tests;
+
+[TestClass]
+public class CurrencyViewViewModelTests : BaseTestViewModel
+{
+ protected override Dictionary GetDefaultData() => new()
+ {
+ { nameof(CurrencyViewModel.Value), 10m }
+ };
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(CurrencyViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ }
+
+ [TestMethod()]
+ public void ValueTest()
+ {
+ Assert.AreEqual(10m, testModel.Value);
+ testModel.Value = 1m;
+ Assert.AreEqual(1m, testModel.Value);
+ Assert.AreEqual(@"PropChg(MVVM_06_Converters.ViewModels.CurrencyViewModel,Value)=1
+", DebugLog);
+ }
+
+ [TestMethod()]
+ public void EnterKeyTest()
+ {
+ testModel.EnterKey.Execute(null);
+ Assert.AreEqual("", DebugLog);
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/App.xaml
new file mode 100644
index 000000000..1653e174f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/App.xaml.cs
new file mode 100644
index 000000000..38e151ee3
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_2
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_06_Converters_2;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/AssemblyInfo.cs
new file mode 100644
index 000000000..37c97a40b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_2
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-01-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MVVM_06_Converters_2.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MVVM_06_Converters_2.csproj
new file mode 100644
index 000000000..fd4c0593b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MVVM_06_Converters_2.csproj
@@ -0,0 +1,34 @@
+
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Resources.resx
+ True
+ True
+
+
+
+
+ Resources.Designer.cs
+ PublicResXFileCodeGenerator
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MVVM_06_Converters_2_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MVVM_06_Converters_2_net.csproj
new file mode 100644
index 000000000..49c7e8f71
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MVVM_06_Converters_2_net.csproj
@@ -0,0 +1,40 @@
+
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MainWindow.xaml
new file mode 100644
index 000000000..726c46e1a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MainWindow.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MainWindow.xaml.cs
new file mode 100644
index 000000000..6a064925c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_2
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_06_Converters_2;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..37cc245a9
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/Properties/Resources.Designer.cs
@@ -0,0 +1,138 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_06_Converters_2.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_06_Converters_2.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page x:Class="MVVM_06_Converters_2.View.Currency2View"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_06_Converters_2.View"
+ /// xmlns:mvvm="clr-namespace:MVVM_06_Converters_2.ViewModels"
+ /// xmlns:vc="clr-namespace:MVVM_06_Converte [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string Currency2View {
+ get {
+ return ResourceManager.GetString("Currency2View", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_6_Converters_2
+ ///// Author : Mir
+ ///// Created : 07-03-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 07-19-2022
+ ///// ***********************************************************************
+ ///// <copyright file="CurrencyValueConverter.cs" company="MVVM_6_Converters_2">
+ ///// Copyright (c) JC-Soft. All rights reserved.
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// **************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CurrencyValueConverter {
+ get {
+ return ResourceManager.GetString("CurrencyValueConverter", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_6_Converters_2
+ ///// Author : Mir
+ ///// Created : 07-03-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 07-04-2022
+ ///// ***********************************************************************
+ ///// <copyright file="CurrencyViewModel.cs" company="MVVM_6_Converters_2">
+ ///// Copyright (c) JC-Soft. All rights reserved.
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ********************* [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CurrencyViewModel {
+ get {
+ return ResourceManager.GetString("CurrencyViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Turorial #06.2: Extended example of Currency-ValueConverter ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM #06 Converter2 ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/Properties/Resources.resx
new file mode 100644
index 000000000..bd868028c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/Properties/Resources.resx
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\View\Currency2View.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ValueConverter\CurrencyValueConverter.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\CurrencyViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ Turorial #06.2: Extended example of Currency-ValueConverter
+
+
+ MVVM #06 Converter2
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/ValueConverter/CurrencyValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/ValueConverter/CurrencyValueConverter.cs
new file mode 100644
index 000000000..197db0b34
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/ValueConverter/CurrencyValueConverter.cs
@@ -0,0 +1,59 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_2
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-19-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_06_Converters_2.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class CurrencyValueConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is decimal dval)
+ return dval.ToString("0.00€",culture);
+ else
+ return value.ToString() ?? "";
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string sval && parameter is string spar)
+ return decimal.Parse(sval.Replace(spar.Substring(spar.Length - 1), "").Trim(),culture);
+ else
+ return "";
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/View/Currency2View.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/View/Currency2View.xaml
new file mode 100644
index 000000000..f7709d38b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/View/Currency2View.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/View/Currency2View.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/View/Currency2View.xaml.cs
new file mode 100644
index 000000000..b33d9c4fb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/View/Currency2View.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_2
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows.Controls;
+
+namespace MVVM_06_Converters_2.View;
+
+///
+/// Interaktionslogik für CurrencyView.xaml
+///
+public partial class Currency2View : Page
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Currency2View()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/ViewModels/CurrencyViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/ViewModels/CurrencyViewModel.cs
new file mode 100644
index 000000000..339c839e2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_2/ViewModels/CurrencyViewModel.cs
@@ -0,0 +1,46 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_2
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_06_Converters_2.ViewModels;
+
+///
+/// Class CurrencyViewModel.
+/// Implements the
+///
+///
+public class CurrencyViewModel : BaseViewModel
+{
+ ///
+ /// The value
+ ///
+ private decimal _value;
+ ///
+ /// Gets or sets the value.
+ ///
+ /// The value.
+ public decimal Value { get => _value;
+ set
+ { if (_value == value) return; _value = value; RaisePropertyChanged(); }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CurrencyViewModel()
+ {
+ Value = 0;
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/App.xaml
new file mode 100644
index 000000000..019c2ea9b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/App.xaml.cs
new file mode 100644
index 000000000..d97a54040
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_3
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_06_Converters_3;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/AssemblyInfo.cs
new file mode 100644
index 000000000..022e1e622
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_3
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-01-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MVVM_06_Converters_3.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MVVM_06_Converters_3.csproj
new file mode 100644
index 000000000..396deb90a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MVVM_06_Converters_3.csproj
@@ -0,0 +1,33 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Resources.resx
+ True
+ True
+
+
+
+
+ Resources.Designer.cs
+ PublicResXFileCodeGenerator
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MVVM_06_Converters_3_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MVVM_06_Converters_3_net.csproj
new file mode 100644
index 000000000..58c6377e4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MVVM_06_Converters_3_net.csproj
@@ -0,0 +1,39 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MainWindow.xaml
new file mode 100644
index 000000000..55aefba07
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MainWindow.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MainWindow.xaml.cs
new file mode 100644
index 000000000..62d9eb4c4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_3
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_06_Converters_3;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..391d44b36
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_06_Converters_3.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_06_Converters_3.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/Properties/Resources.resx
new file mode 100644
index 000000000..ccbd18136
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/Properties/Resources.resx
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ValueConverter/Bool2VisibilityConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ValueConverter/Bool2VisibilityConverter.cs
new file mode 100644
index 000000000..369e6701f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ValueConverter/Bool2VisibilityConverter.cs
@@ -0,0 +1,59 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_3
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace MVVM_06_Converters_3.ValueConverter;
+
+///
+/// Class Bool2VisibilityConverter.
+/// Implements the
+///
+///
+public class Bool2VisibilityConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool x)
+ return x?Visibility.Visible:Visibility.Hidden;
+ else
+ return Visibility.Visible;
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is Visibility v)
+ return Visibility.Visible == v;
+ else
+ return false;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ValueConverter/CurrencyValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ValueConverter/CurrencyValueConverter.cs
new file mode 100644
index 000000000..a1f2f2d16
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ValueConverter/CurrencyValueConverter.cs
@@ -0,0 +1,60 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_3
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_06_Converters_3.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class CurrencyValueConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is decimal dval && parameter is string spar)
+ return dval.ToString(spar,culture);
+ else if (value is decimal dval2)
+ return dval2.ToString(culture);
+ else
+ return $"{value}";
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string sval && parameter is string spar)
+ return decimal.Parse(sval.Replace((" "+spar).Substring(Math.Max(0,spar.Length)), "").Trim());
+ else
+ return decimal.Zero;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/View/CurrencyView3.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/View/CurrencyView3.xaml
new file mode 100644
index 000000000..468780e81
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/View/CurrencyView3.xaml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/View/CurrencyView3.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/View/CurrencyView3.xaml.cs
new file mode 100644
index 000000000..49f6927ee
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/View/CurrencyView3.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_3
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows.Controls;
+
+namespace MVVM_06_Converters_3.View;
+
+///
+/// Interaktionslogik für CurrencyView.xaml
+///
+public partial class CurrencyView3 : Page
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CurrencyView3()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ViewModels/CurrencyViewViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ViewModels/CurrencyViewViewModel.cs
new file mode 100644
index 000000000..c5dbbbabd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3/ViewModels/CurrencyViewViewModel.cs
@@ -0,0 +1,51 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_3
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_06_Converters_3.ViewModels;
+
+///
+/// Class CurrencyViewViewModel.
+/// Implements the
+///
+///
+public class CurrencyViewViewModel : BaseViewModel
+{
+ ///
+ /// The value
+ ///
+ private decimal _value;
+ ///
+ /// Gets or sets the value.
+ ///
+ /// The value.
+ public decimal Value { get => _value;
+ set
+ { if (_value == value) return; _value = value; RaisePropertyChanged(nameof(Value), nameof(ValueIsNotZero) ); }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CurrencyViewViewModel()
+ {
+ Value = 0;
+ }
+
+ ///
+ /// Gets a value indicating whether [value is not zero].
+ ///
+ /// true if [value is not zero]; otherwise, false.
+ public bool ValueIsNotZero => _value != 0;
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/MVVM_06_Converters_3Tests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/MVVM_06_Converters_3Tests.csproj
index f8189f8ad..d9afd6726 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/MVVM_06_Converters_3Tests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/MVVM_06_Converters_3Tests.csproj
@@ -9,8 +9,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/MVVM_06_Converters_3_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/MVVM_06_Converters_3_netTests.csproj
index d741d59c9..1cd9529b8 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/MVVM_06_Converters_3_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/MVVM_06_Converters_3_netTests.csproj
@@ -1,16 +1,22 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
true
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ValueConverter/Bool2VisibilityConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ValueConverter/Bool2VisibilityConverterTests.cs
new file mode 100644
index 000000000..f2f79be16
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ValueConverter/Bool2VisibilityConverterTests.cs
@@ -0,0 +1,69 @@
+// ***********************************************************************
+// Assembly : MVVM_06_ConvertersTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_06_Converters_3.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class Bool2VisibilityConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ Bool2VisibilityConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(10.5, System.Windows.Visibility.Visible)]
+ [DataRow(0.99, System.Windows.Visibility.Visible)]
+ [DataRow(true, System.Windows.Visibility.Visible)]
+ [DataRow(false, System.Windows.Visibility.Hidden)]
+ [DataRow("Hallo", System.Windows.Visibility.Visible)]
+ [DataRow(null, System.Windows.Visibility.Visible)]
+ public void ConvertTest(object? value, System.Windows.Visibility expected)
+ {
+ if (value is double d) value = (decimal)d;
+ var result = testConv.Convert(value!, typeof(string), null!, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod()]
+ [DataRow(true, System.Windows.Visibility.Visible)]
+ [DataRow(false, System.Windows.Visibility.Hidden)]
+ [DataRow(false, null)]
+ public void ConvertBackTest(bool xExp, object eVal)
+ {
+ Assert.AreEqual(xExp, testConv.ConvertBack(eVal,typeof(object),null!,CultureInfo.InvariantCulture));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ValueConverter/CurrencyValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ValueConverter/CurrencyValueConverterTests.cs
new file mode 100644
index 000000000..6e182ef3a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ValueConverter/CurrencyValueConverterTests.cs
@@ -0,0 +1,52 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
+
+namespace MVVM_06_Converters_3.ValueConverter.Tests;
+
+[TestClass()]
+public class CurrencyValueConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ CurrencyValueConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(10.5, null, "10.5")]
+ [DataRow(10.5, "", "10.5")]
+ [DataRow(10.5, "0.00$", "10.50$")]
+ [DataRow(0.99, null, "0.99")]
+ [DataRow(0.99, "", "0.99")]
+ [DataRow(0.999, "0.00€", "1.00€")]
+ [DataRow(true, null, "True")]
+ [DataRow(false, null, "False")]
+ [DataRow("Hallo",null, "Hallo")]
+ [DataRow(null, null, "")]
+ public void ConvertTest(object? value, object? value2, string expected)
+ {
+ if (value is double d) value = (decimal)d;
+ var result = testConv.Convert(value!, typeof(string), value2!, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod()]
+ [DataRow(0.0, "1", null)]
+ [DataRow(0.0, null, "")]
+ [DataRow(1.0, "", "1")]
+ public void ConvertBackTest(object? value, object? value2, string eVal)
+ {
+ if (value is double d) value = (decimal)d;
+ Assert.AreEqual(value, testConv.ConvertBack(eVal, typeof(object), value2!, CultureInfo.InvariantCulture));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/View/CurrencyView3Tests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/View/CurrencyView3Tests.cs
new file mode 100644
index 000000000..ececdc5b3
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/View/CurrencyView3Tests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_06_Converters_3.View.Tests;
+
+[TestClass]
+public class CurrencyView3Tests
+{
+ [TestMethod()]
+ public void CurrencyViewTest()
+ {
+ CurrencyView3? testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(CurrencyView3));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/View/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/View/MainWindowTests.cs
new file mode 100644
index 000000000..6841c5299
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/View/MainWindowTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_06_Converters_3.View.Tests;
+
+[TestClass()]
+public class MainWindowTests
+{
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ViewModels/CurrencyViewViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ViewModels/CurrencyViewViewModelTests.cs
new file mode 100644
index 000000000..61f720f15
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_3Tests/ViewModels/CurrencyViewViewModelTests.cs
@@ -0,0 +1,40 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System;
+using System.Collections.Generic;
+
+namespace MVVM_06_Converters_3.ViewModels.Tests;
+
+[TestClass()]
+public class CurrencyViewViewModelTests:BaseTestViewModel
+{
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsNotNull(testModel2);
+ Assert.IsInstanceOfType(testModel, typeof(CurrencyViewViewModel));
+ Assert.IsInstanceOfType(testModel2, typeof(CurrencyViewViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ }
+
+ [TestMethod]
+ [DataRow(0d, new[] { "" })]
+ [DataRow(1d, new[] { @"PropChg(MVVM_06_Converters_3.ViewModels.CurrencyViewViewModel,Value)=1
+PropChg(MVVM_06_Converters_3.ViewModels.CurrencyViewViewModel,ValueIsNotZero)=True
+" })]
+ public void ValueTest(double fAct, string[] asExp)
+ {
+ Decimal dAct = (decimal)fAct;
+ Assert.AreEqual(0m, testModel.Value);
+ testModel.Value = dAct;
+ Assert.AreEqual(dAct, testModel.Value);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ protected override Dictionary GetDefaultData()
+ => new() {
+ { nameof(CurrencyViewViewModel.Value), 0m },
+ { nameof(CurrencyViewViewModel.ValueIsNotZero), false },
+ };
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.config b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.config
new file mode 100644
index 000000000..71eaca9e5
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.config
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+ 1200
+
+
+ 2000
+
+
+ 800
+
+
+ -200
+
+
+ 400
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.xaml
new file mode 100644
index 000000000..7a91ac1a7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.xaml.cs
new file mode 100644
index 000000000..b69a6d6d4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/App.xaml.cs
@@ -0,0 +1,35 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using BaseLib.Helper;
+using Microsoft.Extensions.DependencyInjection;
+using MVVM_06_Converters_4.Model;
+using System.Windows;
+
+namespace MVVM_06_Converters_4;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ base.OnStartup(e);
+ var sb = new ServiceCollection()
+ .AddSingleton()
+ .AddSingleton()
+ .BuildServiceProvider();
+ IoC.Configure(sb);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/AssemblyInfo.cs
new file mode 100644
index 000000000..45651a730
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-01-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MVVM_06_Converters_4.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MVVM_06_Converters_4.csproj
index 8b8236095..9edd648c7 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MVVM_06_Converters_4.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MVVM_06_Converters_4.csproj
@@ -8,12 +8,12 @@
-
+
-
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MVVM_06_Converters_4_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MVVM_06_Converters_4_net.csproj
index 1cc35b2e7..1c8179f80 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MVVM_06_Converters_4_net.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MVVM_06_Converters_4_net.csproj
@@ -2,11 +2,17 @@
WinExe
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
@@ -14,12 +20,12 @@
-
+
-
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MainWindow.xaml
new file mode 100644
index 000000000..0907f8d37
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MainWindow.xaml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MainWindow.xaml.cs
new file mode 100644
index 000000000..64f2622d4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_06_Converters_4;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Model/AGV_Model.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Model/AGV_Model.cs
new file mode 100644
index 000000000..64d631184
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Model/AGV_Model.cs
@@ -0,0 +1,108 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using MathLibrary.TwoDim;
+using MVVM.ViewModel;
+using MVVM_06_Converters_4.Properties;
+using System;
+using System.Collections.Generic;
+
+namespace MVVM_06_Converters_4.Model;
+
+public partial class AGV_Model : NotificationObjectCT, IAGVModel
+{
+ // private static AGV_Model? _instance;
+ [ObservableProperty]
+ private Math2d.Vector _vehicleDim;
+ [ObservableProperty]
+ private Math2d.Vector _swivelKoor;
+ [ObservableProperty]
+ private double _axisOffset;
+ [ObservableProperty]
+ private double _swivel1Angle;
+ [ObservableProperty]
+ private double _swivel2Angle;
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(Swivel1Velocity))]
+ [NotifyPropertyChangedFor(nameof(Swivel1Rot))]
+ [NotifyPropertyChangedFor(nameof(AGVVelocity))]
+ [NotifyPropertyChangedFor(nameof(VehicleRotation))]
+ private double _wheel1Velocity;
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(Swivel1Velocity))]
+ [NotifyPropertyChangedFor(nameof(Swivel1Rot))]
+ [NotifyPropertyChangedFor(nameof(AGVVelocity))]
+ [NotifyPropertyChangedFor(nameof(VehicleRotation))]
+ private double _wheel2Velocity;
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(Swivel2Velocity))]
+ [NotifyPropertyChangedFor(nameof(Swivel2Rot))]
+ [NotifyPropertyChangedFor(nameof(AGVVelocity))]
+ [NotifyPropertyChangedFor(nameof(VehicleRotation))]
+ private double _wheel3Velocity;
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(Swivel2Velocity))]
+ [NotifyPropertyChangedFor(nameof(Swivel2Rot))]
+ [NotifyPropertyChangedFor(nameof(AGVVelocity))]
+ [NotifyPropertyChangedFor(nameof(VehicleRotation))]
+ private double _wheel4Velocity;
+
+// public static AGV_Model Instance => _instance ??= new();
+
+ public double Swivel1Velocity => (Wheel1Velocity + Wheel2Velocity) * 0.5d;
+ public double Swivel2Velocity => (Wheel3Velocity + Wheel4Velocity) * 0.5d;
+ public double Swivel1Rot => (Wheel2Velocity - Wheel1Velocity) / AxisOffset;
+ public double Swivel2Rot => (Wheel4Velocity - Wheel3Velocity) / AxisOffset;
+ public Math2d.Vector AGVVelocity { get => Math2d.ByLengthAngle(Swivel1Velocity,Swivel1Angle)
+ .Add(Math2d.ByLengthAngle(Swivel2Velocity, Swivel2Angle))
+ .Mult(0.5d); }
+
+ public double VehicleRotation => Math2d.ByLengthAngle(Swivel1Velocity, Swivel1Angle)
+ .Subtract(Math2d.ByLengthAngle(Swivel2Velocity, Swivel2Angle))
+ .Mult(SwivelKoor.Rot90())
+ / (SwivelKoor.Length()*SwivelKoor.Length()*2);
+
+ public IEnumerable<(string, string)> Dependencies => new[]{
+ (nameof(Swivel1Velocity),nameof(Wheel1Velocity)),
+ //(nameof(Swivel1Velocity),nameof(Wheel2Velocity)),
+ //(nameof(Swivel2Velocity),nameof(Wheel3Velocity)),
+ //(nameof(Swivel2Velocity),nameof(Wheel4Velocity)),
+ //(nameof(Swivel1Rot),nameof(Wheel1Velocity)),
+ //(nameof(Swivel1Rot),nameof(Wheel2Velocity)),
+ //(nameof(Swivel2Rot),nameof(Wheel3Velocity)),
+ //(nameof(Swivel2Rot),nameof(Wheel4Velocity)),
+ //(nameof(AGVVelocity),nameof(Swivel1Velocity)),
+ //(nameof(AGVVelocity),nameof(Swivel2Velocity)),
+ //(nameof(AGVVelocity),nameof(Swivel1Angle)),
+ //(nameof(AGVVelocity),nameof(Swivel2Angle)),
+ //(nameof(VehicleRotation),nameof(Swivel1Velocity)),
+ //(nameof(VehicleRotation),nameof(Swivel2Velocity)),
+ //(nameof(VehicleRotation),nameof(Swivel1Angle)),
+ //(nameof(VehicleRotation),nameof(Swivel2Angle)),
+ };
+
+ public bool IsDirty { get; private set; }
+
+ public AGV_Model()
+ {
+ _vehicleDim = new Math2d.Vector(Settings.Default.Vehicle_Length, Settings.Default.Vehicle_Width);
+ _swivelKoor = new Math2d.Vector(Settings.Default.SwivelKoor_X, Settings.Default.SwivelKoor_Y);
+ _axisOffset = Settings.Default.AxisOffset;
+ PropertyChanged += (s, e) => IsDirty = true;
+ }
+#if NET5_0_OR_GREATER
+#else
+ ~AGV_Model()
+ {
+ Save();
+ }
+#endif
+ public void Save()
+ {
+ Settings.Default.Vehicle_Length = VehicleDim.x;
+ Settings.Default.Vehicle_Width = VehicleDim.y;
+ Settings.Default.SwivelKoor_X = SwivelKoor.x;
+ Settings.Default.SwivelKoor_Y = SwivelKoor.y;
+ Settings.Default.AxisOffset = AxisOffset;
+ Settings.Default.Save();
+ IsDirty = false;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Model/IAGVModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Model/IAGVModel.cs
new file mode 100644
index 000000000..4e4049e1a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Model/IAGVModel.cs
@@ -0,0 +1,28 @@
+using MathLibrary.TwoDim;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace MVVM_06_Converters_4.Model;
+
+public interface IAGVModel:INotifyPropertyChanged
+{
+ Math2d.Vector VehicleDim { get; set; }
+ Math2d.Vector SwivelKoor { get; set; }
+ double AxisOffset { get; set; }
+ double Swivel1Angle { get; set; }
+ double Wheel1Velocity { get; set; }
+ double Wheel2Velocity { get; set; }
+ double Swivel1Velocity { get; }
+
+ double Swivel2Angle { get; set; }
+ double Wheel3Velocity { get; set; }
+ double Wheel4Velocity { get; set; }
+ double Swivel2Velocity { get; }
+ IEnumerable<(string Dest, string Src)> Dependencies { get; }
+ Math2d.Vector AGVVelocity { get; }
+ double VehicleRotation { get; }
+ double Swivel1Rot { get; }
+ double Swivel2Rot { get; }
+
+ void Save();
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..b076d3f77
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_06_Converters_4.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_06_Converters_4.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Resources.resx
new file mode 100644
index 000000000..ccbd18136
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Resources.resx
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Settings.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..7724b0aca
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Settings.Designer.cs
@@ -0,0 +1,85 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_06_Converters_4.Properties;
+
+
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("1200")]
+ public double Vehicle_Width {
+ get {
+ return ((double)(this["Vehicle_Width"]));
+ }
+ set {
+ this["Vehicle_Width"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("2000")]
+ public double Vehicle_Length {
+ get {
+ return ((double)(this["Vehicle_Length"]));
+ }
+ set {
+ this["Vehicle_Length"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("800")]
+ public double SwivelKoor_X {
+ get {
+ return ((double)(this["SwivelKoor_X"]));
+ }
+ set {
+ this["SwivelKoor_X"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("-200")]
+ public double SwivelKoor_Y {
+ get {
+ return ((double)(this["SwivelKoor_Y"]));
+ }
+ set {
+ this["SwivelKoor_Y"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("400")]
+ public double AxisOffset {
+ get {
+ return ((double)(this["AxisOffset"]));
+ }
+ set {
+ this["AxisOffset"] = value;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Settings.settings b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Settings.settings
new file mode 100644
index 000000000..4fae06d63
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Properties/Settings.settings
@@ -0,0 +1,21 @@
+
+
+
+
+
+ 1200
+
+
+ 2000
+
+
+ 800
+
+
+ -200
+
+
+ 400
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/Bool2VisibilityConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/Bool2VisibilityConverter.cs
new file mode 100644
index 000000000..e53d03555
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/Bool2VisibilityConverter.cs
@@ -0,0 +1,59 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace MVVM_06_Converters_4.ValueConverter;
+
+///
+/// Class Bool2VisibilityConverter.
+/// Implements the
+///
+///
+public class Bool2VisibilityConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool x)
+ return x?Visibility.Visible:Visibility.Hidden;
+ else
+ return Visibility.Visible;
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is Visibility v)
+ return Visibility.Visible == v;
+ else
+ return false;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/CurrencyValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/CurrencyValueConverter.cs
new file mode 100644
index 000000000..403be98d8
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/CurrencyValueConverter.cs
@@ -0,0 +1,60 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_06_Converters_4.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class CurrencyValueConverter : IValueConverter
+{
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is decimal dval && parameter is string spar)
+ return dval.ToString(spar,culture);
+ else
+ return value?.ToString() ?? "";
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string sval && parameter is string spar
+ && decimal.TryParse(sval.Replace(spar.Substring(spar.Length - 1), "").Trim(),NumberStyles.Float, culture, out var dc))
+ return dc;
+ else
+ return decimal.Zero;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/DoubleValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/DoubleValueConverter.cs
new file mode 100644
index 000000000..e676e54ad
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ValueConverter/DoubleValueConverter.cs
@@ -0,0 +1,78 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_06_Converters_4.ValueConverter;
+
+///
+/// Class CurrencyValueConverter.
+/// Implements the
+///
+///
+public class DoubleValueConverter : IValueConverter
+{
+ public double FixedFactor { get; set; } = 1.0d;
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value switch
+ {
+ double dval when parameter is string spar => (dval * FixedFactor).ToString(spar),
+ double dval => (dval * FixedFactor).ToString(),
+ _ => value?.ToString() ?? ""
+ };
+
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value switch
+ {
+ string sval when
+ double.TryParse(sval, NumberStyles.Float, culture, out double dval) => dval / FixedFactor,
+ //_ when (parameter as string)?.Contains("{") == true
+ // => double.NaN, // Todo:
+ string sval when parameter is string spar && !spar.Contains("{")
+ => InnerParse(sval, spar),
+ _ => double.NaN
+ };
+
+ double InnerParse(string sval, string spar)
+ {
+ var pp = spar.LastIndexOf('0');
+ if (double.TryParse(sval.Replace(spar.Substring(pp + 1), "").Trim(), NumberStyles.Float, culture, out var dVal))
+ return dVal / FixedFactor;
+ else
+ return double.NaN;
+
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/CanvasBehavior.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/CanvasBehavior.cs
new file mode 100644
index 000000000..765039358
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/CanvasBehavior.cs
@@ -0,0 +1,99 @@
+// ***********************************************************************
+// Assembly : MVVM_Lines_on_Grid
+// Author : Mir
+// Created : 08-28-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-29-2022
+// ***********************************************************************
+//
+// (c) by Joe Care 2022
+//
+//
+// ***********************************************************************
+using System.Drawing;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Xaml.Behaviors;
+using MVVM_06_Converters_4.Views.Converter;
+
+namespace MVVM_06_Converters_4.ViewModels;
+
+///
+/// Class CanvasBehavior.
+/// Implements the
+///
+///
+public class CanvasBehavior : Behavior
+{
+ public PointF? RealPos { get; private set; }
+
+ private bool xPressed;
+
+ ///
+ /// Called after the behavior is attached to an AssociatedObject.
+ ///
+ /// Override this to hook up functionality to the AssociatedObject.
+ protected override void OnAttached()
+ {
+ var iObjParent = AssociatedObject.Parent as Page;
+ var cCoordinateConverter = iObjParent?.Resources["vcPortGrid"] as WindowPortToGridLines;
+
+ AssociatedObject.MouseWheel += (s, e) =>
+ {
+ if (AssociatedObject.DataContext is PlotFrameViewModel vm)
+ {
+ var mousePosition = e.GetPosition(s as IInputElement);
+ System.Drawing.RectangleF ActPort = vm.VPWindow;
+ if (e.Delta < 0)
+ {
+ ActPort.Inflate(ActPort.Size.Width * 0.1f, ActPort.Size.Height * 0.1f);
+ vm.VPWindow = ActPort;
+ }
+ else if (e.Delta > 0)
+ {
+ ActPort.Inflate(-ActPort.Size.Width * 0.1f, -ActPort.Size.Height * 0.1f);
+ vm.VPWindow = ActPort;
+ }
+
+ }
+ };
+
+ AssociatedObject.MouseLeftButtonDown += (s, e) =>
+ {
+ if (AssociatedObject.DataContext is PlotFrameViewModel vm)
+ {
+ var mousePosition = e.GetPosition(s as IInputElement);
+ RealPos = cCoordinateConverter?.Vis2RealP(mousePosition, cCoordinateConverter.GetAdjustedRect(vm.WindowPort));
+ xPressed = true;
+ }
+ };
+
+ AssociatedObject.MouseLeftButtonUp += (s, e) =>
+ {
+ if (AssociatedObject.DataContext is PlotFrameViewModel vm)
+ {
+ var mousePosition = e.GetPosition(s as IInputElement);
+ var NewPos = cCoordinateConverter?.Vis2RealP(mousePosition, cCoordinateConverter.GetAdjustedRect(vm.WindowPort));
+ xPressed = false;
+ }
+ };
+
+ AssociatedObject.MouseMove += (s, e) =>
+ {
+ if (AssociatedObject.DataContext is PlotFrameViewModel vm)
+ {
+ var mousePosition = e.GetPosition(s as IInputElement);
+ var NewPos = cCoordinateConverter?.Vis2RealP(mousePosition, cCoordinateConverter.GetAdjustedRect(vm.WindowPort));
+ if (xPressed && NewPos!=null)
+ {
+ System.Drawing.RectangleF ActPort = vm.VPWindow;
+ ActPort.Offset( -NewPos.Value.X + RealPos?.X ?? 0f,-NewPos.Value.Y+RealPos?.Y ?? 0f);
+ vm.VPWindow = ActPort;
+ }
+ }
+ };
+
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/PlotFrameViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/PlotFrameViewModel.cs
new file mode 100644
index 000000000..d47728a9e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/PlotFrameViewModel.cs
@@ -0,0 +1,378 @@
+// ***********************************************************************
+// Assembly : MVVM_Lines_on_Grid
+// Author : Mir
+// Created : 08-28-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-28-2022
+// ***********************************************************************
+//
+// (c) by Joe Care 2022
+//
+//
+// ***********************************************************************
+using CommunityToolkit.Mvvm.ComponentModel;
+using MathLibrary.TwoDim;
+using BaseLib.Helper;
+using MVVM.ViewModel;
+using MVVM_06_Converters_4.Model;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Threading.Tasks;
+
+namespace MVVM_06_Converters_4.ViewModels;
+
+///
+/// Struct SWindowPort
+///
+public struct SWindowPort
+{
+ ///
+ /// The port
+ ///
+ public RectangleF port;
+ ///
+ /// The window size
+ ///
+ public System.Windows.Size WindowSize;
+ ///
+ /// The parent
+ ///
+ public PlotFrameViewModel Parent;
+}
+
+///
+/// Struct DataSet
+///
+public struct DataSet
+{
+ ///
+ /// The datapoints
+ ///
+ public PointF[] Datapoints;
+ ///
+ /// The name
+ ///
+ public string Name;
+ ///
+ /// The description
+ ///
+ public string Description;
+ ///
+ /// The pen
+ ///
+ public System.Windows.Media.Pen Pen;
+}
+
+///
+/// Struct DataSet
+///
+public class ArrowList : List
+{
+ ///
+ /// The name
+ ///
+ public string? Name;
+ ///
+ /// The description
+ ///
+ public string? Description;
+ ///
+ /// The pen
+ ///
+ public System.Windows.Media.Pen? Pen;
+}
+
+public struct ArrowData
+{
+ ///
+ /// The datapoints
+ ///
+ public PointF Start;
+ ///
+ /// The datapoints
+ ///
+ public PointF End;
+}
+
+///
+/// Struct DataSet
+///
+public class CircleList : List
+{
+ ///
+ /// The name
+ ///
+ public string? Name;
+ ///
+ /// The description
+ ///
+ public string? Description;
+ ///
+ /// The pen
+ ///
+ public System.Windows.Media.Pen? Pen;
+}
+public struct CircleData
+{
+ ///
+ /// The datapoints
+ ///
+ public PointF Center;
+ ///
+ /// The datapoints
+ ///
+ public double Radius;
+}
+
+///
+/// Struct DataSet
+///
+public class PolynomeList : List
+{
+ ///
+ /// The name
+ ///
+ public string? Name;
+ ///
+ /// The description
+ ///
+ public string? Description;
+ ///
+ /// The pen
+ ///
+ public System.Windows.Media.Pen? Pen;
+}
+public struct PolynomeData
+{
+ ///
+ /// The datapoints
+ ///
+ public List Points;
+}
+
+
+///
+/// Class PlotFrameViewModel.
+/// Implements the
+///
+///
+public partial class PlotFrameViewModel : BaseViewModelCT
+{
+ ///
+ /// The view port
+ ///
+ private SWindowPort _windowPort;
+ ///
+ /// The dataset
+ ///
+ [ObservableProperty]
+ private DataSet _dataset1;
+ ///
+ /// Gets or sets the Arrows.
+ ///
+ /// The dataset.
+ [ObservableProperty]
+ private ArrowList _arrows;
+
+ ///
+ /// Gets or sets the Circles.
+ ///
+ /// The dataset.
+ [ObservableProperty]
+ private CircleList _circles;
+
+ ///
+ /// Gets or sets the Polinomes.
+ ///
+ /// The dataset.
+ [ObservableProperty]
+ private PolynomeList _polynomes;
+
+ private IAGVModel _agv_Model;
+
+ public SWindowPort WindowPort { get => _windowPort; set => SetProperty(ref _windowPort, value); }
+ ///
+ /// Gets or sets the vp window.
+ ///
+ /// The vp window.
+ public RectangleF VPWindow { get => WindowPort.port; set => SetProperty(ref _windowPort.port, value ); }
+ ///
+ /// Gets or sets the size of the window.
+ ///
+ /// The size of the window.
+ public System.Windows.Size WindowSize
+ {
+ get => WindowPort.WindowSize;
+ set => SetProperty(ref _windowPort.WindowSize, value);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PlotFrameViewModel()
+ {
+ // VPWindow = new RectangleF(-300, 300, 9, 6);
+ VPWindow = new RectangleF(-2000, -1500, 5000, 3000);
+ // VPWindow = new RectangleF(-3, -3, 900, 600);
+ // VPWindow = new RectangleF(-0.03f, -0.03f, 0.09f, 0.06f);
+ WindowSize = new System.Windows.Size(600, 400);
+ _windowPort.Parent = this;
+
+ _dataset1 = new DataSet();
+ _arrows = new ArrowList();
+ _arrows.Pen = new System.Windows.Media.Pen(System.Windows.Media.Brushes.Red, 2.0);
+ _circles = new CircleList();
+ _circles.Pen = new System.Windows.Media.Pen(System.Windows.Media.Brushes.Green, 1.0);
+ _polynomes = new PolynomeList();
+ _polynomes.Pen = new System.Windows.Media.Pen(System.Windows.Media.Brushes.Blue, 2.0);
+
+ // DemoData();
+
+ AddPropertyDependency(nameof(Dataset1), nameof(WindowPort), true);
+ AddPropertyDependency(nameof(Arrows), nameof(WindowPort), true);
+ AddPropertyDependency(nameof(Circles), nameof(WindowPort), true);
+ AddPropertyDependency(nameof(Polynomes), nameof(WindowPort), true);
+ AddPropertyDependency(nameof(WindowPort), nameof(WindowSize), true);
+ AddPropertyDependency(nameof(WindowPort), nameof(VPWindow), true);
+
+ _agv_Model = IoC.GetRequiredService();
+ _agv_Model.PropertyChanged += OnModelPropChanged;
+ AsyncInit();
+ }
+
+ async void AsyncInit()
+ {
+ await Task.Delay(100);
+ OnModelPropChanged(_agv_Model, new PropertyChangedEventArgs(nameof(IAGVModel.VehicleDim)));
+ }
+
+ private void OnModelPropChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ // Has to be Optimized
+ Polynomes.Clear();
+ Circles.Clear();
+ Arrows.Clear();
+ Polynomes.Add(new()
+ {
+ Points = MakeRect(_agv_Model.VehicleDim,0.5f,0.5f)
+ });
+ if (double.IsNaN(_agv_Model.SwivelKoor.x) || _agv_Model.SwivelKoor.y == double.NaN) return;
+ Circles.Add(new()
+ {
+ Center = new((float)_agv_Model.SwivelKoor.x, (float)_agv_Model.SwivelKoor.y),
+ Radius = _agv_Model.AxisOffset * 0.4f
+ });
+ Circles.Add(new()
+ {
+ Center = new(-(float)_agv_Model.SwivelKoor.x, -(float)_agv_Model.SwivelKoor.y),
+ Radius = _agv_Model.AxisOffset * 0.4f
+ });
+ // Rad 1
+ Polynomes.Add(new()
+ {
+ Points = MakeRotRect(_agv_Model.SwivelKoor,_agv_Model.Swivel1Angle,Math2d.ByLengthAngle(_agv_Model.AxisOffset*0.5,Math.PI*0.5), 0.1f, 0.3f)
+ });
+ Arrows.Add(new()
+ {
+ Start = MakeRotArrow(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), 0d, _agv_Model.AxisOffset * 0.5),
+ End = MakeRotArrow(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), _agv_Model.Wheel1Velocity, _agv_Model.AxisOffset * 0.5)
+ });
+ // Rad 2
+ Polynomes.Add(new()
+ {
+ Points = MakeRotRect(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(_agv_Model.AxisOffset * 0.5, -Math.PI * 0.5), 0.1f, 0.3f)
+ });
+ Arrows.Add(new()
+ {
+ Start = MakeRotArrow(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), 0d, _agv_Model.AxisOffset * -0.5),
+ End = MakeRotArrow(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), _agv_Model.Wheel2Velocity, _agv_Model.AxisOffset * -0.5)
+ });
+ Arrows.Add(new()
+ {
+ Start = MakeRotArrow(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), 0d, 0),
+ End = MakeRotArrow(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), _agv_Model.Swivel1Velocity, 0)
+ });
+ Arrows.Add(new()
+ {
+ Start = MakeRotArrow(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), 0d, _agv_Model.AxisOffset * 0.4),
+ End = MakeRotArrow(_agv_Model.SwivelKoor, _agv_Model.Swivel1Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), _agv_Model.Swivel1Rot * _agv_Model.AxisOffset * -0.4, _agv_Model.AxisOffset * 0.4)
+ });
+ // Rad 3
+ Polynomes.Add(new()
+ {
+ Points = MakeRotRect(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(_agv_Model.AxisOffset * 0.5, Math.PI * 0.5), 0.1f, 0.3f)
+ });
+ Arrows.Add(new()
+ {
+ Start = MakeRotArrow(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), 0d, _agv_Model.AxisOffset * 0.5),
+ End = MakeRotArrow(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), _agv_Model.Wheel3Velocity, _agv_Model.AxisOffset * 0.5)
+ });
+ // Rad 4
+ Polynomes.Add(new()
+ {
+ Points = MakeRotRect(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(_agv_Model.AxisOffset * 0.5, -Math.PI * 0.5), 0.1f, 0.3f)
+ });
+ Arrows.Add(new()
+ {
+ Start = MakeRotArrow(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), 0d, _agv_Model.AxisOffset * -0.5),
+ End = MakeRotArrow(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), _agv_Model.Wheel4Velocity, _agv_Model.AxisOffset * -0.5)
+ });
+ Arrows.Add(new()
+ {
+ Start = MakeRotArrow(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), 0d, 0 ),
+ End = MakeRotArrow(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), _agv_Model.Swivel2Velocity, 0)
+ });
+ Arrows.Add(new()
+ {
+ Start = MakeRotArrow(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), 0d, _agv_Model.AxisOffset * 0.4),
+ End = MakeRotArrow(_agv_Model.SwivelKoor.Mult(-1), _agv_Model.Swivel2Angle, Math2d.ByLengthAngle(1, Math.PI * 0.5), _agv_Model.Swivel2Rot* _agv_Model.AxisOffset*-0.4, _agv_Model.AxisOffset * 0.4)
+ });
+ // AGV-Velocity
+ Arrows.Add(new()
+ {
+ Start = PointF.Empty,
+ End = MakeRotArrow(_agv_Model.AGVVelocity, 0, Math2d.eY,0d,0d)
+ });
+
+ OnPropertyChanged(nameof(Polynomes));
+ OnPropertyChanged(nameof(Circles));
+ OnPropertyChanged(nameof(Arrows));
+
+ List MakeRect(Math2d.Vector vs, float xSize,float ySize)
+ {
+ return new List() {
+ new ((float)vs.x * xSize,(float)vs.y * ySize),
+ new ((float)vs.x * -xSize, (float)vs.y * ySize),
+ new ((float)vs.x * -xSize, (float)vs.y * -ySize),
+ new ((float)vs.x * xSize, (float)vs.y * -ySize)
+ };
+ }
+
+ List MakeRotRect(Math2d.Vector vO,double dAngle, Math2d.Vector vO2, double xSize, double ySize)
+ {
+ var vO2r=vO2.Rotate(dAngle);
+ var vO2rn = vO2r.Rot90();
+ return new List() {
+ new ((float)(vO.x + vO2r.x *(1f+xSize) + vO2rn.x *(+ySize))
+ ,(float)(vO.y + vO2r.y *(1f+xSize) + vO2rn.y *(+ySize))),
+ new ((float)(vO.x + vO2r.x *(1f-xSize) + vO2rn.x *(+ySize))
+ ,(float)(vO.y + vO2r.y *(1f-xSize) + vO2rn.y *(+ySize))),
+ new ((float)(vO.x + vO2r.x *(1f-xSize) + vO2rn.x *(-ySize))
+ ,(float)(vO.y + vO2r.y *(1f-xSize) + vO2rn.y *(-ySize))),
+ new ((float)(vO.x + vO2r.x *(1f+xSize) + vO2rn.x *(-ySize))
+ ,(float)(vO.y + vO2r.y *(1f+xSize) + vO2rn.y *(-ySize)))
+ };
+ }
+ PointF MakeRotArrow(Math2d.Vector vO, double dAngle, Math2d.Vector vO2, double xSize, double yOffs)
+ {
+ var vO2r = vO2.Rotate(dAngle).Mult(1 /vO2.Length());
+ var vO2rn = vO2r.Rot90();
+ return
+ new ((float)(vO.x + vO2r.x * yOffs - vO2rn.x * xSize)
+ ,(float)(vO.y + vO2r.y * yOffs - vO2rn.y * xSize));
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/VehicleViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/VehicleViewModel.cs
new file mode 100644
index 000000000..264d3e9e1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/ViewModels/VehicleViewModel.cs
@@ -0,0 +1,97 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using MathLibrary.TwoDim;
+using MVVM_06_Converters_4.Model;
+using System.ComponentModel;
+using System;
+using CommunityToolkit.Mvvm.Input;
+using BaseLib.Helper;
+
+namespace MVVM_06_Converters_4.ViewModels;
+
+///
+/// Class CurrencyViewViewModel.
+/// Implements the
+///
+///
+public class VehicleViewModel : BaseViewModel
+{
+ ///
+ /// The value
+ ///
+ private IAGVModel _agv_Model;
+ ///
+ /// Gets or sets the value.
+ ///
+ /// The value.
+ public double VehicleLength { get => _agv_Model.VehicleDim.x; set => _agv_Model.VehicleDim = new(value, _agv_Model.VehicleDim.y); }
+ public double VehicleWidth { get => _agv_Model.VehicleDim.y; set => _agv_Model.VehicleDim = new(_agv_Model.VehicleDim.x, value); }
+ public double SwivelKoorX { get => _agv_Model.SwivelKoor.x; set => _agv_Model.SwivelKoor = new(value, _agv_Model.SwivelKoor.y); }
+ public double SwivelKoorY { get => _agv_Model.SwivelKoor.y; set => _agv_Model.SwivelKoor = new(_agv_Model.SwivelKoor.x, value); }
+ public double AxisOffset { get => _agv_Model.AxisOffset; set => _agv_Model.AxisOffset = value; }
+
+ public double Swivel1Angle { get => _agv_Model.Swivel1Angle; set => _agv_Model.Swivel1Angle = value; }
+ public double Wheel1Velocity { get => _agv_Model.Wheel1Velocity; set => _agv_Model.Wheel1Velocity = value; }
+ public double Wheel2Velocity { get => _agv_Model.Wheel2Velocity; set => _agv_Model.Wheel2Velocity = value; }
+ public double Swivel2Angle { get => _agv_Model.Swivel2Angle; set => _agv_Model.Swivel2Angle = value; }
+ public double Wheel3Velocity { get => _agv_Model.Wheel3Velocity; set => _agv_Model.Wheel3Velocity = value; }
+ public double Wheel4Velocity { get => _agv_Model.Wheel4Velocity; set => _agv_Model.Wheel4Velocity = value; }
+
+ public double Swivel1Velocity { get => _agv_Model.Swivel1Velocity; }
+ public double Swivel2Velocity { get => _agv_Model.Swivel2Velocity; }
+ public double Swivel1Rot { get => _agv_Model.Swivel1Rot; }
+ public double Swivel2Rot { get => _agv_Model.Swivel2Rot; }
+ public double VehicleVelocityX { get => _agv_Model.AGVVelocity.x; }
+ public double VehicleVelocityY { get => _agv_Model.AGVVelocity.y; }
+ public double VehicleRotation { get => _agv_Model.VehicleRotation; }
+ public Math2d.Vector AGVVelocity { get => _agv_Model.AGVVelocity; }
+
+ public RelayCommand SaveCommand { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public VehicleViewModel() : this(IoC.GetRequiredService()){}
+
+ public VehicleViewModel(IAGVModel model)
+ {
+ _agv_Model = model;
+ _agv_Model.PropertyChanged += OnModelPropChanged;
+ foreach (var d in _agv_Model.Dependencies)
+ AddPropertyDependency(d.Dest, d.Src, true);
+ AddPropertyDependency(nameof(VehicleVelocityX), nameof(AGVVelocity));
+ AddPropertyDependency(nameof(VehicleVelocityY), nameof(AGVVelocity));
+ SaveCommand = new(_agv_Model.Save);
+ }
+
+ ~VehicleViewModel()
+ {
+ _agv_Model.Save();
+ }
+ private void OnModelPropChanged(object? sender, PropertyChangedEventArgs e) => (_ = e.PropertyName switch
+ {
+ nameof(IAGVModel.VehicleDim)
+ => () => RaisePropertyChanged(nameof(VehicleLength), nameof(VehicleWidth)),
+ nameof(IAGVModel.SwivelKoor)
+ => () => RaisePropertyChanged(nameof(SwivelKoorX), nameof(SwivelKoorY)),
+ string s => () => RaisePropertyChanged(s),
+ _ => (Action)(() => { })
+ })();
+
+ ///
+ /// Gets a value indicating whether [value is not zero].
+ ///
+ /// true if [value is not zero]; otherwise, false.
+ public bool ValueIsNotZero => _agv_Model.VehicleDim.AsComplex != 0;
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/Converter/WindowPortToGridLines.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/Converter/WindowPortToGridLines.cs
new file mode 100644
index 000000000..1693e469a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/Converter/WindowPortToGridLines.cs
@@ -0,0 +1,393 @@
+// ***********************************************************************
+// Assembly : MVVM_Lines_on_Grid
+// Author : Mir
+// Created : 08-28-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-29-2022
+// ***********************************************************************
+//
+// (c) by Joe Care 2022
+//
+//
+// ***********************************************************************
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
+using System.Windows.Media;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Shapes;
+using MVVM_06_Converters_4.ViewModels;
+using System.Collections.ObjectModel;
+using System.Windows;
+
+namespace MVVM_06_Converters_4.Views.Converter;
+
+///
+/// Class WindowPortToGridLines.
+/// Implements the
+///
+///
+public class WindowPortToGridLines : IValueConverter
+{
+ ///
+ /// The average GRD pixel
+ ///
+ const int AvgGrdPixel = 40;
+ ///
+ /// The lb
+ ///
+ System.Windows.Size lb = new System.Windows.Size(50, 28);
+
+ ///
+ /// Gets or sets the size of the window.
+ ///
+ /// The size of the window.
+ public System.Windows.Size WindowSize { get; set; } = new System.Windows.Size(600, 600);
+
+ ///
+ /// Real2s the vis.
+ ///
+ /// The value.
+ /// The vis minimum.
+ /// The vis maximum.
+ /// The r minimum.
+ /// The r maximum.
+ /// System.Double.
+ private double Real2Vis(double value, double visMin, double visMax, double rMin, double rMax)
+ => visMin + (value - rMin) * (visMax - visMin) / (rMax - rMin);
+
+ ///
+ /// Real2s the vis p.
+ ///
+ /// The value.
+ /// The port.
+ /// System.Windows.Point.
+ private System.Windows.Point real2VisP(PointF value, RectangleF port) =>
+ new System.Windows.Point(Real2Vis(value.X, 0, WindowSize.Width - lb.Width, port.Left, port.Right) + lb.Width,
+ Real2Vis(value.Y, WindowSize.Height - lb.Height, 0d, port.Top, port.Bottom) + lb.Height);
+
+ ///
+ /// Vis2s the real p.
+ ///
+ /// The value.
+ /// The port.
+ /// PointF.
+ private PointF vis2RealP(System.Windows.Point value, RectangleF port) =>
+ new PointF(
+ (float)Real2Vis(value.X- lb.Width, port.Left, port.Right, 0, WindowSize.Width - lb.Width) ,
+ (float)Real2Vis(value.Y- lb.Height, port.Top, port.Bottom, WindowSize.Height - lb.Height, 0d) );
+
+ ///
+ /// The real2 vis p
+ ///
+ public Func Real2VisP;
+ ///
+ /// The vis2 real p
+ ///
+ public Func Vis2RealP;
+ private RectangleF actPort;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public WindowPortToGridLines()
+ {
+ Real2VisP = real2VisP;
+ Vis2RealP = vis2RealP;
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ ObservableCollection result;
+ switch (value)
+ {
+ case SWindowPort c:
+ var b = new SolidColorBrush(Colors.Black);
+
+ var hOffset = 0d;
+ RectangleF port2; // erweiterter Viewport
+ port2 = GetAdjustedRect(c);
+
+ double BigStep, Step;
+ ComputeGridSteps(port2.Width, out BigStep, out Step);
+
+ double MinStepX = Math.Ceiling(port2.Left / Step) * Step;
+ double MinStepY = Math.Ceiling(port2.Top / Step) * Step;
+
+ actPort = port2;
+ result = new ObservableCollection();
+
+ if (c.port.Contains(PointF.Empty))
+ {
+ var p = Real2VisP(PointF.Empty, port2);
+ Ellipse el = new Ellipse() { Height = 7, Width = 7, Margin = new Thickness(p.X - 3, p.Y - 3, 0, 0), Stroke = b, StrokeThickness = 0.3d };
+ result.Add(el);
+ }
+
+ // var MaxSize = WindowSize.Width > (WindowSize.Height- hOffset) *1.5 ? (WindowSize.Width-lb.Width) / 1.5 : (WindowSize.Height-lb.Height- hOffset) ;
+
+ for (var i = 0; MinStepX + i * Step < port2.Right || MinStepY + i * Step < port2.Bottom; i++)
+ {
+ double X1 = MinStepX + i * Step;
+ var P1x = real2VisP(new PointF((float)X1, port2.Top), port2);
+ var P2x = real2VisP(new PointF((float)X1, port2.Bottom), port2);
+ if (P1x.X < WindowSize.Width)
+ {
+ result.Add(CreateLine(b, GetStroke(X1, Step, BigStep), P1x, P2x));
+
+ if (Math.Abs((Math.Abs(X1) + Step / 5) % BigStep) < Step / 2)
+ {
+ result.Add(CreateLabel(X1, new Thickness((double)(P1x.X - lb.Width / 2d + 7), 0d, 0d, 0d), VerticalAlignment.Bottom, HorizontalAlignment.Center));
+ }
+ }
+
+ double Y1 = MinStepY + i * Step;
+ var P1y = real2VisP(new PointF(port2.Left, (float)Y1), port2);
+ var P2y = real2VisP(new PointF(port2.Right, (float)Y1), port2);
+ if (P1y.Y < WindowSize.Height - hOffset && P1y.Y > lb.Height)
+ {
+ result.Add(CreateLine(b, GetStroke(Y1, Step, BigStep), P1y, P2y));
+ if (Math.Abs((Math.Abs(Y1) + Step / 5) % BigStep) < Step / 2)
+ {
+ result.Add(CreateLabel(Y1, new Thickness(0d, (double)(P1y.Y - hOffset - lb.Height + 5), 0d, 0d), VerticalAlignment.Center, HorizontalAlignment.Right));
+ }
+ }
+ }
+ return result;
+ case DataSet ds:
+ result = new ObservableCollection();
+ if (ds.Datapoints?.Length >1)
+ for (int i = 0; i < ds.Datapoints.Length - 1; i++)
+ {
+ var P1 = real2VisP(ds.Datapoints[i], actPort);
+ var P2 = real2VisP(ds.Datapoints[i + 1], actPort);
+ result.Add(CreateLine(ds.Pen.Brush, ds.Pen.Thickness, P1, P2));
+ }
+ return result;
+ case DataSet[] ads: return new ObservableCollection();
+ case ArrowList al:
+ result = new ObservableCollection();
+ foreach (var sh in al)
+ try
+ {
+ var P1 = real2VisP(sh.Start, actPort);
+ var P2 = real2VisP(sh.End, actPort);
+ foreach (var el in CreateArrow(al.Pen?.Brush, al.Pen?.Thickness ?? 1, P1, P2))
+ result.Add(el);
+ }
+ catch { }
+ return result;
+ case CircleList cl:
+ result = new ObservableCollection();
+ foreach (var sh in cl)
+ try
+ {
+ var P1 = real2VisP(sh.Center, actPort);
+ var r = Real2VisP(PointF.Add(sh.Center, new SizeF((float)sh.Radius, 0)), actPort).X - P1.X;
+ result.Add(CreateCircle(cl.Pen?.Brush, cl.Pen?.Thickness ?? 1, P1, (float)r));
+ }
+ catch { }
+ return result;
+ case PolynomeList pl:
+ result = new ObservableCollection();
+ foreach (var sh in pl)
+ try
+ {
+ var p = new PointCollection();
+ foreach (var pnt in sh.Points)
+ p.Add(real2VisP(pnt, actPort));
+ result.Add(CreatePolynome(pl.Pen?.Brush, pl.Pen?.Thickness ?? 1, p));
+ }
+ catch { }
+ return result;
+ default: return new ObservableCollection();
+ }
+
+ double GetStroke(double X1, double Step, double BigStep)
+ {
+ switch (X1)
+ {
+ case double when Math.Abs(X1) < Step / 5:
+ return 1.5d;
+ case double when Math.Abs((Math.Abs(X1) + Step / 5) % BigStep) < Step / 2:
+ return 0.8d;
+ default: return 0.3d;
+ }
+ }
+
+ }
+
+ ///
+ /// Gets the adjusted rect.
+ ///
+ /// The c.
+ /// RectangleF.
+ public RectangleF GetAdjustedRect(SWindowPort c)
+ {
+ RectangleF port2;
+ if (Math.Abs(WindowSize.Width * c.port.Height) < Math.Abs(WindowSize.Height * c.port.Width))
+ port2 = new RectangleF(c.port.Left, (float)(c.port.Top + c.port.Height * 0.5f - (WindowSize.Height * 0.5f) * c.port.Width / (float)WindowSize.Width), c.port.Width, (float)(c.WindowSize.Height * c.port.Width / WindowSize.Width));
+ else
+ port2 = new RectangleF((float)(c.port.Left + c.port.Width * 0.5 - (WindowSize.Width / 2) * c.port.Height / WindowSize.Height), c.port.Top, (float)(WindowSize.Width * c.port.Height / WindowSize.Height), c.port.Height);
+ return port2;
+ }
+
+ ///
+ /// Creates the line.
+ ///
+ /// The b.
+ /// The value.
+ /// The p1.
+ /// The p2.
+ /// FrameworkElement.
+ FrameworkElement CreateLine(System.Windows.Media.Brush? b, double value, System.Windows.Point P1, System.Windows.Point P2)
+ {
+ return new Line()
+ {
+ X1 = P1.X,
+ Y1 = P1.Y,
+ X2 = P2.X,
+ Y2 = P2.Y,
+ Stroke = b ?? System.Windows.Media.Brushes.Black,
+ StrokeThickness = value
+ };
+ }
+
+ ///
+ /// Creates the line.
+ ///
+ /// The b.
+ /// The value.
+ /// The p1.
+ /// The p2.
+ /// FrameworkElement.
+ FrameworkElement CreateCircle(System.Windows.Media.Brush? b, double value, System.Windows.Point P1, float r)
+ {
+ return new Ellipse()
+ {
+ Margin = new Thickness(P1.X-r, P1.Y-r, 0, 0),
+ Height = r * 2,
+ Width = r * 2,
+ Stroke = b ?? System.Windows.Media.Brushes.Black,
+ StrokeThickness = value
+ };
+ }
+
+ ///
+ /// Creates the line.
+ ///
+ /// The b.
+ /// The value.
+ /// The p1.
+ /// The p2.
+ /// FrameworkElement.
+ FrameworkElement CreatePolynome(System.Windows.Media.Brush? b, double value, PointCollection Pts)
+ {
+ return new Polygon()
+ {
+ Points = Pts,
+ Stroke = b ?? System.Windows.Media.Brushes.Black,
+ StrokeThickness = value
+ };
+ }
+
+ IEnumerable CreateArrow(System.Windows.Media.Brush? b, double value, System.Windows.Point P1, System.Windows.Point P2)
+ {
+ // Linie
+ yield return new Line()
+ {
+ X1 = P1.X,
+ Y1 = P1.Y,
+ X2 = P2.X,
+ Y2 = P2.Y,
+ Stroke = b ?? System.Windows.Media.Brushes.Black,
+ StrokeThickness = value,
+ };
+ System.Numerics.Vector2 v = new((float)(P2.X - P1.X), (float)(P2.Y - P1.Y));
+ var l = v.Length();
+ if (l > 0)
+ {
+ var ve = v * (1 / l);
+ yield return new Line()
+ {
+ X1 = P2.X -ve.X*(value*2) ,
+ Y1 = P2.Y - ve.Y*(value*2) ,
+ X2 = P2.X - ve.X * value,
+ Y2 = P2.Y - ve.Y * value,
+ Stroke = b ?? System.Windows.Media.Brushes.Black,
+ StrokeThickness = value*5,
+ StrokeEndLineCap = PenLineCap.Triangle
+ };
+ }
+ }
+
+
+ ///
+ /// Creates the label.
+ ///
+ /// The y1.
+ /// The margin.
+ /// The va.
+ /// The ha.
+ /// FrameworkElement.
+ FrameworkElement CreateLabel(double Y1, Thickness margin, VerticalAlignment va, HorizontalAlignment ha)
+ {
+ return new Label()
+ {
+ Content = $"{Y1:0.###}",
+ Width = lb.Width,
+ Height = lb.Height,
+ VerticalAlignment = va,
+ HorizontalAlignment = ha,
+ Margin = margin
+ };
+ }
+
+ ///
+ /// Computes the grid steps.
+ ///
+ /// Width of the vp.
+ /// The big step.
+ /// The step.
+ private void ComputeGridSteps(double vpWidth, out double BigStep, out double Step)
+ {
+ BigStep = Math.Pow(10, Math.Floor(Math.Log10(vpWidth * AvgGrdPixel * 10 / WindowSize.Width)));
+ while ((BigStep / vpWidth * WindowSize.Width) < AvgGrdPixel * 3 / 1.5)
+ BigStep *= 2d;
+ while ((BigStep / vpWidth * WindowSize.Width) > AvgGrdPixel * 4.5)
+ BigStep *= 0.5d;
+ switch ((BigStep / vpWidth * WindowSize.Width) / AvgGrdPixel)
+ {
+ case double d when d > 3.5: Step = BigStep / 10; break;
+ case double d when d < 2.5:
+ Step = BigStep / 4; break;
+ default: Step = BigStep / 5; break;
+ }
+ }
+
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/PlotFrame.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/PlotFrame.xaml
new file mode 100644
index 000000000..169a9974c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/PlotFrame.xaml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/PlotFrame.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/PlotFrame.xaml.cs
new file mode 100644
index 000000000..0bc60a925
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/PlotFrame.xaml.cs
@@ -0,0 +1,58 @@
+// ***********************************************************************
+// Assembly : MVVM_Lines_on_Grid
+// Author : Mir
+// Created : 08-28-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-28-2022
+// ***********************************************************************
+//
+// (c) by Joe Care 2022
+//
+//
+// ***********************************************************************
+using MVVM_06_Converters_4.Views.Converter;
+using MVVM_06_Converters_4.ViewModels;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace MVVM_06_Converters_4.Views;
+
+///
+/// Interaktionslogik für PlotFrame.xaml
+///
+public partial class PlotFrame : Page
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PlotFrame()
+ {
+ InitializeComponent();
+
+ DataContextChanged += (s,e)=>DataContextChange(s,e);
+
+ SizeChanged += OnSizeChange;
+
+ }
+
+ protected void DataContextChange(object? sender,object args)
+ {
+ if (this.Resources["vcPortGrid"] is WindowPortToGridLines pc)
+ {
+ // pc.Row = vm.Row;
+ // pc.Col = vm.Column;
+ pc.WindowSize = new Size(Width, Height);
+ }
+ }
+
+ public void OnSizeChange(object? sender,SizeChangedEventArgs e)
+ {
+ if (this.Resources["vcPortGrid"] is WindowPortToGridLines pc)
+ {
+ pc.WindowSize = e.NewSize;
+ if (DataContext is PlotFrameViewModel vm)
+ vm.WindowSize = e.NewSize;
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/VehicleView1.xaml b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/VehicleView1.xaml
new file mode 100644
index 000000000..c277c01b4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/VehicleView1.xaml
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/VehicleView1.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/VehicleView1.xaml.cs
new file mode 100644
index 000000000..2f6b6cc87
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4/Views/VehicleView1.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_6_Converters_4
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows.Controls;
+
+namespace MVVM_06_Converters_4.Views;
+
+///
+/// Interaktionslogik für CurrencyView.xaml
+///
+public partial class VehicleView1 : Page
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public VehicleView1()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/MVVM_06_Converters_4Tests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/MVVM_06_Converters_4Tests.csproj
index d1d163a2e..90df2d2de 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/MVVM_06_Converters_4Tests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/MVVM_06_Converters_4Tests.csproj
@@ -9,8 +9,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -21,6 +21,7 @@
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/MVVM_06_Converters_4_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/MVVM_06_Converters_4_netTests.csproj
index 9c7de1dd6..340026026 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/MVVM_06_Converters_4_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/MVVM_06_Converters_4_netTests.csproj
@@ -1,7 +1,7 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows;net9.0-windows
false
true
@@ -9,8 +9,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -21,6 +21,7 @@
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Model/AGV_ModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Model/AGV_ModelTests.cs
new file mode 100644
index 000000000..be19c8ead
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Model/AGV_ModelTests.cs
@@ -0,0 +1,24 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+
+namespace MVVM_06_Converters_4.Model.Tests;
+
+[TestClass()]
+public class AGV_ModelTests:BaseTestViewModel
+{
+ [TestMethod()]
+ public void AGV_ModelTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsNotNull(testModel2);
+ Assert.IsInstanceOfType(testModel, typeof(AGV_Model));
+ Assert.IsInstanceOfType(testModel, typeof(NotificationObjectCT));
+ }
+
+ [TestMethod()]
+ public void SaveTest()
+ {
+ testModel.Save();
+ Assert.IsFalse(testModel.IsDirty);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/Bool2VisibilityConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/Bool2VisibilityConverterTests.cs
new file mode 100644
index 000000000..c907cf680
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/Bool2VisibilityConverterTests.cs
@@ -0,0 +1,69 @@
+// ***********************************************************************
+// Assembly : MVVM_06_ConvertersTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_06_Converters_4.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class Bool2VisibilityConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ Bool2VisibilityConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(10.5, System.Windows.Visibility.Visible)]
+ [DataRow(0.99, System.Windows.Visibility.Visible)]
+ [DataRow(true, System.Windows.Visibility.Visible)]
+ [DataRow(false, System.Windows.Visibility.Hidden)]
+ [DataRow("Hallo", System.Windows.Visibility.Visible)]
+ [DataRow(null, System.Windows.Visibility.Visible)]
+ public void ConvertTest(object? value, System.Windows.Visibility expected)
+ {
+ if (value is double d) value = (decimal)d;
+ var result = testConv.Convert(value!, typeof(string), null!, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod()]
+ [DataRow(true, System.Windows.Visibility.Visible)]
+ [DataRow(false, System.Windows.Visibility.Hidden)]
+ [DataRow(false, null)]
+ public void ConvertBackTest(bool xExp, object? eVal)
+ {
+ Assert.AreEqual(xExp, testConv.ConvertBack(eVal!,typeof(object),null!,CultureInfo.InvariantCulture));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/CurrencyValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/CurrencyValueConverterTests.cs
new file mode 100644
index 000000000..8b1ac4aac
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/CurrencyValueConverterTests.cs
@@ -0,0 +1,72 @@
+// ***********************************************************************
+// Assembly : MVVM_06_ConvertersTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_06_Converters_4.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class CurrencyValueConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ CurrencyValueConverter testConv = new();
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(10.5, "10.50€")]
+ [DataRow(0.99, "0.99€")]
+ [DataRow("Hallo", "Hallo")]
+ [DataRow(null, "")]
+ public void ConvertTest(object? value, string expected)
+ {
+ if (value is double d) value = (decimal)d;
+ var result = testConv.Convert(value, typeof(string), "0.00€", CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod()]
+ [DataRow(10.5, "10.50€")]
+ [DataRow(0.99, "0.99€")]
+ [DataRow(0.0, "Hallo")]
+ [DataRow(0.0, null)]
+ public void ConvertBackTest(double fExp, object? oVal)
+ {
+ object dcExp = fExp;
+ if (fExp is double d)
+ dcExp = (decimal)d;
+ var result = testConv.ConvertBack(oVal, typeof(decimal), "0.00€", CultureInfo.InvariantCulture);
+ Assert.AreEqual(dcExp, result);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/DoubleValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/DoubleValueConverterTests.cs
new file mode 100644
index 000000000..77678e99f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ValueConverter/DoubleValueConverterTests.cs
@@ -0,0 +1,123 @@
+// ***********************************************************************
+// Assembly : MVVM_06_ConvertersTests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_06_Converters_4.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class DoubleValueConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ DoubleValueConverter testConv = new();
+
+ [TestInitialize]
+ public void Init()
+ {
+ testConv.FixedFactor = 100.0d;
+ CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
+ CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
+ }
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(10.5, "1050")]
+ [DataRow(0.99, "99")]
+ [DataRow("Hallo", "Hallo")]
+ [DataRow(null, "")]
+ public void ConvertTest(object? value, string expected)
+ {
+ if (value is double d) value = (double)d;
+ var result = testConv.Convert(value!, typeof(string), null!, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(10.5, "1050.00€")]
+ [DataRow(0.99, "99.00€")]
+ [DataRow("Hallo", "Hallo")]
+ [DataRow(null, "")]
+ public void ConvertTest2(object? value, string expected)
+ {
+ if (value is double d) value = (double)d;
+ var result = testConv.Convert(value!, typeof(string), "0.00€", CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod]
+ [DataRow(10.5, "1050")]
+ [DataRow(0.99, "99")]
+ [DataRow(double.NaN, "Hallo")]
+ [DataRow(double.NaN, "")]
+ [DataRow(double.NaN, null)]
+ public void ConvertBackTest(object? value, object expected)
+ {
+ var result = testConv.ConvertBack(expected, typeof(object), null!, CultureInfo.InvariantCulture);
+ Assert.AreEqual(value, result);
+ }
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod]
+ [DataRow(10.5, "1050€")]
+ [DataRow(0.99, "99€")]
+ [DataRow(double.NaN, "Hallo")]
+ [DataRow(double.NaN, "")]
+ public void ConvertBackTest2(object? value, string expected)
+ {
+ var result = testConv.ConvertBack(expected, typeof(object), "0.00€", CultureInfo.InvariantCulture);
+ Assert.AreEqual(value, result);
+ }
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod]
+ [DataRow(double.NaN, "Hallo")]
+ [DataRow(double.NaN, "")]
+ [DataRow(double.NaN, null)]
+ public void ConvertBackTest3(object? value, string expected)
+ {
+ var result = testConv.ConvertBack(expected, typeof(object), "{0}", CultureInfo.InvariantCulture);
+ Assert.AreEqual(value, result);
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ViewModels/PlotFrameViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ViewModels/PlotFrameViewModelTests.cs
new file mode 100644
index 000000000..c593abb7c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ViewModels/PlotFrameViewModelTests.cs
@@ -0,0 +1,38 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System.Collections.Generic;
+using System;
+using BaseLib.Helper;
+using MVVM_06_Converters_4.Model;
+using NSubstitute;
+
+namespace MVVM_06_Converters_4.ViewModels.Tests;
+
+[TestClass()]
+public class PlotFrameViewModelTests: BaseTestViewModel
+{
+ private Func _grsOld;
+ private IAGVModel? _model;
+
+ [TestInitialize]
+ public override void Init()
+ {
+ _grsOld = IoC.GetReqSrv;
+ IoC.GetReqSrv = (t) => t switch
+ {
+ Type _t when _t == typeof(IAGVModel) => _model ??= Substitute.For(),
+ _ => throw new System.NotImplementedException($"No Service for {t}")
+ };
+ base.Init();
+ }
+
+ [TestMethod()]
+ public void PlotFrameViewModelTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsNotNull(_model);
+ }
+
+ protected override Dictionary GetDefaultData()
+ => base.GetDefaultData();
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ViewModels/VehicleViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ViewModels/VehicleViewModelTests.cs
new file mode 100644
index 000000000..73cac42d7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/ViewModels/VehicleViewModelTests.cs
@@ -0,0 +1,126 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM_06_Converters_4.Model;
+using NSubstitute;
+using System;
+using System.Collections.Generic;
+using BaseLib.Helper;
+using MVVM.ViewModel;
+using System.ComponentModel;
+
+namespace MVVM_06_Converters_4.ViewModels.Tests;
+
+[TestClass()]
+public class VehicleViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ private IAGVModel _testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public override void Init()
+ {
+ _testModel = Substitute.For();
+ _testModel.SwivelKoor.Returns(new MathLibrary.TwoDim.Math2d.Vector());
+ _testModel.AGVVelocity.Returns(new MathLibrary.TwoDim.Math2d.Vector());
+ _testModel.VehicleDim.Returns(new MathLibrary.TwoDim.Math2d.Vector());
+ _testModel.Dependencies.Returns(returnThis: new (string Dest, string Src)[] { ("1","2") });
+ IoC.GetReqSrv=(t)=>t switch
+ {
+ _ when t == typeof(IAGVModel) => _testModel,
+ _ => throw new NotImplementedException()
+ };
+ base.Init();
+ }
+ [TestMethod()]
+ public void TestSetUp()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(VehicleViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ }
+
+ static IEnumerable VehicleViewModelPropertyTestData
+ {
+ get
+ {
+ foreach (var p in typeof(VehicleViewModel).GetProperties())
+ if (p.CanWrite)
+ switch (p.PropertyType.TC())
+ {
+ case TypeCode.String:
+ yield return new object[] { p.Name, "Null", null!, null! };
+ yield return new object[] { p.Name, "Empty", "", "" };
+ yield return new object[] { p.Name, "Peter", "Peter", "Peter" };
+ yield return new object[] { p.Name, "Müller", "Müller", "Müller" };
+ break;
+ case TypeCode.Int32:
+ yield return new object[] { p.Name, "0", 0, 0 };
+ yield return new object[] { p.Name, "1", 1, 1 };
+ yield return new object[] { p.Name, "-1", -1, -1 };
+ yield return new object[] { p.Name, "MaxInt", int.MaxValue, int.MaxValue };
+ yield return new object[] { p.Name, "MinInt", int.MinValue, int.MinValue };
+ break;
+ case TypeCode.Double:
+ yield return new object[] { p.Name, "0", 0.0d, 0.0d };
+ yield return new object[] { p.Name, "1", 1d, 1d };
+ yield return new object[] { p.Name, "-1", -1d, -1d };
+ yield return new object[] { p.Name, "MaxDouble", double.MaxValue, double.MaxValue };
+ yield return new object[] { p.Name, "MinDouble", double.MinValue, double.MinValue };
+ yield return new object[] { p.Name, "Epsilon", double.Epsilon, double.Epsilon };
+ break;
+ case TypeCode.Object when p.PropertyType == typeof(DateTime?):
+ yield return new object[] { p.Name, "Null", null!, null! };
+ yield return new object[] { p.Name, "0", (DateTime?)new DateTime(1980, 1, 1), (DateTime?)new DateTime(1980, 1, 1) };
+ yield return new object[] { p.Name, "1", (DateTime?)new DateTime(2001, 1, 1), new DateTime(2001, 1, 1) };
+ yield return new object[] { p.Name, "Today", (DateTime?)DateTime.Today, DateTime.Today };
+ yield return new object[] { p.Name, "MaxDate", (DateTime?)DateTime.MaxValue, DateTime.MaxValue };
+ yield return new object[] { p.Name, "MinDate", (DateTime?)DateTime.MinValue, DateTime.MinValue };
+ break;
+ default:
+ yield return new object[] { p.Name, "Null", null!, null! };
+ break;
+ }
+ else if (p.PropertyType == typeof(Double))
+ yield return new object[] { p.Name, "ro", 0d, 0d };
+ else if (p.PropertyType == typeof(Int32))
+ yield return new object[] { p.Name, "ro", 0, 0 };
+ else if (p.PropertyType == typeof(Boolean))
+ yield return new object[] { p.Name, "ro", false, false };
+ //else
+ // yield return new object[] { p.Name, "ro", null!, null! };
+ }
+ }
+ [TestMethod]
+ [DynamicData(nameof(VehicleViewModelPropertyTestData))]
+ public void TestProperties(string sProp, string sName, object oVal, object oExp)
+ {
+ if (oVal is DateTime?)
+ testModel.SetProp(sProp, oVal as DateTime?);
+ else if (sName != "ro")
+ testModel.SetProp(sProp, oVal);
+ Assert.AreEqual(oExp, testModel.GetProp(sProp));
+ }
+ [TestMethod()]
+ [DataRow(nameof(IAGVModel.VehicleDim), new[] { @"PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,VehicleLength)=0
+PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,VehicleWidth)=0
+" })]
+ [DataRow(nameof(IAGVModel.SwivelKoor), new[] { @"PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,SwivelKoorX)=0
+PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,SwivelKoorY)=0
+" })]
+ [DataRow(nameof(IAGVModel.AxisOffset), new[] { @"PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,AxisOffset)=0
+" })]
+ [DataRow(nameof(IAGVModel.AGVVelocity), new[] { @"PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,VehicleVelocityX)=0
+PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,VehicleVelocityY)=0
+PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,AGVVelocity)=( 0, 0)
+" })]
+ [DataRow(nameof(IAGVModel.VehicleRotation), new[] { @"PropChg(MVVM_06_Converters_4.ViewModels.VehicleViewModel,VehicleRotation)=0
+" })]
+ [DataRow(null, new[] { @"" })]
+ public void OnPropertyChangedTest(string sProp, string[] asExp)
+ {
+ _testModel.PropertyChanged += Raise.Event(this, new PropertyChangedEventArgs(sProp));
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ protected override Dictionary GetDefaultData() => new();
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/Converter/WindowPortToGridLinesTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/Converter/WindowPortToGridLinesTests.cs
new file mode 100644
index 000000000..5599c699c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/Converter/WindowPortToGridLinesTests.cs
@@ -0,0 +1,163 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Collections.Generic;
+using MVVM_06_Converters_4.ViewModels;
+using MVVM_06_Converters_4.Model;
+using System.Collections;
+
+namespace MVVM_06_Converters_4.Views.Converter.Tests;
+
+[TestClass()]
+public class WindowPortToGridLinesTests
+{
+ WindowPortToGridLines testVC;
+ SWindowPort wp;
+
+ public static IEnumerable ConvertTestData
+ {
+ get
+ {
+ yield return new object[] { new SWindowPort() { Parent=null!,port=new System.Drawing.RectangleF(-10,-10,20,20) } };
+ yield return new object[] { new SWindowPort() { Parent=null!,port=new System.Drawing.RectangleF(-10,-10,20,20) } };
+ }
+ }
+
+ [TestInitialize]
+ public void TestInit()
+ {
+ testVC = new WindowPortToGridLines();
+ testVC.WindowSize = new System.Windows.Size(200, 100);
+ wp = new SWindowPort() { Parent=null!,port=new System.Drawing.RectangleF(-10,-10,20,20) };
+ }
+
+ [TestMethod()]
+ public void WindowPortToGridLinesTest()
+ {
+ Assert.IsInstanceOfType(testVC, typeof(WindowPortToGridLines));
+ Assert.AreEqual(new System.Windows.Size(200, 100), testVC.WindowSize);
+ Assert.IsInstanceOfType(wp, typeof(SWindowPort));
+ }
+
+ [TestMethod()]
+ [DynamicData(nameof(ConvertTestData))]
+ public void ConvertTest(object o)
+ {
+ var test = testVC.Convert(o, null, null, null);
+ Assert.IsNotNull(test);
+ Assert.IsInstanceOfType(test, typeof(System.Collections.ObjectModel.ObservableCollection));
+ Assert.HasCount(19, test as IList);
+ }
+
+ [TestMethod()]
+ public void GetAdjustedRectTest()
+ {
+ var r2 = testVC.GetAdjustedRect(wp);
+ System.Drawing.RectangleF rExp = new(-20,-10,40,20);
+ Assert.AreEqual(rExp,r2);
+ }
+
+ [TestMethod()]
+ public void ConvertBackTest()
+ {
+ Assert.ThrowsExactly(() => testVC.ConvertBack(null!, null, null, null));
+ }
+
+ private static void RunSTA(Action a)
+ {
+ Exception ex = null;
+ var t = new System.Threading.Thread(() =>
+ {
+ try { a(); }
+ catch (Exception e) { ex = e; }
+ });
+ t.SetApartmentState(System.Threading.ApartmentState.STA);
+ t.Start();
+ t.Join();
+ if (ex != null) throw ex;
+ }
+
+ [TestMethod]
+ [DataRow(200, 100, -10f, -10f, 20f, 20f, -20f, -10f, 40f, 20f)]
+ [DataRow(100, 200, -10f, -10f, 20f, 20f, -10f, -20f, 20f, 40f)]
+ public void GetAdjustedRect_Various_ReturnsExpected(
+ int winW, int winH,
+ float pL, float pT, float pW, float pH,
+ float eL, float eT, float eW, float eH)
+ {
+ RunSTA(() =>
+ {
+ var conv = new WindowPortToGridLines
+ {
+ WindowSize = new System.Windows.Size(winW, winH)
+ };
+ var wp = new SWindowPort
+ {
+ port = new System.Drawing.RectangleF(pL, pT, pW, pH),
+ WindowSize = new System.Windows.Size(winW, winH),
+ Parent = null!
+ };
+ var r2 = conv.GetAdjustedRect(wp);
+ var expected = new System.Drawing.RectangleF(eL, eT, eW, eH);
+ Assert.AreEqual(expected, r2);
+ });
+ }
+
+ [TestMethod]
+ [DataRow(200, 100)]
+ public void Convert_WindowPort_GeneratesGridAndLabels(int winW, int winH)
+ {
+ RunSTA(() =>
+ {
+ var conv = new WindowPortToGridLines
+ {
+ WindowSize = new System.Windows.Size(winW, winH)
+ };
+ var wp = new SWindowPort
+ {
+ port = new System.Drawing.RectangleF(-10, -10, 20, 20),
+ WindowSize = new System.Windows.Size(winW, winH),
+ Parent = null!
+ };
+
+ var result = conv.Convert(wp, null, null, System.Globalization.CultureInfo.InvariantCulture)
+ as System.Collections.ObjectModel.ObservableCollection;
+ Assert.IsNotNull(result);
+ Assert.IsNotEmpty(result, "Es wurden keine Elemente erzeugt.");
+
+ int axisCount = 0, majorCount = 0, minorCount = 0, labelZeroCount = 0, ellipseCount = 0;
+ foreach (var fe in result)
+ {
+ if (fe is System.Windows.Shapes.Line l)
+ {
+ var th = l.StrokeThickness;
+ if (Math.Abs(th - 1.5d) < 1e-6) axisCount++;
+ else if (Math.Abs(th - 0.8d) < 1e-6) majorCount++;
+ else if (Math.Abs(th - 0.3d) < 1e-6) minorCount++;
+ }
+ else if (fe is System.Windows.Controls.Label lab)
+ {
+ if (string.Equals(lab.Content?.ToString(), "0", StringComparison.Ordinal)) labelZeroCount++;
+ }
+ else if (fe is System.Windows.Shapes.Ellipse)
+ {
+ ellipseCount++;
+ }
+ }
+
+ Assert.IsGreaterThanOrEqualTo(2, axisCount, "Achsenlinien (Stärke 1.5) fehlen.");
+ Assert.IsGreaterThan(0, majorCount, "Haupt-Gitternetzlinien (Stärke 0.8) fehlen.");
+ Assert.IsGreaterThan(0, minorCount, "Neben-Gitternetzlinien (Stärke 0.3) fehlen.");
+ Assert.IsGreaterThanOrEqualTo(1, labelZeroCount, "Mindestens eine '0'-Beschriftung wird erwartet.");
+ Assert.AreEqual(1, ellipseCount, "Der Nullpunkt (Ellipse) wird erwartet.");
+ });
+ }
+
+
+ [TestMethod]
+ public void NSubstitute_DummyUsage_ForLibraryPresence()
+ {
+ var d = NSubstitute.Substitute.For();
+ d.Dispose();
+ Assert.IsNotNull(d);
+ }
+ }
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..4dd03c16c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/MainWindowTests.cs
@@ -0,0 +1,42 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters_4Tests
+// Author : Mir
+// Created : 02-03-2024
+//
+// Last Modified By : Mir
+// Last Modified On : 02-03-2024
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+namespace MVVM_06_Converters_4.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+[TestClass()]
+public class MainWindowTests
+{
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/PlotFrameTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/PlotFrameTests.cs
new file mode 100644
index 000000000..e94ea7347
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/PlotFrameTests.cs
@@ -0,0 +1,55 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using BaseLib.Helper;
+using MVVM_06_Converters_4.Model;
+using NSubstitute;
+using System;
+
+namespace MVVM_06_Converters_4.Views.Tests;
+
+[TestClass()]
+public class PlotFrameTests
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ PlotFrame testView;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ private Func _grsOld;
+ private IAGVModel? _model;
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ _grsOld = IoC.GetReqSrv;
+ IoC.GetReqSrv = (t) => t switch
+ {
+ Type _t when _t == typeof(IAGVModel) => _model ??= Substitute.For(),
+ _ => throw new System.NotImplementedException($"No Service for {t}")
+ };
+ testView = new PlotFrame();
+ testView.Visibility = System.Windows.Visibility.Visible;
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ IoC.GetReqSrv = _grsOld;
+ }
+
+ [TestMethod()]
+ public void PlotFrameTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsNotNull(_model);
+ }
+
+ [TestMethod()]
+ public void OnSizeChangeTest()
+ {
+ testView.Height = testView.Height+2;
+ }
+
+ [TestMethod()]
+ public void OnVInitializedTest()
+ {
+ // testView.OnInitialized(;
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/VehicleViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/VehicleViewTests.cs
new file mode 100644
index 000000000..af00eb2a3
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_06_Converters_4Tests/Views/VehicleViewTests.cs
@@ -0,0 +1,52 @@
+// ***********************************************************************
+// Assembly : MVVM_06_Converters_4Tests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 01-28-2024
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Threading;
+using NSubstitute;
+using BaseLib.Helper;
+using MVVM_06_Converters_4.Model;
+
+///
+/// The Tests namespace.
+///
+namespace MVVM_06_Converters_4.Views.Tests;
+
+///
+/// Defines test class VehicleViewTests.
+///
+[TestClass]
+public class VehicleViewTests
+{
+ ///
+ /// Defines the test method VehicleViewTest.
+ ///
+ [TestMethod]
+ public void VehicleViewTest()
+ {
+ IoC.GetReqSrv = (t)=>t switch
+ {
+ _ when t == typeof(IAGVModel) => Substitute.For(),
+ _ => throw new NotImplementedException()
+ };
+ VehicleView1? testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(VehicleView1));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes.sln b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes.sln
new file mode 100644
index 000000000..68a6318a9
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes.sln
@@ -0,0 +1,73 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projektmappenelemente", "Projektmappenelemente", "{658BD492-33FF-4995-AAE7-4F6894E92F0C}"
+ ProjectSection(SolutionItems) = preProject
+ MVVM_Tutorial.props = MVVM_Tutorial.props
+ MVVM_Tutorial_net.props = MVVM_Tutorial_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLib", "..\Libraries\MVVM_BaseLib\MVVM_BaseLib.csproj", "{890CF504-3814-443B-9EE6-E8BCACF68203}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{AF041458-CF94-4D6D-BB0F-8BC50F4F099C}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Libraries\Libraries_net.props = ..\Libraries\Libraries_net.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_BaseLibTests", "..\Libraries\MVVM_BaseLibTests\MVVM_BaseLibTests.csproj", "{66EB60F2-523D-47C8-95EA-C7E10759E239}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_09_DialogBoxes", "MVVM_09_DialogBoxes\MVVM_09_DialogBoxes.csproj", "{26F000B7-BC2E-40B6-B21D-5D70FEA7A1A2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MVVM_09_DialogBoxes", "MVVM_09_DialogBoxes", "{CF6EF320-AC8C-496C-B769-40B1596630E5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_09_DialogBoxes_net", "MVVM_09_DialogBoxes\MVVM_09_DialogBoxes_net.csproj", "{6668264B-B8BC-41AE-A5BA-C79CBF68D246}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_09_DialogBoxes_netTests", "MVVM_09_DialogBoxesTest\MVVM_09_DialogBoxes_netTests.csproj", "{E8264088-F00C-4E8B-851E-A292239B07A4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MVVM_09_DialogBoxesTest", "MVVM_09_DialogBoxesTest\MVVM_09_DialogBoxesTest.csproj", "{DA403752-5030-4BB3-8197-1FB926B240F2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {890CF504-3814-443B-9EE6-E8BCACF68203}.Release|Any CPU.Build.0 = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {66EB60F2-523D-47C8-95EA-C7E10759E239}.Release|Any CPU.Build.0 = Release|Any CPU
+ {26F000B7-BC2E-40B6-B21D-5D70FEA7A1A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {26F000B7-BC2E-40B6-B21D-5D70FEA7A1A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {26F000B7-BC2E-40B6-B21D-5D70FEA7A1A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {26F000B7-BC2E-40B6-B21D-5D70FEA7A1A2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6668264B-B8BC-41AE-A5BA-C79CBF68D246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6668264B-B8BC-41AE-A5BA-C79CBF68D246}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6668264B-B8BC-41AE-A5BA-C79CBF68D246}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6668264B-B8BC-41AE-A5BA-C79CBF68D246}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E8264088-F00C-4E8B-851E-A292239B07A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8264088-F00C-4E8B-851E-A292239B07A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E8264088-F00C-4E8B-851E-A292239B07A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E8264088-F00C-4E8B-851E-A292239B07A4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DA403752-5030-4BB3-8197-1FB926B240F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA403752-5030-4BB3-8197-1FB926B240F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DA403752-5030-4BB3-8197-1FB926B240F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DA403752-5030-4BB3-8197-1FB926B240F2}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {890CF504-3814-443B-9EE6-E8BCACF68203} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {66EB60F2-523D-47C8-95EA-C7E10759E239} = {AF041458-CF94-4D6D-BB0F-8BC50F4F099C}
+ {26F000B7-BC2E-40B6-B21D-5D70FEA7A1A2} = {CF6EF320-AC8C-496C-B769-40B1596630E5}
+ {6668264B-B8BC-41AE-A5BA-C79CBF68D246} = {CF6EF320-AC8C-496C-B769-40B1596630E5}
+ {E8264088-F00C-4E8B-851E-A292239B07A4} = {CF6EF320-AC8C-496C-B769-40B1596630E5}
+ {DA403752-5030-4BB3-8197-1FB926B240F2} = {CF6EF320-AC8C-496C-B769-40B1596630E5}
+ EndGlobalSection
+EndGlobal
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.config b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.config
new file mode 100644
index 000000000..9b6bf3fe6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.xaml
new file mode 100644
index 000000000..f7406565b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.xaml.cs
new file mode 100644
index 000000000..9a119bf4b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_09_DialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 12-29-2021
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_09_DialogBoxes;
+
+///
+/// Interaktionslogik für "App.xaml"
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/MVVM_09_DialogBoxes.csproj b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/MVVM_09_DialogBoxes.csproj
new file mode 100644
index 000000000..5c26a288a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/MVVM_09_DialogBoxes.csproj
@@ -0,0 +1,41 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+ enable
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/MVVM_09_DialogBoxes_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/MVVM_09_DialogBoxes_net.csproj
new file mode 100644
index 000000000..98fb71d72
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/MVVM_09_DialogBoxes_net.csproj
@@ -0,0 +1,37 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..97675dedb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : MVVM_09_DialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 12-29-2021
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("MVVM_09_DialogBoxes")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("MVVM_09_DialogBoxes")]
+[assembly: AssemblyCopyright("Copyright © JC-Soft 2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..686dc47a4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/Resources.Designer.cs
@@ -0,0 +1,157 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_09_DialogBoxes.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_09_DialogBoxes.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Open Dialog ähnelt.
+ ///
+ public static string btnOpenDialog {
+ get {
+ return ResourceManager.GetString("btnOpenDialog", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Open Question ähnelt.
+ ///
+ public static string btnOpenMsg {
+ get {
+ return ResourceManager.GetString("btnOpenMsg", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Tutorial #09: How to display and handle dialog- and message-boxes ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Window x:Class="MVVM_09_DialogBoxes.Views.DialogWindow"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_09_DialogBoxes.Views"
+ /// xmlns:mvvm="clr-namespace:MVVM_09_DialogBoxes.ViewModels"
+ /// mc:Ignorable="d"
+ /// d:DesignHeight= [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DialogWindowView {
+ get {
+ return ResourceManager.GetString("DialogWindowView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_09_DialogBoxes
+ ///// Author : Mir
+ ///// Created : 12-29-2021
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 07-20-2022
+ ///// ***********************************************************************
+ ///// <copyright file="DialogWindow.xaml.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2021
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// **************************************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DialogWindowView_xaml {
+ get {
+ return ResourceManager.GetString("DialogWindowView_xaml", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_09_DialogBoxes
+ ///// Author : Mir
+ ///// Created : 12-29-2021
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 07-20-2022
+ ///// ***********************************************************************
+ ///// <copyright file="DialogWindowViewModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2021
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ************************************************ [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DialogWindowViewModel {
+ get {
+ return ResourceManager.GetString("DialogWindowViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM_#09_DialogBoxes ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/Resources.resx
new file mode 100644
index 000000000..65e48182c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Properties/Resources.resx
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Tutorial #09: How to display and handle dialog- and message-boxes
+
+
+
+ ..\Views\DialogWindow.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\DialogWindowViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\Views\DialogWindow.xaml.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ MVVM_#09_DialogBoxes
+
+
+ Open Dialog
+
+
+ Open Question
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/DialogViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/DialogViewModel.cs
new file mode 100644
index 000000000..ac4ce28ce
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/DialogViewModel.cs
@@ -0,0 +1,114 @@
+// ***********************************************************************
+// Assembly : MVVM_09_DialogBoxes
+// Author : Mir
+// Created : 07-21-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System.Windows;
+
+namespace MVVM_09_DialogBoxes.ViewModels;
+
+///
+/// Class DialogViewModel.
+/// Implements the
+///
+///
+public class DialogViewModel : BaseViewModel
+{
+ ///
+ /// Delegate OpenDialogHandler
+ ///
+ /// The name.
+ /// The email.
+ /// System.ValueTuple<System.String, System.String>.
+ public delegate (string name, string email) OpenDialogHandler(string name, string email);
+
+ ///
+ /// Delegate OpenMessageBoxHandler
+ ///
+ /// The title.
+ /// The name.
+ /// MessageBoxResult.
+ public delegate MessageBoxResult OpenMessageBoxHandler(string title, string name);
+ ///
+ /// Gets or sets the open dialog.
+ ///
+ /// The open dialog.
+ public OpenDialogHandler? DoOpenDialog { get; set; }
+ ///
+ /// Gets or sets the open message box.
+ ///
+ /// The open message box.
+ public OpenMessageBoxHandler? DoOpenMessageBox { get; set; }
+ ///
+ /// Gets or sets the name.
+ ///
+ /// The name.
+ public string Name {
+ get => _name; set {
+ if (SetProperty(ref _name, value)) cnt++;
+ }
+ }
+ ///
+ /// Gets or sets the email.
+ ///
+ /// The email.
+ public string Email { get => _email; set => SetProperty(ref _email, value); }
+
+ public string Cnt => $"Count: {cnt}";
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DialogViewModel()
+ {
+ this.OpenDialogCommand = new DelegateCommand((o) =>
+ {
+ (Name, Email) = DoOpenDialog?.Invoke(this.Name, this.Email)??("","");
+ });
+
+ this.OpenMsgCommand = new DelegateCommand((o) =>
+ {
+ if (this.DoOpenMessageBox?.Invoke("Frage", "Willst Du Das ?") == MessageBoxResult.Yes)
+ {
+ Name = "42 Entwickler";
+ }
+ else
+ {
+ Name = "Nö";
+ }
+ });
+
+ }
+
+ ///
+ /// Gets or sets the open dialog command.
+ ///
+ /// The open dialog command.
+ public DelegateCommand OpenDialogCommand { get; set; }
+ ///
+ /// Gets or sets the open MSG command.
+ ///
+ /// The open MSG command.
+ public DelegateCommand OpenMsgCommand { get; set; }
+
+ ///
+ /// The name
+ ///
+ string _name = "";
+ ///
+ /// The email
+ ///
+ string _email = "";
+ ///
+ /// The count
+ ///
+ private int cnt;
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/DialogWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/DialogWindowViewModel.cs
new file mode 100644
index 000000000..a1f8ed46d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/DialogWindowViewModel.cs
@@ -0,0 +1,80 @@
+// ***********************************************************************
+// Assembly : MVVM_09_DialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 07-20-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System;
+
+namespace MVVM_09_DialogBoxes.ViewModels;
+
+///
+/// Class DialogWindowViewModel.
+/// Implements the
+///
+///
+public class DialogWindowViewModel : BaseViewModel
+{
+ ///
+ /// The name
+ ///
+ private string _name = "";
+ ///
+ /// The email
+ ///
+ private string _email = "";
+
+ ///
+ /// Occurs when [ok].
+ ///
+ public event EventHandler? DoOK;
+ ///
+ /// Occurs when [cancel].
+ ///
+ public event EventHandler? DoCancel;
+ ///
+ /// Gets or sets the ok command.
+ ///
+ /// The ok command.
+ public DelegateCommand OKCommand { get; set; }
+ ///
+ /// Gets or sets the cancel command.
+ ///
+ /// The cancel command.
+ public DelegateCommand CancelCommand { get; set;}
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DialogWindowViewModel()
+ {
+ this.OKCommand = new DelegateCommand((o) =>
+ {
+ this.DoOK?.Invoke(this, EventArgs.Empty);
+ });
+ this.CancelCommand = new DelegateCommand((o) =>
+ {
+ Name = String.Empty;
+ Email = String.Empty;
+ this.DoCancel?.Invoke(this, EventArgs.Empty);
+ });
+ }
+ ///
+ /// Gets or sets the name.
+ ///
+ /// The name.
+ public string Name { get => _name; set => SetProperty(ref _name, value); }
+ ///
+ /// Gets or sets the email.
+ ///
+ /// The email.
+ public string Email { get => _email; set => SetProperty(ref _email, value); }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..0fc09b781
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,29 @@
+// ***********************************************************************
+// Assembly : MVVM_09_DialogBoxes
+// Author : Mir
+// Created : 07-21-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+
+namespace MVVM_09_DialogBoxes.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public class MainWindowViewModel : BaseViewModel
+{
+ public MainWindowViewModel()
+ {
+ ;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogView.xaml
new file mode 100644
index 000000000..106714034
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogView.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogView.xaml.cs
new file mode 100644
index 000000000..65c8f0c26
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogView.xaml.cs
@@ -0,0 +1,44 @@
+using MVVM_09_DialogBoxes.ViewModels;
+using System;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace MVVM_09_DialogBoxes.Views;
+
+///
+/// Interaktionslogik für DialogView.xaml
+///
+public partial class DialogView : Page
+{
+ public Func NewDialogWindow = () => new DialogWindow();
+ public Func MessageBoxShow =
+ (t, n, mbb) => MessageBox.Show(t, n, mbb);
+
+ public DialogView()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Handles the Loaded event of the Window control.
+ ///
+ /// The source of the event.
+ /// The instance containing the event data.
+ public void Dialog_Loaded(object? sender, RoutedEventArgs e)
+ {
+ var vm = (DialogViewModel)DataContext;
+ vm.DoOpenDialog = (Name, email) =>
+ {
+ IDialogWindow dialog = NewDialogWindow();
+ var dialogViewModel = ((DialogWindowViewModel)dialog.DataContext);
+ (dialogViewModel.Name, dialogViewModel.Email) = (Name, email);
+ if (dialog.ShowDialog() == true)
+ {
+ return (dialogViewModel.Name, dialogViewModel.Email);
+ }
+ else
+ return (Name, email);
+ };
+ vm.DoOpenMessageBox = (Title, Name) => MessageBoxShow(Title, Name, MessageBoxButton.YesNo);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogWindow.xaml
new file mode 100644
index 000000000..9165f1975
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogWindow.xaml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogWindow.xaml.cs
new file mode 100644
index 000000000..c6cc77955
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/DialogWindow.xaml.cs
@@ -0,0 +1,48 @@
+// ***********************************************************************
+// Assembly : MVVM_09_DialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 07-20-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM_09_DialogBoxes.ViewModels;
+using System;
+using System.Windows;
+
+namespace MVVM_09_DialogBoxes.Views;
+
+///
+/// Interaktionslogik für DialogWindow.xaml
+///
+public partial class DialogWindow : Window, IDialogWindow
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DialogWindow()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Handles the Loaded event of the Window control.
+ ///
+ /// The source of the event.
+ /// The instance containing the event data.
+ private void Window_Loaded(object? sender, RoutedEventArgs e)
+ {
+ var vm = (DialogWindowViewModel)DataContext;
+ vm.DoCancel += new EventHandler((o,ea) => Hide());
+ vm.DoOK += new EventHandler((o, ea) =>
+ {
+ DialogResult = true;
+ Hide();
+ });
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/IDialogWindow.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/IDialogWindow.cs
new file mode 100644
index 000000000..5531c784c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/IDialogWindow.cs
@@ -0,0 +1,8 @@
+namespace MVVM_09_DialogBoxes.Views;
+
+public interface IDialogWindow
+{
+ object DataContext { get; }
+
+ bool? ShowDialog();
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/MainWindow.xaml
new file mode 100644
index 000000000..cdab450dc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/MainWindow.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/MainWindow.xaml.cs
new file mode 100644
index 000000000..8bf6c45d7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxes/Views/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_09_DialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 07-20-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_09_DialogBoxes.Views;
+
+///
+/// Interaktionslogik für MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/AppTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/AppTests.cs
new file mode 100644
index 000000000..c0b2caa66
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/AppTests.cs
@@ -0,0 +1,53 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using BaseLib.Helper;
+using System;
+
+namespace MVVM_09_DialogBoxes.Tests;
+
+internal class TestApp : App
+{
+ public void DoStartUp()
+ {
+ OnStartup(null);
+ }
+}
+[TestClass()]
+public class AppTests
+{
+ static TestApp app = new();
+ private Func _gsold;
+ private Func _grsold;
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ _gsold = IoC.GetSrv;
+ _grsold = IoC.GetReqSrv;
+ IoC.GetReqSrv = (t) =>t switch {
+ _ => throw new ArgumentException() };
+ }
+
+ [TestCleanup]
+ public void CleanUp()
+ {
+ IoC.GetSrv = _gsold;
+ IoC.GetReqSrv = _grsold;
+ }
+
+ [TestMethod]
+ public void AppTest()
+ {
+ Assert.IsNotNull(app);
+ }
+
+ [TestMethod]
+ public void AppTest2()
+ {
+ app.DoStartUp();
+ Assert.IsNull(IoC.GetSrv(typeof(App)));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/MVVM_09_DialogBoxesTest.csproj b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/MVVM_09_DialogBoxesTest.csproj
index 8925c86db..bebfef9d2 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/MVVM_09_DialogBoxesTest.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/MVVM_09_DialogBoxesTest.csproj
@@ -10,8 +10,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/MVVM_09_DialogBoxes_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/MVVM_09_DialogBoxes_netTests.csproj
index 56bc0c109..502e239ee 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/MVVM_09_DialogBoxes_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/MVVM_09_DialogBoxes_netTests.csproj
@@ -1,17 +1,22 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
false
true
-
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/DialogViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/DialogViewTests.cs
new file mode 100644
index 000000000..7214a38dd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/DialogViewTests.cs
@@ -0,0 +1,134 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using MVVM_09_DialogBoxes.ViewModels;
+using NSubstitute;
+using System.ComponentModel;
+using System.Threading;
+using System.Windows;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_09_DialogBoxes.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+///
+[TestClass()]
+public class DialogViewTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ DialogView testView;
+ DialogViewModel vm;
+ private MessageBoxResult mbResult;
+ private bool? xResult;
+ private string sNewName = "NewName";
+ private string sNewEmail = "NewEmail";
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => {
+ testView = new();
+ vm = (DialogViewModel)testView.DataContext;
+ testView.Dialog_Loaded(this,null!);
+ });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ vm.PropertyChanged += OnVMPropertyChanged;
+ if (vm is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ Assert.IsNotNull(testView.NewDialogWindow);
+ try { Assert.IsNotNull(testView.NewDialogWindow()); }
+ catch { }
+ Assert.IsNotNull(testView.MessageBoxShow);
+ testView.MessageBoxShow = MyMBShow;
+ testView.NewDialogWindow = () =>
+ {
+ var dw = Substitute.For();
+ dw.ShowDialog().Returns((c) =>
+ {
+ DoLog($"ShowDialog()=>{xResult}");
+ var vm = dw.DataContext as DialogWindowViewModel;
+ vm!.Name = sNewName;
+ vm!.Email = sNewEmail;
+ return xResult;
+ });
+ dw.DataContext.Returns(new DialogWindowViewModel());
+ return dw;
+ };
+ }
+
+ private MessageBoxResult MyMBShow(string arg1, string arg2, MessageBoxButton button)
+ {
+ DoLog($"MyMBShow({arg1},{arg2},{button})=>{mbResult}");
+ return mbResult;
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(DialogView));
+ Assert.IsNotNull(vm);
+ Assert.IsInstanceOfType(vm, typeof(DialogViewModel));
+ Assert.IsNotNull(vm.DoOpenDialog);
+ Assert.IsNotNull(vm.DoOpenMessageBox);
+ }
+
+ [TestMethod]
+ [DataRow("OKTitle", "OKLine", MessageBoxResult.OK, new[] { "MyMBShow(OKTitle,OKLine,YesNo)=>OK\r\n" })]
+ [DataRow("CancelTitle", "CancelLine", MessageBoxResult.Cancel, new[] { "MyMBShow(CancelTitle,CancelLine,YesNo)=>Cancel\r\n" })]
+ [DataRow("YesTitle", "YesLine", MessageBoxResult.Yes, new[] { "MyMBShow(YesTitle,YesLine,YesNo)=>Yes\r\n" })]
+ public void DoOpenMessageBoxTest(string sAct1,string sAct2, MessageBoxResult xRes, string[] asExp)
+ {
+ mbResult = xRes;
+ Assert.AreEqual(xRes, vm.DoOpenMessageBox!.Invoke(sAct1, sAct2));
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+
+ [TestMethod]
+ [DataRow("OKName", "OKMail", "OKName1", "OKMail1", true, new[] { "ShowDialog()=>True\r\n", "OKName1", "OKMail1" })]
+ [DataRow("NoName", "NoMail", "NoName1", "NoMail1", false, new[] { "ShowDialog()=>False\r\n", "NoName", "NoMail" })]
+ [DataRow("Name", "Mail", "Name1", "Mail1", null, new[] { "ShowDialog()=>\r\n", "Name", "Mail" })]
+ public void DoDialogWindowTest(string sAct1, string sAct2, string sAct3, string sAct4, bool? xRes, string[] asExp)
+ {
+ xResult = xRes;
+ sNewName = sAct3;
+ sNewEmail = sAct4;
+ var tRes = vm.DoOpenDialog!.Invoke(sAct1, sAct2);
+ Assert.AreEqual(asExp[1], tRes.name);
+ Assert.AreEqual(asExp[2], tRes.email);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/DialogWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/DialogWindowTests.cs
new file mode 100644
index 000000000..4083a795d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/DialogWindowTests.cs
@@ -0,0 +1,110 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using MVVM_09_DialogBoxes.ViewModels;
+using System.ComponentModel;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_09_DialogBoxes.Views.Tests;
+
+///
+/// Defines test class DialogWindowTests.
+///
+///
+[TestClass()]
+public class DialogWindowTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ DialogWindow testView;
+ DialogWindowViewModel vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => { testView = new(); vm = (DialogWindowViewModel)testView.DataContext; testView.Show(); });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ vm.PropertyChanged += OnVMPropertyChanged;
+ if (vm is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ }
+
+ ///
+ /// Defines the test method DialogWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void DialogWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(DialogWindow));
+ Assert.IsNotNull(vm);
+ Assert.IsInstanceOfType(vm, typeof(DialogWindowViewModel));
+ }
+
+ [TestMethod]
+ public void DoCancelTest()
+ {
+ var t = new Thread(() => { testView = new(); vm = (DialogWindowViewModel)testView.DataContext; testView.Show(); vm.CancelCommand.Execute(null); });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsFalse(testView.IsVisible);
+ }
+
+
+ [TestMethod]
+ public void DoOKTest()
+ {
+ bool? xRes = null;
+ bool xVisible = false;
+ var t = new Thread(() =>
+ {
+ testView = new();
+ vm = (DialogWindowViewModel)testView.DataContext;
+ ExecOK();
+ xRes = testView.ShowDialog();
+ // xResult = testView.ShowDialog();
+ xVisible = testView.IsVisible;
+
+ async void ExecOK()
+ {
+ await System.Threading.Tasks.Task.Delay(50);
+ testView.Dispatcher.Invoke(() => vm.OKCommand.Execute(null));
+ }
+
+ });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsFalse(xVisible);
+ Assert.IsTrue(xRes.HasValue);
+ Assert.IsTrue(xRes.Value);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/MainWindowTests.cs
new file mode 100644
index 000000000..14e7b6f7a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/View/MainWindowTests.cs
@@ -0,0 +1,76 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using MVVM_09_DialogBoxes.ViewModels;
+using System.ComponentModel;
+using System.Threading;
+using System.Windows;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_09_DialogBoxes.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+///
+[TestClass()]
+public class MainWindowTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ MainWindow testView;
+ MainWindowViewModel vm;
+ private MessageBoxResult mbResult;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => {
+ testView = new();
+ vm = (MainWindowViewModel)testView.DataContext;
+// testView.Window_Loaded(this,null!);
+ });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ vm.PropertyChanged += OnVMPropertyChanged;
+ if (vm is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ Assert.IsNotNull(vm);
+ Assert.IsInstanceOfType(vm, typeof(MainWindowViewModel));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/DialogViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/DialogViewModelTests.cs
new file mode 100644
index 000000000..add2d7343
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/DialogViewModelTests.cs
@@ -0,0 +1,123 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using System.Windows;
+
+namespace MVVM_09_DialogBoxes.ViewModels.Tests;
+
+[TestClass()]
+public class DialogViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ DialogViewModel testModel;
+ System.Windows.MessageBoxResult mbResult;
+ (string name, string email) tResult;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ testModel.DoOpenDialog += DoOpenDialogTest;
+ testModel.DoOpenMessageBox += DoOpenMessageBoxTest;
+ ClearLog();
+ }
+
+ private System.Windows.MessageBoxResult DoOpenMessageBoxTest(string title, string name)
+ {
+ DoLog($"DoOpenMessageBox({title},{name})=>{mbResult}");
+ return mbResult;
+ }
+
+ private (string name, string email) DoOpenDialogTest(string name, string email)
+ {
+ DoLog($"DoOpenMessageBox({name},{email})=>{tResult}");
+ return tResult;
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(DialogViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ Assert.IsNotNull(testModel.OpenMsgCommand);
+ Assert.IsInstanceOfType(testModel.OpenMsgCommand, typeof(IRelayCommand));
+ Assert.IsNotNull(testModel.OpenDialogCommand);
+ Assert.IsInstanceOfType(testModel.OpenDialogCommand, typeof(IRelayCommand));
+ }
+
+ [TestMethod()]
+ [DataRow(true,MessageBoxResult.Yes, new[] { @"DoOpenMessageBox(Frage,Willst Du Das ?)=>Yes
+PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Name)=42 Entwickler
+", "42 Entwickler" })]
+ [DataRow(false, MessageBoxResult.OK, new[] { @"PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Name)=Nö
+", "Nö" })]
+ [DataRow(null, MessageBoxResult.No, new[] { @"DoOpenMessageBox(Frage,Willst Du Das ?)=>No
+PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Name)=Nö
+", "Nö" })]
+ public void OpenMsgCommandTest(bool? oAct,MessageBoxResult mrExp, string[] asExp)
+ {
+ // Arrange
+ testModel.Name = $"TestName{oAct}";
+ if (oAct == false) testModel.DoOpenMessageBox -= DoOpenMessageBoxTest;
+ mbResult = mrExp;
+ ClearLog();
+
+ // Act
+ Assert.IsTrue(testModel.OpenMsgCommand.CanExecute(oAct));
+ testModel.OpenMsgCommand.Execute(oAct);
+
+ // Assert
+ Assert.AreEqual(asExp[1], testModel.Name);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ [TestMethod()]
+ [DataRow(true,"0", new[] { @"DoOpenMessageBox(TestNameTrue,TestEmailTrue)=>(TestName0, TestMail0)
+PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Name)=TestName0
+PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Email)=TestMail0
+", "TestName0", "TestMail0" })]
+ [DataRow(false, "1", new[] { @"PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Name)=
+PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Email)=
+", "","" })]
+ [DataRow(null, "2", new[] { @"DoOpenMessageBox(TestName,TestEmail)=>(TestName2, TestMail2)
+PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Name)=TestName2
+PropChg(MVVM_09_DialogBoxes.ViewModels.DialogViewModel,Email)=TestMail2
+", "TestName2", "TestMail2" })]
+ public void OpenDialogCommandTest(bool? oAct,string sAct, string[] asExp)
+ {
+ // Arrange
+ testModel.Name = $"TestName{oAct}";
+ testModel.Email = $"TestEmail{oAct}";
+ tResult = ($"TestName{sAct}", $"TestMail{sAct}");
+ if (oAct == false) testModel.DoOpenDialog -= DoOpenDialogTest;
+ ClearLog();
+
+ // Act
+ Assert.IsTrue(testModel.OpenDialogCommand.CanExecute(oAct));
+ testModel.OpenDialogCommand.Execute(oAct);
+
+ // Assert
+ Assert.AreEqual(asExp[1], testModel.Name);
+ Assert.AreEqual(asExp[2], testModel.Email);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/DialogWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/DialogWindowViewModelTests.cs
new file mode 100644
index 000000000..546412ef5
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/DialogWindowViewModelTests.cs
@@ -0,0 +1,84 @@
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace MVVM_09_DialogBoxes.ViewModels.Tests;
+
+[TestClass()]
+public class DialogWindowViewModelTests : BaseTestViewModel
+{
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public override void Init()
+ {
+ base.Init();
+ testModel.DoCancel += DoCancelTest;
+ testModel.DoOK += DoOKTest;
+ }
+
+ private void DoOKTest(object? o, EventArgs e)
+ {
+ DoLog($"DoOK({o},{e})");
+ }
+
+ private void DoCancelTest(object? o, EventArgs e)
+ {
+ DoLog($"DoCancel({o},{e})");
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsNotNull(testModel2);
+ Assert.IsInstanceOfType(testModel, typeof(DialogWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ Assert.IsNotNull(testModel.CancelCommand);
+ Assert.IsInstanceOfType(testModel.CancelCommand, typeof(IRelayCommand));
+ Assert.IsNotNull(testModel.OKCommand);
+ Assert.IsInstanceOfType(testModel.OKCommand, typeof(IRelayCommand));
+ }
+
+ [TestMethod()]
+ [DataRow(true, new[] { @"DoCancel(MVVM_09_DialogBoxes.ViewModels.DialogWindowViewModel,System.EventArgs)
+" })]
+ [DataRow(false, new[] { @"" })]
+ [DataRow(null, new[] { @"DoCancel(MVVM_09_DialogBoxes.ViewModels.DialogWindowViewModel,System.EventArgs)
+" })]
+ public void CancelCommandTest(bool? oAct, string[] asExp)
+ {
+ Assert.IsTrue(testModel.CancelCommand.CanExecute(oAct));
+ if (oAct == false) testModel.DoCancel -= DoCancelTest;
+ testModel.CancelCommand.Execute(oAct);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ [TestMethod()]
+ [DataRow(true, new[] { @"DoOK(MVVM_09_DialogBoxes.ViewModels.DialogWindowViewModel,System.EventArgs)
+" })]
+ [DataRow(false, new[] { @"" })]
+ [DataRow(null, new[] { @"DoOK(MVVM_09_DialogBoxes.ViewModels.DialogWindowViewModel,System.EventArgs)
+" })]
+ public void OKCommandTest(bool? oAct, string[] asExp)
+ {
+ Assert.IsTrue(testModel.OKCommand.CanExecute(oAct));
+ if (oAct == false) testModel.DoOK -= DoOKTest;
+ testModel.OKCommand.Execute(oAct);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ protected override Dictionary GetDefaultData()
+ => new() { {nameof(DialogWindowViewModel.Email),"" }, { nameof(DialogWindowViewModel.Name), "" }, };
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..50384052c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09_DialogBoxesTest/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,46 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System.ComponentModel;
+
+namespace MVVM_09_DialogBoxes.ViewModels.Tests;
+
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+// System.Windows.MessageBoxResult mbResult;
+// (string name, string email) tResult;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ ClearLog();
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.config b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.config
new file mode 100644
index 000000000..9b6bf3fe6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.xaml
new file mode 100644
index 000000000..9c8a57f87
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.xaml.cs
new file mode 100644
index 000000000..2b20f5178
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_09a_CTDialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 12-29-2021
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_09a_CTDialogBoxes;
+
+///
+/// Interaktionslogik für "App.xaml"
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/MVVM_09a_CTDialogBoxes.csproj b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/MVVM_09a_CTDialogBoxes.csproj
new file mode 100644
index 000000000..df499f041
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/MVVM_09a_CTDialogBoxes.csproj
@@ -0,0 +1,44 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+ enable
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ Code
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/MVVM_09a_CTDialogBoxes_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/MVVM_09a_CTDialogBoxes_net.csproj
new file mode 100644
index 000000000..fc893494c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/MVVM_09a_CTDialogBoxes_net.csproj
@@ -0,0 +1,24 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..366dd6da1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : MVVM_09a_CTDialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 12-29-2021
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("MVVM_09a_CTDialogBoxes")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JC-Soft")]
+[assembly: AssemblyProduct("MVVM_09a_CTDialogBoxes")]
+[assembly: AssemblyCopyright("Copyright © JC-Soft 2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
+ // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
+)]
+
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..f59f5eadf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/Resources.Designer.cs
@@ -0,0 +1,163 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_09a_CTDialogBoxes.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_09a_CTDialogBoxes.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Open Dialog ähnelt.
+ ///
+ public static string btnOpenDialog {
+ get {
+ return ResourceManager.GetString("btnOpenDialog", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Open Question ähnelt.
+ ///
+ public static string btnOpenMsg {
+ get {
+ return ResourceManager.GetString("btnOpenMsg", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Tutorial #09: Handling of dialogboxes using Comunity-Toolkit ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page x:Class="MVVM_09a_CTDialogBoxes.Views.DialogView"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:local="clr-namespace:MVVM_09a_CTDialogBoxes.Views"
+ /// xmlns:p="clr-namespace:MVVM_09a_CTDialogBoxes.Properties"
+ /// xmlns:mvvm="clr-namespace:MVVM_09a_CT [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DialogView {
+ get {
+ return ResourceManager.GetString("DialogView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die using System;
+ ///using System.Collections.Generic;
+ ///using System.Linq;
+ ///using System.Text;
+ ///using System.Threading.Tasks;
+ ///using System.Windows;
+ ///using System.Windows.Controls;
+ ///using System.Windows.Data;
+ ///using System.Windows.Documents;
+ ///using System.Windows.Input;
+ ///using System.Windows.Media;
+ ///using System.Windows.Media.Imaging;
+ ///using System.Windows.Navigation;
+ ///using System.Windows.Shapes;
+ ///
+ ///namespace MVVM_09a_CTDialogBoxes.Views
+ ///{
+ /// /// <summary>
+ /// /// Interaktionslogik für DialogView.xaml
+ /// / [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DialogView_xaml {
+ get {
+ return ResourceManager.GetString("DialogView_xaml", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_09_DialogBoxes
+ ///// Author : Mir
+ ///// Created : 07-21-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 08-09-2022
+ ///// ***********************************************************************
+ ///// <copyright file="DialogViewModel.cs" company="JC-Soft">
+ ///// Copyright © JC-Soft 2021
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ***************************************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DialogViewModel {
+ get {
+ return ResourceManager.GetString("DialogViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM #09a CTDialogBoxes ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/Resources.resx
new file mode 100644
index 000000000..e594c9bd7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Properties/Resources.resx
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Open Dialog
+
+
+ Open Question
+
+
+ Tutorial #09: Handling of dialogboxes using Comunity-Toolkit
+
+
+
+ ..\Views\DialogView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\DialogViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\Views\DialogView.xaml.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ MVVM #09a CTDialogBoxes
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/DialogViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/DialogViewModel.cs
new file mode 100644
index 000000000..1aad989d6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/DialogViewModel.cs
@@ -0,0 +1,99 @@
+// ***********************************************************************
+// Assembly : MVVM_09_DialogBoxes
+// Author : Mir
+// Created : 07-21-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using MVVM.ViewModel;
+using System.Windows;
+
+namespace MVVM_09a_CTDialogBoxes.ViewModels;
+
+public partial class DialogViewModel : BaseViewModelCT
+{
+ #region Delegates
+ ///
+ /// Delegate OpenDialogHandler
+ ///
+ /// The name.
+ /// The email.
+ /// System.ValueTuple<System.String, System.String>.
+ public delegate (string name, string email) OpenDialogHandler(string name, string email);
+
+ ///
+ /// Delegate OpenMessageBoxHandler
+ ///
+ /// The title.
+ /// The name.
+ /// MessageBoxResult.
+ public delegate MessageBoxResult OpenMessageBoxHandler(string title, string name);
+ #endregion
+ #region Properties
+ ///
+ /// The name
+ ///
+ [ObservableProperty]
+ string _name = "";
+ ///
+ /// The email
+ ///
+ [ObservableProperty]
+ string _email = "";
+ ///
+ /// The count
+ ///
+ [ObservableProperty]
+ private int cnt = 1;
+
+ ///
+ /// Gets or sets the open dialog.
+ ///
+ /// The open dialog.
+ public OpenDialogHandler? DoOpenDialog { get; set; }
+ ///
+ /// Gets or sets the open message box.
+ ///
+ /// The open message box.
+ public OpenMessageBoxHandler? DoOpenMessageBox { get; set; }
+ #endregion
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DialogViewModel()
+ {
+ }
+
+ [RelayCommand]
+ private void OpenMsg()
+ {
+ if (this.DoOpenMessageBox?.Invoke("Frage", "Willst Du Das ?") == MessageBoxResult.Yes)
+ {
+ Name = "42 Entwickler";
+ }
+ else
+ {
+ Name = "Nö";
+ }
+ }
+ [RelayCommand]
+ private void OpenDialog()
+ {
+ (Name, Email) = DoOpenDialog?.Invoke(this.Name, this.Email) ?? ("", "");
+ }
+
+ partial void OnNameChanged(string value)
+ {
+ Cnt++;
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/DialogWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/DialogWindowViewModel.cs
new file mode 100644
index 000000000..508d234ba
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/DialogWindowViewModel.cs
@@ -0,0 +1,68 @@
+// ***********************************************************************
+// Assembly : MVVM_09a_CTDialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 07-20-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using System;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+
+namespace MVVM_09a_CTDialogBoxes.ViewModels;
+
+///
+/// Class DialogWindowViewModel.
+/// Implements the
+///
+///
+public partial class DialogWindowViewModel : BaseViewModelCT
+{
+ ///
+ /// The name
+ ///
+ [ObservableProperty]
+ private string _name = "";
+ ///
+ /// The email
+ ///
+ [ObservableProperty]
+ private string _email = "";
+
+ ///
+ /// Occurs when [ok].
+ ///
+ public event EventHandler DoOK;
+ ///
+ /// Occurs when [cancel].
+ ///
+ public event EventHandler DoCancel;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DialogWindowViewModel()
+ {
+ }
+
+ [RelayCommand]
+ private void Cancel()
+ {
+ Name = String.Empty;
+ Email = String.Empty;
+ this.DoCancel?.Invoke(this, EventArgs.Empty);
+ }
+
+ [RelayCommand]
+ private void OK()
+ {
+ this.DoOK?.Invoke(this, EventArgs.Empty);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..a77d75cc2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,104 @@
+// ***********************************************************************
+// Assembly : MVVM_09a_CTDialogBoxes
+// Author : Mir
+// Created : 07-21-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-09-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using MVVM.ViewModel;
+using System.Windows;
+
+namespace MVVM_09a_CTDialogBoxes.ViewModels;
+
+///
+/// Class MainWindowViewModel.
+/// Implements the
+///
+///
+public partial class MainWindowViewModel : BaseViewModelCT
+{
+ #region Delegates
+ ///
+ /// Delegate OpenDialogHandler
+ ///
+ /// The name.
+ /// The email.
+ /// System.ValueTuple<System.String, System.String>.
+ public delegate (string name, string email) OpenDialogHandler(string name, string email);
+
+ ///
+ /// Delegate OpenMessageBoxHandler
+ ///
+ /// The title.
+ /// The name.
+ /// MessageBoxResult.
+ public delegate MessageBoxResult OpenMessageBoxHandler(string title, string name);
+ #endregion
+ #region Properties
+ ///
+ /// The name
+ ///
+ [ObservableProperty]
+ string _name = "";
+ ///
+ /// The email
+ ///
+ [ObservableProperty]
+ string _email = "";
+ ///
+ /// The count
+ ///
+ [ObservableProperty]
+ private int cnt = 1;
+
+ ///
+ /// Gets or sets the open dialog.
+ ///
+ /// The open dialog.
+ public OpenDialogHandler? DoOpenDialog { get; set; }
+ ///
+ /// Gets or sets the open message box.
+ ///
+ /// The open message box.
+ public OpenMessageBoxHandler? DoOpenMessageBox { get; set; }
+ #endregion
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindowViewModel()
+ {
+ }
+
+ [RelayCommand]
+ private void OpenMsg()
+ {
+ if (this.DoOpenMessageBox?.Invoke("Frage", "Willst Du Das ?") == MessageBoxResult.Yes)
+ {
+ Name = "42 Entwickler";
+ }
+ else
+ {
+ Name = "Nö";
+ }
+ }
+ [RelayCommand]
+ private void OpenDialog()
+ {
+ (Name, Email) = DoOpenDialog?.Invoke(this.Name, this.Email) ?? ("", "");
+ }
+
+ partial void OnNameChanged(string value)
+ {
+ Cnt++;
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogView.xaml
new file mode 100644
index 000000000..5ca4987bc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogView.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogView.xaml.cs
new file mode 100644
index 000000000..e320379ea
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogView.xaml.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using MVVM_09a_CTDialogBoxes.ViewModels;
+
+namespace MVVM_09a_CTDialogBoxes.Views;
+
+///
+/// Interaktionslogik für DialogView.xaml
+///
+public partial class DialogView : Page
+{
+ public Func NewDialogWindow = () => new DialogWindow();
+ public Func MessageBoxShow =
+ (t, n, mbb) => MessageBox.Show(t, n, mbb);
+
+ public DialogView()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Handles the Loaded event of the Window control.
+ ///
+ /// The source of the event.
+ /// The instance containing the event data.
+ public void Dialog_Loaded(object? sender, RoutedEventArgs e)
+ {
+ var vm = (DialogViewModel)DataContext;
+ vm.DoOpenDialog = (Name, email) =>
+ {
+ IDialogWindow dialog = NewDialogWindow();
+ var dialogViewModel = (DialogWindowViewModel)dialog.DataContext;
+ (dialogViewModel.Name, dialogViewModel.Email) = (Name, email);
+ if (dialog.ShowDialog() == true)
+ {
+ return (dialogViewModel.Name, dialogViewModel.Email);
+ }
+ else
+ return (Name, email);
+ };
+ vm.DoOpenMessageBox = (Title, Name) => MessageBoxShow(Title, Name, MessageBoxButton.YesNo);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogWindow.xaml
new file mode 100644
index 000000000..e725d4c6f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogWindow.xaml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogWindow.xaml.cs
new file mode 100644
index 000000000..da337a306
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/DialogWindow.xaml.cs
@@ -0,0 +1,55 @@
+// ***********************************************************************
+// Assembly : MVVM_09a_CTDialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 07-20-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM_09a_CTDialogBoxes.ViewModels;
+using System;
+using System.Windows;
+
+namespace MVVM_09a_CTDialogBoxes.Views;
+
+///
+/// Interaktionslogik für DialogWindow.xaml
+///
+public partial class DialogWindow : Window, IDialogWindow
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DialogWindow()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Handles the Loaded event of the Window control.
+ ///
+ /// The source of the event.
+ /// The instance containing the event data.
+ private void Window_Loaded(object? sender, RoutedEventArgs e)
+ {
+ var vm = (DialogWindowViewModel)DataContext;
+ vm.DoCancel += DoCancel;
+ vm.DoOK += DoOK;
+ }
+
+ private void DoOK(object? sender,EventArgs e)
+ {
+ DialogResult = true;
+ Hide();
+ }
+
+ private void DoCancel(object? sender, EventArgs e)
+ {
+ Hide();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/IDialogWindow.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/IDialogWindow.cs
new file mode 100644
index 000000000..a915ab8c6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/IDialogWindow.cs
@@ -0,0 +1,8 @@
+namespace MVVM_09a_CTDialogBoxes.Views;
+
+public interface IDialogWindow
+{
+ object DataContext { get; }
+
+ bool? ShowDialog();
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/MainWindow.xaml
new file mode 100644
index 000000000..e2b2acf10
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/MainWindow.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/MainWindow.xaml.cs
new file mode 100644
index 000000000..5c54562eb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxes/Views/MainWindow.xaml.cs
@@ -0,0 +1,63 @@
+// ***********************************************************************
+// Assembly : MVVM_09a_CTDialogBoxes
+// Author : Mir
+// Created : 12-29-2021
+//
+// Last Modified By : Mir
+// Last Modified On : 07-20-2022
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2021
+//
+//
+// ***********************************************************************
+using MVVM_09a_CTDialogBoxes.ViewModels;
+using System;
+using System.Windows;
+
+namespace MVVM_09a_CTDialogBoxes.Views;
+
+///
+/// Interaktionslogik für MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ public Func NewDialogWindow = () => new DialogWindow();
+ public Func MessageBoxShow =
+ (t, n,mbb) => MessageBox.Show(t, n, mbb);
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Handles the Loaded event of the Window control.
+ ///
+ /// The source of the event.
+ /// The instance containing the event data.
+ public void Window_Loaded(object? sender, RoutedEventArgs e)
+ {
+ var vm = (MainWindowViewModel)DataContext;
+ vm.DoOpenDialog = DoOpenDialog;
+ vm.DoOpenMessageBox = DoOpenMessageBox;
+ }
+
+ private MessageBoxResult DoOpenMessageBox(string Title, string Name)
+ => MessageBoxShow(Title, Name, MessageBoxButton.YesNo);
+
+ private (string name, string email) DoOpenDialog(string Name, string email)
+ {
+ IDialogWindow dialog = NewDialogWindow();
+ var dialogViewModel = (DialogWindowViewModel)dialog.DataContext;
+ (dialogViewModel.Name, dialogViewModel.Email) = (Name, email);
+ if (dialog.ShowDialog() == true)
+ {
+ return (dialogViewModel.Name, dialogViewModel.Email);
+ }
+ else
+ return (Name, email);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/AppTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/AppTests.cs
new file mode 100644
index 000000000..27a0bc843
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/AppTests.cs
@@ -0,0 +1,53 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using BaseLib.Helper;
+using System;
+
+namespace MVVM_09a_CTDialogBoxes.Tests;
+
+internal class TestApp : App
+{
+ public void DoStartUp()
+ {
+ OnStartup(null);
+ }
+}
+[TestClass()]
+public class AppTests
+{
+ static TestApp app = new();
+ private Func _gsold;
+ private Func _grsold;
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ _gsold = IoC.GetSrv;
+ _grsold = IoC.GetReqSrv;
+ IoC.GetReqSrv = (t) =>t switch {
+ _ => throw new ArgumentException() };
+ }
+
+ [TestCleanup]
+ public void CleanUp()
+ {
+ IoC.GetSrv = _gsold;
+ IoC.GetReqSrv = _grsold;
+ }
+
+ [TestMethod]
+ public void AppTest()
+ {
+ Assert.IsNotNull(app);
+ }
+
+ [TestMethod]
+ public void AppTest2()
+ {
+ app.DoStartUp();
+ Assert.IsNull(IoC.GetSrv(typeof(App)));
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/MVVM_09a_CTDialogBoxesTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/MVVM_09a_CTDialogBoxesTests.csproj
index 88195f5d0..b30706aa2 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/MVVM_09a_CTDialogBoxesTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/MVVM_09a_CTDialogBoxesTests.csproj
@@ -10,8 +10,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/MVVM_09a_CTDialogBoxes_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/MVVM_09a_CTDialogBoxes_netTests.csproj
index cf3b5bedd..69672d00e 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/MVVM_09a_CTDialogBoxes_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/MVVM_09a_CTDialogBoxes_netTests.csproj
@@ -1,17 +1,22 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
false
true
-
-
-
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/DialogViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/DialogViewModelTests.cs
new file mode 100644
index 000000000..4829e9e77
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/DialogViewModelTests.cs
@@ -0,0 +1,192 @@
+// ***********************************************************************
+// Assembly : MVVM_09a_CTDialogBoxesTests
+// Author : Mir
+// Created : 06-16-2024
+//
+// Last Modified By : Mir
+// Last Modified On : 06-16-2024
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using System.Windows;
+
+///
+/// The Tests namespace.
+///
+namespace MVVM_09a_CTDialogBoxes.ViewModels.Tests;
+
+///
+/// Defines test class DialogViewModelTests.
+/// Implements the
+///
+///
+[TestClass()]
+public class DialogViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ DialogViewModel testModel;
+ ///
+ /// The mb result
+ ///
+ System.Windows.MessageBoxResult mbResult;
+ ///
+ /// The t result
+ ///
+ (string name, string email) tResult;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ testModel.DoOpenDialog += DoOpenDialogTest;
+ testModel.DoOpenMessageBox += DoOpenMessageBoxTest;
+ ClearLog();
+ }
+
+ ///
+ /// Does the open message box test.
+ ///
+ /// The title.
+ /// The name.
+ /// System.Windows.MessageBoxResult.
+ private System.Windows.MessageBoxResult DoOpenMessageBoxTest(string title, string name)
+ {
+ DoLog($"DoOpenMessageBox({title},{name})=>{mbResult}");
+ return mbResult;
+ }
+
+ ///
+ /// Does the open dialog test.
+ ///
+ /// The name.
+ /// The email.
+ /// System.ValueTuple<System.String, System.String>.
+ private (string name, string email) DoOpenDialogTest(string name, string email)
+ {
+ DoLog($"DoOpenMessageBox({name},{email})=>{tResult}");
+ return tResult;
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(DialogViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ Assert.IsNotNull(testModel.OpenMsgCommand);
+ Assert.IsInstanceOfType(testModel.OpenMsgCommand, typeof(IRelayCommand));
+ Assert.IsNotNull(testModel.OpenDialogCommand);
+ Assert.IsInstanceOfType(testModel.OpenDialogCommand, typeof(IRelayCommand));
+ }
+
+ ///
+ /// Opens the MSG command test.
+ ///
+ /// if set to true [o act].
+ /// The mr exp.
+ /// As exp.
+ [TestMethod()]
+ [DataRow(true,MessageBoxResult.Yes, new[] { @"DoOpenMessageBox(Frage,Willst Du Das ?)=>Yes
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=TestNameTrue
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=42 Entwickler
+", "42 Entwickler" })]
+ [DataRow(false, MessageBoxResult.OK, new[] { @"PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=TestNameFalse
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=Nö
+", "Nö" })]
+ [DataRow(null, MessageBoxResult.No, new[] { @"DoOpenMessageBox(Frage,Willst Du Das ?)=>No
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=TestName
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=Nö
+", "Nö" })]
+ public void OpenMsgCommandTest(bool? oAct,MessageBoxResult mrExp, string[] asExp)
+ {
+ // Arrange
+ testModel.Name = $"TestName{oAct}";
+ if (oAct == false) testModel.DoOpenMessageBox -= DoOpenMessageBoxTest;
+ mbResult = mrExp;
+ ClearLog();
+
+ // Act
+ Assert.IsTrue(testModel.OpenMsgCommand.CanExecute(oAct));
+ testModel.OpenMsgCommand.Execute(oAct);
+
+ // Assert
+ Assert.AreEqual(asExp[1], testModel.Name);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ ///
+ /// Opens the dialog command test.
+ ///
+ /// if set to true [o act].
+ /// The s act.
+ /// As exp.
+ [TestMethod()]
+ [DataRow(true,"0", new[] { @"DoOpenMessageBox(TestNameTrue,TestEmailTrue)=>(TestName0, TestMail0)
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=TestNameTrue
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=TestName0
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Email)=TestEmailTrue
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Email)=TestMail0
+", "TestName0", "TestMail0" })]
+ [DataRow(false, "1", new[] { @"PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=TestNameFalse
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Email)=TestEmailFalse
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Email)=
+", "","" })]
+ [DataRow(null, "2", new[] { @"DoOpenMessageBox(TestName,TestEmail)=>(TestName2, TestMail2)
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=TestName
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Name)=TestName2
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Email)=TestEmail
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.DialogViewModel,Email)=TestMail2
+", "TestName2", "TestMail2" })]
+ public void OpenDialogCommandTest(bool? oAct,string sAct, string[] asExp)
+ {
+ // Arrange
+ testModel.Name = $"TestName{oAct}";
+ testModel.Email = $"TestEmail{oAct}";
+ tResult = ($"TestName{sAct}", $"TestMail{sAct}");
+ if (oAct == false) testModel.DoOpenDialog -= DoOpenDialogTest;
+ ClearLog();
+
+ // Act
+ Assert.IsTrue(testModel.OpenDialogCommand.CanExecute(oAct));
+ testModel.OpenDialogCommand.Execute(oAct);
+
+ // Assert
+ Assert.AreEqual(asExp[1], testModel.Name);
+ Assert.AreEqual(asExp[2], testModel.Email);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/DialogWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/DialogWindowViewModelTests.cs
new file mode 100644
index 000000000..8f0d88d0f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/DialogWindowViewModelTests.cs
@@ -0,0 +1,90 @@
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System;
+using System.ComponentModel;
+
+namespace MVVM_09a_CTDialogBoxes.ViewModels.Tests;
+
+[TestClass()]
+public class DialogWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ DialogWindowViewModel testModel;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ testModel.DoCancel += DoCancelTest;
+ testModel.DoOK += DoOKTest;
+ ClearLog();
+ }
+
+ private void DoOKTest(object o, EventArgs e)
+ {
+ DoLog($"DoOK({o},{e})");
+ }
+
+ private void DoCancelTest(object o, EventArgs e)
+ {
+ DoLog($"DoCancel({o},{e})");
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(DialogWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ Assert.IsNotNull(testModel.CancelCommand);
+ Assert.IsInstanceOfType(testModel.CancelCommand, typeof(IRelayCommand));
+ Assert.IsNotNull(testModel.OKCommand);
+ Assert.IsInstanceOfType(testModel.OKCommand, typeof(IRelayCommand));
+ }
+
+ [TestMethod()]
+ [DataRow(true, new[] { @"DoCancel(MVVM_09a_CTDialogBoxes.ViewModels.DialogWindowViewModel,System.EventArgs)
+" })]
+ [DataRow(false, new[] { @"" })]
+ [DataRow(null, new[] { @"DoCancel(MVVM_09a_CTDialogBoxes.ViewModels.DialogWindowViewModel,System.EventArgs)
+" })]
+ public void CancelCommandTest(bool? oAct, string[] asExp)
+ {
+ Assert.IsTrue(testModel.CancelCommand.CanExecute(oAct));
+ if (oAct == false) testModel.DoCancel -= DoCancelTest;
+ testModel.CancelCommand.Execute(oAct);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ [TestMethod()]
+ [DataRow(true, new[] { @"DoOK(MVVM_09a_CTDialogBoxes.ViewModels.DialogWindowViewModel,System.EventArgs)
+" })]
+ [DataRow(false, new[] { @"" })]
+ [DataRow(null, new[] { @"DoOK(MVVM_09a_CTDialogBoxes.ViewModels.DialogWindowViewModel,System.EventArgs)
+" })]
+ public void OKCommandTest(bool? oAct, string[] asExp)
+ {
+ Assert.IsTrue(testModel.OKCommand.CanExecute(oAct));
+ if (oAct == false) testModel.DoOK -= DoOKTest;
+ testModel.OKCommand.Execute(oAct);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..fb9b96e25
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,144 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using System.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using System.Windows;
+
+namespace MVVM_09a_CTDialogBoxes.ViewModels.Tests;
+
+[TestClass()]
+public class MainWindowViewModelTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test model
+ ///
+ ///
+ MainWindowViewModel testModel;
+ System.Windows.MessageBoxResult mbResult;
+ (string name, string email) tResult;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ testModel.PropertyChanged += OnVMPropertyChanged;
+ if (testModel is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ testModel.DoOpenDialog += DoOpenDialogTest;
+ testModel.DoOpenMessageBox += DoOpenMessageBoxTest;
+ ClearLog();
+ }
+
+ private System.Windows.MessageBoxResult DoOpenMessageBoxTest(string title, string name)
+ {
+ DoLog($"DoOpenMessageBox({title},{name})=>{mbResult}");
+ return mbResult;
+ }
+
+ private (string name, string email) DoOpenDialogTest(string name, string email)
+ {
+ DoLog($"DoOpenMessageBox({name},{email})=>{tResult}");
+ return tResult;
+ }
+
+ ///
+ /// Defines the test method SetupTest.
+ ///
+ ///
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModelCT));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ Assert.IsNotNull(testModel.OpenMsgCommand);
+ Assert.IsInstanceOfType(testModel.OpenMsgCommand, typeof(IRelayCommand));
+ Assert.IsNotNull(testModel.OpenDialogCommand);
+ Assert.IsInstanceOfType(testModel.OpenDialogCommand, typeof(IRelayCommand));
+ }
+
+ [TestMethod()]
+ [DataRow(true,MessageBoxResult.Yes, new[] { @"DoOpenMessageBox(Frage,Willst Du Das ?)=>Yes
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=TestNameTrue
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=42 Entwickler
+", "42 Entwickler" })]
+ [DataRow(false, MessageBoxResult.OK, new[] { @"PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=TestNameFalse
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=Nö
+", "Nö" })]
+ [DataRow(null, MessageBoxResult.No, new[] { @"DoOpenMessageBox(Frage,Willst Du Das ?)=>No
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=TestName
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=Nö
+", "Nö" })]
+ public void OpenMsgCommandTest(bool? oAct,MessageBoxResult mrExp, string[] asExp)
+ {
+ // Arrange
+ testModel.Name = $"TestName{oAct}";
+ if (oAct == false) testModel.DoOpenMessageBox -= DoOpenMessageBoxTest;
+ mbResult = mrExp;
+ ClearLog();
+
+ // Act
+ Assert.IsTrue(testModel.OpenMsgCommand.CanExecute(oAct));
+ testModel.OpenMsgCommand.Execute(oAct);
+
+ // Assert
+ Assert.AreEqual(asExp[1], testModel.Name);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+ [TestMethod()]
+ [DataRow(true,"0", new[] { @"DoOpenMessageBox(TestNameTrue,TestEmailTrue)=>(TestName0, TestMail0)
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=TestNameTrue
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=TestName0
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Email)=TestEmailTrue
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Email)=TestMail0
+", "TestName0", "TestMail0" })]
+ [DataRow(false, "1", new[] { @"PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=TestNameFalse
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Email)=TestEmailFalse
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Email)=
+", "","" })]
+ [DataRow(null, "2", new[] { @"DoOpenMessageBox(TestName,TestEmail)=>(TestName2, TestMail2)
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=TestName
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=2
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Cnt)=3
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Name)=TestName2
+PropChgn(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Email)=TestEmail
+PropChg(MVVM_09a_CTDialogBoxes.ViewModels.MainWindowViewModel,Email)=TestMail2
+", "TestName2", "TestMail2" })]
+ public void OpenDialogCommandTest(bool? oAct,string sAct, string[] asExp)
+ {
+ // Arrange
+ testModel.Name = $"TestName{oAct}";
+ testModel.Email = $"TestEmail{oAct}";
+ tResult = ($"TestName{sAct}", $"TestMail{sAct}");
+ if (oAct == false) testModel.DoOpenDialog -= DoOpenDialogTest;
+ ClearLog();
+
+ // Act
+ Assert.IsTrue(testModel.OpenDialogCommand.CanExecute(oAct));
+ testModel.OpenDialogCommand.Execute(oAct);
+
+ // Assert
+ Assert.AreEqual(asExp[1], testModel.Name);
+ Assert.AreEqual(asExp[2], testModel.Email);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/Views/DialogWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/Views/DialogWindowTests.cs
new file mode 100644
index 000000000..4731f2c5d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/Views/DialogWindowTests.cs
@@ -0,0 +1,111 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using MVVM_09a_CTDialogBoxes.ViewModels;
+using System.ComponentModel;
+using System.Threading;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_09a_CTDialogBoxes.Views.Tests;
+
+///
+/// Defines test class DialogWindowTests.
+///
+///
+[TestClass()]
+public class DialogWindowTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ DialogWindow testView;
+ DialogWindowViewModel vm;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => { testView = new(); vm = (DialogWindowViewModel)testView.DataContext; testView.Show(); });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ vm.PropertyChanged += OnVMPropertyChanged;
+ if (vm is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ }
+
+ ///
+ /// Defines the test method DialogWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void DialogWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(DialogWindow));
+ Assert.IsNotNull(vm);
+ Assert.IsInstanceOfType(vm, typeof(DialogWindowViewModel));
+ }
+
+ [TestMethod]
+ public void DoCancelTest()
+ {
+ var t = new Thread(() => { testView = new(); vm = (DialogWindowViewModel)testView.DataContext; testView.Show(); vm.CancelCommand.Execute(null); });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsFalse(testView.IsVisible);
+ }
+
+
+ [TestMethod]
+ public void DoOKTest()
+ {
+ bool? xRes = null;
+ bool xVisible = false;
+ var t = new Thread(() =>
+ {
+ testView = new();
+ vm = (DialogWindowViewModel)testView.DataContext;
+ ExecOK();
+ xRes = testView.ShowDialog();
+ // xResult = testView.ShowDialog();
+ xVisible = testView.IsVisible;
+
+ async void ExecOK()
+ {
+ await System.Threading.Tasks.Task.Delay(50);
+ testView.Dispatcher.Invoke(() => vm.OKCommand.Execute(null));
+ }
+
+ });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsFalse(xVisible);
+ Assert.IsTrue(xRes.HasValue);
+ Assert.IsTrue(xRes.Value);
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..7590a248b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_09a_CTDialogBoxesTests/Views/MainWindowTests.cs
@@ -0,0 +1,134 @@
+// ***********************************************************************
+// Assembly : MVVM_00a_CTTemplate_netTests
+// Author : Mir
+// Created : 05-14-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-14-2023
+// ***********************************************************************
+//
+// Copyright © JC-Soft 2023
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM.ViewModel;
+using MVVM_09a_CTDialogBoxes.ViewModels;
+using NSubstitute;
+using System.ComponentModel;
+using System.Threading;
+using System.Windows;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_09a_CTDialogBoxes.Views.Tests;
+
+///
+/// Defines test class MainWindowTests.
+///
+///
+[TestClass()]
+public class MainWindowTests : BaseTestViewModel
+{
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ ///
+ /// The test view
+ ///
+ ///
+ MainWindow testView;
+ MainWindowViewModel vm;
+ private MessageBoxResult mbResult;
+ private bool? xResult;
+ private string sNewName = "NewName";
+ private string sNewEmail = "NewEmail";
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ ///
+ /// Initializes this instance.
+ ///
+ ///
+ [TestInitialize]
+ public void Init()
+ {
+ var t = new Thread(() => {
+ testView = new();
+ vm = (MainWindowViewModel)testView.DataContext;
+ testView.Window_Loaded(this,null!);
+ });
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ vm.PropertyChanged += OnVMPropertyChanged;
+ if (vm is INotifyPropertyChanging npchgn)
+ npchgn.PropertyChanging += OnVMPropertyChanging;
+ Assert.IsNotNull(testView.NewDialogWindow);
+ try { Assert.IsNotNull(testView.NewDialogWindow()); }
+ catch { }
+ Assert.IsNotNull(testView.MessageBoxShow);
+ testView.MessageBoxShow = MyMBShow;
+ testView.NewDialogWindow = () =>
+ {
+ var dw = Substitute.For();
+ dw.ShowDialog().Returns((c) =>
+ {
+ DoLog($"ShowDialog()=>{xResult}");
+ var vm = dw.DataContext as DialogWindowViewModel;
+ vm!.Name = sNewName;
+ vm!.Email = sNewEmail;
+ return xResult;
+ });
+ dw.DataContext.Returns(new DialogWindowViewModel());
+ return dw;
+ };
+ }
+
+ private MessageBoxResult MyMBShow(string arg1, string arg2, MessageBoxButton button)
+ {
+ DoLog($"MyMBShow({arg1},{arg2},{button})=>{mbResult}");
+ return mbResult;
+ }
+
+ ///
+ /// Defines the test method MainWindowTest.
+ ///
+ ///
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ Assert.IsNotNull(vm);
+ Assert.IsInstanceOfType(vm, typeof(MainWindowViewModel));
+ Assert.IsNotNull(vm.DoOpenDialog);
+ Assert.IsNotNull(vm.DoOpenMessageBox);
+ }
+
+ [TestMethod]
+ [DataRow("OKTitle", "OKLine", MessageBoxResult.OK, new[] { "MyMBShow(OKTitle,OKLine,YesNo)=>OK\r\n" })]
+ [DataRow("CancelTitle", "CancelLine", MessageBoxResult.Cancel, new[] { "MyMBShow(CancelTitle,CancelLine,YesNo)=>Cancel\r\n" })]
+ [DataRow("YesTitle", "YesLine", MessageBoxResult.Yes, new[] { "MyMBShow(YesTitle,YesLine,YesNo)=>Yes\r\n" })]
+ public void DoOpenMessageBoxTest(string sAct1,string sAct2, MessageBoxResult xRes, string[] asExp)
+ {
+ mbResult = xRes;
+ Assert.AreEqual(xRes, vm.DoOpenMessageBox!.Invoke(sAct1, sAct2));
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+
+
+ [TestMethod]
+ [DataRow("OKName", "OKMail", "OKName1", "OKMail1", true, new[] { "ShowDialog()=>True\r\n", "OKName1", "OKMail1" })]
+ [DataRow("NoName", "NoMail", "NoName1", "NoMail1", false, new[] { "ShowDialog()=>False\r\n", "NoName", "NoMail" })]
+ [DataRow("Name", "Mail", "Name1", "Mail1", null, new[] { "ShowDialog()=>\r\n", "Name", "Mail" })]
+ public void DoDialogWindowTest(string sAct1, string sAct2, string sAct3, string sAct4, bool? xRes, string[] asExp)
+ {
+ xResult = xRes;
+ sNewName = sAct3;
+ sNewEmail = sAct4;
+ var tRes = vm.DoOpenDialog!.Invoke(sAct1, sAct2);
+ Assert.AreEqual(asExp[1], tRes.name);
+ Assert.AreEqual(asExp[2], tRes.email);
+ Assert.AreEqual(asExp[0], DebugLog);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/MVVM_16_UserControl1Tests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/MVVM_16_UserControl1Tests.csproj
index 2b087cade..b23ad4ea9 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/MVVM_16_UserControl1Tests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/MVVM_16_UserControl1Tests.csproj
@@ -11,8 +11,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/MVVM_16_UserControl1_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/MVVM_16_UserControl1_netTests.csproj
index 47518257f..59f54d24d 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/MVVM_16_UserControl1_netTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/MVVM_16_UserControl1_netTests.csproj
@@ -1,18 +1,24 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
true
false
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Properties/SettingsTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Properties/SettingsTests.cs
new file mode 100644
index 000000000..2a6821436
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Properties/SettingsTests.cs
@@ -0,0 +1,34 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Configuration;
+
+namespace MVVM_24_UserControl.Properties.Tests
+{
+ [TestClass()]
+ public class SettingsTests
+ {
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ Settings testItem;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testItem = new();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testItem);
+ Assert.IsInstanceOfType(testItem, typeof(Settings));
+ Assert.IsInstanceOfType(testItem, typeof(ApplicationSettingsBase));
+ }
+ [TestMethod()]
+ public void DefaultInstanceTest()
+ {
+ Assert.IsNotNull(Settings.Default);
+ Assert.IsInstanceOfType(Settings.Default, typeof(Settings));
+ Assert.IsInstanceOfType(Settings.Default, typeof(ApplicationSettingsBase));
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/ValueConverters/CurrencyValueConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/ValueConverters/CurrencyValueConverterTests.cs
new file mode 100644
index 000000000..7d859ea39
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/ValueConverters/CurrencyValueConverterTests.cs
@@ -0,0 +1,83 @@
+// ***********************************************************************
+// Assembly : MVVM_16_UserControl1Tests
+// Author : Mir
+// Created : 05-11-2023
+//
+// Last Modified By : Mir
+// Last Modified On : 05-11-2023
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Globalization;
+
+///
+/// The Tests namespace.
+///
+///
+namespace MVVM_16_UserControl1.ValueConverter.Tests;
+
+
+///
+/// Defines test class CurrencyValueConverterTests.
+///
+///
+[TestClass()]
+public class CurrencyValueConverterTests
+{
+ ///
+ /// The converter
+ ///
+ ///
+ CurrencyValueConverter testConv = new();
+#pragma warning disable CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+ private CultureInfo _cOld;
+#pragma warning restore CS8618 // Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable.
+
+ [TestInitialize]
+ public void Init()
+ {
+ testConv = new();
+ _cOld = CultureInfo.CurrentCulture;
+ CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
+ }
+
+ [TestCleanup]
+ public void Cleanup()
+ {
+ CultureInfo.CurrentCulture = _cOld;
+ }
+
+
+ ///
+ /// Converts the correctly formats value.
+ ///
+ /// The value.
+ /// The expected.
+ ///
+ [TestMethod]
+ [DataRow(10.5, "10.50€")]
+ [DataRow(0.99, "0.99€")]
+ [DataRow("Hallo", "Hallo")]
+ [DataRow(null, "")]
+ public void ConvertTest(object? value, string expected)
+ {
+ if (value is double d) value = (decimal)d;
+ var result = testConv.Convert(value, typeof(string), null, CultureInfo.InvariantCulture);
+ Assert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Defines the test method ConvertBackTest.
+ ///
+ ///
+ [TestMethod()]
+ public void ConvertBackTest()
+ {
+ Assert.ThrowsExactly(() => testConv.ConvertBack(null, typeof(object), null, null));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/ViewModels/MainWindowViewModelTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/ViewModels/MainWindowViewModelTests.cs
new file mode 100644
index 000000000..e9b87cfb6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/ViewModels/MainWindowViewModelTests.cs
@@ -0,0 +1,26 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.ComponentModel;
+using MVVM.ViewModel;
+
+namespace MVVM_16_UserControl1.ViewModels.Tests;
+
+[TestClass()]
+public class MainWindowViewModelTests
+{
+ MainWindowViewModel testModel;
+
+ [TestInitialize]
+ public void Init()
+ {
+ testModel = new();
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testModel);
+ Assert.IsInstanceOfType(testModel, typeof(MainWindowViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(BaseViewModel));
+ Assert.IsInstanceOfType(testModel, typeof(INotifyPropertyChanged));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Views/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Views/MainWindowTests.cs
new file mode 100644
index 000000000..42b1b2c51
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Views/MainWindowTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_16_UserControl1.Views.Tests;
+
+[TestClass()]
+public class MainWindowTests
+{
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ MainWindow? mw=null;
+ var t = new Thread(()=> mw = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(mw);
+ Assert.IsInstanceOfType(mw, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Views/UserColtrolViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Views/UserColtrolViewTests.cs
new file mode 100644
index 000000000..04039bbc4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_UserControl1Tests/Views/UserColtrolViewTests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_16_UserControl1.Views.Tests;
+
+[TestClass()]
+public class UserControlViewTests
+{
+ [TestMethod()]
+ public void UserControlViewTest()
+ {
+ UserControlView? testView=null;
+ var t = new Thread(()=> testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(UserControlView));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/App.xaml
new file mode 100644
index 000000000..f690de603
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/App.xaml.cs
new file mode 100644
index 000000000..9958cf98a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/App.xaml.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+namespace MVVM_16_UserControl1;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MVVM_16_UserControl1.csproj b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MVVM_16_UserControl1.csproj
new file mode 100644
index 000000000..12648b107
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MVVM_16_UserControl1.csproj
@@ -0,0 +1,16 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MVVM_16_UserControl1_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MVVM_16_UserControl1_net.csproj
new file mode 100644
index 000000000..6224d3496
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MVVM_16_UserControl1_net.csproj
@@ -0,0 +1,24 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MainWindow.xaml
new file mode 100644
index 000000000..c7827caf7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MainWindow.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MainWindow.xaml.cs
new file mode 100644
index 000000000..39e5ca568
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/MainWindow.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows;
+
+namespace MVVM_16_UserControl1;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ValueConverter/CurrencyValueConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ValueConverter/CurrencyValueConverter.cs
new file mode 100644
index 000000000..3f7f88bf2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ValueConverter/CurrencyValueConverter.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_16_UserControl1.ValueConverter;
+
+public class CurrencyValueConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is decimal dval)
+ return dval.ToString("0.00€");
+ else
+ return value?.ToString()??"";
+
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/CurrencyViewViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/CurrencyViewViewModel.cs
new file mode 100644
index 000000000..124cf5520
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/CurrencyViewViewModel.cs
@@ -0,0 +1,18 @@
+using MVVM.ViewModel;
+
+namespace MVVM_16_UserControl1.ViewModels;
+
+public class CurrencyViewViewModel : BaseViewModel
+{
+ private decimal _value;
+ public decimal Value { get => _value;
+ set
+ { if (_value == value) return; _value = value; RaisePropertyChanged(); }
+ }
+
+ public CurrencyViewViewModel()
+ {
+ Value = 10;
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/MainWindowViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 000000000..88362e003
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,8 @@
+using MVVM.ViewModel;
+
+namespace MVVM_16_UserControl1.ViewModels;
+
+public class MainWindowViewModel : BaseViewModel
+{
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/UserControlViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/UserControlViewModel.cs
new file mode 100644
index 000000000..917f9c568
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/ViewModels/UserControlViewModel.cs
@@ -0,0 +1,35 @@
+using MVVM.ViewModel;
+
+namespace MVVM_16_UserControl1.ViewModels;
+
+public class UserControlViewModel : BaseViewModel
+{
+ private string _text="";
+
+ private string _data = "";
+ public string Text { get => _text; set => SetProperty(ref _text, value); }
+ public string Daten { get => _data; set => SetProperty(ref _data, value); }
+
+ public DelegateCommand Command1 { get; set; }
+ public DelegateCommand Command2 { get; set; }
+
+ public UserControlViewModel()
+ {
+ Command1 = new DelegateCommand(DoCommand1, (o) => string.IsNullOrEmpty(Text));
+ AddPropertyDependency(nameof(Command1), nameof(Text));
+
+ Command2 = new DelegateCommand(DoCommand2, (o) => string.IsNullOrEmpty(Daten));
+ AddPropertyDependency(nameof(Command2), nameof(Daten));
+ }
+
+ private void DoCommand1(object? obj)
+ {
+ Text = "";
+ Daten = "";
+ }
+ private void DoCommand2(object? obj)
+ {
+ Daten = "";
+ Text = "";
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/DoubleButtonUC.xaml b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/DoubleButtonUC.xaml
new file mode 100644
index 000000000..8751c752e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/DoubleButtonUC.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/DoubleButtonUC.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/DoubleButtonUC.xaml.cs
new file mode 100644
index 000000000..a07b7cd87
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/DoubleButtonUC.xaml.cs
@@ -0,0 +1,35 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace MVVM_16_UserControl1.Views;
+
+///
+/// Interaktionslogik für CurrencyView.xaml
+///
+public partial class DoubleButtonUC : UserControl
+{
+ public DoubleButtonUC()
+ {
+ InitializeComponent();
+ visData1 = Visibility.Visible;
+ visData2 = Visibility.Visible;
+ }
+
+ public ICommand Command1 { get; set; }
+ public static readonly DependencyProperty CommandProperty1 = DependencyProperty.Register("Command1", typeof(ICommand), typeof(DoubleButtonUC), new UIPropertyMetadata(null));
+ public ICommand Command2 { get; set; }
+ public static readonly DependencyProperty CommandProperty2 = DependencyProperty.Register("Command2", typeof(ICommand), typeof(DoubleButtonUC), new UIPropertyMetadata(null));
+
+ public Visibility visData1 { get; set; }
+ public Visibility visData2 { get; set; }
+
+ public string CommandParameter1 { get; set; }
+ public string CommandParameter2 { get; set; }
+
+ public string Tooltip1 { get; set; }
+ public string ToolTip2 { get; set; }
+
+ public string Image1 { get; set; }
+ public string Image2 { get; set; }
+ }
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/LabeldMaxLengthTextbox.xaml b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/LabeldMaxLengthTextbox.xaml
new file mode 100644
index 000000000..d9c3b753d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/LabeldMaxLengthTextbox.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/LabeldMaxLengthTextbox.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/LabeldMaxLengthTextbox.xaml.cs
new file mode 100644
index 000000000..c2ccb593c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/LabeldMaxLengthTextbox.xaml.cs
@@ -0,0 +1,44 @@
+using CommunityToolkit.Mvvm.Input;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace MVVM_16_UserControl1.Views;
+
+///
+/// Interaktionslogik für LabeldMaxLengthTextbox.xaml
+///
+public partial class LabeldMaxLengthTextbox : UserControl
+{
+ public static readonly DependencyProperty CaptionProperty =
+ DependencyProperty.Register(nameof(LabeldMaxLengthTextbox.Caption), typeof(string), typeof(LabeldMaxLengthTextbox),
+ new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.None));
+
+ public static readonly DependencyProperty TextProperty =
+ DependencyProperty.Register(nameof(LabeldMaxLengthTextbox.Text), typeof(string), typeof(LabeldMaxLengthTextbox),
+ new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
+
+ public static readonly DependencyProperty CommandProperty =
+ DependencyProperty.Register(nameof(LabeldMaxLengthTextbox.Command), typeof(IRelayCommand), typeof(LabeldMaxLengthTextbox),
+ new FrameworkPropertyMetadata(default(IRelayCommand), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
+
+ public LabeldMaxLengthTextbox()
+ {
+ InitializeComponent();
+ }
+
+ public object Caption { get; set ; } = "";
+
+ public string Text {
+ get => (string)GetValue(TextProperty);
+ set => SetValue(TextProperty,value);
+ }
+
+ public IRelayCommand Command
+ {
+ get => (IRelayCommand)GetValue(CommandProperty);
+ set => SetValue(CommandProperty, value);
+ }
+
+ public int MaxLength { get; set; } = 50;
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/UserControlView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/UserControlView.xaml
new file mode 100644
index 000000000..45ff6b4e7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/UserControlView.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/UserControlView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/UserControlView.xaml.cs
new file mode 100644
index 000000000..30e2da06a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol1/Views/UserControlView.xaml.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+
+namespace MVVM_16_UserControl1.Views;
+
+///
+/// Interaktionslogik für UserControlView.xaml
+///
+public partial class UserControlView : Page {
+ public UserControlView() {
+ InitializeComponent();
+ }
+ }
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/App.xaml
new file mode 100644
index 000000000..25077066e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/App.xaml.cs
new file mode 100644
index 000000000..2c7636837
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/App.xaml.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+namespace MVVM_16_UserControl2;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/AssemblyInfo.cs
new file mode 100644
index 000000000..8b5504ecf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MVVM_16_UserControl2.csproj b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MVVM_16_UserControl2.csproj
new file mode 100644
index 000000000..6688ab906
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MVVM_16_UserControl2.csproj
@@ -0,0 +1,16 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MVVM_16_UserControl2_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MVVM_16_UserControl2_net.csproj
new file mode 100644
index 000000000..e68304082
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MVVM_16_UserControl2_net.csproj
@@ -0,0 +1,19 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MainWindow.xaml
new file mode 100644
index 000000000..30ec8a2dd
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MainWindow.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MainWindow.xaml.cs
new file mode 100644
index 000000000..5b5318909
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/MainWindow.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows;
+
+namespace MVVM_16_UserControl2;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/ViewModels/UserControlViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/ViewModels/UserControlViewModel.cs
new file mode 100644
index 000000000..e4157aa57
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/ViewModels/UserControlViewModel.cs
@@ -0,0 +1,17 @@
+using MVVM.ViewModel;
+
+namespace MVVM_16_UserControl2.ViewModels;
+
+public class UserControlViewModel : BaseViewModel
+{
+ private string _text="";
+
+ public UserControlViewModel()
+ {
+ return;
+ }
+
+ public string Text { get => _text;
+ set => SetProperty(ref _text,value); }
+
+ }
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/TestControl.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/TestControl.cs
new file mode 100644
index 000000000..27f0d1c1c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/TestControl.cs
@@ -0,0 +1,32 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace MVVM_16_UserControl2.Views;
+
+public class TestControl : ContentControl
+{
+ public static readonly DependencyProperty _Text2Property =
+ DependencyProperty.Register(nameof(Text2), typeof(object), typeof(TestControl),
+ new FrameworkPropertyMetadata((object?)null, (PropertyChangedCallback)OnText2Changed));
+
+ private static void OnText2Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ => ((TestControl)d).OnText2Changed(e.OldValue, e.NewValue); // Übergang in die Instanz
+
+
+ private void OnText2Changed(object _, object newValue)
+ {
+ if (newValue is string @s)
+ Content = $"Text2: {@s}";
+ }
+
+ public object Text2 // Nicht Anfassen (setter) !!!
+ {
+ get => GetValue(_Text2Property);
+ set
+ {
+ _ = GetValue(_Text2Property);
+ SetValue(_Text2Property, value); // speichert den Wert - überschreibt ggf. die Bindung zum "Binding"-Object
+// OnText2Changed(oldValue, value);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/UserControlView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/UserControlView.xaml
new file mode 100644
index 000000000..128c3dfdc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/UserControlView.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/UserControlView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/UserControlView.xaml.cs
new file mode 100644
index 000000000..432f1fa6c
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_16_Usercontrol2/Views/UserControlView.xaml.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+
+namespace MVVM_16_UserControl2.Views;
+
+///
+/// Interaktionslogik für UserControlView.xaml
+///
+public partial class UserControlView : Page {
+ public UserControlView() {
+ InitializeComponent();
+ }
+ }
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/App.xaml
new file mode 100644
index 000000000..8f42e09a4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/App.xaml.cs
new file mode 100644
index 000000000..48c514491
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_17_1_CSV_Laden
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-03-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_17_1_CSV_Laden;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/AssemblyInfo.cs
new file mode 100644
index 000000000..25e2f61a2
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_17_1_CSV_Laden
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-03-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MVVM_17_1_CSV_Laden.csproj b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MVVM_17_1_CSV_Laden.csproj
new file mode 100644
index 000000000..9154da05e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MVVM_17_1_CSV_Laden.csproj
@@ -0,0 +1,47 @@
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MVVM_17_1_CSV_Laden_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MVVM_17_1_CSV_Laden_net.csproj
new file mode 100644
index 000000000..c17509939
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MVVM_17_1_CSV_Laden_net.csproj
@@ -0,0 +1,53 @@
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-Windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MainWindow.xaml
new file mode 100644
index 000000000..a018143cb
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MainWindow.xaml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MainWindow.xaml.cs
new file mode 100644
index 000000000..c81413619
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_17_1_CSV_Laden
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_17_1_CSV_Laden;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Model/CsvModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Model/CsvModel.cs
new file mode 100644
index 000000000..7a22d08b3
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Model/CsvModel.cs
@@ -0,0 +1,71 @@
+// ***********************************************************************
+// Assembly : MVVM_17_1_CSV_Laden
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Collections.Generic;
+using System.IO;
+using CsvHelper.Configuration;
+using System.Globalization;
+
+namespace MVVM_17_1_CSV_Laden.Model;
+
+///
+/// Class CsvModel.
+/// Implements the
+///
+///
+public class CsvModel : IDisposable
+{
+
+ ///
+ /// The reader
+ ///
+ TextReader reader;
+ ///
+ /// The CSV reader
+ ///
+ CsvHelper.CsvReader csvReader;
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ reader?.Dispose();
+ csvReader?.Dispose();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The filename.
+ public CsvModel(string filename)
+ {
+ reader = new StreamReader(filename);
+ CsvConfiguration config = new CsvConfiguration(CultureInfo.CurrentCulture);
+ config.Delimiter = ";";
+ config.HasHeaderRecord = true;
+ csvReader = new CsvHelper.CsvReader(reader, config);
+
+ }
+
+ ///
+ /// Reads the CSV.
+ ///
+ /// IAsyncEnumerable<DataPoint>.
+ public IAsyncEnumerable ReadCSV()
+ {
+ var datapoints = csvReader.GetRecordsAsync();
+ return datapoints;
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Model/DataPoint.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Model/DataPoint.cs
new file mode 100644
index 000000000..43fdcb237
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Model/DataPoint.cs
@@ -0,0 +1,44 @@
+// ***********************************************************************
+// Assembly : MVVM_17_1_CSV_Laden
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System;
+using System.Globalization;
+
+namespace MVVM_17_1_CSV_Laden.Model;
+
+///
+/// Class DataPoint.
+///
+public class DataPoint
+{
+ ///
+ /// Gets or sets the time stamp.
+ ///
+ /// The time stamp.
+ public string TimeStamp { get; set; }
+ ///
+ /// Gets or sets the x.
+ ///
+ /// The x.
+ public double X { get; set; }
+ ///
+ /// Gets or sets the y.
+ ///
+ /// The y.
+ public double Y { get; set; }
+ ///
+ /// Gets the dt.
+ ///
+ /// The dt.
+ public DateTime dt => DateTime.TryParse(TimeStamp, CultureInfo.CurrentUICulture.DateTimeFormat, DateTimeStyles.AssumeLocal, out DateTime d) ? d : DateTime.MinValue;
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Properties/Resources.Designer.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..852a8f466
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Properties/Resources.Designer.cs
@@ -0,0 +1,159 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace MVVM_17_1_CSV_Laden.Properties;
+using System;
+
+
+///
+/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+///
+// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MVVM_17_1_CSV_Laden.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_17_1_CSV_Laden
+ ///// Author : Mir
+ ///// Created : 07-03-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 07-04-2022
+ ///// ***********************************************************************
+ ///// <copyright file="CsvModel.cs" company="MVVM_17_1_CSV_Laden">
+ ///// Copyright (c) JC-Soft. All rights reserved.
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ****************************** [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string CsvModel {
+ get {
+ return ResourceManager.GetString("CsvModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die <Page x:Class="MVVM_17_1_CSV_Laden.View.DataPointsView"
+ /// xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ /// xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ /// xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ /// xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+ /// xmlns:local="clr-namespace:MVVM_17_1_CSV_Laden.View"
+ /// xmlns:vc="clr-namespace:MVVM_17_1_CSV_Laden.V [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DataPointsView {
+ get {
+ return ResourceManager.GetString("DataPointsView", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_17_1_CSV_Laden
+ ///// Author : Mir
+ ///// Created : 07-03-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 07-04-2022
+ ///// ***********************************************************************
+ ///// <copyright file="DataPointsView.xaml.cs" company="MVVM_17_1_CSV_Laden">
+ ///// Copyright (c) JC-Soft. All rights reserved.
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ******************* [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DataPointsView_xaml {
+ get {
+ return ResourceManager.GetString("DataPointsView_xaml", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die // ***********************************************************************
+ ///// Assembly : MVVM_17_1_CSV_Laden
+ ///// Author : Mir
+ ///// Created : 07-03-2022
+ /////
+ ///// Last Modified By : Mir
+ ///// Last Modified On : 08-13-2022
+ ///// ***********************************************************************
+ ///// <copyright file="DataPointsViewModel.cs" company="MVVM_17_1_CSV_Laden">
+ ///// Copyright (c) JC-Soft. All rights reserved.
+ ///// </copyright>
+ ///// <summary></summary>
+ ///// ******************* [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
+ ///
+ public static string DataPointsViewModel {
+ get {
+ return ResourceManager.GetString("DataPointsViewModel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die Tutorial #17: using an nuget-package for csv-handling ähnelt.
+ ///
+ public static string Description {
+ get {
+ return ResourceManager.GetString("Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Sucht eine lokalisierte Zeichenfolge, die MVVM #17 CSV-loading ähnelt.
+ ///
+ public static string Title {
+ get {
+ return ResourceManager.GetString("Title", resourceCulture);
+ }
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Properties/Resources.resx b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Properties/Resources.resx
new file mode 100644
index 000000000..88ed71f95
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Properties/Resources.resx
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Model\CsvModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\Views\DataPointsView.xaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\ViewModels\DataPointsViewModel.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\Views\DataPointsView.xaml.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ Tutorial #17: using an nuget-package for csv-handling
+
+
+ MVVM #17 CSV-loading
+
+
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Resources/RBG_XIst_YIst.csv b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Resources/RBG_XIst_YIst.csv
new file mode 100644
index 000000000..38a08f1c4
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Resources/RBG_XIst_YIst.csv
@@ -0,0 +1,7307 @@
+TimeStamp;X;Y
+02.09.2014 12:14:07,074;17032693;4405000
+02.09.2014 12:14:07,572;17032793;4405000
+02.09.2014 12:14:08,085;17032787;4405000
+02.09.2014 12:14:08,581;17032900;4405000
+02.09.2014 12:14:09,102;17032931;4405000
+02.09.2014 12:14:09,608;17032887;4405000
+02.09.2014 12:14:10,115;17032881;4405000
+02.09.2014 12:14:10,637;17032900;4405000
+02.09.2014 12:14:11,148;17032793;4405000
+02.09.2014 12:14:11,667;17032900;4405000
+02.09.2014 12:14:12,167;17033006;4405000
+02.09.2014 12:14:12,698;17032818;4405000
+02.09.2014 12:14:13,206;17033000;4405000
+02.09.2014 12:14:13,709;17032925;4405000
+02.09.2014 12:14:14,211;17032893;4405000
+02.09.2014 12:14:14,716;17032912;4405000
+02.09.2014 12:14:15,236;17032787;4405000
+02.09.2014 12:14:15,730;17033000;4405000
+02.09.2014 12:14:16,245;17033000;4405000
+02.09.2014 12:14:16,739;17032793;4405000
+02.09.2014 12:14:17,258;17032918;4405000
+02.09.2014 12:14:17,766;17033062;4405000
+02.09.2014 12:14:18,266;17032900;4405000
+02.09.2014 12:14:18,773;17032687;4405000
+02.09.2014 12:14:19,286;17032787;4405000
+02.09.2014 12:14:19,782;17032800;4405000
+02.09.2014 12:14:20,302;17032781;4405000
+02.09.2014 12:14:20,808;17033012;4405000
+02.09.2014 12:14:21,310;17032918;4405000
+02.09.2014 12:14:21,807;17032900;4405000
+02.09.2014 12:14:22,336;17032668;4405000
+02.09.2014 12:14:22,833;17032993;4405000
+02.09.2014 12:14:23,346;17032768;4405000
+02.09.2014 12:14:23,848;17032893;4405000
+02.09.2014 12:14:24,363;17032931;4405000
+02.09.2014 12:14:24,873;17032850;4405000
+02.09.2014 12:14:25,369;17032918;4405000
+02.09.2014 12:14:25,867;17032806;4405000
+02.09.2014 12:14:26,380;17032900;4405000
+02.09.2014 12:14:26,879;17032843;4405000
+02.09.2014 12:14:27,401;17032731;4405000
+02.09.2014 12:14:27,913;17033018;4405000
+02.09.2014 12:14:28,408;17032893;4405000
+02.09.2014 12:14:28,915;17032900;4405000
+02.09.2014 12:14:29,417;17032787;4405000
+02.09.2014 12:14:29,928;17032800;4405000
+02.09.2014 12:14:30,442;17032912;4405000
+02.09.2014 12:14:30,953;17032806;4405000
+02.09.2014 12:14:31,450;17032912;4405000
+02.09.2014 12:14:31,956;17032793;4405000
+02.09.2014 12:14:32,474;17032850;4405000
+02.09.2014 12:14:32,971;17032906;4405000
+02.09.2014 12:14:33,483;17033000;4405000
+02.09.2014 12:14:33,981;17032818;4405000
+02.09.2014 12:14:34,494;17032675;4405000
+02.09.2014 12:14:35,005;17032943;4405000
+02.09.2014 12:14:35,509;17032900;4405000
+02.09.2014 12:14:36,041;17033043;4405000
+02.09.2014 12:14:36,517;17032881;4405000
+02.09.2014 12:14:37,029;17032781;4405000
+02.09.2014 12:14:37,580;17032881;4405000
+02.09.2014 12:14:38,038;17032937;4405000
+02.09.2014 12:14:38,595;17032800;4405000
+02.09.2014 12:14:39,063;17032800;4405000
+02.09.2014 12:14:39,604;17032793;4405000
+02.09.2014 12:14:40,072;17032737;4405000
+02.09.2014 12:14:40,625;17032793;4405000
+02.09.2014 12:14:41,098;17032706;4405000
+02.09.2014 12:14:41,603;17032900;4405000
+02.09.2014 12:14:42,116;17032912;4405000
+02.09.2014 12:14:42,617;17033006;4405000
+02.09.2014 12:14:43,116;17032906;4405000
+02.09.2014 12:14:43,625;17032793;4405000
+02.09.2014 12:14:44,141;17032750;4405000
+02.09.2014 12:14:44,632;17032900;4405000
+02.09.2014 12:14:45,148;17033062;4405000
+02.09.2014 12:14:45,657;17033000;4405000
+02.09.2014 12:14:46,157;17033025;4405000
+02.09.2014 12:14:46,666;17032781;4405000
+02.09.2014 12:14:47,183;17032900;4405000
+02.09.2014 12:14:47,679;17032881;4405000
+02.09.2014 12:14:48,194;17032775;4405000
+02.09.2014 12:14:48,687;17033000;4405000
+02.09.2014 12:14:49,208;17032862;4405000
+02.09.2014 12:14:49,716;17032787;4405000
+02.09.2014 12:14:50,218;17033025;4405000
+02.09.2014 12:14:50,726;17032656;4405000
+02.09.2014 12:14:51,229;17032681;4405000
+02.09.2014 12:14:51,727;17033000;4405000
+02.09.2014 12:14:52,257;17032993;4405000
+02.09.2014 12:14:52,756;17032906;4405000
+02.09.2014 12:14:53,277;17032793;4405000
+02.09.2014 12:14:53,789;17032793;4405000
+02.09.2014 12:14:54,316;17032793;4405000
+02.09.2014 12:14:54,822;17032775;4405000
+02.09.2014 12:14:55,318;17032906;4405000
+02.09.2014 12:14:55,847;17032900;4405000
+02.09.2014 12:14:56,327;17032800;4405000
+02.09.2014 12:14:56,855;17032893;4405000
+02.09.2014 12:14:57,352;17032706;4405000
+02.09.2014 12:14:57,857;17032887;4405000
+02.09.2014 12:14:58,365;17032675;4405000
+02.09.2014 12:14:58,886;17032750;4405000
+02.09.2014 12:14:59,381;17032793;4405000
+02.09.2014 12:14:59,903;17032775;4405000
+02.09.2014 12:15:00,397;17032793;4405000
+02.09.2014 12:15:00,908;17033000;4405000
+02.09.2014 12:15:01,408;17032881;4405000
+02.09.2014 12:15:01,917;17032706;4405000
+02.09.2014 12:15:02,424;17032687;4405000
+02.09.2014 12:15:02,937;17033106;4405000
+02.09.2014 12:15:03,431;17032787;4405000
+02.09.2014 12:15:03,947;17032887;4405000
+02.09.2014 12:15:04,452;17032887;4405000
+02.09.2014 12:15:04,968;17039406;4404156
+02.09.2014 12:15:05,459;17135950;4412052
+02.09.2014 12:15:05,988;17368493;4421779
+02.09.2014 12:15:06,478;17673106;4430546
+02.09.2014 12:15:06,997;18075212;4439784
+02.09.2014 12:15:07,497;18558800;4448767
+02.09.2014 12:15:08,007;19004531;4457377
+02.09.2014 12:15:08,502;19482887;4465029
+02.09.2014 12:15:09,017;19860975;4472843
+02.09.2014 12:15:09,533;20157275;4480168
+02.09.2014 12:15:10,035;20338100;4486197
+02.09.2014 12:15:10,528;20402587;4490029
+02.09.2014 12:15:11,055;20399756;4490023
+02.09.2014 12:15:11,551;20399631;4490005
+02.09.2014 12:15:12,061;20400818;4490005
+02.09.2014 12:15:12,572;20398587;4490005
+02.09.2014 12:15:13,077;20398962;4490005
+02.09.2014 12:15:13,594;20399493;4490005
+02.09.2014 12:15:14,089;20399193;4490005
+02.09.2014 12:15:14,602;20399300;4490005
+02.09.2014 12:15:15,099;20399087;4490005
+02.09.2014 12:15:15,626;20399093;4490005
+02.09.2014 12:15:16,121;20399087;4490005
+02.09.2014 12:15:16,633;20399225;4490005
+02.09.2014 12:15:17,126;20399206;4490005
+02.09.2014 12:15:17,637;20399418;4489139
+02.09.2014 12:15:18,159;20399400;4522581
+02.09.2014 12:15:18,658;20399400;4529662
+02.09.2014 12:15:19,164;20398962;4532976
+02.09.2014 12:15:19,674;20399200;4575511
+02.09.2014 12:15:20,195;20399187;4610011
+02.09.2014 12:15:20,686;20399493;4610040
+02.09.2014 12:15:21,194;20399187;4610040
+02.09.2014 12:15:21,704;20399418;4610029
+02.09.2014 12:15:22,199;20399000;4610029
+02.09.2014 12:15:22,708;20399093;4610029
+02.09.2014 12:15:23,214;20399512;4610029
+02.09.2014 12:15:23,731;20399506;4610029
+02.09.2014 12:15:24,249;20399306;4610029
+02.09.2014 12:15:24,749;20399387;4610029
+02.09.2014 12:15:25,243;20399281;4610029
+02.09.2014 12:15:25,769;20399487;4610029
+02.09.2014 12:15:26,264;20399306;4610029
+02.09.2014 12:15:26,781;20399400;4610029
+02.09.2014 12:15:27,275;20399512;4610029
+02.09.2014 12:15:27,783;20399400;4610029
+02.09.2014 12:15:28,285;20399293;4610029
+02.09.2014 12:15:28,807;20399287;4610029
+02.09.2014 12:15:29,302;20399300;4610029
+02.09.2014 12:15:29,814;20399306;4610029
+02.09.2014 12:15:30,317;20399400;4609773
+02.09.2014 12:15:30,828;20343993;4605273
+02.09.2014 12:15:31,333;20187531;4595581
+02.09.2014 12:15:31,839;19907575;4584470
+02.09.2014 12:15:32,345;19548468;4575186
+02.09.2014 12:15:32,856;19070431;4565296
+02.09.2014 12:15:33,351;18629425;4556436
+02.09.2014 12:15:33,867;18145800;4547145
+02.09.2014 12:15:34,380;17692337;4538197
+02.09.2014 12:15:34,884;17358006;4529825
+02.09.2014 12:15:35,381;17165362;4522860
+02.09.2014 12:15:35,899;17041675;4515087
+02.09.2014 12:15:36,404;17025825;4515023
+02.09.2014 12:15:36,915;17024300;4514982
+02.09.2014 12:15:37,410;17026693;4514982
+02.09.2014 12:15:37,920;17026200;4514982
+02.09.2014 12:15:38,432;17026493;4514982
+02.09.2014 12:15:38,942;17027000;4514982
+02.09.2014 12:15:39,454;17027006;4514982
+02.09.2014 12:15:39,971;17026900;4514982
+02.09.2014 12:15:40,482;17026900;4514982
+02.09.2014 12:15:41,000;17027106;4514982
+02.09.2014 12:15:41,511;17027012;4514982
+02.09.2014 12:15:42,005;17027018;4514982
+02.09.2014 12:15:42,533;17026887;4514982
+02.09.2014 12:15:43,044;17027025;4514982
+02.09.2014 12:15:43,539;17026768;4514982
+02.09.2014 12:15:44,056;17026900;4514197
+02.09.2014 12:15:44,557;17027025;4489941
+02.09.2014 12:15:45,073;17027993;4449988
+02.09.2014 12:15:45,594;17027081;4443872
+02.09.2014 12:15:46,085;17027093;4439924
+02.09.2014 12:15:46,577;17027000;4412180
+02.09.2014 12:15:47,121;17026775;4405284
+02.09.2014 12:15:47,604;17027106;4405395
+02.09.2014 12:15:48,138;17026831;4405395
+02.09.2014 12:15:48,609;17027112;4405395
+02.09.2014 12:15:49,159;17026862;4405395
+02.09.2014 12:15:49,630;17026893;4405395
+02.09.2014 12:15:50,178;17026906;4405395
+02.09.2014 12:15:50,646;17026781;4405395
+02.09.2014 12:15:51,150;17027150;4405395
+02.09.2014 12:15:51,649;17026893;4405395
+02.09.2014 12:15:52,176;17027106;4405395
+02.09.2014 12:15:52,671;17027000;4405395
+02.09.2014 12:15:53,181;17027143;4405395
+02.09.2014 12:15:53,694;17026918;4404593
+02.09.2014 12:15:54,206;16968625;4379255
+02.09.2014 12:15:54,701;16811237;4266052
+02.09.2014 12:15:55,212;16530312;4040168
+02.09.2014 12:15:55,706;16169318;3759691
+02.09.2014 12:15:56,225;15690593;3450622
+02.09.2014 12:15:56,729;15249712;3141563
+02.09.2014 12:15:57,245;14766893;2856238
+02.09.2014 12:15:57,745;14286962;2547145
+02.09.2014 12:15:58,256;13844300;2238063
+02.09.2014 12:15:58,752;13434687;1952744
+02.09.2014 12:15:59,266;13162600;1643656
+02.09.2014 12:15:59,756;12988456;1334581
+02.09.2014 12:16:00,285;12910743;1025470
+02.09.2014 12:16:00,785;12910806;740168
+02.09.2014 12:16:01,289;12910493;431656
+02.09.2014 12:16:01,801;12910631;183441
+02.09.2014 12:16:02,316;12910887;48180
+02.09.2014 12:16:02,810;12911975;16645
+02.09.2014 12:16:03,325;12912212;16872
+02.09.2014 12:16:03,821;12911987;16912
+02.09.2014 12:16:04,340;12912206;16906
+02.09.2014 12:16:04,842;12912181;16906
+02.09.2014 12:16:05,353;12911887;16906
+02.09.2014 12:16:05,848;12912306;16906
+02.09.2014 12:16:06,358;12912193;16906
+02.09.2014 12:16:06,863;12912275;16906
+02.09.2014 12:16:07,379;12912393;16906
+02.09.2014 12:16:07,888;12912325;16906
+02.09.2014 12:16:08,407;12912012;16906
+02.09.2014 12:16:08,917;12912200;16906
+02.09.2014 12:16:09,446;12912137;16906
+02.09.2014 12:16:09,946;12912093;16924
+02.09.2014 12:16:10,452;12912056;50767
+02.09.2014 12:16:10,967;12912162;57523
+02.09.2014 12:16:11,456;12912400;60970
+02.09.2014 12:16:11,983;12912131;111313
+02.09.2014 12:16:12,479;12912193;137093
+02.09.2014 12:16:12,993;12912500;137029
+02.09.2014 12:16:13,500;12912312;137017
+02.09.2014 12:16:14,016;12912037;137017
+02.09.2014 12:16:14,525;12912181;137017
+02.09.2014 12:16:15,021;12912143;137017
+02.09.2014 12:16:15,537;12912406;137017
+02.09.2014 12:16:16,043;12912062;137017
+02.09.2014 12:16:16,553;12912312;137017
+02.09.2014 12:16:17,048;12912087;137017
+02.09.2014 12:16:17,560;12912193;137017
+02.09.2014 12:16:18,064;12912381;137017
+02.09.2014 12:16:18,583;12912318;137017
+02.09.2014 12:16:19,077;12912618;137017
+02.09.2014 12:16:19,597;12912612;137017
+02.09.2014 12:16:20,090;12912093;137017
+02.09.2014 12:16:20,609;12912237;137017
+02.09.2014 12:16:21,115;12912512;137017
+02.09.2014 12:16:21,626;12912293;137017
+02.09.2014 12:16:22,120;12912518;137017
+02.09.2014 12:16:22,632;12912300;137017
+02.09.2014 12:16:23,136;12914625;135651
+02.09.2014 12:16:23,653;12998431;174773
+02.09.2014 12:16:24,148;13178150;304395
+02.09.2014 12:16:24,659;13482393;547819
+02.09.2014 12:16:25,165;13901006;855500
+02.09.2014 12:16:25,682;14385381;1164558
+02.09.2014 12:16:26,177;14829556;1449848
+02.09.2014 12:16:26,688;15274000;1758918
+02.09.2014 12:16:27,196;15752818;2068000
+02.09.2014 12:16:27,707;16226481;2377104
+02.09.2014 12:16:28,203;16567912;2662406
+02.09.2014 12:16:28,725;16827393;2971488
+02.09.2014 12:16:29,224;16965600;3256825
+02.09.2014 12:16:29,735;17006050;3565889
+02.09.2014 12:16:30,230;17004325;3874994
+02.09.2014 12:16:30,746;17002293;4160267
+02.09.2014 12:16:31,252;17002781;4419337
+02.09.2014 12:16:31,763;17002006;4565284
+02.09.2014 12:16:32,258;17002100;4605081
+02.09.2014 12:16:32,769;17002300;4605023
+02.09.2014 12:16:33,265;17002331;4604970
+02.09.2014 12:16:33,796;17002200;4604965
+02.09.2014 12:16:34,286;17002300;4604965
+02.09.2014 12:16:34,801;17002300;4604965
+02.09.2014 12:16:35,307;17002200;4604965
+02.09.2014 12:16:35,815;17002300;4604965
+02.09.2014 12:16:36,322;17002287;4604965
+02.09.2014 12:16:36,835;17002300;4604965
+02.09.2014 12:16:37,328;17002300;4604965
+02.09.2014 12:16:37,839;17002300;4604965
+02.09.2014 12:16:38,345;17002300;4604965
+02.09.2014 12:16:38,865;17002300;4604965
+02.09.2014 12:16:39,355;17002306;4604965
+02.09.2014 12:16:39,867;17002300;4604965
+02.09.2014 12:16:40,373;17002300;4604965
+02.09.2014 12:16:40,890;17002300;4603627
+02.09.2014 12:16:41,397;17002206;4571552
+02.09.2014 12:16:41,909;17002400;4530087
+02.09.2014 12:16:42,403;17002400;4523965
+02.09.2014 12:16:42,913;17001843;4519947
+02.09.2014 12:16:43,407;17002093;4492930
+02.09.2014 12:16:43,935;17002087;4484970
+02.09.2014 12:16:44,430;17002100;4485116
+02.09.2014 12:16:44,939;17002100;4485127
+02.09.2014 12:16:45,465;17002112;4485127
+02.09.2014 12:16:45,959;17002100;4485127
+02.09.2014 12:16:46,471;17001993;4485127
+02.09.2014 12:16:46,981;17002100;4485127
+02.09.2014 12:16:47,494;17001993;4485127
+02.09.2014 12:16:47,981;17002081;4485127
+02.09.2014 12:16:48,504;17002100;4485127
+02.09.2014 12:16:49,005;17002100;4485127
+02.09.2014 12:16:49,513;17002100;4485127
+02.09.2014 12:16:50,025;17002100;4485127
+02.09.2014 12:16:50,526;17002100;4485127
+02.09.2014 12:16:51,026;17001993;4485127
+02.09.2014 12:16:51,539;17002100;4485127
+02.09.2014 12:16:52,038;17002100;4485127
+02.09.2014 12:16:52,556;17002100;4485127
+02.09.2014 12:16:53,047;17002087;4485127
+02.09.2014 12:16:53,574;17001993;4485127
+02.09.2014 12:16:54,070;17001993;4485127
+02.09.2014 12:16:54,584;17002100;4485127
+02.09.2014 12:16:55,096;17002100;4485127
+02.09.2014 12:16:55,622;17002125;4485127
+02.09.2014 12:16:56,107;17002000;4485127
+02.09.2014 12:16:56,644;17002106;4485127
+02.09.2014 12:16:57,115;17002118;4485127
+02.09.2014 12:16:57,669;17002125;4485127
+02.09.2014 12:16:58,127;17002093;4485127
+02.09.2014 12:16:58,678;17002100;4485127
+02.09.2014 12:16:59,146;17001993;4485127
+02.09.2014 12:16:59,705;17002093;4485127
+02.09.2014 12:17:00,156;17002093;4485127
+02.09.2014 12:17:00,674;17002100;4485127
+02.09.2014 12:17:01,189;17002100;4485127
+02.09.2014 12:17:01,696;17002000;4485127
+02.09.2014 12:17:02,209;17002100;4485127
+02.09.2014 12:17:02,727;17002100;4485127
+02.09.2014 12:17:03,240;17002000;4485127
+02.09.2014 12:17:03,749;17001968;4485127
+02.09.2014 12:17:04,264;17002100;4485127
+02.09.2014 12:17:04,760;17002112;4485127
+02.09.2014 12:17:05,273;17002006;4485127
+02.09.2014 12:17:05,769;17001987;4485127
+02.09.2014 12:17:06,284;17002112;4485127
+02.09.2014 12:17:06,785;17002106;4485127
+02.09.2014 12:17:07,302;17002106;4485127
+02.09.2014 12:17:07,801;17002000;4485127
+02.09.2014 12:17:08,315;17002000;4485127
+02.09.2014 12:17:08,810;17002100;4485127
+02.09.2014 12:17:09,335;17002000;4485127
+02.09.2014 12:17:09,821;17002000;4485127
+02.09.2014 12:17:10,338;17002000;4485127
+02.09.2014 12:17:10,846;17002112;4485127
+02.09.2014 12:17:11,348;17002000;4485127
+02.09.2014 12:17:11,859;17002000;4485127
+02.09.2014 12:17:12,373;17001981;4485127
+02.09.2014 12:17:12,886;17002100;4485127
+02.09.2014 12:17:13,405;17001993;4485127
+02.09.2014 12:17:13,906;17002000;4485127
+02.09.2014 12:17:14,436;17002100;4485127
+02.09.2014 12:17:14,936;17001987;4485127
+02.09.2014 12:17:15,444;17001993;4485127
+02.09.2014 12:17:15,961;17002000;4485127
+02.09.2014 12:17:16,451;17002000;4485127
+02.09.2014 12:17:16,969;17002000;4485127
+02.09.2014 12:17:17,481;17002000;4485127
+02.09.2014 12:17:17,996;17002025;4485127
+02.09.2014 12:17:18,498;17002000;4485127
+02.09.2014 12:17:19,005;17002000;4485127
+02.09.2014 12:17:19,517;17002000;4485127
+02.09.2014 12:17:20,019;17002000;4485127
+02.09.2014 12:17:20,531;17002000;4485127
+02.09.2014 12:17:21,027;17002100;4485127
+02.09.2014 12:17:21,541;17001993;4485127
+02.09.2014 12:17:22,035;17001962;4485127
+02.09.2014 12:17:22,554;17002018;4485127
+02.09.2014 12:17:23,065;17002100;4485127
+02.09.2014 12:17:23,567;17002000;4485127
+02.09.2014 12:17:24,066;17002000;4485127
+02.09.2014 12:17:24,578;17001987;4485127
+02.09.2014 12:17:25,093;17001987;4485127
+02.09.2014 12:17:25,606;17001993;4485127
+02.09.2014 12:17:26,109;17002112;4485127
+02.09.2014 12:17:26,615;17002100;4485127
+02.09.2014 12:17:27,114;17002000;4485127
+02.09.2014 12:17:27,624;17002000;4485127
+02.09.2014 12:17:28,125;17002000;4485127
+02.09.2014 12:17:28,639;17002100;4485127
+02.09.2014 12:17:29,145;17002000;4485127
+02.09.2014 12:17:29,647;17002100;4485127
+02.09.2014 12:17:30,159;17002000;4485127
+02.09.2014 12:17:30,672;17002000;4485127
+02.09.2014 12:17:31,169;17002000;4485127
+02.09.2014 12:17:31,682;17002000;4485127
+02.09.2014 12:17:32,195;17001987;4485127
+02.09.2014 12:17:32,714;17002000;4485127
+02.09.2014 12:17:33,216;17002000;4485127
+02.09.2014 12:17:33,749;17002006;4485127
+02.09.2014 12:17:34,247;17001893;4485127
+02.09.2014 12:17:34,759;17002000;4485127
+02.09.2014 12:17:35,272;17002006;4485127
+02.09.2014 12:17:35,768;17002000;4485127
+02.09.2014 12:17:36,284;17001906;4485127
+02.09.2014 12:17:36,777;17002000;4485127
+02.09.2014 12:17:37,297;17002000;4485127
+02.09.2014 12:17:37,803;17002012;4485127
+02.09.2014 12:17:38,309;17001993;4485127
+02.09.2014 12:17:38,812;17002000;4485127
+02.09.2014 12:17:39,316;17001993;4485127
+02.09.2014 12:17:39,828;17002000;4485127
+02.09.2014 12:17:40,345;17002000;4485127
+02.09.2014 12:17:40,843;17002000;4485127
+02.09.2014 12:17:41,355;17001856;4485127
+02.09.2014 12:17:41,856;17002000;4485127
+02.09.2014 12:17:42,370;17001893;4485127
+02.09.2014 12:17:42,865;17002000;4485127
+02.09.2014 12:17:43,377;17002000;4485127
+02.09.2014 12:17:43,887;17002000;4485127
+02.09.2014 12:17:44,386;17002000;4485127
+02.09.2014 12:17:44,895;17002000;4485127
+02.09.2014 12:17:45,414;17002000;4485127
+02.09.2014 12:17:45,909;17002006;4485127
+02.09.2014 12:17:46,421;17002000;4485127
+02.09.2014 12:17:46,935;17002000;4485127
+02.09.2014 12:17:47,446;17002000;4485127
+02.09.2014 12:17:47,947;17002000;4485127
+02.09.2014 12:17:48,449;17001893;4485127
+02.09.2014 12:17:48,957;17002000;4485127
+02.09.2014 12:17:49,465;17001968;4485127
+02.09.2014 12:17:49,983;17001993;4485127
+02.09.2014 12:17:50,478;17001993;4485127
+02.09.2014 12:17:50,993;17002000;4485127
+02.09.2014 12:17:51,487;17001987;4485127
+02.09.2014 12:17:52,007;17002000;4485127
+02.09.2014 12:17:52,515;17002000;4485127
+02.09.2014 12:17:53,016;17002000;4485127
+02.09.2014 12:17:53,523;17002000;4485127
+02.09.2014 12:17:54,025;17002000;4485127
+02.09.2014 12:17:54,534;17001900;4485127
+02.09.2014 12:17:55,050;17002000;4485127
+02.09.2014 12:17:55,562;17002000;4485127
+02.09.2014 12:17:56,064;17002000;4485127
+02.09.2014 12:17:56,565;17002000;4485127
+02.09.2014 12:17:57,084;17002000;4485127
+02.09.2014 12:17:57,578;17002000;4485127
+02.09.2014 12:17:58,092;17002000;4485127
+02.09.2014 12:17:58,586;17002025;4485127
+02.09.2014 12:17:59,100;17002000;4485127
+02.09.2014 12:17:59,598;17002000;4485127
+02.09.2014 12:18:00,118;17002000;4485127
+02.09.2014 12:18:00,625;17002000;4485127
+02.09.2014 12:18:01,126;17001993;4485127
+02.09.2014 12:18:01,634;17002000;4485127
+02.09.2014 12:18:02,151;17002006;4485127
+02.09.2014 12:18:02,649;17001993;4485127
+02.09.2014 12:18:03,161;17002000;4485127
+02.09.2014 12:18:03,656;17002000;4485127
+02.09.2014 12:18:04,214;17002000;4485127
+02.09.2014 12:18:04,669;17002000;4485127
+02.09.2014 12:18:05,197;17001900;4485127
+02.09.2014 12:18:05,731;17001900;4485127
+02.09.2014 12:18:06,207;17002000;4485127
+02.09.2014 12:18:06,748;17002000;4485127
+02.09.2014 12:18:07,221;17001868;4485127
+02.09.2014 12:18:07,717;17002000;4485127
+02.09.2014 12:18:08,241;17002000;4485127
+02.09.2014 12:18:08,725;17001893;4485127
+02.09.2014 12:18:09,256;17001993;4485127
+02.09.2014 12:18:09,744;17002000;4485127
+02.09.2014 12:18:10,266;17001900;4485127
+02.09.2014 12:18:10,761;17002000;4485127
+02.09.2014 12:18:11,264;17001900;4485127
+02.09.2014 12:18:11,773;17002000;4485127
+02.09.2014 12:18:12,296;17002006;4485127
+02.09.2014 12:18:12,793;17002000;4485127
+02.09.2014 12:18:13,306;17002025;4485127
+02.09.2014 12:18:13,801;17002000;4485127
+02.09.2014 12:18:14,320;17002006;4485127
+02.09.2014 12:18:14,811;17001900;4485127
+02.09.2014 12:18:15,330;17002000;4485127
+02.09.2014 12:18:15,825;17001868;4485127
+02.09.2014 12:18:16,340;17001893;4485127
+02.09.2014 12:18:16,836;17002000;4485127
+02.09.2014 12:18:17,364;17001912;4485127
+02.09.2014 12:18:17,860;17002000;4485127
+02.09.2014 12:18:18,373;17002000;4485127
+02.09.2014 12:18:18,870;17002000;4485127
+02.09.2014 12:18:19,386;17001862;4485127
+02.09.2014 12:18:19,900;17002000;4485127
+02.09.2014 12:18:20,397;17002000;4485127
+02.09.2014 12:18:20,910;17001987;4485127
+02.09.2014 12:18:21,421;17001887;4485127
+02.09.2014 12:18:21,936;17001993;4485127
+02.09.2014 12:18:22,430;17001900;4485127
+02.09.2014 12:18:22,943;17002012;4485127
+02.09.2014 12:18:23,439;17001875;4485127
+02.09.2014 12:18:23,956;17002000;4485127
+02.09.2014 12:18:24,463;17001900;4485127
+02.09.2014 12:18:24,965;17002006;4485127
+02.09.2014 12:18:25,477;17002056;4485127
+02.09.2014 12:18:26,004;17002000;4485127
+02.09.2014 12:18:26,510;17001906;4485127
+02.09.2014 12:18:27,028;17002031;4485127
+02.09.2014 12:18:27,544;17001900;4485127
+02.09.2014 12:18:28,046;17001900;4485127
+02.09.2014 12:18:28,562;17001943;4485127
+02.09.2014 12:18:29,057;17001900;4485127
+02.09.2014 12:18:29,569;17001906;4485127
+02.09.2014 12:18:30,085;17002000;4485127
+02.09.2014 12:18:30,585;17001993;4485127
+02.09.2014 12:18:31,094;17002000;4485127
+02.09.2014 12:18:31,607;17002000;4485127
+02.09.2014 12:18:32,102;17001906;4485127
+02.09.2014 12:18:32,614;17002006;4485127
+02.09.2014 12:18:33,116;17002006;4485127
+02.09.2014 12:18:33,628;17002000;4485127
+02.09.2014 12:18:34,125;17001900;4485127
+02.09.2014 12:18:34,640;17001893;4485127
+02.09.2014 12:18:35,152;17002000;4485127
+02.09.2014 12:18:35,664;17001900;4485127
+02.09.2014 12:18:36,160;17001900;4485127
+02.09.2014 12:18:36,672;17001887;4485127
+02.09.2014 12:18:37,169;17001900;4485127
+02.09.2014 12:18:37,681;17001906;4485127
+02.09.2014 12:18:38,177;17001831;4485127
+02.09.2014 12:18:38,697;17001887;4485127
+02.09.2014 12:18:39,205;17002000;4485127
+02.09.2014 12:18:39,705;17002000;4485127
+02.09.2014 12:18:40,220;17001900;4485127
+02.09.2014 12:18:40,733;17002000;4485127
+02.09.2014 12:18:41,230;17001881;4485127
+02.09.2014 12:18:41,741;17001893;4485127
+02.09.2014 12:18:42,239;17001993;4485127
+02.09.2014 12:18:42,750;17001850;4485127
+02.09.2014 12:18:43,263;17001887;4485127
+02.09.2014 12:18:43,768;17001768;4485127
+02.09.2014 12:18:44,264;17001931;4485127
+02.09.2014 12:18:44,780;17002006;4485127
+02.09.2014 12:18:45,279;17001893;4485127
+02.09.2014 12:18:45,796;17001893;4485127
+02.09.2014 12:18:46,304;17002012;4485127
+02.09.2014 12:18:46,805;17002000;4485127
+02.09.2014 12:18:47,319;17002018;4485127
+02.09.2014 12:18:47,836;17001893;4485127
+02.09.2014 12:18:48,348;17001993;4485127
+02.09.2014 12:18:48,866;17002000;4485127
+02.09.2014 12:18:49,379;17001900;4485127
+02.09.2014 12:18:49,875;17001893;4485127
+02.09.2014 12:18:50,397;17001893;4485127
+02.09.2014 12:18:50,908;17001893;4485127
+02.09.2014 12:18:51,421;17001900;4485127
+02.09.2014 12:18:51,932;17001900;4485127
+02.09.2014 12:18:52,430;17001906;4485127
+02.09.2014 12:18:52,942;17001900;4485127
+02.09.2014 12:18:53,445;17001893;4485127
+02.09.2014 12:18:53,952;17001900;4485127
+02.09.2014 12:18:54,459;17001893;4485127
+02.09.2014 12:18:54,966;17002006;4485127
+02.09.2014 12:18:55,473;17001900;4485127
+02.09.2014 12:18:55,986;17001906;4485127
+02.09.2014 12:18:56,485;17001900;4485127
+02.09.2014 12:18:57,003;17001893;4485127
+02.09.2014 12:18:57,499;17002006;4485127
+02.09.2014 12:18:58,010;17001906;4485127
+02.09.2014 12:18:58,507;17001912;4485127
+02.09.2014 12:18:59,019;17001900;4485127
+02.09.2014 12:18:59,543;17002000;4485127
+02.09.2014 12:19:00,047;17001800;4485127
+02.09.2014 12:19:00,548;17001900;4485127
+02.09.2014 12:19:01,048;17001900;4485127
+02.09.2014 12:19:01,546;17001900;4485127
+02.09.2014 12:19:02,073;17001893;4485127
+02.09.2014 12:19:02,573;17001900;4485127
+02.09.2014 12:19:03,081;17001925;4485127
+02.09.2014 12:19:03,589;17001906;4485127
+02.09.2014 12:19:04,093;17001900;4485127
+02.09.2014 12:19:04,592;17001900;4485127
+02.09.2014 12:19:05,110;17001900;4485127
+02.09.2014 12:19:05,606;17001900;4485127
+02.09.2014 12:19:06,119;17001893;4485127
+02.09.2014 12:19:06,616;17001906;4485127
+02.09.2014 12:19:07,155;17001881;4485127
+02.09.2014 12:19:07,643;17001893;4485127
+02.09.2014 12:19:08,153;17001900;4485127
+02.09.2014 12:19:08,649;17001793;4485127
+02.09.2014 12:19:09,163;17001900;4485127
+02.09.2014 12:19:09,658;17001881;4485127
+02.09.2014 12:19:10,176;17001900;4485127
+02.09.2014 12:19:10,685;17001906;4485127
+02.09.2014 12:19:11,185;17001900;4485127
+02.09.2014 12:19:11,699;17001893;4485127
+02.09.2014 12:19:12,213;17002006;4485127
+02.09.2014 12:19:12,709;17001837;4485127
+02.09.2014 12:19:13,223;17001900;4485127
+02.09.2014 12:19:13,755;17001887;4485127
+02.09.2014 12:19:14,232;17001906;4485127
+02.09.2014 12:19:14,727;17001900;4485127
+02.09.2014 12:19:15,284;17001900;4485127
+02.09.2014 12:19:15,743;17001900;4485127
+02.09.2014 12:19:16,297;17001900;4485127
+02.09.2014 12:19:16,758;17001875;4485127
+02.09.2014 12:19:17,325;17001900;4485127
+02.09.2014 12:19:17,785;17001900;4485127
+02.09.2014 12:19:18,330;17001900;4485127
+02.09.2014 12:19:18,792;17002000;4485127
+02.09.2014 12:19:19,305;17001900;4485127
+02.09.2014 12:19:19,807;17001900;4485127
+02.09.2014 12:19:20,320;17001900;4485127
+02.09.2014 12:19:20,817;17001900;4485127
+02.09.2014 12:19:21,329;17001887;4485127
+02.09.2014 12:19:21,846;17001900;4485127
+02.09.2014 12:19:22,355;17001906;4485127
+02.09.2014 12:19:22,854;17001900;4485127
+02.09.2014 12:19:23,363;17001900;4485127
+02.09.2014 12:19:23,881;17001900;4485127
+02.09.2014 12:19:24,378;17001900;4485127
+02.09.2014 12:19:24,892;17001900;4485127
+02.09.2014 12:19:25,386;17001900;4485127
+02.09.2014 12:19:25,900;17001900;4485127
+02.09.2014 12:19:26,414;17002000;4485127
+02.09.2014 12:19:26,916;17001900;4485127
+02.09.2014 12:19:27,424;17001900;4485127
+02.09.2014 12:19:27,924;17001887;4485127
+02.09.2014 12:19:28,450;17001893;4485127
+02.09.2014 12:19:28,950;17001906;4485127
+02.09.2014 12:19:29,447;17002006;4485127
+02.09.2014 12:19:29,959;17001900;4485127
+02.09.2014 12:19:30,455;17001887;4485127
+02.09.2014 12:19:30,973;17001900;4485127
+02.09.2014 12:19:31,474;17001900;4485127
+02.09.2014 12:19:31,983;17001900;4485127
+02.09.2014 12:19:32,493;17001900;4485127
+02.09.2014 12:19:32,998;17001893;4485127
+02.09.2014 12:19:33,512;17001900;4485127
+02.09.2014 12:19:34,024;17001900;4485127
+02.09.2014 12:19:34,520;17001893;4485127
+02.09.2014 12:19:35,033;17001900;4485127
+02.09.2014 12:19:35,529;17001900;4485127
+02.09.2014 12:19:36,041;17001906;4485127
+02.09.2014 12:19:36,554;17001900;4485127
+02.09.2014 12:19:37,055;17001887;4485127
+02.09.2014 12:19:37,563;17001900;4485127
+02.09.2014 12:19:38,063;17001900;4485127
+02.09.2014 12:19:38,571;17001900;4485127
+02.09.2014 12:19:39,090;17001893;4485127
+02.09.2014 12:19:39,585;17001900;4485127
+02.09.2014 12:19:40,099;17002000;4485127
+02.09.2014 12:19:40,596;17001900;4485127
+02.09.2014 12:19:41,106;17001900;4485127
+02.09.2014 12:19:41,619;17001900;4485127
+02.09.2014 12:19:42,133;17001893;4485127
+02.09.2014 12:19:42,629;17001900;4485127
+02.09.2014 12:19:43,141;17001900;4485127
+02.09.2014 12:19:43,654;17001887;4485127
+02.09.2014 12:19:44,155;17001750;4485127
+02.09.2014 12:19:44,662;17001887;4485127
+02.09.2014 12:19:45,163;17001900;4485127
+02.09.2014 12:19:45,670;17001900;4485127
+02.09.2014 12:19:46,185;17001887;4485127
+02.09.2014 12:19:46,679;17001900;4485127
+02.09.2014 12:19:47,199;17001900;4485127
+02.09.2014 12:19:47,695;17001881;4485127
+02.09.2014 12:19:48,213;17001900;4485127
+02.09.2014 12:19:48,713;17001900;4485127
+02.09.2014 12:19:49,230;17001900;4485127
+02.09.2014 12:19:49,725;17001900;4485127
+02.09.2014 12:19:50,251;17001900;4485127
+02.09.2014 12:19:50,736;17001887;4485127
+02.09.2014 12:19:51,250;17001900;4485127
+02.09.2014 12:19:51,761;17001906;4485127
+02.09.2014 12:19:52,274;17001900;4485127
+02.09.2014 12:19:52,774;17001887;4485127
+02.09.2014 12:19:53,290;17001900;4485127
+02.09.2014 12:19:53,803;17001893;4485127
+02.09.2014 12:19:54,299;17001906;4485127
+02.09.2014 12:19:54,812;17001900;4485127
+02.09.2014 12:19:55,306;17001731;4485127
+02.09.2014 12:19:55,818;17001900;4485127
+02.09.2014 12:19:56,331;17001781;4485127
+02.09.2014 12:19:56,842;17001900;4485127
+02.09.2014 12:19:57,340;17001900;4485127
+02.09.2014 12:19:57,858;17001900;4485127
+02.09.2014 12:19:58,353;17001900;4485127
+02.09.2014 12:19:58,866;17001893;4485127
+02.09.2014 12:19:59,373;17001900;4485127
+02.09.2014 12:19:59,873;17001900;4485127
+02.09.2014 12:20:00,385;17001900;4485127
+02.09.2014 12:20:00,903;17001787;4485127
+02.09.2014 12:20:01,393;17001875;4485127
+02.09.2014 12:20:01,911;17001900;4485127
+02.09.2014 12:20:02,408;17001900;4485127
+02.09.2014 12:20:02,919;17001887;4485127
+02.09.2014 12:20:03,433;17001900;4485127
+02.09.2014 12:20:03,933;17001900;4485127
+02.09.2014 12:20:04,434;17001900;4485127
+02.09.2014 12:20:04,946;17001900;4485127
+02.09.2014 12:20:05,447;17001893;4485127
+02.09.2014 12:20:05,959;17001900;4485127
+02.09.2014 12:20:06,472;17001900;4485127
+02.09.2014 12:20:06,987;17001775;4485127
+02.09.2014 12:20:07,480;17001900;4485127
+02.09.2014 12:20:07,994;17001893;4485127
+02.09.2014 12:20:08,495;17001800;4485127
+02.09.2014 12:20:09,007;17001900;4485127
+02.09.2014 12:20:09,503;17001906;4485127
+02.09.2014 12:20:10,017;17001800;4485127
+02.09.2014 12:20:10,524;17001900;4485127
+02.09.2014 12:20:11,024;17001900;4485127
+02.09.2014 12:20:11,533;17001900;4485127
+02.09.2014 12:20:12,049;17001900;4485127
+02.09.2014 12:20:12,545;17001900;4485127
+02.09.2014 12:20:13,058;17001900;4485127
+02.09.2014 12:20:13,555;17001900;4485127
+02.09.2014 12:20:14,073;17001925;4485127
+02.09.2014 12:20:14,584;17001900;4485127
+02.09.2014 12:20:15,094;17001893;4485127
+02.09.2014 12:20:15,591;17001900;4485127
+02.09.2014 12:20:16,103;17001900;4485127
+02.09.2014 12:20:16,599;17001900;4485127
+02.09.2014 12:20:17,117;17001900;4485127
+02.09.2014 12:20:17,615;17001900;4485127
+02.09.2014 12:20:18,133;17001900;4485127
+02.09.2014 12:20:18,640;17001887;4485127
+02.09.2014 12:20:19,154;17001900;4485127
+02.09.2014 12:20:19,647;17001900;4485127
+02.09.2014 12:20:20,160;17001800;4485127
+02.09.2014 12:20:20,661;17001900;4485127
+02.09.2014 12:20:21,174;17001893;4485127
+02.09.2014 12:20:21,673;17001900;4485127
+02.09.2014 12:20:22,195;17001793;4485127
+02.09.2014 12:20:22,689;17001900;4485127
+02.09.2014 12:20:23,245;17001900;4485127
+02.09.2014 12:20:23,698;17001900;4485127
+02.09.2014 12:20:24,228;17001887;4485127
+02.09.2014 12:20:24,729;17001768;4485127
+02.09.2014 12:20:25,238;17001893;4485127
+02.09.2014 12:20:25,744;17001906;4485127
+02.09.2014 12:20:26,262;17001900;4485127
+02.09.2014 12:20:26,763;17001906;4485127
+02.09.2014 12:20:27,283;17001900;4485127
+02.09.2014 12:20:27,771;17001900;4485127
+02.09.2014 12:20:28,294;17001900;4485127
+02.09.2014 12:20:28,789;17001893;4485127
+02.09.2014 12:20:29,294;17001900;4485127
+02.09.2014 12:20:29,796;17001900;4485127
+02.09.2014 12:20:30,308;17001900;4485127
+02.09.2014 12:20:30,822;17001900;4485127
+02.09.2014 12:20:31,312;17001900;4485127
+02.09.2014 12:20:31,832;17001981;4485127
+02.09.2014 12:20:32,328;17001787;4485127
+02.09.2014 12:20:32,842;17001900;4485127
+02.09.2014 12:20:33,343;17001900;4485127
+02.09.2014 12:20:33,855;17001800;4485127
+02.09.2014 12:20:34,365;17001900;4485127
+02.09.2014 12:20:34,865;17001800;4485127
+02.09.2014 12:20:35,377;17001900;4485127
+02.09.2014 12:20:35,893;17001781;4485127
+02.09.2014 12:20:36,386;17001781;4485127
+02.09.2014 12:20:36,897;17001900;4485127
+02.09.2014 12:20:37,394;17001900;4485127
+02.09.2014 12:20:37,912;17001900;4485127
+02.09.2014 12:20:38,413;17001793;4485127
+02.09.2014 12:20:38,932;17001900;4485127
+02.09.2014 12:20:39,428;17001918;4485127
+02.09.2014 12:20:39,951;17001900;4485127
+02.09.2014 12:20:40,438;17001800;4485127
+02.09.2014 12:20:40,956;17001787;4485127
+02.09.2014 12:20:41,462;17001762;4485127
+02.09.2014 12:20:41,963;17001781;4485127
+02.09.2014 12:20:42,471;17001900;4485127
+02.09.2014 12:20:42,995;17001893;4485127
+02.09.2014 12:20:43,489;17001900;4485127
+02.09.2014 12:20:44,010;17001893;4485127
+02.09.2014 12:20:44,499;17001793;4485127
+02.09.2014 12:20:45,009;17001900;4485127
+02.09.2014 12:20:45,504;17001793;4485127
+02.09.2014 12:20:46,033;17001900;4485127
+02.09.2014 12:20:46,530;17001900;4485127
+02.09.2014 12:20:47,041;17001900;4485127
+02.09.2014 12:20:47,539;17001800;4485127
+02.09.2014 12:20:48,052;17001787;4485127
+02.09.2014 12:20:48,554;17001900;4485127
+02.09.2014 12:20:49,066;17001900;4485127
+02.09.2014 12:20:49,563;17001900;4485127
+02.09.2014 12:20:50,074;17001900;4485127
+02.09.2014 12:20:50,590;17001900;4485127
+02.09.2014 12:20:51,102;17001887;4485127
+02.09.2014 12:20:51,600;17001900;4485127
+02.09.2014 12:20:52,111;17001900;4485127
+02.09.2014 12:20:52,616;17001900;4485127
+02.09.2014 12:20:53,113;17001781;4485127
+02.09.2014 12:20:53,623;17001900;4485127
+02.09.2014 12:20:54,140;17001925;4485127
+02.09.2014 12:20:54,639;17001900;4485127
+02.09.2014 12:20:55,151;17001781;4485127
+02.09.2014 12:20:55,669;17001906;4485127
+02.09.2014 12:20:56,165;17001900;4485127
+02.09.2014 12:20:56,678;17001756;4485127
+02.09.2014 12:20:57,192;17001868;4485127
+02.09.2014 12:20:57,689;17001900;4485127
+02.09.2014 12:20:58,201;17001900;4485127
+02.09.2014 12:20:58,712;17001912;4485127
+02.09.2014 12:20:59,209;17001906;4485127
+02.09.2014 12:20:59,720;17001800;4485127
+02.09.2014 12:21:00,232;17001906;4485127
+02.09.2014 12:21:00,742;17003956;4484255
+02.09.2014 12:21:01,238;17078262;4486377
+02.09.2014 12:21:01,745;17268362;4488662
+02.09.2014 12:21:02,253;17574281;4490011
+02.09.2014 12:21:02,763;17993006;4490029
+02.09.2014 12:21:03,262;18403206;4490017
+02.09.2014 12:21:03,773;18884493;4490017
+02.09.2014 12:21:04,279;19365018;4490017
+02.09.2014 12:21:04,792;19771006;4490017
+02.09.2014 12:21:05,303;20094931;4490017
+02.09.2014 12:21:05,813;20304000;4490017
+02.09.2014 12:21:06,311;20394756;4490017
+02.09.2014 12:21:06,821;20398987;4490017
+02.09.2014 12:21:07,332;20401350;4490017
+02.09.2014 12:21:07,826;20399843;4490017
+02.09.2014 12:21:08,332;20398837;4490017
+02.09.2014 12:21:08,851;20399506;4490017
+02.09.2014 12:21:09,345;20399387;4490017
+02.09.2014 12:21:09,860;20398887;4490017
+02.09.2014 12:21:10,371;20399418;4490017
+02.09.2014 12:21:10,884;20399400;4490017
+02.09.2014 12:21:11,382;20399600;4490017
+02.09.2014 12:21:11,888;20399312;4490017
+02.09.2014 12:21:12,394;20399500;4490017
+02.09.2014 12:21:12,893;20399268;4490017
+02.09.2014 12:21:13,404;20399300;4489261
+02.09.2014 12:21:13,914;20399400;4513226
+02.09.2014 12:21:14,420;20399393;4528476
+02.09.2014 12:21:14,932;20399287;4532970
+02.09.2014 12:21:15,426;20399181;4558767
+02.09.2014 12:21:15,953;20399475;4609651
+02.09.2014 12:21:16,448;20399018;4610034
+02.09.2014 12:21:16,959;20398993;4610005
+02.09.2014 12:21:17,470;20399431;4610005
+02.09.2014 12:21:17,966;20399193;4610005
+02.09.2014 12:21:18,476;20399300;4610005
+02.09.2014 12:21:18,992;20399200;4610005
+02.09.2014 12:21:19,495;20399400;4610005
+02.09.2014 12:21:19,994;20399100;4610005
+02.09.2014 12:21:20,506;20399400;4610005
+02.09.2014 12:21:21,020;20399518;4610005
+02.09.2014 12:21:21,523;20399187;4610005
+02.09.2014 12:21:22,034;20399443;4610005
+02.09.2014 12:21:22,530;20399406;4610005
+02.09.2014 12:21:23,043;20399150;4610005
+02.09.2014 12:21:23,537;20399181;4610005
+02.09.2014 12:21:24,063;20399168;4610005
+02.09.2014 12:21:24,559;20399387;4610005
+02.09.2014 12:21:25,072;20399425;4610005
+02.09.2014 12:21:25,574;20399518;4610005
+02.09.2014 12:21:26,090;20399175;4610005
+02.09.2014 12:21:26,603;20399000;4610005
+02.09.2014 12:21:27,100;20383137;4607901
+02.09.2014 12:21:27,614;20250106;4558825
+02.09.2014 12:21:28,113;20029231;4501186
+02.09.2014 12:21:28,631;19680456;4443837
+02.09.2014 12:21:29,125;19259993;4390895
+02.09.2014 12:21:29,637;18778837;4333575
+02.09.2014 12:21:30,143;18294912;4276151
+02.09.2014 12:21:30,662;17854068;4223226
+02.09.2014 12:21:31,153;17409787;4166081
+02.09.2014 12:21:31,664;16929743;4109011
+02.09.2014 12:21:32,169;16487493;4056273
+02.09.2014 12:21:32,689;16006368;3999261
+02.09.2014 12:21:33,201;15489468;3937843
+02.09.2014 12:21:33,693;15084493;3889668
+02.09.2014 12:21:34,199;14640375;3832813
+02.09.2014 12:21:34,748;14086631;3767377
+02.09.2014 12:21:35,205;13717656;3723773
+02.09.2014 12:21:35,770;13163925;3658238
+02.09.2014 12:21:36,227;12759137;3610226
+02.09.2014 12:21:36,787;12205062;3545005
+02.09.2014 12:21:37,243;11835000;3497110
+02.09.2014 12:21:37,807;11281031;3432029
+02.09.2014 12:21:38,258;10911031;3388779
+02.09.2014 12:21:38,763;10469206;3336796
+02.09.2014 12:21:39,270;9989600;3280459
+02.09.2014 12:21:39,782;9508062;3224203
+02.09.2014 12:21:40,275;9065593;3168186
+02.09.2014 12:21:40,794;8586531;3116517
+02.09.2014 12:21:41,294;8142343;3060598
+02.09.2014 12:21:41,807;7663218;3004854
+02.09.2014 12:21:42,312;7220068;2953436
+02.09.2014 12:21:42,840;6703068;2893360
+02.09.2014 12:21:43,319;6297656;2842081
+02.09.2014 12:21:43,834;5815718;2791005
+02.09.2014 12:21:44,342;5372850;2735656
+02.09.2014 12:21:44,853;4893850;2680656
+02.09.2014 12:21:45,350;4449775;2629982
+02.09.2014 12:21:45,864;3969618;2575133
+02.09.2014 12:21:46,370;3490218;2520593
+02.09.2014 12:21:46,884;3046825;2466290
+02.09.2014 12:21:47,376;2604556;2416302
+02.09.2014 12:21:47,894;2123937;2362517
+02.09.2014 12:21:48,394;1644431;2309133
+02.09.2014 12:21:48,911;1236075;2260093
+02.09.2014 12:21:49,406;910893;2206866
+02.09.2014 12:21:49,916;712068;2116744
+02.09.2014 12:21:50,422;607750;2094616
+02.09.2014 12:21:50,950;602187;2094825
+02.09.2014 12:21:51,432;601400;2094860
+02.09.2014 12:21:51,943;601518;2094848
+02.09.2014 12:21:52,449;601487;2094848
+02.09.2014 12:21:52,965;602100;2094848
+02.09.2014 12:21:53,455;601718;2094848
+02.09.2014 12:21:53,983;601487;2094848
+02.09.2014 12:21:54,476;601818;2094848
+02.09.2014 12:21:54,989;601693;2094848
+02.09.2014 12:21:55,494;602100;2094848
+02.09.2014 12:21:56,015;602206;2094848
+02.09.2014 12:21:56,505;601812;2094848
+02.09.2014 12:21:57,017;601800;2094848
+02.09.2014 12:21:57,537;602018;2094848
+02.09.2014 12:21:58,035;601381;2094848
+02.09.2014 12:21:58,545;602018;2094848
+02.09.2014 12:21:59,052;601806;2094848
+02.09.2014 12:21:59,554;601900;2094848
+02.09.2014 12:22:00,064;601800;2094848
+02.09.2014 12:22:00,575;601287;2094848
+02.09.2014 12:22:01,084;601918;2094848
+02.09.2014 12:22:01,595;601662;2094848
+02.09.2014 12:22:02,088;601693;2093645
+02.09.2014 12:22:02,604;601700;2074232
+02.09.2014 12:22:03,110;601887;2030087
+02.09.2014 12:22:03,623;601618;2023540
+02.09.2014 12:22:04,133;601950;2019308
+02.09.2014 12:22:04,629;601643;1986296
+02.09.2014 12:22:05,133;601487;1985017
+02.09.2014 12:22:05,649;601906;1985104
+02.09.2014 12:22:06,143;601631;1985093
+02.09.2014 12:22:06,659;601806;1985093
+02.09.2014 12:22:07,161;601606;1985093
+02.09.2014 12:22:07,677;601700;1985093
+02.09.2014 12:22:08,184;602025;1985093
+02.09.2014 12:22:08,682;601706;1985093
+02.09.2014 12:22:09,190;601918;1985093
+02.09.2014 12:22:09,700;601743;1985093
+02.09.2014 12:22:10,211;601368;1985093
+02.09.2014 12:22:10,725;601487;1985093
+02.09.2014 12:22:11,222;602087;1985093
+02.09.2014 12:22:11,733;602162;1985093
+02.09.2014 12:22:12,234;601481;1985093
+02.09.2014 12:22:12,745;601600;1985093
+02.09.2014 12:22:13,245;601700;1985093
+02.09.2014 12:22:13,758;601631;1985093
+02.09.2014 12:22:14,263;601693;1985093
+02.09.2014 12:22:14,770;601506;1985093
+02.09.2014 12:22:15,268;601493;1985093
+02.09.2014 12:22:15,794;601975;1985093
+02.09.2014 12:22:16,294;601606;1985093
+02.09.2014 12:22:16,794;601312;1985093
+02.09.2014 12:22:17,307;601700;1985093
+02.09.2014 12:22:17,819;601218;1985093
+02.09.2014 12:22:18,316;601787;1985093
+02.09.2014 12:22:18,830;601318;1985093
+02.09.2014 12:22:19,329;601706;1985093
+02.09.2014 12:22:19,839;601537;1985093
+02.09.2014 12:22:20,351;601631;1985093
+02.09.2014 12:22:20,863;601081;1985093
+02.09.2014 12:22:21,360;601700;1985093
+02.09.2014 12:22:21,871;601625;1985093
+02.09.2014 12:22:22,367;601968;1985093
+02.09.2014 12:22:22,887;601612;1985093
+02.09.2014 12:22:23,396;601818;1985093
+02.09.2014 12:22:23,894;601606;1985093
+02.09.2014 12:22:24,407;601575;1985093
+02.09.2014 12:22:24,903;601700;1985093
+02.09.2014 12:22:25,415;602125;1985093
+02.09.2014 12:22:25,927;602031;1985093
+02.09.2014 12:22:26,423;601593;1985093
+02.09.2014 12:22:26,937;601812;1985093
+02.09.2014 12:22:27,449;601793;1985093
+02.09.2014 12:22:27,961;601600;1985093
+02.09.2014 12:22:28,458;601800;1985093
+02.09.2014 12:22:28,970;601393;1985093
+02.09.2014 12:22:29,493;601687;1985093
+02.09.2014 12:22:29,984;601737;1985093
+02.09.2014 12:22:30,496;601487;1985093
+02.09.2014 12:22:31,003;602062;1985093
+02.09.2014 12:22:31,505;601625;1985093
+02.09.2014 12:22:32,012;601800;1985093
+02.09.2014 12:22:32,529;601925;1985093
+02.09.2014 12:22:33,024;601606;1985093
+02.09.2014 12:22:33,537;601806;1985093
+02.09.2014 12:22:34,040;601493;1985093
+02.09.2014 12:22:34,556;601762;1985093
+02.09.2014 12:22:35,043;601500;1985093
+02.09.2014 12:22:35,568;602025;1985093
+02.09.2014 12:22:36,070;601500;1985093
+02.09.2014 12:22:36,582;601581;1985093
+02.09.2014 12:22:37,084;601387;1985093
+02.09.2014 12:22:37,601;601825;1985093
+02.09.2014 12:22:38,103;601700;1985093
+02.09.2014 12:22:38,612;601400;1985093
+02.09.2014 12:22:39,103;601800;1985093
+02.09.2014 12:22:39,631;601700;1985093
+02.09.2014 12:22:40,115;601800;1985093
+02.09.2014 12:22:40,642;601368;1985093
+02.09.2014 12:22:41,135;601650;1985093
+02.09.2014 12:22:41,644;601600;1985093
+02.09.2014 12:22:42,156;601506;1985093
+02.09.2014 12:22:42,663;601618;1985093
+02.09.2014 12:22:43,167;601781;1985093
+02.09.2014 12:22:43,688;601700;1985093
+02.09.2014 12:22:44,179;601693;1985093
+02.09.2014 12:22:44,706;601593;1985093
+02.09.2014 12:22:45,194;602006;1985093
+02.09.2014 12:22:45,726;601581;1985093
+02.09.2014 12:22:46,209;601781;1985093
+02.09.2014 12:22:46,739;601381;1985093
+02.09.2014 12:22:47,220;601593;1985093
+02.09.2014 12:22:47,769;601906;1985093
+02.09.2014 12:22:48,238;601668;1985093
+02.09.2014 12:22:48,747;601831;1985093
+02.09.2014 12:22:49,249;601687;1985093
+02.09.2014 12:22:49,763;601693;1985093
+02.09.2014 12:22:50,267;601687;1985093
+02.09.2014 12:22:50,776;601800;1985093
+02.09.2014 12:22:51,282;602118;1985093
+02.09.2014 12:22:51,790;601150;1985093
+02.09.2014 12:22:52,293;601637;1985093
+02.09.2014 12:22:52,807;601825;1985093
+02.09.2014 12:22:53,311;601237;1985093
+02.09.2014 12:22:53,821;601881;1985093
+02.09.2014 12:22:54,314;601693;1985093
+02.09.2014 12:22:54,838;601493;1985093
+02.09.2014 12:22:55,326;601575;1985093
+02.09.2014 12:22:55,850;601850;1985093
+02.09.2014 12:22:56,343;601393;1985093
+02.09.2014 12:22:56,852;601706;1985093
+02.09.2014 12:22:57,357;601587;1985093
+02.09.2014 12:22:57,883;601456;1985093
+02.09.2014 12:22:58,373;601818;1985093
+02.09.2014 12:22:58,886;601575;1985093
+02.09.2014 12:22:59,389;601387;1985093
+02.09.2014 12:22:59,902;601787;1985093
+02.09.2014 12:23:00,411;601950;1985093
+02.09.2014 12:23:00,915;601606;1985093
+02.09.2014 12:23:01,425;602043;1985093
+02.09.2014 12:23:01,929;602018;1985093
+02.09.2014 12:23:02,446;601581;1985093
+02.09.2014 12:23:02,958;601500;1985093
+02.09.2014 12:23:03,471;601468;1985093
+02.09.2014 12:23:03,993;601800;1985093
+02.09.2014 12:23:04,506;601406;1985093
+02.09.2014 12:23:05,015;602168;1985093
+02.09.2014 12:23:05,519;601800;1985093
+02.09.2014 12:23:06,044;602275;1985093
+02.09.2014 12:23:06,548;602112;1985093
+02.09.2014 12:23:07,045;601587;1985093
+02.09.2014 12:23:07,550;601568;1985093
+02.09.2014 12:23:08,058;601668;1985093
+02.09.2014 12:23:08,562;601775;1985093
+02.09.2014 12:23:09,075;601800;1985093
+02.09.2014 12:23:09,581;601787;1985093
+02.09.2014 12:23:10,088;601687;1985093
+02.09.2014 12:23:10,598;601400;1985093
+02.09.2014 12:23:11,106;601687;1985093
+02.09.2014 12:23:11,616;601675;1985093
+02.09.2014 12:23:12,119;601687;1985093
+02.09.2014 12:23:12,612;601500;1985093
+02.09.2014 12:23:13,131;602125;1985093
+02.09.2014 12:23:13,624;601493;1985093
+02.09.2014 12:23:14,148;601856;1985093
+02.09.2014 12:23:14,642;601825;1985093
+02.09.2014 12:23:15,151;601581;1985093
+02.09.2014 12:23:15,671;601700;1985093
+02.09.2014 12:23:16,182;601812;1985093
+02.09.2014 12:23:16,672;601806;1985093
+02.09.2014 12:23:17,194;601662;1985093
+02.09.2014 12:23:17,686;601625;1985093
+02.09.2014 12:23:18,198;602100;1985093
+02.09.2014 12:23:18,709;601700;1985093
+02.09.2014 12:23:19,220;601700;1985093
+02.09.2014 12:23:19,712;601800;1985093
+02.09.2014 12:23:20,235;601518;1985093
+02.09.2014 12:23:20,741;602337;1985093
+02.09.2014 12:23:21,250;601681;1985093
+02.09.2014 12:23:21,741;601175;1985093
+02.09.2014 12:23:22,262;601787;1985093
+02.09.2014 12:23:22,754;601681;1985093
+02.09.2014 12:23:23,264;601575;1985093
+02.09.2014 12:23:23,767;601706;1985093
+02.09.2014 12:23:24,293;601800;1985093
+02.09.2014 12:23:24,786;602000;1985093
+02.09.2014 12:23:25,295;601850;1985093
+02.09.2014 12:23:25,806;601625;1985093
+02.09.2014 12:23:26,313;601606;1985093
+02.09.2014 12:23:26,815;601818;1985093
+02.09.2014 12:23:27,325;601900;1985093
+02.09.2014 12:23:27,831;601668;1985093
+02.09.2014 12:23:28,336;601587;1985093
+02.09.2014 12:23:28,844;601662;1985093
+02.09.2014 12:23:29,352;601381;1985093
+02.09.2014 12:23:29,855;601506;1985093
+02.09.2014 12:23:30,365;601831;1985093
+02.09.2014 12:23:30,868;601875;1985093
+02.09.2014 12:23:31,385;601743;1985093
+02.09.2014 12:23:31,892;601487;1985093
+02.09.2014 12:23:32,396;601431;1985093
+02.09.2014 12:23:32,920;601818;1985093
+02.09.2014 12:23:33,413;601706;1985093
+02.09.2014 12:23:33,932;601631;1985093
+02.09.2014 12:23:34,429;601750;1985093
+02.09.2014 12:23:34,941;602200;1985093
+02.09.2014 12:23:35,450;601775;1985093
+02.09.2014 12:23:35,958;601593;1985093
+02.09.2014 12:23:36,460;601925;1985093
+02.09.2014 12:23:36,969;601581;1985093
+02.09.2014 12:23:37,480;601893;1985093
+02.09.2014 12:23:37,988;601775;1985093
+02.09.2014 12:23:38,474;601468;1985093
+02.09.2014 12:23:38,999;601250;1985093
+02.09.2014 12:23:39,492;601812;1985093
+02.09.2014 12:23:40,013;601581;1985093
+02.09.2014 12:23:40,521;601606;1985093
+02.09.2014 12:23:41,030;601600;1985093
+02.09.2014 12:23:41,538;601600;1985093
+02.09.2014 12:23:42,051;601850;1985093
+02.09.2014 12:23:42,571;601706;1985093
+02.09.2014 12:23:43,086;601925;1985093
+02.09.2014 12:23:43,601;601312;1985093
+02.09.2014 12:23:44,102;602175;1985093
+02.09.2014 12:23:44,611;601687;1985093
+02.09.2014 12:23:45,131;601793;1985093
+02.09.2014 12:23:45,646;601812;1985093
+02.09.2014 12:23:46,154;601437;1985093
+02.09.2014 12:23:46,663;601343;1985093
+02.09.2014 12:23:47,189;601387;1985093
+02.09.2014 12:23:47,692;601075;1985093
+02.09.2014 12:23:48,202;601687;1985093
+02.09.2014 12:23:48,693;601831;1985093
+02.09.2014 12:23:49,232;601606;1985093
+02.09.2014 12:23:49,713;601712;1985093
+02.09.2014 12:23:50,223;601800;1985093
+02.09.2014 12:23:50,730;601800;1985093
+02.09.2014 12:23:51,248;601600;1985093
+02.09.2014 12:23:51,751;601756;1985093
+02.09.2014 12:23:52,284;602043;1985093
+02.09.2014 12:23:52,752;601812;1985093
+02.09.2014 12:23:53,271;601612;1985093
+02.09.2014 12:23:53,777;601600;1985093
+02.09.2014 12:23:54,290;601800;1985093
+02.09.2014 12:23:54,827;602056;1985093
+02.09.2014 12:23:55,292;601368;1985093
+02.09.2014 12:23:55,848;601593;1985093
+02.09.2014 12:23:56,320;601700;1985093
+02.09.2014 12:23:56,850;601918;1985093
+02.09.2014 12:23:57,323;601912;1985093
+02.09.2014 12:23:57,872;601700;1985093
+02.09.2014 12:23:58,336;602131;1985093
+02.09.2014 12:23:58,846;601850;1985093
+02.09.2014 12:23:59,353;601812;1985093
+02.09.2014 12:23:59,857;601868;1985093
+02.09.2014 12:24:00,366;601918;1985093
+02.09.2014 12:24:00,869;601775;1985093
+02.09.2014 12:24:01,385;601387;1985093
+02.09.2014 12:24:01,883;601937;1985093
+02.09.2014 12:24:02,397;602200;1985093
+02.09.2014 12:24:02,905;601925;1985093
+02.09.2014 12:24:03,410;602306;1985093
+02.09.2014 12:24:03,924;601812;1985093
+02.09.2014 12:24:04,427;601362;1985093
+02.09.2014 12:24:04,936;601543;1985093
+02.09.2014 12:24:05,446;601918;1985093
+02.09.2014 12:24:05,952;601593;1985093
+02.09.2014 12:24:06,459;601275;1985093
+02.09.2014 12:24:06,968;601993;1985093
+02.09.2014 12:24:07,465;601900;1985093
+02.09.2014 12:24:07,973;601650;1985093
+02.09.2014 12:24:08,480;601906;1985093
+02.09.2014 12:24:08,993;602012;1985093
+02.09.2014 12:24:09,498;601381;1985093
+02.09.2014 12:24:10,006;601931;1985093
+02.09.2014 12:24:10,510;601650;1985093
+02.09.2014 12:24:11,024;601587;1985093
+02.09.2014 12:24:11,528;601737;1985093
+02.09.2014 12:24:12,036;601275;1985093
+02.09.2014 12:24:12,539;601875;1985093
+02.09.2014 12:24:13,048;601456;1985093
+02.09.2014 12:24:13,558;601687;1985093
+02.09.2014 12:24:14,066;601918;1985093
+02.09.2014 12:24:14,571;601693;1985093
+02.09.2014 12:24:15,082;601706;1985093
+02.09.2014 12:24:15,591;601918;1985093
+02.09.2014 12:24:16,099;601718;1985093
+02.09.2014 12:24:16,592;601775;1985093
+02.09.2014 12:24:17,111;601800;1985093
+02.09.2014 12:24:17,605;601943;1985093
+02.09.2014 12:24:18,113;601700;1985093
+02.09.2014 12:24:18,618;601706;1985093
+02.09.2014 12:24:19,132;602106;1985093
+02.09.2014 12:24:19,637;601581;1985093
+02.09.2014 12:24:20,146;602125;1985093
+02.09.2014 12:24:20,648;601125;1985093
+02.09.2014 12:24:21,163;601468;1985093
+02.09.2014 12:24:21,666;601675;1985093
+02.09.2014 12:24:22,173;602118;1985093
+02.09.2014 12:24:22,683;601475;1985093
+02.09.2014 12:24:23,191;601518;1985093
+02.09.2014 12:24:23,688;601918;1985093
+02.09.2014 12:24:24,201;601937;1985093
+02.09.2014 12:24:24,705;601975;1985093
+02.09.2014 12:24:25,215;601331;1985093
+02.09.2014 12:24:25,724;601800;1985093
+02.09.2014 12:24:26,233;601987;1985093
+02.09.2014 12:24:26,735;601493;1985093
+02.09.2014 12:24:27,245;601506;1985093
+02.09.2014 12:24:27,748;602218;1985093
+02.09.2014 12:24:28,257;601706;1985093
+02.09.2014 12:24:28,765;602118;1985093
+02.09.2014 12:24:29,275;601687;1985093
+02.09.2014 12:24:29,780;601900;1985093
+02.09.2014 12:24:30,290;601700;1985093
+02.09.2014 12:24:30,792;602018;1985093
+02.09.2014 12:24:31,308;601581;1985093
+02.09.2014 12:24:31,809;601631;1985093
+02.09.2014 12:24:32,318;601787;1985093
+02.09.2014 12:24:32,821;602025;1985093
+02.09.2014 12:24:33,330;601906;1985093
+02.09.2014 12:24:33,843;601706;1985093
+02.09.2014 12:24:34,353;602118;1985093
+02.09.2014 12:24:34,876;601900;1985093
+02.09.2014 12:24:35,388;602012;1985093
+02.09.2014 12:24:35,901;601506;1985093
+02.09.2014 12:24:36,422;601387;1985093
+02.09.2014 12:24:36,915;601800;1985093
+02.09.2014 12:24:37,440;602012;1985093
+02.09.2014 12:24:37,932;601768;1985093
+02.09.2014 12:24:38,441;601775;1985093
+02.09.2014 12:24:38,949;601387;1985093
+02.09.2014 12:24:39,466;601806;1985093
+02.09.2014 12:24:39,958;601800;1985093
+02.09.2014 12:24:40,482;601587;1985093
+02.09.2014 12:24:40,972;601687;1985093
+02.09.2014 12:24:41,484;601700;1985093
+02.09.2014 12:24:41,993;601825;1985093
+02.09.2014 12:24:42,502;601931;1985093
+02.09.2014 12:24:43,005;602100;1985093
+02.09.2014 12:24:43,515;601568;1985093
+02.09.2014 12:24:44,023;601350;1985093
+02.09.2014 12:24:44,532;601675;1985093
+02.09.2014 12:24:45,042;601475;1985093
+02.09.2014 12:24:45,545;601593;1985093
+02.09.2014 12:24:46,049;601700;1985093
+02.09.2014 12:24:46,557;601987;1985093
+02.09.2014 12:24:47,050;601281;1985093
+02.09.2014 12:24:47,576;601693;1985093
+02.09.2014 12:24:48,082;601693;1985093
+02.09.2014 12:24:48,591;602075;1985093
+02.09.2014 12:24:49,094;602306;1985093
+02.09.2014 12:24:49,609;601675;1985093
+02.09.2014 12:24:50,101;601612;1985093
+02.09.2014 12:24:50,614;601200;1985093
+02.09.2014 12:24:51,120;601468;1985093
+02.09.2014 12:24:51,623;601612;1985093
+02.09.2014 12:24:52,134;601393;1985093
+02.09.2014 12:24:52,647;601906;1985093
+02.09.2014 12:24:53,143;601443;1985093
+02.09.2014 12:24:53,656;601762;1985093
+02.09.2014 12:24:54,162;601356;1985093
+02.09.2014 12:24:54,681;601387;1985093
+02.09.2014 12:24:55,165;601693;1985093
+02.09.2014 12:24:55,687;601281;1985093
+02.09.2014 12:24:56,201;601500;1985093
+02.09.2014 12:24:56,717;601706;1985093
+02.09.2014 12:24:57,231;601893;1985093
+02.09.2014 12:24:57,750;601906;1985093
+02.09.2014 12:24:58,251;601912;1985093
+02.09.2014 12:24:58,764;601806;1985093
+02.09.2014 12:24:59,279;601650;1985093
+02.09.2014 12:24:59,776;601600;1985093
+02.09.2014 12:25:00,287;601475;1985093
+02.09.2014 12:25:00,784;601843;1985093
+02.09.2014 12:25:01,302;601681;1985093
+02.09.2014 12:25:01,792;601706;1985093
+02.09.2014 12:25:02,322;601687;1985093
+02.09.2014 12:25:02,817;601806;1985093
+02.09.2014 12:25:03,343;601387;1985093
+02.09.2014 12:25:03,832;601725;1985093
+02.09.2014 12:25:04,369;601481;1985093
+02.09.2014 12:25:04,855;601481;1985093
+02.09.2014 12:25:05,393;601700;1985093
+02.09.2014 12:25:05,882;601906;1985093
+02.09.2014 12:25:06,401;602006;1985093
+02.09.2014 12:25:06,890;601543;1985093
+02.09.2014 12:25:07,422;601700;1985093
+02.09.2014 12:25:07,899;601518;1985093
+02.09.2014 12:25:08,411;601537;1985093
+02.09.2014 12:25:08,911;601925;1985093
+02.09.2014 12:25:09,420;601587;1985093
+02.09.2014 12:25:09,931;601587;1985093
+02.09.2014 12:25:10,425;601443;1985093
+02.09.2014 12:25:10,942;602368;1985093
+02.09.2014 12:25:11,440;625475;1985127
+02.09.2014 12:25:11,956;766368;2032755
+02.09.2014 12:25:12,460;1020406;2090244
+02.09.2014 12:25:12,961;1355662;2143162
+02.09.2014 12:25:13,471;1782750;2200505
+02.09.2014 12:25:13,983;2264200;2257918
+02.09.2014 12:25:14,497;2746268;2310802
+02.09.2014 12:25:14,998;3189631;2368052
+02.09.2014 12:25:15,493;3633512;2425156
+02.09.2014 12:25:16,027;4150368;2482296
+02.09.2014 12:25:16,515;4593781;2535023
+02.09.2014 12:25:17,025;5037325;2592058
+02.09.2014 12:25:17,530;5515937;2649093
+02.09.2014 12:25:18,030;5958806;2701726
+02.09.2014 12:25:18,536;6402406;2758662
+02.09.2014 12:25:19,051;6881518;2815581
+02.09.2014 12:25:19,557;7361856;2867953
+02.09.2014 12:25:20,068;7842193;2924691
+02.09.2014 12:25:20,582;8321362;2981308
+02.09.2014 12:25:21,092;8765056;3037924
+02.09.2014 12:25:21,586;9207962;3090162
+02.09.2014 12:25:22,098;9688618;3146750
+02.09.2014 12:25:22,597;10132031;3198854
+02.09.2014 12:25:23,104;10610650;3255151
+02.09.2014 12:25:23,611;11054000;3311325
+02.09.2014 12:25:24,126;11535231;3367540
+02.09.2014 12:25:24,620;11976900;3419436
+02.09.2014 12:25:25,141;12494625;3479906
+02.09.2014 12:25:25,636;12938181;3531500
+02.09.2014 12:25:26,151;13380950;3583040
+02.09.2014 12:25:26,657;13860893;3638825
+02.09.2014 12:25:27,173;14303500;3694494
+02.09.2014 12:25:27,666;14783862;3745796
+02.09.2014 12:25:28,181;15264100;3805575
+02.09.2014 12:25:28,693;15706912;3856610
+02.09.2014 12:25:29,192;16150137;3907348
+02.09.2014 12:25:29,698;16630656;3962215
+02.09.2014 12:25:30,209;17072506;4017046
+02.09.2014 12:25:30,721;17552768;4071633
+02.09.2014 12:25:31,232;18033925;4126017
+02.09.2014 12:25:31,727;18476156;4175906
+02.09.2014 12:25:32,238;18920100;4229796
+02.09.2014 12:25:32,732;19399062;4278976
+02.09.2014 12:25:33,243;19797406;4331906
+02.09.2014 12:25:33,749;20113587;4386843
+02.09.2014 12:25:34,265;20314431;4475377
+02.09.2014 12:25:34,770;20400650;4490174
+02.09.2014 12:25:35,283;20399275;4490005
+02.09.2014 12:25:35,776;20401068;4489988
+02.09.2014 12:25:36,293;20400331;4489982
+02.09.2014 12:25:36,803;20398800;4489982
+02.09.2014 12:25:37,309;20399231;4489982
+02.09.2014 12:25:37,825;20399125;4489982
+02.09.2014 12:25:38,325;20399100;4489982
+02.09.2014 12:25:38,831;20398987;4489982
+02.09.2014 12:25:39,348;20399212;4489982
+02.09.2014 12:25:39,847;20399100;4489982
+02.09.2014 12:25:40,342;20398975;4489982
+02.09.2014 12:25:40,869;20399156;4489982
+02.09.2014 12:25:41,363;20399300;4489982
+02.09.2014 12:25:41,874;20398975;4489133
+02.09.2014 12:25:42,382;20399187;4514976
+02.09.2014 12:25:42,897;20399206;4528691
+02.09.2014 12:25:43,397;20399306;4532959
+02.09.2014 12:25:43,910;20399000;4575895
+02.09.2014 12:25:44,406;20399000;4610005
+02.09.2014 12:25:44,915;20399087;4610017
+02.09.2014 12:25:45,429;20399431;4610023
+02.09.2014 12:25:45,939;20399550;4610011
+02.09.2014 12:25:46,435;20399306;4610011
+02.09.2014 12:25:46,945;20399368;4610011
+02.09.2014 12:25:47,444;20399081;4610011
+02.09.2014 12:25:47,968;20399462;4610011
+02.09.2014 12:25:48,457;20398868;4610011
+02.09.2014 12:25:48,972;20399100;4610011
+02.09.2014 12:25:49,486;20399425;4610011
+02.09.2014 12:25:50,002;20399118;4610011
+02.09.2014 12:25:50,514;20399300;4610011
+02.09.2014 12:25:51,030;20399300;4610011
+02.09.2014 12:25:51,546;20399306;4610011
+02.09.2014 12:25:52,055;20399175;4610011
+02.09.2014 12:25:52,565;20399400;4610011
+02.09.2014 12:25:53,069;20398975;4610011
+02.09.2014 12:25:53,580;20399131;4610011
+02.09.2014 12:25:54,071;20399393;4610011
+02.09.2014 12:25:54,600;20399500;4610011
+02.09.2014 12:25:55,087;20398512;4608895
+02.09.2014 12:25:55,602;20321437;4588523
+02.09.2014 12:25:56,106;20148193;4530860
+02.09.2014 12:25:56,616;19877137;4477761
+02.09.2014 12:25:57,126;19474012;4420343
+02.09.2014 12:25:57,637;18990293;4363069
+02.09.2014 12:25:58,140;18548687;4310110
+02.09.2014 12:25:58,650;18066231;4248284
+02.09.2014 12:25:59,153;17587493;4195418
+02.09.2014 12:25:59,661;17179881;4142604
+02.09.2014 12:26:00,169;16700262;4085436
+02.09.2014 12:26:00,681;16219631;4028377
+02.09.2014 12:26:01,171;15776087;3975610
+02.09.2014 12:26:01,685;15297525;3918412
+02.09.2014 12:26:02,188;14853975;3861360
+02.09.2014 12:26:02,702;14373250;3804441
+02.09.2014 12:26:03,210;13894150;3751912
+02.09.2014 12:26:03,717;13450731;3695093
+02.09.2014 12:26:04,227;12970293;3638313
+02.09.2014 12:26:04,737;12529693;3581558
+02.09.2014 12:26:05,241;12047731;3525046
+02.09.2014 12:26:05,751;11567393;3472784
+02.09.2014 12:26:06,245;11124293;3420511
+02.09.2014 12:26:06,754;10680856;3364046
+02.09.2014 12:26:07,281;10202787;3307593
+02.09.2014 12:26:07,772;9759162;3251284
+02.09.2014 12:26:08,283;9278112;3199366
+02.09.2014 12:26:08,788;8800150;3143191
+02.09.2014 12:26:09,304;8355793;3087250
+02.09.2014 12:26:09,807;7876075;3031226
+02.09.2014 12:26:10,317;7433725;2979587
+02.09.2014 12:26:10,821;6953493;2923732
+02.09.2014 12:26:11,331;6473181;2868075
+02.09.2014 12:26:11,826;6030062;2816779
+02.09.2014 12:26:12,365;5512175;2757203
+02.09.2014 12:26:12,844;5107712;2706151
+02.09.2014 12:26:13,354;4626693;2655180
+02.09.2014 12:26:13,883;4147131;2591744
+02.09.2014 12:26:14,373;3740431;2545598
+02.09.2014 12:26:14,920;3186375;2486970
+02.09.2014 12:26:15,389;2780737;2440901
+02.09.2014 12:26:15,925;2264406;2382674
+02.09.2014 12:26:16,404;1857587;2332715
+02.09.2014 12:26:16,940;1351737;2275337
+02.09.2014 12:26:17,418;1041781;2230622
+02.09.2014 12:26:17,911;796031;2162720
+02.09.2014 12:26:18,420;639593;2096093
+02.09.2014 12:26:18,924;599200;2094720
+02.09.2014 12:26:19,439;600587;2094947
+02.09.2014 12:26:19,944;600987;2095069
+02.09.2014 12:26:20,454;601287;2095063
+02.09.2014 12:26:20,959;601943;2095052
+02.09.2014 12:26:21,468;601906;2095052
+02.09.2014 12:26:21,965;601993;2095052
+02.09.2014 12:26:22,489;601487;2095052
+02.09.2014 12:26:22,980;601787;2095052
+02.09.2014 12:26:23,491;601793;2095052
+02.09.2014 12:26:23,997;601668;2095052
+02.09.2014 12:26:24,512;601856;2095052
+02.09.2014 12:26:25,017;601887;2095052
+02.09.2014 12:26:25,526;601268;2095052
+02.09.2014 12:26:26,019;601425;2095052
+02.09.2014 12:26:26,540;601587;2095052
+02.09.2014 12:26:27,039;602018;2087877
+02.09.2014 12:26:27,558;601687;2034540
+02.09.2014 12:26:28,068;601762;2025191
+02.09.2014 12:26:28,584;601825;2020017
+02.09.2014 12:26:29,093;602031;1994686
+02.09.2014 12:26:29,620;602212;1985186
+02.09.2014 12:26:30,129;601718;1985366
+02.09.2014 12:26:30,622;601687;1985366
+02.09.2014 12:26:31,149;602012;1985360
+02.09.2014 12:26:31,646;601493;1985360
+02.09.2014 12:26:32,156;601568;1985360
+02.09.2014 12:26:32,652;601437;1985360
+02.09.2014 12:26:33,161;602125;1985360
+02.09.2014 12:26:33,672;601693;1985360
+02.09.2014 12:26:34,188;601981;1985360
+02.09.2014 12:26:34,683;602000;1985360
+02.09.2014 12:26:35,194;601968;1985360
+02.09.2014 12:26:35,701;602000;1985360
+02.09.2014 12:26:36,217;602000;1985360
+02.09.2014 12:26:36,710;601481;1985360
+02.09.2014 12:26:37,220;601725;1985360
+02.09.2014 12:26:37,726;602106;1985360
+02.09.2014 12:26:38,236;601768;1985360
+02.09.2014 12:26:38,753;601731;1985360
+02.09.2014 12:26:39,259;601818;1985360
+02.09.2014 12:26:39,773;601756;1985360
+02.09.2014 12:26:40,269;602006;1985360
+02.09.2014 12:26:40,779;602012;1985360
+02.09.2014 12:26:41,287;601650;1985360
+02.09.2014 12:26:41,797;601975;1985360
+02.09.2014 12:26:42,292;601931;1985360
+02.09.2014 12:26:42,819;601506;1985360
+02.09.2014 12:26:43,314;601687;1985360
+02.09.2014 12:26:43,825;601606;1985360
+02.09.2014 12:26:44,337;601731;1985360
+02.09.2014 12:26:44,851;601575;1985360
+02.09.2014 12:26:45,362;601800;1985360
+02.09.2014 12:26:45,891;602156;1985360
+02.09.2014 12:26:46,392;601931;1985360
+02.09.2014 12:26:46,902;602000;1985360
+02.09.2014 12:26:47,413;601406;1985360
+02.09.2014 12:26:47,912;602112;1985360
+02.09.2014 12:26:48,425;601912;1985360
+02.09.2014 12:26:48,920;601937;1985360
+02.09.2014 12:26:49,446;602431;1985360
+02.09.2014 12:26:49,935;601787;1985360
+02.09.2014 12:26:50,449;601793;1985360
+02.09.2014 12:26:50,960;602000;1985360
+02.09.2014 12:26:51,461;601918;1985360
+02.09.2014 12:26:51,971;601812;1985360
+02.09.2014 12:26:52,482;601937;1985360
+02.09.2014 12:26:52,985;601481;1985360
+02.09.2014 12:26:53,495;601800;1985360
+02.09.2014 12:26:54,000;601450;1985360
+02.09.2014 12:26:54,513;602643;1985360
+02.09.2014 12:26:55,017;602131;1985360
+02.09.2014 12:26:55,526;602306;1985360
+02.09.2014 12:26:56,029;601893;1985360
+02.09.2014 12:26:56,538;601350;1985360
+02.09.2014 12:26:57,030;601793;1985360
+02.09.2014 12:26:57,556;602256;1985360
+02.09.2014 12:26:58,064;601825;1985360
+02.09.2014 12:26:58,594;601906;1985360
+02.09.2014 12:26:59,089;601800;1985360
+02.09.2014 12:26:59,614;601818;1985360
+02.09.2014 12:27:00,123;601481;1985360
+02.09.2014 12:27:00,633;602212;1985360
+02.09.2014 12:27:01,141;601762;1985360
+02.09.2014 12:27:01,640;601475;1985360
+02.09.2014 12:27:02,154;601900;1985360
+02.09.2014 12:27:02,659;602387;1985360
+02.09.2014 12:27:03,166;602081;1985360
+02.09.2014 12:27:03,671;601800;1985360
+02.09.2014 12:27:04,191;601693;1985360
+02.09.2014 12:27:04,689;601987;1985360
+02.09.2014 12:27:05,190;601900;1985360
+02.09.2014 12:27:05,693;601812;1985360
+02.09.2014 12:27:06,219;601987;1985360
+02.09.2014 12:27:06,711;601706;1985360
+02.09.2014 12:27:07,219;601693;1985360
+02.09.2014 12:27:07,722;601593;1985360
+02.09.2014 12:27:08,233;601893;1985360
+02.09.2014 12:27:08,742;601806;1985360
+02.09.2014 12:27:09,252;601631;1985360
+02.09.2014 12:27:09,760;602106;1985360
+02.09.2014 12:27:10,270;601900;1985360
+02.09.2014 12:27:10,791;601962;1985360
+02.09.2014 12:27:11,284;601693;1985360
+02.09.2014 12:27:11,792;602243;1985360
+02.09.2014 12:27:12,297;601812;1985360
+02.09.2014 12:27:12,806;601887;1985360
+02.09.2014 12:27:13,310;601706;1985360
+02.09.2014 12:27:13,824;601556;1985360
+02.09.2014 12:27:14,339;602212;1985360
+02.09.2014 12:27:14,837;601668;1985360
+02.09.2014 12:27:15,340;601800;1985360
+02.09.2014 12:27:15,853;601875;1985360
+02.09.2014 12:27:16,357;601818;1985360
+02.09.2014 12:27:16,866;601300;1985360
+02.09.2014 12:27:17,359;601775;1985360
+02.09.2014 12:27:17,886;601893;1985360
+02.09.2014 12:27:18,390;602000;1985360
+02.09.2014 12:27:18,898;601575;1985360
+02.09.2014 12:27:19,391;602106;1985360
+02.09.2014 12:27:19,900;601987;1985360
+02.09.2014 12:27:20,410;601693;1985360
+02.09.2014 12:27:20,929;601287;1985360
+02.09.2014 12:27:21,459;602193;1985360
+02.09.2014 12:27:21,939;601287;1985360
+02.09.2014 12:27:22,444;601687;1985360
+02.09.2014 12:27:22,941;602156;1985360
+02.09.2014 12:27:23,491;602137;1985360
+02.09.2014 12:27:23,979;601812;1985360
+02.09.2014 12:27:24,510;601793;1985360
+02.09.2014 12:27:24,984;601906;1985360
+02.09.2014 12:27:25,489;601600;1985360
+02.09.2014 12:27:26,002;601600;1985360
+02.09.2014 12:27:26,495;602150;1985360
+02.09.2014 12:27:27,002;601231;1985360
+02.09.2014 12:27:27,514;601700;1985360
+02.09.2014 12:27:28,015;602100;1985360
+02.09.2014 12:27:28,531;602000;1985360
+02.09.2014 12:27:29,033;601900;1985360
+02.09.2014 12:27:29,537;601800;1985360
+02.09.2014 12:27:30,045;601843;1985360
+02.09.2014 12:27:30,549;601993;1985360
+02.09.2014 12:27:31,063;601906;1985360
+02.09.2014 12:27:31,567;601600;1985360
+02.09.2014 12:27:32,078;601975;1985360
+02.09.2014 12:27:32,569;601900;1985360
+02.09.2014 12:27:33,090;601887;1985360
+02.09.2014 12:27:33,582;601706;1985360
+02.09.2014 12:27:34,107;602131;1985360
+02.09.2014 12:27:34,604;602087;1985360
+02.09.2014 12:27:35,120;601800;1985360
+02.09.2014 12:27:35,629;602081;1985360
+02.09.2014 12:27:36,138;601918;1985360
+02.09.2014 12:27:36,634;601987;1985360
+02.09.2014 12:27:37,143;602112;1985360
+02.09.2014 12:27:37,649;601812;1985360
+02.09.2014 12:27:38,159;601368;1985360
+02.09.2014 12:27:38,652;601793;1985360
+02.09.2014 12:27:39,177;601900;1985360
+02.09.2014 12:27:39,669;601381;1985360
+02.09.2014 12:27:40,192;601893;1985360
+02.09.2014 12:27:40,684;601718;1985360
+02.09.2014 12:27:41,209;601800;1985360
+02.09.2014 12:27:41,717;601950;1985360
+02.09.2014 12:27:42,210;601587;1985360
+02.09.2014 12:27:42,737;601925;1985360
+02.09.2014 12:27:43,224;602137;1985360
+02.09.2014 12:27:43,748;602006;1985360
+02.09.2014 12:27:44,242;602087;1985360
+02.09.2014 12:27:44,750;601675;1985360
+02.09.2014 12:27:45,272;601900;1985360
+02.09.2014 12:27:45,780;601787;1985360
+02.09.2014 12:27:46,274;601575;1985360
+02.09.2014 12:27:46,782;602025;1985360
+02.09.2014 12:27:47,286;601912;1985360
+02.09.2014 12:27:47,800;601918;1985360
+02.09.2014 12:27:48,304;601700;1985360
+02.09.2014 12:27:48,813;601993;1985360
+02.09.2014 12:27:49,319;601637;1985360
+02.09.2014 12:27:49,825;601912;1985360
+02.09.2014 12:27:50,329;602006;1985360
+02.09.2014 12:27:50,843;602012;1985360
+02.09.2014 12:27:51,348;602125;1985360
+02.09.2014 12:27:51,855;602331;1985360
+02.09.2014 12:27:52,366;601893;1985360
+02.09.2014 12:27:52,880;601600;1985360
+02.09.2014 12:27:53,373;601843;1985360
+02.09.2014 12:27:53,884;601843;1985360
+02.09.2014 12:27:54,389;602200;1985360
+02.09.2014 12:27:54,896;601900;1985360
+02.09.2014 12:27:55,405;602337;1985360
+02.09.2014 12:27:55,913;601568;1985360
+02.09.2014 12:27:56,419;602012;1985360
+02.09.2014 12:27:56,927;601887;1985360
+02.09.2014 12:27:57,420;602212;1985360
+02.09.2014 12:27:57,945;602006;1985360
+02.09.2014 12:27:58,449;601787;1985360
+02.09.2014 12:27:58,958;602100;1985360
+02.09.2014 12:27:59,460;602075;1985360
+02.09.2014 12:27:59,968;602325;1985360
+02.09.2014 12:28:00,480;602006;1985360
+02.09.2014 12:28:00,988;601587;1985360
+02.09.2014 12:28:01,490;601668;1985360
+02.09.2014 12:28:01,990;601475;1985360
+02.09.2014 12:28:02,499;601687;1985360
+02.09.2014 12:28:03,021;601656;1985360
+02.09.2014 12:28:03,506;602393;1985360
+02.09.2014 12:28:04,020;602012;1985360
+02.09.2014 12:28:04,524;601806;1985360
+02.09.2014 12:28:05,033;602243;1985360
+02.09.2014 12:28:05,542;601800;1985360
+02.09.2014 12:28:06,051;602187;1985360
+02.09.2014 12:28:06,561;602100;1985360
+02.09.2014 12:28:07,086;601900;1985360
+02.09.2014 12:28:07,600;602106;1985360
+02.09.2014 12:28:08,120;601943;1985360
+02.09.2014 12:28:08,620;602237;1985360
+02.09.2014 12:28:09,127;601525;1985360
+02.09.2014 12:28:09,644;601656;1985360
+02.09.2014 12:28:10,147;602106;1985360
+02.09.2014 12:28:10,656;601806;1985360
+02.09.2014 12:28:11,149;601581;1985360
+02.09.2014 12:28:11,659;602137;1985360
+02.09.2014 12:28:12,189;601456;1985360
+02.09.2014 12:28:12,686;602075;1985360
+02.09.2014 12:28:13,194;602106;1985360
+02.09.2014 12:28:13,688;602106;1985360
+02.09.2014 12:28:14,216;601987;1985360
+02.09.2014 12:28:14,711;601687;1985360
+02.09.2014 12:28:15,222;602106;1985360
+02.09.2014 12:28:15,727;601556;1985360
+02.09.2014 12:28:16,239;601987;1985360
+02.09.2014 12:28:16,739;601468;1985360
+02.09.2014 12:28:17,251;601681;1985360
+02.09.2014 12:28:17,756;602225;1985360
+02.09.2014 12:28:18,271;601900;1985360
+02.09.2014 12:28:18,783;602162;1985360
+02.09.2014 12:28:19,280;601887;1985360
+02.09.2014 12:28:19,788;602006;1985360
+02.09.2014 12:28:20,299;601925;1985360
+02.09.2014 12:28:20,796;601700;1985360
+02.09.2014 12:28:21,304;602018;1985360
+02.09.2014 12:28:21,808;601318;1985360
+02.09.2014 12:28:22,324;601900;1985360
+02.09.2014 12:28:22,834;601662;1985360
+02.09.2014 12:28:23,350;601806;1985360
+02.09.2014 12:28:23,860;601818;1985360
+02.09.2014 12:28:24,391;602375;1985360
+02.09.2014 12:28:24,894;601475;1985360
+02.09.2014 12:28:25,399;601550;1985360
+02.09.2014 12:28:25,919;601762;1985360
+02.09.2014 12:28:26,415;601843;1985360
+02.09.2014 12:28:26,925;602100;1985360
+02.09.2014 12:28:27,428;601700;1985360
+02.09.2014 12:28:27,934;601900;1985360
+02.09.2014 12:28:28,433;601768;1985360
+02.09.2014 12:28:28,959;602025;1985360
+02.09.2014 12:28:29,458;602337;1985360
+02.09.2014 12:28:29,967;601493;1985360
+02.09.2014 12:28:30,469;602125;1985360
+02.09.2014 12:28:30,983;602106;1985360
+02.09.2014 12:28:31,490;601987;1985360
+02.09.2014 12:28:32,001;602100;1985360
+02.09.2014 12:28:32,488;602262;1985360
+02.09.2014 12:28:33,035;601718;1985360
+02.09.2014 12:28:33,508;601806;1985360
+02.09.2014 12:28:34,061;601693;1985360
+02.09.2014 12:28:34,523;601450;1985360
+02.09.2014 12:28:35,083;601987;1985360
+02.09.2014 12:28:35,530;602193;1985360
+02.09.2014 12:28:36,093;601900;1985360
+02.09.2014 12:28:36,555;602450;1985360
+02.09.2014 12:28:37,067;602100;1985360
+02.09.2014 12:28:37,563;602118;1985360
+02.09.2014 12:28:38,077;602231;1985360
+02.09.2014 12:28:38,580;601581;1985360
+02.09.2014 12:28:39,091;601781;1985360
+02.09.2014 12:28:39,599;601950;1985360
+02.09.2014 12:28:40,103;601787;1985360
+02.09.2014 12:28:40,608;602237;1985360
+02.09.2014 12:28:41,128;601881;1985360
+02.09.2014 12:28:41,624;602300;1985360
+02.09.2014 12:28:42,138;602106;1985360
+02.09.2014 12:28:42,635;601606;1985360
+02.09.2014 12:28:43,147;601587;1985360
+02.09.2014 12:28:43,665;601600;1985360
+02.09.2014 12:28:44,160;601475;1985360
+02.09.2014 12:28:44,670;601962;1985360
+02.09.2014 12:28:45,175;601981;1985360
+02.09.2014 12:28:45,690;601775;1985360
+02.09.2014 12:28:46,193;601881;1985360
+02.09.2014 12:28:46,703;601462;1985360
+02.09.2014 12:28:47,208;601693;1985360
+02.09.2014 12:28:47,720;601806;1985360
+02.09.2014 12:28:48,224;601481;1985360
+02.09.2014 12:28:48,732;601793;1985360
+02.09.2014 12:28:49,238;601675;1985360
+02.09.2014 12:28:49,746;601693;1985360
+02.09.2014 12:28:50,249;602100;1985360
+02.09.2014 12:28:50,764;601943;1985360
+02.09.2014 12:28:51,270;601987;1985360
+02.09.2014 12:28:51,778;602237;1985360
+02.09.2014 12:28:52,269;601575;1985360
+02.09.2014 12:28:52,798;601287;1985360
+02.09.2014 12:28:53,283;601481;1985360
+02.09.2014 12:28:53,808;601906;1985360
+02.09.2014 12:28:54,301;602212;1985360
+02.09.2014 12:28:54,810;601800;1985360
+02.09.2014 12:28:55,314;602043;1985360
+02.09.2014 12:28:55,828;601875;1985360
+02.09.2014 12:28:56,343;601693;1985360
+02.09.2014 12:28:56,848;601781;1985360
+02.09.2014 12:28:57,339;601756;1985360
+02.09.2014 12:28:57,871;601625;1985360
+02.09.2014 12:28:58,354;602087;1985360
+02.09.2014 12:28:58,880;601981;1985360
+02.09.2014 12:28:59,378;601800;1985360
+02.09.2014 12:28:59,882;601787;1985360
+02.09.2014 12:29:00,392;602025;1985360
+02.09.2014 12:29:00,902;601818;1985360
+02.09.2014 12:29:01,410;602206;1985360
+02.09.2014 12:29:01,914;602118;1985360
+02.09.2014 12:29:02,423;602100;1985360
+02.09.2014 12:29:02,931;601493;1985360
+02.09.2014 12:29:03,419;602006;1985360
+02.09.2014 12:29:03,943;602212;1985360
+02.09.2014 12:29:04,446;601900;1985360
+02.09.2014 12:29:04,955;602087;1985360
+02.09.2014 12:29:05,448;601431;1985360
+02.09.2014 12:29:05,975;601700;1985360
+02.09.2014 12:29:06,478;601987;1985360
+02.09.2014 12:29:06,987;602200;1985360
+02.09.2014 12:29:07,490;601962;1985360
+02.09.2014 12:29:08,003;601681;1985360
+02.09.2014 12:29:08,508;602225;1985360
+02.09.2014 12:29:09,028;601681;1985360
+02.09.2014 12:29:09,509;602112;1985360
+02.09.2014 12:29:10,029;602000;1985360
+02.09.2014 12:29:10,538;601606;1985360
+02.09.2014 12:29:11,047;602375;1985360
+02.09.2014 12:29:11,539;602000;1985360
+02.09.2014 12:29:12,048;601706;1985360
+02.09.2014 12:29:12,553;601900;1985360
+02.09.2014 12:29:13,080;602206;1985360
+02.09.2014 12:29:13,559;602125;1985360
+02.09.2014 12:29:14,086;601987;1985360
+02.09.2014 12:29:14,598;602106;1985360
+02.09.2014 12:29:15,089;601800;1985360
+02.09.2014 12:29:15,615;601656;1985360
+02.09.2014 12:29:16,117;601687;1985360
+02.09.2014 12:29:16,628;601687;1985360
+02.09.2014 12:29:17,120;601687;1985360
+02.09.2014 12:29:17,645;601800;1985360
+02.09.2014 12:29:18,148;601900;1985360
+02.09.2014 12:29:18,658;601550;1985360
+02.09.2014 12:29:19,151;602175;1985360
+02.09.2014 12:29:19,671;601931;1985360
+02.09.2014 12:29:20,161;601606;1985360
+02.09.2014 12:29:20,690;601318;1985360
+02.09.2014 12:29:21,182;602106;1985360
+02.09.2014 12:29:21,691;601556;1985360
+02.09.2014 12:29:22,194;601318;1985360
+02.09.2014 12:29:22,708;601800;1985360
+02.09.2014 12:29:23,211;601768;1985360
+02.09.2014 12:29:23,732;601900;1985360
+02.09.2014 12:29:24,225;602087;1985360
+02.09.2014 12:29:24,736;601881;1985360
+02.09.2014 12:29:25,241;601962;1985360
+02.09.2014 12:29:25,754;601918;1985360
+02.09.2014 12:29:26,257;602006;1985360
+02.09.2014 12:29:26,766;602212;1985360
+02.09.2014 12:29:27,259;601368;1985360
+02.09.2014 12:29:27,785;602268;1985360
+02.09.2014 12:29:28,289;602006;1985360
+02.09.2014 12:29:28,797;601806;1985360
+02.09.2014 12:29:29,290;602012;1985360
+02.09.2014 12:29:29,803;602043;1985360
+02.09.2014 12:29:30,308;601800;1985360
+02.09.2014 12:29:30,824;602187;1985360
+02.09.2014 12:29:31,328;601693;1985360
+02.09.2014 12:29:31,836;601562;1985360
+02.09.2014 12:29:32,329;601643;1985360
+02.09.2014 12:29:32,855;601787;1985360
+02.09.2014 12:29:33,357;602312;1985360
+02.09.2014 12:29:33,865;601550;1985360
+02.09.2014 12:29:34,358;601893;1985360
+02.09.2014 12:29:34,880;601818;1985360
+02.09.2014 12:29:35,372;602112;1985360
+02.09.2014 12:29:35,898;601700;1985360
+02.09.2014 12:29:36,394;601487;1985360
+02.09.2014 12:29:36,906;601575;1985360
+02.09.2014 12:29:37,403;602118;1985360
+02.09.2014 12:29:37,919;601906;1985360
+02.09.2014 12:29:38,426;601706;1985360
+02.09.2014 12:29:38,938;601993;1985360
+02.09.2014 12:29:39,435;601881;1985360
+02.09.2014 12:29:39,947;602100;1985360
+02.09.2014 12:29:40,442;601912;1985360
+02.09.2014 12:29:40,961;602006;1985360
+02.09.2014 12:29:41,518;601450;1985360
+02.09.2014 12:29:41,981;601643;1985360
+02.09.2014 12:29:42,482;601918;1985360
+02.09.2014 12:29:43,007;601812;1985360
+02.09.2014 12:29:43,493;601800;1985360
+02.09.2014 12:29:44,017;601875;1985360
+02.09.2014 12:29:44,500;601575;1985360
+02.09.2014 12:29:45,036;601887;1985360
+02.09.2014 12:29:45,511;601568;1985360
+02.09.2014 12:29:46,030;601706;1985360
+02.09.2014 12:29:46,546;601793;1985360
+02.09.2014 12:29:47,043;602093;1985360
+02.09.2014 12:29:47,561;602343;1985360
+02.09.2014 12:29:48,075;601668;1985360
+02.09.2014 12:29:48,594;601893;1985360
+02.09.2014 12:29:49,110;602087;1985360
+02.09.2014 12:29:49,625;602118;1985360
+02.09.2014 12:29:50,137;602156;1985360
+02.09.2014 12:29:50,635;602206;1985360
+02.09.2014 12:29:51,147;602162;1985360
+02.09.2014 12:29:51,645;602000;1985360
+02.09.2014 12:29:52,162;601481;1985360
+02.09.2014 12:29:52,658;602106;1985360
+02.09.2014 12:29:53,171;602206;1985360
+02.09.2014 12:29:53,685;602131;1985360
+02.09.2014 12:29:54,202;601856;1985360
+02.09.2014 12:29:54,693;601856;1985360
+02.09.2014 12:29:55,207;601993;1985360
+02.09.2014 12:29:55,700;603262;1984639
+02.09.2014 12:29:56,210;676512;1967261
+02.09.2014 12:29:56,718;866956;1917831
+02.09.2014 12:29:57,232;1144037;1872319
+02.09.2014 12:29:57,739;1552843;1822976
+02.09.2014 12:29:58,238;1999400;1773715
+02.09.2014 12:29:58,746;2481231;1724529
+02.09.2014 12:29:59,261;2924400;1679122
+02.09.2014 12:29:59,769;3404531;1630011
+02.09.2014 12:30:00,279;3885968;1580930
+02.09.2014 12:30:00,775;4328431;1535674
+02.09.2014 12:30:01,290;4809000;1486779
+02.09.2014 12:30:01,788;5252506;1437918
+02.09.2014 12:30:02,302;5694593;1392924
+02.09.2014 12:30:02,800;6175581;1344203
+02.09.2014 12:30:03,310;6617812;1299273
+02.09.2014 12:30:03,822;7097837;1250598
+02.09.2014 12:30:04,332;7578250;1202081
+02.09.2014 12:30:04,828;8020606;1153779
+02.09.2014 12:30:05,349;8500156;1105401
+02.09.2014 12:30:05,845;8944531;1060813
+02.09.2014 12:30:06,360;9423956;1012651
+02.09.2014 12:30:06,849;9867700;968226
+02.09.2014 12:30:07,378;10347268;916517
+02.09.2014 12:30:07,877;10789743;872354
+02.09.2014 12:30:08,382;11270906;824581
+02.09.2014 12:30:08,887;11749206;780534
+02.09.2014 12:30:09,404;12192256;733011
+02.09.2014 12:30:09,902;12636725;685593
+02.09.2014 12:30:10,411;13115937;638465
+02.09.2014 12:30:10,917;13595512;595029
+02.09.2014 12:30:11,435;14076781;548110
+02.09.2014 12:30:11,924;14518593;505145
+02.09.2014 12:30:12,440;14962431;458511
+02.09.2014 12:30:12,945;15442193;412343
+02.09.2014 12:30:13,456;15921956;366534
+02.09.2014 12:30:13,969;16346650;321581
+02.09.2014 12:30:14,479;16690531;276674
+02.09.2014 12:30:14,974;16906643;219505
+02.09.2014 12:30:15,485;17031706;175098
+02.09.2014 12:30:15,979;17048387;174843
+02.09.2014 12:30:16,506;17049500;174930
+02.09.2014 12:30:17,022;17047587;174947
+02.09.2014 12:30:17,514;17047387;174936
+02.09.2014 12:30:18,032;17047300;174936
+02.09.2014 12:30:18,538;17047200;174936
+02.09.2014 12:30:19,041;17047200;174936
+02.09.2014 12:30:19,547;17046750;174936
+02.09.2014 12:30:20,062;17047118;174936
+02.09.2014 12:30:20,553;17047100;174936
+02.09.2014 12:30:21,070;17046987;174936
+02.09.2014 12:30:21,577;17047000;174936
+02.09.2014 12:30:22,088;17046793;174936
+02.09.2014 12:30:22,583;17046887;174046
+02.09.2014 12:30:23,095;17046818;208011
+02.09.2014 12:30:23,605;17046875;215186
+02.09.2014 12:30:24,117;17047400;223697
+02.09.2014 12:30:24,611;17046725;275116
+02.09.2014 12:30:25,124;17046906;285087
+02.09.2014 12:30:25,642;17046706;285011
+02.09.2014 12:30:26,146;17046700;285005
+02.09.2014 12:30:26,641;17046900;285005
+02.09.2014 12:30:27,152;17046831;285005
+02.09.2014 12:30:27,648;17046875;285005
+02.09.2014 12:30:28,158;17047143;285005
+02.09.2014 12:30:28,679;17046906;285005
+02.09.2014 12:30:29,184;17046906;285005
+02.09.2014 12:30:29,696;17046762;285005
+02.09.2014 12:30:30,212;17046806;285005
+02.09.2014 12:30:30,723;17046850;285005
+02.09.2014 12:30:31,242;17046793;285005
+02.09.2014 12:30:31,754;17046981;285005
+02.09.2014 12:30:32,265;17046587;285005
+02.09.2014 12:30:32,778;17046693;285005
+02.09.2014 12:30:33,274;17046681;285005
+02.09.2014 12:30:33,785;17046800;285005
+02.09.2014 12:30:34,283;17046662;285005
+02.09.2014 12:30:34,796;17046787;285005
+02.09.2014 12:30:35,294;17029843;283273
+02.09.2014 12:30:35,810;16911562;278093
+02.09.2014 12:30:36,317;16677525;272459
+02.09.2014 12:30:36,828;16358593;266709
+02.09.2014 12:30:37,329;15905756;261011
+02.09.2014 12:30:37,839;15461750;255732
+02.09.2014 12:30:38,345;14980218;250244
+02.09.2014 12:30:38,856;14498493;244651
+02.09.2014 12:30:39,351;14056487;239511
+02.09.2014 12:30:39,863;13612825;234174
+02.09.2014 12:30:40,377;13133737;228779
+02.09.2014 12:30:40,888;12654975;223534
+02.09.2014 12:30:41,384;12211056;218674
+02.09.2014 12:30:41,894;11730575;213627
+02.09.2014 12:30:42,389;11287731;208837
+02.09.2014 12:30:42,917;10806987;203813
+02.09.2014 12:30:43,412;10328150;198941
+02.09.2014 12:30:43,923;9885093;194000
+02.09.2014 12:30:44,434;9403750;189284
+02.09.2014 12:30:44,954;8961706;184959
+02.09.2014 12:30:45,461;8482393;180395
+02.09.2014 12:30:45,979;8001075;175883
+02.09.2014 12:30:46,489;7522706;171552
+02.09.2014 12:30:47,007;7078962;167250
+02.09.2014 12:30:47,513;6598793;163139
+02.09.2014 12:30:48,023;6155837;159470
+02.09.2014 12:30:48,518;5711787;155895
+02.09.2014 12:30:49,046;5195706;151866
+02.09.2014 12:30:49,552;4715887;148319
+02.09.2014 12:30:50,051;4271656;145145
+02.09.2014 12:30:50,550;3829537;142017
+02.09.2014 12:30:51,064;3348781;138965
+02.09.2014 12:30:51,561;2905462;136470
+02.09.2014 12:30:52,104;2388868;133709
+02.09.2014 12:30:52,584;1983150;132011
+02.09.2014 12:30:53,119;1467306;132000
+02.09.2014 12:30:53,590;1127556;131988
+02.09.2014 12:30:54,143;816287;131988
+02.09.2014 12:30:54,614;664950;131988
+02.09.2014 12:30:55,123;594450;131988
+02.09.2014 12:30:55,631;595475;131988
+02.09.2014 12:30:56,131;595512;131988
+02.09.2014 12:30:56,642;595531;131988
+02.09.2014 12:30:57,152;595587;131988
+02.09.2014 12:30:57,648;595581;131988
+02.09.2014 12:30:58,157;595762;131988
+02.09.2014 12:30:58,678;596175;131988
+02.09.2014 12:30:59,183;595981;131988
+02.09.2014 12:30:59,689;595693;131988
+02.09.2014 12:31:00,189;595575;131988
+02.09.2014 12:31:00,696;595993;131988
+02.09.2014 12:31:01,207;596218;131988
+02.09.2014 12:31:01,707;595787;131988
+02.09.2014 12:31:02,228;595862;131988
+02.09.2014 12:31:02,727;595968;131988
+02.09.2014 12:31:03,235;596100;131988
+02.09.2014 12:31:03,730;595668;129284
+02.09.2014 12:31:04,256;595875;71918
+02.09.2014 12:31:04,753;596100;54087
+02.09.2014 12:31:05,263;595787;48133
+02.09.2014 12:31:05,758;595387;44534
+02.09.2014 12:31:06,273;595537;12040
+02.09.2014 12:31:06,769;596187;12720
+02.09.2014 12:31:07,292;595656;12784
+02.09.2014 12:31:07,796;596000;12860
+02.09.2014 12:31:08,308;595881;12860
+02.09.2014 12:31:08,802;596206;12860
+02.09.2014 12:31:09,319;596006;12860
+02.09.2014 12:31:09,824;596000;12860
+02.09.2014 12:31:10,342;596025;12860
+02.09.2014 12:31:10,834;596075;12860
+02.09.2014 12:31:11,346;595968;12860
+02.09.2014 12:31:11,843;595768;12860
+02.09.2014 12:31:12,360;596100;12860
+02.09.2014 12:31:12,867;596150;12860
+02.09.2014 12:31:13,378;599218;11970
+02.09.2014 12:31:13,874;681237;47203
+02.09.2014 12:31:14,390;862443;147383
+02.09.2014 12:31:14,896;1167775;241290
+02.09.2014 12:31:15,407;1587193;342924
+02.09.2014 12:31:15,901;2033968;436761
+02.09.2014 12:31:16,411;2479181;538424
+02.09.2014 12:31:16,921;2958600;640069
+02.09.2014 12:31:17,433;3440043;741668
+02.09.2014 12:31:17,939;3883825;843226
+02.09.2014 12:31:18,439;4326062;936912
+02.09.2014 12:31:18,945;4806300;1038377
+02.09.2014 12:31:19,460;5287212;1131970
+02.09.2014 12:31:19,973;5766056;1233424
+02.09.2014 12:31:20,483;6209425;1334790
+02.09.2014 12:31:20,994;6689325;1436081
+02.09.2014 12:31:21,488;7132493;1529418
+02.09.2014 12:31:22,000;7612900;1630529
+02.09.2014 12:31:22,509;8054312;1731546
+02.09.2014 12:31:23,008;8498162;1824808
+02.09.2014 12:31:23,523;8979450;1925697
+02.09.2014 12:31:24,033;9457856;2026610
+02.09.2014 12:31:24,539;9939387;2127500
+02.09.2014 12:31:25,038;10382431;2220604
+02.09.2014 12:31:25,550;10824706;2321430
+02.09.2014 12:31:26,060;11305256;2422081
+02.09.2014 12:31:26,568;11784575;2522627
+02.09.2014 12:31:27,078;12264425;2623122
+02.09.2014 12:31:27,573;12708437;2715813
+02.09.2014 12:31:28,084;13149693;2808488
+02.09.2014 12:31:28,589;13630975;2908720
+02.09.2014 12:31:29,106;14074325;3008843
+02.09.2014 12:31:29,600;14552618;3108831
+02.09.2014 12:31:30,110;14996850;3200953
+02.09.2014 12:31:30,622;15476950;3300633
+02.09.2014 12:31:31,132;15956162;3400284
+02.09.2014 12:31:31,628;16363825;3492284
+02.09.2014 12:31:32,137;16842725;3591633
+02.09.2014 12:31:32,643;17323162;3690720
+02.09.2014 12:31:33,154;17802962;3789441
+02.09.2014 12:31:33,668;18245200;3880459
+02.09.2014 12:31:34,178;18726337;3979087
+02.09.2014 12:31:34,688;19206212;4077691
+02.09.2014 12:31:35,205;19670443;4175593
+02.09.2014 12:31:35,716;19999781;4304424
+02.09.2014 12:31:36,242;20246618;4449703
+02.09.2014 12:31:36,743;20373087;4490034
+02.09.2014 12:31:37,237;20402862;4490081
+02.09.2014 12:31:37,778;20402081;4489976
+02.09.2014 12:31:38,261;20398875;4489976
+02.09.2014 12:31:38,775;20399443;4489970
+02.09.2014 12:31:39,268;20399000;4489970
+02.09.2014 12:31:39,783;20398700;4489970
+02.09.2014 12:31:40,289;20399400;4489970
+02.09.2014 12:31:40,807;20398993;4489970
+02.09.2014 12:31:41,304;20399100;4489970
+02.09.2014 12:31:41,815;20399550;4489970
+02.09.2014 12:31:42,308;20398968;4489970
+02.09.2014 12:31:42,837;20399162;4489970
+02.09.2014 12:31:43,333;20399437;4489970
+02.09.2014 12:31:43,844;20399000;4489843
+02.09.2014 12:31:44,346;20398968;4503168
+02.09.2014 12:31:44,852;20398775;4527366
+02.09.2014 12:31:45,365;20399400;4532918
+02.09.2014 12:31:45,878;20399118;4547534
+02.09.2014 12:31:46,373;20399306;4602011
+02.09.2014 12:31:46,883;20399200;4610081
+02.09.2014 12:31:47,396;20399093;4610040
+02.09.2014 12:31:47,907;20399306;4610034
+02.09.2014 12:31:48,402;20399187;4610034
+02.09.2014 12:31:48,924;20399312;4610034
+02.09.2014 12:31:49,408;20399093;4610034
+02.09.2014 12:31:49,918;20399062;4610034
+02.09.2014 12:31:50,445;20399418;4610034
+02.09.2014 12:31:50,939;20399300;4610034
+02.09.2014 12:31:51,456;20399550;4610034
+02.09.2014 12:31:51,948;20399012;4610034
+02.09.2014 12:31:52,477;20399037;4610034
+02.09.2014 12:31:52,973;20399400;4610034
+02.09.2014 12:31:53,488;20399275;4610034
+02.09.2014 12:31:53,981;20399100;4610034
+02.09.2014 12:31:54,494;20399200;4610034
+02.09.2014 12:31:54,992;20399506;4610034
+02.09.2014 12:31:55,511;20399187;4610034
+02.09.2014 12:31:56,011;20398993;4610034
+02.09.2014 12:31:56,520;20399518;4610034
+02.09.2014 12:31:57,027;20398968;4610034
+02.09.2014 12:31:57,544;20380225;4607203
+02.09.2014 12:31:58,047;20257350;4548505
+02.09.2014 12:31:58,548;20018900;4481633
+02.09.2014 12:31:59,077;19665562;4403860
+02.09.2014 12:31:59,567;19242525;4337098
+02.09.2014 12:32:00,061;18797718;4270569
+02.09.2014 12:32:00,589;18314800;4192965
+02.09.2014 12:32:01,119;17799800;4115465
+02.09.2014 12:32:01,597;17392137;4054593
+02.09.2014 12:32:02,138;16838068;3977098
+02.09.2014 12:32:02,618;16432493;3910802
+02.09.2014 12:32:03,149;15952537;3833447
+02.09.2014 12:32:03,624;15545737;3772825
+02.09.2014 12:32:04,163;15103293;3706715
+02.09.2014 12:32:04,630;14622925;3640686
+02.09.2014 12:32:05,135;14143200;3569180
+02.09.2014 12:32:05,651;13700193;3497691
+02.09.2014 12:32:06,157;13220187;3426255
+02.09.2014 12:32:06,669;12779143;3355040
+02.09.2014 12:32:07,163;12298293;3289459
+02.09.2014 12:32:07,681;11853962;3218325
+02.09.2014 12:32:08,177;11375006;3152912
+02.09.2014 12:32:08,688;10931362;3081906
+02.09.2014 12:32:09,194;10451431;3011000
+02.09.2014 12:32:09,705;10008150;2940424
+02.09.2014 12:32:10,199;9564312;2875366
+02.09.2014 12:32:10,717;9085400;2805145
+02.09.2014 12:32:11,226;8605737;2734796
+02.09.2014 12:32:11,731;8125293;2664767
+02.09.2014 12:32:12,237;7682825;2600500
+02.09.2014 12:32:12,754;7202487;2530860
+02.09.2014 12:32:13,248;6758837;2467063
+02.09.2014 12:32:13,759;6280018;2397970
+02.09.2014 12:32:14,267;5843425;2329441
+02.09.2014 12:32:14,778;5477312;2262860
+02.09.2014 12:32:15,275;5241325;2164511
+02.09.2014 12:32:15,791;5095131;2098831
+02.09.2014 12:32:16,296;5061925;2097738
+02.09.2014 12:32:16,798;5062793;2097889
+02.09.2014 12:32:17,303;5064206;2098063
+02.09.2014 12:32:17,821;5064200;2098046
+02.09.2014 12:32:18,326;5064600;2098034
+02.09.2014 12:32:18,837;5064712;2098034
+02.09.2014 12:32:19,333;5064800;2098034
+02.09.2014 12:32:19,844;5064793;2098034
+02.09.2014 12:32:20,338;5064900;2098034
+02.09.2014 12:32:20,868;5064900;2098034
+02.09.2014 12:32:21,361;5064750;2098034
+02.09.2014 12:32:21,874;5064900;2098034
+02.09.2014 12:32:22,391;5064800;2098034
+02.09.2014 12:32:22,896;5064787;2098034
+02.09.2014 12:32:23,398;5064975;2098034
+02.09.2014 12:32:23,909;5065000;2098034
+02.09.2014 12:32:24,421;5065100;2098034
+02.09.2014 12:32:24,908;5064843;2098034
+02.09.2014 12:32:25,438;5064993;2098034
+02.09.2014 12:32:25,934;5065143;2098034
+02.09.2014 12:32:26,445;5064900;2098034
+02.09.2014 12:32:26,948;5064912;2098034
+02.09.2014 12:32:27,463;5064906;2098034
+02.09.2014 12:32:27,961;5064900;2088738
+02.09.2014 12:32:28,476;5065193;2036075
+02.09.2014 12:32:28,967;5065093;2028319
+02.09.2014 12:32:29,496;5064893;2022941
+02.09.2014 12:32:29,986;5065000;1994389
+02.09.2014 12:32:30,501;5064987;1988750
+02.09.2014 12:32:31,013;5065012;1988982
+02.09.2014 12:32:31,529;5065237;1988982
+02.09.2014 12:32:32,039;5065100;1988970
+02.09.2014 12:32:32,567;5065018;1988970
+02.09.2014 12:32:33,077;5064993;1988970
+02.09.2014 12:32:33,578;5065156;1988970
+02.09.2014 12:32:34,089;5065087;1988970
+02.09.2014 12:32:34,595;5065000;1988970
+02.09.2014 12:32:35,108;5065012;1988970
+02.09.2014 12:32:35,607;5065193;1988970
+02.09.2014 12:32:36,128;5065312;1988970
+02.09.2014 12:32:36,611;5065000;1988970
+02.09.2014 12:32:37,127;5065100;1988970
+02.09.2014 12:32:37,635;5065100;1988970
+02.09.2014 12:32:38,145;5064906;1988970
+02.09.2014 12:32:38,655;5064987;1988970
+02.09.2014 12:32:39,167;5074681;1987767
+02.09.2014 12:32:39,661;5178512;1927563
+02.09.2014 12:32:40,173;5397531;1826279
+02.09.2014 12:32:40,684;5730937;1731633
+02.09.2014 12:32:41,195;6140406;1629319
+02.09.2014 12:32:41,691;6586081;1534825
+02.09.2014 12:32:42,206;7068381;1424639
+02.09.2014 12:32:42,697;7512775;1330238
+02.09.2014 12:32:43,211;7991493;1228209
+02.09.2014 12:32:43,717;8472337;1126279
+02.09.2014 12:32:44,236;8916562;1032313
+02.09.2014 12:32:44,730;9358506;938569
+02.09.2014 12:32:45,241;9838793;829116
+02.09.2014 12:32:45,756;10319418;728127
+02.09.2014 12:32:46,263;10798387;635174
+02.09.2014 12:32:46,758;11242156;541994
+02.09.2014 12:32:47,268;11721243;441290
+02.09.2014 12:32:47,776;12153437;345569
+02.09.2014 12:32:48,286;12514587;219279
+02.09.2014 12:32:48,786;12746800;74186
+02.09.2014 12:32:49,297;12886875;17145
+02.09.2014 12:32:49,804;12914812;16825
+02.09.2014 12:32:50,315;12914175;16860
+02.09.2014 12:32:50,816;12912987;16848
+02.09.2014 12:32:51,337;12912675;16848
+02.09.2014 12:32:51,830;12912437;16848
+02.09.2014 12:32:52,342;12912212;16848
+02.09.2014 12:32:52,852;12912112;16848
+02.09.2014 12:32:53,347;12912087;16848
+02.09.2014 12:32:53,875;12911981;16848
+02.09.2014 12:32:54,368;12912087;16848
+02.09.2014 12:32:54,879;12912306;16848
+02.09.2014 12:32:55,391;12912200;16848
+02.09.2014 12:32:55,901;12912087;16848
+02.09.2014 12:32:56,398;12911862;16645
+02.09.2014 12:32:56,909;12912218;23988
+02.09.2014 12:32:57,416;12911837;54063
+02.09.2014 12:32:57,925;12911787;59610
+02.09.2014 12:32:58,419;12911306;85895
+02.09.2014 12:32:58,945;12911843;136029
+02.09.2014 12:32:59,444;12911487;137069
+02.09.2014 12:32:59,955;12911375;137040
+02.09.2014 12:33:00,454;12911793;137029
+02.09.2014 12:33:00,972;12911712;137029
+02.09.2014 12:33:01,479;12911793;137029
+02.09.2014 12:33:01,978;12911693;137029
+02.09.2014 12:33:02,484;12911800;137029
+02.09.2014 12:33:02,993;12911425;137029
+02.09.2014 12:33:03,506;12911712;137029
+02.09.2014 12:33:04,016;12911618;137029
+02.09.2014 12:33:04,512;12911531;137029
+02.09.2014 12:33:05,023;12911368;137029
+02.09.2014 12:33:05,536;12911493;137029
+02.09.2014 12:33:06,038;12911400;137029
+02.09.2014 12:33:06,544;12911700;137029
+02.09.2014 12:33:07,053;12911575;137029
+02.09.2014 12:33:07,549;12911206;137029
+02.09.2014 12:33:08,065;12911581;137029
+02.09.2014 12:33:08,568;12911568;137029
+02.09.2014 12:33:09,083;12911925;137029
+02.09.2014 12:33:09,595;12911581;137029
+02.09.2014 12:33:10,136;12962187;149482
+02.09.2014 12:33:10,623;13095106;232837
+02.09.2014 12:33:11,173;13383100;449895
+02.09.2014 12:33:11,658;13612725;722994
+02.09.2014 12:33:12,185;13774637;1055784
+02.09.2014 12:33:12,675;13808087;1341122
+02.09.2014 12:33:13,204;13807500;1673947
+02.09.2014 12:33:13,681;13804987;1935517
+02.09.2014 12:33:14,176;13805643;2220808
+02.09.2014 12:33:14,688;13805150;2529889
+02.09.2014 12:33:15,202;13805718;2839023
+02.09.2014 12:33:15,706;13805400;3148063
+02.09.2014 12:33:16,217;13805393;3457139
+02.09.2014 12:33:16,720;13805106;3742465
+02.09.2014 12:33:17,225;13805287;4051552
+02.09.2014 12:33:17,744;13805293;4299279
+02.09.2014 12:33:18,239;13805281;4458197
+02.09.2014 12:33:18,751;13805287;4509930
+02.09.2014 12:33:19,246;13805712;4510029
+02.09.2014 12:33:19,767;13805312;4509988
+02.09.2014 12:33:20,267;13805587;4510000
+02.09.2014 12:33:20,785;13805268;4509988
+02.09.2014 12:33:21,296;13805587;4509988
+02.09.2014 12:33:21,816;13805543;4509988
+02.09.2014 12:33:22,330;13805606;4509988
+02.09.2014 12:33:22,843;13805068;4509988
+02.09.2014 12:33:23,355;13805425;4509988
+02.09.2014 12:33:23,868;13805200;4509988
+02.09.2014 12:33:24,366;13805618;4509988
+02.09.2014 12:33:24,879;13805331;4509988
+02.09.2014 12:33:25,391;13805643;4509988
+02.09.2014 12:33:25,901;13805400;4509988
+02.09.2014 12:33:26,391;13805062;4509988
+02.09.2014 12:33:26,908;13805925;4508203
+02.09.2014 12:33:27,415;13805525;4459110
+02.09.2014 12:33:27,926;13804931;4441569
+02.09.2014 12:33:28,432;13805218;4435552
+02.09.2014 12:33:28,937;13805300;4425453
+02.09.2014 12:33:29,449;13805062;4399767
+02.09.2014 12:33:29,978;13804868;4400970
+02.09.2014 12:33:30,477;13804862;4400982
+02.09.2014 12:33:31,005;13805200;4401011
+02.09.2014 12:33:31,510;13805187;4401011
+02.09.2014 12:33:32,015;13805400;4401011
+02.09.2014 12:33:32,535;13805287;4401011
+02.09.2014 12:33:33,029;13804956;4401011
+02.09.2014 12:33:33,545;13805393;4401011
+02.09.2014 12:33:34,047;13805393;4401011
+02.09.2014 12:33:34,578;13805387;4401011
+02.09.2014 12:33:35,079;13805187;4401011
+02.09.2014 12:33:35,599;13805275;4401011
+02.09.2014 12:33:36,109;13805406;4401011
+02.09.2014 12:33:36,616;13805325;4401011
+02.09.2014 12:33:37,134;13805387;4401011
+02.09.2014 12:33:37,646;13805087;4401011
+02.09.2014 12:33:38,167;13805406;4401011
+02.09.2014 12:33:38,666;13805262;4401011
+02.09.2014 12:33:39,186;13805518;4401011
+02.09.2014 12:33:39,699;13805300;4401011
+02.09.2014 12:33:40,206;13805393;4401011
+02.09.2014 12:33:40,724;13805475;4401011
+02.09.2014 12:33:41,215;13805081;4401011
+02.09.2014 12:33:41,735;13805106;4401011
+02.09.2014 12:33:42,230;13805193;4401011
+02.09.2014 12:33:42,742;13805281;4401011
+02.09.2014 12:33:43,238;13804981;4401011
+02.09.2014 12:33:43,758;13805306;4401011
+02.09.2014 12:33:44,265;13805462;4401011
+02.09.2014 12:33:44,775;13805193;4401011
+02.09.2014 12:33:45,267;13805387;4401011
+02.09.2014 12:33:45,786;13771618;4399348
+02.09.2014 12:33:46,292;13636387;4331988
+02.09.2014 12:33:46,802;13380081;4157470
+02.09.2014 12:33:47,295;13119306;3903098
+02.09.2014 12:33:47,812;12955543;3594395
+02.09.2014 12:33:48,328;12908875;3285331
+02.09.2014 12:33:48,826;12911412;3000023
+02.09.2014 12:33:49,335;12911693;2667122
+02.09.2014 12:33:49,844;12911168;2381825
+02.09.2014 12:33:50,357;12911431;2072744
+02.09.2014 12:33:50,867;12911200;1763651
+02.09.2014 12:33:51,355;12911368;1478331
+02.09.2014 12:33:51,872;12911293;1169284
+02.09.2014 12:33:52,377;12911368;860180
+02.09.2014 12:33:52,877;12911168;574866
+02.09.2014 12:33:53,382;12911575;283366
+02.09.2014 12:33:53,899;12911243;97755
+02.09.2014 12:33:54,405;12911293;22017
+02.09.2014 12:33:54,916;12911750;16750
+02.09.2014 12:33:55,432;12911506;16918
+02.09.2014 12:33:55,926;12911293;16982
+02.09.2014 12:33:56,437;12911631;16976
+02.09.2014 12:33:56,942;12911618;16976
+02.09.2014 12:33:57,454;12911400;16976
+02.09.2014 12:33:57,950;12911500;16976
+02.09.2014 12:33:58,478;12911481;16976
+02.09.2014 12:33:58,972;12911475;16976
+02.09.2014 12:33:59,482;12911287;16976
+02.09.2014 12:33:59,977;12911493;16976
+02.09.2014 12:34:00,505;12911587;16976
+02.09.2014 12:34:01,002;12911806;16976
+02.09.2014 12:34:01,512;12911700;16122
+02.09.2014 12:34:02,007;12911712;35500
+02.09.2014 12:34:02,518;12911600;54924
+02.09.2014 12:34:03,023;12911356;59889
+02.09.2014 12:34:03,542;12911400;94831
+02.09.2014 12:34:04,039;12911287;136575
+02.09.2014 12:34:04,552;12911706;137029
+02.09.2014 12:34:05,048;12911612;137005
+02.09.2014 12:34:05,578;12911356;136994
+02.09.2014 12:34:06,072;12911525;136994
+02.09.2014 12:34:06,583;12911268;136994
+02.09.2014 12:34:07,077;12911893;136994
+02.09.2014 12:34:07,589;12911300;136994
+02.09.2014 12:34:08,095;12911606;136994
+02.09.2014 12:34:08,613;12911087;136994
+02.09.2014 12:34:09,107;12911500;136994
+02.09.2014 12:34:09,619;12911487;136994
+02.09.2014 12:34:10,125;12911387;136994
+02.09.2014 12:34:10,641;12911318;136994
+02.09.2014 12:34:11,138;12911200;136994
+02.09.2014 12:34:11,649;12911506;136994
+02.09.2014 12:34:12,155;12911281;136994
+02.09.2014 12:34:12,656;12911393;136994
+02.09.2014 12:34:13,162;12911600;136994
+02.09.2014 12:34:13,681;12911300;136994
+02.09.2014 12:34:14,186;12911600;136994
+02.09.2014 12:34:14,695;12911375;135883
+02.09.2014 12:34:15,191;12973481;151075
+02.09.2014 12:34:15,707;13153025;261174
+02.09.2014 12:34:16,213;13418837;457848
+02.09.2014 12:34:16,723;13818637;779906
+02.09.2014 12:34:17,229;14300475;1065203
+02.09.2014 12:34:17,729;14743618;1374319
+02.09.2014 12:34:18,235;15187706;1659656
+02.09.2014 12:34:18,751;15668100;1968726
+02.09.2014 12:34:19,247;16110850;2254034
+02.09.2014 12:34:19,766;16508843;2586918
+02.09.2014 12:34:20,290;16806062;2895988
+02.09.2014 12:34:20,783;16946925;3181284
+02.09.2014 12:34:21,314;17006400;3514168
+02.09.2014 12:34:21,794;17002912;3775680
+02.09.2014 12:34:22,324;17002293;4108529
+02.09.2014 12:34:22,800;17003000;4367220
+02.09.2014 12:34:23,350;17001968;4548331
+02.09.2014 12:34:23,826;17002212;4603162
+02.09.2014 12:34:24,322;17001925;4605052
+02.09.2014 12:34:24,833;17002112;4605034
+02.09.2014 12:34:25,328;17001900;4605023
+02.09.2014 12:34:25,856;17001781;4605023
+02.09.2014 12:34:26,351;17001800;4605023
+02.09.2014 12:34:26,865;17001900;4605023
+02.09.2014 12:34:27,381;17001900;4605023
+02.09.2014 12:34:27,876;17001800;4605023
+02.09.2014 12:34:28,387;17001781;4605023
+02.09.2014 12:34:28,892;17001800;4605023
+02.09.2014 12:34:29,404;17001800;4605023
+02.09.2014 12:34:29,899;17001800;4605023
+02.09.2014 12:34:30,426;17001800;4605023
+02.09.2014 12:34:30,926;17001800;4605023
+02.09.2014 12:34:31,454;17001800;4605023
+02.09.2014 12:34:31,956;17001800;4605023
+02.09.2014 12:34:32,485;17001768;4603738
+02.09.2014 12:34:32,995;17001793;4568110
+02.09.2014 12:34:33,491;17002100;4530168
+02.09.2014 12:34:34,008;17002000;4524122
+02.09.2014 12:34:34,497;17001793;4519965
+02.09.2014 12:34:35,024;17001900;4488552
+02.09.2014 12:34:35,520;17001800;4485511
+02.09.2014 12:34:36,035;17001818;4485767
+02.09.2014 12:34:36,530;17001681;4485761
+02.09.2014 12:34:37,048;17001806;4485761
+02.09.2014 12:34:37,555;17001800;4485761
+02.09.2014 12:34:38,066;17001700;4485761
+02.09.2014 12:34:38,559;17001800;4485761
+02.09.2014 12:34:39,076;17001687;4485761
+02.09.2014 12:34:39,570;17001800;4485761
+02.09.2014 12:34:40,086;17001825;4485761
+02.09.2014 12:34:40,595;17001700;4485761
+02.09.2014 12:34:41,119;17001675;4485761
+02.09.2014 12:34:41,603;17001706;4485761
+02.09.2014 12:34:42,121;17001800;4485761
+02.09.2014 12:34:42,620;17001787;4485761
+02.09.2014 12:34:43,130;17001800;4485761
+02.09.2014 12:34:43,627;17001800;4485761
+02.09.2014 12:34:44,150;17001706;4485761
+02.09.2014 12:34:44,637;17001800;4485761
+02.09.2014 12:34:45,167;17001800;4485761
+02.09.2014 12:34:45,665;17001800;4485761
+02.09.2014 12:34:46,177;17001800;4485761
+02.09.2014 12:34:46,674;17001693;4485761
+02.09.2014 12:34:47,191;17001800;4485761
+02.09.2014 12:34:47,685;17001693;4485761
+02.09.2014 12:34:48,195;17001800;4485761
+02.09.2014 12:34:48,700;16991918;4484848
+02.09.2014 12:34:49,217;16885312;4433802
+02.09.2014 12:34:49,705;16685943;4291482
+02.09.2014 12:34:50,235;16329318;4010598
+02.09.2014 12:34:50,730;15918406;3726000
+02.09.2014 12:34:51,240;15435806;3416883
+02.09.2014 12:34:51,745;14991068;3131604
+02.09.2014 12:34:52,267;14511262;2822476
+02.09.2014 12:34:52,760;14068537;2537203
+02.09.2014 12:34:53,280;13607768;2204343
+02.09.2014 12:34:53,770;13287981;1919017
+02.09.2014 12:34:54,286;13050450;1609936
+02.09.2014 12:34:54,775;12932850;1324633
+02.09.2014 12:34:55,303;12909600;1015523
+02.09.2014 12:34:55,798;12909868;730226
+02.09.2014 12:34:56,308;12909900;398191
+02.09.2014 12:34:56,814;12910750;177319
+02.09.2014 12:34:57,331;12911000;45581
+02.09.2014 12:34:57,841;12912537;16610
+02.09.2014 12:34:58,336;12912500;16906
+02.09.2014 12:34:58,865;12912618;16924
+02.09.2014 12:34:59,376;12912700;16912
+02.09.2014 12:34:59,891;12912506;16912
+02.09.2014 12:35:00,407;12912087;16912
+02.09.2014 12:35:00,919;12912700;16912
+02.09.2014 12:35:01,431;12912493;16912
+02.09.2014 12:35:01,929;12912593;16912
+02.09.2014 12:35:02,438;12912706;16912
+02.09.2014 12:35:02,949;12912393;16912
+02.09.2014 12:35:03,459;12912493;16912
+02.09.2014 12:35:03,967;12912700;16912
+02.09.2014 12:35:04,465;12912606;16912
+02.09.2014 12:35:04,978;12912706;17267
+02.09.2014 12:35:05,489;12912493;52395
+02.09.2014 12:35:05,994;12912293;58110
+02.09.2014 12:35:06,496;12912712;71534
+02.09.2014 12:35:07,004;12912700;126622
+02.09.2014 12:35:07,513;12912462;137093
+02.09.2014 12:35:08,007;12912793;137017
+02.09.2014 12:35:08,527;12912587;137017
+02.09.2014 12:35:09,033;12913012;137005
+02.09.2014 12:35:09,538;12912762;137005
+02.09.2014 12:35:10,044;12912918;137005
+02.09.2014 12:35:10,560;12912700;137005
+02.09.2014 12:35:11,067;12912387;137005
+02.09.2014 12:35:11,565;12912700;137005
+02.09.2014 12:35:12,073;12912975;137005
+02.09.2014 12:35:12,586;12912481;137005
+02.09.2014 12:35:13,079;12912700;137005
+02.09.2014 12:35:13,597;12913037;137005
+02.09.2014 12:35:14,104;12912843;137005
+02.09.2014 12:35:14,605;12913018;137005
+02.09.2014 12:35:15,111;12912700;137005
+02.09.2014 12:35:15,628;12912906;137005
+02.09.2014 12:35:16,135;12912625;137005
+02.09.2014 12:35:16,644;12912812;137005
+02.09.2014 12:35:17,140;12912700;137005
+02.09.2014 12:35:17,650;12912681;137005
+02.09.2014 12:35:18,147;12897437;136296
+02.09.2014 12:35:18,676;12891768;208220
+02.09.2014 12:35:19,170;12891893;368372
+02.09.2014 12:35:19,682;12891668;642447
+02.09.2014 12:35:20,193;12891906;951203
+02.09.2014 12:35:20,705;12891418;1260290
+02.09.2014 12:35:21,200;12891681;1545645
+02.09.2014 12:35:21,713;12891681;1854715
+02.09.2014 12:35:22,207;12891931;2140058
+02.09.2014 12:35:22,720;12891556;2449139
+02.09.2014 12:35:23,230;12891831;2734447
+02.09.2014 12:35:23,741;12891593;3067279
+02.09.2014 12:35:24,236;12891381;3352610
+02.09.2014 12:35:24,752;12891375;3661680
+02.09.2014 12:35:25,252;12891612;3947000
+02.09.2014 12:35:25,768;12891475;4253761
+02.09.2014 12:35:26,274;12891350;4474843
+02.09.2014 12:35:26,786;12891900;4586279
+02.09.2014 12:35:27,280;12891600;4602145
+02.09.2014 12:35:27,824;12891718;4602023
+02.09.2014 12:35:28,309;12891400;4602023
+02.09.2014 12:35:28,815;12891818;4602011
+02.09.2014 12:35:29,365;12891831;4602011
+02.09.2014 12:35:29,836;12891612;4602011
+02.09.2014 12:35:30,339;12891650;4602011
+02.09.2014 12:35:30,849;12891756;4602011
+02.09.2014 12:35:31,345;12891700;4602011
+02.09.2014 12:35:31,874;12891487;4602011
+02.09.2014 12:35:32,370;12891481;4602011
+02.09.2014 12:35:32,875;12891575;4602011
+02.09.2014 12:35:33,376;12891887;4602011
+02.09.2014 12:35:33,882;12891575;4602011
+02.09.2014 12:35:34,394;12891818;4602011
+02.09.2014 12:35:34,905;12891362;4602011
+02.09.2014 12:35:35,406;12891575;4602011
+02.09.2014 12:35:35,913;12891712;4595494
+02.09.2014 12:35:36,424;12891706;4542476
+02.09.2014 12:35:36,935;12891387;4523750
+02.09.2014 12:35:37,436;12891600;4518226
+02.09.2014 12:35:37,925;12891656;4514819
+02.09.2014 12:35:38,452;12891381;4481906
+02.09.2014 12:35:38,946;12891837;4482918
+02.09.2014 12:35:39,458;12891468;4482953
+02.09.2014 12:35:39,971;12891800;4482941
+02.09.2014 12:35:40,485;12891418;4482941
+02.09.2014 12:35:40,980;12891587;4482941
+02.09.2014 12:35:41,496;12891356;4482941
+02.09.2014 12:35:41,995;12891518;4482941
+02.09.2014 12:35:42,506;12891706;4482941
+02.09.2014 12:35:42,999;12891800;4482941
+02.09.2014 12:35:43,519;12891850;4482941
+02.09.2014 12:35:44,016;12891800;4482941
+02.09.2014 12:35:44,528;12891593;4482941
+02.09.2014 12:35:45,035;12891787;4482941
+02.09.2014 12:35:45,553;12891481;4482941
+02.09.2014 12:35:46,059;12891237;4482941
+02.09.2014 12:35:46,575;12891387;4482941
+02.09.2014 12:35:47,088;12891581;4482941
+02.09.2014 12:35:47,606;12891143;4482941
+02.09.2014 12:35:48,119;12891593;4482941
+02.09.2014 12:35:48,616;12891812;4482941
+02.09.2014 12:35:49,143;12891362;4482941
+02.09.2014 12:35:49,641;12891387;4482941
+02.09.2014 12:35:50,155;12891700;4482941
+02.09.2014 12:35:50,650;12891981;4482941
+02.09.2014 12:35:51,165;12891837;4482941
+02.09.2014 12:35:51,665;12891600;4482941
+02.09.2014 12:35:52,180;12891300;4482941
+02.09.2014 12:35:52,679;12891506;4482941
+02.09.2014 12:35:53,189;12891350;4482941
+02.09.2014 12:35:53,703;12891600;4482941
+02.09.2014 12:35:54,215;12891500;4482941
+02.09.2014 12:35:54,713;12891600;4482941
+02.09.2014 12:35:55,225;12891475;4482941
+02.09.2014 12:35:55,721;12892081;4482941
+02.09.2014 12:35:56,234;12891600;4482941
+02.09.2014 12:35:56,741;12891912;4482941
+02.09.2014 12:35:57,254;12891487;4482941
+02.09.2014 12:35:57,752;12891600;4482941
+02.09.2014 12:35:58,269;12891687;4482941
+02.09.2014 12:35:58,769;12891600;4482941
+02.09.2014 12:35:59,281;12891600;4482941
+02.09.2014 12:35:59,773;12891206;4482941
+02.09.2014 12:36:00,291;12891781;4482941
+02.09.2014 12:36:00,805;12891693;4482941
+02.09.2014 12:36:01,300;12891625;4482941
+02.09.2014 12:36:01,818;12891493;4482941
+02.09.2014 12:36:02,314;12891818;4482941
+02.09.2014 12:36:02,828;12891625;4482941
+02.09.2014 12:36:03,341;12891800;4482941
+02.09.2014 12:36:03,859;12891506;4482941
+02.09.2014 12:36:04,350;12891700;4482941
+02.09.2014 12:36:04,864;12891912;4482941
+02.09.2014 12:36:05,360;12891637;4482941
+02.09.2014 12:36:05,872;12891712;4482941
+02.09.2014 12:36:06,371;12891887;4482941
+02.09.2014 12:36:06,889;12891818;4482941
+02.09.2014 12:36:07,385;12891200;4482941
+02.09.2014 12:36:07,897;12891706;4482941
+02.09.2014 12:36:08,395;12891406;4482941
+02.09.2014 12:36:08,925;12891587;4482941
+02.09.2014 12:36:09,422;12891412;4482941
+02.09.2014 12:36:09,924;12891912;4482941
+02.09.2014 12:36:10,433;12891318;4482941
+02.09.2014 12:36:10,935;12891831;4482941
+02.09.2014 12:36:11,441;12891406;4482941
+02.09.2014 12:36:11,959;12891681;4482941
+02.09.2014 12:36:12,456;12891506;4482941
+02.09.2014 12:36:12,975;12891425;4482941
+02.09.2014 12:36:13,476;12891568;4482941
+02.09.2014 12:36:13,992;12891293;4482941
+02.09.2014 12:36:14,490;12891818;4482941
+02.09.2014 12:36:15,002;12891725;4482941
+02.09.2014 12:36:15,501;12891543;4482941
+02.09.2014 12:36:16,011;12891712;4482941
+02.09.2014 12:36:16,509;12891293;4482941
+02.09.2014 12:36:17,025;12891787;4482941
+02.09.2014 12:36:17,534;12891693;4482941
+02.09.2014 12:36:18,035;12891350;4482941
+02.09.2014 12:36:18,542;12891281;4482941
+02.09.2014 12:36:19,062;12891500;4482941
+02.09.2014 12:36:19,556;12891618;4482941
+02.09.2014 12:36:20,070;12891706;4482941
+02.09.2014 12:36:20,568;12891718;4482941
+02.09.2014 12:36:21,079;12891687;4482941
+02.09.2014 12:36:21,593;12891500;4482941
+02.09.2014 12:36:22,095;12891737;4482941
+02.09.2014 12:36:22,604;12891506;4482941
+02.09.2014 12:36:23,105;12891475;4482941
+02.09.2014 12:36:23,618;12891500;4482941
+02.09.2014 12:36:24,130;12891693;4482941
+02.09.2014 12:36:24,626;12891943;4482941
+02.09.2014 12:36:25,141;12891875;4482941
+02.09.2014 12:36:25,637;12891468;4482941
+02.09.2014 12:36:26,148;12891818;4482941
+02.09.2014 12:36:26,645;12891512;4482941
+02.09.2014 12:36:27,175;12891481;4482941
+02.09.2014 12:36:27,671;12891587;4482941
+02.09.2014 12:36:28,184;12891500;4482941
+02.09.2014 12:36:28,681;12891931;4482941
+02.09.2014 12:36:29,204;12891487;4482941
+02.09.2014 12:36:29,701;12891462;4482941
+02.09.2014 12:36:30,205;12891918;4482941
+02.09.2014 12:36:30,725;12891181;4482941
+02.09.2014 12:36:31,217;12891475;4482941
+02.09.2014 12:36:31,745;12891500;4482941
+02.09.2014 12:36:32,241;12891587;4482941
+02.09.2014 12:36:32,756;12891625;4482941
+02.09.2014 12:36:33,256;12891400;4482941
+02.09.2014 12:36:33,771;12891600;4482941
+02.09.2014 12:36:34,269;12891612;4482941
+02.09.2014 12:36:34,782;12891481;4482941
+02.09.2014 12:36:35,277;12891800;4482941
+02.09.2014 12:36:35,796;12891625;4482941
+02.09.2014 12:36:36,285;12891393;4482941
+02.09.2014 12:36:36,804;12891693;4482941
+02.09.2014 12:36:37,355;12891500;4482941
+02.09.2014 12:36:37,814;12891606;4482941
+02.09.2014 12:36:38,322;12891293;4482941
+02.09.2014 12:36:38,840;12891606;4482941
+02.09.2014 12:36:39,399;12891525;4482941
+02.09.2014 12:36:39,871;12891700;4482941
+02.09.2014 12:36:40,375;12891718;4482941
+02.09.2014 12:36:40,904;12891393;4482941
+02.09.2014 12:36:41,453;12891818;4482941
+02.09.2014 12:36:41,915;12891412;4482941
+02.09.2014 12:36:42,433;12891268;4482941
+02.09.2014 12:36:42,929;12891700;4482941
+02.09.2014 12:36:43,442;12891062;4482941
+02.09.2014 12:36:43,938;12891925;4482941
+02.09.2014 12:36:44,451;12891868;4482941
+02.09.2014 12:36:44,954;12891293;4482941
+02.09.2014 12:36:45,471;12891312;4482941
+02.09.2014 12:36:45,973;12891700;4482941
+02.09.2014 12:36:46,484;12891718;4482941
+02.09.2014 12:36:46,985;12891168;4482941
+02.09.2014 12:36:47,498;12891493;4482941
+02.09.2014 12:36:47,995;12891618;4482941
+02.09.2014 12:36:48,510;12891593;4482941
+02.09.2014 12:36:49,006;12891400;4482941
+02.09.2014 12:36:49,519;12891575;4482941
+02.09.2014 12:36:50,031;12891706;4482941
+02.09.2014 12:36:50,536;12891581;4482941
+02.09.2014 12:36:51,041;12891843;4482941
+02.09.2014 12:36:51,556;12891750;4482941
+02.09.2014 12:36:52,056;12891568;4482941
+02.09.2014 12:36:52,571;12891800;4482941
+02.09.2014 12:36:53,062;12891700;4482941
+02.09.2014 12:36:53,584;12891800;4482941
+02.09.2014 12:36:54,075;12891843;4482941
+02.09.2014 12:36:54,590;12891581;4482941
+02.09.2014 12:36:55,103;12891475;4482941
+02.09.2014 12:36:55,616;12891506;4482941
+02.09.2014 12:36:56,112;12891506;4482941
+02.09.2014 12:36:56,615;12891500;4482941
+02.09.2014 12:36:57,127;12891500;4482941
+02.09.2014 12:36:57,641;12891500;4482941
+02.09.2014 12:36:58,143;12891412;4482941
+02.09.2014 12:36:58,651;12891912;4482941
+02.09.2014 12:36:59,147;12891493;4482941
+02.09.2014 12:36:59,663;12891593;4482941
+02.09.2014 12:37:00,157;12891487;4482941
+02.09.2014 12:37:00,678;12891487;4482941
+02.09.2014 12:37:01,193;12891618;4482941
+02.09.2014 12:37:01,718;12891243;4482941
+02.09.2014 12:37:02,217;12891731;4482941
+02.09.2014 12:37:02,736;12891800;4482941
+02.09.2014 12:37:03,253;12891518;4482941
+02.09.2014 12:37:03,769;12891587;4482941
+02.09.2014 12:37:04,276;12891687;4482941
+02.09.2014 12:37:04,778;12891837;4482941
+02.09.2014 12:37:05,274;12891587;4482941
+02.09.2014 12:37:05,787;12891825;4482941
+02.09.2014 12:37:06,298;12891700;4482941
+02.09.2014 12:37:06,814;12891800;4482941
+02.09.2014 12:37:07,310;12891543;4482941
+02.09.2014 12:37:07,823;12891706;4482941
+02.09.2014 12:37:08,331;12891606;4482941
+02.09.2014 12:37:08,838;12891487;4482941
+02.09.2014 12:37:09,335;12891500;4482941
+02.09.2014 12:37:09,848;12891706;4482941
+02.09.2014 12:37:10,344;12891875;4482941
+02.09.2014 12:37:10,861;12891418;4482941
+02.09.2014 12:37:11,354;12891706;4482941
+02.09.2014 12:37:11,875;12891137;4482941
+02.09.2014 12:37:12,388;12891275;4482941
+02.09.2014 12:37:12,885;12891831;4482941
+02.09.2014 12:37:13,393;12891731;4482941
+02.09.2014 12:37:13,911;12891725;4482941
+02.09.2014 12:37:14,407;12891800;4482941
+02.09.2014 12:37:14,920;12891493;4482941
+02.09.2014 12:37:15,417;12891887;4482941
+02.09.2014 12:37:15,929;12891600;4482941
+02.09.2014 12:37:16,426;12891906;4482941
+02.09.2014 12:37:16,944;12891537;4482941
+02.09.2014 12:37:17,445;12891593;4482941
+02.09.2014 12:37:17,959;12891387;4482941
+02.09.2014 12:37:18,469;12892100;4482941
+02.09.2014 12:37:18,979;12891600;4482941
+02.09.2014 12:37:19,476;12891700;4482941
+02.09.2014 12:37:19,989;12891912;4482941
+02.09.2014 12:37:20,485;12891600;4482941
+02.09.2014 12:37:20,996;12891687;4482941
+02.09.2014 12:37:21,493;12891587;4482941
+02.09.2014 12:37:22,023;12891606;4482941
+02.09.2014 12:37:22,531;12891693;4482941
+02.09.2014 12:37:23,033;12891875;4482941
+02.09.2014 12:37:23,530;12891587;4482941
+02.09.2014 12:37:24,046;12891468;4482941
+02.09.2014 12:37:24,556;12891800;4482941
+02.09.2014 12:37:25,055;12891600;4482941
+02.09.2014 12:37:25,564;12891506;4482941
+02.09.2014 12:37:26,067;12891375;4482941
+02.09.2014 12:37:26,574;12891512;4482941
+02.09.2014 12:37:27,092;12891787;4482941
+02.09.2014 12:37:27,590;12891868;4482941
+02.09.2014 12:37:28,102;12892006;4482941
+02.09.2014 12:37:28,598;12891712;4482941
+02.09.2014 12:37:29,117;12891662;4482941
+02.09.2014 12:37:29,624;12891931;4482941
+02.09.2014 12:37:30,138;12891575;4482941
+02.09.2014 12:37:30,634;12891662;4482941
+02.09.2014 12:37:31,135;12891806;4482941
+02.09.2014 12:37:31,642;12891362;4482941
+02.09.2014 12:37:32,162;12891400;4482941
+02.09.2014 12:37:32,660;12891400;4482941
+02.09.2014 12:37:33,171;12891481;4482941
+02.09.2014 12:37:33,693;12891450;4482941
+02.09.2014 12:37:34,189;12891331;4482941
+02.09.2014 12:37:34,695;12891600;4482941
+02.09.2014 12:37:35,205;12891712;4482941
+02.09.2014 12:37:35,723;12891700;4482941
+02.09.2014 12:37:36,223;12891612;4482941
+02.09.2014 12:37:36,733;12891600;4482941
+02.09.2014 12:37:37,229;12891625;4482941
+02.09.2014 12:37:37,741;12891656;4482941
+02.09.2014 12:37:38,256;12891387;4482941
+02.09.2014 12:37:38,758;12891700;4482941
+02.09.2014 12:37:39,265;12891662;4482941
+02.09.2014 12:37:39,773;12891543;4482941
+02.09.2014 12:37:40,279;12891393;4482941
+02.09.2014 12:37:40,792;12891831;4482941
+02.09.2014 12:37:41,289;12891600;4482941
+02.09.2014 12:37:41,804;12891687;4482941
+02.09.2014 12:37:42,311;12891656;4482941
+02.09.2014 12:37:42,815;12891300;4482941
+02.09.2014 12:37:43,327;12891906;4482941
+02.09.2014 12:37:43,828;12891368;4482941
+02.09.2014 12:37:44,335;12891662;4482941
+02.09.2014 12:37:44,837;12891468;4482941
+02.09.2014 12:37:45,344;12891900;4482941
+02.09.2014 12:37:45,864;12891593;4482941
+02.09.2014 12:37:46,382;12891725;4482941
+02.09.2014 12:37:46,874;12891381;4482941
+02.09.2014 12:37:47,371;12891593;4482941
+02.09.2014 12:37:47,921;12891500;4482941
+02.09.2014 12:37:48,386;12891706;4482941
+02.09.2014 12:37:48,947;12891800;4482941
+02.09.2014 12:37:49,412;12891450;4482941
+02.09.2014 12:37:49,955;12891587;4482941
+02.09.2014 12:37:50,412;12891431;4482941
+02.09.2014 12:37:50,977;12891537;4482941
+02.09.2014 12:37:51,440;12891693;4482941
+02.09.2014 12:37:51,940;12891700;4482941
+02.09.2014 12:37:52,455;12891806;4482941
+02.09.2014 12:37:52,980;12891487;4482941
+02.09.2014 12:37:53,475;12891625;4482941
+02.09.2014 12:37:54,005;12891500;4482941
+02.09.2014 12:37:54,508;12891262;4482941
+02.09.2014 12:37:55,013;12891293;4482941
+02.09.2014 12:37:55,533;12891750;4482941
+02.09.2014 12:37:56,033;12891800;4482941
+02.09.2014 12:37:56,565;12891693;4482941
+02.09.2014 12:37:57,070;12891600;4482941
+02.09.2014 12:37:57,585;12891762;4482941
+02.09.2014 12:37:58,097;12891318;4482941
+02.09.2014 12:37:58,594;12891450;4482941
+02.09.2014 12:37:59,123;12891600;4482941
+02.09.2014 12:37:59,603;12891812;4482941
+02.09.2014 12:38:00,132;12891287;4482941
+02.09.2014 12:38:00,630;12891943;4482941
+02.09.2014 12:38:01,142;12891706;4482941
+02.09.2014 12:38:01,639;12891700;4482941
+02.09.2014 12:38:02,156;12891506;4482941
+02.09.2014 12:38:02,665;12891487;4482941
+02.09.2014 12:38:03,168;12891687;4482941
+02.09.2014 12:38:03,673;12891631;4482941
+02.09.2014 12:38:04,190;12891487;4482941
+02.09.2014 12:38:04,683;12891493;4482941
+02.09.2014 12:38:05,201;12891962;4482941
+02.09.2014 12:38:05,705;12891825;4482941
+02.09.2014 12:38:06,223;12891931;4482941
+02.09.2014 12:38:06,734;12891543;4482941
+02.09.2014 12:38:07,230;12891437;4482941
+02.09.2014 12:38:07,742;12891706;4482941
+02.09.2014 12:38:08,238;12891831;4482941
+02.09.2014 12:38:08,757;12891550;4482941
+02.09.2014 12:38:09,265;12891593;4482941
+02.09.2014 12:38:09,767;12891456;4482941
+02.09.2014 12:38:10,274;12891700;4482941
+02.09.2014 12:38:10,776;12891500;4482941
+02.09.2014 12:38:11,289;12891700;4482941
+02.09.2014 12:38:11,802;12891512;4482941
+02.09.2014 12:38:12,298;12891606;4482941
+02.09.2014 12:38:12,812;12891825;4482941
+02.09.2014 12:38:13,314;12891656;4482941
+02.09.2014 12:38:13,825;12891681;4482941
+02.09.2014 12:38:14,333;12891700;4482941
+02.09.2014 12:38:14,838;12891818;4482941
+02.09.2014 12:38:15,342;12891700;4482941
+02.09.2014 12:38:15,844;12891593;4482941
+02.09.2014 12:38:16,371;12891943;4482941
+02.09.2014 12:38:16,872;12891493;4482941
+02.09.2014 12:38:17,369;12891606;4482941
+02.09.2014 12:38:17,882;12891337;4482941
+02.09.2014 12:38:18,395;12891400;4482941
+02.09.2014 12:38:18,895;12891306;4482941
+02.09.2014 12:38:19,403;12891693;4482941
+02.09.2014 12:38:19,905;12891387;4482941
+02.09.2014 12:38:20,412;12892018;4482941
+02.09.2014 12:38:20,915;12891293;4482941
+02.09.2014 12:38:21,428;12891606;4482941
+02.09.2014 12:38:21,933;12891612;4482941
+02.09.2014 12:38:22,440;12891506;4482941
+02.09.2014 12:38:22,945;12891918;4482941
+02.09.2014 12:38:23,463;12891737;4482941
+02.09.2014 12:38:23,974;12891706;4482941
+02.09.2014 12:38:24,470;12891500;4482941
+02.09.2014 12:38:24,972;12891800;4482941
+02.09.2014 12:38:25,479;12891600;4482941
+02.09.2014 12:38:25,991;12891562;4482941
+02.09.2014 12:38:26,505;12891706;4482941
+02.09.2014 12:38:27,006;12891406;4482941
+02.09.2014 12:38:27,512;12891856;4482941
+02.09.2014 12:38:28,022;12891775;4482941
+02.09.2014 12:38:28,534;12890787;4482156
+02.09.2014 12:38:29,044;12814200;4474773
+02.09.2014 12:38:29,539;12640693;4464232
+02.09.2014 12:38:30,050;12343431;4453238
+02.09.2014 12:38:30,545;11965843;4443372
+02.09.2014 12:38:31,056;11483237;4432953
+02.09.2014 12:38:31,564;11085981;4423011
+02.09.2014 12:38:32,080;10821987;4413587
+02.09.2014 12:38:32,592;10646100;4403720
+02.09.2014 12:38:33,119;10585993;4399918
+02.09.2014 12:38:33,630;10588675;4399982
+02.09.2014 12:38:34,134;10588300;4399976
+02.09.2014 12:38:34,645;10588700;4399976
+02.09.2014 12:38:35,152;10589100;4399976
+02.09.2014 12:38:35,671;10589487;4399976
+02.09.2014 12:38:36,185;10589618;4399976
+02.09.2014 12:38:36,677;10589993;4399976
+02.09.2014 12:38:37,204;10589812;4399976
+02.09.2014 12:38:37,683;10589587;4399976
+02.09.2014 12:38:38,204;10589800;4399976
+02.09.2014 12:38:38,711;10590275;4399976
+02.09.2014 12:38:39,215;10589581;4399976
+02.09.2014 12:38:39,722;10590100;4399976
+02.09.2014 12:38:40,237;10590125;4399976
+02.09.2014 12:38:40,743;10590106;4399976
+02.09.2014 12:38:41,243;10589887;4399976
+02.09.2014 12:38:41,751;10589906;4402098
+02.09.2014 12:38:42,269;10589993;4435494
+02.09.2014 12:38:42,769;10590006;4441250
+02.09.2014 12:38:43,284;10589893;4446412
+02.09.2014 12:38:43,784;10590206;4500226
+02.09.2014 12:38:44,293;10590187;4510122
+02.09.2014 12:38:44,803;10589987;4510017
+02.09.2014 12:38:45,309;10589993;4510005
+02.09.2014 12:38:45,810;10589868;4510005
+02.09.2014 12:38:46,328;10590100;4510005
+02.09.2014 12:38:46,816;10589843;4510005
+02.09.2014 12:38:47,333;10589987;4510005
+02.09.2014 12:38:47,822;10589962;4510005
+02.09.2014 12:38:48,350;10590006;4510005
+02.09.2014 12:38:48,869;10590100;4510005
+02.09.2014 12:38:49,380;10590093;4510005
+02.09.2014 12:38:49,890;10589893;4510005
+02.09.2014 12:38:50,416;10590200;4510005
+02.09.2014 12:38:50,920;10589750;4510005
+02.09.2014 12:38:51,416;10590106;4510005
+02.09.2014 12:38:51,943;10590143;4510005
+02.09.2014 12:38:52,438;10590150;4510005
+02.09.2014 12:38:52,950;10590218;4510005
+02.09.2014 12:38:53,467;10590081;4510005
+02.09.2014 12:38:53,967;10590006;4510005
+02.09.2014 12:38:54,462;10590118;4510005
+02.09.2014 12:38:54,982;10590231;4510005
+02.09.2014 12:38:55,477;10589875;4510005
+02.09.2014 12:38:55,988;10590081;4510005
+02.09.2014 12:38:56,493;10590037;4510005
+02.09.2014 12:38:57,046;10590325;4510005
+02.09.2014 12:38:57,505;10585518;4508639
+02.09.2014 12:38:58,065;10454475;4515750
+02.09.2014 12:38:58,524;10283625;4521017
+02.09.2014 12:38:59,079;9960256;4527168
+02.09.2014 12:38:59,549;9523156;4532831
+02.09.2014 12:39:00,046;9076012;4538843
+02.09.2014 12:39:00,556;8633468;4544709
+02.09.2014 12:39:01,054;8151150;4550017
+02.09.2014 12:39:01,563;7708637;4555744
+02.09.2014 12:39:02,079;7229262;4561319
+02.09.2014 12:39:02,574;6785318;4566366
+02.09.2014 12:39:03,086;6305775;4571674
+02.09.2014 12:39:03,599;5825231;4576837
+02.09.2014 12:39:04,109;5382581;4581982
+02.09.2014 12:39:04,600;4939825;4586558
+02.09.2014 12:39:05,116;4458293;4591255
+02.09.2014 12:39:05,612;4016000;4595500
+02.09.2014 12:39:06,122;3536293;4599889
+02.09.2014 12:39:06,630;3092237;4604029
+02.09.2014 12:39:07,145;2612468;4607941
+02.09.2014 12:39:07,659;2133237;4611238
+02.09.2014 12:39:08,164;1652825;4614540
+02.09.2014 12:39:08,682;1210506;4617383
+02.09.2014 12:39:09,176;909968;4619005
+02.09.2014 12:39:09,689;693731;4619011
+02.09.2014 12:39:10,188;601231;4619000
+02.09.2014 12:39:10,700;586000;4619000
+02.09.2014 12:39:11,198;585462;4619000
+02.09.2014 12:39:11,715;587943;4619000
+02.09.2014 12:39:12,226;587125;4619000
+02.09.2014 12:39:12,744;587493;4619000
+02.09.2014 12:39:13,254;587687;4619000
+02.09.2014 12:39:13,779;587800;4619000
+02.09.2014 12:39:14,287;588293;4619000
+02.09.2014 12:39:14,797;587675;4619000
+02.09.2014 12:39:15,310;587575;4619000
+02.09.2014 12:39:15,808;587712;4619000
+02.09.2014 12:39:16,317;588106;4619000
+02.09.2014 12:39:16,814;587987;4619000
+02.09.2014 12:39:17,329;587681;4619000
+02.09.2014 12:39:17,832;587900;4619000
+02.09.2014 12:39:18,348;587906;4617866
+02.09.2014 12:39:18,843;588006;4586197
+02.09.2014 12:39:19,355;588475;4544447
+02.09.2014 12:39:19,864;588006;4537813
+02.09.2014 12:39:20,379;588100;4533924
+02.09.2014 12:39:20,874;588012;4502552
+02.09.2014 12:39:21,386;588137;4499058
+02.09.2014 12:39:21,892;587800;4499162
+02.09.2014 12:39:22,403;588000;4499308
+02.09.2014 12:39:22,898;588018;4499313
+02.09.2014 12:39:23,419;588100;4499302
+02.09.2014 12:39:23,920;588412;4499302
+02.09.2014 12:39:24,436;587900;4499302
+02.09.2014 12:39:24,927;587818;4499302
+02.09.2014 12:39:25,454;587981;4499302
+02.09.2014 12:39:25,943;588006;4499302
+02.09.2014 12:39:26,469;587906;4499302
+02.09.2014 12:39:26,954;587593;4499302
+02.09.2014 12:39:27,470;588225;4499302
+02.09.2014 12:39:27,964;587931;4499023
+02.09.2014 12:39:28,492;587793;4479941
+02.09.2014 12:39:28,992;587893;4376110
+02.09.2014 12:39:29,497;587993;4160523
+02.09.2014 12:39:30,003;587506;3859598
+02.09.2014 12:39:30,522;587987;3550546
+02.09.2014 12:39:31,015;587950;3265220
+02.09.2014 12:39:31,527;587775;2956145
+02.09.2014 12:39:32,032;588112;2647046
+02.09.2014 12:39:32,542;588106;2337970
+02.09.2014 12:39:33,037;587875;2052662
+02.09.2014 12:39:33,552;588100;1743558
+02.09.2014 12:39:34,060;588118;1461610
+02.09.2014 12:39:34,570;588306;1287715
+02.09.2014 12:39:35,064;588106;1222703
+02.09.2014 12:39:35,593;588100;1220284
+02.09.2014 12:39:36,088;588200;1220418
+02.09.2014 12:39:36,597;588200;1220424
+02.09.2014 12:39:37,092;588131;1220424
+02.09.2014 12:39:37,604;588106;1220424
+02.09.2014 12:39:38,110;588418;1220424
+02.09.2014 12:39:38,624;587787;1220424
+02.09.2014 12:39:39,135;588218;1220424
+02.09.2014 12:39:39,646;588300;1220424
+02.09.2014 12:39:40,159;588081;1220424
+02.09.2014 12:39:40,664;588206;1220424
+02.09.2014 12:39:41,165;588456;1220424
+02.09.2014 12:39:41,670;588443;1220424
+02.09.2014 12:39:42,186;588193;1221401
+02.09.2014 12:39:42,687;588006;1255482
+02.09.2014 12:39:43,195;588412;1261366
+02.09.2014 12:39:43,697;588543;1265587
+02.09.2014 12:39:44,206;588093;1318808
+02.09.2014 12:39:44,712;588506;1340587
+02.09.2014 12:39:45,228;588318;1340482
+02.09.2014 12:39:45,733;588118;1340494
+02.09.2014 12:39:46,233;587956;1340482
+02.09.2014 12:39:46,738;588306;1340482
+02.09.2014 12:39:47,255;588000;1340482
+02.09.2014 12:39:47,745;588193;1340482
+02.09.2014 12:39:48,274;588218;1340482
+02.09.2014 12:39:48,769;588093;1340482
+02.09.2014 12:39:49,279;588162;1340482
+02.09.2014 12:39:49,790;587968;1340482
+02.09.2014 12:39:50,302;587987;1340482
+02.09.2014 12:39:50,812;588143;1340482
+02.09.2014 12:39:51,312;588312;1340482
+02.09.2014 12:39:51,813;588325;1340482
+02.09.2014 12:39:52,324;588281;1340482
+02.09.2014 12:39:52,814;588087;1340482
+02.09.2014 12:39:53,342;588300;1340482
+02.09.2014 12:39:53,836;587668;1340482
+02.09.2014 12:39:54,347;596350;1339406
+02.09.2014 12:39:54,855;708618;1293470
+02.09.2014 12:39:55,373;912631;1198116
+02.09.2014 12:39:55,868;1213918;1110116
+02.09.2014 12:39:56,380;1649262;1014924
+02.09.2014 12:39:56,873;2096056;927215
+02.09.2014 12:39:57,407;2614831;832069
+02.09.2014 12:39:57,891;3020081;737058
+02.09.2014 12:39:58,409;3501206;642540
+02.09.2014 12:39:58,908;3945131;555866
+02.09.2014 12:39:59,420;4397293;460593
+02.09.2014 12:39:59,932;4732806;300470
+02.09.2014 12:40:00,432;4939181;173220
+02.09.2014 12:40:00,938;5053212;136645
+02.09.2014 12:40:01,449;5062731;136802
+02.09.2014 12:40:01,942;5064293;136837
+02.09.2014 12:40:02,473;5063481;136825
+02.09.2014 12:40:02,962;5062600;136825
+02.09.2014 12:40:03,479;5062600;136825
+02.09.2014 12:40:03,973;5062287;136825
+02.09.2014 12:40:04,488;5062331;136825
+02.09.2014 12:40:05,038;5062193;136825
+02.09.2014 12:40:05,507;5062200;136825
+02.09.2014 12:40:06,014;5062093;136825
+02.09.2014 12:40:06,516;5062000;136825
+02.09.2014 12:40:07,064;5062143;136825
+02.09.2014 12:40:07,535;5062000;136825
+02.09.2014 12:40:08,025;5062000;136825
+02.09.2014 12:40:08,554;5061950;136825
+02.09.2014 12:40:09,046;5061887;136825
+02.09.2014 12:40:09,571;5062075;135534
+02.09.2014 12:40:10,076;5060612;102470
+02.09.2014 12:40:10,582;5060175;62279
+02.09.2014 12:40:11,082;5060475;56331
+02.09.2014 12:40:11,588;5060775;51982
+02.09.2014 12:40:12,105;5061000;20976
+02.09.2014 12:40:12,610;5060993;17180
+02.09.2014 12:40:13,122;5060881;17308
+02.09.2014 12:40:13,616;5060900;17331
+02.09.2014 12:40:14,129;5060712;17319
+02.09.2014 12:40:14,639;5060906;17319
+02.09.2014 12:40:15,156;5060812;17319
+02.09.2014 12:40:15,650;5060800;17319
+02.09.2014 12:40:16,162;5060806;17319
+02.09.2014 12:40:16,666;5060793;17319
+02.09.2014 12:40:17,181;5060581;17319
+02.09.2014 12:40:17,675;5060700;17319
+02.09.2014 12:40:18,187;5060675;17319
+02.09.2014 12:40:18,682;5060700;17319
+02.09.2014 12:40:19,195;5060862;17319
+02.09.2014 12:40:19,708;5060793;17319
+02.09.2014 12:40:20,219;5060700;17319
+02.09.2014 12:40:20,722;5060762;17319
+02.09.2014 12:40:21,228;5060900;17319
+02.09.2014 12:40:21,741;5060912;17319
+02.09.2014 12:40:22,255;5060675;17319
+02.09.2014 12:40:22,750;5061037;17319
+02.09.2014 12:40:23,264;5060800;17319
+02.09.2014 12:40:23,767;5060612;17319
+02.09.2014 12:40:24,271;5060806;17319
+02.09.2014 12:40:24,773;5060787;17319
+02.09.2014 12:40:25,286;5060800;17319
+02.09.2014 12:40:25,782;5060800;17319
+02.09.2014 12:40:26,294;5060668;17319
+02.09.2014 12:40:26,808;5060675;17319
+02.09.2014 12:40:27,320;5060825;17319
+02.09.2014 12:40:27,816;5060600;17319
+02.09.2014 12:40:28,329;5060681;17319
+02.09.2014 12:40:28,825;5060600;17319
+02.09.2014 12:40:29,337;5060700;17319
+02.09.2014 12:40:29,834;5060712;17319
+02.09.2014 12:40:30,352;5060800;17319
+02.09.2014 12:40:30,862;5060781;17319
+02.09.2014 12:40:31,365;5060900;17319
+02.09.2014 12:40:31,865;5060818;17319
+02.09.2014 12:40:32,386;5060893;17319
+02.09.2014 12:40:32,891;5060900;17319
+02.09.2014 12:40:33,393;5060825;17319
+02.09.2014 12:40:33,912;5060712;17319
+02.09.2014 12:40:34,402;5060800;17319
+02.09.2014 12:40:34,914;5060800;17319
+02.09.2014 12:40:35,428;5060712;17319
+02.09.2014 12:40:35,924;5060943;17319
+02.09.2014 12:40:36,436;5060818;17319
+02.09.2014 12:40:36,950;5060800;17319
+02.09.2014 12:40:37,464;5060812;17319
+02.09.2014 12:40:37,960;5060662;17319
+02.09.2014 12:40:38,473;5060781;17319
+02.09.2014 12:40:38,972;5060881;17319
+02.09.2014 12:40:39,481;5060800;17319
+02.09.2014 12:40:39,977;5060787;17319
+02.09.2014 12:40:40,494;5060700;17319
+02.09.2014 12:40:41,016;5060806;17319
+02.09.2014 12:40:41,517;5060700;17319
+02.09.2014 12:40:42,023;5060693;17319
+02.09.2014 12:40:42,531;5060912;17319
+02.09.2014 12:40:43,032;5060687;17319
+02.09.2014 12:40:43,539;5060806;17319
+02.09.2014 12:40:44,059;5060800;17319
+02.09.2014 12:40:44,553;5060900;17319
+02.09.2014 12:40:45,067;5060937;17319
+02.09.2014 12:40:45,563;5060700;17319
+02.09.2014 12:40:46,075;5060681;17319
+02.09.2014 12:40:46,588;5060568;17319
+02.09.2014 12:40:47,093;5060775;17319
+02.09.2014 12:40:47,602;5060800;17319
+02.09.2014 12:40:48,106;5060800;17319
+02.09.2014 12:40:48,613;5060925;17319
+02.09.2014 12:40:49,131;5060900;17319
+02.09.2014 12:40:49,622;5061025;17319
+02.09.2014 12:40:50,139;5060831;17319
+02.09.2014 12:40:50,635;5060806;17319
+02.09.2014 12:40:51,150;5060662;17319
+02.09.2014 12:40:51,644;5060643;17319
+02.09.2014 12:40:52,166;5060700;17319
+02.09.2014 12:40:52,673;5060762;17319
+02.09.2014 12:40:53,173;5060987;17319
+02.09.2014 12:40:53,680;5060793;17319
+02.09.2014 12:40:54,198;5060656;17319
+02.09.2014 12:40:54,694;5060668;17319
+02.09.2014 12:40:55,208;5060781;17319
+02.09.2014 12:40:55,704;5060687;17319
+02.09.2014 12:40:56,217;5060562;17319
+02.09.2014 12:40:56,730;5060731;17319
+02.09.2014 12:40:57,242;5060681;17319
+02.09.2014 12:40:57,737;5060700;17319
+02.09.2014 12:40:58,249;5060687;17319
+02.09.2014 12:40:58,744;5060750;17319
+02.09.2014 12:40:59,265;5060800;17319
+02.09.2014 12:40:59,755;5060906;17319
+02.09.2014 12:41:00,275;5060693;17319
+02.09.2014 12:41:00,782;5060687;17319
+02.09.2014 12:41:01,284;5060768;17319
+02.09.2014 12:41:01,791;5060737;17319
+02.09.2014 12:41:02,308;5060593;17319
+02.09.2014 12:41:02,805;5060787;17319
+02.09.2014 12:41:03,321;5060693;17319
+02.09.2014 12:41:03,817;5060800;17319
+02.09.2014 12:41:04,338;5060787;17319
+02.09.2014 12:41:04,834;5060681;17319
+02.09.2014 12:41:05,348;5060775;17319
+02.09.2014 12:41:05,842;5060725;17319
+02.09.2014 12:41:06,358;5060906;17319
+02.09.2014 12:41:06,870;5060681;17319
+02.09.2014 12:41:07,371;5060412;17319
+02.09.2014 12:41:07,879;5060800;17319
+02.09.2014 12:41:08,391;5060806;17319
+02.09.2014 12:41:08,886;5060800;17319
+02.09.2014 12:41:09,411;5060593;17319
+02.09.2014 12:41:09,896;5061018;17319
+02.09.2014 12:41:10,414;5060762;17319
+02.09.2014 12:41:10,921;5060868;17319
+02.09.2014 12:41:11,423;5060781;17319
+02.09.2014 12:41:11,930;5060800;17319
+02.09.2014 12:41:12,449;5060793;17319
+02.09.2014 12:41:12,962;5060731;17319
+02.09.2014 12:41:13,463;5060900;17319
+02.09.2014 12:41:13,975;5060787;17319
+02.09.2014 12:41:14,494;5060643;17319
+02.09.2014 12:41:14,984;5060775;17319
+02.09.2014 12:41:15,491;5060693;17319
+02.09.2014 12:41:15,996;5060468;17319
+02.09.2014 12:41:16,529;5060600;17319
+02.09.2014 12:41:17,017;5060931;17319
+02.09.2014 12:41:17,530;5060806;17319
+02.09.2014 12:41:18,076;5060831;17319
+02.09.2014 12:41:18,560;5060762;17319
+02.09.2014 12:41:19,100;5060862;17319
+02.09.2014 12:41:19,583;5060787;17319
+02.09.2014 12:41:20,091;5060912;17319
+02.09.2014 12:41:20,612;5060812;17319
+02.09.2014 12:41:21,108;5060800;17319
+02.09.2014 12:41:21,621;5060712;17319
+02.09.2014 12:41:22,115;5060806;17319
+02.09.2014 12:41:22,629;5060906;17319
+02.09.2014 12:41:23,124;5060800;17319
+02.09.2014 12:41:23,642;5061018;17319
+02.09.2014 12:41:24,148;5060662;17319
+02.09.2014 12:41:24,662;5060800;17319
+02.09.2014 12:41:25,161;5060787;17319
+02.09.2014 12:41:25,677;5060693;17319
+02.09.2014 12:41:26,172;5060775;17319
+02.09.2014 12:41:26,685;5060700;17319
+02.09.2014 12:41:27,182;5060706;17319
+02.09.2014 12:41:27,695;5060706;17319
+02.09.2014 12:41:28,202;5060768;17319
+02.09.2014 12:41:28,720;5060487;17319
+02.09.2014 12:41:29,215;5060806;17319
+02.09.2014 12:41:29,728;5060693;17319
+02.09.2014 12:41:30,223;5060800;17319
+02.09.2014 12:41:30,740;5060787;17319
+02.09.2014 12:41:31,249;5060681;17319
+02.09.2014 12:41:31,763;5060700;17319
+02.09.2014 12:41:32,258;5060906;17319
+02.09.2014 12:41:32,771;5060687;17319
+02.09.2014 12:41:33,270;5060918;17319
+02.09.2014 12:41:33,785;5060675;17319
+02.09.2014 12:41:34,291;5060700;17319
+02.09.2014 12:41:34,792;5060950;17319
+02.09.2014 12:41:35,301;5060818;17319
+02.09.2014 12:41:35,820;5060562;17319
+02.09.2014 12:41:36,317;5060800;17319
+02.09.2014 12:41:36,829;5098306;23645
+02.09.2014 12:41:37,331;5250150;103755
+02.09.2014 12:41:37,834;5492000;232244
+02.09.2014 12:41:38,343;5863950;361779
+02.09.2014 12:41:38,854;6299706;491162
+02.09.2014 12:41:39,362;6779550;610436
+02.09.2014 12:41:39,872;7261800;739732
+02.09.2014 12:41:40,366;7705881;859058
+02.09.2014 12:41:40,882;8148137;988354
+02.09.2014 12:41:41,388;8629112;1117633
+02.09.2014 12:41:41,900;9109462;1246808
+02.09.2014 12:41:42,393;9552675;1365877
+02.09.2014 12:41:42,904;9996643;1494895
+02.09.2014 12:41:43,430;10512625;1623877
+02.09.2014 12:41:43,925;10955856;1742901
+02.09.2014 12:41:44,435;11436187;1871779
+02.09.2014 12:41:44,945;11877050;2000529
+02.09.2014 12:41:45,459;12321025;2129191
+02.09.2014 12:41:45,950;12802431;2247837
+02.09.2014 12:41:46,464;13244706;2376308
+02.09.2014 12:41:46,968;13724568;2504883
+02.09.2014 12:41:47,479;14205200;2633197
+02.09.2014 12:41:47,973;14647606;2751523
+02.09.2014 12:41:48,501;15128218;2879656
+02.09.2014 12:41:49,006;15607900;3007494
+02.09.2014 12:41:49,507;16051218;3125517
+02.09.2014 12:41:50,001;16494837;3243511
+02.09.2014 12:41:50,529;16972906;3380866
+02.09.2014 12:41:51,022;17416443;3498308
+02.09.2014 12:41:51,537;17897581;3625529
+02.09.2014 12:41:52,031;18339106;3743005
+02.09.2014 12:41:52,546;18819593;3869744
+02.09.2014 12:41:53,058;19300325;3991802
+02.09.2014 12:41:53,575;19718837;4120860
+02.09.2014 12:41:54,064;20057856;4327133
+02.09.2014 12:41:54,581;20268518;4454069
+02.09.2014 12:41:55,086;20387406;4490162
+02.09.2014 12:41:55,598;20400481;4490017
+02.09.2014 12:41:56,102;20401893;4489965
+02.09.2014 12:41:56,602;20399406;4489953
+02.09.2014 12:41:57,106;20399075;4489953
+02.09.2014 12:41:57,617;20399431;4489953
+02.09.2014 12:41:58,122;20399325;4489953
+02.09.2014 12:41:58,638;20399156;4489953
+02.09.2014 12:41:59,131;20399200;4489953
+02.09.2014 12:41:59,642;20398975;4489953
+02.09.2014 12:42:00,148;20399162;4489953
+02.09.2014 12:42:00,666;20399275;4489953
+02.09.2014 12:42:01,171;20399331;4489953
+02.09.2014 12:42:01,682;20399193;4489953
+02.09.2014 12:42:02,176;20399300;4489790
+02.09.2014 12:42:02,692;20399187;4510145
+02.09.2014 12:42:03,183;20399131;4527627
+02.09.2014 12:42:03,709;20399300;4532953
+02.09.2014 12:42:04,211;20399300;4550174
+02.09.2014 12:42:04,715;20399500;4606215
+02.09.2014 12:42:05,225;20399200;4610063
+02.09.2014 12:42:05,736;20399275;4610034
+02.09.2014 12:42:06,231;20399306;4610023
+02.09.2014 12:42:06,741;20399131;4610023
+02.09.2014 12:42:07,246;20399056;4610023
+02.09.2014 12:42:07,770;20399268;4610023
+02.09.2014 12:42:08,259;20398993;4610023
+02.09.2014 12:42:08,780;20398862;4610023
+02.09.2014 12:42:09,274;20399506;4610023
+02.09.2014 12:42:09,786;20399400;4610023
+02.09.2014 12:42:10,291;20399131;4610023
+02.09.2014 12:42:10,807;20398993;4610023
+02.09.2014 12:42:11,306;20399200;4610023
+02.09.2014 12:42:11,817;20399181;4610023
+02.09.2014 12:42:12,311;20399375;4610023
+02.09.2014 12:42:12,839;20399500;4610023
+02.09.2014 12:42:13,336;20399181;4610023
+02.09.2014 12:42:13,846;20399187;4610023
+02.09.2014 12:42:14,354;20399387;4610023
+02.09.2014 12:42:14,856;20399293;4610023
+02.09.2014 12:42:15,371;20398612;4608866
+02.09.2014 12:42:15,877;20318200;4579360
+02.09.2014 12:42:16,388;20123718;4512860
+02.09.2014 12:42:16,900;19841825;4440395
+02.09.2014 12:42:17,411;19427050;4367994
+02.09.2014 12:42:17,906;18978475;4295627
+02.09.2014 12:42:18,417;18500237;4228866
+02.09.2014 12:42:18,912;18055150;4156703
+02.09.2014 12:42:19,424;17575131;4090168
+02.09.2014 12:42:19,934;17131293;4018069
+02.09.2014 12:42:20,445;16652531;3945994
+02.09.2014 12:42:20,954;16172006;3873982
+02.09.2014 12:42:21,452;15728300;3807546
+02.09.2014 12:42:21,964;15285662;3735732
+02.09.2014 12:42:22,475;14805531;3663924
+02.09.2014 12:42:22,981;14325212;3592180
+02.09.2014 12:42:23,482;13882356;3526075
+02.09.2014 12:42:24,020;13365050;3448959
+02.09.2014 12:42:24,516;12923125;3377517
+02.09.2014 12:42:25,006;12481012;3317133
+02.09.2014 12:42:25,543;11962731;3240337
+02.09.2014 12:42:26,011;11556468;3180081
+02.09.2014 12:42:26,561;11039606;3098034
+02.09.2014 12:42:27,034;10632837;3038058
+02.09.2014 12:42:27,590;10117137;2956412
+02.09.2014 12:42:28,041;9710712;2902081
+02.09.2014 12:42:28,602;9156093;2820761
+02.09.2014 12:42:29,059;8787937;2761104
+02.09.2014 12:42:29,571;8307318;2696081
+02.09.2014 12:42:30,081;7827293;2620500
+02.09.2014 12:42:30,583;7422862;2556273
+02.09.2014 12:42:31,088;6941443;2487017
+02.09.2014 12:42:31,601;6462231;2423220
+02.09.2014 12:42:32,101;6019287;2353936
+02.09.2014 12:42:32,612;5602737;2290651
+02.09.2014 12:42:33,118;5326131;2188877
+02.09.2014 12:42:33,630;5137206;2103843
+02.09.2014 12:42:34,125;5063531;2094674
+02.09.2014 12:42:34,636;5064400;2094843
+02.09.2014 12:42:35,147;5063625;2093511
+02.09.2014 12:42:35,660;5063693;2093511
+02.09.2014 12:42:36,154;5064606;2092720
+02.09.2014 12:42:36,666;5064393;2092691
+02.09.2014 12:42:37,176;5064700;2092883
+02.09.2014 12:42:37,688;5064900;2092924
+02.09.2014 12:42:38,183;5064700;2092924
+02.09.2014 12:42:38,695;5064637;2092918
+02.09.2014 12:42:39,201;5065000;2092918
+02.09.2014 12:42:39,701;5064906;2093540
+02.09.2014 12:42:40,209;5065018;2094982
+02.09.2014 12:42:40,726;5064887;2094970
+02.09.2014 12:42:41,224;5064900;2094970
+02.09.2014 12:42:41,736;5064768;2094970
+02.09.2014 12:42:42,248;5064887;2093883
+02.09.2014 12:42:42,761;5064806;2072261
+02.09.2014 12:42:43,256;5065093;2030517
+02.09.2014 12:42:43,767;5065200;2023848
+02.09.2014 12:42:44,272;5065200;2019924
+02.09.2014 12:42:44,772;5065462;1989168
+02.09.2014 12:42:45,283;5065193;1985250
+02.09.2014 12:42:45,794;5065250;1985383
+02.09.2014 12:42:46,317;5065175;1985372
+02.09.2014 12:42:46,800;5065193;1985372
+02.09.2014 12:42:47,327;5065300;1985372
+02.09.2014 12:42:47,822;5065218;1985372
+02.09.2014 12:42:48,333;5065181;1985372
+02.09.2014 12:42:48,843;5065200;1985372
+02.09.2014 12:42:49,342;5065300;1985372
+02.09.2014 12:42:49,851;5065300;1985372
+02.09.2014 12:42:50,369;5065206;1985372
+02.09.2014 12:42:50,880;5065400;1985372
+02.09.2014 12:42:51,400;5065318;1985372
+02.09.2014 12:42:51,913;5065275;1985372
+02.09.2014 12:42:52,429;5065306;1985372
+02.09.2014 12:42:52,931;5065162;1985372
+02.09.2014 12:42:53,439;5065400;1985372
+02.09.2014 12:42:53,958;5065206;1985372
+02.09.2014 12:42:54,452;5065300;1985372
+02.09.2014 12:42:54,965;5065187;1985372
+02.09.2014 12:42:55,462;5065300;1985372
+02.09.2014 12:42:55,975;5065200;1985372
+02.09.2014 12:42:56,474;5065187;1985372
+02.09.2014 12:42:56,995;5065406;1985372
+02.09.2014 12:42:57,491;5065387;1985372
+02.09.2014 12:42:58,005;5065300;1985372
+02.09.2014 12:42:58,518;5065193;1985372
+02.09.2014 12:42:59,029;5065306;1985372
+02.09.2014 12:42:59,526;5065293;1985372
+02.09.2014 12:43:00,037;5065300;1985372
+02.09.2014 12:43:00,533;5065175;1985372
+02.09.2014 12:43:01,046;5065268;1985372
+02.09.2014 12:43:01,543;5065218;1985372
+02.09.2014 12:43:02,062;5065200;1985372
+02.09.2014 12:43:02,575;5065406;1985372
+02.09.2014 12:43:03,092;5065281;1985372
+02.09.2014 12:43:03,607;5065400;1985372
+02.09.2014 12:43:04,130;5065381;1985372
+02.09.2014 12:43:04,635;5065293;1985372
+02.09.2014 12:43:05,131;5065300;1985372
+02.09.2014 12:43:05,663;5065162;1985372
+02.09.2014 12:43:06,157;5065206;1985372
+02.09.2014 12:43:06,661;5065168;1985372
+02.09.2014 12:43:07,167;5065093;1985372
+02.09.2014 12:43:07,679;5065300;1985372
+02.09.2014 12:43:08,174;5065087;1985372
+02.09.2014 12:43:08,692;5065293;1985372
+02.09.2014 12:43:09,200;5065293;1985372
+02.09.2014 12:43:09,702;5065300;1985372
+02.09.2014 12:43:10,230;5065300;1985372
+02.09.2014 12:43:10,725;5065400;1985372
+02.09.2014 12:43:11,222;5065193;1985372
+02.09.2014 12:43:11,735;5065381;1985372
+02.09.2014 12:43:12,235;5065500;1985372
+02.09.2014 12:43:12,751;5065300;1985372
+02.09.2014 12:43:13,254;5065300;1985372
+02.09.2014 12:43:13,768;5065400;1985372
+02.09.2014 12:43:14,263;5065400;1985372
+02.09.2014 12:43:14,776;5065500;1985372
+02.09.2014 12:43:15,270;5065200;1985372
+02.09.2014 12:43:15,801;5065312;1985372
+02.09.2014 12:43:16,298;5065300;1985372
+02.09.2014 12:43:16,811;5065287;1985372
+02.09.2014 12:43:17,329;5065400;1985372
+02.09.2014 12:43:17,825;5065406;1985372
+02.09.2014 12:43:18,339;5065175;1985372
+02.09.2014 12:43:18,833;5065187;1985372
+02.09.2014 12:43:19,348;5065531;1985372
+02.09.2014 12:43:19,841;5065525;1985372
+02.09.2014 12:43:20,372;5065300;1985372
+02.09.2014 12:43:20,873;5065425;1985372
+02.09.2014 12:43:21,392;5065431;1985372
+02.09.2014 12:43:21,905;5065400;1985372
+02.09.2014 12:43:22,423;5065381;1985372
+02.09.2014 12:43:22,935;5065293;1985372
+02.09.2014 12:43:23,450;5065400;1985372
+02.09.2014 12:43:23,950;5065412;1985372
+02.09.2014 12:43:24,459;5065300;1985372
+02.09.2014 12:43:24,962;5065400;1985372
+02.09.2014 12:43:25,469;5065200;1985372
+02.09.2014 12:43:25,970;5065312;1985372
+02.09.2014 12:43:26,490;5065406;1985372
+02.09.2014 12:43:26,996;5065287;1985372
+02.09.2014 12:43:27,491;5065275;1985372
+02.09.2014 12:43:28,005;5065287;1985372
+02.09.2014 12:43:28,521;5065400;1985372
+02.09.2014 12:43:29,021;5065512;1985372
+02.09.2014 12:43:29,521;5065418;1985372
+02.09.2014 12:43:30,033;5065275;1985372
+02.09.2014 12:43:30,540;5065275;1985372
+02.09.2014 12:43:31,042;5065187;1985372
+02.09.2014 12:43:31,558;5065281;1985372
+02.09.2014 12:43:32,070;5065443;1985372
+02.09.2014 12:43:32,565;5065275;1985372
+02.09.2014 12:43:33,077;5065400;1985372
+02.09.2014 12:43:33,590;5065406;1985372
+02.09.2014 12:43:34,139;5065293;1985372
+02.09.2014 12:43:34,599;5065500;1985372
+02.09.2014 12:43:35,148;5065400;1985372
+02.09.2014 12:43:35,607;5065400;1985372
+02.09.2014 12:43:36,119;5065406;1985372
+02.09.2014 12:43:36,620;5065400;1985372
+02.09.2014 12:43:37,149;5065300;1985372
+02.09.2014 12:43:37,640;5065287;1985372
+02.09.2014 12:43:38,155;5065287;1985372
+02.09.2014 12:43:38,655;5065293;1985372
+02.09.2014 12:43:39,168;5065300;1985372
+02.09.2014 12:43:39,664;5065431;1985372
+02.09.2014 12:43:40,177;5065393;1985372
+02.09.2014 12:43:40,673;5065406;1985372
+02.09.2014 12:43:41,186;5065268;1985372
+02.09.2014 12:43:41,683;5065518;1985372
+02.09.2014 12:43:42,201;5065206;1985372
+02.09.2014 12:43:42,714;5065150;1985372
+02.09.2014 12:43:43,232;5065293;1985372
+02.09.2014 12:43:43,745;5065250;1985372
+02.09.2014 12:43:44,264;5065268;1985372
+02.09.2014 12:43:44,780;5065400;1985372
+02.09.2014 12:43:45,283;5065418;1985372
+02.09.2014 12:43:45,800;5065300;1985372
+02.09.2014 12:43:46,295;5065406;1985372
+02.09.2014 12:43:46,809;5065300;1985372
+02.09.2014 12:43:47,305;5065193;1985372
+02.09.2014 12:43:47,818;5065300;1985372
+02.09.2014 12:43:48,336;5065293;1985372
+02.09.2014 12:43:48,832;5065387;1985372
+02.09.2014 12:43:49,349;5065500;1985372
+02.09.2014 12:43:49,841;5065143;1985372
+02.09.2014 12:43:50,361;5065443;1985372
+02.09.2014 12:43:50,868;5065362;1985372
+02.09.2014 12:43:51,370;5065400;1985372
+02.09.2014 12:43:51,878;5065300;1985372
+02.09.2014 12:43:52,381;5065400;1985372
+02.09.2014 12:43:52,886;5065400;1985372
+02.09.2014 12:43:53,406;5065400;1985372
+02.09.2014 12:43:53,903;5065187;1985372
+02.09.2014 12:43:54,416;5065400;1985372
+02.09.2014 12:43:54,911;5065287;1985372
+02.09.2014 12:43:55,430;5065300;1985372
+02.09.2014 12:43:55,937;5065375;1985372
+02.09.2014 12:43:56,452;5065381;1985372
+02.09.2014 12:43:56,945;5065406;1985372
+02.09.2014 12:43:57,460;5065281;1985372
+02.09.2014 12:43:57,957;5065300;1985372
+02.09.2014 12:43:58,475;5065400;1985372
+02.09.2014 12:43:58,970;5065300;1985372
+02.09.2014 12:43:59,484;5065393;1985372
+02.09.2014 12:43:59,982;5065393;1985372
+02.09.2014 12:44:00,512;5065200;1985372
+02.09.2014 12:44:01,009;5065400;1985372
+02.09.2014 12:44:01,516;5065387;1985372
+02.09.2014 12:44:02,011;5065300;1985372
+02.09.2014 12:44:02,526;5065406;1985372
+02.09.2014 12:44:03,027;5065400;1985372
+02.09.2014 12:44:03,552;5065300;1985372
+02.09.2014 12:44:04,049;5065293;1985372
+02.09.2014 12:44:04,561;5065268;1985372
+02.09.2014 12:44:05,056;5065412;1985372
+02.09.2014 12:44:05,576;5065400;1985372
+02.09.2014 12:44:06,075;5065525;1985372
+02.09.2014 12:44:06,585;5065281;1985372
+02.09.2014 12:44:07,090;5065300;1985372
+02.09.2014 12:44:07,593;5065400;1985372
+02.09.2014 12:44:08,100;5065443;1985372
+02.09.2014 12:44:08,618;5065281;1985372
+02.09.2014 12:44:09,113;5065400;1985372
+02.09.2014 12:44:09,627;5065368;1985372
+02.09.2014 12:44:10,129;5065400;1985372
+02.09.2014 12:44:10,641;5065181;1985372
+02.09.2014 12:44:11,151;5065381;1985372
+02.09.2014 12:44:11,651;5065393;1985372
+02.09.2014 12:44:12,161;5065293;1985372
+02.09.2014 12:44:12,662;5065275;1985372
+02.09.2014 12:44:13,169;5065500;1985372
+02.09.2014 12:44:13,689;5065418;1985372
+02.09.2014 12:44:14,184;5065400;1985372
+02.09.2014 12:44:14,704;5065281;1985372
+02.09.2014 12:44:15,193;5065275;1985372
+02.09.2014 12:44:15,712;5065193;1985372
+02.09.2014 12:44:16,221;5065387;1985372
+02.09.2014 12:44:16,724;5065500;1985372
+02.09.2014 12:44:17,220;5065393;1985372
+02.09.2014 12:44:17,737;5065275;1985372
+02.09.2014 12:44:18,233;5065400;1985372
+02.09.2014 12:44:18,765;5065393;1985372
+02.09.2014 12:44:19,261;5065300;1985372
+02.09.2014 12:44:19,762;5065400;1985372
+02.09.2014 12:44:20,280;5065393;1985372
+02.09.2014 12:44:20,789;5065293;1985372
+02.09.2014 12:44:21,290;5065193;1985372
+02.09.2014 12:44:21,804;5065400;1985372
+02.09.2014 12:44:22,317;5065400;1985372
+02.09.2014 12:44:22,808;5065268;1985372
+02.09.2014 12:44:23,326;5065368;1985372
+02.09.2014 12:44:23,822;5065387;1985372
+02.09.2014 12:44:24,334;5065293;1985372
+02.09.2014 12:44:24,831;5065500;1985372
+02.09.2014 12:44:25,349;5065300;1985372
+02.09.2014 12:44:25,864;5065400;1985372
+02.09.2014 12:44:26,383;5065412;1985372
+02.09.2014 12:44:26,894;5065400;1985372
+02.09.2014 12:44:27,412;5065281;1985372
+02.09.2014 12:44:27,925;5065518;1985372
+02.09.2014 12:44:28,421;5065393;1985372
+02.09.2014 12:44:28,951;5065156;1985372
+02.09.2014 12:44:29,429;5065368;1985372
+02.09.2014 12:44:29,960;5065393;1985372
+02.09.2014 12:44:30,456;5065300;1985372
+02.09.2014 12:44:30,969;5065493;1985372
+02.09.2014 12:44:31,465;5065418;1985372
+02.09.2014 12:44:31,982;5065300;1985372
+02.09.2014 12:44:32,490;5065387;1985372
+02.09.2014 12:44:32,995;5065293;1985372
+02.09.2014 12:44:33,491;5065400;1985372
+02.09.2014 12:44:34,013;5065400;1985372
+02.09.2014 12:44:34,505;5065400;1985372
+02.09.2014 12:44:35,022;5065400;1985372
+02.09.2014 12:44:35,531;5065525;1985372
+02.09.2014 12:44:36,031;5065618;1985372
+02.09.2014 12:44:36,544;5065243;1985372
+02.09.2014 12:44:37,056;5065512;1985372
+02.09.2014 12:44:37,554;5065300;1985372
+02.09.2014 12:44:38,066;5065256;1985372
+02.09.2014 12:44:38,563;5065306;1985372
+02.09.2014 12:44:39,081;5065400;1985372
+02.09.2014 12:44:39,589;5065412;1985372
+02.09.2014 12:44:40,090;5065275;1985372
+02.09.2014 12:44:40,599;5065181;1985372
+02.09.2014 12:44:41,112;5065300;1985372
+02.09.2014 12:44:41,607;5065400;1985372
+02.09.2014 12:44:42,126;5065406;1985372
+02.09.2014 12:44:42,621;5065400;1985372
+02.09.2014 12:44:43,133;5065400;1985372
+02.09.2014 12:44:43,634;5065512;1985372
+02.09.2014 12:44:44,161;5065406;1985372
+02.09.2014 12:44:44,659;5065306;1985372
+02.09.2014 12:44:45,204;5065281;1985372
+02.09.2014 12:44:45,665;5065293;1985372
+02.09.2014 12:44:46,216;5065400;1985372
+02.09.2014 12:44:46,674;5065156;1985372
+02.09.2014 12:44:47,239;5065375;1985372
+02.09.2014 12:44:47,700;5065512;1985372
+02.09.2014 12:44:48,201;5065400;1985372
+02.09.2014 12:44:48,710;5065268;1985372
+02.09.2014 12:44:49,234;5065300;1985372
+02.09.2014 12:44:49,726;5065456;1985372
+02.09.2014 12:44:50,231;5065412;1985372
+02.09.2014 12:44:50,744;5065281;1985372
+02.09.2014 12:44:51,239;5065618;1985372
+02.09.2014 12:44:51,771;5065293;1985372
+02.09.2014 12:44:52,269;5065393;1985372
+02.09.2014 12:44:52,780;5065318;1985372
+02.09.2014 12:44:53,274;5065400;1985372
+02.09.2014 12:44:53,793;5065293;1985372
+02.09.2014 12:44:54,300;5065406;1985372
+02.09.2014 12:44:54,802;5065181;1985372
+02.09.2014 12:44:55,314;5065400;1985372
+02.09.2014 12:44:55,811;5065506;1985372
+02.09.2014 12:44:56,319;5065400;1985372
+02.09.2014 12:44:56,837;5065525;1985372
+02.09.2014 12:44:57,333;5065400;1985372
+02.09.2014 12:44:57,845;5065637;1985372
+02.09.2014 12:44:58,341;5065418;1985372
+02.09.2014 12:44:58,859;5065518;1985372
+02.09.2014 12:44:59,367;5065393;1985372
+02.09.2014 12:44:59,879;5065512;1985354
+02.09.2014 12:45:00,372;5114856;1993808
+02.09.2014 12:45:00,882;5280062;2056319
+02.09.2014 12:45:01,389;5559987;2128686
+02.09.2014 12:45:01,905;5919456;2200988
+02.09.2014 12:45:02,400;6360906;2273226
+02.09.2014 12:45:02,910;6839625;2339796
+02.09.2014 12:45:03,424;7321600;2411901
+02.09.2014 12:45:03,933;7765987;2484040
+02.09.2014 12:45:04,439;8245006;2556215
+02.09.2014 12:45:04,951;8726431;2622779
+02.09.2014 12:45:05,449;9169375;2694866
+02.09.2014 12:45:05,950;9612587;2761093
+02.09.2014 12:45:06,459;10092481;2832813
+02.09.2014 12:45:06,975;10571887;2904575
+02.09.2014 12:45:07,471;11015618;2970750
+02.09.2014 12:45:07,981;11459056;3042436
+02.09.2014 12:45:08,487;11937000;3114058
+02.09.2014 12:45:09,003;12381075;3185406
+02.09.2014 12:45:09,514;12862725;3256744
+02.09.2014 12:45:10,019;13341250;3322337
+02.09.2014 12:45:10,516;13784775;3393406
+02.09.2014 12:45:11,026;14265100;3459093
+02.09.2014 12:45:11,522;14708012;3529994
+02.09.2014 12:45:12,050;15188431;3600691
+02.09.2014 12:45:12,543;15630656;3665941
+02.09.2014 12:45:13,055;16111375;3736505
+02.09.2014 12:45:13,561;16554893;3806901
+02.09.2014 12:45:14,078;17032912;3877180
+02.09.2014 12:45:14,574;17514231;3942058
+02.09.2014 12:45:15,084;17957500;4011906
+02.09.2014 12:45:15,590;18436468;4081505
+02.09.2014 12:45:16,099;18880200;4150715
+02.09.2014 12:45:16,595;19323006;4214372
+02.09.2014 12:45:17,109;19767518;4277604
+02.09.2014 12:45:17,616;20092112;4359581
+02.09.2014 12:45:18,127;20289981;4463720
+02.09.2014 12:45:18,628;20393737;4490186
+02.09.2014 12:45:19,151;20398843;4490005
+02.09.2014 12:45:19,644;20401456;4489959
+02.09.2014 12:45:20,157;20400031;4489959
+02.09.2014 12:45:20,650;20398168;4489959
+02.09.2014 12:45:21,164;20399406;4489959
+02.09.2014 12:45:21,661;20399037;4489959
+02.09.2014 12:45:22,181;20398868;4489959
+02.09.2014 12:45:22,693;20399593;4489959
+02.09.2014 12:45:23,198;20399056;4489959
+02.09.2014 12:45:23,714;20399237;4489959
+02.09.2014 12:45:24,218;20399218;4489959
+02.09.2014 12:45:24,719;20399381;4489959
+02.09.2014 12:45:25,224;20399400;4489959
+02.09.2014 12:45:25,741;20399312;4489296
+02.09.2014 12:45:26,231;20399306;4505715
+02.09.2014 12:45:26,761;20399293;4528058
+02.09.2014 12:45:27,254;20399625;4532953
+02.09.2014 12:45:27,766;20399256;4554651
+02.09.2014 12:45:28,259;20399018;4606098
+02.09.2014 12:45:28,788;20399193;4610040
+02.09.2014 12:45:29,281;20399287;4610029
+02.09.2014 12:45:29,795;20399193;4610017
+02.09.2014 12:45:30,294;20399568;4610017
+02.09.2014 12:45:30,818;20399000;4610017
+02.09.2014 12:45:31,306;20399518;4610017
+02.09.2014 12:45:31,823;20399400;4610017
+02.09.2014 12:45:32,331;20399275;4610017
+02.09.2014 12:45:32,830;20399287;4610017
+02.09.2014 12:45:33,336;20399287;4610017
+02.09.2014 12:45:33,855;20399087;4610017
+02.09.2014 12:45:34,362;20399087;4610017
+02.09.2014 12:45:34,860;20399162;4610017
+02.09.2014 12:45:35,367;20399393;4610017
+02.09.2014 12:45:35,886;20399318;4610017
+02.09.2014 12:45:36,376;20399400;4610017
+02.09.2014 12:45:36,896;20399168;4610017
+02.09.2014 12:45:37,392;20398881;4610017
+02.09.2014 12:45:37,904;20398987;4610017
+02.09.2014 12:45:38,402;20399406;4610017
+02.09.2014 12:45:38,929;20399406;4610017
+02.09.2014 12:45:39,424;20384631;4607877
+02.09.2014 12:45:39,935;20268850;4550116
+02.09.2014 12:45:40,431;20037906;4419267
+02.09.2014 12:45:40,960;19692200;4286779
+02.09.2014 12:45:41,450;19274668;4164517
+02.09.2014 12:45:41,967;18793218;4022034
+02.09.2014 12:45:42,463;18346550;3900081
+02.09.2014 12:45:42,974;17868081;3767953
+02.09.2014 12:45:43,469;17424037;3646122
+02.09.2014 12:45:43,997;16943925;3514244
+02.09.2014 12:45:44,493;16501925;3392470
+02.09.2014 12:45:45,004;16020687;3250587
+02.09.2014 12:45:45,500;15577750;3129308
+02.09.2014 12:45:46,031;15061287;2997825
+02.09.2014 12:45:46,518;14654887;2876633
+02.09.2014 12:45:47,036;14174656;2745755
+02.09.2014 12:45:47,529;13735443;2601930
+02.09.2014 12:45:48,043;13352937;2361511
+02.09.2014 12:45:48,549;13086025;2182697
+02.09.2014 12:45:49,065;12934793;2111598
+02.09.2014 12:45:49,560;12896918;2109744
+02.09.2014 12:45:50,071;12898100;2109831
+02.09.2014 12:45:50,583;12898293;2110081
+02.09.2014 12:45:51,093;12898218;2110075
+02.09.2014 12:45:51,583;12899068;2110063
+02.09.2014 12:45:52,101;12899493;2110063
+02.09.2014 12:45:52,608;12899900;2110063
+02.09.2014 12:45:53,119;12899987;2110063
+02.09.2014 12:45:53,659;12900018;2110063
+02.09.2014 12:45:54,148;12900125;2110063
+02.09.2014 12:45:54,652;12899906;2110063
+02.09.2014 12:45:55,158;12899943;2110063
+02.09.2014 12:45:55,662;12900150;2110063
+02.09.2014 12:45:56,182;12900100;2110063
+02.09.2014 12:45:56,669;12900000;2110063
+02.09.2014 12:45:57,200;12900506;2110063
+02.09.2014 12:45:57,688;12900218;2110063
+02.09.2014 12:45:58,183;12900593;2110063
+02.09.2014 12:45:58,701;12900493;2110063
+02.09.2014 12:46:00,016;12900475;2110063
+02.09.2014 12:46:00,527;12900487;2110063
+02.09.2014 12:46:01,043;12900881;2109000
+02.09.2014 12:46:01,551;12901100;2068267
+02.09.2014 12:46:02,055;12900956;2042947
+02.09.2014 12:46:02,557;12900975;2036970
+02.09.2014 12:46:03,073;12901100;2031069
+02.09.2014 12:46:03,568;12901112;2000087
+02.09.2014 12:46:04,080;12900750;1999965
+02.09.2014 12:46:04,588;12900900;2000011
+02.09.2014 12:46:05,099;12901131;2000000
+02.09.2014 12:46:05,610;12900768;2000000
+02.09.2014 12:46:06,108;12900700;2000000
+02.09.2014 12:46:06,616;12900968;2000000
+02.09.2014 12:46:07,129;12900706;2000000
+02.09.2014 12:46:07,638;12900981;2000000
+02.09.2014 12:46:08,150;12900987;2000000
+02.09.2014 12:46:08,648;12900568;2000000
+02.09.2014 12:46:09,157;12901081;2000000
+02.09.2014 12:46:09,659;12901000;2000000
+02.09.2014 12:46:10,160;12901100;2000000
+02.09.2014 12:46:10,660;12900906;2000000
+02.09.2014 12:46:11,178;12901093;2000000
+02.09.2014 12:46:11,686;12900881;2000000
+02.09.2014 12:46:12,198;12900693;2000000
+02.09.2014 12:46:12,704;12901206;2000000
+02.09.2014 12:46:13,213;12901012;2000000
+02.09.2014 12:46:13,710;12900993;2000000
+02.09.2014 12:46:14,230;12901206;2000000
+02.09.2014 12:46:14,718;12900768;2000000
+02.09.2014 12:46:15,230;12900975;2000000
+02.09.2014 12:46:15,738;12901018;2000000
+02.09.2014 12:46:16,257;12900906;2000000
+02.09.2014 12:46:16,754;12900893;2000000
+02.09.2014 12:46:17,266;12900731;2000000
+02.09.2014 12:46:17,779;12901000;2000000
+02.09.2014 12:46:18,285;12900831;2000000
+02.09.2014 12:46:18,789;12900693;2000000
+02.09.2014 12:46:19,290;12900987;2000000
+02.09.2014 12:46:19,798;12901225;2000000
+02.09.2014 12:46:20,301;12901000;2000000
+02.09.2014 12:46:20,807;12900856;2000000
+02.09.2014 12:46:21,325;12900887;2000000
+02.09.2014 12:46:21,821;12900781;2000000
+02.09.2014 12:46:22,334;12900912;2000000
+02.09.2014 12:46:22,832;12900950;2000000
+02.09.2014 12:46:23,352;12901000;2000000
+02.09.2014 12:46:23,859;12901012;2000000
+02.09.2014 12:46:24,360;12900900;2000000
+02.09.2014 12:46:24,890;12901368;2000000
+02.09.2014 12:46:25,379;12901031;2000000
+02.09.2014 12:46:25,889;12901200;2000000
+02.09.2014 12:46:26,398;12901106;2000000
+02.09.2014 12:46:26,915;12900793;2000000
+02.09.2014 12:46:27,400;12900681;2000000
+02.09.2014 12:46:27,930;12900893;2000000
+02.09.2014 12:46:28,425;12900893;2000000
+02.09.2014 12:46:28,938;12901000;2000000
+02.09.2014 12:46:29,434;12900893;2000000
+02.09.2014 12:46:29,954;12901106;2000000
+02.09.2014 12:46:30,443;12900887;2000000
+02.09.2014 12:46:30,963;12901137;2000000
+02.09.2014 12:46:31,460;12901068;2000000
+02.09.2014 12:46:31,973;12900775;2000000
+02.09.2014 12:46:32,470;12900987;2000000
+02.09.2014 12:46:32,998;12901000;2000000
+02.09.2014 12:46:33,495;12901143;2000000
+02.09.2014 12:46:34,008;12901062;2000000
+02.09.2014 12:46:34,504;12901100;2000000
+02.09.2014 12:46:35,022;12901100;2000000
+02.09.2014 12:46:35,519;12901118;2000000
+02.09.2014 12:46:36,031;12900993;2000000
+02.09.2014 12:46:36,538;12901006;2000000
+02.09.2014 12:46:37,039;12900900;2000000
+02.09.2014 12:46:37,548;12901100;2000000
+02.09.2014 12:46:38,067;12901075;2000000
+02.09.2014 12:46:38,563;12900925;2000000
+02.09.2014 12:46:39,075;12901131;2000000
+02.09.2014 12:46:39,571;12900987;2000000
+02.09.2014 12:46:40,091;12900787;2000000
+02.09.2014 12:46:40,582;12901143;2000000
+02.09.2014 12:46:41,098;12900956;2000000
+02.09.2014 12:46:41,606;12900668;2000000
+02.09.2014 12:46:42,112;12900587;2000000
+02.09.2014 12:46:42,615;12901337;2000000
+02.09.2014 12:46:43,130;12901037;2000000
+02.09.2014 12:46:43,637;12901137;2000000
+02.09.2014 12:46:44,138;12900787;2000000
+02.09.2014 12:46:44,650;12900581;2000000
+02.09.2014 12:46:45,166;12901012;2000000
+02.09.2014 12:46:45,662;12900793;2000000
+02.09.2014 12:46:46,176;12901100;2000000
+02.09.2014 12:46:46,672;12901037;2000000
+02.09.2014 12:46:47,184;12900887;2000000
+02.09.2014 12:46:47,680;12900987;2000000
+02.09.2014 12:46:48,209;12901025;2000000
+02.09.2014 12:46:48,704;12901006;2000000
+02.09.2014 12:46:49,216;12901100;2000000
+02.09.2014 12:46:49,713;12900875;2000000
+02.09.2014 12:46:50,230;12900918;2000000
+02.09.2014 12:46:50,725;12901000;2000000
+02.09.2014 12:46:51,239;12901162;2000000
+02.09.2014 12:46:51,749;12900900;2000000
+02.09.2014 12:46:52,252;12900993;2000000
+02.09.2014 12:46:52,759;12900906;2000000
+02.09.2014 12:46:53,276;12900981;2000000
+02.09.2014 12:46:53,773;12901037;2000000
+02.09.2014 12:46:54,285;12900656;2000000
+02.09.2014 12:46:54,782;12901006;2000000
+02.09.2014 12:46:55,300;12900875;2000000
+02.09.2014 12:46:55,812;12900900;2000000
+02.09.2014 12:46:56,320;12901093;2000000
+02.09.2014 12:46:56,823;12901112;2000000
+02.09.2014 12:46:57,335;12900993;2000000
+02.09.2014 12:46:57,850;12901100;2000000
+02.09.2014 12:46:58,349;12900906;2000000
+02.09.2014 12:46:58,854;12901318;2000000
+02.09.2014 12:46:59,369;12901143;2000000
+02.09.2014 12:46:59,880;12900912;2000000
+02.09.2014 12:47:00,377;12901056;2000000
+02.09.2014 12:47:00,889;12900993;2000000
+02.09.2014 12:47:01,387;12900850;2000000
+02.09.2014 12:47:01,923;12900737;2000000
+02.09.2014 12:47:02,395;12900768;2000000
+02.09.2014 12:47:02,912;12900768;2000000
+02.09.2014 12:47:03,418;12901225;2000000
+02.09.2014 12:47:03,957;12900893;2000000
+02.09.2014 12:47:04,428;12901093;2000000
+02.09.2014 12:47:04,983;12900837;2000000
+02.09.2014 12:47:05,442;12900906;2000000
+02.09.2014 12:47:05,990;12900893;2000000
+02.09.2014 12:47:06,452;12901131;2000000
+02.09.2014 12:47:07,004;12900906;2000000
+02.09.2014 12:47:07,477;12900812;2000000
+02.09.2014 12:47:07,978;12901187;2000000
+02.09.2014 12:47:08,488;12900881;2000000
+02.09.2014 12:47:08,999;12901043;2000000
+02.09.2014 12:47:09,502;12900787;2000000
+02.09.2014 12:47:10,017;12900637;2000000
+02.09.2014 12:47:10,512;12901218;2000000
+02.09.2014 12:47:11,024;12900987;2000000
+02.09.2014 12:47:11,521;12900900;2000000
+02.09.2014 12:47:12,037;12900693;2000000
+02.09.2014 12:47:12,530;12901162;2000000
+02.09.2014 12:47:13,060;12900793;2000000
+02.09.2014 12:47:13,555;12900843;2000000
+02.09.2014 12:47:14,058;12901018;2000000
+02.09.2014 12:47:14,568;12901087;2000000
+02.09.2014 12:47:15,078;12901000;2000000
+02.09.2014 12:47:15,586;12900800;2000000
+02.09.2014 12:47:16,098;12900887;2000000
+02.09.2014 12:47:16,598;12900968;2000000
+02.09.2014 12:47:17,107;12900768;2000000
+02.09.2014 12:47:17,608;12901100;2000000
+02.09.2014 12:47:18,120;12900881;2000000
+02.09.2014 12:47:18,628;12900718;2000000
+02.09.2014 12:47:19,130;12901318;2000000
+02.09.2014 12:47:19,648;12901181;2000000
+02.09.2014 12:47:20,156;12900693;2000000
+02.09.2014 12:47:20,653;12900993;2000000
+02.09.2014 12:47:21,165;12901118;2000000
+02.09.2014 12:47:21,662;12900781;2000000
+02.09.2014 12:47:22,174;12901287;2000000
+02.09.2014 12:47:22,670;12900987;2000000
+02.09.2014 12:47:23,198;12900900;2000000
+02.09.2014 12:47:23,694;12900793;2000000
+02.09.2014 12:47:24,207;12900787;2000000
+02.09.2014 12:47:24,703;12900793;2000000
+02.09.2014 12:47:25,221;12900543;2000000
+02.09.2014 12:47:25,729;12901012;2000000
+02.09.2014 12:47:26,229;12901006;2000000
+02.09.2014 12:47:26,751;12901106;2000000
+02.09.2014 12:47:27,238;12900887;2000000
+02.09.2014 12:47:27,759;12901100;2000000
+02.09.2014 12:47:28,266;12900893;2000000
+02.09.2014 12:47:28,768;12901000;2000000
+02.09.2014 12:47:29,275;12901137;2000000
+02.09.2014 12:47:29,794;12901287;2000000
+02.09.2014 12:47:30,292;12901000;2000000
+02.09.2014 12:47:30,807;12901506;2000000
+02.09.2014 12:47:31,310;12901212;2000000
+02.09.2014 12:47:31,824;12900900;2000000
+02.09.2014 12:47:32,318;12901093;2000000
+02.09.2014 12:47:32,832;12900918;2000000
+02.09.2014 12:47:33,328;12901100;2000000
+02.09.2014 12:47:33,841;12900943;2000000
+02.09.2014 12:47:34,349;12900556;2000000
+02.09.2014 12:47:34,867;12900831;2000000
+02.09.2014 12:47:35,364;12901225;2000000
+02.09.2014 12:47:35,877;12901106;2000000
+02.09.2014 12:47:36,374;12900481;2000000
+02.09.2014 12:47:36,893;12901000;2000000
+02.09.2014 12:47:37,384;12901200;2000000
+02.09.2014 12:47:37,900;12900875;2000000
+02.09.2014 12:47:38,406;12901450;2000000
+02.09.2014 12:47:38,908;12901112;2000000
+02.09.2014 12:47:39,420;12900431;2000000
+02.09.2014 12:47:39,933;12900900;2000000
+02.09.2014 12:47:40,428;12900868;2000000
+02.09.2014 12:47:40,943;12900800;2000000
+02.09.2014 12:47:41,450;12901118;2000000
+02.09.2014 12:47:41,968;12900850;2000000
+02.09.2014 12:47:42,459;12900900;2000000
+02.09.2014 12:47:42,976;12901106;2000000
+02.09.2014 12:47:43,473;12901206;2000000
+02.09.2014 12:47:43,985;12900837;2000000
+02.09.2014 12:47:44,481;12901100;2000000
+02.09.2014 12:47:45,011;12901025;2000000
+02.09.2014 12:47:45,507;12901000;2000000
+02.09.2014 12:47:46,017;12900925;2000000
+02.09.2014 12:47:46,508;12900900;2000000
+02.09.2014 12:47:47,041;12901037;2000000
+02.09.2014 12:47:47,521;12900787;2000000
+02.09.2014 12:47:48,038;12900806;2000000
+02.09.2014 12:47:48,548;12901106;2000000
+02.09.2014 12:47:49,060;12900912;2000000
+02.09.2014 12:47:49,560;12900925;2000000
+02.09.2014 12:47:50,073;12900831;2000000
+02.09.2014 12:47:50,571;12901000;2000000
+02.09.2014 12:47:51,094;12901237;2000000
+02.09.2014 12:47:51,581;12900893;2000000
+02.09.2014 12:47:52,099;12901118;2000000
+02.09.2014 12:47:52,590;12901000;2000000
+02.09.2014 12:47:53,118;12901100;2000000
+02.09.2014 12:47:53,615;12901100;2000000
+02.09.2014 12:47:54,127;12900900;2000000
+02.09.2014 12:47:54,624;12900900;2000000
+02.09.2014 12:47:55,142;12900562;2000000
+02.09.2014 12:47:55,638;12901200;2000000
+02.09.2014 12:47:56,152;12900793;2000000
+02.09.2014 12:47:56,663;12900906;2000000
+02.09.2014 12:47:57,177;12901093;2000000
+02.09.2014 12:47:57,668;12900975;2000000
+02.09.2014 12:47:58,187;12900750;2000000
+02.09.2014 12:47:58,690;12901218;2000000
+02.09.2014 12:47:59,202;12900481;2000000
+02.09.2014 12:47:59,715;12900656;2000000
+02.09.2014 12:48:00,212;12901100;2000000
+02.09.2014 12:48:00,724;12900793;2000000
+02.09.2014 12:48:01,220;12901037;2000000
+02.09.2014 12:48:01,750;12901100;2000000
+02.09.2014 12:48:02,246;12900537;2000000
+02.09.2014 12:48:02,752;12900793;2000000
+02.09.2014 12:48:03,259;12900900;2000000
+02.09.2014 12:48:03,766;12901075;2000000
+02.09.2014 12:48:04,278;12900887;2000000
+02.09.2014 12:48:04,780;12901012;2000000
+02.09.2014 12:48:05,287;12900875;2000000
+02.09.2014 12:48:05,790;12900675;2000000
+02.09.2014 12:48:06,301;12901087;2000000
+02.09.2014 12:48:06,815;12901225;2000000
+02.09.2014 12:48:07,311;12900868;2000000
+02.09.2014 12:48:07,823;12900800;2000000
+02.09.2014 12:48:08,319;12900218;2000000
+02.09.2014 12:48:08,834;12901350;2000000
+02.09.2014 12:48:09,347;12901412;2000000
+02.09.2014 12:48:09,859;12900887;2000000
+02.09.2014 12:48:10,356;12901125;2000000
+02.09.2014 12:48:10,868;12900893;2000000
+02.09.2014 12:48:11,370;12901118;2000000
+02.09.2014 12:48:11,924;12901006;2000000
+02.09.2014 12:48:12,379;12901306;2000000
+02.09.2014 12:48:12,892;12901025;2000000
+02.09.2014 12:48:13,399;12901006;2000000
+02.09.2014 12:48:13,900;12901125;2000000
+02.09.2014 12:48:14,425;12901006;2000000
+02.09.2014 12:48:14,925;12900931;2000000
+02.09.2014 12:48:15,449;12900893;2000000
+02.09.2014 12:48:15,933;12900587;2000000
+02.09.2014 12:48:16,447;12901112;2000000
+02.09.2014 12:48:16,960;12901225;2000000
+02.09.2014 12:48:17,457;12900962;2000000
+02.09.2014 12:48:17,957;12900812;2000000
+02.09.2014 12:48:18,465;12901206;2000000
+02.09.2014 12:48:18,971;12900818;2000000
+02.09.2014 12:48:19,487;12900787;2000000
+02.09.2014 12:48:19,999;12900887;2000000
+02.09.2014 12:48:20,502;12900775;2000000
+02.09.2014 12:48:21,007;12900993;2000000
+02.09.2014 12:48:21,509;12901200;2000000
+02.09.2014 12:48:22,022;12900987;2000000
+02.09.2014 12:48:22,518;12900793;2000000
+02.09.2014 12:48:23,030;12900818;2000000
+02.09.2014 12:48:23,537;12900818;2000000
+02.09.2014 12:48:24,038;12901000;2000000
+02.09.2014 12:48:24,553;12900881;2000000
+02.09.2014 12:48:25,066;12901112;2000000
+02.09.2014 12:48:25,563;12900881;2000000
+02.09.2014 12:48:26,075;12900737;2000000
+02.09.2014 12:48:26,588;12901106;2000000
+02.09.2014 12:48:27,099;12900806;2000000
+02.09.2014 12:48:27,596;12900900;2000000
+02.09.2014 12:48:28,107;12900643;2000000
+02.09.2014 12:48:28,603;12901100;2000000
+02.09.2014 12:48:29,117;12900781;2000000
+02.09.2014 12:48:29,635;12901125;2000000
+02.09.2014 12:48:30,130;12901200;2000000
+02.09.2014 12:48:30,644;12900900;2000000
+02.09.2014 12:48:31,138;12901106;2000000
+02.09.2014 12:48:31,658;12900681;2000000
+02.09.2014 12:48:32,164;12900881;2000000
+02.09.2014 12:48:32,678;12900706;2000000
+02.09.2014 12:48:33,175;12901100;2000000
+02.09.2014 12:48:33,693;12901000;2000000
+02.09.2014 12:48:34,183;12900881;2000000
+02.09.2014 12:48:34,703;12900956;2000000
+02.09.2014 12:48:35,201;12900906;2000000
+02.09.2014 12:48:35,717;12900893;2000000
+02.09.2014 12:48:36,212;12901125;2000000
+02.09.2014 12:48:36,729;12901025;2000000
+02.09.2014 12:48:37,239;12900968;2000000
+02.09.2014 12:48:37,739;12901106;2000000
+02.09.2014 12:48:38,248;12900875;2000000
+02.09.2014 12:48:38,767;12900900;2000000
+02.09.2014 12:48:39,256;12901200;2000000
+02.09.2014 12:48:39,774;12900681;2000000
+02.09.2014 12:48:40,269;12901212;2000000
+02.09.2014 12:48:40,784;12900900;2000000
+02.09.2014 12:48:41,290;12900875;2000000
+02.09.2014 12:48:41,807;12900825;2000000
+02.09.2014 12:48:42,302;12901212;2000000
+02.09.2014 12:48:42,817;12901100;2000000
+02.09.2014 12:48:43,314;12900787;2000000
+02.09.2014 12:48:43,830;12900750;2000000
+02.09.2014 12:48:44,321;12901212;2000000
+02.09.2014 12:48:44,839;12901212;2000000
+02.09.2014 12:48:45,348;12901212;2000000
+02.09.2014 12:48:45,850;12900900;2000000
+02.09.2014 12:48:46,357;12900568;2000000
+02.09.2014 12:48:46,874;12900906;2000000
+02.09.2014 12:48:47,373;12901012;2000000
+02.09.2014 12:48:47,885;12900906;2000000
+02.09.2014 12:48:48,381;12900900;2000000
+02.09.2014 12:48:48,899;12900587;2000000
+02.09.2014 12:48:49,397;12900693;2000000
+02.09.2014 12:48:49,918;12901025;2000000
+02.09.2014 12:48:50,415;12900681;2000000
+02.09.2014 12:48:50,919;12900875;2000000
+02.09.2014 12:48:51,428;12900987;2000000
+02.09.2014 12:48:51,937;12900887;2000000
+02.09.2014 12:48:52,447;12901000;2000000
+02.09.2014 12:48:52,960;12900987;2000000
+02.09.2014 12:48:53,456;12900562;2000000
+02.09.2014 12:48:53,973;12900568;2000000
+02.09.2014 12:48:54,464;12901000;2000000
+02.09.2014 12:48:54,982;12901000;2000000
+02.09.2014 12:48:55,478;12901056;2000000
+02.09.2014 12:48:55,991;12900662;2000000
+02.09.2014 12:48:56,498;12901000;2000000
+02.09.2014 12:48:57,016;12901225;2000000
+02.09.2014 12:48:57,513;12901000;2000000
+02.09.2014 12:48:58,031;12901106;2000000
+02.09.2014 12:48:58,522;12900793;2000000
+02.09.2014 12:48:59,042;12900900;2000000
+02.09.2014 12:48:59,530;12900706;2000000
+02.09.2014 12:49:00,050;12901125;2000000
+02.09.2014 12:49:00,563;12901131;2000000
+02.09.2014 12:49:01,058;12901200;2000000
+02.09.2014 12:49:01,588;12900956;2000000
+02.09.2014 12:49:02,094;12901168;2000000
+02.09.2014 12:49:02,617;12900743;2000000
+02.09.2014 12:49:03,119;12900900;2000000
+02.09.2014 12:49:03,637;12900987;2000000
+02.09.2014 12:49:04,151;12900943;2000000
+02.09.2014 12:49:04,663;12901000;2000000
+02.09.2014 12:49:05,175;12900668;2000000
+02.09.2014 12:49:05,677;12901037;2000000
+02.09.2014 12:49:06,184;12901225;2000000
+02.09.2014 12:49:06,680;12900900;2000000
+02.09.2014 12:49:07,197;12900981;2000000
+02.09.2014 12:49:07,699;12900900;2000000
+02.09.2014 12:49:08,215;12900993;2000000
+02.09.2014 12:49:08,711;12900681;2000000
+02.09.2014 12:49:09,223;12901100;2000000
+02.09.2014 12:49:09,737;12901200;2000000
+02.09.2014 12:49:10,248;12901218;2000000
+02.09.2014 12:49:10,744;12901006;2000000
+02.09.2014 12:49:11,258;12900787;2000000
+02.09.2014 12:49:11,754;12900856;2000000
+02.09.2014 12:49:12,267;12901018;2000000
+02.09.2014 12:49:12,774;12900793;2000000
+02.09.2014 12:49:13,279;12900825;2000000
+02.09.2014 12:49:13,787;12900875;2000000
+02.09.2014 12:49:14,294;12900893;2000000
+02.09.2014 12:49:14,807;12900818;2000000
+02.09.2014 12:49:15,312;12900787;2000000
+02.09.2014 12:49:15,808;12901006;2000000
+02.09.2014 12:49:16,329;12901075;2000000
+02.09.2014 12:49:16,827;12900787;2000000
+02.09.2014 12:49:17,329;12901118;2000000
+02.09.2014 12:49:17,843;12900900;2000000
+02.09.2014 12:49:18,356;12901006;2000000
+02.09.2014 12:49:18,851;12901037;2000000
+02.09.2014 12:49:19,367;12901000;2000000
+02.09.2014 12:49:19,867;12900987;2000000
+02.09.2014 12:49:20,380;12900887;2000000
+02.09.2014 12:49:20,897;12901218;2000000
+02.09.2014 12:49:21,398;12901212;2000000
+02.09.2014 12:49:21,913;12900650;2000000
+02.09.2014 12:49:22,398;12900893;2000000
+02.09.2014 12:49:22,917;12900937;2000000
+02.09.2014 12:49:23,427;12900787;2000000
+02.09.2014 12:49:23,940;12900900;2000000
+02.09.2014 12:49:24,429;12901106;2000000
+02.09.2014 12:49:24,961;12900993;2000000
+02.09.2014 12:49:25,455;12901112;2000000
+02.09.2014 12:49:25,986;12900887;2000000
+02.09.2014 12:49:26,464;12901212;2000000
+02.09.2014 12:49:26,977;12900800;2000000
+02.09.2014 12:49:27,473;12900875;2000000
+02.09.2014 12:49:27,967;12900918;2000000
+02.09.2014 12:49:28,501;12900706;2000000
+02.09.2014 12:49:28,992;12900925;2000000
+02.09.2014 12:49:29,505;12900587;2000000
+02.09.2014 12:49:30,000;12900875;2000000
+02.09.2014 12:49:30,518;12900793;2000000
+02.09.2014 12:49:31,030;12900962;2000000
+02.09.2014 12:49:31,527;12901006;2000000
+02.09.2014 12:49:32,057;12901206;2000000
+02.09.2014 12:49:32,553;12900900;2000000
+02.09.2014 12:49:33,065;12901256;2000000
+02.09.2014 12:49:33,562;12900993;2000000
+02.09.2014 12:49:34,074;12901206;2000000
+02.09.2014 12:49:34,588;12900762;2000000
+02.09.2014 12:49:35,096;12901087;2000000
+02.09.2014 12:49:35,596;12900662;2000000
+02.09.2014 12:49:36,098;12901006;2000000
+02.09.2014 12:49:36,606;12900668;2000000
+02.09.2014 12:49:37,124;12901237;2000000
+02.09.2014 12:49:37,615;12901000;2000000
+02.09.2014 12:49:38,133;12900800;2000000
+02.09.2014 12:49:38,628;12901200;2000000
+02.09.2014 12:49:39,146;12900562;2000000
+02.09.2014 12:49:39,644;12901000;2000000
+02.09.2014 12:49:40,163;12901200;2000000
+02.09.2014 12:49:40,661;12900993;2000000
+02.09.2014 12:49:41,174;12900993;2000000
+02.09.2014 12:49:41,672;12900675;2000000
+02.09.2014 12:49:42,192;12900781;2000000
+02.09.2014 12:49:42,687;12901162;2000000
+02.09.2014 12:49:43,204;12901106;2000000
+02.09.2014 12:49:43,707;12900756;2000000
+02.09.2014 12:49:44,211;12900856;2000000
+02.09.2014 12:49:44,715;12901137;2000000
+02.09.2014 12:49:45,232;12901106;2000000
+02.09.2014 12:49:45,729;12901362;2000000
+02.09.2014 12:49:46,244;12900693;2000000
+02.09.2014 12:49:46,738;12901206;2000000
+02.09.2014 12:49:47,256;12900781;2000000
+02.09.2014 12:49:47,749;12900912;2000000
+02.09.2014 12:49:48,277;12901100;2000000
+02.09.2014 12:49:48,781;12900750;2000000
+02.09.2014 12:49:49,286;12901000;2000000
+02.09.2014 12:49:49,782;12900993;2000000
+02.09.2014 12:49:50,299;12901418;2000000
+02.09.2014 12:49:50,806;12900975;2000000
+02.09.2014 12:49:51,308;12901318;2000000
+02.09.2014 12:49:51,817;12901093;2000000
+02.09.2014 12:49:52,335;12900893;2000000
+02.09.2014 12:49:52,826;12901262;2000000
+02.09.2014 12:49:53,345;12900987;2000000
+02.09.2014 12:49:53,839;12900887;2000000
+02.09.2014 12:49:54,353;12901025;2000000
+02.09.2014 12:49:54,865;12901243;2000000
+02.09.2014 12:49:55,368;12901350;2000000
+02.09.2014 12:49:55,868;12901225;2000000
+02.09.2014 12:49:56,392;12901200;2000000
+02.09.2014 12:49:56,881;12900918;2000000
+02.09.2014 12:49:57,399;12901100;2000000
+02.09.2014 12:49:57,902;12901025;2000000
+02.09.2014 12:49:58,407;12900656;2000000
+02.09.2014 12:49:58,915;12900775;2000000
+02.09.2014 12:49:59,426;12900787;2000000
+02.09.2014 12:49:59,923;12900887;2000000
+02.09.2014 12:50:00,443;12900906;2000000
+02.09.2014 12:50:00,956;12901006;2000000
+02.09.2014 12:50:01,474;12901312;2000000
+02.09.2014 12:50:01,987;12901131;2000000
+02.09.2014 12:50:02,506;12901312;2000000
+02.09.2014 12:50:03,017;12901000;2000000
+02.09.2014 12:50:03,539;12901206;2000000
+02.09.2014 12:50:04,032;12900900;2000000
+02.09.2014 12:50:04,546;12900962;2000000
+02.09.2014 12:50:05,061;12901106;2000000
+02.09.2014 12:50:05,556;12900681;2000000
+02.09.2014 12:50:06,057;12900900;2000000
+02.09.2014 12:50:06,571;12900800;2000000
+02.09.2014 12:50:07,067;12900762;2000000
+02.09.2014 12:50:07,582;12901100;2000000
+02.09.2014 12:50:08,093;12901206;2000000
+02.09.2014 12:50:08,605;12900681;2000000
+02.09.2014 12:50:09,100;12900881;2000000
+02.09.2014 12:50:09,614;12901100;2000000
+02.09.2014 12:50:10,110;12900662;2000000
+02.09.2014 12:50:10,622;12900781;2000000
+02.09.2014 12:50:11,121;12900800;2000000
+02.09.2014 12:50:11,643;12900862;2000000
+02.09.2014 12:50:12,137;12900743;2000000
+02.09.2014 12:50:12,655;12901143;2000000
+02.09.2014 12:50:13,168;12901125;2000000
+02.09.2014 12:50:13,670;12900993;2000000
+02.09.2014 12:50:14,176;12901025;2000000
+02.09.2014 12:50:14,677;12901187;2000000
+02.09.2014 12:50:15,186;12901250;2000000
+02.09.2014 12:50:15,687;12901118;2000000
+02.09.2014 12:50:16,211;12901337;2000000
+02.09.2014 12:50:16,714;12901075;2000000
+02.09.2014 12:50:17,226;12901000;2000000
+02.09.2014 12:50:17,746;12901200;2000000
+02.09.2014 12:50:18,258;12901181;2000000
+02.09.2014 12:50:18,775;12901125;2000000
+02.09.2014 12:50:19,288;12901075;2000000
+02.09.2014 12:50:19,791;12900687;2000000
+02.09.2014 12:50:20,302;12900993;2000000
+02.09.2014 12:50:20,799;12901000;2000000
+02.09.2014 12:50:21,311;12900668;2000000
+02.09.2014 12:50:21,807;12901006;2000000
+02.09.2014 12:50:22,319;12901200;2000000
+02.09.2014 12:50:22,827;12900987;2000000
+02.09.2014 12:50:23,346;12900987;2000000
+02.09.2014 12:50:23,843;12901106;2000000
+02.09.2014 12:50:24,354;12900793;2000000
+02.09.2014 12:50:24,850;12900993;2000000
+02.09.2014 12:50:25,368;12901106;2000000
+02.09.2014 12:50:25,876;12901200;2000000
+02.09.2014 12:50:26,388;12900775;2000000
+02.09.2014 12:50:26,885;12900906;2000000
+02.09.2014 12:50:27,396;12901212;2000000
+02.09.2014 12:50:27,902;12900800;2000000
+02.09.2014 12:50:28,414;12900543;2000000
+02.09.2014 12:50:28,915;12901006;2000000
+02.09.2014 12:50:29,416;12900781;2000000
+02.09.2014 12:50:29,928;12900987;2000000
+02.09.2014 12:50:30,468;12900887;2000000
+02.09.2014 12:50:30,940;12900881;2000000
+02.09.2014 12:50:31,453;12900675;2000000
+02.09.2014 12:50:31,949;12900800;2000000
+02.09.2014 12:50:32,472;12901018;2000000
+02.09.2014 12:50:32,957;12900968;2000000
+02.09.2014 12:50:33,497;12901000;2000000
+02.09.2014 12:50:33,988;12900893;2000000
+02.09.2014 12:50:34,508;12900800;2000000
+02.09.2014 12:50:35,014;12900837;2000000
+02.09.2014 12:50:35,522;12900793;2000000
+02.09.2014 12:50:36,024;12901025;2000000
+02.09.2014 12:50:36,520;12900587;2000000
+02.09.2014 12:50:37,037;12900912;2000000
+02.09.2014 12:50:37,546;12901125;2000000
+02.09.2014 12:50:38,048;12901200;2000000
+02.09.2014 12:50:38,557;12900656;2000000
+02.09.2014 12:50:39,058;12901100;2000000
+02.09.2014 12:50:39,572;12901112;2000000
+02.09.2014 12:50:40,085;12901206;2000000
+02.09.2014 12:50:40,581;12901100;2000000
+02.09.2014 12:50:41,103;12900893;2000000
+02.09.2014 12:50:41,590;12901081;2000000
+02.09.2014 12:50:42,106;12900893;2000000
+02.09.2014 12:50:42,613;12901006;2000000
+02.09.2014 12:50:43,118;12900787;2000000
+02.09.2014 12:50:43,624;12901125;2000000
+02.09.2014 12:50:44,138;12901062;2000000
+02.09.2014 12:50:44,627;12900800;2000000
+02.09.2014 12:50:45,147;12900893;2000000
+02.09.2014 12:50:45,656;12901131;2000000
+02.09.2014 12:50:46,158;12900856;2000000
+02.09.2014 12:50:46,665;12901000;2000000
+02.09.2014 12:50:47,184;12901025;2000000
+02.09.2014 12:50:47,680;12900787;2000000
+02.09.2014 12:50:48,193;12900987;2000000
+02.09.2014 12:50:48,688;12900925;2000000
+02.09.2014 12:50:49,200;12901218;2000000
+02.09.2014 12:50:49,714;12901000;2000000
+02.09.2014 12:50:50,216;12901062;2000000
+02.09.2014 12:50:50,723;12901025;2000000
+02.09.2014 12:50:51,237;12900993;2000000
+02.09.2014 12:50:51,733;12901100;2000000
+02.09.2014 12:50:52,251;12901093;2000000
+02.09.2014 12:50:52,753;12901118;2000000
+02.09.2014 12:50:53,261;12901106;2000000
+02.09.2014 12:50:53,758;12901218;2000000
+02.09.2014 12:50:54,270;12900762;2000000
+02.09.2014 12:50:54,786;12901275;2000000
+02.09.2014 12:50:55,295;12901100;2000000
+02.09.2014 12:50:55,793;12901106;2000000
+02.09.2014 12:50:56,306;12900906;2000000
+02.09.2014 12:50:56,802;12900887;2000000
+02.09.2014 12:50:57,319;12900962;2000000
+02.09.2014 12:50:57,811;12900731;2000000
+02.09.2014 12:50:58,328;12900906;2000000
+02.09.2014 12:50:58,837;12900887;2000000
+02.09.2014 12:50:59,337;12900650;2000000
+02.09.2014 12:50:59,847;12901000;2000000
+02.09.2014 12:51:00,356;12901156;2000000
+02.09.2014 12:51:00,868;12901212;2000000
+02.09.2014 12:51:01,370;12900787;2000000
+02.09.2014 12:51:01,867;12900943;2000000
+02.09.2014 12:51:02,395;12901000;2000000
+02.09.2014 12:51:02,892;12901312;2000000
+02.09.2014 12:51:03,405;12900575;2000000
+02.09.2014 12:51:03,901;12900756;2000000
+02.09.2014 12:51:04,413;12900775;2000000
+02.09.2014 12:51:04,925;12901131;2000000
+02.09.2014 12:51:05,425;12901081;2000000
+02.09.2014 12:51:05,939;12900956;2000000
+02.09.2014 12:51:06,455;12901106;2000000
+02.09.2014 12:51:06,964;12901300;2000000
+02.09.2014 12:51:07,461;12901018;2000000
+02.09.2014 12:51:07,974;12901062;2000000
+02.09.2014 12:51:08,470;12900887;2000000
+02.09.2014 12:51:08,984;12901006;2000000
+02.09.2014 12:51:09,480;12901000;2000000
+02.09.2014 12:51:09,998;12900887;2000000
+02.09.2014 12:51:10,506;12900593;2000000
+02.09.2014 12:51:11,006;12901243;2000000
+02.09.2014 12:51:11,519;12900781;2000000
+02.09.2014 12:51:12,032;12900856;2000000
+02.09.2014 12:51:12,529;12901100;2000000
+02.09.2014 12:51:13,043;12901000;2000000
+02.09.2014 12:51:13,538;12900975;2000000
+02.09.2014 12:51:14,051;12901293;2000000
+02.09.2014 12:51:14,549;12901325;2000000
+02.09.2014 12:51:15,067;12901087;2000000
+02.09.2014 12:51:15,579;12901000;2000000
+02.09.2014 12:51:16,100;12901200;2000000
+02.09.2014 12:51:16,614;12900787;2000000
+02.09.2014 12:51:17,137;12901100;2000000
+02.09.2014 12:51:17,637;12901275;2000000
+02.09.2014 12:51:18,151;12901225;2000000
+02.09.2014 12:51:18,663;12900900;2000000
+02.09.2014 12:51:19,157;12901200;2000000
+02.09.2014 12:51:19,671;12901200;2000000
+02.09.2014 12:51:20,184;12900918;2000000
+02.09.2014 12:51:20,679;12900693;2000000
+02.09.2014 12:51:21,186;12900900;2000000
+02.09.2014 12:51:21,706;12901100;2000000
+02.09.2014 12:51:22,213;12900900;2000000
+02.09.2014 12:51:22,715;12901000;2000000
+02.09.2014 12:51:23,210;12900993;2000000
+02.09.2014 12:51:23,728;12900843;2000000
+02.09.2014 12:51:24,239;12900900;2000000
+02.09.2014 12:51:24,738;12900781;2000000
+02.09.2014 12:51:25,247;12900806;2000000
+02.09.2014 12:51:25,749;12900793;2000000
+02.09.2014 12:51:26,262;12900993;2000000
+02.09.2014 12:51:26,775;12901112;2000000
+02.09.2014 12:51:27,270;12900856;2000000
+02.09.2014 12:51:27,784;12901118;2000000
+02.09.2014 12:51:28,296;12900775;2000000
+02.09.2014 12:51:28,808;12900750;2000000
+02.09.2014 12:51:29,304;12901112;2000000
+02.09.2014 12:51:29,816;12900900;2000000
+02.09.2014 12:51:30,312;12901006;2000000
+02.09.2014 12:51:30,824;12901081;2000000
+02.09.2014 12:51:31,319;12901018;2000000
+02.09.2014 12:51:31,838;12900900;2000000
+02.09.2014 12:51:32,357;12901050;2000000
+02.09.2014 12:51:32,875;12900712;2000000
+02.09.2014 12:51:33,379;12901212;2000000
+02.09.2014 12:51:33,900;12901337;2000000
+02.09.2014 12:51:34,410;12901100;2000000
+02.09.2014 12:51:34,906;12901218;2000000
+02.09.2014 12:51:35,437;12901231;2000000
+02.09.2014 12:51:35,931;12901006;2000000
+02.09.2014 12:51:36,446;12900906;2000000
+02.09.2014 12:51:36,964;12900793;2000000
+02.09.2014 12:51:37,460;12900987;2000000
+02.09.2014 12:51:37,974;12900975;2000000
+02.09.2014 12:51:38,475;12900893;2000000
+02.09.2014 12:51:38,983;12900987;2000000
+02.09.2014 12:51:39,496;12901000;2000000
+02.09.2014 12:51:40,023;12900781;2000000
+02.09.2014 12:51:40,506;12900800;2000000
+02.09.2014 12:51:41,018;12901031;2000000
+02.09.2014 12:51:41,515;12901262;2000000
+02.09.2014 12:51:42,070;12901087;2000000
+02.09.2014 12:51:42,535;12900793;2000000
+02.09.2014 12:51:43,080;12900975;2000000
+02.09.2014 12:51:43,538;12901112;2000000
+02.09.2014 12:51:44,100;12900975;2000000
+02.09.2014 12:51:44,565;12901000;2000000
+02.09.2014 12:51:45,066;12901000;2000000
+02.09.2014 12:51:45,574;12901381;2000000
+02.09.2014 12:51:46,087;12900781;2000000
+02.09.2014 12:51:46,583;12900806;2000000
+02.09.2014 12:51:47,099;12901037;2000000
+02.09.2014 12:51:47,591;12900793;2000000
+02.09.2014 12:51:48,109;12900700;2000000
+02.09.2014 12:51:48,625;12901306;2000000
+02.09.2014 12:51:49,147;12900893;2000000
+02.09.2014 12:51:49,649;12901106;2000000
+02.09.2014 12:51:50,166;12901218;2000000
+02.09.2014 12:51:50,679;12900775;2000000
+02.09.2014 12:51:51,192;12900962;2000000
+02.09.2014 12:51:51,696;12900881;2000000
+02.09.2014 12:51:52,202;12900881;2000000
+02.09.2014 12:51:52,714;12900993;2000000
+02.09.2014 12:51:53,208;12901100;2000000
+02.09.2014 12:51:53,738;12901212;1999587
+02.09.2014 12:51:54,230;12950706;2012430
+02.09.2014 12:51:54,743;13099256;2114284
+02.09.2014 12:51:55,247;13370737;2246436
+02.09.2014 12:51:55,749;13723125;2367994
+02.09.2014 12:51:56,259;14161262;2499418
+02.09.2014 12:51:56,771;14639812;2630494
+02.09.2014 12:51:57,280;15122556;2751482
+02.09.2014 12:51:57,775;15565700;2882697
+02.09.2014 12:51:58,282;16046387;3003668
+02.09.2014 12:51:58,810;16526693;3145000
+02.09.2014 12:51:59,305;16968300;3265988
+02.09.2014 12:51:59,815;17449575;3396209
+02.09.2014 12:52:00,310;17893081;3516406
+02.09.2014 12:52:00,820;18334931;3646761
+02.09.2014 12:52:01,333;18815125;3777046
+02.09.2014 12:52:01,846;19296056;3908598
+02.09.2014 12:52:02,352;19745137;4108790
+02.09.2014 12:52:02,851;20055168;4325093
+02.09.2014 12:52:03,346;20267100;4453011
+02.09.2014 12:52:03,875;20386293;4490174
+02.09.2014 12:52:04,371;20400500;4490017
+02.09.2014 12:52:04,884;20401993;4489965
+02.09.2014 12:52:05,382;20399475;4489982
+02.09.2014 12:52:05,893;20399100;4489970
+02.09.2014 12:52:06,404;20399650;4489970
+02.09.2014 12:52:06,916;20399300;4489970
+02.09.2014 12:52:07,410;20399200;4489970
+02.09.2014 12:52:07,922;20399187;4489970
+02.09.2014 12:52:08,437;20399062;4489970
+02.09.2014 12:52:08,944;20399181;4489970
+02.09.2014 12:52:09,455;20399350;4489970
+02.09.2014 12:52:09,949;20399281;4489970
+02.09.2014 12:52:10,461;20399300;4489970
+02.09.2014 12:52:10,956;20399487;4489401
+02.09.2014 12:52:11,485;20399400;4511558
+02.09.2014 12:52:11,978;20399262;4528267
+02.09.2014 12:52:12,502;20399081;4532965
+02.09.2014 12:52:13,001;20399187;4556482
+02.09.2014 12:52:13,513;20399306;4608500
+02.09.2014 12:52:14,008;20399293;4610069
+02.09.2014 12:52:14,520;20399062;4610046
+02.09.2014 12:52:15,024;20399181;4610034
+02.09.2014 12:52:15,538;20399418;4610034
+02.09.2014 12:52:16,030;20399412;4610034
+02.09.2014 12:52:16,549;20399425;4610034
+02.09.2014 12:52:17,054;20399268;4610034
+02.09.2014 12:52:17,555;20399400;4610034
+02.09.2014 12:52:18,061;20399400;4610034
+02.09.2014 12:52:18,578;20399281;4610034
+02.09.2014 12:52:19,084;20399400;4610034
+02.09.2014 12:52:19,594;20399300;4610034
+02.09.2014 12:52:20,088;20398843;4610034
+02.09.2014 12:52:20,605;20399318;4610034
+02.09.2014 12:52:21,106;20399393;4610034
+02.09.2014 12:52:21,629;20399187;4610034
+02.09.2014 12:52:22,118;20399506;4610034
+02.09.2014 12:52:22,630;20399387;4610034
+02.09.2014 12:52:23,138;20399493;4610034
+02.09.2014 12:52:23,655;20399600;4610034
+02.09.2014 12:52:24,149;20399175;4610034
+02.09.2014 12:52:24,661;20367318;4605965
+02.09.2014 12:52:25,166;20215643;4526662
+02.09.2014 12:52:25,672;19975300;4403058
+02.09.2014 12:52:26,175;19604750;4269290
+02.09.2014 12:52:26,695;19170737;4135581
+02.09.2014 12:52:27,187;18728650;4012598
+02.09.2014 12:52:27,699;18245675;3879575
+02.09.2014 12:52:28,205;17767250;3746470
+02.09.2014 12:52:28,722;17286137;3613360
+02.09.2014 12:52:29,216;16843187;3490523
+02.09.2014 12:52:29,729;16362550;3357389
+02.09.2014 12:52:30,236;15919387;3224866
+02.09.2014 12:52:30,735;15476706;3102936
+02.09.2014 12:52:31,247;14997500;2970854
+02.09.2014 12:52:31,760;14515743;2838726
+02.09.2014 12:52:32,265;14073550;2715511
+02.09.2014 12:52:32,768;13610712;2515959
+02.09.2014 12:52:33,278;13287100;2274453
+02.09.2014 12:52:33,791;13044500;2134639
+02.09.2014 12:52:34,297;12923193;2099668
+02.09.2014 12:52:34,796;12897425;2099802
+02.09.2014 12:52:35,306;12898518;2099970
+02.09.2014 12:52:35,837;12898925;2100011
+02.09.2014 12:52:36,335;12898700;2100011
+02.09.2014 12:52:36,864;12899156;2100011
+02.09.2014 12:52:37,369;12899831;2100011
+02.09.2014 12:52:37,869;12899793;2100011
+02.09.2014 12:52:38,396;12899875;2100011
+02.09.2014 12:52:38,896;12900025;2100011
+02.09.2014 12:52:39,407;12899893;2100011
+02.09.2014 12:52:39,922;12899831;2100011
+02.09.2014 12:52:40,425;12899806;2100011
+02.09.2014 12:52:40,929;12900206;2100011
+02.09.2014 12:52:41,433;12900006;2100011
+02.09.2014 12:52:41,952;12899787;2100011
+02.09.2014 12:52:42,442;12900200;2098662
+02.09.2014 12:52:42,958;12900068;2074203
+02.09.2014 12:52:43,475;12900306;2034313
+02.09.2014 12:52:43,989;12900175;2028104
+02.09.2014 12:52:44,505;12899962;2023052
+02.09.2014 12:52:45,017;12899856;1990139
+02.09.2014 12:52:45,534;12900081;1990296
+02.09.2014 12:52:46,036;12899762;1990343
+02.09.2014 12:52:46,553;12899987;1990331
+02.09.2014 12:52:47,047;12900375;1990331
+02.09.2014 12:52:47,559;12900443;1990331
+02.09.2014 12:52:48,065;12900181;1990331
+02.09.2014 12:52:48,584;12900187;1990331
+02.09.2014 12:52:49,070;12899981;1990331
+02.09.2014 12:52:49,617;12900425;1990331
+02.09.2014 12:52:50,090;12899987;1990331
+02.09.2014 12:52:50,600;12899900;1990331
+02.09.2014 12:52:51,096;12900218;1990331
+02.09.2014 12:52:51,627;12900200;1990331
+02.09.2014 12:52:52,123;12900175;1990331
+02.09.2014 12:52:52,639;12900418;1990331
+02.09.2014 12:52:53,137;12900168;1990331
+02.09.2014 12:52:53,661;12900200;1990331
+02.09.2014 12:52:54,139;12900000;1990331
+02.09.2014 12:52:54,656;12900043;1990331
+02.09.2014 12:52:55,164;12899918;1990331
+02.09.2014 12:52:55,667;12900343;1990331
+02.09.2014 12:52:56,175;12900200;1990331
+02.09.2014 12:52:56,693;12900112;1990331
+02.09.2014 12:52:57,205;12900406;1990331
+02.09.2014 12:52:57,723;12900300;1990331
+02.09.2014 12:52:58,244;12900100;1990331
+02.09.2014 12:52:58,755;12900118;1990331
+02.09.2014 12:52:59,256;12900300;1990331
+02.09.2014 12:52:59,769;12900012;1990331
+02.09.2014 12:53:00,283;12900068;1990331
+02.09.2014 12:53:00,774;12899900;1990331
+02.09.2014 12:53:01,292;12900418;1990331
+02.09.2014 12:53:01,805;12900312;1990331
+02.09.2014 12:53:02,322;12899875;1990331
+02.09.2014 12:53:02,837;12900193;1990331
+02.09.2014 12:53:03,354;12900531;1990331
+02.09.2014 12:53:03,855;12900300;1990331
+02.09.2014 12:53:04,364;12900175;1990331
+02.09.2014 12:53:04,881;12900412;1990331
+02.09.2014 12:53:05,394;12900100;1990331
+02.09.2014 12:53:05,912;12900187;1990331
+02.09.2014 12:53:06,424;12900193;1990331
+02.09.2014 12:53:06,944;12900187;1990331
+02.09.2014 12:53:07,446;12900318;1990331
+02.09.2014 12:53:07,955;12900318;1990331
+02.09.2014 12:53:08,472;12900306;1990331
+02.09.2014 12:53:08,956;12900406;1990331
+02.09.2014 12:53:09,486;12900531;1990331
+02.09.2014 12:53:09,991;12900468;1990331
+02.09.2014 12:53:10,488;12900193;1990331
+02.09.2014 12:53:11,010;12900287;1990331
+02.09.2014 12:53:11,519;12899887;1990331
+02.09.2014 12:53:12,041;12900093;1990331
+02.09.2014 12:53:12,557;12900075;1990331
+02.09.2014 12:53:13,070;12900325;1990331
+02.09.2014 12:53:13,584;12899968;1990331
+02.09.2014 12:53:14,079;12900300;1990331
+02.09.2014 12:53:14,598;12900418;1990331
+02.09.2014 12:53:15,109;12900262;1990331
+02.09.2014 12:53:15,608;12900325;1990331
+02.09.2014 12:53:16,115;12899987;1990331
+02.09.2014 12:53:16,618;12900212;1990331
+02.09.2014 12:53:17,130;12900506;1990331
+02.09.2014 12:53:17,645;12900125;1990331
+02.09.2014 12:53:18,144;12900087;1990331
+02.09.2014 12:53:18,654;12900275;1990331
+02.09.2014 12:53:19,154;12900100;1990331
+02.09.2014 12:53:19,668;12900087;1990331
+02.09.2014 12:53:20,176;12899918;1990331
+02.09.2014 12:53:20,677;12900443;1990331
+02.09.2014 12:53:21,184;12899818;1990331
+02.09.2014 12:53:21,688;12900068;1990331
+02.09.2014 12:53:22,194;12900500;1990331
+02.09.2014 12:53:22,713;12899881;1990331
+02.09.2014 12:53:23,208;12899768;1990331
+02.09.2014 12:53:23,721;12900031;1990331
+02.09.2014 12:53:24,224;12899987;1990331
+02.09.2014 12:53:24,735;12899856;1990331
+02.09.2014 12:53:25,234;12900200;1990331
+02.09.2014 12:53:25,750;12900100;1990331
+02.09.2014 12:53:26,250;12900025;1990331
+02.09.2014 12:53:26,764;12900106;1990331
+02.09.2014 12:53:27,276;12900425;1990331
+02.09.2014 12:53:27,778;12900300;1990331
+02.09.2014 12:53:28,285;12900475;1990331
+02.09.2014 12:53:28,786;12900225;1990331
+02.09.2014 12:53:29,298;12900425;1990331
+02.09.2014 12:53:29,811;12900618;1990331
+02.09.2014 12:53:30,308;12900000;1990331
+02.09.2014 12:53:30,821;12899787;1990331
+02.09.2014 12:53:31,316;12900400;1990331
+02.09.2014 12:53:31,831;12900287;1990331
+02.09.2014 12:53:32,326;12900343;1990331
+02.09.2014 12:53:32,846;12900218;1990331
+02.09.2014 12:53:33,353;12900331;1990331
+02.09.2014 12:53:33,855;12900206;1990331
+02.09.2014 12:53:34,366;12900106;1990331
+02.09.2014 12:53:34,882;12900212;1990331
+02.09.2014 12:53:35,378;12900093;1990331
+02.09.2014 12:53:35,894;12899837;1990331
+02.09.2014 12:53:36,387;12900356;1990331
+02.09.2014 12:53:36,901;12900237;1990331
+02.09.2014 12:53:37,413;12900081;1990331
+02.09.2014 12:53:37,921;12900481;1990331
+02.09.2014 12:53:38,423;12900537;1990331
+02.09.2014 12:53:38,925;12900206;1990331
+02.09.2014 12:53:39,439;12900181;1990331
+02.09.2014 12:53:39,952;12900187;1990331
+02.09.2014 12:53:40,447;12900000;1990331
+02.09.2014 12:53:40,962;12900093;1990331
+02.09.2014 12:53:41,468;12900181;1990331
+02.09.2014 12:53:41,985;12899843;1990331
+02.09.2014 12:53:42,487;12899993;1990331
+02.09.2014 12:53:42,993;12900300;1990331
+02.09.2014 12:53:43,495;12899868;1990331
+02.09.2014 12:53:44,004;12900306;1990331
+02.09.2014 12:53:44,519;12899887;1990331
+02.09.2014 12:53:45,016;12899881;1990331
+02.09.2014 12:53:45,529;12900425;1990331
+02.09.2014 12:53:46,025;12900300;1990331
+02.09.2014 12:53:46,540;12900087;1990331
+02.09.2014 12:53:47,051;12900187;1990331
+02.09.2014 12:53:47,563;12900093;1990331
+02.09.2014 12:53:48,060;12900012;1990331
+02.09.2014 12:53:48,573;12900343;1990331
+02.09.2014 12:53:49,085;12900431;1990331
+02.09.2014 12:53:49,588;12900087;1990331
+02.09.2014 12:53:50,095;12900156;1990331
+02.09.2014 12:53:50,596;12900312;1990331
+02.09.2014 12:53:51,103;12900218;1990331
+02.09.2014 12:53:51,604;12900031;1990331
+02.09.2014 12:53:52,117;12900300;1990331
+02.09.2014 12:53:52,632;12900381;1990331
+02.09.2014 12:53:53,128;12900000;1990331
+02.09.2014 12:53:53,643;12900331;1990331
+02.09.2014 12:53:54,156;12900506;1990331
+02.09.2014 12:53:54,657;12900193;1990331
+02.09.2014 12:53:55,164;12900043;1990331
+02.09.2014 12:53:55,666;12900075;1990331
+02.09.2014 12:53:56,175;12900418;1990331
+02.09.2014 12:53:56,675;12900118;1990331
+02.09.2014 12:53:57,194;12900200;1990331
+02.09.2014 12:53:57,694;12900181;1990331
+02.09.2014 12:53:58,194;12900212;1990331
+02.09.2014 12:53:58,707;12900000;1990331
+02.09.2014 12:53:59,223;12900200;1990331
+02.09.2014 12:53:59,744;12900306;1990331
+02.09.2014 12:54:00,227;12900200;1990331
+02.09.2014 12:54:00,787;12900087;1990331
+02.09.2014 12:54:01,250;12900206;1990331
+02.09.2014 12:54:01,756;12900100;1990331
+02.09.2014 12:54:02,286;12899900;1990331
+02.09.2014 12:54:02,764;12899975;1990331
+02.09.2014 12:54:03,286;12899875;1990331
+02.09.2014 12:54:03,774;12900200;1990331
+02.09.2014 12:54:04,287;12900300;1990331
+02.09.2014 12:54:04,801;12900087;1990331
+02.09.2014 12:54:05,296;12900300;1990331
+02.09.2014 12:54:05,810;12900362;1990331
+02.09.2014 12:54:06,309;12900212;1990331
+02.09.2014 12:54:06,835;12900068;1990331
+02.09.2014 12:54:07,328;12899787;1990331
+02.09.2014 12:54:07,842;12900012;1990331
+02.09.2014 12:54:08,339;12900487;1990331
+02.09.2014 12:54:08,851;12900068;1990331
+02.09.2014 12:54:09,366;12900062;1990331
+02.09.2014 12:54:09,865;12900006;1990331
+02.09.2014 12:54:10,372;12900106;1990331
+02.09.2014 12:54:10,885;12899781;1990331
+02.09.2014 12:54:11,382;12900431;1990331
+02.09.2014 12:54:11,901;12899893;1990331
+02.09.2014 12:54:12,413;12900200;1990331
+02.09.2014 12:54:12,916;12900300;1990331
+02.09.2014 12:54:13,426;12900537;1990331
+02.09.2014 12:54:13,923;12900300;1990331
+02.09.2014 12:54:14,445;12900325;1990331
+02.09.2014 12:54:14,942;12900156;1990331
+02.09.2014 12:54:15,455;12900187;1990331
+02.09.2014 12:54:15,952;12900206;1990331
+02.09.2014 12:54:16,470;12899925;1990331
+02.09.2014 12:54:16,967;12900325;1990331
+02.09.2014 12:54:17,494;12900006;1990331
+02.09.2014 12:54:17,975;12899850;1990331
+02.09.2014 12:54:18,489;12900206;1990331
+02.09.2014 12:54:19,002;12900287;1990331
+02.09.2014 12:54:19,509;12899887;1990331
+02.09.2014 12:54:20,016;12899768;1990331
+02.09.2014 12:54:20,536;12900325;1990331
+02.09.2014 12:54:21,049;12900225;1990331
+02.09.2014 12:54:21,568;12900293;1990331
+02.09.2014 12:54:22,081;12900425;1990331
+02.09.2014 12:54:22,578;12900306;1990331
+02.09.2014 12:54:23,094;12899987;1990331
+02.09.2014 12:54:23,603;12900206;1990331
+02.09.2014 12:54:24,104;12900212;1990331
+02.09.2014 12:54:24,617;12900187;1990331
+02.09.2014 12:54:25,146;12900200;1990331
+02.09.2014 12:54:25,648;12900393;1990331
+02.09.2014 12:54:26,166;12900300;1990331
+02.09.2014 12:54:26,679;12900331;1990331
+02.09.2014 12:54:27,175;12900087;1990331
+02.09.2014 12:54:27,694;12900187;1990331
+02.09.2014 12:54:28,206;12900262;1990331
+02.09.2014 12:54:28,714;12900206;1990331
+02.09.2014 12:54:29,210;12900012;1990331
+02.09.2014 12:54:29,734;12900168;1990331
+02.09.2014 12:54:30,227;12900275;1990331
+02.09.2014 12:54:30,745;12900300;1990331
+02.09.2014 12:54:31,239;12900100;1990331
+02.09.2014 12:54:31,751;12903725;1989517
+02.09.2014 12:54:32,246;12987143;1999441
+02.09.2014 12:54:32,775;13206175;2010267
+02.09.2014 12:54:33,271;13498675;2020145
+02.09.2014 12:54:33,781;13889700;2030802
+02.09.2014 12:54:34,275;14337212;2040476
+02.09.2014 12:54:34,804;14854200;2051656
+02.09.2014 12:54:35,299;15298737;2061087
+02.09.2014 12:54:35,810;15778687;2071168
+02.09.2014 12:54:36,306;16222825;2080319
+02.09.2014 12:54:36,817;16702593;2090081
+02.09.2014 12:54:37,323;17144487;2099662
+02.09.2014 12:54:37,840;17626275;2109005
+02.09.2014 12:54:38,334;18069075;2117406
+02.09.2014 12:54:38,848;18548181;2126215
+02.09.2014 12:54:39,353;18992137;2134715
+02.09.2014 12:54:39,870;19466131;2142732
+02.09.2014 12:54:40,364;19813631;2149819
+02.09.2014 12:54:40,876;20080456;2156837
+02.09.2014 12:54:41,382;20225231;2162831
+02.09.2014 12:54:41,894;20270987;2165023
+02.09.2014 12:54:42,389;20269412;2165017
+02.09.2014 12:54:42,911;20267300;2165011
+02.09.2014 12:54:43,412;20268000;2165011
+02.09.2014 12:54:43,924;20267618;2165011
+02.09.2014 12:54:44,441;20267200;2165011
+02.09.2014 12:54:44,934;20266893;2165011
+02.09.2014 12:54:45,447;20267125;2165011
+02.09.2014 12:54:45,946;20266993;2165011
+02.09.2014 12:54:46,457;20266875;2165011
+02.09.2014 12:54:46,973;20266993;2165011
+02.09.2014 12:54:47,484;20267193;2165011
+02.09.2014 12:54:47,979;20266968;2165011
+02.09.2014 12:54:48,490;20267200;2164075
+02.09.2014 12:54:49,001;20267362;2195784
+02.09.2014 12:54:49,512;20266737;2204680
+02.09.2014 12:54:50,007;20266975;2210953
+02.09.2014 12:54:50,519;20267100;2264308
+02.09.2014 12:54:51,014;20267306;2275104
+02.09.2014 12:54:51,525;20267312;2274994
+02.09.2014 12:54:52,034;20267050;2275000
+02.09.2014 12:54:52,553;20267100;2274988
+02.09.2014 12:54:53,061;20266993;2274988
+02.09.2014 12:54:53,580;20267362;2274988
+02.09.2014 12:54:54,090;20267018;2274988
+02.09.2014 12:54:54,607;20267293;2274988
+02.09.2014 12:54:55,117;20266993;2274988
+02.09.2014 12:54:55,628;20267237;2274988
+02.09.2014 12:54:56,142;20266981;2274988
+02.09.2014 12:54:56,641;20267006;2274988
+02.09.2014 12:54:57,152;20267012;2274988
+02.09.2014 12:54:57,647;20267043;2274988
+02.09.2014 12:54:58,158;20266843;2274988
+02.09.2014 12:54:58,670;20267300;2274988
+02.09.2014 12:54:59,183;20267100;2274988
+02.09.2014 12:54:59,688;20267000;2274988
+02.09.2014 12:55:00,188;20266987;2274988
+02.09.2014 12:55:00,700;20229962;2277604
+02.09.2014 12:55:01,211;20072975;2328406
+02.09.2014 12:55:01,707;19827562;2378366
+02.09.2014 12:55:02,223;19453806;2436523
+02.09.2014 12:55:02,722;19016700;2486406
+02.09.2014 12:55:03,234;18536075;2540447
+02.09.2014 12:55:03,734;18090943;2590145
+02.09.2014 12:55:04,247;17612037;2644063
+02.09.2014 12:55:04,753;17130300;2697936
+02.09.2014 12:55:05,264;16687500;2751732
+02.09.2014 12:55:05,767;16244893;2801331
+02.09.2014 12:55:06,278;15764500;2854936
+02.09.2014 12:55:06,784;15285000;2908534
+02.09.2014 12:55:07,285;14841418;2958063
+02.09.2014 12:55:07,791;14361400;3011622
+02.09.2014 12:55:08,302;13919093;3065133
+02.09.2014 12:55:08,804;13475256;3114476
+02.09.2014 12:55:09,316;12995018;3167854
+02.09.2014 12:55:09,828;12517368;3221133
+02.09.2014 12:55:10,363;11999400;3278569
+02.09.2014 12:55:10,834;11591768;3323552
+02.09.2014 12:55:11,371;11075193;3380901
+02.09.2014 12:55:11,855;10669306;3429959
+02.09.2014 12:55:12,391;10153218;3486924
+02.09.2014 12:55:12,863;9746062;3531662
+02.09.2014 12:55:13,412;9192493;3592563
+02.09.2014 12:55:13,871;8824525;3633250
+02.09.2014 12:55:14,391;8343087;3685901
+02.09.2014 12:55:14,901;7863987;3738436
+02.09.2014 12:55:15,396;7458343;3786895
+02.09.2014 12:55:15,914;6977937;3839145
+02.09.2014 12:55:16,423;6497781;3891313
+02.09.2014 12:55:16,924;6054856;3939500
+02.09.2014 12:55:17,433;5574175;3995482
+02.09.2014 12:55:17,952;5094856;4047232
+02.09.2014 12:55:18,449;4651693;4094970
+02.09.2014 12:55:18,954;4207818;4142482
+02.09.2014 12:55:19,462;3728693;4193779
+02.09.2014 12:55:19,973;3247350;4244953
+02.09.2014 12:55:20,468;2805218;4291901
+02.09.2014 12:55:20,984;2326200;4342470
+02.09.2014 12:55:21,491;1844818;4392726
+02.09.2014 12:55:22,003;1406118;4442610
+02.09.2014 12:55:22,499;1054650;4488575
+02.09.2014 12:55:23,014;782812;4543709
+02.09.2014 12:55:23,520;633912;4615941
+02.09.2014 12:55:24,033;583693;4619052
+02.09.2014 12:55:24,526;587162;4618976
+02.09.2014 12:55:25,038;587293;4619011
+02.09.2014 12:55:25,551;586675;4617412
+02.09.2014 12:55:26,061;588012;4617412
+02.09.2014 12:55:26,557;587900;4616476
+02.09.2014 12:55:27,066;587906;4616773
+02.09.2014 12:55:27,574;587900;4616912
+02.09.2014 12:55:28,091;588275;4616895
+02.09.2014 12:55:28,587;588300;4616848
+02.09.2014 12:55:29,097;588468;4616848
+02.09.2014 12:55:29,593;588212;4616848
+02.09.2014 12:55:30,104;588137;4616843
+02.09.2014 12:55:30,611;587718;4618994
+02.09.2014 12:55:31,127;587768;4618994
+02.09.2014 12:55:31,623;588018;4618982
+02.09.2014 12:55:32,140;588400;4618982
+02.09.2014 12:55:32,645;588106;4618837
+02.09.2014 12:55:33,160;587937;4611523
+02.09.2014 12:55:33,655;588275;4557843
+02.09.2014 12:55:34,169;587918;4541197
+02.09.2014 12:55:34,664;588200;4535162
+02.09.2014 12:55:35,179;588300;4525220
+02.09.2014 12:55:35,703;588618;4498732
+02.09.2014 12:55:36,202;588081;4499459
+02.09.2014 12:55:36,708;587656;4499465
+02.09.2014 12:55:37,207;587637;4499453
+02.09.2014 12:55:37,719;588300;4499453
+02.09.2014 12:55:38,230;588200;4499453
+02.09.2014 12:55:38,725;587781;4499453
+02.09.2014 12:55:39,238;587925;4499453
+02.09.2014 12:55:39,755;587968;4499453
+02.09.2014 12:55:40,271;588006;4499453
+02.09.2014 12:55:40,774;587993;4499453
+02.09.2014 12:55:41,302;587893;4499453
+02.09.2014 12:55:41,813;588075;4499453
+02.09.2014 12:55:42,307;587775;4499453
+02.09.2014 12:55:42,823;607437;4496540
+02.09.2014 12:55:43,329;724731;4422296
+02.09.2014 12:55:43,842;961156;4270575
+02.09.2014 12:55:44,353;1311012;4127709
+02.09.2014 12:55:44,863;1769400;3961046
+02.09.2014 12:55:45,363;2213956;3818383
+02.09.2014 12:55:45,868;2659818;3675738
+02.09.2014 12:55:46,380;3139075;3521250
+02.09.2014 12:55:46,891;3582506;3366616
+02.09.2014 12:55:47,402;4063762;3212232
+02.09.2014 12:55:47,913;4543056;3057831
+02.09.2014 12:55:48,430;5024412;2903680
+02.09.2014 12:55:48,942;5466431;2761284
+02.09.2014 12:55:49,457;5945906;2606970
+02.09.2014 12:55:49,967;6389668;2452784
+02.09.2014 12:55:50,484;6868668;2298668
+02.09.2014 12:55:50,995;7348856;2144831
+02.09.2014 12:55:51,498;7791693;2002941
+02.09.2014 12:55:52,003;8271618;1849203
+02.09.2014 12:55:52,498;8715768;1707296
+02.09.2014 12:55:53,009;9194600;1553558
+02.09.2014 12:55:53,513;9638275;1399877
+02.09.2014 12:55:54,030;10118706;1247034
+02.09.2014 12:55:54,525;10561456;1106186
+02.09.2014 12:55:55,038;11041181;953418
+02.09.2014 12:55:55,550;11521643;800470
+02.09.2014 12:55:56,059;11963806;609639
+02.09.2014 12:55:56,555;12349043;326639
+02.09.2014 12:55:57,063;12653981;122459
+02.09.2014 12:55:57,571;12841718;25767
+02.09.2014 12:55:58,081;12913450;16656
+02.09.2014 12:55:58,593;12913412;16912
+02.09.2014 12:55:59,103;12912581;16976
+02.09.2014 12:55:59,598;12913425;16994
+02.09.2014 12:56:00,108;12912362;16982
+02.09.2014 12:56:00,604;12912293;16982
+02.09.2014 12:56:01,133;12912093;16982
+02.09.2014 12:56:01,630;12912006;16982
+02.09.2014 12:56:02,140;12911750;16982
+02.09.2014 12:56:02,646;12912006;16982
+02.09.2014 12:56:03,144;12912181;16982
+02.09.2014 12:56:03,656;12911887;16982
+02.09.2014 12:56:04,167;12912300;16982
+02.09.2014 12:56:04,674;12912093;16982
+02.09.2014 12:56:05,188;12911975;16273
+02.09.2014 12:56:05,684;12912031;45895
+02.09.2014 12:56:06,195;12911150;55343
+02.09.2014 12:56:06,695;12911825;59947
+02.09.2014 12:56:07,208;12911075;90290
+02.09.2014 12:56:07,703;12911825;135889
+02.09.2014 12:56:08,223;12911275;137034
+02.09.2014 12:56:08,724;12911706;137005
+02.09.2014 12:56:09,237;12911575;136994
+02.09.2014 12:56:09,750;12911493;136994
+02.09.2014 12:56:10,266;12911587;136994
+02.09.2014 12:56:10,778;12910987;136994
+02.09.2014 12:56:11,294;12911362;136994
+02.09.2014 12:56:11,806;12911537;136994
+02.09.2014 12:56:12,316;12911381;136994
+02.09.2014 12:56:12,828;12911500;136994
+02.09.2014 12:56:13,335;12911718;136994
+02.09.2014 12:56:13,834;12911300;136994
+02.09.2014 12:56:14,341;12911487;136994
+02.09.2014 12:56:14,847;12911506;136994
+02.09.2014 12:56:15,351;12911387;136994
+02.09.2014 12:56:15,868;12911506;136994
+02.09.2014 12:56:16,379;12911518;136994
+02.09.2014 12:56:16,875;12911737;136994
+02.09.2014 12:56:17,405;12911600;136994
+02.09.2014 12:56:17,938;12911600;136994
+02.09.2014 12:56:18,435;12911718;135889
+02.09.2014 12:56:18,943;12973825;154203
+02.09.2014 12:56:19,458;13151881;256936
+02.09.2014 12:56:19,993;13470556;492191
+02.09.2014 12:56:20,492;13850787;795517
+02.09.2014 12:56:21,005;14297793;1080750
+02.09.2014 12:56:21,483;14740618;1366110
+02.09.2014 12:56:22,025;15259031;1698982
+02.09.2014 12:56:22,497;15665468;1960534
+02.09.2014 12:56:23,047;16212881;2317156
+02.09.2014 12:56:23,524;16533293;2578715
+02.09.2014 12:56:24,023;16788150;2887779
+02.09.2014 12:56:24,533;16946187;3196895
+02.09.2014 12:56:25,043;17006587;3505959
+02.09.2014 12:56:25,538;17003062;3791290
+02.09.2014 12:56:26,055;17002375;4076593
+02.09.2014 12:56:26,562;17003112;4361174
+02.09.2014 12:56:27,072;17002100;4536406
+02.09.2014 12:56:27,566;17002287;4603947
+02.09.2014 12:56:28,077;17002200;4605034
+02.09.2014 12:56:28,589;17002137;4604994
+02.09.2014 12:56:29,101;17002100;4605000
+02.09.2014 12:56:29,595;17002100;4605000
+02.09.2014 12:56:30,106;17002100;4605000
+02.09.2014 12:56:30,612;17002093;4605000
+02.09.2014 12:56:31,128;17002093;4605000
+02.09.2014 12:56:31,618;17001956;4605000
+02.09.2014 12:56:32,137;17002100;4605000
+02.09.2014 12:56:32,648;17002081;4605000
+02.09.2014 12:56:33,165;17002100;4605000
+02.09.2014 12:56:33,677;17002100;4605000
+02.09.2014 12:56:34,203;17002250;4605000
+02.09.2014 12:56:34,703;17002100;4605000
+02.09.2014 12:56:35,209;17002100;4605000
+02.09.2014 12:56:35,726;17002100;4605000
+02.09.2014 12:56:36,234;17002100;4603691
+02.09.2014 12:56:36,745;17002125;4567982
+02.09.2014 12:56:37,245;17002112;4529790
+02.09.2014 12:56:37,756;17002000;4523418
+02.09.2014 12:56:38,261;17001987;4519959
+02.09.2014 12:56:38,766;17001987;4487081
+02.09.2014 12:56:39,272;17002100;4485744
+02.09.2014 12:56:39,776;17002100;4485837
+02.09.2014 12:56:40,287;17002100;4485866
+02.09.2014 12:56:40,798;17001981;4485860
+02.09.2014 12:56:41,303;17002100;4485860
+02.09.2014 12:56:41,804;17001993;4485860
+02.09.2014 12:56:42,313;17002100;4485860
+02.09.2014 12:56:42,829;17002100;4485860
+02.09.2014 12:56:43,334;17002068;4485860
+02.09.2014 12:56:43,837;17002075;4485860
+02.09.2014 12:56:44,344;17002087;4485860
+02.09.2014 12:56:44,844;17002106;4485860
+02.09.2014 12:56:45,355;17002100;4485860
+02.09.2014 12:56:45,866;17002100;4485860
+02.09.2014 12:56:46,371;16982906;4481761
+02.09.2014 12:56:46,882;16862762;4419162
+02.09.2014 12:56:47,377;16647818;4337732
+02.09.2014 12:56:47,903;16273812;4242831
+02.09.2014 12:56:48,403;15888531;4161470
+02.09.2014 12:56:48,909;15407437;4073476
+02.09.2014 12:56:49,419;14924493;3985598
+02.09.2014 12:56:49,914;14481587;3904453
+02.09.2014 12:56:50,442;14001743;3816558
+02.09.2014 12:56:50,938;13521400;3735430
+02.09.2014 12:56:51,448;13078562;3647494
+02.09.2014 12:56:51,942;12637156;3559674
+02.09.2014 12:56:52,470;12156775;3472058
+02.09.2014 12:56:52,965;11712525;3391308
+02.09.2014 12:56:53,475;11232600;3303831
+02.09.2014 12:56:53,980;10752343;3216343
+02.09.2014 12:56:54,492;10272825;3128866
+02.09.2014 12:56:54,990;9829443;3048197
+02.09.2014 12:56:55,507;9349362;2960941
+02.09.2014 12:56:56,007;8907893;2873796
+02.09.2014 12:56:56,516;8464300;2793383
+02.09.2014 12:56:57,027;7983475;2706308
+02.09.2014 12:56:57,539;7504968;2619261
+02.09.2014 12:56:58,044;7023531;2532412
+02.09.2014 12:56:58,544;6581137;2452313
+02.09.2014 12:56:59,050;6137656;2365703
+02.09.2014 12:56:59,562;5657206;2279244
+02.09.2014 12:57:00,055;5214950;2199540
+02.09.2014 12:57:00,583;4734325;2106686
+02.09.2014 12:57:01,093;4253831;2027331
+02.09.2014 12:57:01,609;3774293;1941500
+02.09.2014 12:57:02,120;3330756;1855750
+02.09.2014 12:57:02,638;2851487;1769994
+02.09.2014 12:57:03,150;2408856;1691354
+02.09.2014 12:57:03,644;1927400;1606372
+02.09.2014 12:57:04,172;1450818;1521465
+02.09.2014 12:57:04,667;1087043;1439604
+02.09.2014 12:57:05,177;804118;1313645
+02.09.2014 12:57:05,683;645818;1225860
+02.09.2014 12:57:06,194;584368;1220215
+02.09.2014 12:57:06,694;586525;1220401
+02.09.2014 12:57:07,208;587187;1220418
+02.09.2014 12:57:07,711;587356;1220406
+02.09.2014 12:57:08,222;587618;1220406
+02.09.2014 12:57:08,717;587912;1220406
+02.09.2014 12:57:09,245;587887;1220406
+02.09.2014 12:57:09,742;587900;1220406
+02.09.2014 12:57:10,251;587781;1220406
+02.09.2014 12:57:10,748;587693;1220406
+02.09.2014 12:57:11,262;588000;1220406
+02.09.2014 12:57:11,761;587675;1220406
+02.09.2014 12:57:12,278;588200;1220406
+02.09.2014 12:57:12,783;587868;1219686
+02.09.2014 12:57:13,283;587900;1235226
+02.09.2014 12:57:13,794;588006;1258005
+02.09.2014 12:57:14,306;587993;1263430
+02.09.2014 12:57:14,812;588612;1284325
+02.09.2014 12:57:15,322;587818;1336180
+02.09.2014 12:57:15,817;587800;1340593
+02.09.2014 12:57:16,327;587687;1340517
+02.09.2014 12:57:16,834;587800;1340511
+02.09.2014 12:57:17,358;588106;1340511
+02.09.2014 12:57:17,846;588000;1340511
+02.09.2014 12:57:18,357;587893;1340511
+02.09.2014 12:57:18,870;587693;1340511
+02.09.2014 12:57:19,380;587687;1340511
+02.09.2014 12:57:19,891;587987;1340511
+02.09.2014 12:57:20,402;587800;1340511
+02.09.2014 12:57:20,904;587775;1340511
+02.09.2014 12:57:21,413;588000;1340511
+02.09.2014 12:57:21,914;588125;1340511
+02.09.2014 12:57:22,427;588106;1340511
+02.09.2014 12:57:22,934;587950;1340511
+02.09.2014 12:57:23,447;588000;1340511
+02.09.2014 12:57:23,944;588431;1340511
+02.09.2014 12:57:24,449;587912;1340511
+02.09.2014 12:57:24,961;588231;1340511
+02.09.2014 12:57:25,455;617000;1335127
+02.09.2014 12:57:25,972;759143;1259581
+02.09.2014 12:57:26,478;992087;1162145
+02.09.2014 12:57:26,991;1353462;1064610
+02.09.2014 12:57:27,529;1858043;958406
+02.09.2014 12:57:28,012;2265087;875087
+02.09.2014 12:57:28,572;2819462;761790
+02.09.2014 12:57:29,043;3227031;678651
+02.09.2014 12:57:29,603;3781062;565610
+02.09.2014 12:57:30,065;4149362;490697
+02.09.2014 12:57:30,615;4605331;373308
+02.09.2014 12:57:31,082;4822356;237819
+02.09.2014 12:57:31,583;5002081;144302
+02.09.2014 12:57:32,094;5065943;136645
+02.09.2014 12:57:32,606;5063406;136808
+02.09.2014 12:57:33,097;5063143;136953
+02.09.2014 12:57:33,613;5063043;136953
+02.09.2014 12:57:34,120;5062506;136953
+02.09.2014 12:57:34,632;5062381;136953
+02.09.2014 12:57:35,132;5062256;136953
+02.09.2014 12:57:35,654;5062287;136953
+02.09.2014 12:57:36,149;5062287;136953
+02.09.2014 12:57:36,660;5062300;136953
+02.09.2014 12:57:37,159;5062200;136953
+02.09.2014 12:57:37,682;5062300;136953
+02.09.2014 12:57:38,176;5062175;136953
+02.09.2014 12:57:38,687;5062312;136953
+02.09.2014 12:57:39,182;5062243;136953
+02.09.2014 12:57:39,703;5062093;136953
+02.09.2014 12:57:40,198;5062243;136953
+02.09.2014 12:57:40,714;5062206;132250
+02.09.2014 12:57:41,222;5061168;76034
+02.09.2014 12:57:41,733;5061200;58523
+02.09.2014 12:57:42,243;5061293;52587
+02.09.2014 12:57:42,742;5061381;41494
+02.09.2014 12:57:43,239;5061700;16738
+02.09.2014 12:57:43,762;5061181;17750
+02.09.2014 12:57:44,261;5061200;17779
+02.09.2014 12:57:44,761;5061087;17808
+02.09.2014 12:57:45,271;5061100;17808
+02.09.2014 12:57:45,796;5061112;17808
+02.09.2014 12:57:46,294;5060893;17808
+02.09.2014 12:57:46,801;5061000;17808
+02.09.2014 12:57:47,295;5060893;17808
+02.09.2014 12:57:47,815;5060743;17808
+02.09.2014 12:57:48,312;5061106;17808
+02.09.2014 12:57:48,827;5061012;17808
+02.09.2014 12:57:49,338;5061137;17808
+02.09.2014 12:57:49,857;5060900;17808
+02.09.2014 12:57:50,369;5061006;17808
+02.09.2014 12:57:50,890;5061137;17808
+02.09.2014 12:57:51,398;5060893;17808
+02.09.2014 12:57:51,917;5060887;17808
+02.09.2014 12:57:52,412;5060900;17808
+02.09.2014 12:57:52,925;5060968;17808
+02.09.2014 12:57:53,439;5060881;17808
+02.09.2014 12:57:53,954;5060868;17808
+02.09.2014 12:57:54,447;5060981;17808
+02.09.2014 12:57:54,960;5061000;17808
+02.09.2014 12:57:55,456;5061000;17808
+02.09.2014 12:57:55,970;5061106;17808
+02.09.2014 12:57:56,465;5061118;17808
+02.09.2014 12:57:56,984;5061031;17808
+02.09.2014 12:57:57,501;5060987;17808
+02.09.2014 12:57:58,013;5061106;17808
+02.09.2014 12:57:58,526;5060906;17808
+02.09.2014 12:57:59,045;5061000;17808
+02.09.2014 12:57:59,561;5060993;17808
+02.09.2014 12:58:00,064;5060950;17808
+02.09.2014 12:58:00,581;5060668;17808
+02.09.2014 12:58:01,075;5061100;17808
+02.09.2014 12:58:01,588;5061006;17808
+02.09.2014 12:58:02,101;5060868;17808
+02.09.2014 12:58:02,596;5061006;17808
+02.09.2014 12:58:03,092;5061000;17808
+02.09.2014 12:58:03,621;5060893;17808
+02.09.2014 12:58:04,116;5061143;17808
+02.09.2014 12:58:04,628;5061012;17808
+02.09.2014 12:58:05,123;5061031;17808
+02.09.2014 12:58:05,641;5060756;17808
+02.09.2014 12:58:06,149;5060793;17808
+02.09.2014 12:58:06,661;5061200;17808
+02.09.2014 12:58:07,159;5060681;17808
+02.09.2014 12:58:07,671;5060881;17808
+02.09.2014 12:58:08,182;5060887;17808
+02.09.2014 12:58:08,684;5061031;17808
+02.09.2014 12:58:09,192;5061018;17808
+02.09.2014 12:58:09,692;5061250;17808
+02.09.2014 12:58:10,204;5061125;17808
+02.09.2014 12:58:10,718;5061106;17808
+02.09.2014 12:58:11,214;5061000;17808
+02.09.2014 12:58:11,728;5060906;17808
+02.09.2014 12:58:12,223;5061025;17808
+02.09.2014 12:58:12,738;5061000;17808
+02.09.2014 12:58:13,244;5060893;17808
+02.09.2014 12:58:13,762;5060693;17808
+02.09.2014 12:58:14,258;5060893;17808
+02.09.2014 12:58:14,772;5060875;17808
+02.09.2014 12:58:15,284;5060912;17808
+02.09.2014 12:58:15,788;5060900;17808
+02.09.2014 12:58:16,298;5060987;17808
+02.09.2014 12:58:16,801;5061000;17808
+02.09.2014 12:58:17,297;5060912;17808
+02.09.2014 12:58:17,809;5061043;17808
+02.09.2014 12:58:18,305;5060893;17808
+02.09.2014 12:58:18,834;5061112;17808
+02.09.2014 12:58:19,331;5060993;17808
+02.09.2014 12:58:19,843;5061000;17808
+02.09.2014 12:58:20,343;5061100;17808
+02.09.2014 12:58:20,856;5060975;17808
+02.09.2014 12:58:21,369;5061006;17808
+02.09.2014 12:58:21,881;5060881;17808
+02.09.2014 12:58:22,383;5060981;17808
+02.09.2014 12:58:22,875;5060993;17808
+02.09.2014 12:58:23,393;5061000;17808
+02.09.2014 12:58:23,906;5060968;17808
+02.09.2014 12:58:24,402;5060993;17808
+02.09.2014 12:58:24,909;5061018;17808
+02.09.2014 12:58:25,427;5061012;17808
+02.09.2014 12:58:25,923;5060800;17808
+02.09.2014 12:58:26,437;5061181;17808
+02.09.2014 12:58:26,948;5060887;17808
+02.09.2014 12:58:27,463;5060831;17808
+02.09.2014 12:58:27,958;5060900;17808
+02.09.2014 12:58:28,471;5060906;17808
+02.09.2014 12:58:28,965;5061000;17808
+02.09.2014 12:58:29,478;5061018;17808
+02.09.2014 12:58:29,990;5060900;17808
+02.09.2014 12:58:30,503;5060881;17808
+02.09.2014 12:58:30,999;5060887;17808
+02.09.2014 12:58:31,502;5060893;17808
+02.09.2014 12:58:32,020;5060900;17808
+02.09.2014 12:58:32,533;5060993;17808
+02.09.2014 12:58:33,021;5061006;17808
+02.09.2014 12:58:33,542;5061106;17808
+02.09.2014 12:58:34,037;5060993;17808
+02.09.2014 12:58:34,551;5061000;17808
+02.09.2014 12:58:35,051;5060900;17808
+02.09.2014 12:58:35,564;5060906;17808
+02.09.2014 12:58:36,071;5060900;17808
+02.09.2014 12:58:36,595;5060906;17808
+02.09.2014 12:58:37,079;5060893;17808
+02.09.2014 12:58:37,598;5060900;17808
+02.09.2014 12:58:38,118;5061000;17808
+02.09.2014 12:58:38,607;5060900;17808
+02.09.2014 12:58:39,146;5060868;17808
+02.09.2014 12:58:39,615;5061118;17808
+02.09.2014 12:58:40,159;5060893;17808
+02.09.2014 12:58:40,643;5060993;17808
+02.09.2014 12:58:41,181;5060931;17808
+02.09.2014 12:58:41,653;5060800;17808
+02.09.2014 12:58:42,154;5060862;17808
+02.09.2014 12:58:42,666;5061000;17808
+02.09.2014 12:58:43,161;5060893;17808
+02.09.2014 12:58:43,675;5060868;17808
+02.09.2014 12:58:44,189;5060981;17808
+02.09.2014 12:58:44,704;5061150;17808
+02.09.2014 12:58:45,217;5060900;17808
+02.09.2014 12:58:45,737;5061118;17808
+02.09.2014 12:58:46,249;5061162;17808
+02.09.2014 12:58:46,745;5060893;17808
+02.09.2014 12:58:47,263;5060900;17808
+02.09.2014 12:58:47,774;5061006;17808
+02.09.2014 12:58:48,275;5060881;17808
+02.09.2014 12:58:48,775;5061100;17808
+02.09.2014 12:58:49,292;5060775;17808
+02.09.2014 12:58:49,800;5060887;17808
+02.09.2014 12:58:50,311;5060750;17808
+02.09.2014 12:58:50,806;5060787;17808
+02.09.2014 12:58:51,319;5060887;17808
+02.09.2014 12:58:51,832;5060806;17808
+02.09.2014 12:58:52,345;5061000;17808
+02.09.2014 12:58:52,841;5061006;17808
+02.09.2014 12:58:53,352;5061000;17808
+02.09.2014 12:58:53,871;5060793;17808
+02.09.2014 12:58:54,366;5060875;17808
+02.09.2014 12:58:54,878;5060743;17808
+02.09.2014 12:58:55,375;5060906;17808
+02.09.2014 12:58:55,887;5060893;17808
+02.09.2014 12:58:56,403;5060868;17808
+02.09.2014 12:58:56,902;5060900;17808
+02.09.2014 12:58:57,410;5060900;17808
+02.09.2014 12:58:57,912;5061081;17808
+02.09.2014 12:58:58,424;5061100;17808
+02.09.2014 12:58:58,938;5060750;17808
+02.09.2014 12:58:59,434;5061037;17808
+02.09.2014 12:58:59,946;5061000;17808
+02.09.2014 12:59:00,442;5060981;17808
+02.09.2014 12:59:00,956;5060793;17808
+02.09.2014 12:59:01,469;5060900;17808
+02.09.2014 12:59:01,981;5061162;17808
+02.09.2014 12:59:02,483;5060887;17808
+02.09.2014 12:59:03,011;5060887;17808
+02.09.2014 12:59:03,514;5061012;17808
+02.09.2014 12:59:04,036;5060993;17808
+02.09.2014 12:59:04,552;5060950;17808
+02.09.2014 12:59:05,049;5060987;17808
+02.09.2014 12:59:05,568;5061012;17808
+02.09.2014 12:59:06,062;5061012;17808
+02.09.2014 12:59:06,576;5060987;17808
+02.09.2014 12:59:07,081;5061006;17808
+02.09.2014 12:59:07,583;5061018;17808
+02.09.2014 12:59:08,098;5060900;17808
+02.09.2014 12:59:08,607;5060987;17808
+02.09.2014 12:59:09,102;5060893;17808
+02.09.2014 12:59:09,615;5061000;17808
+02.09.2014 12:59:10,129;5061106;17808
+02.09.2014 12:59:10,643;5060793;17808
+02.09.2014 12:59:11,138;5060787;17808
+02.09.2014 12:59:11,653;5061100;17808
+02.09.2014 12:59:12,148;5061000;17808
+02.09.2014 12:59:12,660;5060893;17808
+02.09.2014 12:59:13,171;5060987;17808
+02.09.2014 12:59:13,673;5061125;17808
+02.09.2014 12:59:14,185;5060787;17808
+02.09.2014 12:59:14,704;5061000;17808
+02.09.2014 12:59:15,215;5060931;17808
+02.09.2014 12:59:15,736;5060893;17808
+02.09.2014 12:59:16,247;5061106;17808
+02.09.2014 12:59:16,760;5060750;17808
+02.09.2014 12:59:17,272;5061112;17808
+02.09.2014 12:59:17,767;5060900;17808
+02.09.2014 12:59:18,278;5060950;17808
+02.09.2014 12:59:18,791;5061043;17808
+02.09.2014 12:59:19,287;5060787;17808
+02.09.2014 12:59:19,784;5060893;17808
+02.09.2014 12:59:20,305;5060993;17808
+02.09.2014 12:59:20,811;5060993;17808
+02.09.2014 12:59:21,341;5061000;17808
+02.09.2014 12:59:21,843;5060981;17808
+02.09.2014 12:59:22,376;5060850;17808
+02.09.2014 12:59:22,873;5061106;17808
+02.09.2014 12:59:23,380;5060750;17808
+02.09.2014 12:59:23,899;5060887;17808
+02.09.2014 12:59:24,410;5060837;17808
+02.09.2014 12:59:24,907;5060900;17808
+02.09.2014 12:59:25,425;5060931;17808
+02.09.2014 12:59:25,933;5060875;17808
+02.09.2014 12:59:26,434;5061100;17808
+02.09.2014 12:59:26,943;5061000;17808
+02.09.2014 12:59:27,444;5061006;17808
+02.09.2014 12:59:27,958;5061018;17808
+02.09.2014 12:59:28,469;5061100;17808
+02.09.2014 12:59:28,964;5061106;17808
+02.09.2014 12:59:29,477;5061025;17808
+02.09.2014 12:59:29,989;5060793;17808
+02.09.2014 12:59:30,502;5060793;17808
+02.09.2014 12:59:30,998;5061093;17808
+02.09.2014 12:59:31,511;5061000;17808
+02.09.2014 12:59:32,006;5061018;17808
+02.09.2014 12:59:32,519;5060987;17808
+02.09.2014 12:59:33,021;5061037;17808
+02.09.2014 12:59:33,536;5061050;17808
+02.09.2014 12:59:34,042;5060906;17808
+02.09.2014 12:59:34,551;5061050;17808
+02.09.2014 12:59:35,057;5061025;17808
+02.09.2014 12:59:35,571;5061100;17808
+02.09.2014 12:59:36,052;5060856;17808
+02.09.2014 12:59:36,571;5061012;17808
+02.09.2014 12:59:37,087;5061093;17808
+02.09.2014 12:59:37,606;5061118;17808
+02.09.2014 12:59:38,118;5060893;17808
+02.09.2014 12:59:38,637;5061000;17808
+02.09.2014 12:59:39,151;5060856;17808
+02.09.2014 12:59:39,652;5060931;17808
+02.09.2014 12:59:40,164;5061100;17808
+02.09.2014 12:59:40,671;5060906;17808
+02.09.2014 12:59:41,172;5061112;17808
+02.09.2014 12:59:41,691;5061006;17808
+02.09.2014 12:59:42,198;5060893;17808
+02.09.2014 12:59:42,696;5060887;17808
+02.09.2014 12:59:43,206;5060793;17808
+02.09.2014 12:59:43,702;5060887;17808
+02.09.2014 12:59:44,213;5060900;17808
+02.09.2014 12:59:44,729;5060862;17808
+02.09.2014 12:59:45,240;5060900;17808
+02.09.2014 12:59:45,736;5061000;17808
+02.09.2014 12:59:46,298;5061100;17808
+02.09.2014 12:59:46,745;5061000;17808
+02.09.2014 12:59:47,263;5061175;17808
+02.09.2014 12:59:47,771;5060993;17808
+02.09.2014 12:59:48,284;5060800;17808
+02.09.2014 12:59:48,780;5060900;17808
+02.09.2014 12:59:49,303;5061000;17808
+02.09.2014 12:59:49,794;5061000;17808
+02.09.2014 12:59:50,319;5061100;17808
+02.09.2014 12:59:50,803;5060912;17808
+02.09.2014 12:59:51,314;5060968;17808
+02.09.2014 12:59:51,829;5060793;17808
+02.09.2014 12:59:52,334;5061000;17808
+02.09.2014 12:59:52,841;5060931;17808
+02.09.2014 12:59:53,346;5060887;17808
+02.09.2014 12:59:53,843;5061112;17808
+02.09.2014 12:59:54,356;5061006;17808
+02.09.2014 12:59:54,875;5060993;17808
+02.09.2014 12:59:55,381;5060868;17808
+02.09.2014 12:59:55,893;5061006;17808
+02.09.2014 12:59:56,388;5060893;17808
+02.09.2014 12:59:56,905;5060981;17808
+02.09.2014 12:59:57,403;5061106;17808
+02.09.2014 12:59:57,915;5060981;17808
+02.09.2014 12:59:58,411;5060768;17808
+02.09.2014 12:59:58,925;5061000;17808
+02.09.2014 12:59:59,421;5061250;17808
+02.09.2014 12:59:59,952;5061000;17808
+02.09.2014 13:00:00,447;5060900;17808
+02.09.2014 13:00:00,960;5060893;17808
+02.09.2014 13:00:01,455;5061000;17808
+02.09.2014 13:00:01,974;5061093;17808
+02.09.2014 13:00:02,485;5061006;17808
+02.09.2014 13:00:02,982;5060900;17808
+02.09.2014 13:00:03,490;5061006;17808
+02.09.2014 13:00:04,000;5060900;17808
+02.09.2014 13:00:04,501;5061100;17808
+02.09.2014 13:00:05,013;5061006;17808
+02.09.2014 13:00:05,524;5060906;17808
+02.09.2014 13:00:06,023;5061162;17808
+02.09.2014 13:00:06,532;5060800;17808
+02.09.2014 13:00:07,049;5060993;17808
+02.09.2014 13:00:07,548;5061093;17808
+02.09.2014 13:00:08,057;5061006;17808
+02.09.2014 13:00:08,558;5060962;17808
+02.09.2014 13:00:09,072;5060893;17808
+02.09.2014 13:00:09,572;5061000;17808
+02.09.2014 13:00:10,088;5061000;17808
+02.09.2014 13:00:10,584;5061000;17808
+02.09.2014 13:00:11,096;5060900;17808
+02.09.2014 13:00:11,592;5060906;17808
+02.09.2014 13:00:12,122;5061012;17808
+02.09.2014 13:00:12,617;5060925;17808
+02.09.2014 13:00:13,131;5061031;17808
+02.09.2014 13:00:13,628;5061100;17808
+02.09.2014 13:00:14,140;5060887;17808
+02.09.2014 13:00:14,641;5061000;17808
+02.09.2014 13:00:15,153;5061118;17808
+02.09.2014 13:00:15,666;5061006;17808
+02.09.2014 13:00:16,183;5061100;17808
+02.09.2014 13:00:16,697;5061000;17808
+02.09.2014 13:00:17,213;5060918;17808
+02.09.2014 13:00:17,728;5060950;17808
+02.09.2014 13:00:18,240;5060893;17808
+02.09.2014 13:00:18,741;5060893;17808
+02.09.2014 13:00:19,248;5061100;17808
+02.09.2014 13:00:19,765;5061012;17808
+02.09.2014 13:00:20,257;5060912;17808
+02.09.2014 13:00:20,770;5061018;17808
+02.09.2014 13:00:21,266;5060906;17808
+02.09.2014 13:00:21,784;5061006;17808
+02.09.2014 13:00:22,281;5060993;17808
+02.09.2014 13:00:22,793;5061125;17808
+02.09.2014 13:00:23,300;5060887;17808
+02.09.2014 13:00:23,818;5061100;17808
+02.09.2014 13:00:24,315;5060968;17808
+02.09.2014 13:00:24,821;5061106;17808
+02.09.2014 13:00:25,333;5060768;17808
+02.09.2014 13:00:25,835;5060893;17808
+02.09.2014 13:00:26,347;5060993;17808
+02.09.2014 13:00:26,860;5061000;17808
+02.09.2014 13:00:27,372;5060900;17808
+02.09.2014 13:00:27,875;5060912;17808
+02.09.2014 13:00:28,387;5061100;17808
+02.09.2014 13:00:28,883;5061006;17808
+02.09.2014 13:00:29,396;5061100;17808
+02.09.2014 13:00:29,892;5061112;17808
+02.09.2014 13:00:30,423;5060900;17808
+02.09.2014 13:00:30,901;5061150;17808
+02.09.2014 13:00:31,435;5060793;17808
+02.09.2014 13:00:31,933;5061031;17808
+02.09.2014 13:00:32,450;5061006;17808
+02.09.2014 13:00:32,963;5061012;17808
+02.09.2014 13:00:33,490;5061112;17808
+02.09.2014 13:00:33,999;5060868;17808
+02.09.2014 13:00:34,493;5060893;17808
+02.09.2014 13:00:35,020;5061031;17808
+02.09.2014 13:00:35,512;5061000;17808
+02.09.2014 13:00:36,021;5061000;17808
+02.09.2014 13:00:36,538;5060868;17808
+02.09.2014 13:00:37,040;5061106;17808
+02.09.2014 13:00:37,541;5061000;17808
+02.09.2014 13:00:38,054;5060993;17808
+02.09.2014 13:00:38,558;5061100;17808
+02.09.2014 13:00:39,068;5060900;17808
+02.09.2014 13:00:39,577;5060962;17808
+02.09.2014 13:00:40,085;5061000;17808
+02.09.2014 13:00:40,591;5060981;17808
+02.09.2014 13:00:41,095;5061000;17808
+02.09.2014 13:00:41,611;5061106;17808
+02.09.2014 13:00:42,111;5061118;17808
+02.09.2014 13:00:42,600;5061000;17808
+02.09.2014 13:00:43,127;5061000;17808
+02.09.2014 13:00:43,632;5061112;17808
+02.09.2014 13:00:44,140;5060993;17808
+02.09.2014 13:00:44,634;5061150;17808
+02.09.2014 13:00:45,158;5060887;17808
+02.09.2014 13:00:45,669;5060993;17808
+02.09.2014 13:00:46,183;5061025;17808
+02.09.2014 13:00:46,692;5061100;17808
+02.09.2014 13:00:47,217;5061031;17808
+02.09.2014 13:00:47,730;5060893;17808
+02.09.2014 13:00:48,239;5060993;17808
+02.09.2014 13:00:48,748;5061100;17808
+02.09.2014 13:00:49,253;5061000;17808
+02.09.2014 13:00:49,750;5060800;17808
+02.09.2014 13:00:50,253;5061000;17808
+02.09.2014 13:00:50,764;5060862;17808
+02.09.2014 13:00:51,284;5060987;17808
+02.09.2014 13:00:51,783;5060887;17808
+02.09.2014 13:00:52,287;5061100;17808
+02.09.2014 13:00:52,797;5061087;17808
+02.09.2014 13:00:53,305;5060868;17808
+02.09.2014 13:00:53,814;5061000;17808
+02.09.2014 13:00:54,317;5060981;17808
+02.09.2014 13:00:54,829;5060900;17808
+02.09.2014 13:00:55,331;5060900;17808
+02.09.2014 13:00:55,841;5060993;17808
+02.09.2014 13:00:56,356;5060825;17808
+02.09.2014 13:00:56,862;5060987;17808
+02.09.2014 13:00:57,378;5061031;17808
+02.09.2014 13:00:57,869;5060900;17808
+02.09.2014 13:00:58,409;5060993;17808
+02.09.2014 13:00:58,888;5061087;17808
+02.09.2014 13:00:59,421;5060993;17808
+02.09.2014 13:00:59,900;5060993;17808
+02.09.2014 13:01:00,445;5061000;17808
+02.09.2014 13:01:00,903;5060775;17808
+02.09.2014 13:01:01,430;5060900;17808
+02.09.2014 13:01:01,940;5061006;17808
+02.09.2014 13:01:02,455;5061031;17808
+02.09.2014 13:01:02,964;5061181;17808
+02.09.2014 13:01:03,489;5060900;17808
+02.09.2014 13:01:03,998;5061018;17808
+02.09.2014 13:01:04,490;5061200;17808
+02.09.2014 13:01:05,015;5061093;17808
+02.09.2014 13:01:05,520;5060868;17808
+02.09.2014 13:01:06,031;5060887;17808
+02.09.2014 13:01:06,522;5060900;17808
+02.09.2014 13:01:07,033;5061031;17808
+02.09.2014 13:01:07,537;5061006;17808
+02.09.2014 13:01:08,061;5060900;17808
+02.09.2014 13:01:08,553;5060881;17808
+02.09.2014 13:01:09,061;5060993;17808
+02.09.2014 13:01:09,568;5061218;17808
+02.09.2014 13:01:10,082;5060843;17808
+02.09.2014 13:01:10,587;5060987;17808
+02.09.2014 13:01:11,095;5060900;17808
+02.09.2014 13:01:11,599;5060912;17808
+02.09.2014 13:01:12,108;5061106;17808
+02.09.2014 13:01:12,600;5061112;17808
+02.09.2014 13:01:13,129;5061000;17808
+02.09.2014 13:01:13,622;5061000;17808
+02.09.2014 13:01:14,137;5060987;17808
+02.09.2014 13:01:14,642;5060775;17808
+02.09.2014 13:01:15,156;5060900;17808
+02.09.2014 13:01:15,660;5061156;17808
+02.09.2014 13:01:16,169;5061000;17808
+02.09.2014 13:01:16,661;5061012;17808
+02.09.2014 13:01:17,181;5061100;17808
+02.09.2014 13:01:17,675;5060900;17808
+02.09.2014 13:01:18,199;5060900;17808
+02.09.2014 13:01:18,693;5060893;17808
+02.09.2014 13:01:19,201;5060781;17808
+02.09.2014 13:01:19,705;5061100;17808
+02.09.2014 13:01:20,233;5061000;17808
+02.09.2014 13:01:20,735;5060993;17808
+02.09.2014 13:01:21,235;5061000;17808
+02.09.2014 13:01:21,739;5061031;17808
+02.09.2014 13:01:22,250;5060787;17808
+02.09.2014 13:01:22,743;5061000;17808
+02.09.2014 13:01:23,270;5061000;17808
+02.09.2014 13:01:23,770;5061018;17808
+02.09.2014 13:01:24,280;5061000;17808
+02.09.2014 13:01:24,773;5061093;17808
+02.09.2014 13:01:25,297;5061100;17808
+02.09.2014 13:01:25,791;5061106;17808
+02.09.2014 13:01:26,310;5061000;17808
+02.09.2014 13:01:26,803;5061000;17808
+02.09.2014 13:01:27,311;5061006;17808
+02.09.2014 13:01:27,815;5060893;17808
+02.09.2014 13:01:28,332;5061006;17808
+02.09.2014 13:01:28,836;5061125;17808
+02.09.2014 13:01:29,349;5060987;17808
+02.09.2014 13:01:29,865;5060806;17808
+02.09.2014 13:01:30,372;5061012;17808
+02.09.2014 13:01:30,880;5060900;17808
+02.09.2014 13:01:31,374;5060887;17808
+02.09.2014 13:01:31,897;5061000;17808
+02.09.2014 13:01:32,401;5061125;17808
+02.09.2014 13:01:32,900;5060900;17808
+02.09.2014 13:01:33,403;5060893;17808
+02.09.2014 13:01:33,912;5061000;17808
+02.09.2014 13:01:34,421;5060881;17808
+02.09.2014 13:01:34,932;5060993;17808
+02.09.2014 13:01:35,435;5061000;17808
+02.09.2014 13:01:35,945;5060887;17808
+02.09.2014 13:01:36,449;5060993;17808
+02.09.2014 13:01:36,963;5061000;17808
+02.09.2014 13:01:37,467;5060975;17808
+02.09.2014 13:01:37,978;5060900;17808
+02.09.2014 13:01:38,481;5060893;17808
+02.09.2014 13:01:38,990;5061018;17808
+02.09.2014 13:01:39,504;5061100;17808
+02.09.2014 13:01:40,009;5061000;17808
+02.09.2014 13:01:40,502;5061093;17808
+02.09.2014 13:01:41,010;5060787;17808
+02.09.2014 13:01:41,515;5061106;17808
+02.09.2014 13:01:42,041;5061100;17808
+02.09.2014 13:01:42,530;5061000;17808
+02.09.2014 13:01:43,051;5060787;17808
+02.09.2014 13:01:43,548;5060750;17808
+02.09.2014 13:01:44,057;5060900;17808
+02.09.2014 13:01:44,561;5060968;17808
+02.09.2014 13:01:45,074;5061056;17808
+02.09.2014 13:01:45,571;5060943;17808
+02.09.2014 13:01:46,081;5061000;17808
+02.09.2014 13:01:46,594;5060987;17808
+02.09.2014 13:01:47,105;5061031;17808
+02.09.2014 13:01:47,607;5060887;17808
+02.09.2014 13:01:48,116;5061018;17808
+02.09.2014 13:01:48,622;5061000;17808
+02.09.2014 13:01:49,131;5060875;17808
+02.09.2014 13:01:49,641;5060856;17808
+02.09.2014 13:01:50,155;5060906;17808
+02.09.2014 13:01:50,643;5061025;17808
+02.09.2014 13:01:51,156;5061112;17808
+02.09.2014 13:01:51,656;5060881;17808
+02.09.2014 13:01:52,171;5060956;17808
+02.09.2014 13:01:52,670;5061025;17808
+02.09.2014 13:01:53,195;5060956;17808
+02.09.2014 13:01:53,689;5060893;17808
+02.09.2014 13:01:54,198;5060893;17808
+02.09.2014 13:01:54,709;5061100;17808
+02.09.2014 13:01:55,217;5061006;17808
+02.09.2014 13:01:55,711;5061000;17808
+02.09.2014 13:01:56,230;5060881;17808
+02.09.2014 13:01:56,740;5061100;17808
+02.09.2014 13:01:57,249;5060887;17808
+02.09.2014 13:01:57,741;5060993;17808
+02.09.2014 13:01:58,251;5061137;17808
+02.09.2014 13:01:58,755;5060893;17808
+02.09.2014 13:01:59,267;5061006;17808
+02.09.2014 13:01:59,768;5061137;17808
+02.09.2014 13:02:00,293;5061143;17808
+02.09.2014 13:02:00,792;5061000;17808
+02.09.2014 13:02:01,295;5061000;17808
+02.09.2014 13:02:01,813;5060875;17808
+02.09.2014 13:02:02,328;5060887;17808
+02.09.2014 13:02:02,819;5061000;17808
+02.09.2014 13:02:03,326;5060987;17808
+02.09.2014 13:02:03,834;5061000;17808
+02.09.2014 13:02:04,338;5061118;17808
+02.09.2014 13:02:04,853;5061250;17808
+02.09.2014 13:02:05,355;5061125;17808
+02.09.2014 13:02:05,867;5061118;17808
+02.09.2014 13:02:06,370;5060981;17808
+02.09.2014 13:02:06,910;5060837;17808
+02.09.2014 13:02:07,388;5060975;17808
+02.09.2014 13:02:07,923;5061168;17808
+02.09.2014 13:02:08,390;5061100;17808
+02.09.2014 13:02:08,949;5061200;17808
+02.09.2014 13:02:09,421;5061093;17808
+02.09.2014 13:02:09,966;5061143;17808
+02.09.2014 13:02:10,422;5061012;17808
+02.09.2014 13:02:10,932;5061100;17808
+02.09.2014 13:02:11,441;5060900;17808
+02.09.2014 13:02:11,951;5061093;17808
+02.09.2014 13:02:12,455;5060893;17808
+02.09.2014 13:02:12,967;5060893;17808
+02.09.2014 13:02:13,469;5061000;17808
+02.09.2014 13:02:13,980;5061100;17808
+02.09.2014 13:02:14,473;5060850;17808
+02.09.2014 13:02:14,998;5060781;17808
+02.09.2014 13:02:15,491;5060881;17808
+02.09.2014 13:02:16,011;5060993;17808
+02.09.2014 13:02:16,504;5060987;17808
+02.09.2014 13:02:17,030;5061225;17808
+02.09.2014 13:02:17,528;5060993;17808
+02.09.2014 13:02:18,038;5060906;17808
+02.09.2014 13:02:18,536;5060900;17808
+02.09.2014 13:02:19,044;5060781;17808
+02.09.2014 13:02:19,554;5061006;17808
+02.09.2014 13:02:20,062;5060750;17808
+02.09.2014 13:02:20,569;5060906;17808
+02.09.2014 13:02:21,088;5061093;17808
+02.09.2014 13:02:21,587;5060987;17808
+02.09.2014 13:02:22,096;5061006;17808
+02.09.2014 13:02:22,600;5061000;17808
+02.09.2014 13:02:23,109;5060993;17808
+02.09.2014 13:02:23,602;5060900;17808
+02.09.2014 13:02:24,111;5061100;17808
+02.09.2014 13:02:24,615;5060875;17808
+02.09.2014 13:02:25,134;5060950;17808
+02.09.2014 13:02:25,634;5061012;17808
+02.09.2014 13:02:26,144;5060900;17808
+02.09.2014 13:02:26,648;5060956;17808
+02.09.2014 13:02:27,163;5060800;17808
+02.09.2014 13:02:27,671;5060993;17808
+02.09.2014 13:02:28,178;5061000;17808
+02.09.2014 13:02:28,671;5061131;17808
+02.09.2014 13:02:29,179;5061156;17808
+02.09.2014 13:02:29,687;5060881;17808
+02.09.2014 13:02:30,208;5061000;17808
+02.09.2014 13:02:30,700;5060893;17808
+02.09.2014 13:02:31,226;5060900;17808
+02.09.2014 13:02:31,737;5060975;17808
+02.09.2014 13:02:32,241;5060893;17808
+02.09.2014 13:02:32,751;5061150;17808
+02.09.2014 13:02:33,244;5061200;17808
+02.09.2014 13:02:33,771;5060900;17808
+02.09.2014 13:02:34,261;5060737;17808
+02.09.2014 13:02:34,779;5061000;17808
+02.09.2014 13:02:35,270;5061012;17808
+02.09.2014 13:02:35,781;5060893;17808
+02.09.2014 13:02:36,283;5061000;17808
+02.09.2014 13:02:36,809;5060893;17808
+02.09.2014 13:02:37,302;5061243;17808
+02.09.2014 13:02:37,811;5060993;17808
+02.09.2014 13:02:38,317;5061118;17808
+02.09.2014 13:02:38,832;5060993;17808
+02.09.2014 13:02:39,344;5061100;17808
+02.09.2014 13:02:39,847;5061087;17808
+02.09.2014 13:02:40,340;5061118;17808
+02.09.2014 13:02:40,860;5060881;17808
+02.09.2014 13:02:41,368;5061106;17808
+02.09.2014 13:02:41,877;5061000;17808
+02.09.2014 13:02:42,374;5061081;17808
+02.09.2014 13:02:42,881;5060787;17808
+02.09.2014 13:02:43,385;5061100;17808
+02.09.2014 13:02:43,900;5060893;17808
+02.09.2014 13:02:44,399;5061000;17808
+02.09.2014 13:02:44,913;5061018;17808
+02.09.2014 13:02:45,415;5060893;17808
+02.09.2014 13:02:45,927;5060875;17808
+02.09.2014 13:02:46,431;5060781;17808
+02.09.2014 13:02:46,946;5060962;17808
+02.09.2014 13:02:47,439;5060868;17808
+02.09.2014 13:02:47,950;5061000;17808
+02.09.2014 13:02:48,459;5061000;17808
+02.09.2014 13:02:48,976;5061100;17808
+02.09.2014 13:02:49,467;5060993;17808
+02.09.2014 13:02:49,988;5060968;17808
+02.09.2014 13:02:50,495;5061081;17808
+02.09.2014 13:02:50,989;5061000;17808
+02.09.2014 13:02:51,497;5061018;17808
+02.09.2014 13:02:52,015;5060993;17808
+02.09.2014 13:02:52,512;5061200;17808
+02.09.2014 13:02:53,026;5061025;17808
+02.09.2014 13:02:53,521;5061018;17808
+02.09.2014 13:02:54,051;5060887;17808
+02.09.2014 13:02:54,532;5060881;17808
+02.09.2014 13:02:55,049;5060787;17808
+02.09.2014 13:02:55,556;5061000;17808
+02.09.2014 13:02:56,069;5061231;17808
+02.09.2014 13:02:56,568;5060900;17808
+02.09.2014 13:02:57,084;5060993;17808
+02.09.2014 13:02:57,581;5060925;17808
+02.09.2014 13:02:58,093;5060900;17808
+02.09.2014 13:02:58,591;5060843;17808
+02.09.2014 13:02:59,120;5060887;17808
+02.09.2014 13:02:59,601;5061031;17808
+02.09.2014 13:03:00,129;5060793;17808
+02.09.2014 13:03:00,625;5060843;17808
+02.09.2014 13:03:01,137;5061031;17808
+02.09.2014 13:03:01,633;5060850;17808
+02.09.2014 13:03:02,152;5061018;17808
+02.09.2014 13:03:02,649;5061106;17808
+02.09.2014 13:03:03,161;5061018;17808
+02.09.2014 13:03:03,684;5061000;17808
+02.09.2014 13:03:04,187;5061112;17808
+02.09.2014 13:03:04,704;5060906;17808
+02.09.2014 13:03:05,195;5060906;17808
+02.09.2014 13:03:05,708;5060906;17808
+02.09.2014 13:03:06,210;5060793;17808
+02.09.2014 13:03:06,729;5060900;17808
+02.09.2014 13:03:07,227;5061012;17808
+02.09.2014 13:03:07,740;5061012;17808
+02.09.2014 13:03:08,238;5060843;17808
+02.09.2014 13:03:08,754;5061106;17808
+02.09.2014 13:03:09,260;5060993;17808
+02.09.2014 13:03:09,761;5060912;17808
+02.09.2014 13:03:10,269;5060993;17808
+02.09.2014 13:03:10,774;5061000;17808
+02.09.2014 13:03:11,281;5060900;17808
+02.09.2014 13:03:11,792;5060993;17808
+02.09.2014 13:03:12,298;5061131;17808
+02.09.2014 13:03:12,813;5100100;18918
+02.09.2014 13:03:13,309;5237968;25848
+02.09.2014 13:03:13,820;5496956;33098
+02.09.2014 13:03:14,331;5870800;40325
+02.09.2014 13:03:14,875;6381937;48563
+02.09.2014 13:03:15,334;6750131;53953
+02.09.2014 13:03:15,889;7232012;60918
+02.09.2014 13:03:16,357;7676131;67779
+02.09.2014 13:03:16,911;8155200;74598
+02.09.2014 13:03:17,376;8636568;80779
+02.09.2014 13:03:17,875;9080200;87366
+02.09.2014 13:03:18,392;9560112;93912
+02.09.2014 13:03:18,903;10003981;100302
+02.09.2014 13:03:19,414;10482175;106645
+02.09.2014 13:03:19,906;10926000;111848
+02.09.2014 13:03:20,407;11406918;117947
+02.09.2014 13:03:20,921;11848462;123912
+02.09.2014 13:03:21,419;12328743;129686
+02.09.2014 13:03:21,932;12772781;135279
+02.09.2014 13:03:22,432;13214287;140308
+02.09.2014 13:03:22,946;13695518;145563
+02.09.2014 13:03:23,459;14175125;150226
+02.09.2014 13:03:23,968;14655150;155453
+02.09.2014 13:03:24,461;15099000;159674
+02.09.2014 13:03:24,973;15578112;163941
+02.09.2014 13:03:25,484;16021037;167604
+02.09.2014 13:03:25,979;16425381;171087
+02.09.2014 13:03:26,486;16747143;173872
+02.09.2014 13:03:27,003;16953762;174994
+02.09.2014 13:03:27,509;17043087;175005
+02.09.2014 13:03:28,021;17047406;175011
+02.09.2014 13:03:28,524;17048575;175011
+02.09.2014 13:03:29,032;17047862;175005
+02.09.2014 13:03:29,539;17047181;175005
+02.09.2014 13:03:30,049;17047168;175005
+02.09.2014 13:03:30,549;17047093;175005
+02.09.2014 13:03:31,054;17047100;175005
+02.09.2014 13:03:31,549;17047218;175005
+02.09.2014 13:03:32,076;17046993;175005
+02.09.2014 13:03:32,589;17046887;175005
+02.09.2014 13:03:33,105;17047000;175005
+02.09.2014 13:03:33,616;17046943;175005
+02.09.2014 13:03:34,133;17046912;175005
+02.09.2014 13:03:34,645;17046900;175005
+02.09.2014 13:03:35,161;17046887;175005
+02.09.2014 13:03:35,667;17046881;175005
+02.09.2014 13:03:36,178;17046850;174122
+02.09.2014 13:03:36,676;17046787;203883
+02.09.2014 13:03:37,190;17046800;214319
+02.09.2014 13:03:37,696;17046662;218523
+02.09.2014 13:03:38,207;17046906;267127
+02.09.2014 13:03:38,708;17046687;285110
+02.09.2014 13:03:39,214;17046900;285046
+02.09.2014 13:03:39,722;17046906;285029
+02.09.2014 13:03:40,234;17046612;285023
+02.09.2014 13:03:40,730;17046906;285023
+02.09.2014 13:03:41,243;17046606;285023
+02.09.2014 13:03:41,749;17046800;285023
+02.09.2014 13:03:42,268;17046806;285023
+02.09.2014 13:03:42,755;17046793;285023
+02.09.2014 13:03:43,271;17046906;285023
+02.09.2014 13:03:43,777;17046593;285023
+02.09.2014 13:03:44,289;17046518;285023
+02.09.2014 13:03:44,784;17046700;285023
+02.09.2014 13:03:45,300;17046906;285023
+02.09.2014 13:03:45,807;17047025;285023
+02.09.2014 13:03:46,320;17046687;285023
+02.09.2014 13:03:46,815;17046831;285023
+02.09.2014 13:03:47,332;17046800;285023
+02.09.2014 13:03:47,838;17046762;285023
+02.09.2014 13:03:48,341;17047025;285023
+02.09.2014 13:03:48,847;17046912;285023
+02.09.2014 13:03:49,359;17046931;285023
+02.09.2014 13:03:49,858;17046800;285023
+02.09.2014 13:03:50,371;17047018;285023
+02.09.2014 13:03:50,877;17046950;285023
+02.09.2014 13:03:51,388;17046687;285023
+02.09.2014 13:03:51,885;17042162;283697
+02.09.2014 13:03:52,401;16936993;278755
+02.09.2014 13:03:52,891;16739600;273058
+02.09.2014 13:03:53,420;16416181;267436
+02.09.2014 13:03:53,914;16016562;262052
+02.09.2014 13:03:54,430;15532475;255970
+02.09.2014 13:03:54,925;15089306;250866
+02.09.2014 13:03:55,445;14607693;245290
+02.09.2014 13:03:55,941;14164875;240186
+02.09.2014 13:03:56,453;13685162;234784
+02.09.2014 13:03:56,960;13204593;229511
+02.09.2014 13:03:57,486;12763468;224127
+02.09.2014 13:03:57,970;12320106;219389
+02.09.2014 13:03:58,491;11801600;214244
+02.09.2014 13:03:58,987;11359275;209447
+02.09.2014 13:03:59,498;10878787;204476
+02.09.2014 13:03:59,993;10472400;199866
+02.09.2014 13:04:00,510;9993193;194674
+02.09.2014 13:04:01,018;9512081;190261
+02.09.2014 13:04:01,531;9033506;185546
+02.09.2014 13:04:02,024;8590906;181395
+02.09.2014 13:04:02,543;8109437;176790
+02.09.2014 13:04:03,031;7704206;172796
+02.09.2014 13:04:03,560;7187318;168191
+02.09.2014 13:04:04,053;6743968;164383
+02.09.2014 13:04:04,566;6263937;160360
+02.09.2014 13:04:05,060;5820193;156767
+02.09.2014 13:04:05,589;5340837;152715
+02.09.2014 13:04:06,089;4897875;149430
+02.09.2014 13:04:06,601;4416343;145976
+02.09.2014 13:04:07,112;3973687;142738
+02.09.2014 13:04:07,620;3494537;139709
+02.09.2014 13:04:08,131;3013193;136936
+02.09.2014 13:04:08,628;2571093;134517
+02.09.2014 13:04:09,138;2091368;132401
+02.09.2014 13:04:09,648;1611206;131994
+02.09.2014 13:04:10,149;1209406;131988
+02.09.2014 13:04:10,658;910637;131988
+02.09.2014 13:04:11,160;697512;131988
+02.09.2014 13:04:11,659;603437;131988
+02.09.2014 13:04:12,186;596231;131988
+02.09.2014 13:04:12,693;595506;131988
+02.09.2014 13:04:13,193;595268;131988
+02.09.2014 13:04:13,699;595275;131988
+02.09.2014 13:04:14,199;595506;131988
+02.09.2014 13:04:14,712;596143;131988
+02.09.2014 13:04:15,224;595662;131988
+02.09.2014 13:04:15,718;595887;131988
+02.09.2014 13:04:16,230;596200;131988
+02.09.2014 13:04:16,743;595643;131988
+02.09.2014 13:04:17,254;595831;131988
+02.09.2014 13:04:17,749;595668;131988
+02.09.2014 13:04:18,260;596331;131988
+02.09.2014 13:04:18,773;595693;131988
+02.09.2014 13:04:19,289;596318;131988
+02.09.2014 13:04:19,801;596200;131988
+02.09.2014 13:04:20,318;596125;130889
+02.09.2014 13:04:20,832;595981;93331
+02.09.2014 13:04:21,343;596281;56430
+02.09.2014 13:04:21,856;596187;50447
+02.09.2014 13:04:22,348;595912;46860
+02.09.2014 13:04:22,866;596218;13122
+02.09.2014 13:04:23,368;596100;12308
+02.09.2014 13:04:23,896;595900;12389
+02.09.2014 13:04:24,393;596218;12377
+02.09.2014 13:04:24,915;595800;12377
+02.09.2014 13:04:25,451;596112;12377
+02.09.2014 13:04:25,943;595931;12377
+02.09.2014 13:04:26,478;595487;12377
+02.09.2014 13:04:26,956;596125;12377
+02.09.2014 13:04:27,490;596100;12377
+02.09.2014 13:04:27,966;596100;12377
+02.09.2014 13:04:28,518;595906;12377
+02.09.2014 13:04:28,982;595862;12377
+02.09.2014 13:04:29,489;596400;12377
+02.09.2014 13:04:29,993;596175;12377
+02.09.2014 13:04:30,515;595918;12377
+02.09.2014 13:04:31,010;596043;12377
+02.09.2014 13:04:31,519;596287;12377
+02.09.2014 13:04:32,024;596318;12377
+02.09.2014 13:04:32,538;596081;12377
+02.09.2014 13:04:33,041;595687;12377
+02.09.2014 13:04:33,550;595787;12377
+02.09.2014 13:04:34,051;596043;12377
+02.09.2014 13:04:34,563;595762;12377
+02.09.2014 13:04:35,071;596118;12377
+02.09.2014 13:04:35,580;596162;12377
+02.09.2014 13:04:36,083;596100;12377
+02.09.2014 13:04:36,591;595993;12377
+02.09.2014 13:04:37,113;595918;12377
+02.09.2014 13:04:37,602;596025;12377
+02.09.2014 13:04:38,126;595862;12377
+02.09.2014 13:04:38,619;595762;12377
+02.09.2014 13:04:39,130;595881;12377
+02.09.2014 13:04:39,633;596100;12377
+02.09.2014 13:04:40,148;595850;12377
+02.09.2014 13:04:40,652;596425;12377
+02.09.2014 13:04:41,160;596431;12377
+02.09.2014 13:04:41,663;596100;12377
+02.09.2014 13:04:42,185;595850;12377
+02.09.2014 13:04:42,675;596462;12377
+02.09.2014 13:04:43,194;596106;12377
+02.09.2014 13:04:43,692;595987;12377
+02.09.2014 13:04:44,204;595993;12377
+02.09.2014 13:04:44,701;595400;12377
+02.09.2014 13:04:45,218;595881;12377
+02.09.2014 13:04:45,727;595700;12377
+02.09.2014 13:04:46,238;596075;12377
+02.09.2014 13:04:46,736;596031;12377
+02.09.2014 13:04:47,253;595987;12377
+02.09.2014 13:04:47,743;595900;12377
+02.09.2014 13:04:48,258;596050;12377
+02.09.2014 13:04:48,766;595987;11941
+02.09.2014 13:04:49,276;586743;28895
+02.09.2014 13:04:49,771;587225;130040
+02.09.2014 13:04:50,297;587700;342726
+02.09.2014 13:04:50,793;588087;642558
+02.09.2014 13:04:51,303;588000;915302
+02.09.2014 13:04:51,799;588650;1105860
+02.09.2014 13:04:52,328;587600;1212470
+02.09.2014 13:04:52,819;587581;1220680
+02.09.2014 13:04:53,334;588231;1220511
+02.09.2014 13:04:53,830;588212;1220476
+02.09.2014 13:04:54,348;587656;1220476
+02.09.2014 13:04:54,847;587412;1220476
+02.09.2014 13:04:55,365;587912;1220476
+02.09.2014 13:04:55,869;588006;1220476
+02.09.2014 13:04:56,369;587912;1220476
+02.09.2014 13:04:56,874;587743;1220476
+02.09.2014 13:04:57,398;587900;1220476
+02.09.2014 13:04:57,881;587881;1220476
+02.09.2014 13:04:58,408;587975;1220476
+02.09.2014 13:04:58,903;588325;1220476
+02.09.2014 13:04:59,422;587962;1219901
+02.09.2014 13:04:59,918;588550;1253488
+02.09.2014 13:05:00,433;587787;1259784
+02.09.2014 13:05:00,929;588300;1263500
+02.09.2014 13:05:01,440;587575;1298593
+02.09.2014 13:05:01,948;588100;1340325
+02.09.2014 13:05:02,465;588012;1340569
+02.09.2014 13:05:02,954;588131;1340529
+02.09.2014 13:05:03,470;588118;1340529
+02.09.2014 13:05:03,976;588231;1340529
+02.09.2014 13:05:04,488;588237;1340529
+02.09.2014 13:05:04,993;588018;1340529
+02.09.2014 13:05:05,498;588406;1340529
+02.09.2014 13:05:06,005;588093;1340529
+02.09.2014 13:05:06,520;588418;1340529
+02.09.2014 13:05:07,014;588100;1340529
+02.09.2014 13:05:07,532;588368;1340529
+02.09.2014 13:05:08,046;588106;1340529
+02.09.2014 13:05:08,541;588006;1340529
+02.09.2014 13:05:09,050;588100;1340529
+02.09.2014 13:05:09,558;588312;1340529
+02.09.2014 13:05:10,073;588625;1340529
+02.09.2014 13:05:10,569;588087;1340529
+02.09.2014 13:05:11,079;587975;1340529
+02.09.2014 13:05:11,592;587993;1339686
+02.09.2014 13:05:12,103;652775;1326465
+02.09.2014 13:05:12,598;815931;1295156
+02.09.2014 13:05:13,110;1102662;1261604
+02.09.2014 13:05:13,618;1467493;1228209
+02.09.2014 13:05:14,129;1948087;1194819
+02.09.2014 13:05:14,624;2392700;1163994
+02.09.2014 13:05:15,143;2872800;1130639
+02.09.2014 13:05:15,639;3353881;1097441
+02.09.2014 13:05:16,156;3797931;1064238
+02.09.2014 13:05:16,650;4240075;1033593
+02.09.2014 13:05:17,168;4720456;1000581
+02.09.2014 13:05:17,677;5201556;967616
+02.09.2014 13:05:18,186;5680062;934627
+02.09.2014 13:05:18,680;6123625;904273
+02.09.2014 13:05:19,192;6566193;871465
+02.09.2014 13:05:19,703;7046712;838790
+02.09.2014 13:05:20,212;7490237;808697
+02.09.2014 13:05:20,709;7968818;776122
+02.09.2014 13:05:21,233;8449550;743674
+02.09.2014 13:05:21,728;8893656;713790
+02.09.2014 13:05:22,244;9371868;681377
+02.09.2014 13:05:22,739;9815837;649156
+02.09.2014 13:05:23,250;10258656;619587
+02.09.2014 13:05:23,757;10738362;587627
+02.09.2014 13:05:24,278;11218962;555767
+02.09.2014 13:05:24,764;11660912;526406
+02.09.2014 13:05:25,279;12141237;494726
+02.09.2014 13:05:25,786;12622118;463197
+02.09.2014 13:05:26,296;13064043;431970
+02.09.2014 13:05:26,792;13507300;403273
+02.09.2014 13:05:27,320;13988650;372279
+02.09.2014 13:05:27,817;14466087;341534
+02.09.2014 13:05:28,327;14910100;313377
+02.09.2014 13:05:28,824;15391481;283191
+02.09.2014 13:05:29,334;15833556;253401
+02.09.2014 13:05:29,845;16291693;224046
+02.09.2014 13:05:30,357;16609087;197273
+02.09.2014 13:05:30,851;16841931;165168
+02.09.2014 13:05:31,366;16956837;129953
+02.09.2014 13:05:31,868;16975131;129784
+02.09.2014 13:05:32,380;16976000;129877
+02.09.2014 13:05:32,888;16974731;129877
+02.09.2014 13:05:33,424;16974175;129877
+02.09.2014 13:05:33,900;16974187;129877
+02.09.2014 13:05:34,457;16973881;129877
+02.09.2014 13:05:34,932;16973887;129877
+02.09.2014 13:05:35,487;16974006;129877
+02.09.2014 13:05:35,959;16973906;129877
+02.09.2014 13:05:36,504;16973906;129877
+02.09.2014 13:05:36,984;16974000;129877
+02.09.2014 13:05:37,528;16973906;129877
+02.09.2014 13:05:37,991;16973900;129877
+02.09.2014 13:05:38,497;16973900;129877
+02.09.2014 13:05:39,018;16973900;129877
+02.09.2014 13:05:39,528;16973793;129877
+02.09.2014 13:05:40,023;16973912;129877
+02.09.2014 13:05:40,536;16973800;129877
+02.09.2014 13:05:41,047;16973781;129877
+02.09.2014 13:05:41,549;16973787;129877
+02.09.2014 13:05:42,061;16973900;129877
+02.09.2014 13:05:42,580;16973900;129877
+02.09.2014 13:05:43,091;16974006;128877
+02.09.2014 13:05:43,620;16973900;92151
+02.09.2014 13:05:44,118;16973800;63645
+02.09.2014 13:05:44,632;16973900;57686
+02.09.2014 13:05:45,143;16973887;53837
+02.09.2014 13:05:45,647;16974525;20831
+02.09.2014 13:05:46,148;16974281;20639
+02.09.2014 13:05:46,652;16974300;20761
+02.09.2014 13:05:47,165;16974356;20750
+02.09.2014 13:05:47,664;16974393;20750
+02.09.2014 13:05:48,184;16974400;20750
+02.09.2014 13:05:48,688;16974362;20750
+02.09.2014 13:05:49,189;16974406;20750
+02.09.2014 13:05:49,694;16974387;20750
+02.09.2014 13:05:50,212;16974393;20750
+02.09.2014 13:05:50,718;16974381;20750
+02.09.2014 13:05:51,232;16974400;20750
+02.09.2014 13:05:51,727;16974293;20750
+02.09.2014 13:05:52,229;16974400;20750
+02.09.2014 13:05:52,749;16974400;20750
+02.09.2014 13:05:53,257;16974393;20750
+02.09.2014 13:05:53,759;16974312;20750
+02.09.2014 13:05:54,257;16974400;20750
+02.09.2014 13:05:54,765;16938506;28715
+02.09.2014 13:05:55,281;16785506;91063
+02.09.2014 13:05:55,777;16544493;158180
+02.09.2014 13:05:56,299;16173381;236308
+02.09.2014 13:05:56,793;15736750;303116
+02.09.2014 13:05:57,308;15258056;375511
+02.09.2014 13:05:57,798;14813312;442261
+02.09.2014 13:05:58,326;14332868;520127
+02.09.2014 13:05:58,822;13890106;586895
+02.09.2014 13:05:59,333;13408450;659034
+02.09.2014 13:05:59,843;12930162;731058
+02.09.2014 13:06:00,352;12487987;797563
+02.09.2014 13:06:00,847;12043068;869529
+02.09.2014 13:06:01,358;11561968;935784
+02.09.2014 13:06:01,863;11082900;1007581
+02.09.2014 13:06:02,380;10639662;1079238
+02.09.2014 13:06:02,886;10160037;1150709
+02.09.2014 13:06:03,396;9680018;1222098
+02.09.2014 13:06:03,899;9236500;1287970
+02.09.2014 13:06:04,430;8757293;1364703
+02.09.2014 13:06:04,930;8313337;1430220
+02.09.2014 13:06:05,458;7797106;1506627
+02.09.2014 13:06:05,968;7354493;1571750
+02.09.2014 13:06:06,468;6910556;1636802
+02.09.2014 13:06:06,979;6431775;1707151
+02.09.2014 13:06:07,485;5950656;1777168
+02.09.2014 13:06:07,994;5507306;1847017
+02.09.2014 13:06:08,488;5066131;1911011
+02.09.2014 13:06:09,017;4657737;1979500
+02.09.2014 13:06:09,505;4367300;2076000
+02.09.2014 13:06:10,019;4202287;2159819
+02.09.2014 13:06:10,532;4133256;2168151
+02.09.2014 13:06:11,044;4135237;2168000
+02.09.2014 13:06:11,554;4135787;2167965
+02.09.2014 13:06:12,067;4135825;2167965
+02.09.2014 13:06:12,579;4136312;2167965
+02.09.2014 13:06:13,089;4136706;2167965
+02.09.2014 13:06:13,617;4136787;2167965
+02.09.2014 13:06:14,127;4137000;2167965
+02.09.2014 13:06:14,628;4137000;2167965
+02.09.2014 13:06:15,139;4136993;2167965
+02.09.2014 13:06:15,644;4136993;2167965
+02.09.2014 13:06:16,154;4137000;2167965
+02.09.2014 13:06:16,649;4137000;2167965
+02.09.2014 13:06:17,159;4136887;2167005
+02.09.2014 13:06:17,671;4137000;2196715
+02.09.2014 13:06:18,191;4136993;2207761
+02.09.2014 13:06:18,686;4136993;2210947
+02.09.2014 13:06:19,197;4136881;2245197
+02.09.2014 13:06:19,701;4137106;2278017
+02.09.2014 13:06:20,211;4137100;2278011
+02.09.2014 13:06:20,710;4137100;2278000
+02.09.2014 13:06:21,222;4137093;2278000
+02.09.2014 13:06:21,729;4137000;2278000
+02.09.2014 13:06:22,228;4137093;2278000
+02.09.2014 13:06:22,739;4136993;2278000
+02.09.2014 13:06:23,250;4137112;2278000
+02.09.2014 13:06:23,757;4137025;2278000
+02.09.2014 13:06:24,267;4137106;2278000
+02.09.2014 13:06:24,773;4136993;2278000
+02.09.2014 13:06:25,278;4136987;2278000
+02.09.2014 13:06:25,784;4137100;2278000
+02.09.2014 13:06:26,297;4136987;2278000
+02.09.2014 13:06:26,791;4137093;2278000
+02.09.2014 13:06:27,302;4136981;2278000
+02.09.2014 13:06:27,797;4137106;2278000
+02.09.2014 13:06:28,326;4136987;2278000
+02.09.2014 13:06:28,819;4137006;2278000
+02.09.2014 13:06:29,333;4137100;2278000
+02.09.2014 13:06:29,844;4134325;2276947
+02.09.2014 13:06:30,355;4046156;2314500
+02.09.2014 13:06:30,848;3843312;2442343
+02.09.2014 13:06:31,360;3554406;2683895
+02.09.2014 13:06:31,866;3167743;2967656
+02.09.2014 13:06:32,375;2683493;3300453
+02.09.2014 13:06:32,870;2241700;3585779
+02.09.2014 13:06:33,397;1723125;3894877
+02.09.2014 13:06:33,893;1296600;4180093
+02.09.2014 13:06:34,403;950837;4435023
+02.09.2014 13:06:34,922;731556;4580023
+02.09.2014 13:06:35,427;605287;4619110
+02.09.2014 13:06:35,924;584662;4619011
+02.09.2014 13:06:36,438;585187;4619011
+02.09.2014 13:06:36,932;587781;4619000
+02.09.2014 13:06:37,444;586993;4619000
+02.09.2014 13:06:37,955;587656;4619000
+02.09.2014 13:06:38,466;587887;4619000
+02.09.2014 13:06:38,974;587968;4619000
+02.09.2014 13:06:39,469;588200;4619000
+02.09.2014 13:06:39,976;588025;4619000
+02.09.2014 13:06:40,492;587475;4619000
+02.09.2014 13:06:41,002;587800;4619000
+02.09.2014 13:06:41,497;588162;4619000
+02.09.2014 13:06:42,008;588112;4619000
+02.09.2014 13:06:42,519;587712;4619000
+02.09.2014 13:06:43,033;587862;4619000
+02.09.2014 13:06:43,539;587812;4618383
+02.09.2014 13:06:44,038;588243;4605697
+02.09.2014 13:06:44,598;588131;4544604
+02.09.2014 13:06:45,062;587912;4539982
+02.09.2014 13:06:45,604;587962;4533994
+02.09.2014 13:06:46,077;588018;4510250
+02.09.2014 13:06:46,571;587787;4498726
+02.09.2014 13:06:47,092;587875;4499139
+02.09.2014 13:06:47,594;587793;4498761
+02.09.2014 13:06:48,103;588081;4498761
+02.09.2014 13:06:48,597;588006;4498761
+02.09.2014 13:06:49,108;588000;4498761
+02.09.2014 13:06:49,632;587731;4498761
+02.09.2014 13:06:50,131;587587;4498761
+02.09.2014 13:06:50,629;588250;4498761
+02.09.2014 13:06:51,142;587662;4498761
+02.09.2014 13:06:51,638;588000;4498761
+02.09.2014 13:06:52,153;587937;4498761
+02.09.2014 13:06:52,657;587931;4498761
+02.09.2014 13:06:53,168;587975;4498761
+02.09.2014 13:06:53,674;624750;4492936
+02.09.2014 13:06:54,184;776787;4395750
+02.09.2014 13:06:54,699;1019275;4251773
+02.09.2014 13:06:55,206;1391843;4093500
+02.09.2014 13:06:55,707;1827062;3946087
+02.09.2014 13:06:56,210;2270718;3786738
+02.09.2014 13:06:56,722;2752162;3627215
+02.09.2014 13:06:57,227;3233118;3467877
+02.09.2014 13:06:57,721;3676956;3320802
+02.09.2014 13:06:58,247;4155856;3161488
+02.09.2014 13:06:58,757;4637118;3002145
+02.09.2014 13:06:59,273;5080693;2842883
+02.09.2014 13:06:59,784;5558918;2683697
+02.09.2014 13:07:00,299;6039937;2524877
+02.09.2014 13:07:00,809;6482906;2378430
+02.09.2014 13:07:01,322;6961962;2219744
+02.09.2014 13:07:01,832;7442975;2060895
+02.09.2014 13:07:02,339;7922018;1914191
+02.09.2014 13:07:02,847;8364631;1755319
+02.09.2014 13:07:03,342;8808712;1597063
+02.09.2014 13:07:03,852;9288268;1439040
+02.09.2014 13:07:04,366;9731512;1293331
+02.09.2014 13:07:04,873;10211481;1135424
+02.09.2014 13:07:05,384;10691237;977354
+02.09.2014 13:07:05,899;11134456;831377
+02.09.2014 13:07:06,408;11614275;661494
+02.09.2014 13:07:06,938;12089968;491587
+02.09.2014 13:07:07,437;12443531;266395
+02.09.2014 13:07:07,949;12716456;88563
+02.09.2014 13:07:08,463;12873187;18406
+02.09.2014 13:07:08,961;12915981;16779
+02.09.2014 13:07:09,482;12915100;16906
+02.09.2014 13:07:09,977;12913118;16895
+02.09.2014 13:07:10,485;12912925;16895
+02.09.2014 13:07:10,980;12912418;16895
+02.09.2014 13:07:11,506;12912093;16895
+02.09.2014 13:07:12,000;12911993;16895
+02.09.2014 13:07:12,511;12911881;16895
+02.09.2014 13:07:13,028;12912156;16895
+02.09.2014 13:07:13,539;12912200;16895
+02.09.2014 13:07:14,065;12912106;16895
+02.09.2014 13:07:14,576;12912000;16895
+02.09.2014 13:07:15,092;12911512;16895
+02.09.2014 13:07:15,603;12912006;16895
+02.09.2014 13:07:16,108;12911693;28319
+02.09.2014 13:07:16,625;12911168;53656
+02.09.2014 13:07:17,130;12911081;59674
+02.09.2014 13:07:17,630;12911400;68970
+02.09.2014 13:07:18,135;12911687;130563
+02.09.2014 13:07:18,646;12911300;137087
+02.09.2014 13:07:19,169;12911650;136744
+02.09.2014 13:07:19,657;12911093;136744
+02.09.2014 13:07:20,163;12911200;136744
+02.09.2014 13:07:20,692;12911375;136744
+02.09.2014 13:07:21,184;12911431;136744
+02.09.2014 13:07:21,695;12911531;136744
+02.09.2014 13:07:22,189;12911400;136744
+02.09.2014 13:07:22,704;12911193;136744
+02.09.2014 13:07:23,206;12911512;136744
+02.09.2014 13:07:23,719;12911012;136744
+02.09.2014 13:07:24,216;12911400;136744
+02.09.2014 13:07:24,732;12911168;136744
+02.09.2014 13:07:25,229;12911293;136744
+02.09.2014 13:07:25,746;12911425;136744
+02.09.2014 13:07:26,239;12911187;136744
+02.09.2014 13:07:26,767;12911118;136744
+02.09.2014 13:07:27,276;12911331;136744
+02.09.2014 13:07:27,776;12911393;136744
+02.09.2014 13:07:28,280;12911612;136744
+02.09.2014 13:07:28,780;12911175;136744
+02.09.2014 13:07:29,292;12919300;135482
+02.09.2014 13:07:29,804;13031806;186383
+02.09.2014 13:07:30,298;13236906;343843
+02.09.2014 13:07:30,811;13537931;607982
+02.09.2014 13:07:31,324;13973906;916441
+02.09.2014 13:07:31,837;14456250;1201802
+02.09.2014 13:07:32,332;14902237;1510901
+02.09.2014 13:07:32,843;15383100;1796186
+02.09.2014 13:07:33,339;15825243;2105255
+02.09.2014 13:07:33,850;16258343;2414377
+02.09.2014 13:07:34,356;16614950;2723441
+02.09.2014 13:07:34,872;16856043;3008761
+02.09.2014 13:07:35,383;16978950;3317848
+02.09.2014 13:07:35,900;17004837;3626930
+02.09.2014 13:07:36,409;17005025;3912232
+02.09.2014 13:07:36,927;17001993;4243697
+02.09.2014 13:07:37,439;17002700;4470209
+02.09.2014 13:07:37,951;17002100;4581424
+02.09.2014 13:07:38,461;17002200;4605122
+02.09.2014 13:07:38,967;17002393;4605000
+02.09.2014 13:07:39,477;17002118;4605017
+02.09.2014 13:07:39,972;17002287;4605011
+02.09.2014 13:07:40,485;17002443;4605011
+02.09.2014 13:07:40,988;17002175;4605011
+02.09.2014 13:07:41,500;17002300;4605011
+02.09.2014 13:07:42,005;17002400;4605011
+02.09.2014 13:07:42,518;17002193;4605011
+02.09.2014 13:07:43,028;17002181;4605011
+02.09.2014 13:07:43,528;17002300;4605011
+02.09.2014 13:07:44,041;17002250;4605011
+02.09.2014 13:07:44,544;17002300;4605011
+02.09.2014 13:07:45,055;17002300;4605011
+02.09.2014 13:07:45,567;17002400;4605011
+02.09.2014 13:07:46,078;17002300;4605011
+02.09.2014 13:07:46,573;17002300;4605011
+02.09.2014 13:07:47,083;17002300;4597872
+02.09.2014 13:07:47,578;17002200;4544424
+02.09.2014 13:07:48,104;17002406;4526755
+02.09.2014 13:07:48,600;17002300;4521279
+02.09.2014 13:07:49,109;17002193;4512720
+02.09.2014 13:07:49,618;17002200;4484755
+02.09.2014 13:07:50,127;17002281;4485162
+02.09.2014 13:07:50,621;17002300;4485180
+02.09.2014 13:07:51,146;17002200;4485191
+02.09.2014 13:07:51,643;17002300;4485191
+02.09.2014 13:07:52,174;17002300;4485191
+02.09.2014 13:07:52,685;17002300;4485191
+02.09.2014 13:07:53,171;17002400;4485191
+02.09.2014 13:07:53,716;17002300;4485191
+02.09.2014 13:07:54,179;17002300;4485191
+02.09.2014 13:07:54,722;17002300;4485191
+02.09.2014 13:07:55,187;17002300;4485191
+02.09.2014 13:07:55,739;17002300;4485191
+02.09.2014 13:07:56,209;17002300;4485191
+02.09.2014 13:07:56,706;16995150;4484290
+02.09.2014 13:07:57,234;16973018;4443750
+02.09.2014 13:07:57,729;16971087;4297174
+02.09.2014 13:07:58,243;16972725;4043284
+02.09.2014 13:07:58,737;16974800;3758709
+02.09.2014 13:07:59,247;16973200;3473430
+02.09.2014 13:07:59,760;16973687;3164354
+02.09.2014 13:08:00,264;16973981;2855255
+02.09.2014 13:08:00,775;16973768;2546180
+02.09.2014 13:08:01,285;16973887;2237093
+02.09.2014 13:08:01,780;16974000;1951761
+02.09.2014 13:08:02,289;16974100;1642686
+02.09.2014 13:08:02,800;16974025;1333587
+02.09.2014 13:08:03,310;16974100;1048284
+02.09.2014 13:08:03,819;16974200;715430
+02.09.2014 13:08:04,329;16974225;430761
+02.09.2014 13:08:04,823;16974200;198656
+02.09.2014 13:08:05,333;16974112;56540
+02.09.2014 13:08:05,845;16974300;19656
+02.09.2014 13:08:06,355;16974075;19883
+02.09.2014 13:08:06,849;16974218;19924
+02.09.2014 13:08:07,360;16974306;19947
+02.09.2014 13:08:07,872;16974406;19947
+02.09.2014 13:08:08,382;16974393;19947
+02.09.2014 13:08:08,886;16974193;19947
+02.09.2014 13:08:09,396;16974300;19947
+02.09.2014 13:08:09,891;16974193;19947
+02.09.2014 13:08:10,400;16974193;19947
+02.09.2014 13:08:10,911;16974293;19947
+02.09.2014 13:08:11,423;16974293;19947
+02.09.2014 13:08:11,917;16974300;19947
+02.09.2014 13:08:12,428;16974193;19947
+02.09.2014 13:08:12,944;16974193;19947
+02.09.2014 13:08:13,447;16974168;19947
+02.09.2014 13:08:13,954;16974293;19947
+02.09.2014 13:08:14,463;16974325;20116
+02.09.2014 13:08:14,980;16974093;54616
+02.09.2014 13:08:15,468;16974093;60616
+02.09.2014 13:08:15,986;16974187;71040
+02.09.2014 13:08:16,491;16974256;125436
+02.09.2014 13:08:17,003;16974100;130069
+02.09.2014 13:08:17,509;16974193;129982
+02.09.2014 13:08:18,031;16974200;130000
+02.09.2014 13:08:18,536;16974300;129988
+02.09.2014 13:08:19,052;16974087;129988
+02.09.2014 13:08:19,564;16974293;129988
+02.09.2014 13:08:20,086;16974206;129988
+02.09.2014 13:08:20,592;16974200;129988
+02.09.2014 13:08:21,087;16974293;129988
+02.09.2014 13:08:21,616;16974062;129988
+02.09.2014 13:08:22,108;16974318;129988
+02.09.2014 13:08:22,623;16974168;129988
+02.09.2014 13:08:23,129;16974200;129988
+02.09.2014 13:08:23,628;16974300;129988
+02.09.2014 13:08:24,139;16974306;129988
+02.09.2014 13:08:24,649;16974187;129988
+02.09.2014 13:08:25,155;16974281;129988
+02.09.2014 13:08:25,666;16974193;129988
+02.09.2014 13:08:26,160;16974287;129988
+02.09.2014 13:08:26,679;16974306;129988
+02.09.2014 13:08:27,183;16974300;129988
+02.09.2014 13:08:27,696;16974206;129988
+02.09.2014 13:08:28,190;16974300;129988
+02.09.2014 13:08:28,702;16974300;129988
+02.09.2014 13:08:29,217;16974181;129988
+02.09.2014 13:08:29,720;16974300;129988
+02.09.2014 13:08:30,226;16974325;129988
+02.09.2014 13:08:30,727;16940725;129197
+02.09.2014 13:08:31,236;16789375;131331
+02.09.2014 13:08:31,759;16524962;132029
+02.09.2014 13:08:32,266;16179087;132029
+02.09.2014 13:08:32,765;15706631;132017
+02.09.2014 13:08:33,259;15265937;132017
+02.09.2014 13:08:33,771;14820487;132017
+02.09.2014 13:08:34,282;14339575;132017
+02.09.2014 13:08:34,793;13859968;132017
+02.09.2014 13:08:35,287;13416156;132017
+02.09.2014 13:08:35,798;12974100;132017
+02.09.2014 13:08:36,311;12494468;132017
+02.09.2014 13:08:36,821;12013400;132017
+02.09.2014 13:08:37,326;11532462;132017
+02.09.2014 13:08:37,826;11089418;132017
+02.09.2014 13:08:38,331;10646018;132017
+02.09.2014 13:08:38,842;10167500;132017
+02.09.2014 13:08:39,353;9723962;132017
+02.09.2014 13:08:39,864;9243237;132017
+02.09.2014 13:08:40,358;8801168;132017
+02.09.2014 13:08:40,869;8320912;132017
+02.09.2014 13:08:41,380;7840981;132017
+02.09.2014 13:08:41,892;7398550;132017
+02.09.2014 13:08:42,396;6918243;132017
+02.09.2014 13:08:42,898;6476025;132017
+02.09.2014 13:08:43,402;5995093;132017
+02.09.2014 13:08:43,915;5551593;132017
+02.09.2014 13:08:44,407;5109650;132017
+02.09.2014 13:08:44,931;4629637;132017
+02.09.2014 13:08:45,437;4148800;132017
+02.09.2014 13:08:45,940;3669593;132017
+02.09.2014 13:08:46,467;3225887;132017
+02.09.2014 13:08:46,962;2745818;132017
+02.09.2014 13:08:47,483;2266693;132017
+02.09.2014 13:08:47,968;1823000;132017
+02.09.2014 13:08:48,477;1386306;132017
+02.09.2014 13:08:48,984;1016781;132017
+02.09.2014 13:08:49,505;761631;132017
+02.09.2014 13:08:49,995;635431;132017
+02.09.2014 13:08:50,506;592750;132017
+02.09.2014 13:08:51,011;594275;132017
+02.09.2014 13:08:51,528;595100;132017
+02.09.2014 13:08:52,035;595475;132017
+02.09.2014 13:08:52,546;595481;132017
+02.09.2014 13:08:53,040;596350;132017
+02.09.2014 13:08:53,551;595900;132017
+02.09.2014 13:08:54,046;596106;132017
+02.09.2014 13:08:54,574;595931;132017
+02.09.2014 13:08:55,068;595462;132017
+02.09.2014 13:08:55,585;595381;132017
+02.09.2014 13:08:56,085;596112;132017
+02.09.2014 13:08:56,600;595825;132017
+02.09.2014 13:08:57,105;596206;132017
+02.09.2014 13:08:57,623;596100;132017
+02.09.2014 13:08:58,114;596012;132017
+02.09.2014 13:08:58,625;595681;132017
+02.09.2014 13:08:59,120;595581;123337
+02.09.2014 13:08:59,646;596218;65651
+02.09.2014 13:09:00,145;595887;53953
+02.09.2014 13:09:00,654;595468;47994
+02.09.2014 13:09:01,151;595812;37732
+02.09.2014 13:09:01,696;596125;11720
+02.09.2014 13:09:02,176;595931;12319
+02.09.2014 13:09:02,702;595893;12343
+02.09.2014 13:09:03,182;596406;12331
+02.09.2014 13:09:03,735;596000;12331
+02.09.2014 13:09:04,199;596093;12331
+02.09.2014 13:09:04,757;595587;12331
+02.09.2014 13:09:05,222;596200;12331
+02.09.2014 13:09:05,722;595987;12331
+02.09.2014 13:09:06,232;595906;12331
+02.09.2014 13:09:06,737;595668;12331
+02.09.2014 13:09:07,243;595918;12331
+02.09.2014 13:09:07,756;596306;12331
+02.09.2014 13:09:08,254;596000;12331
+02.09.2014 13:09:08,761;596331;11610
+02.09.2014 13:09:09,265;674368;13104
+02.09.2014 13:09:09,782;848493;15261
+02.09.2014 13:09:10,276;1120662;17011
+02.09.2014 13:09:10,787;1523662;17029
+02.09.2014 13:09:11,292;1968868;17017
+02.09.2014 13:09:11,808;2451650;17017
+02.09.2014 13:09:12,316;2930687;17017
+02.09.2014 13:09:12,826;3412218;17017
+02.09.2014 13:09:13,320;3779250;17017
+02.09.2014 13:09:13,829;3788437;17017
+02.09.2014 13:09:14,335;3792162;17017
+02.09.2014 13:09:14,846;3792306;17017
+02.09.2014 13:09:15,350;3792381;17017
+02.09.2014 13:09:15,859;3792693;17017
+02.09.2014 13:09:16,363;3793100;17017
+02.09.2014 13:09:16,877;3793068;17017
+02.09.2014 13:09:17,392;3793000;17017
+02.09.2014 13:09:17,892;3793106;17017
+02.09.2014 13:09:18,408;3792556;17017
+02.09.2014 13:09:18,915;3792793;17017
+02.09.2014 13:09:19,423;3792618;17017
+02.09.2014 13:09:19,927;3792981;17017
+02.09.2014 13:09:20,435;3792806;17017
+02.09.2014 13:09:20,929;3793056;17017
+02.09.2014 13:09:21,453;3792562;17017
+02.09.2014 13:09:21,946;3793006;17017
+02.09.2014 13:09:22,466;3792968;17017
+02.09.2014 13:09:22,975;3792906;17017
+02.09.2014 13:09:23,490;3793000;17017
+02.09.2014 13:09:23,972;3793100;17017
+02.09.2014 13:09:24,500;3792931;17017
+02.09.2014 13:09:25,005;3793050;17017
+02.09.2014 13:09:25,521;3792987;17017
+02.09.2014 13:09:26,030;3792787;17017
+02.09.2014 13:09:26,555;3793025;17017
+02.09.2014 13:09:27,063;3792693;17017
+02.09.2014 13:09:27,556;3793025;17017
+02.09.2014 13:09:28,080;3792662;17017
+02.09.2014 13:09:28,585;3792793;17017
+02.09.2014 13:09:29,092;3792912;17017
+02.09.2014 13:09:29,595;3792381;17017
+02.09.2014 13:09:30,110;3792606;17017
+02.09.2014 13:09:30,615;3792912;17017
+02.09.2014 13:09:31,125;3793018;17017
+02.09.2014 13:09:31,627;3792687;17017
+02.09.2014 13:09:32,135;3793006;17017
+02.09.2014 13:09:32,650;3792893;17017
+02.09.2014 13:09:33,155;3792906;17017
+02.09.2014 13:09:33,650;3792562;17017
+02.09.2014 13:09:34,162;3792600;17017
+02.09.2014 13:09:34,679;3793000;17017
+02.09.2014 13:09:35,188;3792693;17017
+02.09.2014 13:09:35,714;3792925;17017
+02.09.2014 13:09:36,223;3792581;17017
+02.09.2014 13:09:36,737;3792900;17017
+02.09.2014 13:09:37,234;3792768;17017
+02.09.2014 13:09:37,748;3792781;17017
+02.09.2014 13:09:38,252;3792600;17017
+02.09.2014 13:09:38,761;3792350;17017
+02.09.2014 13:09:39,270;3792668;17017
+02.09.2014 13:09:39,778;3792900;17017
+02.09.2014 13:09:40,282;3792762;17017
+02.09.2014 13:09:40,790;3792781;17017
+02.09.2014 13:09:41,298;3792375;17017
+02.09.2014 13:09:41,807;3793143;17017
+02.09.2014 13:09:42,324;3792550;17017
+02.09.2014 13:09:42,822;3793037;17017
+02.09.2014 13:09:43,325;3793118;17017
+02.09.2014 13:09:43,833;3792762;17017
+02.09.2014 13:09:44,326;3793025;17017
+02.09.2014 13:09:44,861;3792787;17017
+02.09.2014 13:09:45,361;3792456;17017
+02.09.2014 13:09:45,886;3792700;17017
+02.09.2014 13:09:46,385;3792518;17017
+02.09.2014 13:09:46,910;3793000;17017
+02.09.2014 13:09:47,420;3792893;17017
+02.09.2014 13:09:47,930;3792587;17017
+02.09.2014 13:09:48,438;3792893;17017
+02.09.2014 13:09:48,948;3792775;17017
+02.09.2014 13:09:49,452;3793050;17017
+02.09.2014 13:09:49,965;3793112;17017
+02.09.2014 13:09:50,461;3792893;17017
+02.09.2014 13:09:50,975;3793018;17017
+02.09.2014 13:09:51,480;3792900;17017
+02.09.2014 13:09:51,987;3792650;17017
+02.09.2014 13:09:52,491;3792781;17017
+02.09.2014 13:09:53,006;3793200;17017
+02.09.2014 13:09:53,513;3792687;17017
+02.09.2014 13:09:54,021;3793231;17017
+02.09.2014 13:09:54,524;3793125;17017
+02.09.2014 13:09:55,033;3792900;17017
+02.09.2014 13:09:55,543;3792912;17017
+02.09.2014 13:09:56,051;3793018;17017
+02.09.2014 13:09:56,557;3792887;17017
+02.09.2014 13:09:57,074;3792887;17017
+02.09.2014 13:09:57,573;3793318;17017
+02.09.2014 13:09:58,081;3792787;17017
+02.09.2014 13:09:58,585;3792787;17017
+02.09.2014 13:09:59,094;3792825;17017
+02.09.2014 13:09:59,586;3792556;17017
+02.09.2014 13:10:00,095;3792925;17017
+02.09.2014 13:10:00,600;3792800;17017
+02.09.2014 13:10:01,127;3792825;17017
+02.09.2014 13:10:01,635;3792900;17017
+02.09.2014 13:10:02,150;3793431;17017
+02.09.2014 13:10:02,661;3792893;17017
+02.09.2014 13:10:03,184;3792925;17017
+02.09.2014 13:10:03,692;3793131;17017
+02.09.2014 13:10:04,202;3792550;17017
+02.09.2014 13:10:04,713;3792937;17017
+02.09.2014 13:10:05,215;3792587;17017
+02.09.2014 13:10:05,726;3792487;17017
+02.09.2014 13:10:06,222;3792456;17017
+02.09.2014 13:10:06,736;3792687;17017
+02.09.2014 13:10:07,226;3793000;17017
+02.09.2014 13:10:07,753;3793106;17017
+02.09.2014 13:10:08,247;3792881;17017
+02.09.2014 13:10:08,756;3793137;17017
+02.09.2014 13:10:09,274;3792600;17017
+02.09.2014 13:10:09,784;3792906;17017
+02.09.2014 13:10:10,277;3792893;17017
+02.09.2014 13:10:10,787;3792700;17017
+02.09.2014 13:10:11,303;3792800;17017
+02.09.2014 13:10:11,798;3792793;17017
+02.09.2014 13:10:12,356;3792625;17017
+02.09.2014 13:10:12,819;3792862;17017
+02.09.2014 13:10:13,365;3793231;17017
+02.09.2014 13:10:13,831;3792600;17017
+02.09.2014 13:10:14,381;3792800;17017
+02.09.2014 13:10:14,851;3792687;17017
+02.09.2014 13:10:15,354;3792875;17017
+02.09.2014 13:10:15,880;3792912;17017
+02.09.2014 13:10:16,356;3792806;17017
+02.09.2014 13:10:16,876;3793043;17017
+02.09.2014 13:10:17,384;3792993;17017
+02.09.2014 13:10:17,894;3793206;17017
+02.09.2014 13:10:18,387;3792787;17017
+02.09.2014 13:10:18,895;3793100;17017
+02.09.2014 13:10:19,416;3792800;17017
+02.09.2014 13:10:19,926;3792893;17017
+02.09.2014 13:10:20,434;3792906;17017
+02.09.2014 13:10:20,930;3793000;17017
+02.09.2014 13:10:21,452;3792475;17017
+02.09.2014 13:10:21,946;3792912;17017
+02.09.2014 13:10:22,458;3792906;17017
+02.09.2014 13:10:22,964;3793081;17017
+02.09.2014 13:10:23,473;3792562;17017
+02.09.2014 13:10:23,968;3792875;17017
+02.09.2014 13:10:24,491;3792925;17017
+02.09.2014 13:10:24,995;3792787;17017
+02.09.2014 13:10:25,505;3792875;17017
+02.09.2014 13:10:26,015;3792800;17017
+02.09.2014 13:10:26,525;3793243;17017
+02.09.2014 13:10:27,010;3793343;17017
+02.09.2014 13:10:27,526;3792700;17017
+02.09.2014 13:10:28,029;3792781;17017
+02.09.2014 13:10:28,540;3792900;17017
+02.09.2014 13:10:29,043;3792493;17017
+02.09.2014 13:10:29,558;3792993;17017
+02.09.2014 13:10:30,060;3792543;17017
+02.09.2014 13:10:30,570;3792900;17017
+02.09.2014 13:10:31,074;3792768;17017
+02.09.2014 13:10:31,588;3792693;17017
+02.09.2014 13:10:32,086;3792800;17017
+02.09.2014 13:10:32,601;3793118;17017
+02.09.2014 13:10:33,107;3793012;17017
+02.09.2014 13:10:33,615;3792900;17017
+02.09.2014 13:10:34,125;3792562;17017
+02.09.2014 13:10:34,635;3792762;17017
+02.09.2014 13:10:35,129;3792681;17017
+02.09.2014 13:10:35,637;3792706;17017
+02.09.2014 13:10:36,141;3793093;17017
+02.09.2014 13:10:36,655;3793000;17017
+02.09.2014 13:10:37,155;3792956;17017
+02.09.2014 13:10:37,672;3793050;17017
+02.09.2014 13:10:38,175;3792593;17017
+02.09.2014 13:10:38,677;3792906;17017
+02.09.2014 13:10:39,180;3792806;17017
+02.09.2014 13:10:39,695;3792925;17017
+02.09.2014 13:10:40,198;3793450;17017
+02.09.2014 13:10:40,707;3792793;17017
+02.09.2014 13:10:41,212;3792900;17017
+02.09.2014 13:10:41,726;3793112;17017
+02.09.2014 13:10:42,230;3793037;17017
+02.09.2014 13:10:42,740;3792600;17017
+02.09.2014 13:10:43,244;3793000;17017
+02.09.2014 13:10:43,754;3792425;17017
+02.09.2014 13:10:44,246;3792806;17017
+02.09.2014 13:10:44,772;3792606;17017
+02.09.2014 13:10:45,279;3793006;17017
+02.09.2014 13:10:45,785;3792956;17017
+02.09.2014 13:10:46,294;3792687;17017
+02.09.2014 13:10:46,816;3792937;17017
+02.09.2014 13:10:47,297;3792918;17017
+02.09.2014 13:10:47,810;3793025;17017
+02.09.2014 13:10:48,313;3792793;17017
+02.09.2014 13:10:48,826;3793131;17017
+02.09.2014 13:10:49,325;3792800;17017
+02.09.2014 13:10:49,836;3792700;17017
+02.09.2014 13:10:50,348;3833362;17017
+02.09.2014 13:10:50,860;3988506;17017
+02.09.2014 13:10:51,370;4233481;17017
+02.09.2014 13:10:51,864;4575750;17017
+02.09.2014 13:10:52,376;5046368;17017
+02.09.2014 13:10:52,883;5525918;17017
+02.09.2014 13:10:53,394;6007562;17017
+02.09.2014 13:10:53,890;6451225;17017
+02.09.2014 13:10:54,409;6893668;17017
+02.09.2014 13:10:54,907;7374306;17017
+02.09.2014 13:10:55,418;7817362;17017
+02.09.2014 13:10:55,929;8296606;17017
+02.09.2014 13:10:56,440;8778350;17017
+02.09.2014 13:10:56,935;9220575;17017
+02.09.2014 13:10:57,445;9663868;17017
+02.09.2014 13:10:57,952;10143881;17017
+02.09.2014 13:10:58,462;10623437;17017
+02.09.2014 13:10:58,973;11103281;17017
+02.09.2014 13:10:59,489;11546731;17017
+02.09.2014 13:10:59,977;11988568;17017
+02.09.2014 13:11:00,488;12395375;17017
+02.09.2014 13:11:00,993;12684631;17017
+02.09.2014 13:11:01,512;12848387;17017
+02.09.2014 13:11:02,021;12916012;17017
+02.09.2014 13:11:02,539;12914612;17017
+02.09.2014 13:11:03,048;12912906;17017
+02.09.2014 13:11:03,566;12912700;17017
+02.09.2014 13:11:04,086;12912293;17017
+02.09.2014 13:11:04,582;12912093;17017
+02.09.2014 13:11:05,092;12912256;17017
+02.09.2014 13:11:05,587;12912193;17017
+02.09.2014 13:11:06,115;12912081;17017
+02.09.2014 13:11:06,608;12911987;17017
+02.09.2014 13:11:07,120;12911737;17017
+02.09.2014 13:11:07,625;12912087;17017
+02.09.2014 13:11:08,141;12911962;17017
+02.09.2014 13:11:08,636;12912000;17017
+02.09.2014 13:11:09,146;12912193;16418
+02.09.2014 13:11:09,652;12912118;51668
+02.09.2014 13:11:10,162;12911568;57709
+02.09.2014 13:11:10,666;12912075;61453
+02.09.2014 13:11:11,180;12911881;113325
+02.09.2014 13:11:11,675;12911381;137104
+02.09.2014 13:11:12,185;12912006;137023
+02.09.2014 13:11:12,698;12911912;137000
+02.09.2014 13:11:13,208;12911768;137000
+02.09.2014 13:11:13,716;12911706;137000
+02.09.2014 13:11:14,225;12911831;137000
+02.09.2014 13:11:14,720;12911687;137000
+02.09.2014 13:11:15,230;12911806;137000
+02.09.2014 13:11:15,725;12911900;137000
+02.09.2014 13:11:16,252;12911818;137000
+02.09.2014 13:11:16,764;12911925;137000
+02.09.2014 13:11:17,279;12911825;137000
+02.09.2014 13:11:17,789;12911600;137000
+02.09.2014 13:11:18,305;12911537;137000
+02.09.2014 13:11:18,821;12911493;137000
+02.09.2014 13:11:19,326;12911493;137000
+02.09.2014 13:11:19,843;12911706;137000
+02.09.2014 13:11:20,361;12911843;137000
+02.09.2014 13:11:20,850;12912025;137000
+02.09.2014 13:11:21,355;12911700;137000
+02.09.2014 13:11:21,891;12911806;137000
+02.09.2014 13:11:22,382;12906731;136081
+02.09.2014 13:11:22,915;12789275;143808
+02.09.2014 13:11:23,388;12604712;151273
+02.09.2014 13:11:23,931;12221581;161250
+02.09.2014 13:11:24,412;11843981;168511
+02.09.2014 13:11:24,959;11324031;178261
+02.09.2014 13:11:25,418;10952056;184651
+02.09.2014 13:11:25,930;10473493;192872
+02.09.2014 13:11:26,444;9992512;201011
+02.09.2014 13:11:26,940;9548425;208401
+02.09.2014 13:11:27,455;9068893;216831
+02.09.2014 13:11:27,950;8626262;223953
+02.09.2014 13:11:28,460;8144850;231540
+02.09.2014 13:11:28,972;7702625;238325
+02.09.2014 13:11:29,482;7223712;245534
+02.09.2014 13:11:29,976;6779518;252000
+02.09.2014 13:11:30,488;6299981;258750
+02.09.2014 13:11:30,999;5819550;265186
+02.09.2014 13:11:31,513;5376306;271319
+02.09.2014 13:11:32,001;4938493;276633
+02.09.2014 13:11:32,517;4561762;281813
+02.09.2014 13:11:33,023;4302287;286470
+02.09.2014 13:11:33,534;4163718;289953
+02.09.2014 13:11:34,045;4123506;289988
+02.09.2014 13:11:34,556;4123987;290011
+02.09.2014 13:11:35,052;4125475;290000
+02.09.2014 13:11:35,562;4125981;290000
+02.09.2014 13:11:36,074;4126400;290000
+02.09.2014 13:11:36,585;4126700;290000
+02.09.2014 13:11:37,079;4126887;290000
+02.09.2014 13:11:37,590;4127000;290000
+02.09.2014 13:11:38,094;4127006;290000
+02.09.2014 13:11:38,595;4127100;290000
+02.09.2014 13:11:39,101;4126987;290000
+02.09.2014 13:11:39,618;4126943;290000
+02.09.2014 13:11:40,124;4127112;290000
+02.09.2014 13:11:40,634;4127100;290000
+02.09.2014 13:11:41,144;4126987;290000
+02.09.2014 13:11:41,655;4127068;290000
+02.09.2014 13:11:42,149;4126981;290000
+02.09.2014 13:11:42,666;4126875;290000
+02.09.2014 13:11:43,164;4126987;290000
+02.09.2014 13:11:43,674;4126893;290000
+02.09.2014 13:11:44,189;4127112;290000
+02.09.2014 13:11:44,685;4127018;290000
+02.09.2014 13:11:45,190;4126968;290000
+02.09.2014 13:11:45,701;4127006;281174
+02.09.2014 13:11:46,195;4127106;228325
+02.09.2014 13:11:46,725;4127000;219912
+02.09.2014 13:11:47,218;4126993;214970
+02.09.2014 13:11:47,729;4127131;186848
+02.09.2014 13:11:48,234;4126968;179936
+02.09.2014 13:11:48,735;4126887;180110
+02.09.2014 13:11:49,256;4127012;180151
+02.09.2014 13:11:49,755;4126937;180139
+02.09.2014 13:11:50,260;4126993;180139
+02.09.2014 13:11:50,772;4127025;180139
+02.09.2014 13:11:51,265;4127093;180139
+02.09.2014 13:11:51,792;4126962;180139
+02.09.2014 13:11:52,284;4126887;180139
+02.09.2014 13:11:52,797;4127000;180139
+02.09.2014 13:11:53,307;4126981;180139
+02.09.2014 13:11:53,805;4126993;180139
+02.09.2014 13:11:54,325;4126962;180139
+02.09.2014 13:11:54,839;4126906;180139
+02.09.2014 13:11:55,364;4127106;180139
+02.09.2014 13:11:55,874;4126987;180139
+02.09.2014 13:11:56,389;4126900;180139
+02.09.2014 13:11:56,900;4170400;175447
+02.09.2014 13:11:57,412;4330687;166947
+02.09.2014 13:11:57,923;4604212;157255
+02.09.2014 13:11:58,416;4958387;149011
+02.09.2014 13:11:58,932;5434168;140186
+02.09.2014 13:11:59,428;5877931;132104
+02.09.2014 13:11:59,938;6322375;123459
+02.09.2014 13:12:00,444;6801106;115633
+02.09.2014 13:12:00,961;7282368;106540
+02.09.2014 13:12:01,466;7726006;98953
+02.09.2014 13:12:01,965;8168262;91482
+02.09.2014 13:12:02,471;8648631;83447
+02.09.2014 13:12:02,987;9092262;75651
+02.09.2014 13:12:03,493;9572412;68034
+02.09.2014 13:12:04,004;10052362;60593
+02.09.2014 13:12:04,497;10494912;53930
+02.09.2014 13:12:05,008;10975431;47075
+02.09.2014 13:12:05,520;11455656;40889
+02.09.2014 13:12:06,031;11897318;34604
+02.09.2014 13:12:06,525;12298412;29145
+02.09.2014 13:12:07,036;12618387;23406
+02.09.2014 13:12:07,547;12811587;19325
+02.09.2014 13:12:08,058;12909306;17000
+02.09.2014 13:12:08,564;12913000;17005
+02.09.2014 13:12:09,074;12912731;16994
+02.09.2014 13:12:09,574;12913375;16994
+02.09.2014 13:12:10,079;12912743;16994
+02.09.2014 13:12:10,584;12912368;16994
+02.09.2014 13:12:11,100;12912237;16994
+02.09.2014 13:12:11,607;12912206;16994
+02.09.2014 13:12:12,103;12912262;16994
+02.09.2014 13:12:12,613;12912325;16994
+02.09.2014 13:12:13,129;12912112;16994
+02.09.2014 13:12:13,638;12911968;16994
+02.09.2014 13:12:14,145;12912112;16994
+02.09.2014 13:12:14,641;12912025;16994
+02.09.2014 13:12:15,153;12911887;16197
+02.09.2014 13:12:15,651;12911731;43889
+02.09.2014 13:12:16,166;12911600;55970
+02.09.2014 13:12:16,673;12911400;59947
+02.09.2014 13:12:17,183;12911806;95848
+02.09.2014 13:12:17,684;12911612;136633
+02.09.2014 13:12:18,211;12911262;137011
+02.09.2014 13:12:18,699;12911837;136988
+02.09.2014 13:12:19,211;12911575;136976
+02.09.2014 13:12:19,716;12911731;136976
+02.09.2014 13:12:20,226;12911618;136976
+02.09.2014 13:12:20,721;12911593;136976
+02.09.2014 13:12:21,237;12911562;136976
+02.09.2014 13:12:21,750;12911781;136976
+02.09.2014 13:12:22,264;12911600;136976
+02.09.2014 13:12:22,775;12911487;136976
+02.09.2014 13:12:23,301;12911693;136976
+02.09.2014 13:12:23,815;12911600;136976
+02.09.2014 13:12:24,307;12911475;136976
+02.09.2014 13:12:24,829;12911500;136976
+02.09.2014 13:12:25,340;12911506;136976
+02.09.2014 13:12:25,851;12911693;136976
+02.09.2014 13:12:26,363;12911387;136976
+02.09.2014 13:12:26,857;12911818;136976
+02.09.2014 13:12:27,368;12911175;136976
+02.09.2014 13:12:27,874;12911568;136976
+02.09.2014 13:12:28,383;12911387;136976
+02.09.2014 13:12:28,884;12957687;150465
+02.09.2014 13:12:29,393;13120700;247348
+02.09.2014 13:12:29,898;13372331;455308
+02.09.2014 13:12:30,410;13755550;753186
+02.09.2014 13:12:30,904;14196850;1038482
+02.09.2014 13:12:31,425;14674606;1347575
+02.09.2014 13:12:31,918;15120668;1656674
+02.09.2014 13:12:32,458;15636993;1965744
+02.09.2014 13:12:32,943;16080368;2251034
+02.09.2014 13:12:33,478;16513600;2583912
+02.09.2014 13:12:33,961;16757875;2869226
+02.09.2014 13:12:34,472;16929562;3178279
+02.09.2014 13:12:35,004;17006331;3511162
+02.09.2014 13:12:35,477;17002300;3772697
+02.09.2014 13:12:35,989;17002912;4058023
+02.09.2014 13:12:36,499;17003100;4347052
+02.09.2014 13:12:36,994;17002100;4518732
+02.09.2014 13:12:37,505;17002400;4602970
+02.09.2014 13:12:38,012;17002200;4605087
+02.09.2014 13:12:38,523;17002100;4605017
+02.09.2014 13:12:39,018;17002393;4605005
+02.09.2014 13:12:39,534;17002300;4605005
+02.09.2014 13:12:40,045;17002187;4605005
+02.09.2014 13:12:40,578;17002300;4605005
+02.09.2014 13:12:41,083;17002300;4605005
+02.09.2014 13:12:41,599;17002300;4605005
+02.09.2014 13:12:42,111;17002300;4605005
+02.09.2014 13:12:42,622;17002300;4605005
+02.09.2014 13:12:43,133;17002293;4605005
+02.09.2014 13:12:43,628;17002431;4605005
+02.09.2014 13:12:44,138;17002275;4605005
+02.09.2014 13:12:44,644;17002287;4605005
+02.09.2014 13:12:45,154;17002400;4605005
+02.09.2014 13:12:45,650;17002300;4605005
+02.09.2014 13:12:46,165;17002400;4603901
+02.09.2014 13:12:46,672;17002300;4572209
+02.09.2014 13:12:47,174;17002387;4530511
+02.09.2014 13:12:47,693;17002606;4524500
+02.09.2014 13:12:48,200;17002225;4519970
+02.09.2014 13:12:48,707;17002400;4490412
+02.09.2014 13:12:49,204;17002300;4485000
+02.09.2014 13:12:49,713;17002300;4485191
+02.09.2014 13:12:50,221;17002200;4485174
+02.09.2014 13:12:50,734;17002306;4485174
+02.09.2014 13:12:51,243;17002306;4485174
+02.09.2014 13:12:51,738;17002300;4485174
+02.09.2014 13:12:52,247;17002300;4485174
+02.09.2014 13:12:52,759;17002300;4485174
+02.09.2014 13:12:53,268;17002306;4485174
+02.09.2014 13:12:53,762;17002325;4485174
+02.09.2014 13:12:54,279;17002300;4485174
+02.09.2014 13:12:54,775;17002187;4485174
+02.09.2014 13:12:55,286;17002281;4485174
+02.09.2014 13:12:55,815;17002287;4485174
+02.09.2014 13:12:56,307;16992918;4484238
+02.09.2014 13:12:56,820;16877068;4433215
+02.09.2014 13:12:57,324;16649862;4355069
+02.09.2014 13:12:57,840;16307931;4277000
+02.09.2014 13:12:58,334;15929287;4205005
+02.09.2014 13:12:58,846;15446887;4127145
+02.09.2014 13:12:59,359;14964793;4049348
+02.09.2014 13:12:59,862;14522093;3977529
+02.09.2014 13:13:00,361;14079131;3905709
+02.09.2014 13:13:00,883;13561693;3821994
+02.09.2014 13:13:01,376;13119212;3750302
+02.09.2014 13:13:01,887;12640787;3672790
+02.09.2014 13:13:02,391;12197387;3595325
+02.09.2014 13:13:02,910;11715506;3518017
+02.09.2014 13:13:03,406;11273031;3446755
+02.09.2014 13:13:03,922;10792368;3369587
+02.09.2014 13:13:04,415;10350556;3298366
+02.09.2014 13:13:04,926;9907706;3221255
+02.09.2014 13:13:05,433;9426225;3144645
+02.09.2014 13:13:05,949;8947718;3068087
+02.09.2014 13:13:06,443;8505250;2997418
+02.09.2014 13:13:06,956;8024106;2920976
+02.09.2014 13:13:07,462;7581743;2850523
+02.09.2014 13:13:07,978;7101525;2768296
+02.09.2014 13:13:08,485;6658437;2698267
+02.09.2014 13:13:08,986;6178393;2622953
+02.09.2014 13:13:09,499;5734162;2553604
+02.09.2014 13:13:10,006;5255337;2478563
+02.09.2014 13:13:10,505;4831931;2409563
+02.09.2014 13:13:11,019;4488087;2321552
+02.09.2014 13:13:11,525;4260968;2200802
+02.09.2014 13:13:12,024;4151393;2169674
+02.09.2014 13:13:12,532;4134912;2169866
+02.09.2014 13:13:13,050;4134312;2169930
+02.09.2014 13:13:13,554;4135387;2169918
+02.09.2014 13:13:14,058;4136093;2169918
+02.09.2014 13:13:14,563;4136500;2169918
+02.09.2014 13:13:15,074;4136700;2169918
+02.09.2014 13:13:15,570;4136825;2169918
+02.09.2014 13:13:16,085;4136887;2169918
+02.09.2014 13:13:16,591;4136937;2169918
+02.09.2014 13:13:17,104;4137012;2169918
+02.09.2014 13:13:17,600;4136887;2169918
+02.09.2014 13:13:18,118;4136775;2169918
+02.09.2014 13:13:18,625;4136900;2169918
+02.09.2014 13:13:19,124;4137000;2169918
+02.09.2014 13:13:19,635;4137006;2169918
+02.09.2014 13:13:20,140;4136893;2169918
+02.09.2014 13:13:20,637;4137000;2169459
+02.09.2014 13:13:21,155;4136900;2185377
+02.09.2014 13:13:21,661;4136900;2207622
+02.09.2014 13:13:22,172;4137000;2212965
+02.09.2014 13:13:22,688;4137081;2234337
+02.09.2014 13:13:23,194;4137193;2279744
+02.09.2014 13:13:23,689;4137475;2280052
+02.09.2014 13:13:24,200;4137112;2280046
+02.09.2014 13:13:24,707;4136987;2280034
+02.09.2014 13:13:25,206;4137006;2280034
+02.09.2014 13:13:25,721;4136987;2280034
+02.09.2014 13:13:26,231;4137100;2280034
+02.09.2014 13:13:26,744;4137000;2280034
+02.09.2014 13:13:27,237;4137100;2280034
+02.09.2014 13:13:27,764;4137000;2280034
+02.09.2014 13:13:28,258;4137125;2280034
+02.09.2014 13:13:28,770;4136875;2280034
+02.09.2014 13:13:29,264;4137000;2280034
+02.09.2014 13:13:29,793;4136887;2280034
+02.09.2014 13:13:30,287;4137000;2280034
+02.09.2014 13:13:30,798;4136993;2280034
+02.09.2014 13:13:31,293;4137000;2280034
+02.09.2014 13:13:31,805;4136993;2280034
+02.09.2014 13:13:32,313;4137000;2280034
+02.09.2014 13:13:32,831;4136981;2280034
+02.09.2014 13:13:33,327;4137000;2280034
+02.09.2014 13:13:33,837;4137106;2280034
+02.09.2014 13:13:34,349;4137000;2280034
+02.09.2014 13:13:34,863;4137000;2280034
+02.09.2014 13:13:35,361;4137000;2280034
+02.09.2014 13:13:35,871;4136843;2280034
+02.09.2014 13:13:36,367;4137006;2280034
+02.09.2014 13:13:36,879;4120118;2279593
+02.09.2014 13:13:37,390;3999168;2345383
+02.09.2014 13:13:37,901;3762518;2517854
+02.09.2014 13:13:38,396;3442768;2770732
+02.09.2014 13:13:38,910;2987875;3079406
+02.09.2014 13:13:39,415;2543487;3388476
+02.09.2014 13:13:39,933;2062300;3697563
+02.09.2014 13:13:40,426;1618206;3982883
+02.09.2014 13:13:40,963;1155400;4306215
+02.09.2014 13:13:41,433;889987;4487319
+02.09.2014 13:13:41,980;660500;4609767
+02.09.2014 13:13:42,448;590375;4619110
+02.09.2014 13:13:43,002;587706;4619011
+02.09.2014 13:13:43,472;585387;4619017
+02.09.2014 13:13:44,021;586800;4619005
+02.09.2014 13:13:44,478;587668;4619005
+02.09.2014 13:13:44,994;587175;4619005
+02.09.2014 13:13:45,502;587762;4619005
+02.09.2014 13:13:46,012;587706;4619005
+02.09.2014 13:13:46,505;587993;4619005
+02.09.2014 13:13:47,018;588200;4619005
+02.09.2014 13:13:47,530;588018;4619005
+02.09.2014 13:13:48,041;588037;4619005
+02.09.2014 13:13:48,534;587887;4619005
+02.09.2014 13:13:49,047;587925;4619005
+02.09.2014 13:13:49,553;588031;4619005
+02.09.2014 13:13:50,069;587481;4619005
+02.09.2014 13:13:50,563;588143;4617802
+02.09.2014 13:13:51,076;587618;4580238
+02.09.2014 13:13:51,584;587868;4543517
+02.09.2014 13:13:52,086;587700;4537895
+02.09.2014 13:13:52,601;587462;4533941
+02.09.2014 13:13:53,114;587668;4501790
+02.09.2014 13:13:53,612;587912;4499017
+02.09.2014 13:13:54,119;587887;4499151
+02.09.2014 13:13:54,629;588150;4499145
+02.09.2014 13:13:55,141;588056;4499145
+02.09.2014 13:13:55,638;587681;4499145
+02.09.2014 13:13:56,146;588100;4499145
+02.09.2014 13:13:56,656;587800;4499145
+02.09.2014 13:13:57,185;587937;4499145
+02.09.2014 13:13:57,687;588187;4499145
+02.09.2014 13:13:58,204;587900;4499145
+02.09.2014 13:13:58,719;588106;4499145
+02.09.2014 13:13:59,236;587893;4499145
+02.09.2014 13:13:59,744;587793;4499145
+02.09.2014 13:14:00,254;588306;4498732
+02.09.2014 13:14:00,755;635787;4486081
+02.09.2014 13:14:01,273;799881;4380854
+02.09.2014 13:14:01,771;1052806;4237441
+02.09.2014 13:14:02,283;1436981;4082348
+02.09.2014 13:14:02,777;1878806;3939296
+02.09.2014 13:14:03,289;2357981;3772453
+02.09.2014 13:14:03,785;2802106;3629581
+02.09.2014 13:14:04,314;3283206;3474906
+02.09.2014 13:14:04,808;3727350;3332145
+02.09.2014 13:14:05,323;4205956;3177244
+02.09.2014 13:14:05,826;4687206;3022505
+02.09.2014 13:14:06,342;5167493;2867860
+02.09.2014 13:14:06,836;5609412;2725325
+02.09.2014 13:14:07,348;6052956;2570837
+02.09.2014 13:14:07,845;6496100;2428517
+02.09.2014 13:14:08,360;6975831;2274360
+02.09.2014 13:14:08,871;7455793;2108430
+02.09.2014 13:14:09,382;7935731;1966319
+02.09.2014 13:14:09,878;8378668;1812203
+02.09.2014 13:14:10,388;8822781;1670005
+02.09.2014 13:14:10,893;9300756;1516412
+02.09.2014 13:14:11,411;9782331;1362813
+02.09.2014 13:14:11,904;10225862;1221174
+02.09.2014 13:14:12,417;10704087;1068087
+02.09.2014 13:14:12,923;11147468;915005
+02.09.2014 13:14:13,424;11627793;763372
+02.09.2014 13:14:13,932;12067700;510656
+02.09.2014 13:14:14,445;12452443;240005
+02.09.2014 13:14:14,952;12705556;74965
+02.09.2014 13:14:15,462;12868418;17186
+02.09.2014 13:14:15,957;12915881;16813
+02.09.2014 13:14:16,484;12914700;16906
+02.09.2014 13:14:16,977;12913112;16895
+02.09.2014 13:14:17,488;12912893;16895
+02.09.2014 13:14:17,983;12912187;16895
+02.09.2014 13:14:18,494;12912406;16895
+02.09.2014 13:14:19,000;12912400;16895
+02.09.2014 13:14:19,517;12912343;16895
+02.09.2014 13:14:20,023;12911993;16895
+02.09.2014 13:14:20,533;12912212;16895
+02.09.2014 13:14:21,043;12912237;16895
+02.09.2014 13:14:21,555;12912187;16895
+02.09.2014 13:14:22,049;12911681;16895
+02.09.2014 13:14:22,560;12912118;16895
+02.09.2014 13:14:23,054;12911800;18494
+02.09.2014 13:14:23,566;12911600;52598
+02.09.2014 13:14:24,073;12911575;59633
+02.09.2014 13:14:24,582;12911437;83831
+02.09.2014 13:14:25,089;12911818;134023
+02.09.2014 13:14:25,607;12911300;137063
+02.09.2014 13:14:26,098;12911400;137017
+02.09.2014 13:14:26,617;12911843;137005
+02.09.2014 13:14:27,127;12911950;137005
+02.09.2014 13:14:27,624;12911625;137005
+02.09.2014 13:14:28,129;12911337;137005
+02.09.2014 13:14:28,642;12911743;137005
+02.09.2014 13:14:29,162;12911700;137005
+02.09.2014 13:14:29,655;12911537;137005
+02.09.2014 13:14:30,165;12911725;137005
+02.09.2014 13:14:30,678;12911506;137005
+02.09.2014 13:14:31,188;12911587;137005
+02.09.2014 13:14:31,685;12911587;137005
+02.09.2014 13:14:32,199;12911500;137005
+02.09.2014 13:14:32,696;12911800;137005
+02.09.2014 13:14:33,215;12911931;137005
+02.09.2014 13:14:33,705;12911693;137005
+02.09.2014 13:14:34,224;12911718;137005
+02.09.2014 13:14:34,731;12911237;137005
+02.09.2014 13:14:35,247;12911500;137005
+02.09.2014 13:14:35,752;12911800;137005
+02.09.2014 13:14:36,263;12886575;152377
+02.09.2014 13:14:36,760;12891868;251883
+02.09.2014 13:14:37,272;12892693;462720
+02.09.2014 13:14:37,774;12891606;738075
+02.09.2014 13:14:38,285;12891806;1047098
+02.09.2014 13:14:38,794;12891906;1332482
+02.09.2014 13:14:39,304;12891781;1665296
+02.09.2014 13:14:39,802;12891606;1950604
+02.09.2014 13:14:40,316;12891937;2259709
+02.09.2014 13:14:40,804;12891600;2544982
+02.09.2014 13:14:41,323;12891675;2854081
+02.09.2014 13:14:41,836;12892256;3139401
+02.09.2014 13:14:42,354;12891387;3472261
+02.09.2014 13:14:42,866;12891575;3781343
+02.09.2014 13:14:43,393;12891956;4090436
+02.09.2014 13:14:43,894;12891500;4352936
+02.09.2014 13:14:44,399;12891287;4530691
+02.09.2014 13:14:44,918;12891562;4600639
+02.09.2014 13:14:45,424;12891531;4602034
+02.09.2014 13:14:45,924;12891631;4602005
+02.09.2014 13:14:46,428;12891700;4601994
+02.09.2014 13:14:46,940;12891593;4601994
+02.09.2014 13:14:47,434;12891925;4601994
+02.09.2014 13:14:47,952;12891612;4601994
+02.09.2014 13:14:48,458;12891475;4601994
+02.09.2014 13:14:48,969;12891600;4601994
+02.09.2014 13:14:49,475;12891500;4601994
+02.09.2014 13:14:50,011;12891287;4601994
+02.09.2014 13:14:50,498;12891400;4601994
+02.09.2014 13:14:50,999;12891693;4601994
+02.09.2014 13:14:51,516;12891556;4601994
+02.09.2014 13:14:52,014;12891681;4601994
+02.09.2014 13:14:52,538;12891593;4601994
+02.09.2014 13:14:53,025;12891793;4601994
+02.09.2014 13:14:53,556;12891593;4600784
+02.09.2014 13:14:54,043;12892012;4572744
+02.09.2014 13:14:54,536;12891700;4527348
+02.09.2014 13:14:55,053;12891381;4521389
+02.09.2014 13:14:55,559;12891300;4516941
+02.09.2014 13:14:56,072;12891606;4489436
+02.09.2014 13:14:56,567;12891593;4481924
+02.09.2014 13:14:57,081;12891731;4482110
+02.09.2014 13:14:57,576;12891068;4482098
+02.09.2014 13:14:58,104;12891350;4482098
+02.09.2014 13:14:58,597;12891831;4482098
+02.09.2014 13:14:59,109;12891162;4482098
+02.09.2014 13:14:59,621;12891625;4482098
+02.09.2014 13:15:00,131;12891643;4482098
+02.09.2014 13:15:00,643;12891700;4482098
+02.09.2014 13:15:01,141;12891493;4482098
+02.09.2014 13:15:01,653;12891712;4482098
+02.09.2014 13:15:02,148;12891812;4482098
+02.09.2014 13:15:02,665;12891518;4482098
+02.09.2014 13:15:03,171;12891831;4482098
+02.09.2014 13:15:03,684;12884112;4481220
+02.09.2014 13:15:04,176;12785593;4446232
+02.09.2014 13:15:04,704;12571300;4327360
+02.09.2014 13:15:05,195;12272281;4221029
+02.09.2014 13:15:05,709;11874756;4114360
+02.09.2014 13:15:06,219;11389887;3999151
+02.09.2014 13:15:06,736;10908800;3883988
+02.09.2014 13:15:07,245;10467406;3769145
+02.09.2014 13:15:07,772;9949850;3654238
+02.09.2014 13:15:08,282;9469443;3539372
+02.09.2014 13:15:08,776;9027000;3433209
+02.09.2014 13:15:09,304;8546462;3309529
+02.09.2014 13:15:09,798;8102593;3203622
+02.09.2014 13:15:10,309;7660512;3089215
+02.09.2014 13:15:10,802;7217168;2983593
+02.09.2014 13:15:11,331;6700306;2869127
+02.09.2014 13:15:11,842;6221200;2754738
+02.09.2014 13:15:12,361;5776893;2640447
+02.09.2014 13:15:12,865;5333893;2526122
+02.09.2014 13:15:13,391;4818000;2412540
+02.09.2014 13:15:13,904;4373200;2298720
+02.09.2014 13:15:14,395;3930962;2193982
+02.09.2014 13:15:14,909;3450437;2080110
+02.09.2014 13:15:15,414;2970018;1966331
+02.09.2014 13:15:15,937;2527837;1852686
+02.09.2014 13:15:16,435;2047675;1749034
+02.09.2014 13:15:16,942;1603925;1637430
+02.09.2014 13:15:17,441;1174218;1535343
+02.09.2014 13:15:17,957;863462;1357860
+02.09.2014 13:15:18,461;678643;1239819
+02.09.2014 13:15:18,972;590625;1220127
+02.09.2014 13:15:19,465;587393;1220395
+02.09.2014 13:15:19,979;585487;1220436
+02.09.2014 13:15:20,482;586975;1220465
+02.09.2014 13:15:20,998;587350;1220465
+02.09.2014 13:15:21,492;587931;1220465
+02.09.2014 13:15:22,004;587793;1220465
+02.09.2014 13:15:22,511;587900;1220465
+02.09.2014 13:15:23,028;588031;1220465
+02.09.2014 13:15:23,522;588431;1220465
+02.09.2014 13:15:24,033;588200;1220465
+02.09.2014 13:15:24,544;588106;1220465
+02.09.2014 13:15:25,052;587900;1220465
+02.09.2014 13:15:25,547;588262;1220465
+02.09.2014 13:15:26,064;588043;1230883
+02.09.2014 13:15:26,571;588400;1256581
+02.09.2014 13:15:27,082;588100;1262604
+02.09.2014 13:15:27,578;588181;1269500
+02.09.2014 13:15:28,095;587781;1327005
+02.09.2014 13:15:28,593;587325;1340558
+02.09.2014 13:15:29,110;587750;1340517
+02.09.2014 13:15:29,611;588350;1340511
+02.09.2014 13:15:30,119;587787;1340511
+02.09.2014 13:15:30,877;588175;1340511
+02.09.2014 13:15:31,367;587781;1340511
+02.09.2014 13:15:31,884;587968;1340511
+02.09.2014 13:15:32,395;588181;1340511
+02.09.2014 13:15:32,914;588075;1340511
+02.09.2014 13:15:33,425;587793;1340511
+02.09.2014 13:15:33,947;588150;1340511
+02.09.2014 13:15:34,456;588643;1340511
+02.09.2014 13:15:34,961;588206;1340511
+02.09.2014 13:15:35,478;588093;1340511
+02.09.2014 13:15:35,972;588087;1340511
+02.09.2014 13:15:36,483;588187;1340511
+02.09.2014 13:15:36,991;588306;1340511
+02.09.2014 13:15:37,503;588331;1340511
+02.09.2014 13:15:38,013;587781;1340511
+02.09.2014 13:15:38,525;599206;1336406
+02.09.2014 13:15:39,017;598175;1256337
+02.09.2014 13:15:39,530;597425;1087156
+02.09.2014 13:15:40,025;596293;805767
+02.09.2014 13:15:40,554;596725;500151
+02.09.2014 13:15:41,049;596100;272470
+02.09.2014 13:15:41,561;595987;156610
+02.09.2014 13:15:42,071;596718;117191
+02.09.2014 13:15:42,564;596800;117191
+02.09.2014 13:15:43,066;597125;117191
+02.09.2014 13:15:43,593;596987;117191
+02.09.2014 13:15:44,086;596550;117191
+02.09.2014 13:15:44,599;596768;117191
+02.09.2014 13:15:45,113;597037;117191
+02.09.2014 13:15:45,621;597112;117191
+02.09.2014 13:15:46,108;596781;117191
+02.09.2014 13:15:46,632;596856;117191
+02.09.2014 13:15:47,125;596887;117191
+02.09.2014 13:15:47,635;596681;117191
+02.09.2014 13:15:48,143;597062;117191
+02.09.2014 13:15:48,656;596687;117191
+02.09.2014 13:15:49,155;596775;117191
+02.09.2014 13:15:49,666;596775;117191
+02.09.2014 13:15:50,169;596743;117191
+02.09.2014 13:15:50,684;596462;117191
+02.09.2014 13:15:51,187;596493;117191
+02.09.2014 13:15:51,697;597100;117191
+02.09.2014 13:15:52,202;596700;117191
+02.09.2014 13:15:52,712;596668;117191
+02.09.2014 13:15:53,203;597106;117191
+02.09.2014 13:15:53,732;596925;117191
+02.09.2014 13:15:54,224;596900;117191
+02.09.2014 13:15:54,735;596906;117191
+02.09.2014 13:15:55,237;596906;117191
+02.09.2014 13:15:55,756;596981;117191
+02.09.2014 13:15:56,257;596750;117191
+02.09.2014 13:15:56,767;596881;117191
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/CanvasBehavior.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/CanvasBehavior.cs
new file mode 100644
index 000000000..b652baa75
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/CanvasBehavior.cs
@@ -0,0 +1,66 @@
+// ***********************************************************************
+// Assembly : MVVM_Lines_on_Grid
+// Author : Mir
+// Created : 08-28-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-29-2022
+// ***********************************************************************
+//
+// (c) by Joe Care 2022
+//
+//
+// ***********************************************************************
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Xaml.Behaviors;
+using MVVM_17_1_CSV_Laden.Views.Converter;
+
+namespace MVVM_17_1_CSV_Laden.ViewModels;
+
+///
+/// Class CanvasBehavior.
+/// Implements the
+///
+///
+public class CanvasBehavior : Behavior
+{
+ ///
+ /// Called after the behavior is attached to an AssociatedObject.
+ ///
+ /// Override this to hook up functionality to the AssociatedObject.
+ protected override void OnAttached()
+ {
+ var iObjParent = AssociatedObject.Parent as Page;
+ var cCoordinateConverter = iObjParent?.Resources["vcPortGrid"] as WindowPortToGridLines;
+
+ AssociatedObject.MouseWheel += (s, e) =>
+ {
+ if (AssociatedObject.DataContext is DataPointsViewModel vm)
+ {
+ var mousePosition = e.GetPosition(s as IInputElement);
+ System.Drawing.RectangleF ActPort = vm.VPWindow;
+ if (e.Delta > 0)
+ {
+ ActPort.Inflate(ActPort.Size.Width * 0.1f, ActPort.Size.Height * 0.1f);
+ vm.VPWindow = ActPort;
+ }
+ else if (e.Delta < 0)
+ {
+ ActPort.Inflate(-ActPort.Size.Width * 0.1f, -ActPort.Size.Height * 0.1f);
+ vm.VPWindow = ActPort;
+ }
+
+ }
+ };
+
+ AssociatedObject.MouseLeftButtonDown += (s, e) =>
+ {
+ if (AssociatedObject.DataContext is DataPointsViewModel vm)
+ {
+ var mousePosition = e.GetPosition(s as IInputElement);
+ var RealPos = cCoordinateConverter?.Vis2RealP(mousePosition, cCoordinateConverter.GetAdjustedRect(vm.WindowPort));
+ }
+ };
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/DataPointsViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/DataPointsViewModel.cs
new file mode 100644
index 000000000..84fdbc9dc
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/DataPointsViewModel.cs
@@ -0,0 +1,126 @@
+// ***********************************************************************
+// Assembly : MVVM_17_1_CSV_Laden
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-13-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Collections.ObjectModel;
+using System.Drawing;
+using MVVM.ViewModel;
+using MVVM_17_1_CSV_Laden.Model;
+
+namespace MVVM_17_1_CSV_Laden.ViewModels;
+
+
+///
+/// Class DataPointsViewModel.
+/// Implements the
+///
+///
+public class DataPointsViewModel : BaseViewModel
+{
+ ///
+ /// The view port
+ ///
+ private SWindowPort _viewPort;
+
+ ///
+ /// Gets or sets the window port.
+ ///
+ /// The window port.
+ public SWindowPort WindowPort { get => _viewPort; set => SetProperty(ref _viewPort, value); }
+
+ ///
+ /// Gets or sets the vp window.
+ ///
+ /// The vp window.
+ public RectangleF VPWindow { get => _viewPort.port; set => SetProperty(ref _viewPort.port, value, new string[] { nameof(WindowPort), nameof(DataPoints) }); }
+ ///
+ /// Gets or sets the size of the window.
+ ///
+ /// The size of the window.
+ public System.Windows.Size WindowSize
+ {
+ get => _viewPort.WindowSize;
+ set => SetProperty(ref _viewPort.WindowSize, value, new string[] { nameof(WindowPort), nameof(DataPoints) });
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DataPointsViewModel()
+ {
+ LoadCsV = new DelegateCommand((o) => ExecLoadCsV());
+ DataPoints = new ObservableCollection();
+ IsLoading = false;
+
+ VPWindow = new RectangleF(-3, -3, 9, 6);
+ // VPWindow = new RectangleF(-3, -3, 900, 600);
+ // VPWindow = new RectangleF(-0.03f, -0.03f, 0.09f, 0.06f);
+ WindowSize = new System.Windows.Size(300, 400);
+ _viewPort.Parent = this;
+
+ }
+
+ ///
+ /// Gets or sets the data points.
+ ///
+ /// The data points.
+ public ObservableCollection DataPoints { get; set; }
+
+ ///
+ /// Executes the load cs v.
+ ///
+ private async void ExecLoadCsV()
+ {
+ IsLoading = true;
+ using (var service = new CsvModel("Resources\\RBG_XIst_YIst.csv"))
+ {
+ var result = service.ReadCSV();
+ await foreach (var item in result)
+ DataPoints.Add(item);
+ }
+ var max = new PointF((float)DataPoints[0].X, (float)DataPoints[0].Y);
+ var min = new PointF(max.X,max.Y);
+ foreach (var item in DataPoints)
+ {
+ if (item.X > max.X) max.X = (float)item.X;
+ if (item.Y > max.Y) max.Y = (float)item.Y;
+ if (item.X < min.X) min.X = (float)item.X;
+ if (item.Y < min.Y) min.Y = (float)item.Y;
+ }
+ VPWindow = new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y);
+ IsLoading = false;
+ RaisePropertyChanged(nameof(DataPoints));
+ }
+
+ ///
+ /// The is loading
+ ///
+ private bool _isLoading;
+
+ ///
+ /// Gets or sets a value indicating whether this instance is loading.
+ ///
+ /// true if this instance is loading; otherwise, false.
+ public bool IsLoading {
+ get { return _isLoading; }
+ set { if (_isLoading != value) return; _isLoading = value; RaisePropertyChanged(); }
+ }
+
+ ///
+ /// Gets or sets the load cs v.
+ ///
+ /// The load cs v.
+ public DelegateCommand LoadCsV { get; set; } = new DelegateCommand(
+ (o) => { },
+ (o)=>false
+ );
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/SWindowPort.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/SWindowPort.cs
new file mode 100644
index 000000000..61edce972
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/ViewModels/SWindowPort.cs
@@ -0,0 +1,35 @@
+// ***********************************************************************
+// Assembly : MVVM_17_1_CSV_Laden
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-13-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Drawing;
+
+namespace MVVM_17_1_CSV_Laden.ViewModels;
+
+///
+/// Struct SWindowPort
+///
+public struct SWindowPort
+{
+ ///
+ /// The port
+ ///
+ public RectangleF port;
+ ///
+ /// The window size
+ ///
+ public System.Windows.Size WindowSize;
+ ///
+ /// The parent
+ ///
+ public DataPointsViewModel Parent;
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/Converter/WindowPortToGridLines.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/Converter/WindowPortToGridLines.cs
new file mode 100644
index 000000000..824d418e0
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/Converter/WindowPortToGridLines.cs
@@ -0,0 +1,294 @@
+// ***********************************************************************
+// Assembly : MVVM_Lines_on_Grid
+// Author : Mir
+// Created : 08-28-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 08-29-2022
+// ***********************************************************************
+//
+// (c) by Joe Care 2022
+//
+//
+// ***********************************************************************
+using System;
+using System.Drawing;
+using System.Globalization;
+using System.Windows.Media;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Shapes;
+using MVVM_17_1_CSV_Laden.Model;
+using MVVM_17_1_CSV_Laden.ViewModels;
+using System.Collections.ObjectModel;
+using System.Windows;
+
+namespace MVVM_17_1_CSV_Laden.Views.Converter;
+
+///
+/// Class WindowPortToGridLines.
+/// Implements the
+///
+///
+public class WindowPortToGridLines : IValueConverter
+{
+ ///
+ /// The average GRD pixel
+ ///
+ const int AvgGrdPixel = 40;
+ ///
+ /// The lb
+ ///
+ System.Windows.Size lb = new System.Windows.Size(50, 28);
+
+ ///
+ /// Gets or sets the size of the window.
+ ///
+ /// The size of the window.
+ public System.Windows.Size WindowSize { get; set; } = new System.Windows.Size(600, 600);
+
+ public System.Windows.Media.Pen DefaultPen { get; set; } = new(System.Windows.Media.Brushes.Red, 0.8d);
+ ///
+ /// Real2s the vis.
+ ///
+ /// The value.
+ /// The vis minimum.
+ /// The vis maximum.
+ /// The r minimum.
+ /// The r maximum.
+ /// System.Double.
+ private double Real2Vis(double value, double visMin, double visMax, double rMin, double rMax)
+ => visMin + (value - rMin) * (visMax - visMin) / (rMax - rMin);
+
+ ///
+ /// Real2s the vis p.
+ ///
+ /// The value.
+ /// The port.
+ /// System.Windows.Point.
+ private System.Windows.Point real2VisP(PointF value, RectangleF port) =>
+ new System.Windows.Point(Real2Vis(value.X, 0, WindowSize.Width - lb.Width, port.Left, port.Right) + lb.Width,
+ Real2Vis(value.Y, WindowSize.Height - lb.Height, 0d, port.Top, port.Bottom) + lb.Height);
+
+ ///
+ /// Vis2s the real p.
+ ///
+ /// The value.
+ /// The port.
+ /// PointF.
+ private PointF vis2RealP(System.Windows.Point value, RectangleF port) =>
+ new PointF(
+ (float)Real2Vis(value.X - lb.Width, port.Left, port.Right, 0, WindowSize.Width - lb.Width),
+ (float)Real2Vis(value.Y - lb.Height, port.Top, port.Bottom, WindowSize.Height - lb.Height, 0d));
+
+ ///
+ /// The real2 vis p
+ ///
+ public Func Real2VisP;
+ ///
+ /// The vis2 real p
+ ///
+ public Func Vis2RealP;
+ private RectangleF actPort;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public WindowPortToGridLines()
+ {
+ Real2VisP = real2VisP;
+ Vis2RealP = vis2RealP;
+ }
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value produced by the binding source.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ ObservableCollection result;
+ switch (value)
+ {
+ case SWindowPort c:
+ var b = new SolidColorBrush(Colors.Black);
+
+ var hOffset = 0d;
+ RectangleF port2; // erweiterter Viewport
+ port2 = GetAdjustedRect(c);
+
+ double BigStep, Step;
+ ComputeGridSteps(port2.Width, out BigStep, out Step);
+
+ double MinStepX = Math.Ceiling(port2.Left / Step) * Step;
+ double MinStepY = Math.Ceiling(port2.Top / Step) * Step;
+
+ actPort = port2;
+ result = new ObservableCollection();
+
+ if (c.port.Contains(PointF.Empty))
+ {
+ var p = Real2VisP(PointF.Empty, port2);
+ Ellipse el = new Ellipse() { Height = 7, Width = 7, Margin = new Thickness(p.X - 3, p.Y - 3, 0, 0), Stroke = b, StrokeThickness = 0.3d };
+ result.Add(el);
+ }
+
+ // var MaxSize = WindowSize.Width > (WindowSize.Height- hOffset) *1.5 ? (WindowSize.Width-lb.Width) / 1.5 : (WindowSize.Height-lb.Height- hOffset) ;
+
+ for (var i = 0; MinStepX + i * Step < port2.Right || MinStepY + i * Step < port2.Bottom; i++)
+ {
+ double X1 = MinStepX + i * Step;
+ var P1x = real2VisP(new PointF((float)X1, port2.Top), port2);
+ var P2x = real2VisP(new PointF((float)X1, port2.Bottom), port2);
+ if (P1x.X < WindowSize.Width)
+ {
+ result.Add(CreateLine(b, GetStroke(X1, Step, BigStep), P1x, P2x));
+
+ if (Math.Abs((Math.Abs(X1) + Step / 5) % BigStep) < Step / 2)
+ {
+ result.Add(CreateLabel(X1, new Thickness((double)(P1x.X - lb.Width / 2d + 7), 0d, 0d, 0d), VerticalAlignment.Bottom, HorizontalAlignment.Center));
+ }
+ }
+
+ double Y1 = MinStepY + i * Step;
+ var P1y = real2VisP(new PointF(port2.Left, (float)Y1), port2);
+ var P2y = real2VisP(new PointF(port2.Right, (float)Y1), port2);
+ if (P1y.Y < WindowSize.Height - hOffset && P1y.Y > lb.Height)
+ {
+ result.Add(CreateLine(b, GetStroke(Y1, Step, BigStep), P1y, P2y));
+ if (Math.Abs((Math.Abs(Y1) + Step / 5) % BigStep) < Step / 2)
+ {
+ result.Add(CreateLabel(Y1, new Thickness(0d, (double)(P1y.Y - hOffset - lb.Height + 5), 0d, 0d), VerticalAlignment.Center, HorizontalAlignment.Right));
+ }
+ }
+ }
+ return result;
+ case ObservableCollection ds:
+ result = new ObservableCollection();
+ for (int i = 0; i < ds.Count - 1; i++)
+ {
+ var P1 = real2VisP(new PointF((float)ds[i].X, (float)ds[i].Y), actPort);
+ var P2 = real2VisP(new PointF((float)ds[i + 1].X, (float)ds[i + 1].Y), actPort);
+ if (IsInWindow(P1,WindowSize) || IsInWindow(P2, WindowSize))
+ result.Add(CreateLine(DefaultPen.Brush, DefaultPen.Thickness, P1, P2));
+ }
+ return result;
+ case DataPoint[] ads: return new ObservableCollection();
+ default: return new ObservableCollection();
+ }
+
+ double GetStroke(double X1, double Step, double BigStep)
+ {
+ switch (X1)
+ {
+ case double when Math.Abs(X1) < Step / 5:
+ return 1.5d;
+ case double when Math.Abs((Math.Abs(X1) + Step / 5) % BigStep) < Step / 2:
+ return 0.8d;
+ default: return 0.3d;
+ }
+ }
+
+ }
+
+ private bool IsInWindow(System.Windows.Point p1, System.Windows.Size windowSize)
+ => p1.X >= 0 && p1.X <= windowSize.Width && p1.Y >= 0 && p1.Y <= windowSize.Height;
+
+ ///
+ /// Gets the adjusted rect.
+ ///
+ /// The c.
+ /// RectangleF.
+ public RectangleF GetAdjustedRect(SWindowPort c)
+ {
+ RectangleF port2;
+ if (Math.Abs(WindowSize.Width * c.port.Height) < Math.Abs(WindowSize.Height * c.port.Width))
+ port2 = new RectangleF(c.port.Left, (float)(c.port.Top + c.port.Height * 0.5f - WindowSize.Height * 0.5f * c.port.Width / (float)WindowSize.Width), c.port.Width, (float)(c.WindowSize.Height * c.port.Width / WindowSize.Width));
+ else
+ port2 = new RectangleF((float)(c.port.Left + c.port.Width * 0.5 - WindowSize.Width / 2 * c.port.Height / WindowSize.Height), c.port.Top, (float)(WindowSize.Width * c.port.Height / WindowSize.Height), c.port.Height);
+ return port2;
+ }
+
+ ///
+ /// Creates the line.
+ ///
+ /// The b.
+ /// The value.
+ /// The p1.
+ /// The p2.
+ /// FrameworkElement.
+ FrameworkElement CreateLine(System.Windows.Media.Brush b, double value, System.Windows.Point P1, System.Windows.Point P2)
+ {
+ return new Line()
+ {
+ X1 = P1.X,
+ Y1 = P1.Y,
+ X2 = P2.X,
+ Y2 = P2.Y,
+ Stroke = b,
+ StrokeThickness = value
+ };
+ }
+
+
+ ///
+ /// Creates the label.
+ ///
+ /// The y1.
+ /// The margin.
+ /// The va.
+ /// The ha.
+ /// FrameworkElement.
+ FrameworkElement CreateLabel(double Y1, Thickness margin, VerticalAlignment va, HorizontalAlignment ha)
+ {
+ return new Label()
+ {
+ Content = $"{Y1:0.###}",
+ Width = lb.Width,
+ Height = lb.Height,
+ VerticalAlignment = va,
+ HorizontalAlignment = ha,
+ Margin = margin
+ };
+ }
+
+ ///
+ /// Computes the grid steps.
+ ///
+ /// Width of the vp.
+ /// The big step.
+ /// The step.
+ private void ComputeGridSteps(double vpWidth, out double BigStep, out double Step)
+ {
+ BigStep = Math.Pow(10, Math.Floor(Math.Log10(vpWidth * AvgGrdPixel * 10 / WindowSize.Width)));
+ while (BigStep / vpWidth * WindowSize.Width < AvgGrdPixel * 3 / 1.5)
+ BigStep *= 2d;
+ while (BigStep / vpWidth * WindowSize.Width > AvgGrdPixel * 4.5)
+ BigStep *= 0.5d;
+ switch (BigStep / vpWidth * WindowSize.Width / AvgGrdPixel)
+ {
+ case double d when d > 3.5: Step = BigStep / 10; break;
+ case double d when d < 2.5:
+ Step = BigStep / 4; break;
+ default: Step = BigStep / 5; break;
+ }
+ }
+
+
+ ///
+ /// Converts a value.
+ ///
+ /// The value that is produced by the binding target.
+ /// The type to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value. If the method returns , the valid null value is used.
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/DataPointsView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/DataPointsView.xaml
new file mode 100644
index 000000000..aec4f52b1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/DataPointsView.xaml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/DataPointsView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/DataPointsView.xaml.cs
new file mode 100644
index 000000000..44adccedf
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_Laden/Views/DataPointsView.xaml.cs
@@ -0,0 +1,54 @@
+// ***********************************************************************
+// Assembly : MVVM_17_1_CSV_Laden
+// Author : Mir
+// Created : 07-03-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-04-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM_17_1_CSV_Laden.ViewModels;
+using MVVM_17_1_CSV_Laden.Views.Converter;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace MVVM_17_1_CSV_Laden.Views;
+
+///
+/// Interaktionslogik für DataPointsView.xaml
+///
+public partial class DataPointsView : Page
+{
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ public DataPointsView()
+ {
+ InitializeComponent();
+ DataContextChanged += (object? sender, DependencyPropertyChangedEventArgs e) =>
+ {
+ if (e.NewValue is DataPointsViewModel vm && this.Resources["vcPortGrid"] is WindowPortToGridLines pc)
+ {
+ // pc.Row = vm.Row;
+ // pc.Col = vm.Column;
+ pc.WindowSize = new Size(Width, Height);
+ }
+ };
+
+ var f = this.FindName("ViewPort") as FrameworkElement;
+ f.SizeChanged += (object? sender, SizeChangedEventArgs e) =>
+ {
+ if (this.Resources["vcPortGrid"] is WindowPortToGridLines pc)
+ {
+ pc.WindowSize = e.NewSize;
+ if (DataContext is DataPointsViewModel vm)
+ vm.WindowSize = e.NewSize;
+ }
+ };
+
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/MVVM_17_1_CSV_LadenTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/MVVM_17_1_CSV_LadenTests.csproj
new file mode 100644
index 000000000..fea95fd4e
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/MVVM_17_1_CSV_LadenTests.csproj
@@ -0,0 +1,38 @@
+
+
+
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/MVVM_17_1_CSV_Laden_netTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/MVVM_17_1_CSV_Laden_netTests.csproj
new file mode 100644
index 000000000..5452ccf42
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/MVVM_17_1_CSV_Laden_netTests.csproj
@@ -0,0 +1,41 @@
+
+
+
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-Windows
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/Views/Converter/WindowPortToGridLinesTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/Views/Converter/WindowPortToGridLinesTests.cs
new file mode 100644
index 000000000..246e34922
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_17_1_CSV_LadenTests/Views/Converter/WindowPortToGridLinesTests.cs
@@ -0,0 +1,231 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Collections.Generic;
+using MVVM_17_1_CSV_Laden.ViewModels;
+using System.Collections;
+
+namespace MVVM_17_1_CSV_Laden.Views.Converter.Tests;
+
+[TestClass()]
+public class WindowPortToGridLinesTests
+{
+ WindowPortToGridLines testVC;
+ SWindowPort wp;
+
+ public static IEnumerable ConvertTestData
+ {
+ get
+ {
+ yield return new object[] { new SWindowPort() { Parent = null!, port = new System.Drawing.RectangleF(-10, -10, 20, 20) } };
+ yield return new object[] { new SWindowPort() { Parent = null!, port = new System.Drawing.RectangleF(-10, -10, 20, 20) } };
+ }
+ }
+
+ [TestInitialize]
+ public void TestInit()
+ {
+ testVC = new WindowPortToGridLines();
+ testVC.WindowSize = new System.Windows.Size(200, 100);
+ wp = new SWindowPort() { Parent = null!, port = new System.Drawing.RectangleF(-10, -10, 20, 20) };
+ }
+
+ [TestMethod()]
+ public void WindowPortToGridLinesTest()
+ {
+ Assert.IsNotNull(testVC);
+ Assert.IsInstanceOfType(testVC, typeof(WindowPortToGridLines));
+ Assert.AreEqual(new System.Windows.Size(200, 100), testVC.WindowSize);
+ Assert.IsNotNull(wp);
+ Assert.IsInstanceOfType(wp, typeof(SWindowPort));
+ }
+
+ [TestMethod()]
+ [DynamicData(nameof(ConvertTestData))]
+ public void ConvertTest(object o)
+ {
+ var test = testVC.Convert(o, null!, null!, null!);
+ Assert.IsNotNull(test);
+ Assert.IsInstanceOfType(test, typeof(System.Collections.ObjectModel.ObservableCollection));
+ Assert.HasCount(19, test as IList);
+ }
+
+ [TestMethod()]
+ public void GetAdjustedRectTest()
+ {
+ var r2 = testVC.GetAdjustedRect(wp);
+ System.Drawing.RectangleF rExp = new(-20, -10, 40, 20);
+ Assert.AreEqual(rExp, r2);
+ }
+
+ [TestMethod()]
+ public void ConvertBackTest()
+ {
+ Assert.ThrowsExactly(() => testVC.ConvertBack(null!, null!, null!, null!));
+ }
+
+ private static void RunSTA(Action a)
+ {
+ Exception ex = null;
+ var t = new System.Threading.Thread(() =>
+ {
+ try { a(); }
+ catch (Exception e) { ex = e; }
+ });
+ t.SetApartmentState(System.Threading.ApartmentState.STA);
+ t.Start();
+ t.Join();
+ if (ex != null) throw ex;
+ }
+
+ [DataTestMethod]
+ [DataRow(200, 100, -10f, -10f, 20f, 20f, -20f, -10f, 40f, 20f)]
+ [DataRow(100, 200, -10f, -10f, 20f, 20f, -10f, -20f, 20f, 40f)]
+ public void GetAdjustedRect_Various_ReturnsExpected(
+ int winW, int winH,
+ float pL, float pT, float pW, float pH,
+ float eL, float eT, float eW, float eH)
+ {
+ RunSTA(() =>
+ {
+ var conv = new WindowPortToGridLines
+ {
+ WindowSize = new System.Windows.Size(winW, winH)
+ };
+ var wp = new SWindowPort
+ {
+ port = new System.Drawing.RectangleF(pL, pT, pW, pH),
+ WindowSize = new System.Windows.Size(winW, winH),
+ Parent = null!
+ };
+ var r2 = conv.GetAdjustedRect(wp);
+ var expected = new System.Drawing.RectangleF(eL, eT, eW, eH);
+ Assert.AreEqual(expected, r2);
+ });
+ }
+
+ [DataTestMethod]
+ [DataRow(200, 100)]
+ public void Convert_WindowPort_GeneratesGridAndLabels(int winW, int winH)
+ {
+ RunSTA(() =>
+ {
+ var conv = new WindowPortToGridLines
+ {
+ WindowSize = new System.Windows.Size(winW, winH)
+ };
+ var wp = new SWindowPort
+ {
+ port = new System.Drawing.RectangleF(-10, -10, 20, 20),
+ WindowSize = new System.Windows.Size(winW, winH),
+ Parent = null!
+ };
+
+ var result = conv.Convert(wp, null!, null!, System.Globalization.CultureInfo.InvariantCulture)
+ as System.Collections.ObjectModel.ObservableCollection;
+ Assert.IsNotNull(result);
+ Assert.IsNotEmpty(result, "Es wurden keine Elemente erzeugt.");
+
+ int axisCount = 0, majorCount = 0, minorCount = 0, labelZeroCount = 0, ellipseCount = 0;
+ foreach (var fe in result)
+ {
+ if (fe is System.Windows.Shapes.Line l)
+ {
+ var th = l.StrokeThickness;
+ if (Math.Abs(th - 1.5d) < 1e-6) axisCount++;
+ else if (Math.Abs(th - 0.8d) < 1e-6) majorCount++;
+ else if (Math.Abs(th - 0.3d) < 1e-6) minorCount++;
+ }
+ else if (fe is System.Windows.Controls.Label lab)
+ {
+ if (string.Equals(lab.Content?.ToString(), "0", StringComparison.Ordinal)) labelZeroCount++;
+ }
+ else if (fe is System.Windows.Shapes.Ellipse)
+ {
+ ellipseCount++;
+ }
+ }
+
+ Assert.IsGreaterThanOrEqualTo(2, axisCount, "Achsenlinien (Stärke 1.5) fehlen.");
+ Assert.IsGreaterThan(0, majorCount, "Haupt-Gitternetzlinien (Stärke 0.8) fehlen.");
+ Assert.IsGreaterThan(0, minorCount, "Neben-Gitternetzlinien (Stärke 0.3) fehlen.");
+ Assert.IsGreaterThanOrEqualTo(1, labelZeroCount, "Mindestens eine '0'-Beschriftung wird erwartet.");
+ Assert.AreEqual(1, ellipseCount, "Der Nullpunkt (Ellipse) wird erwartet.");
+ });
+ }
+
+ [DataTestMethod]
+ [DataRow(200, 100, -10.0, -10.0, 10.0, 10.0, 1)]
+ [DataRow(200, 100, 0.0, 0.0, 1000.0, 1000.0, 1)]
+ [DataRow(200, 100, -1000.0, -1000.0, 1000.0, 1000.0, 0)]
+ public void Convert_DataPoints_FiltersByViewport(int winW, int winH,
+ double x1, double y1, double x2, double y2, int expectedLines)
+ {
+ RunSTA(() =>
+ {
+ var conv = new WindowPortToGridLines
+ {
+ WindowSize = new System.Windows.Size(winW, winH)
+ };
+
+ // Erst Viewport konvertieren, um actPort zu setzen
+ var wp = new SWindowPort
+ {
+ port = new System.Drawing.RectangleF(-10, -10, 20, 20),
+ WindowSize = new System.Windows.Size(winW, winH),
+ Parent = null!
+ };
+ _ = conv.Convert(wp, null!, null!, System.Globalization.CultureInfo.InvariantCulture);
+
+ var ds = new System.Collections.ObjectModel.ObservableCollection
+ {
+ new Model.DataPoint { X = x1, Y = y1 },
+ new Model.DataPoint { X = x2, Y = y2 }
+ };
+
+ var res = conv.Convert(ds, null!, null!, System.Globalization.CultureInfo.InvariantCulture)
+ as System.Collections.ObjectModel.ObservableCollection;
+ Assert.IsNotNull(res);
+
+ int lineCount = 0;
+ foreach (var fe in res)
+ if (fe is System.Windows.Shapes.Line) lineCount++;
+
+ Assert.AreEqual(expectedLines, lineCount, "Anzahl der erzeugten Datenpunkt-Linien entspricht nicht der Erwartung.");
+ });
+ }
+
+ [TestMethod]
+ public void Convert_DataPointArray_ReturnsEmpty()
+ {
+ RunSTA(() =>
+ {
+ var conv = new WindowPortToGridLines
+ {
+ WindowSize = new System.Windows.Size(200, 100)
+ };
+
+ // actPort initialisieren
+ var wp = new SWindowPort
+ {
+ port = new System.Drawing.RectangleF(-10, -10, 20, 20),
+ WindowSize = new System.Windows.Size(200, 100),
+ Parent = null!
+ };
+ _ = conv.Convert(wp, null, null, System.Globalization.CultureInfo.InvariantCulture);
+
+ var arr = new Model.DataPoint[0];
+ var res = conv.Convert(arr, null, null, System.Globalization.CultureInfo.InvariantCulture)
+ as System.Collections.ObjectModel.ObservableCollection;
+ Assert.IsNotNull(res);
+ Assert.IsEmpty(res);
+ });
+ }
+
+ [TestMethod]
+ public void NSubstitute_DummyUsage_ForLibraryPresence()
+ {
+ var d = NSubstitute.Substitute.For();
+ d.Dispose();
+ Assert.IsNotNull(d);
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/App.xaml b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/App.xaml
new file mode 100644
index 000000000..3727fe99a
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/App.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/App.xaml.cs
new file mode 100644
index 000000000..25f841e39
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/App.xaml.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_18_MultiConverters
+// Author : Mir
+// Created : 07-05-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-05-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_18_MultiConverters;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/AssemblyInfo.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/AssemblyInfo.cs
new file mode 100644
index 000000000..3da8344a6
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+// ***********************************************************************
+// Assembly : MVVM_18_MultiConverters
+// Author : Mir
+// Created : 07-05-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-05-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MVVM_18_MultiConverters.csproj b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MVVM_18_MultiConverters.csproj
new file mode 100644
index 000000000..d6a4fe949
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MVVM_18_MultiConverters.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+ WinExe
+ net462;net472;net48;net481
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MVVM_18_MultiConverters_net.csproj b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MVVM_18_MultiConverters_net.csproj
new file mode 100644
index 000000000..36688343f
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MVVM_18_MultiConverters_net.csproj
@@ -0,0 +1,27 @@
+
+
+
+
+ WinExe
+ net8.0-windows
+ true
+
+
+
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MainWindow.xaml b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MainWindow.xaml
new file mode 100644
index 000000000..f17fcc864
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MainWindow.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MainWindow.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MainWindow.xaml.cs
new file mode 100644
index 000000000..93547271b
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/MainWindow.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_18_MultiConverters
+// Author : Mir
+// Created : 07-05-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-05-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows;
+
+namespace MVVM_18_MultiConverters;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/Model/DateDifFormat.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/Model/DateDifFormat.cs
new file mode 100644
index 000000000..aa0751404
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/Model/DateDifFormat.cs
@@ -0,0 +1,38 @@
+// ***********************************************************************
+// Assembly : MVVM_18_MultiConverters
+// Author : Mir
+// Created : 07-05-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-05-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+
+namespace MVVM_18_MultiConverters.Model;
+
+///
+/// Enum DateDifFormat
+///
+public enum DateDifFormat
+{
+ ///
+ /// The days
+ ///
+ Days,
+ ///
+ /// The hours
+ ///
+ Hours,
+ ///
+ /// The minutes
+ ///
+ Minutes,
+ ///
+ /// The seconds
+ ///
+ Seconds
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/ValueConverter/TimeSpanConverter.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/ValueConverter/TimeSpanConverter.cs
new file mode 100644
index 000000000..22a1a1060
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/ValueConverter/TimeSpanConverter.cs
@@ -0,0 +1,73 @@
+// ***********************************************************************
+// Assembly : MVVM_18_MultiConverters
+// Author : Mir
+// Created : 07-05-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-05-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM_18_MultiConverters.Model;
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_18_MultiConverters.ValueConverter;
+
+///
+/// Class TimeSpanConverter.
+/// Implements the
+///
+///
+public class TimeSpanConverter : IMultiValueConverter
+{
+ ///
+ /// Converts source values to a value for the binding target. The data binding engine calls this method when it propagates the values from source bindings to the binding target.
+ ///
+ /// The array of values that the source bindings in the produces. The value indicates that the source binding has no value to provide for conversion.
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// A converted value.
+ /// If the method returns , the valid value is used.
+ /// A return value of . indicates that the converter did not produce a value, and that the binding will use the if it is available, or else will use the default value.
+ /// A return value of . indicates that the binding does not transfer the value or use the or the default value.
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ DateDifFormat df = DateDifFormat.Days;
+ if (values?.Length >= 2 && values[1] is DateDifFormat _d)
+ df = _d;
+
+ if (values?.Length >= 1 && values[0] is TimeSpan ts)
+ {
+ return df switch
+ {
+ DateDifFormat.Days => ts.TotalDays.ToString((string)parameter),
+ DateDifFormat.Hours => ts.TotalHours.ToString((string)parameter),
+ DateDifFormat.Minutes => ts.TotalMinutes.ToString((string)parameter),
+ DateDifFormat.Seconds => ts.TotalSeconds.ToString((string)parameter),
+ _ => "0",
+ };
+ }
+ else
+ return "0";
+ }
+
+ ///
+ /// Converts a binding target value to the source binding values.
+ ///
+ /// The value that the binding target produces.
+ /// The array of types to convert to. The array length indicates the number and types of values that are suggested for the method to return.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// An array of values that have been converted from the target value back to the source values.
+ ///
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/View/DateDifView.xaml b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/View/DateDifView.xaml
new file mode 100644
index 000000000..25f2cb346
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/View/DateDifView.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/View/DateDifView.xaml.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/View/DateDifView.xaml.cs
new file mode 100644
index 000000000..59ebe44e7
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/View/DateDifView.xaml.cs
@@ -0,0 +1,30 @@
+// ***********************************************************************
+// Assembly : MVVM_18_MultiConverters
+// Author : Mir
+// Created : 07-05-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-05-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using System.Windows.Controls;
+
+namespace MVVM_18_MultiConverters.View;
+
+///
+/// Interaktionslogik für DateDifView.xaml
+///
+public partial class DateDifView : Page
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DateDifView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/ViewModels/DateDifViewModel.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/ViewModels/DateDifViewModel.cs
new file mode 100644
index 000000000..9bb046fd1
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConverters/ViewModels/DateDifViewModel.cs
@@ -0,0 +1,72 @@
+// ***********************************************************************
+// Assembly : MVVM_18_MultiConverters
+// Author : Mir
+// Created : 07-05-2022
+//
+// Last Modified By : Mir
+// Last Modified On : 07-05-2022
+// ***********************************************************************
+//
+// Copyright (c) JC-Soft. All rights reserved.
+//
+//
+// ***********************************************************************
+using MVVM.ViewModel;
+using MVVM_18_MultiConverters.Model;
+using System;
+
+namespace MVVM_18_MultiConverters.ViewModels;
+
+///
+/// Class DateDifViewModel.
+/// Implements the
+///
+///
+public class DateDifViewModel : BaseViewModel
+{
+ public static Func GetNow { get; set; } = () => DateTime.Now;
+ ///
+ /// The start date
+ ///
+ private DateTime _startDate = GetNow().AddDays(-30);
+ ///
+ /// The end date
+ ///
+ private DateTime _endDate = GetNow();
+ ///
+ /// The format
+ ///
+ private DateDifFormat _format = DateDifFormat.Days;
+ ///
+ /// Gets or sets the start date.
+ ///
+ /// The start date.
+ public DateTime StartDate { get => _startDate; set => SetProperty(ref _startDate, value); }
+ ///
+ /// Gets or sets the end date.
+ ///
+ /// The end date.
+ public DateTime EndDate { get => _endDate; set => SetProperty(ref _endDate, value); }
+ ///
+ /// Gets or sets the format.
+ ///
+ /// The format.
+ public DateDifFormat Format { get => _format; set => SetProperty(ref _format, value); }
+
+ ///
+ /// Gets the date dif.
+ ///
+ /// The date dif.
+ public TimeSpan DateDif { get => _endDate - _startDate; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DateDifViewModel()
+ {
+ AddPropertyDependency(nameof(DateDif), nameof(StartDate));
+ AddPropertyDependency(nameof(DateDif), nameof(EndDate));
+ AddPropertyDependency(nameof(DateDif), nameof(Format));
+ }
+
+}
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/Helper/.info b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/Helper/.info
new file mode 100644
index 000000000..3f85a7822
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/Helper/.info
@@ -0,0 +1 @@
+This is an empty folder for linked Files ...
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/MVVM_18_MultiConvertersTests.csproj b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/MVVM_18_MultiConvertersTests.csproj
index 0d9985abb..6451e73e0 100644
--- a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/MVVM_18_MultiConvertersTests.csproj
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/MVVM_18_MultiConvertersTests.csproj
@@ -1,12 +1,18 @@
- net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows
+ net8.0-windows
false
-
+
+ $(TargetFrameworks);net9.0-windows
+
+
+ $(TargetFrameworks);net10.0-windows
+
+
@@ -17,8 +23,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/ValueConverter/TimeSpanConverterTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/ValueConverter/TimeSpanConverterTests.cs
new file mode 100644
index 000000000..cb0564c1d
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/ValueConverter/TimeSpanConverterTests.cs
@@ -0,0 +1,53 @@
+using BaseLib.Helper;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MVVM_18_MultiConverters.Model;
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace MVVM_18_MultiConverters.ValueConverter.Tests;
+
+[TestClass()]
+public class TimeSpanConverterTests
+{
+ TimeSpanConverter testConverter;
+
+ [TestInitialize]
+ public void Init() {
+ testConverter = new TimeSpanConverter();
+ CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
+ CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
+ }
+
+ [TestMethod()]
+ public void SetupTest()
+ {
+ Assert.IsNotNull(testConverter);
+ Assert.IsInstanceOfType(testConverter,typeof(IMultiValueConverter));
+ Assert.IsInstanceOfType(testConverter, typeof(TimeSpanConverter));
+ }
+
+ [TestMethod()]
+ [DataRow("Null", TypeCode.String, null,null,"0")]
+ [DataRow("Null2", TypeCode.String, new object[] { }, null, "0")]
+ [DataRow("1", TypeCode.String, new object[] {"1" }, null, "0")]
+ [DataRow("3.6 Days", TypeCode.String, new object[] {(double)3.6d }, null, "3.6")]
+ [DataRow("4.1 Days", TypeCode.String, new object[] { 4.1d, DateDifFormat.Hours }, null, "98.39999999997222")]
+ [DataRow("5.9 Days", TypeCode.String, new object[] { 5.9d, DateDifFormat.Minutes }, null, "8496")]
+ [DataRow("6.001 Days", TypeCode.String, new object[] { 6.001d, DateDifFormat.Seconds }, null, "518486.4")]
+ [DataRow("6.501 Days", TypeCode.String, new object[] { 6.501d, (DateDifFormat)4 }, null, "0")]
+ [DataRow("7.123 Days", TypeCode.String, new object[] { 7.123d, "Weeks" }, null, "7.123")]
+ public void ConvertTest(string name, TypeCode tc, object[] oVal, object oPar, object oExp)
+ {
+ if (oVal?.Length > 0 && oVal[0] is decimal d) oVal[0] = TimeSpan.FromDays((double)d);
+ if (oVal?.Length > 0 && oVal[0] is double d2) oVal[0] = TimeSpan.FromDays(d2);
+ var tVal = tc.ToType();
+ Assert.AreEqual(oExp,testConverter.Convert(oVal,tVal,oPar,CultureInfo.InvariantCulture), $"Convert({name}).result");
+ }
+
+ [TestMethod()]
+ public void ConvertBackTest()
+ {
+ Assert.ThrowsExactly(()=>testConverter.ConvertBack(null!,null!,null!,null!));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/View/DateDifViewTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/View/DateDifViewTests.cs
new file mode 100644
index 000000000..bf54e2191
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/View/DateDifViewTests.cs
@@ -0,0 +1,22 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_18_MultiConverters.View.Tests;
+
+[TestClass()]
+public class DateDifViewTests
+{
+ DateDifView? testView;
+
+ [TestMethod()]
+ public void DateDifViewTest()
+ {
+ testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(DateDifView));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/View/MainWindowTests.cs b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/View/MainWindowTests.cs
new file mode 100644
index 000000000..cb3b16d99
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_18_MultiConvertersTests/View/MainWindowTests.cs
@@ -0,0 +1,22 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Threading;
+
+namespace MVVM_18_MultiConverters.View.Tests;
+
+[TestClass()]
+public class MainWindowTests
+{
+ MainWindow? testView;
+
+ [TestMethod()]
+ public void MainWindowTest()
+ {
+ testView = null;
+ var t = new Thread(() => testView = new());
+ t.SetApartmentState(ApartmentState.STA); //Set the thread to STA
+ t.Start();
+ t.Join(); //Wait for the thread to end
+ Assert.IsNotNull(testView);
+ Assert.IsInstanceOfType(testView, typeof(MainWindow));
+ }
+}
\ No newline at end of file
diff --git a/CSharpBible/MVVM_Tutorial/MVVM_19_FilterLists/App.config b/CSharpBible/MVVM_Tutorial/MVVM_19_FilterLists/App.config
new file mode 100644
index 000000000..51cf15f82
--- /dev/null
+++ b/CSharpBible/MVVM_Tutorial/MVVM_19_FilterLists/App.config
@@ -0,0 +1,26 @@
+
+
+
+
+