Tuesday 29 September 2009

Custom .Net Two Way Bindable Properties

Often when creating .Net applications that interface with a database a developer will want to create custom controls that bind in a similar way to database fields as the existing controls like TextBoxes and CheckBoxes.

Getting the control to update to reflect the database value is trivial. You can just add a DataBinding as you would with another control. But this will not propogate updates back to the data object. In order to do this you need to implement two way data binding on your custom control. Start by adding the attribute [Bindable(true)] to the property in question and then implement the INotifyPropertyChanged interface. An example of how to do this can be found on MSDN here: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx.

The catch comes if you ignore the advice to not implement INotifyPropertyChanged and provide an additional event for the property you want to bind to. If you call the additional event Property Name + "Changed" then the .Net framework binding code seems to get confused and the two way binding will not work. Eg if you property is called ClickCount and the additional event is called ClickCountChanged the two way binding will fail.

I was surprised by this as I have seen tutorials on two way data binding that encourage the provision of events for individual properties as well as the generic PropertyChanged event. Just remember that if you must do this be careful with the name of the events.

Here is a quick code sample that demonstrates how to create a control with two way binding:
  1 public class ButtonCounter : Button, INotifyPropertyChanged
2
{
3
private int clickCount;
4
5
public event PropertyChangedEventHandler PropertyChanged;
6
7
[Bindable(true)]
8
public int ClickCount
9
{
10
get
11
{
12
return clickCount;
13
}
14
set
15
{
16
if (value != clickCount)
17
{
18
clickCount = value;
19
Text = clickCount.ToString();
20
DoPropertyChanged("ClickCount");
21
}
22
}
23
}
24
25
public ButtonCounter()
26
{
27
Click += (o, e) =>
28
{
29
ClickCount += 1;
30
};
31
ClickCount = 0;
32
}
33
34
public void DoPropertyChanged(string propertyName)
35
{
36
if (PropertyChanged != null)
37
{
38
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
39
}
40
}
41
}

Monday 21 September 2009

Channel Swim Progress

I'm currently 4.5 km across the channel, just over an eighth of the way, now 30.9 km to go.

Monday 14 September 2009

Channel Swim under way!

I'm currently 1 km accross the channel, only 34.4 km to go!

Now all I need is to get some sponsorship...

Monday 3 August 2009

Creating Synchronous Web Requests in Silverlight

A word of warning out there to all you budding Silverlight developers. Don't try and do something like this...
private class RequestState
{
public HttpWebRequest Resquest { get; set; }
public HttpWebResponse Response { get; set; }
public ManualResetEvent ResetEvent { get; set; }
}

public static IEnumerable<string> GetFolders(string uri)
{
var requestState = new RequestState();
requestState.Resquest = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(new Uri(uri, UriKind.Absolute));
requestState.ResetEvent = new ManualResetEvent(false);
requestState.Resquest.BeginGetResponse(GetFoldersCallback, requestState);
requestState.ResetEvent.WaitOne();
return ProcessFolderResponse(requestState.Response);
}

private static void GetFoldersCallback(IAsyncResult result)
{
var requestState = (RequestState)result.AsyncState;
requestState.Response = (HttpWebResponse)requestState.Resquest.EndGetResponse(result);
requestState.ResetEvent.Set();
}
It won't work and will just hang. Even though the exact same code will work a treat in a non-Silverlight environment. I tried all sorts to get this to work and pulled my hair out for hours. The reason for this is that the Silverlight runtime gets upset if the main thread gets blocked. So you'll just have to stick with the asynchronous methods and callbacks.

The best I could do was to ask for my own callback and wrap that in a call to Dispatcher.BeginInvoke.
public static void GetFolders(Dispatcher dispatcher, string uri, Action<IEnumerable<string>> folderAction)
{
var request = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(new Uri(uri, UriKind.Absolute));
request.BeginGetResponse(ar =>
{
var response = request.EndGetResponse(ar);
var folders = ProcessFolderResponse(response);
dispatcher.BeginInvoke(() => folderAction(folders));
}, null);
}

