Tuesday, March 13, 2018

Overlapping ranges made easy

A while ago a team member wrote a routine to determine if two ranges of dates overlapped. He wrote seven different IF statements covering all the scenarios he saw. I tried to explain to him that you can do this with one statement. I don't think he ever understood.

It's really quite simple. Two ranges do not overlap if the largest of the minimums is greater than the smallest of the maximums. For example, if you have Min1-Max1 and Min2-Max2 as your ranges then there is no overlap if Max(Min1,Min2) > Min(Max1,Max2).

Here's a way too complex demo of this algorithm in action. Start a new WPF application project and call it Overlap. Target any Framework from 3.5 onward.


MainWindow.xaml uses a canvas and looks like this...

<Window x:Class="Overlap.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:local="clr-namespace:OverlappingDates"
        mc:Ignorable="d" ResizeMode="NoResize"
        Title="Overlapping Ranges" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Canvas Background="AliceBlue" MouseMove="Canvas_MouseMove" MouseUp="Canvas_MouseUp" MouseLeave="Canvas_MouseLeave">
        <TextBlock Canvas.Left="10" Canvas.Top="10" Text="{Binding Line1Start, StringFormat='Min: {0}'}"/>
        <Line X1="{Binding Line1Start}" X2="{Binding Line1End}" Y1="30" Y2="30" Stroke="Red" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeThickness="10" MouseLeftButtonDown="Line1_MouseLeftButtonDown"/>
        <TextBlock Canvas.Left="450" Canvas.Top="10" Text="{Binding Line1End,StringFormat='Max: {0}'}"/>
       
        <TextBlock Canvas.Left="10" Canvas.Top="70" Text="{Binding Line2Start, StringFormat='Min: {0}'}"/>
        <Line X1="{Binding Line2Start}" X2="{Binding Line2End}" Y1="90" Y2="90" Stroke="Green" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeThickness="10" MouseLeftButtonDown="Line2_MouseLeftButtonDown"/>
        <TextBlock Canvas.Left="450" Canvas.Top="70" Text="{Binding Line2End, StringFormat='Max: {0}'}"/>

        <TextBlock Canvas.Left="10" Canvas.Top="120" Text="{Binding Path=MaxMin, StringFormat='Maximum Min value is {0}'}"/>
        <TextBlock Canvas.Left="10" Canvas.Top="140" Text="{Binding Path=MinMax, StringFormat='Minimum Max value is {0}'}"/>
        <TextBlock Canvas.Left="10" Canvas.Top="160" Text="{Binding Path=Overlap, StringFormat='Max(Min1,Min2) less than or equal to Min(Max1,Max2) = Ranges overlap: {0}'}"/>

        <TextBlock Canvas.Left="10" Canvas.Top="250" Text="Click and drag the ends of the lines to see the algorithm in action" FontWeight="Bold"/>
    </Canvas>
</Window>


Most of the code behind is involved in the graphics. It looks like this. I have bolded the three lines that are important.

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;

namespace Overlap
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {

        public Double Line1Start
        {
            get { return _Line1Start; }
            set
            {
                SetProperty(ref _Line1Start, value);
                OnPropertyChanged("MaxMin");
            }
        }

        public Double Line1End
        {
            get { return _Line1End; }
            set
            {
                SetProperty(ref _Line1End, value);
                OnPropertyChanged("MinMax");
            }
        }

        private Double _Line1Start = 25;
        private Double _Line1End = 475;

        public Double Line2Start
        {
            get { return _Line2Start; }
            set
            {
                SetProperty(ref _Line2Start, value);
                OnPropertyChanged("MaxMin");
            }
        }

        public Double Line2End
        {
            get { return _Line2End; }
            set
            {
                SetProperty(ref _Line2End, value);
                OnPropertyChanged("MinMax");
            }
        }

        private Double _MinMax;
        public Double MinMax
        {
            get
            {
                _MinMax =  Math.Min(Line1End, Line2End);
                OnPropertyChanged("Overlap");
                return _MinMax;
            }
        }

        private Double _MaxMin;
        public double MaxMin
        {
            get
            {
                _MaxMin = Math.Max(Line1Start, Line2Start);
                OnPropertyChanged("Overlap");
                return _MaxMin;
            }
        }

        public string Overlap
        {
            get { return (_MaxMin <= _MinMax ? "True" : "False"); }
        }

        private Double _Line2Start = 25;
        private Double _Line2End = 475;

        private Point PriorMousePosition;
        private bool TrackingLine1Start = false;
        private bool TrackingLine1End = false;
        private bool TrackingLine2Start = false;
        private bool TrackingLine2End = false;

        public event PropertyChangedEventHandler PropertyChanged;
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(storage, value)) return false;
            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }
        private void OnPropertyChanged(String PropertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
            }
        }
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Line1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            PriorMousePosition = Mouse.GetPosition(this);
            if (PriorMousePosition.X > Line1Start + (Line1End - Line1Start) / 2)
                TrackingLine1End = true;
            else
                TrackingLine1Start = true;
        }
        private void Line2_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            PriorMousePosition = Mouse.GetPosition(this);
            if (PriorMousePosition.X > Line2Start + (Line2End - Line2Start) / 2)
                TrackingLine2End = true;
            else
                TrackingLine2Start = true;
        }
        private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            StopTracking();
        }

        private void Canvas_MouseLeave(object sender, MouseEventArgs e)
        {
            StopTracking();
        }
        private void StopTracking()
        {
            TrackingLine1End = false;
            TrackingLine1Start = false;
            TrackingLine2End = false;
            TrackingLine2Start = false;
        }

        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {
            double DeltaX = Mouse.GetPosition(this).X - PriorMousePosition.X;
            if (TrackingLine1Start)
            {
                Line1Start += DeltaX;
                if (Line1Start < 25) Line1Start = 25;
                if (Line1Start > Line1End - 10) Line1Start = Line1End - 10;
            }
            if (TrackingLine1End)
            {
                Line1End += DeltaX;
                if (Line1End > 475) Line1End = 475;
                if (Line1End < Line1Start + 10) Line1End = Line1Start + 10;
            }
            if (TrackingLine2Start)
            {
                Line2Start += DeltaX;
                if (Line2Start < 25) Line2Start = 25;
                if (Line2Start > Line2End - 10) Line2Start = Line2End - 10;
            }
            if (TrackingLine2End)
            {
                Line2End += DeltaX;
                if (Line2End > 475) Line2End = 475;
                if (Line2End < Line2Start + 10) Line2End = Line2Start + 10;
            }
            PriorMousePosition = Mouse.GetPosition(this);
        }

    }
}


