C# – Excel, Image, PDF Report with RDLC in Master/Details and MultiColumn

There is maybe a complicated definition of the word “report” in IT application but for me it’s pretty simple. A report is just an interface between what the user gives and what he wants to see. He may have many data sources which build up a same designed form of report or he has one data source and would like to display it in different ways. Because of this loosing relation there are a lot of Report components for .NET framework to fulfill programmer’s entire requirement. Some popular commercial ones are Crystal Report, List & Label, ComponentOne Reports, etc. … which provide flexible ways so that a user (without having any programming skills) can customize his report as he desires. Of course, a commercial one is always expensive because of his numerous abilities. However in some cases we don’t need such complicated reporting tool but only a simple one to illustrate data as predefined template. In this case we can use the Report Viewer (accompanied with .NET Framework) to create many kinds of basic reports. I know there are a lot of tutorials about RDLC (Report Viewer) in Internet, for examples at this site http://gotreportviewer.com/ but I would like to write another one. First it’s for myself to look up when I need (because I don’t regularly use Report Viewer component, I use List & Label at work) and second it maybe will help some newbies who need very basic tutorial about Report Viewer. If you are professional, you can just skip this post.

This blog post will discuss 3 supported types of Report Viewer (.RDLC): PDF, Excel and Image. For each type of report, I will insert some custom modifications to keep it not so boring and to illustrate how we can customize the reports. Of course I can’t cover all kinds of customizing but I think these customizations will be most asked for. All reports are embedded report (that means you don’t see .rdlc file at output folder). Now let’s create a console project to play around with Report Viewer. A report contains always 2 parts: data and template. So I need some data sources to feed Report Viewer. I have prepared some classes as VOs (ValueObject) containing our data and I will display values of these objects on report file. PDF and Image report use same data source (Example 1 and 3), Excel uses another ones (Example 2). These classes can be found in attachment at the end of post.

I. PDF Report
1. Right click on project and choose “Add New Item” –> Report –> Give “ReportMetaDataMember.rdlc” as new item’s name.

Add new report item

2. A new report design window will be opened. In this window, I can define how my report looks like when printing but let’s choose a data source for it first. In “Report Data” window click on New -> DataSet.

New DataSet

Enter MetaDataMember as Name, click on button new at Data source. On the new opened window, choose Object –> MetaDataDoc –> Click Finish. Go back to DataSet property choose MetaDataDoc (Members) in available datasets –> Click OK. At the end I have “Dataset Properties” as image below

Dataset Properties

3. Now I can feed my report with data. It’s the first part of what a report needs. The second part is the template. From Toolbox window, drag and drop a table on .rdlc designer then drag each property of VO to a column of table but Icon property. The icon property is an image property. However if I use Image type and I drag and drop this property whose type is Image on designer, I can’t show it on the report. The report will only show the name of property type. Therefore to show image on report, I need to convert it to byte array, and in the report template, I use Image control to parse this byte array.

public byte[] Icon 
{
	get 
	{
		Bitmap result = null;
		if (Name.StartsWith("T:"))
			result = Properties.Resources.Auto_icon;
		else if (Name.StartsWith("M:"))
			result = Properties.Resources.EVE_icon;
		else if (Name.StartsWith("F:"))
			result = Properties.Resources.M_O_icon;
		else if (Name.StartsWith("P:"))
			result = Properties.Resources.Wall_E_icon;
		else
			result = Properties.Resources.Wall_E_icon;

		MemoryStream ms = new MemoryStream();
		result.Save(ms, ImageFormat.Png);
		ms.Flush();
		return ms.ToArray();
	}
}

Image Properties

4. If I don’t use Image in data source but would like to link it from external source like file on disk. I can add a parameter to “Report Data” and pass the link of image to this parameter. In Windows “Report Data”, right click on “Parameters” folder –> Add Parameter

Report Data Add Parameter

On “Report Parameter Properties”, set name as “LogoPath” and data type as “Text”

Report Parameter Properties

Go back to design window, insert an image control and set its properties like image below

Image Properties

As you can see, the image source is set as External and its expression is linked to the parameter added before “= “file:\\\” & Parameters!LogoPath.Value“. Now I have a simple template to display my data. I can customize my report more likely
– Add Page Footer and Page number (from Built-in Fields at Report Data)

Page Footer

– Add Sorting, for example according to Name and Summary

Row Groups Details

Group Properties Sorting

– Repeat table header on new page

Advanced Mode

Repeat on new page

Play around with report designer as long as you want until the template satisfies you. After finishing with template, I need to write code to use this template for printing to pdf file.

internal class PDFReportGenerator
{
	private IEnumerable<MetaDataMember> members;

	public PDFReportGenerator(IEnumerable<MetaDataMember> members)
	{
		this.members = members;
	}

	public byte[] CreateReport()
	{
		byte[] result = null;

		using (LocalReport localReport = new LocalReport())
		{
			localReport.ReportEmbeddedResource = "RDLC_as_embedded_resource.ReportMetaDataMember.rdlc";
			localReport.DataSources.Add(new ReportDataSource("MetaDataMember", members));

			localReport.EnableExternalImages = true;
			ReportParameter logoParam = new ReportParameter("LogoPath");
			logoParam.Values.Add(Path.Combine(Application.StartupPath, "Logo.png"));
			localReport.SetParameters(new ReportParameter[] { logoParam });

			string mimeType, encoding, fileNameExtension, deviceInfo;
			string[] streams;
			Warning[] warnings;
			deviceInfo =
				"<DeviceInfo>" +
				"	<SimplePageHeaders>True</SimplePageHeaders>" +
				"</DeviceInfo>";

			result = localReport.Render(
				"PDF",
				deviceInfo,
				out mimeType,
				out encoding,
				out fileNameExtension,
				out streams,
				out warnings);

			if (warnings.Length > 0)
				foreach (Warning warning in warnings)
					Console.WriteLine(warning.Message);
		}
		return result;
	}
}

The code is pretty simple. It’s just a bridge between data and template. I just have to initialize a LocalReport, set its ReportEmbeddedResource to report template and add data to data source. The value of parameter must be added before calling printing function (I have inserted a parameter LogoPath in template before). I intend to use external image as logo therefore the EnableExternalImages must be set to true. Call the generator with data and I will have a PDF report as output