Sunday 2 August 2009

Swim it With Dave

I've just signed up to swim the channel!!!

It's not as mad as it seems. I have to swim 22 miles over 12 weeks at my local swimming pool. For more information go to http://www.aspire.org.uk/channelswim.

Wednesday 29 July 2009

User Control Snap Lines in Visual Studio (Part 2)

The earlier code does indeed only work when the contained label is at position (0,0). Here is a fix that offsets the position of the snap lines based on the label position.
public override IList SnapLines
{
get
{
if (labelDesigner == null)
{
return base.SnapLines;
}
return labelDesigner.SnapLines.Cast<SnapLine>().Select(sn => BuildSnapLine(sn)).ToList();
}
}

private SnapLine BuildSnapLine(SnapLine source)
{
return new SnapLine(source.SnapLineType, BuildOffset(source.Offset, source.IsHorizontal),
source.Filter, source.Priority);
}

private int BuildOffset(int offset, bool isHorizontal)
{
return offset + (isHorizontal ? labelDesigner.Control.Top : labelDesigner.Control.Left);
}

User Control Snap Lines in Visual Studio

When creating a user control in .Net it is often hard to line your new control up with the other controls on the form. This is because the new control with use the default control designer to tell Visual Studio about its layout properties. Where I work we have a number of user controls in which are placed Labels or LinkLabels. It would be very handy if we could override the designer behaviour so that all alignment was done based on the properties of the label contained within the control.

To this end I created a new ControlDesigner that creates a local LabelDesigner and passes its snap lines out to Visual Studio. My experiment works if the label contained in the user control is at position (0,0). Some work changes might be necessary to make this work if the label has a different position.
using System;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace WrappedLabel
{
public interface ICustomSnapLineControl
{
Label GetSnapLineLabel();
}

public class CustomSnapLineControlDesigner : ControlDesigner
{
private ControlDesigner labelDesigner;

public override void Initialize(IComponent component)
{
base.Initialize(component);
if (component is ICustomSnapLineControl)
{
var designAssembly = Assembly.Load("System.Design");
var type = designAssembly.GetType("System.Windows.Forms.Design.LabelDesigner");
labelDesigner = (ControlDesigner)Activator.CreateInstance(type);
labelDesigner.Initialize(((ICustomSnapLineControl)component).GetSnapLineLabel());
}
}

public override IList SnapLines
{
get
{
return labelDesigner != null ? labelDesigner.SnapLines : base.SnapLines;
}
}

}
}
To implement this feature add the Designer attribute to your user control and implement the ICustomSnapLineControl interface. Here is a simple example.
using System.ComponentModel;
using System.Windows.Forms;

namespace WrappedLabel
{
[Designer(typeof(CustomSnapLineControlDesigner))]
public partial class NewLabel : UserControl, ICustomSnapLineControl
{
public NewLabel()
{
InitializeComponent();
}

public override string Text
{
get
{
return label1.Text;
}
set
{
label1.Text = value;
}
}

public Label GetSnapLineLabel()
{
return label1;
}
}
}

Tuesday 21 July 2009

Excel Interop Bug

