Skip to content

Commit 2d75ce3

Browse files
committed
-Add 'ResetOrigin' property to 'AutoResetStream'
-Update documentation -update unit test
1 parent 5ad4454 commit 2d75ce3

File tree

7 files changed

+163
-27
lines changed

7 files changed

+163
-27
lines changed

BionicCode.Net/BionicCode.Utilities.Net.Core.Wpf/BionicCode.Utilities.Net.Core.Wpf.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1010
<PackageProjectUrl>https://github.com/BionicCode/BionicCode.Net</PackageProjectUrl>
1111
<PackageIcon>BionicCode_Logo.png</PackageIcon>
12-
<PackageTags>Base View Model ViewModel BaseViewModel AsyncRelayCommand Async RelayCommand WPF VisualTreeHelper Profiler Profiling Measure Measuring View Model ViewModel ExtensionMethods ObservablePropertyChangedCollection Collection ApplicationSettings Settings AppSettings MRU MostRecentlyUsedFile LastUsedFile RecentlyUsedFile EventAggregator Aggregator Event MVVMDialog MVVM Dialog DialogService ExtensioMethod EnumExtension InvertExtension MarkupExtension EventAggregator Aggregator Event MVVM NullObject NullObjectFactory Factory IFatory ValueChangedEventArgs AutoResetStream Stream PropertyValueChangedArgs FileExtension FileExtensions PropertyValueChanged RichTextBox AttachedBehavior Attached Behavior</PackageTags>
12+
<PackageTags>Base View Model ViewModel BaseViewModel AsyncRelayCommand Async RelayCommand WPF VisualTreeHelper Profiler Profiling Measure Measuring View Model ViewModel ExtensionMethods ObservablePropertyChangedCollection Collection ApplicationSettings Settings AppSettings MRU MostRecentlyUsedFile LastUsedFile RecentlyUsedFile EventAggregator Aggregator Event MVVMDialog MVVM Dialog DialogService ExtensioMethod EnumExtension Enum Extension InvertExtension MarkupExtension EventAggregator Aggregator Event MVVM NullObject NullObjectFactory Factory IFatory ValueChangedEventArgs AutoResetStream Stream PropertyValueChangedArgs FileExtension FileExtensions PropertyValueChanged RichTextBox AttachedBehavior Attached Behavior</PackageTags>
1313
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
1414
<Company>BionicCode</Company>
1515
<Copyright>2020</Copyright>