string fileName = "Read Metadata from Assembly.XML";
XDocument doc = XDocument.Load(fileName);
MetaDataDoc metadataDoc = (from d in doc.Descendants("doc")
						   select new MetaDataDoc(d)).FirstOrDefault();

IEnumerable<MetaDataMember> members = metadataDoc.Members;
PDFReportGenerator reportGenerator = new PDFReportGenerator(members);
byte[] pdf = reportGenerator.CreateReport();
File.WriteAllBytes("test.pdf", pdf);
Process.Start("test.pdf");

PDF Report Output

II. Excel report
In this second part, I will discuss about Excel report with Master/Details relationship. It’s pretty simple to create this kind of report. The simply implemented master/details need at least 2 (or more) report templates. One is for master and the other is for details. The detail template will be referenced in master template through inserting a Subreport component in master and set its properties to detail one

Subreport properties

Master report

The most important thing at master/details view is the relationship of parameters. Which property of master object will be used to categorize the children? In this example, the property “EmployeeID” of detailed part will be linked with ID of parent object

Subreport Properties Parameters

The code will be fast as same as the one for PDF report. However I must handle the Event SubreportProcessing to give the correct sub data for each parent object

internal class ExcelReportGenerator
{
	private IEnumerable<Employee> members;

	public ExcelReportGenerator(IEnumerable<Employee> members)
	{
		this.members = members;
	}

	public byte[] CreateReport()
	{
		byte[] result = null;

		using (LocalReport localReport = new LocalReport())
		{
			localReport.ReportEmbeddedResource = "RDLC_as_embedded_resource.ReportMetaDataMemberMaster.rdlc";
			localReport.DataSources.Add(new ReportDataSource("Employee", members));

			localReport.SubreportProcessing += new SubreportProcessingEventHandler(localReport_SubreportProcessing);

			string mimeType, encoding, fileNameExtension, deviceInfo;
			string[] streams;
			Warning[] warnings;
			deviceInfo =
				"<DeviceInfo>" +
				"	<SimplePageHeaders>True</SimplePageHeaders>" +
				"</DeviceInfo>";

			result = localReport.Render(
				"Excel",
				deviceInfo,
				out mimeType,
				out encoding,
				out fileNameExtension,
				out streams,
				out warnings);

			if (warnings.Length > 0)
				foreach (Warning warning in warnings)
					Console.WriteLine(warning.Message);
		}
		return result;
	}

	private void localReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
	{
		string employeeId = e.Parameters["EmployeeID"].Values[0];
		string dataSourceName = e.DataSourceNames[0];
		IEnumerable<Phone> phones = members.Where(x => x.ID.ToString() == employeeId).Select(x => x.Phone).FirstOrDefault();
		e.DataSources.Add(new ReportDataSource(dataSourceName, phones));
	}
}

The master/details in Excel report displays the data like image below

Excel Report

III. Image report with multi-column
Now we reach the last supported type of Report Viewer. There’s nothing much to say with this kind of report. Instead of PDF exporting, I’ll export data in .tiff format. Of course I still need a template to define how the data looks like at the end. The report page of image example will be split into 2 columns. To do this, just right click out of the body template, choose “Report Properties” and Column property tells how many columns will be used in each report page. The code for image report is exactly same as in PDF report. I just replace the export type from “PDF” to “Image”.

Report Properties

Column Property

I have discussed all of 3 kinds of Report Viewer. It plays no role which kind of report, it’s just output format. What is more important is the customizing which you can only learn through more practicing and complete source code can be downloaded as “RDLC as embedded resource

C# – MemoryCache for Object, File and SQL Server – Part 1/2

Caching is always a must-to-have feature in all applications, especially in database application. For example, I have to load a nested set of about 10.000 items from database, build up their path then load them to control (combo box, list box, etc…). The query to get 10.000 entries from SQL Server takes not so long to finish but let’s think about the case that we have many users working at the same time, server will be almost overloaded for processing this query parallel. The CPU and RAM usage go quickly up and SQL Server will react slower than normal. Of course, if you own a good server, there are maybe small problem but for a small server it’ll be a big problem and there is no benefit that the program make a same query for many times without any changes from database. In addition to server performance, the program performance should be also discussed. The result of this query will be mapped into objects. This mapping process takes even longer than executing query to finish. At the end, we have such long time action which we can avoid to call frequently through cache.

In this blog post, I would like to make some basic examples for introducing new kind of cache which is available from .Net Framework 4 : MemoryCache. The MemoryCache implements the abstract ObjectCache class and represents the type that implements an in-memory cache. For more details you can read at MSDN http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.aspx . I wouldn’t discuss deeply how this class works but rather make concrete examples with it.

1. Object cache
The typical use of cache is storing an object in “clipboard” and gives the object back when required with its all information. In this example, I’ll create an object with a long time running method. This method takes time at the first call, the object will be then stored in cache. I call this method again in second time and now it gives the result back immediately. The object looks like this

internal class LongTimeRunningObject
{
	public string Name { get; set; }

	public int Seconds { get; set; }

	public void LongTimeRunningMethod()
	{
		Seconds = new Random().Next(1, 2);
		Thread.Sleep(new TimeSpan(0, 0, Seconds));
	}
}

The long time running method will takes randomly one or two seconds to finish (yeah, the duration is not really long but it’s ok for an example). Now I create an object of this class and store it in MemoryCache.

private static long CacheSimpleObject()
{
	string keyName = "LongTimeRunningObject";
	LongTimeRunningObject longTimeRunningObject = new LongTimeRunningObject();
	Stopwatch stopWatch = Stopwatch.StartNew();

	MemoryCache cache = MemoryCache.Default;

	if (cache.Contains(keyName))
	{
		longTimeRunningObject = (LongTimeRunningObject)cache.Get(keyName);
	}
	else
	{
		longTimeRunningObject = new LongTimeRunningObject();
		longTimeRunningObject.Name = "CacheSimpleObject";
		longTimeRunningObject.LongTimeRunningMethod();
		CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
		cacheItemPolicy.AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddSeconds(2));
		cache.Add(keyName, longTimeRunningObject, cacheItemPolicy);
	}

	Console.WriteLine(string.Format("Example: {0} - Long time running methods took {1} to finish", longTimeRunningObject.Name, longTimeRunningObject.Seconds));
	stopWatch.Stop();
	return stopWatch.ElapsedMilliseconds;
}

