Wednesday, January 28, 2009

Perfect Random numbers


#region Copyright © 2009 Ruben Knuijver [r.knuijver@primecoder.com]
/*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the author(s) be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
#endregion

using System;
using System.Threading;

class TaskRnd : IDisposable
{
internal class RandomParam
{
public int Seed { get; set; }
public int minValue { get; set; }
public int maxValue { get; set; }
}

private Thread worker = null;
public delegate void WorkCompleteHandle(int rnd);
public event WorkCompleteHandle OnWorkComplete;

private RandomParam _randomParam = new RandomParam();

public int Seed { set { _randomParam.Seed = value; } }
public int minValue { set { _randomParam.minValue = value; } }
public int maxValue { set { _randomParam.maxValue = value; } }

public TaskRnd()
{
Seed = 1978;
minValue = int.MinValue;
maxValue = int.MaxValue;
}

private void DoWork(Object param)
{
RandomParam p = param as RandomParam;
Random rnd = new Random(p.Seed);
int min = p.minValue,
max = p.maxValue;

while (OnWorkComplete != null)
{
int value = rnd.Next(min, max);

OnWorkComplete(value);
}
}

public void Start()
{
worker = new Thread(new ParameterizedThreadStart(this.DoWork));
worker.Start(_randomParam);
Thread.Sleep(1);
}

#region IDisposable Members

public void Dispose()
{
if (worker.IsAlive)
{
worker.Abort();
worker.Join();
worker = null;
}
}

#endregion
}


Here is how to use the code:

1: using (TaskRnd t = new TaskRnd())
2: {
3: int i = 0;
4: t.maxValue = 255;
5: t.minValue = 0;
6: t.OnWorkComplete += (rnd) => { i = rnd; };
7: t.Start();
8:
9: for (int x = 0; x < 10; x++)
10: {
11: listValues2.Items.Add(string.Format("rnd {0}: {1}", x, i));
12: }
13: }

Monday, January 5, 2009

How to write a download task list in WPF


In this short tutorial I’ll explain how to write a ListBox that is databound
to a list of download tasks. The sourcecode for this tutorial is in C# 3.5 (WPF)
XAML. Let’s start by writing our DownloadTaskItem class.




internal class DownloadTaskItem : INotifyPropertyChanged, IDisposable



The INotifyPropertyChanged event can be used to signal
the parent the one of our property values has changed.



public event PropertyChangedEventHandler PropertyChanged;

void OnPropertyCanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}



Now let’s add a fue priviate members.



private WebClient webClient;


This WebClient object will use for our async download.


private string fileName;

private Uri uriData;

private long fileSize;

private Guid _guid;

private string _progressText;

private string _buttonText;

private string _associatedGroupText;



To implement this in our properties and make this all
work we’ll have to call OnPropertyChanged in our properties.



public string AssociatedGroupText
{
get { return _associatedGroupText; }
set
{
_associatedGroupText = value;
OnPropertyCanged("AssociatedGroupText");
}
}

public string ProgressText
{
get { return _progressText; }
set
{
_progressText = value;
OnPropertyCanged("ProgressText");
}
} // DETAIL_BYTES

public string ButtonText
{
get { return _buttonText; }
set
{
_buttonText = value;
OnPropertyCanged("ButtonText");
}
}


The rest of the properties can be write as usual.



public string OriginalUrl { get { return uriData.OriginalString; } } // DETAIL_URI

public string TaskGuid
{
get
{
if (null != _guid)
_guid = new Guid();
return _guid.ToString();
}
}

public long ReceivedFileSize { get { return fileSize; } }



Now how does the parent object know if this downloadtask
has completed. Well the best way to do this is by adding an other event.

First thing we need is an EventArgs class:



public class DownloadTaskEventArgs : EventArgs
{
private readonly Uri uri;

private readonly System.ComponentModel.AsyncCompletedEventArgs args;

//Constructor.
public DownloadTaskEventArgs(Uri uri, System.ComponentModel.AsyncCompletedEventArgs e)
{
this.uri = uri;
this.args = e;
}

public Uri DataUri
{
get { return uri; }
}

public System.ComponentModel.AsyncCompletedEventArgs Args
{
get { return args; }
}

// Notifying Text property that contains a status message.
//
public string Text
{
get
{
if (args.Error == null || args.Cancelled)
{
return ("completed");
}
else
{
return ("failed");
}
}
}
}


Now adding the event to our DownloadTaskItem class:



public delegate void CompletedEventHandler(object sender, DownloadTaskEventArgs e);

public event CompletedEventHandler Completed;

// The protected OnCompleted method raises the event by invoking
// the delegates. The sender is always this, the current instance
// of the class.
//
protected virtual void OnCompleted(DownloadTaskEventArgs e)
{
CompletedEventHandler handler = Completed;
if (handler != null)
{
// Invokes the delegates.
handler(this, e);
}
}


DownloadTaskItem Class:



internal class DownloadTaskItem : INotifyPropertyChanged, IDisposable
{
#region Task Completed events
public delegate void CompletedEventHandler(object sender, DownloadTaskEventArgs e);

public event CompletedEventHandler Completed;

// The protected OnCompleted method raises the event by invoking
// the delegates. The sender is always this, the current instance
// of the class.
//
protected virtual void OnCompleted(DownloadTaskEventArgs e)
{
CompletedEventHandler handler = Completed;

if (handler != null)
{
// Invokes the delegates.
handler(this, e);
}
}
#endregion

#region Private fields
private WebClient webClient;

private string fileName;

private Uri uriData;

private long fileSize;

private Guid _guid;

private string _progressText;

private string _buttonText;

private string _associatedGroupText;
#endregion

#region Great Properties
public string AssociatedGroupText
{
get { return _associatedGroupText; }
set
{
_associatedGroupText = value;
OnPropertyCanged("AssociatedGroupText");
}
}

public string ProgressText
{
get { return _progressText; }
set
{
_progressText = value;
OnPropertyCanged("ProgressText");
}
} // DETAIL_BYTES

public string OriginalUrl { get { return uriData.OriginalString; } } // DETAIL_URI

public string TaskGuid
{
get
{
if (null != _guid)
_guid = new Guid();
return _guid.ToString();
}
}

public string ButtonText
{
get { return _buttonText; }
set { _buttonText = value; OnPropertyCanged("ButtonText"); }
}

public long ReceivedFileSize { get { return fileSize; } }
#endregion

#region DownloadTaskItem Constructors
///
/// Initializes a new instance of the class.
///

public DownloadTaskItem(Uri uriData, string path)
{
#region Prepare for download
this.uriData = uriData;

fileName = this.uriData.Segments[this.uriData.Segments.Length - 1];
fileName = path.Equals(string.Empty) ? fileName : string.Format("{0}\\{1}", path, fileName);

webClient = new WebClient();
#endregion

#region Assign call backs
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler((sender, e) =>
{
#region Check if additional info. is required
#endregion

#region Show progress info
fileSize = e.TotalBytesToReceive;
AssociatedGroupText = String.Format("{0} - {1}%", fileName, e.ProgressPercentage);

ProgressText = String.Format("Downloaded {0}/{1}", GetHumanReadableFileSize(e.BytesReceived), GetHumanReadableFileSize(fileSize));
#endregion
});

webClient.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler((sender, e) =>
{
#region Check if additional info. is required
#endregion

#region Process error state
if (e.Error == null)
{
AssociatedGroupText = String.Format("{0} - completed", fileName);
ProgressText = String.Format("File size - {0}", GetHumanReadableFileSize(fileSize));
}
else if (e.Cancelled == false && e.Error != null)
{
AssociatedGroupText = String.Format("{0} - Failed", fileName);
ProgressText = e.Error.Message;
}
#endregion

#region Process canceled download
if (e.Cancelled == true)
{
AssociatedGroupText = String.Format("{0} - Canceled", fileName);
}
#endregion

ButtonText = "Remove";

OnCompleted(new DownloadTaskEventArgs(uriData, e));
});

AssociatedGroupText = String.Format("{0} - starting ... ", fileName);
ProgressText = "Downloaded 0/? bytes";
ButtonText = "Cancel";
#endregion

#region Start file download
webClient.DownloadFileAsync(uriData, fileName, this);
#endregion
}
#endregion

public bool StopDownload(string message)
{
ProgressText = message;
if (webClient.IsBusy == true)
{
webClient.CancelAsync();
return true;
}
else
{
webClient.CancelAsync();
return false;
}
}

#region Helper classes
///
/// Convert bytes to a human readable format
///

/// the number bytes
/// apropriate amount of bytes (Gb, Mb, Kb, bytes)
private string GetHumanReadableFileSize(long fileSize)
{
#region Gb
if ((fileSize / (1024 * 1024 * 1024)) > 0)
{
return String.Format("{0} Gb", (double)Math.Round((double)(fileSize / (1024 * 1024 * 1024)), 2));
}
#endregion

#region Mb
if ((fileSize / (1024 * 1024)) > 0)
{
return String.Format("{0} Mb", (double)Math.Round((double)(fileSize / (1024 * 1024)), 2));
}
#endregion

#region Kb
if ((fileSize / 1024) > 0)
{
return String.Format("{0} Kb", (double)Math.Round((double)(fileSize / 1024), 2));
}
#endregion

#region Bytes
return String.Format("{0} b", fileSize);
#endregion
}
#endregion

#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;

void OnPropertyCanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion

#region IDisposable Members
public void Dispose()
{
if (this.webClient != null)
{
this.webClient.CancelAsync();

this.webClient.Dispose();
}
}
#endregion
}



How to use this class.

First we’ll create a window and set it’s properties
like the sample below.

I chose a grid layout for this tutorial but you could
use canvas if you like.



xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006”
Title="My Downloader"
Height="480" Width="640"
mc:Ignorable="d"
xmlns:src="clr-namespace:MyDownloader" WindowStartupLocation="CenterScreen">






Now we can add our ListBox. You have to give it a name
using the x:Name property. Now let’s add a ItemTemplate with a DataTemplate
and add our databound fields in a layout. The binding source of these fields are
the properties of our DownloadTask object. I chose to use StackPanel orientation
vertical to have a first row containing the title and the the second row tobe an
other StackPanel with it’s orientation set to horizontal. The second StackPanel
content is in this case a Textbox showing the text for an Cancel Button and a Textbox showing our download progress.