BionicCode.Net/BionicCode.Utilities.Net.Core.Wpf/Extensions/HelperExtensions.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,19 @@ public static bool TryFindVisualParentElement<TParent>(this DependencyObject chi
4444
/// <param name="elementName">The element name the visual parent must match.</param>
4545
/// <param name="resultElement"></param>
4646
/// <returns></returns>
47-
public static bool TryFindVisualParentElementByName(
47+
public static bool TryFindVisualParentElementByName<TChild>(
4848
this DependencyObject child,
4949
string elementName,
50-
out FrameworkElement resultElement)
50+
out TChild resultElement) where TChild : FrameworkElement
5151
{
5252
resultElement = null;
5353

54-
var parentElement = VisualTreeHelper.GetParent(child);
54+
DependencyObject parentElement = VisualTreeHelper.GetParent(child);
5555

5656
if (parentElement is FrameworkElement frameworkElement &&
5757
frameworkElement.Name.Equals(elementName, StringComparison.OrdinalIgnoreCase))
5858
{
59-
resultElement = frameworkElement;
59+
resultElement = frameworkElement as TChild;
6060
return true;
6161
}
6262

@@ -110,14 +110,13 @@ public static bool TryFindVisualChildElement<TChild>(this DependencyObject paren
110110
/// <param name="childElementName">The name the visual child's name must match.</param>
111111
/// <param name="resultElement">The found element or <c>null</c> if no matching element was found.</param>
112112
/// <returns><c>true</c> when an element with the specified <paramref name="childElementName"/> was found, otherwise <c>false</c>.</returns>
113-
public static bool TryFindVisualChildElementByName(
113+
public static bool TryFindVisualChildElementByName<TChild>(
114114
this DependencyObject parent,
115115
string childElementName,
116-
out FrameworkElement resultElement)
116+
out TChild resultElement) where TChild : FrameworkElement
117117
{
118118
resultElement = null;
119-
120-
119+
121120
if (parent is Popup popup)
122121
{
123122
parent = popup.Child;
@@ -132,10 +131,10 @@ public static bool TryFindVisualChildElementByName(
132131
DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);
133132

134133
if (childElement is FrameworkElement uiElement && uiElement.Name.Equals(
135-
childElementName,
136-
StringComparison.OrdinalIgnoreCase))
134+
childElementName,
135+
StringComparison.OrdinalIgnoreCase))
137136
{
138-
resultElement = uiElement;
137+
resultElement = uiElement as TChild;
139138
return true;
140139
}
141140

BionicCode.Net/BionicCode.Utilities.Net.Framework.Wpf/Extensions/HelperExtensions.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,19 @@ public static bool TryFindVisualParentElement<TParent>(this DependencyObject chi
4444
/// <param name="elementName">The element name the visual parent must match.</param>
4545
/// <param name="resultElement"></param>
4646
/// <returns></returns>
47-
public static bool TryFindVisualParentElementByName(
47+
public static bool TryFindVisualParentElementByName<TChild>(
4848
this DependencyObject child,
4949
string elementName,
50-
out FrameworkElement resultElement)
50+
out TChild resultElement) where TChild : FrameworkElement
5151
{
5252
resultElement = null;
5353

54-
var parentElement = VisualTreeHelper.GetParent(child);
54+
DependencyObject parentElement = VisualTreeHelper.GetParent(child);
5555

5656
if (parentElement is FrameworkElement frameworkElement &&
5757
frameworkElement.Name.Equals(elementName, StringComparison.OrdinalIgnoreCase))
5858
{
59-
resultElement = frameworkElement;
59+
resultElement = frameworkElement as TChild;
6060
return true;
6161
}
6262

@@ -110,14 +110,13 @@ public static bool TryFindVisualChildElement<TChild>(this DependencyObject paren
110110
/// <param name="childElementName">The name the visual child's name must match.</param>
111111
/// <param name="resultElement">The found element or <c>null</c> if no matching element was found.</param>
112112
/// <returns><c>true</c> when an element with the specified <paramref name="childElementName"/> was found, otherwise <c>false</c>.</returns>
113-
public static bool TryFindVisualChildElementByName(
113+
public static bool TryFindVisualChildElementByName<TChild>(
114114
this DependencyObject parent,
115115
string childElementName,
116-
out FrameworkElement resultElement)
116+
out TChild resultElement) where TChild : FrameworkElement
117117
{
118118
resultElement = null;
119-
120-
119+
121120
if (parent is Popup popup)
122121
{
123122
parent = popup.Child;
@@ -135,7 +134,7 @@ public static bool TryFindVisualChildElementByName(
135134
childElementName,
136135
StringComparison.OrdinalIgnoreCase))
137136
{
138-
resultElement = uiElement;
137+
resultElement = uiElement as TChild;
139138
return true;
140139
}
141140

BionicCode.Net/BionicCode.Utilities.Net.Standard/BionicCode.Utilities.Net.Standard.xml

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

BionicCode.Net/BionicCode.Utilities.Net.Standard/IO/AutoResetStream.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ public Stream BaseStream
2525
}
2626
}
2727

28+
/// <summary>
29+
/// Defines the position to which the stream should reset to.
30+
/// </summary>
31+
/// <value>A <see cref="SeekOrigin"/> value.</value>
32+
public SeekOrigin ResetOrigin { get; set; }
33+
2834
/// <summary>
2935
/// Gets whether the decorated underlying <see cref="Stream"/> will be closed or disposed when the <see cref="AutoResetStream"/> instance is closed or disposed. Use constructor to set the value in order to configure the behavior.
3036
/// </summary>
@@ -42,26 +48,39 @@ public AutoResetStream()
4248
/// Constructor which accepts the <see cref="Stream"/> instance to decorate in order to extend its behavior.
4349
/// </summary>
4450
/// <param name="baseStream">The <see cref="Stream"/> instance to decorate in order to extend its behavior.</param>
45-
public AutoResetStream(Stream baseStream) : this(baseStream, true)
51+
public AutoResetStream(Stream baseStream) : this(baseStream, SeekOrigin.Begin, true)
4652
{
4753
}
4854

4955
/// <summary>
5056
/// Constructor which accepts the <see cref="Stream"/> instance to decorate in order to extend its behavior.
5157
/// </summary>
5258
/// <param name="baseStream">The <see cref="Stream"/> instance to decorate in order to extend its behavior.</param>
53-
/// <param name="isDisposingDecoratedStream">When set to <c>true</c> the decorated underlying <see cref="Stream"/> will be disposed or closed too, if the <see cref="AutoResetStream"/> is disposed or closed.</param>
54-
public AutoResetStream(Stream baseStream, bool isDisposingDecoratedStream)
59+
/// <param name="leaveDecoratedStreamOpen">When set to <c>true</c> the decorated underlying <see cref="Stream"/> will be disposed or closed too, if the <see cref="AutoResetStream"/> is disposed or closed.</param>
60+
public AutoResetStream(Stream baseStream, bool leaveDecoratedStreamOpen) : this(baseStream, SeekOrigin.Begin, leaveDecoratedStreamOpen)
61+
{
62+
this.BaseStream = baseStream;
63+
this.IsDisposingDecoratedStream = leaveDecoratedStreamOpen;
64+
}
65+
66+
/// <summary>
67+
/// Constructor which accepts the <see cref="Stream"/> instance to decorate in order to extend its behavior.
68+
/// </summary>
69+
/// <param name="baseStream">The <see cref="Stream"/> instance to decorate in order to extend its behavior.</param>
70+
/// <param name="resetOrigin">The origin to which the stream should be reset to.</param>
71+
/// <param name="leaveDecoratedStreamOpen">When set to <c>true</c> the decorated underlying <see cref="Stream"/> will be disposed or closed too, if the <see cref="AutoResetStream"/> is disposed or closed.</param>
72+
public AutoResetStream(Stream baseStream, SeekOrigin resetOrigin, bool leaveDecoratedStreamOpen)
5573
{
5674
this.BaseStream = baseStream;
57-
this.IsDisposingDecoratedStream = isDisposingDecoratedStream;
75+
this.IsDisposingDecoratedStream = leaveDecoratedStreamOpen;
76+
this.ResetOrigin = resetOrigin;
5877
}
5978

6079
/// <summary>
6180
/// Resets the <see cref="Stream.Position"/> to an offset of '0' relative to the provided <paramref name="seekOrigin"/>.
6281
/// </summary>
6382
/// <param name="seekOrigin">The optional relative position of the <see cref="Stream"/> to apply the zero offset to. The default is <see cref="SeekOrigin.Begin"/>.</param>
64-
public void Reset(SeekOrigin seekOrigin = SeekOrigin.Begin) => this.BaseStream.Seek(0, seekOrigin);
83+
public void Reset(SeekOrigin seekOrigin = SeekOrigin.Current) => this.BaseStream.Seek(0, seekOrigin == SeekOrigin.Current ? this.ResetOrigin : seekOrigin);
6584

6685
#region Overrides of Stream
6786

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
6+
namespace BionicCode.Utilities.Net.Standard.Profiling
7+
{
8+
/// <summary>
9+
/// Helper methods to measure code execution time.
10+
/// </summary>
11+
public static class Profiler
12+
{
13+
/// <summary>
14+
/// A <see cref="Action{T}"/> delegate that can be used to redirect the log output. By default the log output will be send to the output window.
15+
/// </summary>
16+
/// <value>A <see cref="Action{T}"/> delegate which will be invoked to output the elapsed <see cref="TimeSpan"/>. The default delegate will print the output to the output window.</value>
17+
public static Action<TimeSpan> LogPrinter { get; set; }
18+
19+
/// <summary>
20+
/// Measures the execution time of a method.
21+
/// </summary>
22+
/// <param name="action">The code to measure execution time.</param>
23+
/// <returns>The execution time as a <see cref="TimeSpan"/>.</returns>
24+
/// <remarks>Specify a <see cref="LogPrinter"/> <see cref="Action"/> to customize the output target and formatting.</remarks>
25+
public static TimeSpan LogTime(Action action)
26+
{
27+
var stopwatch = new Stopwatch();
28+
stopwatch.Start();
29+
action.Invoke();
30+
stopwatch.Stop();
31+
TimeSpan stopwatchElapsed = stopwatch.Elapsed;
32+
if (Profiler.LogPrinter == null)
33+
{
34+
Profiler.LogPrinter = (elapsedTime) =>
35+
Console.WriteLine($"Elapsed time: {elapsedTime.TotalMilliseconds} [ms]");
36+
}
37+
Profiler.LogPrinter?.Invoke(stopwatchElapsed);
38+
39+
return stopwatchElapsed;
40+
}
41+
42+
43+
/// <summary>
44+
/// Measures the execution time of a method.
45+
/// </summary>
46+
/// <param name="action">The code to measure execution time.</param>
47+
/// <param name="runCount">Number of iterations the <paramref name="action"/> should be executed.</param>
48+
/// <returns>A list of execution times for all <paramref name="runCount"/> number of iterations <see cref="TimeSpan"/>.</returns>
49+
/// <remarks>Specify a <see cref="LogPrinter"/> <see cref="Action"/> to customize the output target and formatting.</remarks>
50+
public static List<TimeSpan> LogTimes(Action action, int runCount)
51+
{
52+
if (Profiler.LogPrinter == null)
53+
{
54+
Profiler.LogPrinter = (elapsedTime) =>
55+
Console.WriteLine($"Iteration #{runCount}: Elapsed time: {elapsedTime.TotalMilliseconds} [ms]");
56+
}
57+
var stopwatch = new Stopwatch();
58+
var measuredTimes = new List<TimeSpan>();
59+
60+
for (; runCount > 0; runCount--)
61+
{
62+
stopwatch.Start();
63+
action.Invoke();
64+
stopwatch.Stop();
65+
TimeSpan stopwatchElapsed = stopwatch.Elapsed;
66+
measuredTimes.Add(stopwatchElapsed);
67+
Profiler.LogPrinter.Invoke(stopwatchElapsed);
68+
}
69+
70+
return measuredTimes;
71+
}
72+
73+
/// <summary>
74+
/// Measures the execution time of a method.
75+
/// </summary>
76+
/// <param name="action">The code to measure execution time.</param>
77+
/// <param name="runCount">Number of iterations the <paramref name="action"/> should be executed.</param>
78+
/// <returns>The average execution time of all <paramref name="runCount"/> number of iterations as <see cref="TimeSpan"/>.</returns>
79+
/// <remarks>Specify a <see cref="LogPrinter"/> <see cref="Action"/> to customize the output target and formatting.</remarks>
80+
public static TimeSpan LogAverageTime(Action action, int runCount)
81+
{
82+
var stopwatch = new Stopwatch();
83+
var measuredTimes = new List<TimeSpan>();
84+
85+
for (; runCount > 0; runCount--)
86+
{
87+
stopwatch.Start();
88+
action.Invoke();
89+
stopwatch.Stop();
90+
TimeSpan stopwatchElapsed = stopwatch.Elapsed;
91+
measuredTimes.Add(stopwatchElapsed);
92+
}
93+
94+
var logAverageTime = new TimeSpan((long) measuredTimes.Average((time) => time.Ticks));
95+
if (Profiler.LogPrinter == null)
96+
{
97+
Profiler.LogPrinter = (elapsedTime) =>
98+
Console.WriteLine($"Iterations={runCount}; Average elapsed time: {elapsedTime.TotalMilliseconds} [ms]");
99+
}
100+
Profiler.LogPrinter.Invoke(logAverageTime);
101+
return logAverageTime;
102+
}
103+
}
104+
}

BionicCode.Net/BionicCode.Utilities.UnitTest.Net.Standard/AutoResetStreamTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public void Initialize()
2626

2727
private void FillStream()
2828
{
29+
var f = new FileStream("C:/Temp", FileMode.CreateNew);
2930
var memStream = new MemoryStream();
3031
using (var streamWriter = new StreamWriter(memStream, Encoding.Default, 1024, true))
3132
{

0 commit comments

Comments
 (0)