With the CacheItemPolicy I can define how long the object should be still located in MemoryCache. In this case, MemoryCache will delete the object after 2 seconds. If the object was deleted from MemoryCache or not loaded into MemoryCache, I need to initialize it again. If it was there, then hold it back. Let’s test to see how the cache works.

Console.WriteLine("Cache simple object");
Console.WriteLine(string.Format("First time running took {0} miliseconds to finish ", CacheSimpleObject()));
Console.WriteLine(string.Format("Second time running took {0} miliseconds to finish ", CacheSimpleObject()));
Thread.Sleep(new TimeSpan(0, 0, new Random().Next(2, 4)));
Console.WriteLine(string.Format("Third time running took {0} miliseconds to finish ", CacheSimpleObject()));

At the first and third time, the object was first created or just deleted from memory cache. Therefore it takes time for the method to finish his job. At the second time, object was “cached” and therefore the method doesn’t have to run again. The cache gives his result simply back.

2. List of object cache
The object cache works perfectly. However, in real case, we often need to cache a list of object and create a missing one when it’s not in the list anymore. That mean we provide a cache of a list, when this cache is required with an available item, he returns this item back. If not, he should call the Callback function to create this item. This feature can be accomplished with help of a MemoryCacheWrapper

internal class MemoryCacheWrapper<T> where T : class
{
	private readonly Func<string, T> callBack;
	private readonly MemoryCache memoryCache;

	public MemoryCacheWrapper(Func<string, T> factory, TimeSpan timeout, string name)
	{
		memoryCache = MemoryCache.Default;
		callBack = factory;
		this.TimeOut = timeout;
		this.Name = name;
	}

	public virtual T this[string key]
	{
		get
		{
			T value = default(T);

			if (memoryCache.Contains(key))
				value = (T)memoryCache[key];
			else
			{
				value = callBack(key);
				CacheItemPolicy policy = new CacheItemPolicy();
				policy.AbsoluteExpiration = new DateTimeOffset(DateTime.Now.Add(this.TimeOut));
				memoryCache.Set(key, value, policy);
			}
			return value;
		}
	}

	public TimeSpan TimeOut { get; set; }

	public string Name { get; set; }

	public void Add(object objectToCache, string key)
	{
		memoryCache.Add(key, objectToCache, DateTime.Now.AddMilliseconds(TimeOut.Ticks));
	}

	public T Get(string key)
	{
		return this[key];
	}

	public void Clear(string key)
	{
		memoryCache.Remove(key);
	}

	public bool Exists(string key)
	{
		return memoryCache.Get(key) != null;
	}

	public List<string> GetAll()
	{
		return memoryCache.Select(keyValuePair => keyValuePair.Key).ToList();
	}
}

This wrapper contains as his member variable one callback function, one MemoryCache object and for example TimeOut property to clean cache after an interval. Moreover, he provides some basic functions to access MemoryCache as List-way to make the access easier. The code below demonstrates how this wrapper works

private static void RunCustomCacheWrapper()
{
	Func<string, LongTimeRunningObject> cacheMissCreateCallback = CacheMissCreateCallback;
	MemoryCacheWrapper<LongTimeRunningObject> wrapper = new MemoryCacheWrapper<LongTimeRunningObject>(cacheMissCreateCallback, new TimeSpan(0, 0, 10), "CacheListOfLongTimeRunningObject");
	wrapper.Add(new LongTimeRunningObject() { Name = "One", Seconds = rnd.Next(2, 4) }, "One");
	wrapper.Add(new LongTimeRunningObject() { Name = "Two", Seconds = rnd.Next(2, 4) }, "Two");
	Console.WriteLine(string.Format("Name: {0}, Seconds: {1}", wrapper["Two"].Name, wrapper["Two"].Seconds));
	Console.WriteLine(string.Format("Name: {0}, Seconds: {1}", wrapper["Three"].Name, wrapper["Three"].Seconds));
	Console.WriteLine(string.Format("Name: {0}, Seconds: {1}", wrapper["Four"].Name, wrapper["Four"].Seconds));
}

private static LongTimeRunningObject CacheMissCreateCallback(string key)
{
	return new LongTimeRunningObject() { Name = "Name" + DateTime.Now.Ticks.ToString(), Seconds = rnd.Next(5, 10) };
}

The object with key “One” and “Two” were intentionally added to the list. It’s clearly accessible, but the object with key “Three” and “Four” was not in the cache. I have never added them before I access them from the cache. In this missing case, the Callback function will be called to add these missing one. If you access the key “Three” again, you’ll see that you got the same object created before

MemoryCacheWrapper

So now we reach the end of this part 1. The complete source code will be given at the end of Part 2. In part 2 we’ll discuss how we handle specific cases such as file cache and SQL Server cache.

C#, AForge.Net – Examples for average color and motion detection

