C# | .NET : Smart Duration Class

C# | .NET : Smart Duration Class

[Originally posted on 11 October 13]

Sometimes you need to have start and end dates, validate whether these dates define valid duration, or determine overlapping duration/timespan in two given duration(s)? I wrote an MVVM ready, equitable, duration class which does all the above and a little more. :-). I call this class TimeDuration. Let’s go through the code of the class.

TimeDuration implements two interfaces,  IEquatable and INotifyPropertyChanged. In later parts we will see the implementation of methods for IEquatable, let’s see INotifyPropertyChanged implementation first.  It has System.ComponentModel and a PropertyChangedEventHandler type public event PropertyChanged. It has a private method onPropertyChanged with a return type void. The complete INotifyPropertyChanged implementation looks like this:

using System;
using System.ComponentModel;

namespace Demo.DateExtentions
{
    public class TimeDuration : IEquatable, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void onPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}

The class encapsulate two basic information elements – start time and end time. To expose this information class has two public DateTime type properties; Start and End. To notify views when the basic information changes in VM the class calls onPropertyChanged method from inside settters of both these properties. The implementation code looks like this:

        DateTime start;
        DateTime end;
        public DateTime Start
        {
            get { return start; }
            set
            {
                if (start != value)
                {
                    start = value;
                    onPropertyChanged("Start");
                }
            }
        }
        public DateTime End
        {
            get { return end; }
            set
            {
                if (end != value)
                {
                    end = value;
                    onPropertyChanged("End");
                }
            }
        }

The class initializes DateTime structured in the constructor so that object based on this class is ready for use on initialization. Constructor looks like so:

        public TimeDuration()
        {
            start = new DateTime();
            end = new DateTime();
        }

(Fields could have been initialized at the time of defining the field and not have the constructor at all.)
Duration represents a time span, so TimeDuration class has a TimeSpan type public property named Duration. This property returns the difference of start and end times. Code is like this:

        public TimeSpan Duration { get { return end - start; } }

This part makes the class a smart duration class. TimeDuration is capable of telling intersecting duration/timespan between two given durations. Following figure illustrates intersecting durations:
DurationFigure

In the figure above AB, CD, EF, and GH represent durations. Class returns information regarding CB in the context of AB and CD, where starting part of CD overlaps with end part of AB. The method returns overlap duration if one duration completely falls inside of another duration, as in AB and EF, where EF completely lies inside AB. The third scenario is where tail part of one duration overlaps with head part of another duration as in AB and GH where overlap is occurring at AH. Following is the code to return appropriate type, TimeSpan or TimeDuration, after checking the intersection.

        public TimeSpan IntersectingSpan(TimeDuration other)
        {
            return getIntersection(other).Duration;
        }
        public TimeDuration IntersectingDuration(TimeDuration other)
        {
            return getIntersection(other);
        }
        private TimeDuration getIntersection(TimeDuration other)
        {
            if (this.Equals(other)) return this;
            DateTime iStart = this.Start < other.Start ? other.Start : this.Start;
            DateTime iEnd = this.End < other.End ? this.End : other.End;
            return iStart < iEnd ? new TimeDuration(iStart, iEnd) : new TimeDuration();
        }

IEquatable interface implementation:

        public bool Equals(TimeDuration compareWith)
        {
            return CompareWith.Start == this.Start && CompareWith.End == this.End;
        }
        public override int GetHashCode()
        {
            return _start.GetHashCode() ^ _end.GetHashCode();
        }

The entire SmartDuration class:

