Added NuGet package build recipe
This commit is contained in:
parent
9fa86e97f0
commit
69eba99b71
|
@ -1 +1 @@
|
||||||
Subproject commit 54cab3a63f4f678a3173dee3085d45268c560e8c
|
Subproject commit e818fae81012b0a819abcf2180b90eee1d998fb7
|
246
ReadMe.md
Normal file
246
ReadMe.md
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
Nuclex Foundation Libraries
|
||||||
|
===========================
|
||||||
|
|
||||||
|
A set of clean and carefully designed utility classes for .NET.
|
||||||
|
|
||||||
|
For specific documentation, see the individual projects in this package.
|
||||||
|
|
||||||
|
|
||||||
|
Settings (`.ini` / Windows Registry)
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Many applications have to store their settings in an external file or,
|
||||||
|
for pure Windows applications, in the registry. This can be tedious and
|
||||||
|
difficult to unit test, too. Nuclex.Support provides an autonomous ini
|
||||||
|
parser (which works cross-platform and does **not** rely on
|
||||||
|
`GetPrivateProfileString`).
|
||||||
|
|
||||||
|
Furthermore, it uses an interface to provide the same functionality for
|
||||||
|
the Registry and in-memory settings. This lets you switch between storing
|
||||||
|
your settings in the registry, in an .ini file or constructing a settings
|
||||||
|
container in memory to appropriately unit-test your code with mock data.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
static readonly string BasicCategoryName = "Basic";
|
||||||
|
static readonly string HintsCategoryName = "Hints";
|
||||||
|
|
||||||
|
void saveSettings(ISettingStore settingsStore) {
|
||||||
|
settingsStore.Set(BasicCategoryName, "AskSaveOnQuit", this.askSaveOnQuit);
|
||||||
|
settingsStore.Set(BasicCategoryName, "ActivePanel", this.activePanelIndex);
|
||||||
|
settingsStore.Set(HintsCategoryName, "ShowNameHint", this.showNameHint);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveSettingsToIni() {
|
||||||
|
var iniStore = new ConfigurationFileStore();
|
||||||
|
saveSettings(iniStore);
|
||||||
|
|
||||||
|
using(var writer = new StreamWriteR("awesome-app.ini")) {
|
||||||
|
iniStore.Save(writer);
|
||||||
|
writer.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveSettingsToRegistry() {
|
||||||
|
using(
|
||||||
|
var registryStore = new WindowsRegistryStore(
|
||||||
|
RegistryHive.HKCU, "AwesomeApplication"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
saveSettings(registryStore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
MVVM with Nuclex.Windows.Forms
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
This library implements the MVVM pattern through its `WindowManager` class:
|
||||||
|
|
||||||
|
![The WindowManager and its related classes](./Documents/WindowManager.svg)
|
||||||
|
|
||||||
|
The `WindowManager` keeps track of all open windows and their view models,
|
||||||
|
so your basic `Main()` method, which normally looks like this:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[STAThread]
|
||||||
|
static void Main() {
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now becomes this:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[STAThread]
|
||||||
|
static void Main() {
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
|
||||||
|
using(var windowManager = new WindowManager()) {
|
||||||
|
Application.Run(windowManager.OpenRoot<MainViewModel>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, we no longer mention the `MainForm` by name, instead we ask
|
||||||
|
the `WindowManager` to construct a new `MainViewModel` and also create a view
|
||||||
|
that displays it.
|
||||||
|
|
||||||
|
It does so by using a "convention over configuration" approach, meaning it
|
||||||
|
assumes that if you request a view for `FlurgleSettingsViewModel`, it will
|
||||||
|
look for a view named `FlurgleSettingsView`, `FlurgleSettingsForm`,
|
||||||
|
`FlurgeSettingsWindow` or `FlurgleSettingsDialog` class and try to construct
|
||||||
|
an instance of that class.
|
||||||
|
|
||||||
|
Furthermore, if that class implements the `IView` interface, the view model
|
||||||
|
will be assigned to its `DataContext` property, establishing
|
||||||
|
the View/ViewModel relationship.
|
||||||
|
|
||||||
|
|
||||||
|
Adding an IoC Container (Ninject) to the MVVM Example
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
In the previous example, the view and its view model were constructed using
|
||||||
|
`Activator.CreateInstance()` - a method provided by .NET that creates a new
|
||||||
|
instance via a type's default constructor.
|
||||||
|
|
||||||
|
Most of the time, ViewModels have constructor parameters, however. For example
|
||||||
|
to provide the ViewModel with the data it is supposed to be an adapter for.
|
||||||
|
You can achieve that by constructing the ViewModel yourself and passing it
|
||||||
|
to the `WindowManager.OpenRoot()` or `WindowManager.ShowModal()` methods.
|
||||||
|
|
||||||
|
A much better approach is to use a dependency injector - an IoC container with
|
||||||
|
automatic constructor parameter injection. My favorite one is Ninject (due to
|
||||||
|
its neat setup with a fluent interface), but you can use any container you
|
||||||
|
wish, simply by inheriting your own `WindowManager` class:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class NinjectWindowManager : WindowManager {
|
||||||
|
|
||||||
|
public NinjectWindowManager(IKernel kernel, IAutoBinder autoBinder = null) :
|
||||||
|
base(autoBinder) {
|
||||||
|
this.kernel = kernel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override object CreateInstance(Type type) {
|
||||||
|
return this.kernel.Get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IKernel kernel;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Your `NinjectWindowManager` will now use `IKernel.Get()` to construct its
|
||||||
|
ViewModels, allowing their constructors to require any services and instances
|
||||||
|
you have set up in your Ninject kernel.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class MainViewModel {
|
||||||
|
|
||||||
|
public MainViewModel(IMyService myService, IMySettings mySettings) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Observable Base Class for Data Binding and ORMs
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
.NET provides the `INotifyPropertyChanged` interface for objects to expose
|
||||||
|
an event that reports when a property of the object has changed. This is
|
||||||
|
used by data binding UI controls and some ORMs to detect when an object has
|
||||||
|
been changed and the UI or database need to be updated.
|
||||||
|
|
||||||
|
It is a bit tedious to implement, so here's a base class to make it much
|
||||||
|
more pleasant to use:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class CreateUserViewModel : Observable {
|
||||||
|
|
||||||
|
public string FirstName {
|
||||||
|
get { return this.firstName; }
|
||||||
|
set {
|
||||||
|
if(value != this.firstName) {
|
||||||
|
this.firstName = value;
|
||||||
|
OnPropertyChanged(nameof(FirstName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string firstName;
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There's an extension method for the consuming side, too, with proper handling
|
||||||
|
of *wildcard* change notifications that are often overlooked:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
CreateUserViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
|
void onPropertyChanged(object sender, PropertyChangedEventArgs arguments) {
|
||||||
|
if(arguments.AreAffecting(nameof(ViewModel.FirstName))) {
|
||||||
|
this.firstNameLine.Text = ViewModel.FirstName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Cloning Objects
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Whether you use the prototye design patten on complex objects or have another
|
||||||
|
reason, sometimes a deep clone of an object tree is needed. This library
|
||||||
|
provides three complete solutions to cloning objects in .NET:
|
||||||
|
|
||||||
|
- The `SerializationCloner`. It uses .NET's BinarySerializer in a way that
|
||||||
|
will serialize your object tree regardless of whether your objects have
|
||||||
|
the `Serializable` attribute or not. This is the slowest, least efficient
|
||||||
|
object cloner, but it relies on built-in .NET classes only.
|
||||||
|
|
||||||
|
- The `ReflectionCloner` uses .NET's reflection capabilities (that means
|
||||||
|
interrogating an object what fields and properties it has) to create
|
||||||
|
complete clones of an object, including any arrays and referenced objects.
|
||||||
|
This serializer has no setup time and has pretty decent performance.
|
||||||
|
|
||||||
|
- The `ExpressionTreeCloner` uses Linq expression trees to generate tailored
|
||||||
|
cloning code for your classes at runtime. This method of cloning has a setup
|
||||||
|
time (meaning it takes longer the first time it is confronted with a new
|
||||||
|
class), but from the second clone onwards, is much faster than the others.
|
||||||
|
|
||||||
|
All three object cloners can create *shallow clones* (meaning any references
|
||||||
|
to other object will be kept without copying the referenced objects, too) and
|
||||||
|
*deep clones* meaning any refeferenced objects (and their referenced objects)
|
||||||
|
will be cloned as well. Careful, this means event subscribers, such a forms
|
||||||
|
and unexpected hangers-on will be cloned, too.
|
||||||
|
|
||||||
|
Furthermore, all three object cloners can create *property-based clones*
|
||||||
|
(where only those settings exposed via properties are cloned), which may skip
|
||||||
|
the non-exposed parts of an object, as well as *field-based clones* which
|
||||||
|
replicate all the data of a class - any private field and hidden state.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class Example {
|
||||||
|
public Example(Example child = null) {
|
||||||
|
Child = child;
|
||||||
|
}
|
||||||
|
public Example Child { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
public static void CloneSomething() {
|
||||||
|
var test = new Example(new Example());
|
||||||
|
|
||||||
|
var reflectionCloner = new ReflectionCloner();
|
||||||
|
var clone = reflectionCloner.DeepFieldClone(test);
|
||||||
|
|
||||||
|
// Clone is now a complete copy of test, including the child object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
35
foundation-package-old.nuspec
Normal file
35
foundation-package-old.nuspec
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
|
<metadata>
|
||||||
|
<id>Nuclex.Foundation</id>
|
||||||
|
<version>1.1.0</version>
|
||||||
|
<title>Nuclex Foundation Libraries</title>
|
||||||
|
<authors>Markus Ewald</authors>
|
||||||
|
<owners></owners>
|
||||||
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
|
<license type="expression">Apache 2.0</license>
|
||||||
|
<description>Set of basic libraries offering tools for MVVM-based UIs, storage of settings, license keys, collections and observer/subscriber pattern helpers</description>
|
||||||
|
<language>en-US</language>
|
||||||
|
<tags>mvvm,util</tags>
|
||||||
|
<repository type="svn" url="https://devel.nuclex.org/framework/svn" commit="3469" />
|
||||||
|
<repository type="git" url="https://github.com/nuclex-shared-dotnet/Nuclex.Support.git" commit="a59facff90816bb3d7d53761ffea43c0102e6047" />
|
||||||
|
<repository type="git" url="https://github.com/nuclex-shared-dotnet/Nuclex.Windows.Forms.git" commit="7a0ae3b3c70da4a1202d2d5fc5fe6ebf6c984b6e" />
|
||||||
|
<repository type="git" url="https://github.com/nuclex-shared-dotnet/Nuclex.Windows.Forms.Ninject.git" commit="69842a3790dc2e3ce87f1b82694dd4cdc218fff8" />
|
||||||
|
<dependencies>
|
||||||
|
<group targetFramework=".NETFramework4.62">
|
||||||
|
<dependency id="NUnit" version="[2.6.0, 3.0.0)" />
|
||||||
|
<dependency id="NMock3" version="3.0.0" />
|
||||||
|
</group>
|
||||||
|
<group targetFramework=".NETFramework4.0">
|
||||||
|
<dependency id="NUnit" version="[2.6.0, 3.0.0)" />
|
||||||
|
<dependency id="NMock3" version="3.0.0" />
|
||||||
|
</group>
|
||||||
|
</dependencies>
|
||||||
|
<dependencies />
|
||||||
|
</metadata>
|
||||||
|
<files>
|
||||||
|
<file src="MultiLib.Project\bin\$configuration$\netstandard2.0\MultiLib.Project.dll" target="lib/netstandard2.0" />
|
||||||
|
<file src="MultiLib.Project\bin\$configuration$\netstandard2.0\MultiLib.Dependency1.dll" target="lib/netstandard2.0" />
|
||||||
|
<file src="MultiLib.Project\bin\$configuration$\netstandard2.0\MultiLib.Dependency2.dll" target="lib/netstandard2.0" />
|
||||||
|
</files>
|
||||||
|
</package>
|
|
@ -13,26 +13,49 @@
|
||||||
<description>Set of basic libraries offering tools for MVVM-based UIs, storage of settings, license keys, collections and observer/subscriber pattern helpers</description>
|
<description>Set of basic libraries offering tools for MVVM-based UIs, storage of settings, license keys, collections and observer/subscriber pattern helpers</description>
|
||||||
<language>en-US</language>
|
<language>en-US</language>
|
||||||
<tags>mvvm,util</tags>
|
<tags>mvvm,util</tags>
|
||||||
|
<icon>nuclex-logo.png</icon>
|
||||||
<repository type="git" url="https://github.com/nuclex-shared-dotnet/Nuclex.Support.git" commit="a59facff90816bb3d7d53761ffea43c0102e6047" />
|
<repository type="git" url="https://github.com/nuclex-shared-dotnet/Nuclex.Support.git" commit="a59facff90816bb3d7d53761ffea43c0102e6047" />
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<group targetFramework=".NETFramework4.6.2" />
|
|
||||||
<group targetFramework=".NETStandard2.0" />
|
<group targetFramework=".NETStandard2.0" />
|
||||||
<group targetFramework="net6.0-windows7.0" />
|
<group targetFramework=".NETFramework4.6.2">
|
||||||
|
<dependency id="Ninject" version="[3.3.6, 4.0.0)" exclude="Build,Analyzers" />
|
||||||
|
</group>
|
||||||
|
<group targetFramework="net6.0-windows7.0">
|
||||||
|
<dependency id="Ninject" version="[3.3.6, 4.0.0)" exclude="Build,Analyzers" />
|
||||||
|
</group>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<frameworkReferences>
|
<frameworkReferences>
|
||||||
|
<group targetFramework=".NETFramework4.6.2" />
|
||||||
<group targetFramework="net6.0-windows7.0">
|
<group targetFramework="net6.0-windows7.0">
|
||||||
<frameworkReference name="Microsoft.WindowsDesktop.App.WindowsForms" />
|
<frameworkReference name="Microsoft.WindowsDesktop.App.WindowsForms" />
|
||||||
</group>
|
</group>
|
||||||
<group targetFramework=".NETFramework4.6.2" />
|
|
||||||
</frameworkReferences>
|
</frameworkReferences>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
|
|
||||||
|
<file src="ReadMe.md" target="ReadMe.md" />
|
||||||
|
<file src="nuclex-logo.png" target="nuclex-logo.png" />
|
||||||
|
|
||||||
|
<!-- Feature set for .NET Standard 2.0 (available to ,NET versions from 4.6 onwards) -->
|
||||||
<file src="Nuclex.Support\bin\$Configuration$\netstandard2.0\Nuclex.Support.dll" target="lib/netstandard2.0" />
|
<file src="Nuclex.Support\bin\$Configuration$\netstandard2.0\Nuclex.Support.dll" target="lib/netstandard2.0" />
|
||||||
<file src="Nuclex.Support\bin\$Configuration$\netstandard2.0\Nuclex.Support.xml" target="lib/netstandard2.0" />
|
<file src="Nuclex.Support\bin\$Configuration$\netstandard2.0\Nuclex.Support.xml" target="lib/netstandard2.0" />
|
||||||
|
|
||||||
|
<!-- Feature set for .NET Framework 4.6.2 (with Windows.Forms) -->
|
||||||
|
<file src="Nuclex.Support\bin\$Configuration$\netstandard2.0\Nuclex.Support.dll" target="lib/net462" />
|
||||||
|
<file src="Nuclex.Support\bin\$Configuration$\netstandard2.0\Nuclex.Support.xml" target="lib/net462" />
|
||||||
<file src="Nuclex.Windows.Forms\bin\$Configuration$\net462\Nuclex.Windows.Forms.dll" target="lib/net462" />
|
<file src="Nuclex.Windows.Forms\bin\$Configuration$\net462\Nuclex.Windows.Forms.dll" target="lib/net462" />
|
||||||
<file src="Nuclex.Windows.Forms\bin\$Configuration$\net6.0-windows\Nuclex.Windows.Forms.xml" target="lib/net462" />
|
<file src="Nuclex.Windows.Forms\bin\$Configuration$\net462\Nuclex.Windows.Forms.xml" target="lib/net462" />
|
||||||
<file src="Nuclex.Windows.Forms.Ninject\bin\$Configuration$\net462\Nuclex.Windows.Forms.Ninject.dll" target="lib/net6.0-windows7.0" />
|
<file src="Nuclex.Windows.Forms.Ninject\bin\$Configuration$\net462\Nuclex.Windows.Forms.Ninject.dll" target="lib/net462" />
|
||||||
|
<file src="Nuclex.Windows.Forms.Ninject\bin\$Configuration$\net462\Nuclex.Windows.Forms.Ninject.xml" target="lib/net462" />
|
||||||
|
|
||||||
|
<!-- Featue set for .NET Framework 4.6.2 (with Windows.Forms) -->
|
||||||
|
<!-- Btw, '-windows7'.0 means Windows Seven, the one between Vista and 8, not the internal Windows relaese number -->
|
||||||
|
<file src="Nuclex.Support\bin\$Configuration$\netstandard2.0\Nuclex.Support.dll" target="lib/net6.0-windows7.0" />
|
||||||
|
<file src="Nuclex.Support\bin\$Configuration$\netstandard2.0\Nuclex.Support.xml" target="lib/net6.0-windows7.0" />
|
||||||
|
<file src="Nuclex.Windows.Forms\bin\$Configuration$\net6.0-windows\Nuclex.Windows.Forms.dll" target="lib/net6.0-windows7.0" />
|
||||||
|
<file src="Nuclex.Windows.Forms\bin\$Configuration$\net6.0-windows\Nuclex.Windows.Forms.xml" target="lib/net6.0-windows7.0" />
|
||||||
|
<file src="Nuclex.Windows.Forms.Ninject\bin\$Configuration$\net6.0-windows\Nuclex.Windows.Forms.Ninject.dll" target="lib/net6.0-windows7.0" />
|
||||||
<file src="Nuclex.Windows.Forms.Ninject\bin\$Configuration$\net6.0-windows\Nuclex.Windows.Forms.Ninject.xml" target="lib/net6.0-windows7.0" />
|
<file src="Nuclex.Windows.Forms.Ninject\bin\$Configuration$\net6.0-windows\Nuclex.Windows.Forms.Ninject.xml" target="lib/net6.0-windows7.0" />
|
||||||
<file src="Nuclex.Support\ReadMe.md" target="ReadMe.md" />
|
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
BIN
nuclex-logo.png
Normal file
BIN
nuclex-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
Loading…
Reference in New Issue
Block a user