As I was student at chair in Data and Signal processing of TUM (http://tum.de), I had a course of Computer Vision which discusses about the image processing and his uses in real application. Matlab is often used for calculating, evaluating the algorithms and displaying data on chart. However in .NET I would like to introduce another library which is also powerful for image processing. That’s AForge.NET (http://aforgenet.com).

AForge.NET is a C# framework designed for developers and researchers in the fields of Computer Vision and Artificial Intelligence – image processing, neural networks, genetic algorithms, machine learning, robotics, etc.

The reason that I come to AForge.NET, is my laziness. I often see films/video on my computer from my bed. And I am so lazy that when I go to sleep, I must crawl from the bed to the computer to turn it off. Therefore I have an idea to write a small program using my webcam to monitor the brightness in the room when I turn off the light, room turns black and my program will turn off the computer. That means I can turn off my computer just by turning off the light. Is it great isn’t it? ^_^. So let’s go to the homepage of AForge.Net and download its installer (http://aforge.googlecode.com/files/AForge.NET%20Framework-2.2.2.exe) to have full of examples, documentation, etc…

1. Create new MVVM Light WPF 4 Project (you can use any type of project you want. This is just the GUI).
2. Insert an image control, a combo box and a TextBlock as below

<Grid x:Name="LayoutRoot">
	<Grid.RowDefinitions>
		<RowDefinition Height="8*"></RowDefinition>
		<RowDefinition Height="1*"></RowDefinition>
		<RowDefinition Height="1*"></RowDefinition>
	</Grid.RowDefinitions>
	<Image x:Name="imgCamera" Grid.Row="0" Source="{Binding CameraFrame}"></Image>
	<ComboBox Grid.Row="1" Margin="5" ItemsSource="{Binding Cameras}" SelectedIndex="{Binding CameraIndex, Mode=TwoWay}">
		<i:Interaction.Triggers>
			<i:EventTrigger EventName="SelectionChanged">
				<cmd:EventToCommand Command="{Binding SelectionChangedCommand, Mode=OneWay}" PassEventArgsToCommand="True" />
			</i:EventTrigger>                
		</i:Interaction.Triggers>
	</ComboBox>
	<TextBlock x:Name="tbAverageColor" Text="{Binding AverageColor, StringFormat='Average Color: \{0\}'}" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center">            
		<TextBlock.Background>
			<SolidColorBrush Color="{Binding AverageColor}"></SolidColorBrush>
		</TextBlock.Background>
	</TextBlock>
</Grid>

3. It looks maybe a little involved for someone who doesn’t work with WPF MVVM. But just skipping it if you can’t understand, the code does nothing special than showing the control, binding them to data context and handling event SelectionChanged of combo box. So that’s all preparations for the GUI. Now go to MainViewModel() to build our logics When the program is started, I will load all available webcams into combo box so that I can select later which one I would like to use. This action can take long time so it should run asynchronously and I won’t block the GUI during its executing.

public MainViewModel()
{
	if (IsInDesignMode)
	{
		// Code runs in Blend --> create design time data.
	}
	else
	{
		// Code runs "for real"
		Task.Factory.StartNew(() => Initialize());		
	}
}

AForgeUtil aforgeUtil = null;
public List<string> Cameras {...}

private void Initialize()
{
	aforgeUtil = new AForgeUtil();
	Cameras = aforgeUtil.GetCameras();
}

In this initialization step, I create a Task asynchronously to initialize my custom AForgeUtil class and call GetCameras() function to get list of camera connected to the computer

private FilterInfoCollection filterInfoCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
public  List<string> GetCameras()
{            
	return filterInfoCollection.Cast<FilterInfo>().ToList().Select(x=>x.Name).ToList();
}

As you can see, with the help of FilterInfoCollection in AForge.NET, I can easily get list of cameras. When I start the program, the combo box will be filled with all webcams connected to my computer. To make code above work, of course, we need to add references to AForge.Video and AForge.Video.DirectShow.

AForge Get Cameras

4. In next step, I would like to handle the SelectionChanged event of combo box to start the selected webcam and streaming what it records on image control

private RelayCommand<SelectionChangedEventArgs> selectionChangedCommand;
public ICommand SelectionChangedCommand
{
	get
	{
		if (selectionChangedCommand == null)
			selectionChangedCommand = new RelayCommand<SelectionChangedEventArgs>(e => HandleSelectionChangedCommand(e));
		return selectionChangedCommand;
	}
}

private void HandleSelectionChangedCommand(SelectionChangedEventArgs e)
{
	Task.Factory.StartNew(() => aforgeUtil.StartCamera(CameraIndex));
}

private  VideoCaptureDevice videoCaptureDevice = null;
public  void StartCamera(int CameraIndex)
{
	videoCaptureDevice = new VideoCaptureDevice(filterInfoCollection[CameraIndex].MonikerString);
	videoCaptureDevice.NewFrame += new AForge.Video.NewFrameEventHandler(videoCaptureDevice_NewFrame);
	videoCaptureDevice.Start();
}

void videoCaptureDevice_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
{
	Messenger.Default.Send(new NotificationMessage<Bitmap>((Bitmap)eventArgs.Frame.Clone(), MessengerMessages.NewFrame));
}

public MainViewModel()
{
	...
		Task.Factory.StartNew(() => Initialize());
		Messenger.Default.Register<NotificationMessage<Bitmap>>(this, (message) => UpdateCameraFrame(message));
}

private void UpdateCameraFrame(NotificationMessage<Bitmap> message)
{
	if (message.Notification == MessengerMessages.NewFrame)
	{
		CameraFrame = message.Content.ToWpfBitmap();		          
	}   
}

public static class ExtensionMethod
{
	public static BitmapSource ToWpfBitmap(this Bitmap bitmap)
	{
		using (MemoryStream memorystream = new MemoryStream())
		{
			bitmap.Save(memorystream, ImageFormat.Bmp);
			memorystream.Position = 0;
			BitmapImage result = new BitmapImage();
			result.BeginInit();
			// According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
			// Force the bitmap to load right now so we can dispose the stream.
			result.CacheOption = BitmapCacheOption.OnLoad;
			result.StreamSource = memorystream;
			result.EndInit();
			result.Freeze();
			return result;
		}
	}

}

It’s really long, long code for a simply action. The SelectionChanged event will be handled by a new Task calling to StartCamera() function of AForgeUtil class with index of selected camera in the list. In StartCamera(), I create then an instance of VideoCaptureDevice of given camera, subscribe to its NewFrame event and turn the camera on. That means anytime when a new frame comes, function videoCaptureDevice_NewFrame(…) will be called and I send the current frame back to MainViewModel() to show it on image control. MainViewModel receives this message, converts current frame from Bitmap to WPFBitmap and “streams” it on image control by setting CurrentFrame property. ToWpfBitmap() is an extension method for converting Bitmap to WPFBitmap without causing memory leaking.

The code look maybe a little complicated but most of them relate to communicate within program. The part of AForge.Net is just some line of codes. But before starting the program, we must be sure that VideoCaptureDevice must be disposed correctly when program is closed to avoid memory leak.

public static void ClearMain()
{
	_main.CloseCamera();
	_main.Cleanup();
	_main = null;
}

public void CloseCamera()
{
	aforgeUtil.CloseCamera();
}

public void CloseCamera()
{
	if (videoCaptureDevice != null && videoCaptureDevice.IsRunning)
	{
		videoCaptureDevice.SignalToStop();
		videoCaptureDevice.WaitForStop();
		videoCaptureDevice = null;
	}
}

You can override the CleanUp in MainViewModel and dispose object too. It’s better than changing code in ViewModelLocator like me. Then now start program to see if it works

AForge Webcam to Image Control

5. The capturing must look smoothly and RAM should be about 40MB. If not, then check your code again to see if there is any position causing memory leak with any profiler. I have now current frame on my image control and what I still have to do now is calculating its average color to decide if the light in room is turned off.

private void UpdateCameraFrame(NotificationMessage<Bitmap> message)
{
	if (message.Notification == MessengerMessages.NewFrame)
	{
		CameraFrame = message.Content.ToWpfBitmap();
		aforgeUtil.GetAverageColor(message.Content);                
	}   
}

public void GetAverageColor(Bitmap bitmap)
{
	Color tempColor;
	long sumA = 0, sumR = 0, sumG = 0, sumB = 0;
	for (int i = 0; i < bitmap.Height; i++)
	{
		for (int j = 0; j < bitmap.Width; j++)
		{
			tempColor = bitmap.GetPixel(j, i);
			sumA += Convert.ToInt32(tempColor.A);
			sumR += Convert.ToInt32(tempColor.R);
			sumG += Convert.ToInt32(tempColor.G);
			sumB +=Convert.ToInt32(tempColor.B);
		}
	}

	System.Windows.Media.Color result = new System.Windows.Media.Color();
	result.A = (byte)(sumA / (bitmap.Height * bitmap.Width));
	result.R = (byte)(sumR / (bitmap.Height * bitmap.Width));
	result.G = (byte)(sumG / (bitmap.Height * bitmap.Width));
	result.B = (byte)(sumB / (bitmap.Height * bitmap.Width));
	Messenger.Default.Send(new NotificationMessage<System.Windows.Media.Color>(result, MessengerMessages.NewAverageColor));
}

bool alreadyStart = false;
const int BlackThreshhold = 47;
public MainViewModel()
{
	...
		Messenger.Default.Register<NotificationMessage<Bitmap>>(this, (message) => UpdateCameraFrame(message));
		Messenger.Default.Register<NotificationMessage<System.Windows.Media.Color>>(this, (message) =>
			{
				AverageColor = message.Content;
				if (AverageColor.R < BlackThreshhold && AverageColor.G < BlackThreshhold && AverageColor.B < BlackThreshhold && alreadyStart)
				{
					alreadyStart = false;
					Messenger.Default.Send(MessengerMessages.RunYourActionNow);
				}
				else if (AverageColor.R > BlackThreshhold && AverageColor.G > BlackThreshhold && AverageColor.B > BlackThreshhold)
				{
					alreadyStart = true;
				}
			});
	}
}

public MainWindow()
{
	InitializeComponent();
	Closing += (s, e) =>
		{                    
			ViewModelLocator.Cleanup();
		};

	Messenger.Default.Register<string>(this, (message) => HandleStringMessage(message));
}

private void HandleStringMessage(string message)
{
	if (message == MessengerMessages.RunYourActionNow)
	{
		MessageBox.Show("Run your action now");
	}
}

Again the code looks a little confusing but it is really simple. When current frame changed, we’ll calculate its average color through summing color values and dividing through its area. After that, send color back to MainViewModel to show it on TextBlock and check if color is nearly black. If yes, then show message box. Of course, you should replace the message box with your defined action, for example turn off computer and make it stand by.

6. We can extend the example with motion detection to decide if there is any motion in the room. If not, turn off the light or something like that or raise an alarm for instrusion.

IMotionDetector motionDetector = new TwoFramesDifferenceDetector();
BlobCountingObjectsProcessing motionProcessing = new BlobCountingObjectsProcessing();
MotionDetector detector = null;
const int MinObjectsSize = 30;
public AForgeUtil()
{
	motionProcessing.HighlightColor = System.Drawing.Color.Green;
	motionProcessing.MinObjectsHeight = MinObjectsSize;
	motionProcessing.MinObjectsWidth = MinObjectsSize;            
	detector = new MotionDetector(motionDetector, motionProcessing);
	
}

void videoCaptureDevice_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
{
	MotionDetection((Bitmap)eventArgs.Frame.Clone());
}

public void MotionDetection(Bitmap bitmap)
{
	if (detector.ProcessFrame(bitmap) > 0.02)
	{
		if (motionProcessing.ObjectsCount > 1)
		{
			Messenger.Default.Send(MessengerMessages.IntrusionAlarm);
		}                
	}           

	Messenger.Default.Send(new NotificationMessage<Bitmap>(bitmap, MessengerMessages.NewFrame));
}

AForge Motion Detection

There are still lot of interesting functionalities of AForge.Net which can be not all discussed at this small blog. I’ll write more about this library when I have time to make research with it. As usual, at the end, you’ll find the source code “AForge Webcam Average Color

C# – MVVMLight Toolkit Messages

MVVM pattern is the most popular pattern to developers in WPF environment and there are already many platforms/frameworks supporting this pattern out there in internet. For example, I use Caliburn Micro Framework at work and use MVVM Light Toolkit at home for my private studying. Each framework has its own advantages and disadvantages so just choose one which you like or it satisfies you at most. Today I would like to make a small post to describe how the Messenger of MVVM Light Toolkit (MLT) works so that we can make the communication within application without violating the MVVM pattern.

Continue reading C# – MVVMLight Toolkit Messages

C# – Microsoft Sync Framework for file and database

Data synchronization is always a top feature for any product in IT zone, especially if your product is a database-based product. It provides the customers more flexibilities and “freedom” when using the products. For example, the customer is on construction area, enter data through a concise mini mobile client, then sync them with the server and when they are back to the office, they have all data in the main software and finish the rest of work. That means at any time and anywhere they can access their updated data. However, it’s not easy at all to implement this dreaming feature in any application because of the complexity. However, if you’re using Microsoft products, you can easily build this feature in your apps thanks to Microsoft Sync Framework (MSF) http://msdn.microsoft.com/en-us/sync/bb736753. Maybe you’ll say that there is already the replication feature in MS SQL Server, why do you need to use MSF? Yes, it’s correct that MS SQL has already this feature but obviously, we can only sync between MS SQL Servers. How about if we want to sync with MS SQL Express? The answer can be MSF.

Continue reading C# – Microsoft Sync Framework for file and database

C#, Tools – PC Time to track on/off time of computer

One boring task at my company is that each employee should report how he allocates time for each project he was following. Of course it is a must-do-work for project managers or supporters because they have to monitor how much time they have worked for each project so that the accounting department can send billings to customers later. But it doesn’t mean to me because I am a programmer and I have only 2 or 3 projects to take care of. I am just swinging between 2 phases: developing and bug fixing. The Test-phase will be made by Testers. Sometimes I write some Unit-Tests too but it’s not my fulltime field. As you see, my tasks are pretty “simple” however I must make myself an hour booking report at the end of each month too.
So I try to keep this task as simple as it is. Each day I make a notice on my calendar about with which project I am on. For a small, short-time project, I notice its name and its duration. It works great. But for a long-time project then I really don’t know how long I was on because it takes weeks, months to accomplish. The problem is that I always forget to book hours after working day. Then I have an idea to approximate my PC on time as project time (Of course I have to subtract lunch pause and some minutes for short break) It won’t be exact as project time but I believe it will be nearly exact. With this solution I don’t need to remember to book hour after each day anymore.
To monitor PC on/off time there is also half-free software on Internet, you can easily find him with search engine. In free version, I can only see 3 weeks before today. If I want to see more, I must buy a PRO-version. I am a poor man so I don’t have money to buy one therefore I must write myself to help me making my hour booking report.
The tool is not very complicated because any action of computer was logged by operating system. The On/Off events are not exceptions. I just have to read them out from log files and show them in easy-read way. Every time when I turn the computer on/off, the actions will be logged in Windows Logs –> System which can be viewed through Event Viewer

Windows Logs System

As you can see in image above (in case that you can read German ^_^), Event-ID 12 is ID of ON-Event which was sent by Kernel when I turn the computer on. The OFF-Event has ID 13. So basing on this log, I can trace back when I switch my computer on/off, calculate the ON time, make some magical calculations and then I have my project time at last. I will show you how to read on/off time from this log with C#.

Dictionary<DateTime, List<TimeBlock>> PCOnOffTime = new Dictionary<DateTime, List<TimeBlock>>();
private void IntializeData()
{
	EventLog log = new EventLog();
	log.Log = "System";
	log.MachineName = ".";

	var entries = log.Entries.Cast<EventLogEntry>();
	var itemMin = from e in entries
				  group e by e.TimeGenerated.Date into g
				  select new CustomEntry { EntryDate = g.Key, TimeGenerated = g.Min(e => e.TimeGenerated), ActionType = "On" };
	var itemMax = from e in entries
				  group e by e.TimeGenerated.Date into g
				  select new CustomEntry { EntryDate = g.Key, TimeGenerated = g.Max(e => e.TimeGenerated), ActionType = "Off" };
	var itemOn = from e in entries
				 where e.InstanceId == 12 && e.Source.Contains("Kernel-General") && e.EntryType == EventLogEntryType.Information
				 select new CustomEntry { EntryDate = e.TimeGenerated.Date, TimeGenerated = e.TimeGenerated, ActionType = "On" };
	var itemOff = from e in entries
				  where e.InstanceId == 13 && e.Source.Contains("Kernel-General") && e.EntryType == EventLogEntryType.Information
				  select new CustomEntry { EntryDate = e.TimeGenerated.Date, TimeGenerated = e.TimeGenerated, ActionType = "Off" };


	List<CustomEntry> temp = new List<CustomEntry>();
	itemMin.ToList().ForEach(x => temp.Add(x));
	itemMax.ToList().ForEach(x =>
	{
		if (temp.FirstOrDefault(y => y.TimeGenerated == x.TimeGenerated) == null)
			temp.Add(x);
	});
	itemOn.ToList().ForEach(x =>
	{
		if (temp.FirstOrDefault(y => y.TimeGenerated == x.TimeGenerated) == null)
			temp.Add(x);
	});
	itemOff.ToList().ForEach(x =>
	{
		if (temp.FirstOrDefault(y => y.TimeGenerated == x.TimeGenerated) == null)
			temp.Add(x);
	});

	List<CustomEntry> result = new List<CustomEntry>();
	string actionType = "";
	CustomEntry itemBefore = null;
	foreach (CustomEntry item in temp.Distinct().OrderBy(x => x.TimeGenerated))
	{
		if (actionType == item.ActionType)
			result.Remove(itemBefore);
		result.Add(item);
		actionType = item.ActionType;
		itemBefore = item;
	}

	for (int index = 0; index < result.Count; index++)
	{
		if (!PCOnOffTime.ContainsKey(result[index].EntryDate))
		{
			List<TimeBlock> listTimeBlocks = new List<TimeBlock>();
			PCOnOffTime.Add(result[index].EntryDate, listTimeBlocks);
		}

		PCOnOffTime[result[index].EntryDate].Add(new TimeBlock() { EntryStartTime = result[index].TimeGenerated, EntryEndTime = result[++index].TimeGenerated });
	}
}

The code is pretty simple. I just instantiate a variable whose type is EventLog, indicate it to work with the System-Log at my local machine (MachineName = “.”). Then I read the first entry of each day and set it as ON action and last entry of each day and set it as OFF action. Now I have a likely ON duration but it will be wrong if I turn on the computer in the morning at 8:00, turn it off at 8:15, go out, come back to company at 17:00, turn computer on again and turn it off at 17:15. Then the first entry is 8:00 and the last is 17:15 but the truth is I am not in company during this time. So I have to catch the ON/OFF events within day as in variables itemOn/itemOff.
After having all ON/OFF events, I just add them together, order ascending according to time, remove duplicated ones and then construct them in a Dictionary whose Key is date and Value is a list of time block marked with On and Off time. That means in one date, computer can be turned on/off many times. These on/off time will be parsed as EntryStartTime/EntryOffTime and saved in list.

class CustomEntry
{
	public DateTime EntryDate { get; set; }
	public DateTime TimeGenerated { get; set; }
	public string ActionType { get; set; }
}
class TimeBlock
{
	public DateTime EntryStartTime { get; set; }
	public DateTime EntryEndTime { get; set; }
}

At the end is the GUI. With some mock-up from WPF, I have beautiful PC Time software to show me when I turned on/off my computer.

PC Time On Loading

PC Time Load Finished

Maybe you’ll tell me that you can let the computer on and go out. My tool cannot detect that. Yes, it’s true but this tool is to help you to control yourself, not to cheat someone else. 😀
Binary: http://hintdesk.com/Web/Tool/PC%20Time.zip
Source code: https://bitbucket.org/hintdesk/dotnet-tools-pc-time-to-track-onoff-time-of-computer

C# – AutomationElement left click and send keys

As I discussed in last post about UI Automation of .Net Framework http://hintdesk.com/c-list-all-opened-tabs-of-firefox/ , I am trying to make a shortcut of multimedia keys to any flash player in web browser, for example YouTube. At the end of last blog I mentioned about AutomationElement.Current.NativeWindowHandle but it doesn’t really help. If I try to get the Handle of ControlType.Custom, I always become an IntPtr.Zero as result. Therefore I can’t use SendMessage or something equivalent to send message to flash player.

It turns to be difficult now. When SendMessage doesn’t work, so far as I know, I can’t send keys to it without bringing it to front or rather bringing it to active window. That’s really not what I want. I would like to control the flash player with multimedia keys silently. The Firefox window should be “behind” as it was when I control it. So until now I don’t have a perfect solution for my case. However I will show you how to make the flash player to active and send keys to it. If you find a better solution (which doesn’t have to make YouTube as active windown), then share me. YouTube and Firefox again are our candidates to test.

As Firefox users, we already know that we can open many tabs in browser’s window. Our YouTube Tab can be hidden by other tabs like image below

Firefox Tab

So if we want to make flash player as active control, we must set this Tab as active tab by getting it as AutomationElement and call Invoke() function to simulate left click on control. To find the Tab in UI tree quickly, we can use the Ctrl-Mouse-Click function of UI Spy. Click on the Mouse Icon on menu and then Ctrl-Click on YouTube Tab, we’ll land on the correct node of UI tree.

UI Spy Ctrl-Mouse-Click

I just edit and expand the code of the blog post before to set YouTube as active tab. Please read that post to understand more about the variables and their meanings.

...
AutomationElement firstCustomControl = GetNextCustomControl(rootElement, condCustomControl);

Condition condToolbarControl = new AndCondition(
	new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar),
	new PropertyCondition(AutomationElement.NameProperty, "Browser-Tabs"));