There is a bug in the Excel Interop libraries (at least in version 2003, I'm not sure about other versions) that means that when an Excel Application object is no longer referenced it's process is not ended. The Excel process will remain running on the host PC even after the application that created the COM object has ended too.

This can be a particular issue in a web application where a page might process data in an uploaded Excel file or dynamically produce an Excel file for download. Every time that page is requested a new process is started and the old processes quickly bog down the PC.

To kill a process is easy enough, just use the Process.Kill() method. However in order to kill the correct process you have to be able to find it. Enter the GetWindowThreadProcessId(IntPtr, int) Windows function.

To demonstrate its use here is a quick sample application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace ConsoleApplication4
{
class Program
{
[DllImport("user32.dll")]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

static void Main(string[] args)
{
var app = new Microsoft.Office.Interop.Excel.Application();
if (app.Workbooks.Count == 0)
{
app.Workbooks.Add(Type.Missing);
}
var book = (Workbook)app.Workbooks[1];
var sheet = (Worksheet)book.Worksheets[1];
var cells = (Range)sheet.Cells[1, 1];
cells.Value2 = "Hello Dave";
book.SaveAs("C:\\Users\\David Brunger\\Desktop\\Test.xlsx", Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
cells = null;
book.Close(false, Type.Missing, Type.Missing);
book = null;
var appid = app.Hwnd;
app = null;
int excelProcessId;
GetWindowThreadProcessId(new IntPtr(appid), out excelProcessId);
var excelProcess = Process.GetProcessById(excelProcessId);
excelProcess.Kill();
}
}
}
It might be adding a few checks around the GetWindowThreadProcessId function call, so that you don't try and kill the wrong or a non-existent process.

Friday 10 July 2009

Window Mobile Accelerometer Apps

My wife has a HTC Diamond mobile phone and I recently stumbled across this article, http://blog.enterprisemobile.com/2008/07/using-htc-diamonds-sensor-sdk-from-managed-code/, so I thought I'd give it a go myself.

The first thing I want to try, after checking it works, is the ubiquitous spirit level application, then who knows what, an egg timer, one of those things that makes a silly cow noise when you turn it over. The sky's the limit.

Thursday 9 July 2009

Get the Current Revision Number from Subversion over HTTP

Using code similar to that in the last article I found I could get the current revision number just change the code to match the following two lines:
request.Method = "PROPFIND";
and
var requestbody = "<D:propfind xmlns:D=\"DAV:\"><D:allprop/></D:propfind>";
The current revision number is buried somewhere in the response.

Getting more information about each revision is pretty simple too.
var requestbody = "<S:log-report xmlns:S=\"svn:\"><S:start-revision>2</S:start-revision>" +
"<S:end-revision>2</S:end-revision><S:discover-changed-paths/></S:log-report>";

Wednesday 8 July 2009

Subversion Log Queries

I want to show off how good Subversion is as a source control system, and one of it's strengths is the vast amount of information you can get about the code that is in your repository. At a previous employer we had a very cool set of old style ASP pages that tied our source repository to our timesheet system to make some extremely useful reports. To make a start at trying to recreate these reports in subversion, I needed to be able to query the subversion logs.

Rather than relying on having the svn command line client installed, or using SharpSVN which I found a bit unsatisfactory for querying logs I thought I'd have a bash at writing a standalone HTTP client in .Net. After a bit of searching for some documentation, I came up with the following fairly simple code:
static void Main(string[] args)
{
var sandpiperUri = new Uri(@"https://sandpiper.svn.sourceforge.net/svnroot/sandpiper");
var request = WebRequest.Create(sandpiperUri + "/!svn/vcc/default");
request.Method = "REPORT";
request.ContentType = "text/xml";
var requestbody = "<S:log-report xmlns:S=\"svn:\"><S:start-revision>2</S:start-revision>" +
"<S:end-revision>2</S:end-revision></S:log-report>";
request.ContentLength = requestbody.Length;
using (var requestStream = request.GetRequestStream())
{
using (var writer = new StreamWriter(requestStream))
{
writer.Write(requestbody);
}
}
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
Console.WriteLine(reader.ReadToEnd());
}
}
}
Console.ReadLine();
}
This code queries the log of my rarely updated sourceforge project and produces the following results:
<?xml version="1.0" encoding="utf-8"?>
<S:log-report xmlns:S="svn:" xmlns:D="DAV:">
<S:log-item>
<D:version-name>2</D:version-name>
<D:comment>Initial Commit of Pre-Alpha Code</D:comment>
<D:creator-displayname>davebrunger</D:creator-displayname>
<S:date>2008-02-02T10:08:17.918376Z</S:date>
</S:log-item>
</S:log-report>
Now all I have to do is parse the ouput and create a nice interface to call the code. Oh yes, and implement some more of the subversion report features.

For information on the subversion http interface see this file: http://svn.collab.net/repos/svn/trunk/notes/webdav-protocol.