You can click and drag the ends of the lines to verify that the algorithm works correctly.






Today a team member asked me how to check date ranges when any of the range limits could be null. For example, a range of null-today means any date up to and including today. I mentioned that the datetime class has static minvalue and maxvalue properties that can be substituted for null values. Most primitive classes in .Net do.



Thursday, February 15, 2018

WPF program "Has stopped working"


Recently I started getting a slew of these errors being reported by my QA group for an existing Accounts Payable project. It turned out that my boss had commented out the unhandled exception handlers for the project. The original designer of the project didn't really understand exception handling so he relied on the unhandled exception handler a lot.

Let's create a project that throws an unhandled exception, then we'll see how to handle them. Create a new C# WPF application called UnhandledExceptions. Target any framework you want.


The main window will have a single button. When the user clicks on it we will attempt to add to an uninitialized list which will throw an exception. As we have no exception handling, we will see the dialog at the top of this blog entry.

The XAML and code behind look like this...

<Window x:Class="UnhandledExceptions.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:local="clr-namespace:UnhandledExceptions"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Content="Click Me" Click="Button_Click"/>
    </Grid>
</Window>


and

using System;
using System.Collections.Generic;
using System.Windows;

namespace UnhandledExceptions
{
    public partial class MainWindow : Window
    {
        List<String> l = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            l.Add("A");
        }
    }
}

If you run this in visual studio you will find that visual studio is very helpful and displays the exception on the line that throws it, but the user will not have such a pleasant experience. Build the project and then browse to the executable and run it. Mine is found at C:\Users\me\Documents\Visual Studio 2015\Projects\UnhandledExceptions\UnhandledExceptions\bin\Debug\UnhandledExceptions.exe.



Fortunately this is an easy fix.

In MainWindow, add an unhandled exception handler and then write the code for the handler. I have chosen to use the exception's ToString method and flag the event as handled, so the default dialog box does not get shown. You can also opt to terminate the application here if you want. The code behind now looks like this.

using System;
using System.Collections.Generic;
using System.Windows;

namespace UnhandledExceptions
{
    public partial class MainWindow : Window
    {
        List<String> l = null;

        public MainWindow()
        {
            InitializeComponent();
            Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
        }

        private void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            MessageBox.Show(e.Exception.ToString());
            Application.Current.Shutdown();
            e.Handled = true;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            l.Add("A");
        }
    }
}

Build the project again and run the executable from Windows Explorer.



Tuesday, January 30, 2018

In defense of foreach

I recently inherited the project from hell that was abandoned by the technical lead who resigned rather than see it to conclusion. Looking through his code, I see some terrible coding habits such as if/then/else ladders instead of switch statements and an inability to consolidate code or use base classes. However, in this post, I'm going to focus on his refusal to use foreach.

Let's see just how inefficient for loops are compared to foreach. I'm going to create a list of integers and then execute two loops that reference the list elements three times. The stopwatch class will tell me how much time we can save by utilizing foreach.

Create a new console application.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ForEach
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            List<int> l = Enumerable.Range(1, 10000000).ToList<int>();
            int x;

            sw.Start();
            for (int i = 0; i < l.Count; i++)
            {
                x = l[i];
                x = l[i] + 1;
                x = l[i] * 2;
            }

            sw.Stop();
            Console.WriteLine("for i took " + sw.ElapsedMilliseconds.ToString() + "ms");

            sw.Restart();
            foreach (int i in l)
            {
                x = i;
                x = i + 1;
                x = i * 2;
            }

            sw.Stop();
            Console.WriteLine("for each took " + sw.ElapsedMilliseconds.ToString() + "ms");
            Console.ReadKey();
        }
    }
}



On my computer the for i loop takes 107ms and the foreach took 52ms. Quite a significant difference.