Monday, December 17, 2018

Unit test green and no sleep!

When you have a unit test that depends on the outcome of a slow process, you could use Thread.Sleep to ensure the test is asserted after the process finishes. However this approach will make your test consistently much slower than the system under test.
You can use a delegate in another thread to make a much faster test, but only if you avoid the little pitfall I had today.

The system under test I was working with revolved around a FileSystemWatcher object.  By subscribing to the Created event of this class, you can react to the addition of a new file to a folder. In this case, the name of the new file should be added to a list to be viewed by the user.


using System;
using System.Collections.ObjectModel;
using System.IO;

namespace PDFViewer
{
    /// <summary>
    /// monitors input folder for files,
    /// adds new files to the list and removes read ones if list is over ten items long
    /// </summary>
    internal class ListManager
    {
        internal FileSystemWatcher FileSystemWatcher;
        internal ObservableCollection<ListItem> ListOfItems { get; set; }

        public ListManager(string folderToManage)
        {
            if (Directory.Exists(folderToManage)  == false)
            {
                throw new DirectoryNotFoundException("Listmanager must have existing folder with read permission");
            }

            ListOfItems = new ObservableCollection<ListItem>();
            FileSystemWatcher = new FileSystemWatcher(folderToManage, "*.pdf");
            FileSystemWatcher.Created += OnPdfCreated;
            FileSystemWatcher.Deleted += OnPdfDeleted;
            FileSystemWatcher.EnableRaisingEvents = true;
        }

        private void OnPdfDeleted(object sender, FileSystemEventArgs e)
        {
            throw new NotImplementedException();
        }

        public void OnPdfCreated(object item, FileSystemEventArgs e)
        {
            ListOfItems.Add(new ListItem(e.Name));
        }
    }
}


I wanted to test that I correctly understood how it worked. My first test was straightforward:
[TestMethod]
        public void ManagerAddsNewPdfToList()
        {
            //create temporary filename
            var folder = Path.GetTempPath();
            var file = Path.GetTempFileName() + ".pdf";
            var path = Path.Combine(folder, file);

            //start watching target folder
            var manager = new ListManager(folder);

            File.Create(path);

            //If a file is created, manager should add its name to the list
            Assert.IsTrue(manager.ListOfItems.Count == 1);

            //cleanup
            File.Delete(path);
        }

This test failed. Puzzled I stepped through it and I found that the OnPdfCreated was called AFTER the test was asserted. So the test was going to fast for the file operation to complete.
After poking around the internet a little I found how to write a better test using an AutoResetEvent:
- The event is non-signalled, (stopped).
- When a file is created the event is signalled.
- The test checks if the event was signalled or a timeout happened.

Testing, take two:

        [TestMethod]
        public void ManagerAddsNewPdfToList()
        {
            var creation = new AutoResetEvent(false);

            //Create temporary filename
            var folder = Path.GetTempPath();
            var file = Path.GetTempFileName() + ".pdf";
            var path = Path.Combine(folder, file);

            //Start watching target folder
            var manager = new ListManager(folder);
            
            //Subscribe a delegate to the Created event to set creation to signalled.
            manager.FileSystemWatcher.Created += (s, e) => { creation.Set(); };

            File.Create(path);

            
            //Verify that the Created event is fired.
            var eventFired = creation.WaitOne(1000);
            Assert.IsTrue(eventFired, "Event did not fire.");
            
            //If a file is created, manager should add its name to the list
            //Assert.IsTrue(manager.ListOfItems.Count == 1, "List count is not 1.");

            //Cleanup
            File.Delete(path);
        }

So this should work like a charm, right?
Wrong!

Message: Test method WPFViewerTest.FileManagerTest.ManagerAddsNewPdfToList threw exception: 
System.IO.IOException: The process cannot access the file 
'C:\Users\<snip>\AppData\Local\Temp\tmp404.tmp.pdf' because it is being used by another process.

Again, this error was not consistently reproducible by stepping through code. Sometimes I would get an exception, sometimes not. I do not quite understand how adding an AutoResetEvent, which does not itself operate on the file, can cause this problem.