/*
 * Disclaimer
 * Unless otherwise noted, code snippets in this repository are licensed under a Creative Commons Attribution 4.0 International license (http://creativecommons.org/licenses/by/4.0/)
 * Please do not forget to credit if you choose to use code in any which way.  You can credit in any way you please as below:
        By Sanjay (https://sharpsnippets.wordpress.com/)
        By Sanjay (http://www.twitter.com/SanjayAtPilcrow)
 * Blog post about following code: http://wp.me/p2iWZr-4T
 * General Notes
 *      - This is working code, but not production code.
 *      - Code follows universal C# code convention but might not follow your company's internal convention.
 *      - Code is more of POC and thus does not have full exception handling and parameter checking.
 *      - If you choose to use the code in production, do re-code to make it production ready as per your org's engineering policy.
*/
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace POCs.Sanjay.SharpSnippets.Dates
{
    public class TimeDuration : IEquatable, INotifyPropertyChanged
    {
        DateTime start;
        DateTime end;
        public DateTime Start
        {
            get { return start; }
            set
            {
                if (start != value)
                {
                    start = value;
                    onPropertyChanged("Start");
                }
            }
        }
        public DateTime End
        {
            get { return end; }
            set
            {
                if (end != value)
                {
                    end = value;
                    onPropertyChanged("End");
                }
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        public TimeDuration()
        {
            start = new DateTime();
            end = new DateTime();
        }
        public TimeDuration(DateTime start, DateTime end)
        {
            start = start;
            end = end;
        }
        public bool IsValidDuration
        {
            get { return _start <= _end; }
        }
        public TimeSpan Duration { get { return end - start; } }
        public TimeSpan IntersectingSpan(TimeDuration other)
        {
            return getIntersection(other).Duration;
        }
        public TimeDuration IntersectingDuration(TimeDuration other)
        {
            return getIntersection(other);
        }
        private TimeDuration getIntersection(TimeDuration other)
        {
            if (this.Equals(other)) return this;
            DateTime iStart = this.Start < other.Start ? other.Start : this.Start;
            DateTime iEnd = this.End < other.End ? this.End : other.End;
            return iStart < iEnd ? new TimeDuration(iStart, iEnd) : new TimeDuration();
        }

        #region Equatable
        public bool Equals(TimeDuration compareWith)
        {
            return CompareWith.Start == this.Start && CompareWith.End == this.End;
        }
        public override int GetHashCode()
        {
            return _start.GetHashCode() ^ _end.GetHashCode();
        }
        #endregion //Equatable

        #region notify property changed
        private void onPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion

    }
}

Find code on my GitHub repository.

 

C#|.NET : Size String, Truncate Lines

Swiss Crop Circle 2009 Aerial by Kecko, on Flickr
Swiss Crop Circle 2009 Aerial, a photo by Kecko on Flickr.

I needed to truncate lines from CSV string prepared for export through mail in my app. Windows Phone mail restricts text size to 1MB. In my app’s case some CSV reports could go beyond 1MB. To make sure that the CSV report does not exceed a desired size in bytes, I created this extension method – SizeIt. SizeIt can truncate lines (and characters if required) from a given string from beginning or from end and also inserts information text line (how many lines and characters removed) in the resulting string. Following is the test form created to show what the method does to a string. In this example a 300 byte 10 line string is given as input and asked to reduce it to <200 bytes, first from beginning, and then from end.

SizeTail

SizeFront

Here is the code for the extension method:

    public static class StringBuilderExtensions
    {
        /// <summary>
        /// Sizes StringBuilder to given size
        /// </summary>
        /// <param name="sb">StringBuilder</param>
        /// <param name="bytes">Maximum Size of resultant string. Mostly the size of string will be less than max size</param>
        /// <param name="removeLinesFromBeginning">Pass true, if clipping has to happen in the beginning</param>
        public static void SizeIt(this StringBuilder sb, int bytes, bool removeLinesFromBeginning)
        {
            bool _stringReplacementHappened = false;
            bool _maxBytesAdjusted_for_lines = false;
            bool _maxBytesAdjusted_for_chars = false;
            string _insertThismessage = "";
            int _linesRemoved = 0;
            int _charsRemoved = 0;
            //try removing lines.
            while (System.Text.Encoding.Unicode.GetByteCount(sb.ToString()) > bytes)
            {
                char _alternateNewLine = '\r';
                string _workingString = sb.ToString();
                if (!_workingString.Contains(Environment.NewLine) && !_workingString.Contains(_alternateNewLine))
                {
                    break;
                }
                int _newlinelocation = sb.ToString().IndexOf(Environment.NewLine);
                int _lastNewlinelocation = sb.ToString().LastIndexOf(Environment.NewLine);
                if (_newlinelocation <= 0)
                {
                    _newlinelocation = sb.ToString().IndexOf(_alternateNewLine);
                }
                if (_lastNewlinelocation <= 0)
                {
                    _lastNewlinelocation = sb.ToString().LastIndexOf(_alternateNewLine);
                }

                if (removeLinesFromBeginning)
                {
                    sb = sb.Remove(0, _newlinelocation + 1);
                    _linesRemoved++;
                    _stringReplacementHappened = true;
                    _insertThismessage = string.Format("... {0} lines removed{1}", _linesRemoved, Environment.NewLine);
                }
                else
                {
                    int _lastReturnAt = _lastNewlinelocation;
                    int _charsInLastLine = sb.Length - _lastNewlinelocation;
                    sb = sb.Remove(_lastReturnAt, _charsInLastLine);
                    _linesRemoved++;
                    _stringReplacementHappened = true;
                    _insertThismessage = string.Format("{1}{0} lines removed...", _linesRemoved, Environment.NewLine);
                }
                if (!_maxBytesAdjusted_for_lines)
                {
                    bytes -= 50; //40 extra bytes for information text
                    _maxBytesAdjusted_for_lines = true;
                }
            }
            //if it's still more than the desired size
            if (System.Text.Encoding.Unicode.GetByteCount(sb.ToString()) > bytes)
            {
                int _currentBytes = System.Text.Encoding.Unicode.GetByteCount(sb.ToString());
                if (!_maxBytesAdjusted_for_lines)
                {
                    bytes -= 100;
                    _maxBytesAdjusted_for_lines = true;
                    _maxBytesAdjusted_for_chars = true;
                }
                else if (!_maxBytesAdjusted_for_chars)
                {
                    bytes -= 50;
                    _maxBytesAdjusted_for_chars = true;
                }
                int _removeChars = (_currentBytes - bytes) / 2;
                if (removeLinesFromBeginning)
                {
                    sb = sb.Remove(0, _removeChars);
                    _stringReplacementHappened = true;
                    _insertThismessage = string.Format("... {0} lines and {1} chars removed{2}", _linesRemoved, _removeChars, Environment.NewLine);
                }
                else
                {
                    sb = sb.Remove(sb.Length - _removeChars, _removeChars);
                    _stringReplacementHappened = true;
                    _insertThismessage = string.Format("{2}{0} lines and {1} chars removed...", _linesRemoved, _removeChars, Environment.NewLine);
                }
            }
            if (_stringReplacementHappened)
            {
                if (removeLinesFromBeginning)
                {
                    sb.Insert(0, _insertThismessage);
                }
                else
                {
                    sb.Append(_insertThismessage);
                }
            }
        }
    }

You may extend SizeIt to make it even smarter because it has following shortcomings in current version:

  • Does not truncate very closely to Max size. Will always be less than 50 bytes from max.
  • If there are only two lines and line of truncating side is huge, even a slight reduction in size will remove the complete content of the huge line, meaning loosing most of the text. This could be taken care of by firsts analyzing the string and then truncating line/character, as required
  • The logic inherently tries to remove lines first, should intelligently decide whether to remove line or chars.

Navigation Hierarchy

Avirall Time Suite is an extensive app with 60,000+ working LOC in .cs files. I have not calculated the lines of code in XAML because a major portion of them are auto-generated. Still, there is about same number of lines in XAML files, as well. Avirall has 15 pages and 3 of them have about 5 types, so in all about 30 pages show up in Avirall. During documentation I created a high level diagram of navigation hierarchy in the app. I think it gives a bird’s eye view of how the app’s control flow, kind of circulatory system. This is how it looks like :

MTTHNavigation

XAML|C# Step By Step : UserControl – Wait Spinner

Waiting by moonux, on Flickr
Waiting, a photo by moonux on Flickr.

Something is cooking in the background? Do not forget to inform your user. A modern, flat, light weight, wait spinner is an animated and prominent way to inform user about running background processes (you would not have long running processes on the UI thread, right? See how to effectively free up UI). You would choose to show this control in that area of the screen to which the information, related to which the BG work is taking place – loading/processing etc, belongs. You may have multiple information points on screen for which background work is running.

For this step-by-step we will create this example:

A practical example of implementation of this spinner is this panorama app there are two wait spinners being used, one in Quick Stopwatch pano, and other in Recent pano. When data related to Quick stopwatch is loading, the wait spinner shows beside the title “Quick”, and when data related to Recent is loading, the wait spinner shows near the pano title “Recent”. This way user is informed about which section of the app is doing something in the background.

WaitSpinnerExample

Don’t have this app on your Windows Phone? Visit here

Let’s go through step-by-step of creating a WaitSpinner UserControl and using it in a page.

Step 1 : Create Solution and Project

  1. Create a project in VS with the name “WaitSpinner”. The solution gets created automatically.
  2. Right click on the solution in Solution Explorer, and choose “Add”>”New Project”.
  3. Select Class Library type project and name it “XAMLControls”.

Step 2 : Create UserControl

  1. Right click on XAMLControls and select “Add”>”New item”.
  2. Choose User Control and name it “UCWaitSpinner”.
  3. A new UCWaitSpinner.xaml and its code behind is created.

Step 3 : Open in Blend

Right click on UCWaitSpinner.xaml and choose to open in Blend. Depending on the version of Visual Studio the view you get might be a little different, but most of the UI will be similar. You will get something like this:
01_OpenUCInBlend

Step 4 : Change the type of Layout control

Change the type of Layout from Grid to ViewBox.
02_ChangeLayoutType

Step 5 : Add a new Grid to Layout ViewBox

  1. Choose Grid from control and right click and drag in XAML design view area.
  2. Update Layout properties; Height and Width to 50, HorizontalAlignment and VerticalAlignment to Stretch.

03_AddGridToLayoutRoot

Step 6 : Add Outer border

  1. Choose Ellipse tool from Toolbox and create a circle in the design area. Don’t worry about size and fill at this point.
  2. Make sure Ellipse is selected.
  3. In Properties.Brushes;
    • Set Fill to “No Brush”.
    • Set Stroke to “PhoneForegroundBrush”.
  4. In Layout section;
    • Set HorizontalAlignment to stretch.
    • Set VerticalAlignment to stretch.
    • Set all margins to 0.

04_CreateACircle

Step 7 : Add axis for hands

  1. Choose Ellipse tool from Toolbox again and create a circle in the middle of XAML design. Don’t bother about size at this point.
  2. Make sure this ellipse is selected.
  3. In Properties.Brushes;
    • Set Fill to “PhoneForegroundBrush”.
    • Set Stroke to “No Brush”.
  4. In Layout section;
    • Set Width and Height to 5.
    • Set HorizontalAlignment to center.
    • Set VerticalAlignment to center.
    • Set all margins to 0.

05_CreateAxisForHands

Step 8 : Add minute and hour hands

  1. Choose Rectangle tool from Toolbox and create a rectangle in the design area. Don’t worry about size and placement at this point.
  2. Rename the [Rectangle] to “MinuteHand”.
  3. Make sure you have MinuteHand selected.
  4. In Properties.Brushes;
    • Set Fill to “PhoneForegroundBrush”.
    • Set Stroke to “No Brush”.
  5. In Layout section;
    • Set Width to 2 and Height to 20.
    • Set HorizontalAlignment to Center.
    • Set VerticalAlignment to Bottom.
    • Set Bottom margin to 25 and all others to 0.
  6. In Properties.Transform;
    • Select Center Point tab.
    • Set X to 0.5.
    • Set Y to 1.
  7. Copy and paste “MinuteHand” control and name it “HourHand”. Keep everything same and change only the Properties.Layout.Height to 13.

06_CreateMinuteHand

Step 9 : Create Storyboard for moving hands

  1. In Objects and Timeline, click on “+” sign to add a new storyboard.
  2. Name the storyboard as “MoveHands”.

07_CreateStoryBoard

Step 10 : Create animation for minute hand

  1. In timeline window slide the marker to 3 second.
  2. Select MinuteHand control.
  3. In Properties.Transform select Rotate tab and set Angle property to 1440.

07b_CreateStoryBoard

Step 11 : Create animation for hour hand

  1. Select HourHand control.
  2. In Properties.Transform select Rotate tab and set Angle property to 360.
  3. c. In timeline windows click on play button to see hands are rotating properly (Hour hand completes 1 rotation and minute hand completes 4 rotations in 3 seconds).

07c_CreateStoryBoard

Step 11B : Follow me on Twitter 😀

Step 12 : Review XAML code

Close Blend and go back to XAML view of UCWaitSpinner in VS. You should see following XAML code:

<UserControl x:Class="XAMLControls.UCWaitSpinner"
    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"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480">
	<UserControl.Resources>
		<Storyboard x:Name="MoveHands">
			<DoubleAnimation Duration="0:0:3" To="360" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="HourHand" d:IsOptimized="True"/>
			<DoubleAnimation Duration="0:0:3" To="1440" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="MinuteHand" d:IsOptimized="True"/>
		</Storyboard>
    </UserControl.Resources>

    <Viewbox x:Name="LayoutRoot">
    	<Grid Width="50" Height="50">
    		<Ellipse Margin="0">
    			<Ellipse.Stroke>
    				<SolidColorBrush Color="{StaticResource PhoneForegroundColor}"/>
    			</Ellipse.Stroke>
    		</Ellipse>
    		<Ellipse Margin="0" Width="4" Height="4" HorizontalAlignment="Center" VerticalAlignment="Center">
    			<Ellipse.Fill>
    				<SolidColorBrush Color="{StaticResource PhoneForegroundColor}"/>
    			</Ellipse.Fill>
    		</Ellipse>
    		<Rectangle x:Name="MinuteHand" Height="20" Margin="0,0,0,25" Width="2" StrokeThickness="0" HorizontalAlignment="Center" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,1">
    			<Rectangle.RenderTransform>
    				<CompositeTransform/>
    			</Rectangle.RenderTransform>
    			<Rectangle.Fill>
    				<SolidColorBrush Color="{StaticResource PhoneContrastBackgroundColor}"/>
    			</Rectangle.Fill>
    		</Rectangle>
    		<Rectangle x:Name="HourHand" Height="13" Margin="0,0,0,25" Width="2" StrokeThickness="0" HorizontalAlignment="Center" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,1">
    			<Rectangle.RenderTransform>
    				<CompositeTransform/>
    			</Rectangle.RenderTransform>
    			<Rectangle.Fill>
    				<SolidColorBrush Color="{StaticResource PhoneContrastBackgroundColor}"/>
    			</Rectangle.Fill>
    		</Rectangle>
    	</Grid>
    </Viewbox>
</UserControl>

Step 13 : Add appear disappear animations

Add AppearClock and DisappearClock animations just below MoveHands inside UserControl.Resource, like so:

        <Storyboard x:Name="AppearClock">
            <DoubleAnimation Duration="0:0:0.3" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="LayoutRoot" d:IsOptimized="True"/>
        </Storyboard>
        <Storyboard x:Name="DisappearClock">
            <DoubleAnimation Duration="0:0:2" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="LayoutRoot" d:IsOptimized="True"/>
        </Storyboard>

Step 14 : Update code behind

Open code behind UCWaitSpinner.xaml.cs and add following lines to its constructor:

        public UCWaitSpinner()
        {
            InitializeComponent();
            MoveHands.RepeatBehavior = RepeatBehavior.Forever;
            LayoutRoot.Opacity = 0d;
            DisappearClock.Completed += (object sender, EventArgs e) => { MoveHands.Stop(); };
        }

Step 15 : Add start functionality

Add a public Start method to the class:

        public void Start()
        {
            MoveHands.Stop();
            AppearClock.Begin();
            MoveHands.Begin();
        }

Step 16 : Add stop functionality

Add a public Stop methods to the class:

        public void Stop()
        {
            DisappearClock.Begin();
        }

Step 17 : Build

Build XAMLControls project. If your project builds properly you should see UCWaitSpinner listed in your Toolbox in XAML Controls.

Step 18 : Add spinner to form

Open MainPage.xaml from the main project WaitSpinner and drag UCWaitSpinner from the Toolbox to the page. Open MainPage.xaml in code view and your will find the newly added control in ContentPanel:

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <my:UCWaitSpinner/>
        </Grid>

Step 19 : Edit control properties

In MainPage.xaml name UCWaitSpinner control as “waitSpinner”, add Height and Width properties with a value of 120 in both, and wrap the control in a StackPanel:

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel>
                <my:UCWaitSpinner x:Name="waitSpinner" Height="120" Width="120" />
            </StackPanel>
        </Grid>

Step 20 : Add buttons to control test

Add two buttons, just below WaitSpinner, Start and Stop with Click handlers to control WaitSpinner:

        <my:UCWaitSpinner x:Name="waitSpinner" Height="120" Width="120" />
        <Button x:Name="buttonStart" Content="Start" Click="buttonStart_Click"/>
        <Button x:Name="buttonStop" Content="Stop"  Click="buttonStop_Click"/>

Step 21 : Write code in click handlers

Open MainPage.xaml.cs and call Start and Stop of WaitSpinner in buttons’ click handlers:

        private void buttonStart_Click(object sender, RoutedEventArgs e)
        {
            waitSpinner.Start();
        }

        private void buttonStop_Click(object sender, RoutedEventArgs e)
        {
            waitSpinner.Stop();
        }

You can run your code and click on Start button to start spinner. Click on Stop button and spinner will slowly fade out.

Let’s see the example code to integrate the control with background processes.

Make following changes to MainPage.xaml’s ContentPanel control:

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" x:Name="textBlockSeconds" Text="..."/>
                    <my:UCWaitSpinner Grid.Column="1" x:Name="waitSpinner2" Height="90" Width="90" />
                </Grid>
                <my:UCWaitSpinner x:Name="waitSpinner" Height="120" Width="120" />
                <Button x:Name="buttonStart" Content="Start" Click="buttonStart_Click"/>
                <Button x:Name="buttonStop" Content="Stop"  Click="buttonStop_Click"/>
                <Button x:Name="buttonBackground" Content="Background" Click="buttonBackground_Click"/>
            </StackPanel>
        </Grid>

And add following code to the code behind MainPage.xaml.cs:

    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        Thread bgThread;
        public MainPage()
        {
            InitializeComponent();
            bgThread = new Thread(new ThreadStart(() => 
            {
                Dispatcher.BeginInvoke(new Action(() => { waitSpinner2.Start(); }));
                for (int cnt = 0; cnt < 5; cnt++)
                {
                    Dispatcher.BeginInvoke(new Action(() => { textBlockSeconds.Text = string.Format("Step - {0}/4", cnt); }));
                    Thread.CurrentThread.Join(2000);
                }
                Dispatcher.BeginInvoke(new Action(() => { waitSpinner2.Stop(); }));
            }));
        }

        private void buttonStart_Click(object sender, RoutedEventArgs e)
        {
            waitSpinner.Start();
        }

        private void buttonStop_Click(object sender, RoutedEventArgs e)
        {
            waitSpinner.Stop();
        }

        private void buttonBackground_Click(object sender, RoutedEventArgs e)
        {
            buttonBackground.IsEnabled = false;
            bgThread.Start();
        }
    }

F5. You should see something like this:

Download entire code here.

C# | .NET Extension Methods

Here are some extension methods in, C# | .NET,  I shared on this blog overtime:

Deep Copy: JSON Route

Smart Duration Class

Safe Substring

Get Children

Scale With Aspect Ratio

UpdateSource

Start Date of The Week

End Date of The Week

Start Date of The Month

End Date of The Month

Hope you find them useful.

Deep Copy: JSON Route

Sharp Snippets

Introduction

This post is not about:

  • … when to deep copy and when to shallow copy
  • … answering what will happen to events
  • … design problem when deep copy is overused (do research)
  • … JSON v/s Binary serialization
  • … having extension at object level
  • … performance

This post is about:

Cloning

View original post

C#, .NET – Smart Duration Class

Sharp Snippets

Have you ever come across a scenario where you require to have start and end dates, validate whether these dates define valid duration, or determine overlapping duration/timespan in two given duration(s)?

If yes, here I present to you a beautiful MVVM ready, equitable, duration class which does all the above and a little more. 🙂

We will call this class TimeDuration.

TimeDuration implements two interfaces,  IEquatable and INotifyPropertyChanged. In later parts we will see the implementation of methods for IEquatable, let’s have INotifyPropertyChanged implementation first.  Include System.ComponentModel to your cs file and define a PropertyChangedEventHandler type public event PropertyChanged. Also create a private method onPropertyChanged with a return type void. The complete INotifyPropertyChanged implementation looks like this:

Basic information encapsulation this class represent consists of start time and end time. So we will have two public DateTime type properties; Start and End in this class. We wish to notify our…

View original post 298 more words