Thursday 2 July 2009

More Basic C++ Resources

I've also found this good introduction to C++ with exercises from the nice people at Imperial College, London. http://www.doc.ic.ac.uk/~wjk/C++Intro/CourseStructure.html

Tuesday 23 June 2009

Ray Tracer Progress

Here is the image file that I generated using the ray tracer that I have written in C#.

The ray tracer handles difuse colour of objects, lighting colours and uses the Blinn Phong method of calculating specular shading. It super-samples each pixel 16 times in order to produce a very graduated anti-alias effect.

Unfortunately at the moment the only objects that are supported in the scene are spheres. When I get some time I will add code for planes.

If my C++ skills improve I may try a C++ version to compare performance. If I manage it I'll let you know how I get on.

C++ Tutorials and Exercises

I think that it is good for all programmers to have an idea of programming with C and C++, mainly due to the number of platforms that you can write code for using them.

For reasons that are completely unknown to me I have decided to have another go at writing software for my Series 60 mobile phone. I'd rather write software for it in Java as it is much faster to code but some of the phone's feature just simply aren't available in Java applications.

For this reason I want to improve my extremely patchy knowledge of C and C++ so I decided to look on the internet for some tutorials and exercises to help me. I've found the following useful:

http://www.maths.manchester.ac.uk/~ahazel/C++_ex_win.pdf

http://www.cplusplus.com/doc/tutorial/

Wednesday 10 June 2009

Write a Ray Tracer - For Fun!

If you have a couple of hours to waste over a few evenings what better fun could you have than to have a go at writing a ray tracer? Actually I can think of several things that might be more fun, but if you want to give it a go I have found a nice tutorial here: http://www.codermind.com/articles/Raytracer-in-C++-Introduction-What-is-ray-tracing.html.

I'm working through it myself, although I'm writing c# code. I'll let you know how I get on.

Thursday 28 May 2009

Simulating the Earth Tones Palette in SQL Reporting Services

For a task at work I had to create a table in an SQL Server Reporting Services report that simulated the colours used in the Earth Tones colour palette. I tried to find some documentation that would tell me, and then tried to find out programmatically, finally after a bit of number crunching and research I worked it out manually.

So you don't have to - here is a handy SQL query that returns the colours in order
SELECT 1 As [Index], 'DarkOrange' AS Colour UNION
SELECT 2, 'DarkGoldenRod' UNION
SELECT 3, '#C04000' UNION -- Mahogany
SELECT 4, 'OliveDrab' UNION
SELECT 5, 'Peru' UNION
SELECT 6, '#C0C000' UNION -- DarkYellow
SELECT 7, 'ForestGreen' UNION
SELECT 8, 'Chocolate' UNION
SELECT 9, 'Olive' UNION
SELECT 10, 'LightSeaGreen' UNION
SELECT 11, 'SandyBrown' UNION
SELECT 12, '#00C000' UNION -- Dark Green
SELECT 13, 'DarkSeaGreen' UNION
SELECT 14, 'FireBrick' UNION
SELECT 15, 'SaddleBrown' UNION
SELECT 16, '#C04000' -- DarkRed

Friday 22 May 2009

Stop a Number Being Multiplied by 100 When the '%' Custom Number Format Character is Used

When formatting a number with the % character the value is multiplied by 100 before it is displayed. This is not always the desired behaviour as percentage values are often stored as integers. I.E 15% might be stored in a database as 15. The easiest way to get the character to work in this way is simply to place a single quote character ' before the percentage sign. A quick example follows.
static void Main(string[] args)
{
var value = 15;
Console.WriteLine("{0:#,##0.00%}", value);
Console.WriteLine("{0:#,##0.00'%}", value);
Console.ReadLine();
}

Gives the output:
1,500.00%
15.00%

Thursday 21 May 2009

Concatenate Values in a Column in a Child Table so they Appear as one Value when Joined to the Parent Table

Here is some code that might make your SQL Server reporting queries a bit simpler. The solution given here allows this task to be completed uning a join rather than involving variables and function calls.

First I'll set up some sample data
SET NOCOUNT ON

CREATE TABLE Family
(
FamilyID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
FamilyName VARCHAR(20) NOT NULL
)

CREATE TABLE FamilyMember
(
MemberID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
FamilyID INT NOT NULL REFERENCES Family (FamilyID),
MemberName VARCHAR(20) NOT NULL
)

DECLARE @LastID INT

INSERT INTO Family(FamilyName) VALUES ('Brunger')
SET @LastID = @@IDENTITY
INSERT INTO FamilyMember(FamilyID, MemberName) VALUES (@LastID, 'David')
INSERT INTO FamilyMember(FamilyID, MemberName) VALUES (@LastID, 'Katherine')
INSERT INTO FamilyMember(FamilyID, MemberName) VALUES (@LastID, 'Jack')

INSERT INTO Family(FamilyName) VALUES ('Bailey')
SET @LastID = @@IDENTITY
INSERT INTO FamilyMember(FamilyID, MemberName) VALUES (@LastID, 'Elizabeth')
INSERT INTO FamilyMember(FamilyID, MemberName) VALUES (@LastID, 'Chris')
INSERT INTO FamilyMember(FamilyID, MemberName) VALUES (@LastID, 'Lucy')
INSERT INTO FamilyMember(FamilyID, MemberName) VALUES (@LastID, 'James')
INSERT INTO FamilyMember(FamilyID, MemberName) VALUES (@LastID, 'Matty')

SELECT * FROM Family
SELECT * FROM FamilyMember

Now comes the clever query using the FOR XML clause to do the concatenation for us, but ensuring that no XML tags are inserted into the resultant string. The CROSS APPLY clause calls the enclosed "function" for each row in the result set.
SELECT
FamilyName,
-- 1 is subtracted from the length of Members rather
-- than 2 as the LEN() function ignores the training
-- whitespace
LEFT(Members, LEN(Members) - 1) AS Members
FROM
Family CROSS APPLY
(
SELECT
-- No column alias such as [text()] or "*"
-- is necessary here as the concatenation
-- operation means that the the column is
-- already nameless
MemberName + ', '
FROM
FamilyMember
WHERE
FamilyMember.FamilyID = Family.FamilyID
-- The empty string here means that now
-- tag is generated for each row
FOR XML PATH('')
) AS Data(Members)

Now tidy up.
DROP TABLE FamilyMember
DROP TABLE Family

This gives the following output:
FamilyID    FamilyName
----------- --------------------
1 Brunger
2 Bailey

MemberID FamilyID MemberName
----------- ----------- --------------------
1 1 David
2 1 Katherine
3 1 Jack
4 2 Elizabeth
5 2 Chris
6 2 Lucy
7 2 James
8 2 Matty

FamilyName Members
-------------------- --------------------------------------
Brunger David, Katherine, Jack
Bailey Elizabeth, Chris, Lucy, James, Matty

Wednesday 20 May 2009

URL Rewriting for Apache/PHP

Here's a good short introduction to URL rewriting in Apache, and how to make use of it in a PHP application.
http://www.phpaddiction.com/tags/category/url-routing/

Find the Row of a Control in a DataGrid in its Click Event

The Click Event in DataGrids has reared its ugly head again. A colleague of mine has spent hours searching the internet, and there doesn't seem to be a solution to this one. Using the control's ClientID or UniqueID properties will not work as the row does not have an id. The simple solution is use the control's NamingContainer property. If you go back through the hierarchy you can also get to the containing grid. To make life easier I wrote an extension method that also performs the cast to the desired container.
public static class ControlExtensions
{
public static TControl GetNamingContainer<TControl>(this Control control) where TControl : Control
{
var namingContainer = control.NamingContainer;
if (namingContainer == null)
{
return null;
}
if (namingContainer is TControl)
{
return (TControl)namingContainer;
}
return GetNamingContainer(namingContainer);
}
}

Get a control's row by calling the extension method.
protected void LatestCheckBox_CheckedChanged(object sender, EventArgs e)
{
var checkBox = (CheckBox)sender;
var row = checkBox.GetNamingContainer<DataGridItem>();

var control = row.FindControl("TestLabel");
if ((control != null) && (control is Label))
{
var label = (Label)control;
label.Text = checkBox.Checked ? "Checked" : "Not Checked";
}
}

Friday 8 May 2009

Generic Singleton

I've no idea why a generic singleton class is not available in the .Net framework, so I had to write my own. Here's the result.
public class Singleton<T> where T : Singleton<T>
{
private static object instanceLock = new object();
private static T value;

public static T Value { get { return GetValue(); } }

private static T GetValue()
{
lock (instanceLock)
{
if (value == null)
{
value = (T)Activator.CreateInstance(typeof(T), true);
}
}
return value;
}
}

To use it define a sealed subclass with a private constructor and away you go. For example
public sealed class ContextSingleton : Singleton<ContextSingleton>
{
private ContextSingleton() { }
}

If you have got a few extra minutes, it might be worth checking to ensure that the subclass is sealed and has no public constructors at runtime in the GetValue() method.

Thursday 7 May 2009

Building Expression Trees

Don't do what I did and implement an Expression Tree Visitor by hand. I was creating an application that used LINQ to SQL to connect to a database, but I wanted to customise the generated SQL queries. Use this very useful bit of code provided on the MSDN website:
http://msdn.microsoft.com/en-us/library/bb882521.aspx

Friday 1 May 2009

Listing All Controls on a Form and Flattening Hierarchies

For an application I'm developing it is necessary to enumerate my way through all the controls of a form. Using the Controls property only gives the immediate children, so it must be called recursively in order to retrieve all the controls. This started me thinking. Wouldn't it be easier to have a function that could flatten out any hierarchy so it could be easily enumerated?

At first I thought I'd just used IEnumerable.SelectMany, but again I fell foul of the deferred execution. Also some nasty jiggery-pokery would have been necessary to ensure I just didn't end up enumerating only the controls at the bottom of the hierarchy.

My solution is this simple method:
IEnumerable<T> Flatten<T>(T parent, Func<T, IEnumerable<T>> childFunction)
{
yield return parent;
foreach (T t in childFunction(parent))
{
foreach (T u in Flatten(t, childFunction))
{
yield return u;
}
}
}

To call this method so it pops up a message box containing the names of all the controls on a form (including the form itself) add a method to the form similar to this:
private void button1_Click(object sender, EventArgs e)
{
var controls = Flatten<Control>(this, c => c.Controls.Cast<Control>());
var builder = new StringBuilder();
controls.ForEach(c => builder.AppendLine(c.Name));
MessageBox.Show(builder.ToString());
}

For an explanation of the ForEach method see my previous post.

Perform an Action for each value in a Collection

I really don't know why there is no ForEach method in LINQ. If you want to perform an action on every item in a collection you have to use the TAccumulate Aggregate<TSource, TAccumulate>(TAccumulate, Func<TAccumulate, TSource TAccumulate>) method in order to avoid deferred execution, but calling it with a simple action leads to unnecessary ugly and hard to understand code.

To solve this issue just create a new extension method that wraps the call Aggregate call:

public static class IEnumerableExtensions
{
public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
{
source.Aggregate<TSource, object>(null, (dummy, item) => { action(item); return null; });
}
}

Thursday 30 April 2009

Ruby Quiz

I often find that I need a little task to keep my programming mind happy, but often have trouble coming up with problems to solve. I stumbled across Ruby Quiz a few weeks ago, and I have used the ideas there, but have been creating solutions in C# rather than Ruby.

Wednesday 29 April 2009

Server Side Click, CommandName and Command Argument on an ASP.Net TextBox

We had a requirement a work recently to provide support for a TextBox in an ASP.Net Web Forms application that posted back to the server when it was clicked on. We needed to support both the Click event style functionality and also the CommandName - CommandArgument functionality so that the TextBoxcould be included in a DataGrid and fire the DataGrid's ItemCommand event. It would not be necessary to read the value entered into the text box as it would be read only and be updated server side.

The solution I came up with was to try and find a control that supported this functionality and that the server would think was a control of that type, but would be rendered in the browser as a TextBox. The best matching control was LinkButton. LinkButton provided all the server side functionality that I required, and little more, so I decided that I would subclass that control. Now all I had to do was make ASP render the control as a text box.

Rather than do all the hard work myself, I thought that the easiest way to create the rendering code was to get the LinkButton to render itself to a dummy renderer, read in the string it rendered and then just change the bits I needed to. In order to get the control to look like a text box I have to change the element name to Input and add a new attribute type="text". In order to get the value of the LinkButton's Text property to display in the TextBox I had to add value="". Finally to make the events fire when the TextBox was clicked I had to assign the value of the href attribute to the TextBox's onclick attribute. In order to get the events to fire when the user tabbed into the control I also added it to the onfocus attribute.

In order to not render the data part of the LinkButton in the ASP.Net page I overrode the RenderContents method so that it did not do anything. Finally I removed all the re-useable code to another class so that I could use the technique again with other controls.

Here is the final code...
public abstract class CustomLinkButton : LinkButton
{
protected abstract string GetInputType();
protected abstract void WriteControlSpecificAttributes(HtmlTextWriter writer);

public override void RenderBeginTag(HtmlTextWriter writer)
{
var builder = new StringBuilder();
using (var dummyWriter = new HtmlTextWriter(new StringWriter(builder)))
{
base.RenderBeginTag(dummyWriter);
var attributes = GetAttributes(builder.ToString());
foreach (var kvp in attributes)
{
if (kvp.Key == "href")
{
writer.AddAttribute("onClick", kvp.Value);
WritePostBackAttributes(writer, kvp.Value);
}
else
{
writer.AddAttribute(kvp.Key, kvp.Value);
}
}
writer.AddAttribute("type", GetInputType());
writer.AddAttribute("readonly", "readonly");
WriteControlSpecificAttributes(writer);
writer.RenderBeginTag("input");
}
}

protected virtual void WritePostBackAttributes(HtmlTextWriter writer, string hrefValue)
{
// do nothing
}

protected override void RenderContents(HtmlTextWriter writer)
{
// don't write anything
}

private Dictionary GetAttributes(string tag)
{
using (var reader = new XmlTextReader(tag, XmlNodeType.Element, null))
{
var result = new Dictionary();
reader.Read();
if (reader.MoveToFirstAttribute())
{
do
{
result[reader.Name] = reader.Value;
}
while (reader.MoveToNextAttribute());
}
return result;
}
}
}

public class NewTextBox : CustomLinkButton
{
protected override string GetInputType()
{
return "text";
}

protected override void WriteControlSpecificAttributes(HtmlTextWriter writer)
{
writer.AddAttribute("value", Text);
}

protected override void WritePostBackAttributes(HtmlTextWriter writer, string hrefValue)
{
writer.AddAttribute("onfocus", hrefValue);
}
}

Thursday 23 April 2009

Including up to date Generated Code in a Rebuild

I recently came up against a problem where I had a pre-build event in a Visual Studio project that generated some code that was to be included when that project was built. While the code files were generated successfully the generated assembly always seemed to use the files generated for the previous build.

Most of the time this was not an issue as the generated code stayed the same, but if I changed one of the files that the generator used as a source, or the generator code itself I had to hit Rebuild twice in order for the new code to be picked up. It was as if Visual Studio was checking what files or versions of files were to be included before running the pre-build event. I tried some remedies suggested on the internet, but none of them worked; I always had to click Rebuild twice.

I reasoned that if a pre-build event did not work, perhaps I could use a post-build event on another project in the solution, and copy the results to the project I wanted them in. That way the code would be generated before Visual Studio got it’s mucking hands on it.

To reproduce this solution:
  • Imagine the project you want the generated code to be compiled into is called RealProject
  • Create another project in your solution. I chose a class library and called it CodeGenerator, but you could use any project type I guess.
  • Put your generating code in the post-build event of the CodeGenerator project. My code was similar to the following:
    CodeGeneratingApp.exe parameter1 parameter2 > “$(SolutionDir)RealProject\GeneratedCode.cs
  • Build CodeGenerator
  • Highlight RealProject in the Solution Explorer and Click the show all files button.
  • Include GeneratedCode.cs in Real Project
  • Right click RealProject in Solution Explorer and choose Project Dependencies
  • Check the CodeGenerator project.
Now whenever you rebuild RealProject, CodeGenerator should also be rebuilt and the newly generated code will be compiled into RealProject.

I used this procedure to generate a set of WCF Data and Service contracts based on some LinqToSql classes in a project that I am in the VERY EARLY stages of developing on sourceforge. I did not want to use the LinqToSql classes directly in the contract, so that the client did not need to reference the data layer’s assembly. Here is a link to the project page. http://sourceforge.net/projects/sandpiper

Wednesday 15 April 2009

Dice Roller using LINQ

For my day job I'm a software developer at a company that produces telephone billing software. It's something I've done in the past, and I was lucky to get another job working in the same field. Having read a few posts about how useful and easy to read LINQ can be, I decided to take a quick look at writing a dice rolling utility using link to perform common dice rolling tasks.

Here is the code...
using System;
using System.Collections.Generic;
using System.Linq;

namespace DiceUtils
{
public static class Dice
{
private readonly static Random random = new Random();

public static int Roll(int sides)
{
return random.Next(sides) + 1;
}

public static IEnumerable<int> Roll(int dice, int sides)
{
return Enumerable.Range(0, dice).Select(x => Roll(sides));
}

public static IEnumerable<int> Roll(int dice, int sides, int keep)
{
return Roll(dice, sides).AsQueryable().OrderByDescending(x => x).Take(keep);
}
}
}

Monday 13 April 2009

Pendragon Resources

While I'm on the subject here is a link to some handy Pendragon resouces:

http://homepages.nyu.edu/~dp58/pendragon.htm

Creating Pendragon Characters

I last played Pendragon about 10 years ago, but after I met one of my old gaming friends that I hadn't seen for three or four years, at the local supermarket I was desparate to dig out my old rulebooks and see if I could remember the rules.

I remember from my playing days that we had trouble with character creation. If we used the points method every single character have maximum SIZ and close to maximum STR and CON, so every character also had minimal DEX and APP. If you use the random method the characters were very much underpowered as rolling 14d6 + 6 gives an average of 55 points, compared to the 60 of the points method and quite often resulted in unplayable characters.

I remebered that I once saw a system based on playing cards for creating d20 characters and I had a quick play arround to see if that were adaptable to Pendragon. The following system creates characters that have a stat total of 60. Nobody will have to play a character with a SIZ less than 13 or another stat less than 9 if they don't want to. It should also mean that not every character in the group has exactly the same stats.

Method:
  1. Take the following cards from a standard deck of cards... 1 Ace (counts as 1), 3 2s, 4 3s, 3 4s, 2 5s and 3 6s.
  2. Set asside 2 of the 2s and this should leave you a deck of 14 cards, one card for each die rolled during a standard character creation.
  3. Shuffle the 14 cards together.
  4. Deal 2 cards to SIZ and three cards to each of the other stats in order.
  5. SIZ will equal the total of the two cards plus 6.
  6. The other stats will be equal to the total of the three cards dealt to them.
  7. Now the spare 2 2s are assigned to any stat as the player wishes, as long as no stat is increased above 18. they can be assigned to different stats or both to the same stat as desired.
  8. Cultural modifiers are now applied as normal and can take stats above 18 as usual.

Hello

Here is the first post of my new blog. I'm rather belatedly joining the blog community. I have decided to start a blog as I have had many ideas that I keep meaning to post on the internet, but have never gotten round to. My wife has also signed me up for FaceBook and I noticed that you can link to a blog from there. As FaceBook is not really the best place for posting RPG ideas and code snippets I thought that writting a blog would be better, and easier than maintaining my own website.