The solution though proved remarkable simple:
replace File.Create(path); with

//Without using, you get an IO exception
using (File.Create(path))
    { }

This ensures that the file handle is closed after the file is created, and the test passes.

Saturday, October 28, 2017

C# Filter duplicates from a collection quickly!

Today I did some testing to determine which of two methods was faster at finding duplicates in a collection: a LINQ query or a Dictionary.


In C#, there are always more than one way to do things. For example: I have a list of items which may or may not contain some duplicate items. In order to select just the duplicates, i wanted to put those in a separate list. I found two pieces of code able to do that.

For the hasty ones: a Dictionary is faster when you have very few duplicates. When you have a lot of duplicates, the dictionary becomes MUCH slower. The query stays fast.

The first is a LINQ : A Query written directly in C# source code.
In this program "source" is a list<String>. Lets assume that it contains: "aaa", "bbb", "ccc", "aaa". The query groups identical strings together, and then counts how many items are in each group. In this case, executing the query will produce three groups: One group with two strings "aaa", and two groups which each have one string, "bbb" and "ccc" respectively. Only the group with more than one string will be selected.
 ( This function was written just to test. To measure a  meaningful amount of time, the function is repeated a number of times. )

public void LINQ()
 {
 for (int i = 0; i < cycles; i++)
  {
  duplicates.Clear();

  var duplicatefilenames = from string item in source
     group item by item into duplicates
     where duplicates.Count() > 1
     select duplicates;

  foreach (var items in duplicatefilenames)
   {
   duplicates.Add(items.First());      
   }
  }
 }


The second method is by using a Dictionary. The idea here is that a Dictionary can only contain unique keys. If you try to enter a duplicate value an exception will be thrown:


public void Dictionary()
 {
 Dictionary<string, int> UniqueList = new Dictionary<string, int>();

 for (int i = 0; i < cycles; i++)
  {
  duplicates.Clear();
  UniqueList.Clear();

  foreach (string item in source)
   {
   try
    {
    UniqueList.Add(item, i);
    }
   catch (ArgumentException) //item already exists in uniquelist
    {
    duplicates.Add(item);
    }
   }
  }
 }


