# BionicCode.Net [![GitHub](https://img.shields.io/github/license/BionicCode/BionicCode.Net)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--) [![Release](https://img.shields.io/github/last-commit/BionicCode/BionicCode.Net)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--) 
.NET libraries containing utilities and controls that target .NET Standard, .NET Core, .NET Framework, .NET Core WPF, .NET Framework WPF and UWP

[![BC](https://img.shields.io/badge/.NET-5-informational)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)
[![BC](https://img.shields.io/badge/.NET-Core-informational)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)
[![BC](https://img.shields.io/badge/.NET-Standard-informational)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)
[![BC](https://img.shields.io/badge/.NET-Framework-informational)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)

[![BC](https://img.shields.io/badge/-WPF-informational?logo=windows)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)
[![BC](https://img.shields.io/badge/-UWP-informational?logo=windows)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)
[![BC](https://img.shields.io/badge/-Windows_Forms-informational?logo=windows)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)
[![BC](https://img.shields.io/badge/-Linux-informational?logo=linux)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)
[![BC](https://img.shields.io/badge/-macOS-informational?logo=apple)](https://github.com/BionicCode/BionicCode.Net#bioniccodenet--)



## NuGet Packages Download
[![BionicCode.Utilities.Net.Wpf](https://img.shields.io/badge/.NET_5_WPF-BionicCode.Utilities.Net.Wpf-orange)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Wpf/)[![BionicCode.Utilities.Net.Wpf.NuGet](https://img.shields.io/nuget/v/BionicCode.Utilities.Net.Wpf.svg?logo=nuget)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Wpf/)

[![BionicCode.Utilities.Net.Core.Wpf](https://img.shields.io/badge/.NET_Core_3.0_WPF-BionicCode.Utilities.Net.Core.Wpf-orange)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Core.Wpf/)[![BionicCode.Utilities.Net.Core.Wpf.NuGet](https://img.shields.io/nuget/v/BionicCode.Utilities.Net.Core.Wpf.svg?logo=nuget)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Core.Wpf/)
<!--
[![NuGet Downloads](https://img.shields.io/nuget/dt/BionicCode.Utilities.Net.Core.Wpf.svg)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Core.Wpf/)
-->

[![BionicCode.Utilities.Net.Core](https://img.shields.io/badge/.NET_Core_3.0-BionicCode.Utilities.Net.Core-orange)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Core/)[![BionicCode.Utilities.Net.Core.Wpf.NuGet](https://img.shields.io/nuget/v/BionicCode.Utilities.Net.Core.svg?logo=nuget)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Core/)
<!--
[![NuGet Downloads](https://img.shields.io/nuget/dt/BionicCode.Utilities.Net.Core.Wpf.svg)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Core.Wpf/)
-->

[![BionicCode.Utilities.Net.Standard](https://img.shields.io/badge/.NET_Standard_2.0-BionicCode.Utilities.Net.Standard-orange)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Standard/)[![BionicCode.Utilities.Net.Standard.NuGet](https://img.shields.io/nuget/v/BionicCode.Utilities.Net.Standard.svg?logo=nuget)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Standard/)

[![BionicCode.Utilities.Net.Framework.Wpf](https://img.shields.io/badge/.NET_Framework_4.8_WPF-BionicCode.Utilities.Net.Framework.Wpf-orange)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Framework.Wpf/)[![BionicCode.Utilities.Net.Framework.Wpf.NuGet](https://img.shields.io/nuget/v/BionicCode.Utilities.Net.Framework.Wpf.svg?logo=nuget)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Framework.Wpf/)

[![BionicCode.Utilities.Net.Framework](https://img.shields.io/badge/.NET_Framework_4.8-BionicCode.Utilities.Net.Framework-orange)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Framework/)[![BionicCode.Utilities.Net.Framework.NuGet](https://img.shields.io/nuget/v/BionicCode.Utilities.Net.Framework.svg?logo=nuget)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Framework/)

[![BionicCode.Utilities.Net.Uwp](https://img.shields.io/badge/.NET_UWP-BionicCode.Utilities.Net.Uwp-orange)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Uwp/)[![BionicCode.Utilities.Net.Uwp.NuGet](https://img.shields.io/nuget/v/BionicCode.Utilities.Net.Uwp.svg?logo=nuget)](https://www.nuget.org/packages/BionicCode.Utilities.Net.Uwp/)

## [Class Reference](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/R_Project_Documentation.htm)
[![BionicCode.Utilities.Net.Core.Wpf](https://img.shields.io/badge/Namespace-BionicCode.Utilities.Net.Core-orange)](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm)

[![BionicCode.Utilities.Net.Framework.Wpf](https://img.shields.io/badge/Namespace-BionicCode.Utilities.Net.Framework-orange)](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm)

[![BionicCode.Utilities.Net.Standard](https://img.shields.io/badge/Namespace-BionicCode.Utilities.Net.Standard-orange)](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Standard.htm)

[![BionicCode.Utilities.Net.UWP](https://img.shields.io/badge/Namespace-BionicCode.Utilities.Net.Uwp-orange)](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Uwp.htm)



## Table of Contents (selection)
| class (example)  |description   |namespace   |
|---|---|---|
|  [`ViewModel`](https://github.com/BionicCode/BionicCode.Net#baseviewmodel)  | Configurable implementation of `IViewModel`, `INotifyPropertyChanged`, `INotifyDataErrorInfo` with property validation support via delegates or Lambda expressions   | [.Net.Standard](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Standard.htm)|  
| [`AsyncRelayCommand<T>`](https://github.com/BionicCode/BionicCode.Net#asyncrelaycomandtcommandparam)   | Implementatiom of `ICommand` that supports asynchrnous command execution.   | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm) [.Net.Uwp](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Uwp.htm)| 
| [Extension Methods for WPF](https://github.com/BionicCode/BionicCode.Net#Extension-Methods-WPF)   | E.g., `DependencyObject.TryFindVisualChildElement`, `DependencyObject.TryFindVisualChildElementByName`, `DependencyObject.TryFindVisualParentElement`, `DependencyObject.TryFindVisualParentElementByName`, `DependencyObject.EnumerateVisualChildElements`, `object.ToDictionary`  | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm)| 
| Extension Methods for .NET Standard |   |[.Net.Standard](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Standard.htm)   |
| [ValueConverters](https://github.com/BionicCode/BionicCode.Net#ValueConverters)  | Default implementations of `IValueConverter` and `IMultiValueConverter` e.g., `BooleanMultiValueConverter`, `FilePathTruncateConverter`, [`BoolToStringConverter`](https://github.com/BionicCode/BionicCode.Net#BoolToStringConverter), [`InvertValueConverter`](https://github.com/BionicCode/BionicCode.Net#InvertValueConverter)   | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm) |
|  Collections WPF  |  `ObservablePropertyChangedCollection<T>` | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm) |
|  [`Profiler`](https://github.com/BionicCode/BionicCode.Net#Profiler) |   | [.Net.Standard](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Standard.htm)  |
| [`AppSettingsConnector`](https://github.com/BionicCode/BionicCode.Net#AppSettingsConnector)  | A default API to the `AppSettings` that provides strongly typed reading and writing (e.g. `bool`, `int`, `double`, `string`) of key-value pair values.  | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm) |
|[`MruManager`](https://github.com/BionicCode/BionicCode.Net#mru-most-recently-used-file-manager)   | Most Recently Used (MRU) file manager. An API that maintains an MRU table stored in the Application Settings file.  | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm)  |
|[`EventAggregator`](https://github.com/BionicCode/BionicCode.Net#eventaggregator)  |Implementation of the Event Aggregator pattern that supports dynamic aggregation of different typed event sources.   | [.Net.Standard](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Standard.htm)  |
| [`AutoResetStream`](https://github.com/BionicCode/BionicCode.Net#autoresetstream)   |A `Stream` decorator that automatically resets the stream's position after read/write access.   | [.Net.Standard](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Standard.htm)  |
|[`Dialog`](https://github.com/BionicCode/BionicCode.Net#mvvm-dialog-attached-behavior)   | Easy to use attached behavior and infrastructure to allow MVVM friendly dialog handling from a view model class in a fire-and-forget manner. To display dialogs implement `IDialogViewModel` classes and create a `DataTemplate` for each implementation. The `DataTemplate` is the rendered in a native `Window`. Addition attached properties allow for styling of the dialog `Window` or to assign an optional `DataTemplateSelector`. The attached behavior will handle showing and closing of  the dialog.  | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm) |
| Attached Behaviors for WPF  | [`Popup`](https://github.com/BionicCode/BionicCode.Net#popup) - e.g., allows to make the `Popup` sticky and moves it with the current placement target. </br> [`TextControl`](https://github.com/BionicCode/BionicCode.Net#textcontrol) - Allows to highlight text ranges in `TextBlock` and `RichTextBox` controls. </br> [`PasswordBox`](https://github.com/BionicCode/BionicCode.Net#passwordbox) - Enables to send the `PasswordBox.SecurePassword` value to the view model using a `ICommand`.   | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm) |
|EventArgs   |[`ValueChangedEventArgs<T>`](https://github.com/BionicCode/BionicCode.Net#valuechangedeventargst), [`ValueEventArgs<T>`](https://github.com/BionicCode/BionicCode.Net#valueeventargst), `PropertyValueChangedArgs<T>`   | [.Net.Standard](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Standard.htm)  |
| Markup Extensions  | [`InvertExtension`](https://github.com/BionicCode/BionicCode.Net#invertExtension), [`EnumExtension`](https://github.com/BionicCode/BionicCode.Net#enumExtension), `PrimitiveTypeExtension`, `EqualsExtension`   | [.Net.Core.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Core.htm) [.Net.Framework.Wpf](https://rawcdn.githack.com/BionicCode/BionicCode.Net/0dcf23826e90a9b54757fa705d77f263056f5fc6/Documentation/html/G_BionicCode_Utilities_Net_Framework.htm)  |
 
---
### `BaseViewModel`
* Implements and encapsulates `INotifyPropertyChanged` and `INotifyDataErrorInfo`. 
* Allows to control whether invalid data is set on a property or neglected until validation passes by setting the default parameter `isRejectInvalidValueEnabled` of `TrySetValue()` to `true` (neglects invalid values by default). 
* Also allows to control whether to throw an exception on validation error or not (silent) by setting the default parameter `isThrowExceptionOnValidationErrorEnabled` of `TrySetValue()` to `true` (is silent by default).
* Additionally exposes a `PropertyValueChanged` event which is raised in tandem with `INotifyPropertyChanged.PropertyChanged` but additionally carries old value and new value as event args.

#### Example with validation

```c#
private string name;
public string Name
{
  get => this.name;
  set
  {
    if (TrySetValue(
      value,
      (stringValue) =>
      {
        var messages = new List<string>() {"Name must start with an underscore"};
        return (stringValue.StartsWith("_"), messages);
      },
      ref this.name))
    {
      DoSomething(this.name);
    }
  }
}
```
#### Example without validation

```c#
private string name;
public string Name
{
  get => this.name;
  set
  {
    if (TrySetValue(value, ref this.name))
    {
      DoSomething(this.name);
    }
  }
}
```
----

### `AsyncRelayComand<TCommandParam>` 
Reusable generic command class that encapsulates `ICommand` and allows asynchronous execution.
When used with a `Binding`, the command will always execute asynchronously as long as an awaitable execute handler is assigned to the command.

#### Example

Declare async `ICommand`:

```c#
// ICommand. The command delegate 'Task ProcessStringAsync(string)' returns a 'Task' object and is awaitable.
public IAsyncRelayCommand<string> SomeAsyncCommand => new AsyncRelayCommand<string>(ProcessStringAsync);
```
Execute XAML:

```XAML

<!-- Executes asynchronously, because an awaitable delegate was registered with the IAsyncRelayCommand -->
<Button Command="{Binding SomeAsyncCommand}" />

```

Execute C#:

```C#
    
// Execute asynchronously
await this.SomeAsyncCommand.ExecuteAsync("String value");
    
// Execute synchronously
this.SomeAsyncCommand.Execute("String value");
    
```

----
### Extension Methods WPF

```C#
var listView = new ListView();
if (listView.TryFindVisualChildElement(out ScrollViewer scrollViewer)
{
  scrollViewer.ScrollToVerticalOffset(12);
}
```
```C#
var myControl = new MyControl();
if (myControl.TryFindVisualChildElementByName("PART_ContentHost", out ContentPresenter contentPresenter)
{
  var content = contentPresenter.Content;
}
```

```C#
var myControl = new MyControl();
foreach (TextBox childTextBox in myControl.EnumerateVisualChildElements<TextBox>()
{
  childTextBox.Text = string.Empty;
}
```

```C#
var listView = new ListView();
if (listView.TryFindVisualParentElement(out MyControl myControl)
{
  myControl.DoSomething();
}
```
```C#
var listView = new ListView();
if (listView.TryFindVisualParentElementByName("MyControl", out MyControl myControl)
{
  myControl.DoSomething();
}
```


```C#
var myClass = new MyClass();
Dictionary<string, object> myClassDictionary = myClass.ToDictionary();
object value = myClass["PropertyName"];
```
---
### ValueConverters


#### `BoolToStringConverter`
Implementation of `IValueConverter` that converts a `bool` to a custom string representation e.g., convert `true` to `"Enabled"`.

```XAML
<ToggleButton IsChecked="{Binding IsEnabled}">
  <ToggleButton.Content>
    <Binding Path="IsEnabled">
      <Binding.Converter>
        <BoolToStringConverter TrueValue="On" 
                               FalseValue="{Binding DisabledText}" 
                               NullValue="Undefined" />
      </Binding.Converter>
    </Binding>
  </ToggleButton.Content>
</ToggleButton>
  
  ```
#### `InvertValueConverter`
Implementation of `IValueConverter` that inverts a `bool`, `Visibility`, `double`, `decimal`, `float`, `int`.

```XAML
<ToggleButton>
  <ToggleButton.IsChecked>
    <Binding>
      <Binding.Converter>
        <InvertValueConverter />
      </Binding.Converter>
    </Binding>
  </ToggleButton.IsChecked>
</ToggleButton>
  
  ```
----

### `Profiler`
Static helper methods to measure performance e.g. the execution time of a code portion.

#### Example

```c#
// Specify a custom output
Profiler.LogPrinter = (timeSpan) => PrintToFile(timeSpan);
    
// Measure the average execution time of a specified number of iterations.
TimeSpan elapsedTime = Profiler.LogAverageTime(() => ReadFromDatabase(), 1000);
    
// Measure the execution times of a specified number of iterations.
List<TimeSpan> elapsedTime = Profiler.LogTimes(() => ReadFromDatabase(), 1000);
    
// Measure the execution time.
TimeSpan elapsedTime = Profiler.LogTime(() => ReadFromDatabase());
```

--------
### `PasswordBox`
Invokes a `ICommand` when `PasswordBox.PasswordChanged`event was raised. The password is send as `SecureString` to secure the value.

#### Example

```XAML

<PasswordBox PasswordBox.Command="{Binding VerifyPasswordCommand}" />
  
  ```
----
### `TextControl`
Attached behavior that supports dynamic text highlighting for controls derived from `TextBlock` or `RichTextBox`.

#### Example

```XAML

<!-- Bind Text and HighlightRanges to a view model -->
<TextBlock Text="{Binding Message}" 
           TextControl.HighlighBackground="Orange" 
           TextControl.HighlighForeground="White" 
           TextControl.HighlightRanges="{Binding HighlightTextRanges}"
           TextControl.IsHighlighEnabled="True" />
           
 
<!-- Alternatively define the HighlightRange items inline -->
<TextBlock Text="{Binding Message}" 
           TextControl.HighlighBackground="Orange" 
           TextControl.HighlighForeground="White" 
           TextControl.IsHighlighEnabled="True">
  <attachedBehaviors:TextControl.HighlightRanges>
    <attachedBehaviors:HighlightRange StartIndex="2" EndIndex="8"/>
    <attachedBehaviors:HighlightRange StartIndex="12" EndIndex="15"/>
  </attachedBehaviors:TextControl.HighlightRanges>
</TextBlock>
  
<!-- 
  Use a RichTextBox and bind it to a string by binding TextControl.Text instead of RichTextBox.Document.
  The text will be automatically converted to a FlowDocument and assigned to RichTextBox.Document.
-->
<RichTextBox TextControlText="{Binding Message}" 
             TextControl.HighlighBackground="Orange" 
             TextControl.HighlighForeground="White" 
             TextControl.IsHighlighEnabled="True" 
             TextControl.HighlightRanges="{Binding HighlightTextRanges}" />
  ```
----
### `Popup`
Set of attached behaviors for the `System.Windows.Controls.Primitives.Popup` control.
When `Popup.IsSticky` is set to `true`, the `Popup` is forced to stick to the current `Popup.PlacementTarget`. The `Popup` will follow the `Popup.PlacementTarget` whenever it changes it's screen coordinates.

#### Example

```XAML

<Popup Popup.IsSticky="True" />
  
  ```
----
### `ValueChangedEventArgs<T>`
Generic `EventArgs` implementation that provides value change information like `OldValue` and `NewValue`.

#### Example

```c#
// Specify a named ValueTuple as event argument
event EventHandler<ValueChangedEventArgs<(bool HasError, string Message)>> Completed;    
    
// Publish event
protected virtual void RaiseCompleted((bool HasError, string Message) oldValue, (bool HasError, string Message) newValue)
{
  this.Completed?.Invoke(this, new ValueChangedEventArgs<(bool HasError, string Message)>(oldValue, newValue));
}

// Receive event
private void OnCompleted(object sender, ValueChangedEventArgs<(bool HasError, string Message)> e)
{
  (bool HasError, string Message) newValue = e.NewValue;
  if (newValue.HasError)
  {
    this.TaskCompletionSource.TrySetException(new InvalidOperationException(newValue.Message));
  }
  this.TaskCompletionSource.TrySetResult(true);
}
```

----
### `ValueEventArgs<T>`
Generic `EventArgs` implementation that provides to carry a value.

#### Example

```c#
// Specify a named ValueTuple as event argument
event EventHandler<ValueEventArgs<int> Completed;    
    
// Publish event
protected virtual void RaiseCompleted(int value)
{
  this.Completed?.Invoke(this, new ValueEventArgs<int>(value));
}

// Receive event
private void OnCompleted(object sender, ValueEventArgs<int> e)
{
  int value = e.Value;  
}
```

----
### `AppSettingsConnector` 
A static default API to the AppSettings that provides strongly typed reading and writing (e.g. `boo`, `int`, `double`, `string`) of key-value pair values.

#### Example

```c#
// Write the Most Recently Used file count to the AppSettings file
AppSettingsConnector.WriteInt("mruCount", 10);

// If key exists read the Most Recently Used file count from the AppSettings file
if (TryReadInt("mruCount", out int mruCount))
{
  this.MruCount = mruCount;
}
```

----
### MRU (Most Recently Used) file manager
The `MruManager` maintains a collection of `MostRecentlyUsedFileItem` elrecentlyements that map to a recently used file. Once the max number of recently used files is reached and a new file is added, the `MruManager` automatically removes the least used file to free the MRU table.

#### Example: Save file to MRU list

```c#

var mruManager = new MruManager();
mruManager.MaxMostRecentlyUsedCount = 30;

var openFileDialog = new OpenFileDialog();
bool? result = openFileDialog.ShowDialog();

// Process open file dialog box results
if (result == true)
{
  string filename = openFileDialog.FileName;

  // Save the picked file in the MRU list
  mruManager.AddMostRecentlyUsedFile(filename);
}
```

#### Example: Read file from MRU list

```c#

var mruManager = new MruManager();
mruManager.MaxMostRecentlyUsedCount = 30;

MostRecentlyUsedFileItem lastUsedFile = mruManager.MostRecentlyUsedFile;

// Since the list is a ReadOnlyObservableCollection you can directly bind to it 
// and receive CollectionChanged notifications, which will automatically update the binding target
ReadOnlyObservableCollection<MostRecentlyUsedFileItem> mruList = mruManager.MostRecentlyUsedFiles;
```

----
### `EventAggregator`
Dynamic implementation of the EventAggregator design pattern. 
Listen to broadcasted events by a specific source type, by a specific event name, by matching event handler signature or by matching `EventArgs` type.

Allows to listen to an event without the need to reference the source instance.

#### Example
##### Aggregate events
Register event sources with an instance of `EventAggregator`:

```C#
var aggregator = new EventAggregator();

// Create instances that are source of an event
var mainWindowViewModel = new MainWindowViewModel();
var mainPageViewModel = new MainPageViewModel();
var settingsPageViewModel = new SettingsPageViewModel();

// Listen to a list of events published by a specific instance
aggregator.TryRegisterObservable(mainWindowViewModel, 
  new[] 
  {
    nameof(INotifyPropertyChanged.PropertyChanged), 
    nameof(MainWindowViewModel.ItemCreated)
  });
  
aggregator.TryRegisterObservable(
  mainPageViewModel, 
  new[] {nameof(INotifyPropertyChanged.PropertyChanged)});
  
aggregator.TryRegisterObservable(
  settingsPageViewModel, 
  new[] {nameof(INotifyPropertyChanged.PropertyChanged)});
```

##### Listen to events by name, raised by any aggregated event source
Subscribe to the `EventAggregator` and listen to **specific events** of **all event sources**:

```C#
// Listen to everything that publishes the 'INotifyPropertyChanged.PropertyChanged' event
aggregator.TryRegisterObserver<PropertyChangedEventHandler>(
  nameof(INotifyPropertyChanged.PropertyChanged), 
  ShowMessage_OnPropertyChanged);
```

##### Listen to events by name, raised by all event sources that match a specific type (e.g., class or interface)
Subscribe to the `EventAggregator` and listen to **specific events** of **specific event sources** (by source type):

```C#
// Only listen to the 'INotifyPropertyChanged.PropertyChanged' event raised by any instance of type 'MainWindowViewModel' 
aggregator.TryRegisterObserver<PropertyChangedEventHandler>(
  nameof(INotifyPropertyChanged.PropertyChanged), 
  mainWindowViewModel.GetType(), 
  ShowMessage_OnPropertyChanged);

// Only listen to the 'INotifyPropertyChanged.PropertyChanged' event of all instances that implement 'IPage' interface
aggregator.TryRegisterObserver<PropertyChangedEventHandler>(
  nameof(INotifyPropertyChanged.PropertyChanged), 
  typeof(IPage), 
  ShowMessage_OnPropertyChanged);
  
```
##### Listen to all events that match the signature of the event handler or that use a matching `EventArgs` type

Subscribe to the `EventAggregator` and listen to all events that have an **event delegate with matching signature**:
```C#

// Subscribe by defining the event delegate explicitly
aggregator.TryRegisterGlobalObserver(new PropertyChangedEventHandler(ShowMessage_OnPropertyChanged));
```

Subscribe to the `EventAggregator` and listen to all events that use a **matching `EventArgs` type**:

```C#
// Subscribe by defining the EventArgs as generic type parameter
aggregator.TryRegisterGlobalObserver<PropertyChangedEventArgs>(ShowMessage_OnPropertyChanged);
```
      
##### Type declarations used in above examples

```C#
// Event callback
private void ShowMessage_OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
  MessageBox.Show($"Handling 'PropertyChanged event'. Sender={sender.GetType().Name}; Value={e.PropertyName}");
}

// Event sources
class MainPageViewModel : IPage, INotifyPropertyChanged
{
  public string Title 
  { 
    private string title;
    get => this.title; 
    set 
    { 
      this.title = value;
      OnPropertyChanged();
    }
  }
}

class SettingsPageViewModel : IPage, INotifyPropertyChanged
{
  private string title;
  public string Title 
  { 
    get => this.title; 
    set 
    { 
      this.title = value;
      OnPropertyChanged();
    }
  }
}

class MainWindowViewModel : INotifyPropertyChanged
{
  public void CreateItem()
  {
    this.Items.Add("New Item");
    OnItemCreated();
  }
  
  private ObservableCollection<string> items;
  public ObservableCollection<string> Items
  { 
    get => this.items; 
    set 
    { 
      this.items = value;
      OnPropertyChanged();
    }
  }
  
  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  
  public event EventHandler ItemCreated;
  protected virtual void OnItemCreated() => this.ItemCreated?.Invoke(this, EventArgs.Empty);
}
```

----
### MVVM Dialog attached behavior
Provides a clean way to show dialogs that are requested by view models. This apporach uses dialog view models that are templated by specific `DataTemplate` definitions, This templates are rendered as content of native dialog `Window` instances.

Because once the dialog was closed a custom asynchronous continuation delegate is invoked by the `DialogViewModel` the dialog can be shown without the requirement for the requesting view model to wait for a response. This way the dialogs can be shown in a fire-and-forget manner supporting asynchronous delegates.

`DialogViewModel` is the only mandatory abstract class (or alternatively the `IDialogViewModel` interface) to implement. `DialogViewModel` offers ready to use logic for the data and response handling. Virtual methods add a way to add customization or adjust behavior. The `Dialog` attached behavior handles the view logic which includes aplying styles and templates on the dialog and showing/ closing the Window.

The interfaces `IDialogViewModelProvider` and `IDialogViewModelProviderSource` are not a requirement and are supposed to provide a clean separation. The `IDialogViewModelProviderSource.DialogRequested` event driven flow can be omitted. Just provide a mechanism to bind a `DialogViewModel` (or `IDialogViewModel`) implementation to the `Dialog.DialogDataContext` Attached Property. `Dialog` and `DialogViewMoel` are the only core classes that are required to make it work.


#### Minimal Example

##### Implementing IDialogViewModelProvider (just provide a binding source for the `Dialog` attached behavior)

```C#
class MainWindowViewModel : IDialogViewModelProvider
{
  public void ShowDialog()
  {  
    // Create the IDialogViewModel for the File Exists dialog
    var dialogTitleBarIcon = new BitmapImage(new Uri("../../logo.ico", UriKind.Relative));
    if (titleBarIcon.CanFreeze)
    {
      titleBarIcon.Freeze();
    }
    
    var message = "File exists. Do you want to replace it?";
    var dialogTitle = "File Exists";
      
    // Set the continuation callback which will be invoked once the dialog closed
    var fileExistsdialogViewModel = new FileExistsDialogViewModel(
      message, 
      dialogTitle, 
      dialogTitleBarIcon, 
      dialogViewModel => HandleFileExistsDialogResponseAsync(dialogViewModel, filePath, settingsData));
    
    // Show the dialog by setting the DialogViewModel property to an instance of IDialogViewModel
    this.DialogViewModel = fileExistsdialogViewModel;
  }
  
  // Continuation callback. Will be invoked once the dialog closed. 
  // The parameter is the previously created FileExistsDialogViewmodel containing data set from the dialog.
  private async Task HandleFileExistsDialogResponseAsync(IDialogViewModel dialogViewModel, string filePath, string settingsData)
  {
    if (dialogViewModel.DialogResult == DialogResult.Accepted)
    {
      await SaveFileAsync(filePath, settingsData);
    }
  }
  
  // IDialogViewModelProvider interface implementation
  private IDialogViewModel dialogViewModel;  
  public IDialogViewModel DialogViewModel
  {
    get => this.dialogViewModel;
    private set => TrySetValue(value, ref this.dialogViewModel);
  }
}
```

##### Implementing DialogViewModel (required)
This is the only mandatory abstract class (or alternatively the `IDialogViewModel` interface) to implement.
The interfaces `IDialogViewModelProvider` and `IDialogViewModelProvider` are just to provide a clean separation. The `IDialogViewModelProviderSource.DialogRequested` can be omitted. Just provide a mechanism to bind a `DialogViewModel` (or `IDialogViewModel`) implementation to the `Dialog.DialogDataContext` Attached Property. 
`Dialog` and `DialogViewMoel` are the core classes to make it work.

```C#
public class FileExistsDialogViewModel : DialogViewModel
{
  public FileExistsDialogViewModel(string message, string title) : base(message, title)
  { 
  }
  public FileExistsDialogViewModel(string message, string title, Func<IDialogViewModel, Task> sendResponseCallbackAsync) : base(message, title, sendResponseCallbackAsync)
  { 
  }
  public FileExistsDialogViewModel(string message, string title, ImageSource titleBarIcon, Func<IDialogViewModel, Task> sendResponseCallbackAsync) : base(message, title, titleBarIcon, sendResponseCallbackAsync)
  { 
  }
}
```

##### Implementing `DataTemplate` for `FileExistsDialogViewModel` (required)
Make sure the templates are declared in the proper scope. It is recommended to declare them in the App.xaml.

```XAML
Application x:Class="BionicCode.BionicNuGetDeploy.Main.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:pages="clr-namespace:BionicCode.BionicNuGetDeploy.Main.Pages"
             xmlns:dialog="clr-namespace:BionicUtilities.Net.Dialog;assembly=BionicUtilities.Net"
             Startup="RunApplication">
    <Application.Resources>

      <Viewbox x:Key="WarningIcon"
               x:Shared="False">
        <ContentControl FontFamily="Segoe MDL2 Assets"
                        Content="&#xE814;" />
      </Viewbox>

      <Viewbox x:Key="WarningLightIcon"
               x:Shared="False">
        <ContentControl FontFamily="Segoe MDL2 Assets"
                        Content="&#xE7BA;" />
      </Viewbox>
    
      <DataTemplate DataType="{x:Type pages:FileExistsDialogViewModel}">
        <Grid Margin="12">
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
          </Grid.RowDefinitions>
          <StackPanel Grid.Row="0"
                      Orientation="Horizontal"
                      Margin="0,0,48,24">
            <Grid Margin="0,0,16,0">
              <ContentControl Panel.ZIndex="1"
                              Content="{StaticResource WarningIcon}"
                              VerticalAlignment="Center"
                              Height="32"
                              Foreground="Orange"
                              Background="Black" />
              <ContentControl Panel.ZIndex="2"
                              Content="{StaticResource WarningLightIcon}"
                              VerticalAlignment="Center"
                              Height="32"
                              Margin="0,4,0,0" />
            </Grid>
            <TextBlock Text="{Binding Message}" />
          </StackPanel>
          <StackPanel Grid.Row="1"
                      FocusManager.FocusedElement="{Binding ElementName=CancelButton}"
                      Orientation="Horizontal"
                      HorizontalAlignment="Right">
            <Button Content="Yes"
                    Padding="0"
                    Command="{Binding SendResponseAsyncCommand}"
                    CommandParameter="{x:Static dialog:DialogResult.Accepted}"
                    Margin="0,0,16,0" />
            <Button x:Name="CancelButton"
                    Content="No"
                    IsCancel="True"
                    IsDefault="True"
                    BorderThickness="3"
                    Padding="0"
                    Command="{Binding SendResponseAsyncCommand}"
                    CommandParameter="{x:Static dialog:DialogResult.Denied}" />

          </StackPanel>
        </Grid>
      </DataTemplate>
  </Application.Resources>
</Application>
```


##### Setting the Attached Property  `Dialog.DialogDataContext` on `Window` (or any other `FrameworkElement` - required)

```XAML
<Window x:Class="BionicCode.BionicNuGetDeploy.Main.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        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:dialog="clr-namespace:BionicUtilities.Net.Dialog;assembly=BionicUtilities.Net"
        mc:Ignorable="d"
        Title="MainWindow"
        Dialog.DialogDataContext="{Binding DialogViewModel}">
</Window>      
```

#### Extended Example

##### Implementing IDialogViewModelProviderSource (optional)

```C#

class SettingsPageViewModel : IDialogViewModelProviderSource
{
  public async Task TrySaveFileAsync(string filePath, string settingsData)
  {
    if (File.Exists(filePath))
    {
      // Create the IDialogViewModel for the File Exists dialog
      var dialogTitleBarIcon = new BitmapImage(new Uri("../../logo.ico", UriKind.Relative));
      if (titleBarIcon.CanFreeze)
      {
        titleBarIcon.Freeze();
      }
      var message = "File exists. Do you want to replace it?";
      var dialogTitle = "File Exists";
      
      // Set the continuation callback which will be invoked once the dialog closed
      var fileExistsdialogViewModel = new FileExistsDialogViewModel(
        message, 
        dialogTitle, 
        dialogTitleBarIcon, 
        dialogViewModel => HandleFileExistsDialogResponseAsync(dialogViewModel, filePath, settingsData));
      
      // Request File Exists dialog to ask if existing file can be overwritten
      OnDialogRequested(newfileExistsdialogViewModel);
      return;
    }
    
    await SaveFileAsync(filePath, settingsData);
  }
  
  // Continuation callback. Will be invoked once the dialog closed. 
  // The parameter is the previously created FileExistsDialogViewmodel containing data set from the dialog.
  private async Task HandleFileExistsDialogResponseAsync(IDialogViewModel dialogViewModel, string filePath, string settingsData)
  {
    if (dialogViewModel.DialogResult == DialogResult.Accepted)
    {
      await SaveFileAsync(filePath, settingsData);
    }
  }

  // IDialogViewModelProviderSource interface implementation
  public event EventHandler<ValueEventArgs<IDialogViewModel>> DialogRequested;
  protected virtual void OnDialogRequested(IDialogViewModel dialogViewModel)
  {
    this.DialogRequested?.Invoke(this, new ValueEventArgs<IDialogViewModel>(dialogViewModel));
  }
}
```

##### Implementing IDialogViewModelProvider (optional - just provide a binding source for the `Dialog` attached behavior)

```C#
class MainWindowViewModel : IDialogViewModelProvider
{
  public MainPageViewModel()
  {
    // A view model that implements IDialogViewModelProvider and can request dispalying of a dialog
    var settingsPageViewModel = new SettingsPageViewModel();
    
    // Listen for dialog requests and provide the dialog view model for binding of the attahced behavior.    
    // Show the dialog by setting the DialogViewModel property to an instance of IDialogViewModel.
    settingsPageViewModel.DialogRequested += (sender, args) => this.DialogViewModel = args.Value);
    
    this.Pages = new ObservableCollection<IPage>() { settingsPageViewModel };
  }
  
  // IDialogViewModelProvider interface implementation
  private IDialogViewModel dialogViewModel;  
  public IDialogViewModel DialogViewModel
  {
    get => this.dialogViewModel;
    private set => TrySetValue(value, ref this.dialogViewModel);
  }
}
```

##### Implementing DialogViewModel (required)
This is the only mandatory abstract class (or alternatively the `IDialogViewModel` interface) to implement.
The interfaces `IDialogViewModelProvider` and `IDialogViewModelProvider` are just to provide a clean separation. The `IDialogViewModelProviderSource.DialogRequested` can be omitted. Just provide a mechanism to bind a `DialogViewModel` (or `IDialogViewModel`) implementation to the `Dialog.DialogDataContext` Attached Property. 
`Dialog` and `DialogViewMoel` are the core classes to make it work.

```C#
public class FileExistsDialogViewModel : DialogViewModel
{
  public FileExistsDialogViewModel(string message, string title) : base(message, title)
  { 
  }
  public FileExistsDialogViewModel(string message, string title, Func<IDialogViewModel, Task> sendResponseCallbackAsync) : base(message, title, sendResponseCallbackAsync)
  { 
  }
  public FileExistsDialogViewModel(string message, string title, ImageSource titleBarIcon, Func<IDialogViewModel, Task> sendResponseCallbackAsync) : base(message, title, titleBarIcon, sendResponseCallbackAsync)
  { 
  }
}
```

##### Implementing `DataTemplate` for `FileExistsDialogViewModel` (required)
Make sure the templates are declared in the proper scope. It is recommended to declare them in the App.xaml.

```XAML
Application x:Class="BionicCode.BionicNuGetDeploy.Main.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:pages="clr-namespace:BionicCode.BionicNuGetDeploy.Main.Pages"
             xmlns:dialog="clr-namespace:BionicUtilities.Net.Dialog;assembly=BionicUtilities.Net"
             Startup="RunApplication">
    <Application.Resources>

      <Viewbox x:Key="WarningIcon"
               x:Shared="False">
        <ContentControl FontFamily="Segoe MDL2 Assets"
                        Content="&#xE814;" />
      </Viewbox>

      <Viewbox x:Key="WarningLightIcon"
               x:Shared="False">
        <ContentControl FontFamily="Segoe MDL2 Assets"
                        Content="&#xE7BA;" />
      </Viewbox>
    
      <DataTemplate DataType="{x:Type pages:FileExistsDialogViewModel}">
        <Grid Margin="12">
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
          </Grid.RowDefinitions>
          <StackPanel Grid.Row="0"
                      Orientation="Horizontal"
                      Margin="0,0,48,24">
            <Grid Margin="0,0,16,0">
              <ContentControl Panel.ZIndex="1"
                              Content="{StaticResource WarningIcon}"
                              VerticalAlignment="Center"
                              Height="32"
                              Foreground="Orange"
                              Background="Black" />
              <ContentControl Panel.ZIndex="2"
                              Content="{StaticResource WarningLightIcon}"
                              VerticalAlignment="Center"
                              Height="32"
                              Margin="0,4,0,0" />
            </Grid>
            <TextBlock Text="{Binding Message}" />
          </StackPanel>
          <StackPanel Grid.Row="1"
                      FocusManager.FocusedElement="{Binding ElementName=CancelButton}"
                      Orientation="Horizontal"
                      HorizontalAlignment="Right">
            <Button Content="Yes"
                    Padding="0"
                    Command="{Binding SendResponseAsyncCommand}"
                    CommandParameter="{x:Static dialog:DialogResult.Accepted}"
                    Margin="0,0,16,0" />
            <Button x:Name="CancelButton"
                    Content="No"
                    IsCancel="True"
                    IsDefault="True"
                    BorderThickness="3"
                    Padding="0"
                    Command="{Binding SendResponseAsyncCommand}"
                    CommandParameter="{x:Static dialog:DialogResult.Denied}" />

          </StackPanel>
        </Grid>
      </DataTemplate>
  </Application.Resources>
</Application>
```


##### Setting the Attached Property  `Dialog.DialogDataContext` on `Window` (or any other `FrameworkElement` - required)

```XAML
<Window x:Class="BionicCode.BionicNuGetDeploy.Main.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        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:dialog="clr-namespace:BionicUtilities.Net.Dialog;assembly=BionicUtilities.Net"
        mc:Ignorable="d"
        Title="MainWindow"
        Dialog.DialogDataContext="{Binding DialogViewModel}"
        Dialog.DialogDataContext="{Binding DialogViewModel}"
        Dialog.IsModal="True"
        Dialog.IsClosable="False">
</Window>      
```

----

### InvertExtension
`MarkupExtension`that inverts a value provided locally or by any `MarkupExtension` e.g., `Binding`.
Setting the `Invert.ValueInverter` property (similar to `Binding.Converter`) allows to specify a custom inversion logic and conversion type support.

#### Example

```XAML   

<!-- Use Binding value -->
<TextBlock Text="{Invert {Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Text}}"/>

<!-- Returns: -12 -->
<TextBlock Text="{Invert Value=1.2}"/> 

<!-- Provide a custom implementation of IValueInverter. Returns: 0.12 -->
<TextBlock Text="{Invert -0.12, ValueInverter={StaticResource CustomValueInverter}"/> 

<!-- Returns: Visibility.Collapsed -->
<TextBlock Text="{Invert {x:Static Visibility.Visible}}"/>

<!-- Returns: False -->
<TextBlock Text="{Invert True}"/>

```

----
### EnumExtension
`MarkupExtension` to display return a collection of enumeration values of a specific `enum` type.

#### Example 

```XAML   

<ComboBox ItemsSource="{Enum {x:Static MyEnum}}"/>

<ComboBox ItemsSource="{Enum EnumType={x:Static MyEnum}}"/>

```

---

### AutoResetStream
A `Stream` decorator that resets the stream's position after read/write access. Resets to the optionally provided `SeekOrigin` assigned to `AutoResetStream.ResetOrigin`

#### Example 

```C#   
using (var fileStream = new FileStream("C:\Temp", FileMode.CreateNew))
{
  bool leaveFileStreamOpen = true;
  
  using (var autoResetStream = new AutoResetStream(fileStream, leaveFileStreamOpen))
  {
    byte[] buffer = new byte[1024];
    int bytesRead = await autoResetStream.ReadAsync(buffer, 0, buffer.Length); // bytesRead: 1024
    int currentPosition = autoResetStream.Position; // currentPosition: 0
  }
  
  currentPosition = fileStream.Position; // currentPosition: 0
}

```
---