AutomationElement toolbarFF = firstCustomControl.FindFirst(TreeScope.Children, condToolbarControl);

Condition condTabItemControl =new AndCondition(
	new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TabItem),
	new PropertyCondition(AutomationElement.NameProperty, "YouTube - Broadcast Yourself"));

AutomationElement youTubeTab = toolbarFF.FindFirst(TreeScope.Descendants, condTabItemControl);
(youTubeTab.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern).Invoke();

UI Spy

As you can see, I use an AndCondition to limit the search result and use two types of wide range searching: one is Children (not recursive) and the other is Descendants (recursive). The tab YouTube can be then set as active by treating it as InvokePattern and calling its Invoke() function. To check if a control supports Invoke() function, on the window of UI Spy, right click on it, choose Control Patterns and see in the new window if there’s a Invoke function

UI Spy Control Patterns

UI Spy Invoke Function

After setting YouTube tab as active one, we move along the UI Tree again, localize the Flash Player control and set it active as following code (which bases on UI tree in UI Spy). Because I use a hardly-attached-way therefore if YouTube changes its structur we must rewrite our code. The loosely-attached-way was discussed in the blog before, you can read it again or invent one way yourself.

AutomationElement flashElement = docElement.FindAll(TreeScope.Children, condCustomControl)[1].
	FindAll(TreeScope.Children, condCustomControl)[2]
	.FindFirst(TreeScope.Children, condCustomControl)
	.FindFirst(TreeScope.Children, condCustomControl)
	.FindAll(TreeScope.Children, condCustomControl)[1]
	.FindFirst(TreeScope.Children, condCustomControl)
	.FindAll(TreeScope.Children, condCustomControl)[0];
(flashElement.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern).Invoke();

UI tree

Thanks to screen reader function of YouTube http://www.google.com/support/youtube/bin/answer.py?answer=189278, we can send key to this flash player to order what he should do. This SendKeys.SendWait() function requires an active window/control. Therefore we must “prepare” a lot of things before just to be sure that this function sends key correctly to desired control. In this example, I send key “0” to rewind the current playing video.

(flashElement.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern).Invoke();
Keys key = Keys.D0;
SendKeys.SendWait(key.ToString());

So I hope that after this blog post, you can automatically animate any control you want with UIAutomation. Remember to share me if you know how to send message to “hidden” tab in Firefox. I think there must be a solution that we can interact with the flash player without popping it up to be active. As usual, the complete source code can be download at the end “List all opened tabs of Firefox

C# – List all opened tabs of Firefox with UIAutomation

I use at home a keyboard with multimedia keys looks like this one of Logitech Media Keyboard K200 With One-touch Media and Internet Keys (920-002719). It’s very comfortable with this type of keyboard because when I am listening with Windows Media Player, I can start/stop/forward/backward without switching to Windows Media Player. Unfortunately these multimedia keys don’t work with Firefox. Let’s consider this case: I have multi tabs opened in Firefox. One of them is YouTube website and is playing a video. I am on chatting with my family on Yahoo Messenger and would like to make a voice chat. So I would like to pause the video of YouTube and resume it after chatting. However the multimedia key don’t work with Firefox (when Firefox is in background and YouTube is not active tab), I must browse to Firefox and then YouTube site to pause the video. It’s really uncomfortable. Therefore I would like make the multimedia keys work with Firefox so that I don’t have to jump between windows to stop the sound.

Continue reading C# – List all opened tabs of Firefox with UIAutomation

C# – Insert Images and Hyperlinks into RichTextBox in WPF

Like the name of control, the RichTextBox inheriting from TextBox control, allows user to enter and edit text. However it also provides more advanced formatting features than the standard TextBox, for example inserting rich media objects like images and hyperlinks. The objects (text, images, hyperlinks…) can be assigned directly to the control or can be loaded from a rich text format (RTF) file. We can also load data from an already opened data stream, edit and then save its content to RTF or ASCII file format. Moreover the RichTextBox control provides a number of properties we can use to apply formatting to any portion of text within the control as font, color, indent… In this small blog post I would like to demonstrate how I can parse Smiley code and Hyperlink in a RichTextBox.

After creating a normal WPF application, insert a TextBox and a RichTextBox as following XAML code

<Window x:Class="RichTextBox_Hyperlink.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:RichTextBox_Hyperlink"
	mc:Ignorable="d"
	Height="300"
	Width="300"
	Title="RichTextBox - Images and Hyperlinks"
	DataContext="{Binding Main, Source={StaticResource Locator}}">
...
<Grid x:Name="LayoutRoot">
	<Grid.RowDefinitions>
		<RowDefinition Height="5*"/>
		<RowDefinition Height="5*"/>
	</Grid.RowDefinitions>
	<TextBox x:Name="TextWithUrl" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True" Grid.Row="0" />
	<RichTextBox IsDocumentEnabled="True" Grid.Row="1" local:RTBNavigationService.Content="{Binding Text, ElementName=TextWithUrl}">
	</RichTextBox>
</Grid>

My demo contains simply a TextBox for inputting plain text and the RichTextBox control will parse this plain text as I want. Therefore I bound the Text property of RichTextBox to the Text property of TextBox control. As you know that the Text property of RichTextBox in WPF is “invisible”. To “hook up” its content, I have to write a class with a DependencyProperty bound to this Text property and handle its Changed event to modify the text as it should be displayed

public static class RTBNavigationService
{
	public static string GetContent(DependencyObject d)
	{ return d.GetValue(ContentProperty) as string; }

	public static void SetContent(DependencyObject d, string value)
	{ d.SetValue(ContentProperty, value); }

	private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
	{
	...
	}
}

Now I have “hooked” the Changed event of Text property, then I go to the first Parse function: Smiley Code Parsing.

1. Smiley Code Parsing
As every Internet citizen knows, each smiley is bound with a smiley code. For each application there is a other smiley code set. In this small demo I just want to handle only one smiley code : ). That means when I type in TextBox : ), then the RichTextBox should show a smile icon.

private static readonly Regex regexSmilies = new Regex(@"(:\)(?!\)))");
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
	RichTextBox richTextBox = d as RichTextBox;
	if (richTextBox == null)
		return;

	
	string content = (string)e.NewValue;
	if (string.IsNullOrEmpty(content))
		return;

	richTextBox.Document.Blocks.Clear();

	int lastPos = 0;
	Paragraph block = new Paragraph();
	foreach (Match match in regexSmilies.Matches(content))
	{                
		if (match.Index != lastPos)
			block.Inlines.Add(content.Substring(lastPos, match.Index - lastPos));

		BitmapImage bitmapSmiley = new BitmapImage(new Uri("giggle.gif", UriKind.Relative));
		Image smiley = new Image();
		smiley.Source = bitmapSmiley;
		smiley.Width = bitmapSmiley.Width;
		smiley.Height = bitmapSmiley.Height;
		block.Inlines.Add(smiley);
		
		lastPos = match.Index + match.Length;                     
	}
	if (lastPos < content.Length)
		block.Inlines.Add(content.Substring(lastPos));
	richTextBox.Document.Blocks.Add(block);
	
	...
}

The code is pretty simple. First I need to find where the smiley code was entered. Then I just split the Text into 3 parts: before smiley, smiley and after smiley. The parts – before and after smiley – will be inserted as Text through the AppendText function. The smiley code will be firstly replaced with an image and then inserted into the paragraph. Appending these parts with each other I have something like this

RichTextBox - Images and Hyperlinks

2. Hyperlink Parsing
In the Smiley parsing, I use a paragraph to store and then append the objects (text, image) with each other. You can use this technique to parse hyperlink too. However I would like to introduce another way with TextPointer to parse hyperlinks in RichTextBox text.
The idea is same as above, I need to find all hyperlinks with Regex and then replace the text with a real hyperlink one which one can click on it. But instead of adding directly hyperlink object into a paragraph, I will mark these hyperlinks with TextPointer and create a hyperlink object for it.

private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
	...
	List<Hyperlink> results = new List<Hyperlink>();
	foreach (Match match in regexUrl.Matches(content))
	{
		TextPointer p1 = richTextBox.ToTextPointer(match.Index);
		TextPointer p2 = richTextBox.ToTextPointer(match.Index + match.Length);
		if (p1 == null || p2 == null)
		{
			//Donothing
		}
		else
		{
			(new Hyperlink(p1, p2)).Click += OnUrlClick;
		}
	}
}

private static void OnUrlClick(object sender, RoutedEventArgs e)
{
	Process.Start((sender as Hyperlink).NavigateUri.AbsoluteUri);
}

As you can see that I convert the start and end offsets of hyperlink to TextPointer and create a object of hyperlink with these TextPointers inclusive adding a Click handler for it. The ToTextPointer () function walks through all components of RichTextBox and converts the start/end offset of hyperlink to TextPointer.

public static TextPointer ToTextPointer(this RichTextBox rtb, int index)
{
	int count = 0;
	TextPointer position = rtb.Document.ContentStart.GetNextContextPosition(LogicalDirection.Forward).GetNextContextPosition(LogicalDirection.Forward);
	while (position != null)
	{
		if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
		{
			string textRun = position.GetTextInRun(LogicalDirection.Forward);
			int length = textRun.Length;
			if (count + length > index)
			{
				return position.GetPositionAtOffset(index - count);
			}
			count += length;
		}
		position = position.GetNextContextPosition(LogicalDirection.Forward);
	}
	return null;
}

At the end we have a RichTextBox which can parse smiley code and hyperlink dynamically. It’s very comfortable when using MVVM with data binding

RichTextBox - Images and Hyperlinks

The demo source code can be downloaded here “RichTextBox – Images and Hyperlinks