Now, my assumption was that maybe using a dictionary would be faster than using a LINQ query. So i set up a test with a Stopwatch. (Not a manual one, the .Net class. Click for details --->>

Conclusion:

As it turns out, I was wrong. When you have a source containing no duplicates, the dictionary is actually faster than the query. When you have some duplicates, the dictionary is MUCH slower.

For these tests, the list "source" is filled with 1000 unique strings. Between 0  and 100 % of those are inserted into the source a second time.
Below is the output of the program:

Starting speed test.
Repetitions: 10.000, percentage of duplicates: 0

Starting Linq test
Linq test finished in 2438 ms.

Starting Dictionary test. If there are a lot of collisions this might take a while...
Dictionary test finished in 716 ms.

So far, so good. Until we add some duplicates. The first time i ran the program, i shut it down because i thought it wasn't working right. As it turns out, catching exceptions just takes a really, really long time. Note that the output below has just one hundred cycles:

Starting speed test.
Repetitions: 100, percentage of duplicates: 1

Starting Linq test
Linq test finished in 35 ms.

Starting Dictionary test. If there are a lot of collisions this might take a while...
Dictionary test finished in 5210 ms.

WOW. The dictionary takes almost 149 times as long! For more information on how to use LINQ, a good source is these examples. I hope this helps you out, and have a great day coding!

Sunday, March 26, 2017

Make a 3D cylinder the easy way

To make a 3D cylinder in a WPF application, you could use a 3D model. However in this post I describe a quick alternative: With a progress bar, a adorner and an opacity mask you can get the same effect using only two dimensions.

When deciding to make a vertical cylinder I decided to try to do so by subclassing a progress bar. This offers easy access to the maximum, minimum and value properties, enabling the making of cylinders with arbitrary length.
I updated the appearance of the progress bar in three steps:

1- Paint over the solid color of the bar with a gradient brush to simulate a lightsource
2- Hide some of the bottom of the progress bar to make it look round. This is done using the property OpacityMask
3- Paint an ellipse on the top of the cylinder.

The result looks like this:


Applying the effects to the cylinders is done with an adorner. After the control is loaded, the function AddDecorator gets the adornerlayer for the control, and adds an instance of cylinderadorner to it:

public class Cylinder : ProgressBar
{
 public Cylinder()
  {
  this.Loaded += AddDecorator;
  Background = null;
  if (Foreground as SolidColorBrush is null)
   {
   throw new ArgumentException("Exception: Foreground of cylinder  must be a solid color");
   }
  }

 private void AddDecorator(object sender, RoutedEventArgs e)
  {
  AdornerLayer a = AdornerLayer.GetAdornerLayer(this);
  a.Add(new CylinderAdorner(this));
  }
}
 

Cylinderadorner is a class which inherits from Adorner. When the control is painted, the adorner is painted on top of it by OnRender.
Another example of this can be found Here.


public class CylinderAdorner : Adorner
 {
 //store two variables needed more than once
 private Cylinder cylinder;
 private PathGeometry pathgeometry;
 const int sizeFactor = 4; //ratio of width vs height used for ellipses

 public CylinderAdorner(UIElement element) : base(element)
  {
  cylinder = this.AdornedElement as Cylinder;
  }

 protected override void OnRender(DrawingContext drawingContext)
  {
  //get outline of cylinder, used by all three next functions
  pathgeometry = getCylinderOutline();

  //overlay gradientbrush to simulate 3D shape
  overlayGradient(drawingContext);

  //Now hide the bottom / right of cylinder with an opacitymask    
  cylinder.OpacityMask = getOpacityMask();

  //place ellipse on top of cylinder
  placeEllipse(drawingContext);
  }
       ...

OverlayGradient draws a white and black shape over the progress bar. The shape of this is a cylinder with a rounded bottom, as tall as the "value" part of the progress bar.

GetOpacityMask uses the same shape, filled with a black brush, to only show this part of the bar.

Finally placeEllipse draws the top of the cylinder. It also uses pathgeometry to get the dimensions of the ellipse: pathgeometry contains a RECT poperty called Bounds which contains the outer dimensions.

The work is done by the other members of CylinderAdorner:
public class CylinderAdorner : Adorner
{
//store for two variables needed more than once
private Cylinder cylinder;
private PathGeometry pathgeometry;
const int sizeFactor = 4; //ratio of width vs height used for ellipses

public CylinderAdorner(UIElement element) : base(element)
 {
 cylinder = this.AdornedElement as Cylinder;
 }

protected override void OnRender(DrawingContext drawingContext)
 {
 //get outline of cylinder, used by both PaintGradient and getOpacityMask
 pathgeometry = getCylinderOutline();

 //overlay gradientbrush to simulate 3D shape
 overlayGradient(drawingContext);

 //Now hide the bottom / right of cylinder with an opacitymask    
 cylinder.OpacityMask = getOpacityMask();

 //place ellipse on top of cylinder
 placeEllipse(drawingContext);
 }

 private Rect getCylinderOutlineRect(Orientation orientation)
  {
  Rect rect = new Rect();
  if (cylinder.Orientation == Orientation.Vertical)
   {
   //creatre rectangle on the bottom part of progress bar, from the bottom of the cylinder up to value
   rect.Width = cylinder.ActualWidth;
   rect.Y = cylinder.ActualHeight - ((cylinder.Value * cylinder.ActualHeight) / (cylinder.Maximum - cylinder.Minimum));
   rect.Height = cylinder.ActualHeight - rect.Y;
   }
  else
   {
   //create rectangle on the left of the progress bar, with width equivalent to value
   rect.Height = cylinder.ActualHeight;
   rect.Width = (cylinder.Value * cylinder.ActualWidth) / (cylinder.Maximum - cylinder.Minimum);
   }
  return rect;
  }

 /// <summary>
 /// Create a PathFigure with the outline of a cylinder with rounded bottom
 /// Start drawing from top right corner clockwise
 /// </summary>
 /// <returns>Outline of a cylinder</returns>
 private PathGeometry getCylinderOutline()
  {
  List<PathSegment> pathSegments = new List<PathSegment>();

  //get rect around the straight part of the cylinder
  Rect rect = getCylinderOutlineRect(cylinder.Orientation);

  if (cylinder.Orientation == Orientation.Vertical)
   {
   //draw line to bottom right corner, arc to bottom left, line to top left:
   pathSegments.Add(new LineSegment(new Point(rect.Width, rect.Bottom - (rect.Width / sizeFactor)), false));
   pathSegments.Add(new ArcSegment(new Point(0, rect.Bottom - (rect.Width / sizeFactor)), new Size(rect.Width / 2, rect.Width / sizeFactor), 0, false, SweepDirection.Clockwise, false));
   pathSegments.Add(new LineSegment(new Point(0, rect.Top), false));
   }
  else
   {
   //draw line to bottom right, line to bottom left, arc to top left:
   pathSegments.Add(new LineSegment(new Point(rect.Width, rect.Height), false));
   pathSegments.Add(new LineSegment(new Point(rect.Height / sizeFactor, rect.Height), false)); // stop some distance short of left side
   pathSegments.Add(new ArcSegment(new Point(rect.Height / sizeFactor, 0), new Size(rect.Height / sizeFactor, rect.Height / 2), 0, false, SweepDirection.Clockwise, false));
   }

  //drawing starts at top right:
  PathFigure pf = new PathFigure(new Point(rect.Width, rect.Top), pathSegments, true);
  return new PathGeometry(new List<PathFigure> { pf });
  }

 /// <summary>
 /// Draw a lineair gradient brush over the cylinder to create 3D effect
 /// Start and end points are relative, from 0 to 1. 0.5 is half the dimension of the control
 /// </summary>
 /// <param name="drawingContext"></param>
 private void overlayGradient(DrawingContext drawingContext)
  {
  LinearGradientBrush lbg = new LinearGradientBrush();
  if (cylinder.Orientation == Orientation.Vertical)
   {
   lbg.StartPoint = new Point(0, 0.5);
   lbg.EndPoint = new Point(1, 0.5);
   }
  else
   {
   lbg.StartPoint = new Point(0.5, 0);
   lbg.EndPoint = new Point(0.5, 1);
   }
  lbg.GradientStops.Add(new GradientStop(Colors.White, 0));
  lbg.GradientStops.Add(new GradientStop((cylinder.Foreground as SolidColorBrush).Color, 0.3));
  lbg.GradientStops.Add(new GradientStop(Colors.Black, 0.7));
  lbg.Opacity = 0.25;

  drawingContext.DrawGeometry(lbg, null, pathgeometry);
  }

 /// <summary>
 /// Create an opacitymask composed of a rectangle and a circle,
 /// to round to bottom end of the progress bar
 /// </summary>
 /// <param name="radius">Radius of the circle to use</param>
 public Brush getOpacityMask()
  {
  GeometryDrawing maskDrawing = new GeometryDrawing(Brushes.Black, null, pathgeometry);
  DrawingBrush drawingBrush = new DrawingBrush
   {
   Drawing = maskDrawing,
   Stretch = Stretch.None,
   ViewboxUnits = BrushMappingMode.Absolute,
   AlignmentX = AlignmentX.Left,
   AlignmentY = AlignmentY.Top,
   };
  return drawingBrush;
  }

 /// <summary>
 /// Determine size and place of an ellipse,
 /// it goes on top of the cylinder.
 /// </summary>
 /// <param name="origin">Center of the ellipse</param>
 /// <param name="radius">Width and height of the circle</param>
 private void placeEllipse(DrawingContext drawingContext)
  {
  Point origin = new Point();
  Vector radius = new Vector();

  if (cylinder.Orientation == Orientation.Vertical)
   {
   radius.X = pathgeometry.Bounds.Width / 2;
   radius.Y = pathgeometry.Bounds.Width / sizeFactor;

   origin.X = pathgeometry.Bounds.Width / 2;
   origin.Y = pathgeometry.Bounds.Top;
   }
  else
   {
   radius.X = pathgeometry.Bounds.Height / sizeFactor;
   radius.Y = pathgeometry.Bounds.Height / 2;

   origin.Y = pathgeometry.Bounds.Height / 2;
   origin.X = cylinder.Value / (cylinder.Maximum - cylinder.Minimum) * cylinder.ActualWidth;
   }

  //Paint ellipse on control
  drawingContext.DrawEllipse(cylinder.Foreground, new Pen
   (new SolidColorBrush(Colors.Black), 0.5), origin, radius.X, radius.Y);
  }
 }

As it turns out, this code works quite nicely. There is one caveat though: If you give the control a margin, and make the window small enough for the control to be clipped, the adorner is not clipped with it.
And while this is not a problem in practice when showing a full cylinder, I am still making a proper 3D cylinder the next time around, as shown here.

Want to check out the code for yourself?
download 3D Cylinder.zip from Sabercat File Hosting



Tuesday, January 24, 2017

My free batchwatermarker program is finally done!

So after a few MONTHS of tinkering the first program I decided to make, fully develop and complete is finally finished! Below, you will find some screenshots and a download link to Batchwatermarker V 1.0. There's even a manual!

I am of course fully aware that making this program was not a lot different from pissing into the ocean. But hey, I chose to make it, see it through, and i learned a ton doing it. Mostly, that i need to get a lot better at planning what i want to make. Seriously. Just today I was testing the installation package I made. The first one didn't work because it missed a .dll file. After more testing I fixed the last bugs, and I made a few small improvements. Not good imho, after spending so much time messing with it.
However I did not just spend time making it work, I spent a considerable time to make it work RIGHT. As a result, the few things that still needed attention were very easy to find and change. Properly putting classes in separate files instead of spaghetti code is key. There are only a few functions in this program, so I was able to test them thoroughly and make sure that everything works. Even when you don't press the buttons in the proper order....

So here is what is looks like. First page:
Select the files you wish to mark and where you wish to save the marked files to disc.











Second page:
Specify and move your watermark. You can preview the result on the right.







Third page:
Change the font, style, color, opacity and size of the text used to watermark your image. 
When you press the "Mark my images" button, the watermark is applied to each image in the list from step 1 and written to disk with the location and filename specified.








So, would you like to give it a try? Click the link just below, and let me know what you think. Its FREE.

download BatchWaterMarker.msi from Sabercat File Hosting

Monday, November 28, 2016

Always show tooltips!

An interface with disabled controls, without any discernible reason why they are disabled, might be confusing to the applications user. Tooltips can prevent this, but adding a second tooltip for a disabled control will result in XAML bloat. In this blog post i describe an alternative to have two tooltips on one control, without the overhead.

If you want to add a tooltip to a control in XAML, and a second tooltip to show when that control is disabled, the simplest way is to add a style, as shown here:


 <Button x:Name="btnClearAll" DockPanel.Dock="Left" Content="Clear _All" 
 IsEnabled="{Binding ElementName=lbSelectedFiles, Path=HasItems}" Padding="10,0" TabIndex="2" VerticalAlignment="Bottom">  
      <Button.Style>  
           <Style TargetType="Button">  
                <Setter Property="ToolTip" Value="Remove all files from the list" />  
                <Style.Triggers>  
                     <Trigger Property="IsEnabled" Value="False">  
                          <Setter Property="ToolTip" Value="No files in the list"/>  
                          <Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>  
                     </Trigger>  
                </Style.Triggers>  
           </Style>  
      </Button.Style>  
 </Button> 


While this might be just fine for a single tooltip, a window with a lot of controls would add up to a LOT of extra XAML. I decided to look something more compact. One possibility I considered was to make a new dependency property and manipulate the tooltip text that way. But after reading up on that i decided there were two reasons to not do so:
1- It would have gotten pretty complicated,
2- it would not have fitted in smoothly with the existing property.

What i came up with is more elegant and a lot simpler: I used a converter to assign the tooltip text. Unfortunately a converter does not have (that I know of) a convenient way to tell which control called it, so I included the name of the caller in the parameter:

  <Window.Resources>  
     <local:FetchToolTip x:Key="ToolTipProvider"/>  
 </Window.Resources>  
  <Button x:Name="btnClearSelected" Content ="Remove Selected"  
        ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled,  
        Converter={StaticResource ToolTipProvider}, ConverterParameter=btnClearSelected}">  
 </Button>  


The class FetchToolTip gets the tooltip form the project resources, based on the name of the caller and the value of the boolean:

 Public Class FetchToolTip  
   Implements IValueConverter  
   Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert  
     Dim bEnabled As Boolean  
     Dim strName As String  
     Dim strToolTip As String  
     Try  
       bEnabled = CBool(value)  
       strName = CStr(parameter)  
       If (bEnabled) Then  
         strToolTip = My.Resources.ResourceManager.GetString(strName)  
       Else  
         strToolTip = My.Resources.ResourceManager.GetString(strName & "_Disabled")  
       End If  
       Return strToolTip  
     Catch ex As Exception  
       MsgBox("exception in ToolTipProvider")  
       Debug.Write(Utilities.GetExceptionInfo(ex))  
     End Try  
     Return "Tooltip not found."  
   End Function  
   Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack  
     Throw New NotImplementedException  
   End Function  
 End Class  

The last bit of the puzzle then is to add some strings to our project to be used for the tooltip:


And Voila! Two tooltips per control, with overhead included only once!
Hope this helps someone, and have fun coding!

Saturday, November 5, 2016

Focusing was unexpectedly hard!

Today i demonstrate how to set the focus on a textbox after selecting a tab item. I assumed that doing so would just require a call to textboxname.Focus. Of course if I had been right, i would have had to blog about the topic of my next blog post today....


I was working on a WPF application in visual basic containing a tab control with two TabItems. My goal was to set the keyboard focus on a specific textbox contained on the second tab item, as soon as it was clicked. After trying to call the Focus method from tabcontrol.SelectionChanged i did a little digging, and apparently the page was still too busy updating its layout when i called the focus method.

Explicitly calling UpdateLayout did not work. The way around that was to set the focus asynchronously, by using the windows dispatcher. Doing this creates a new thread , allowing the old threat to continue doing its job, while the new thread sets the focus.

The code i have used for this:

Application.Current.Dispatcher.BeginInvoke _
(System.Windows.Threading.DispatcherPriority.Background, _
New Action(Sub() txtWatermark.Focus()))

This code produced the desired result, by creating a new sub with one instruction, called a Lambda Expression. If you would like to have a bigger sub to do the work, the code becomes:


dim action as Action = sub() FocusOnTextBlock()

Private Sub NextTabItem
TabsApp.SelectedIndex += 1
Application.Current.Dispatcher.BeginInvoke
(System.Windows.Threading.DispatcherPriority.Background, action ))
End Sub

Private Sub FocusOnTextblock()
txtWatermark.Focus()
End Sub
On a side note: I just tested the methods shown in the linked article on Focus. It did not work for me. If I missed another way to set the focus, let me know! I hope that this helps someone out and happy coding!

Monday, October 31, 2016

Shorty before bedtime....

So what have i been up to today: I have been studying some more WPF, and have learned how to apply an animation style to a data template. This means i can now make controls in a window appear out of nowhere on command, and make them sing. The dancing part i'm still working on.
Big thanks to mr. Daniel Pratt, whom you can find Here.
I have also learned that adding the coloranimation property Autoreverse=true makes the animation go in reverse after it has completed. Here is my code:

For completeness, the code used to show this datatemplate is below: