Introduction
Visual Studio provides powerful databinding features which allow you, the developer, to attach controls to fields in a database table and have those fields update automatically when the current record in the table is changed. What makes it so powerful is that you can do this with little or no code. With common data types like strings or numbers, databinding is very simple, but it gets slightly more complicated when the data to be bound to is a custom type. This article addresses that special case, and discusses how to bind a control to ink stored in a database. Note that the ink I'm referring to is the ink from the Microsoft Tablet PC SDK.
This article will not discuss 2-way binding, allowing you to save in the database with bound controls, because that will be addressed in a future article. Due to unusual behavior in the InkPicture
control (from the Tablet PC SDK), the process of saving ink is significantly more complicated than the process of reading ink.
Background
This article assumes that you have knowledge of or experience using bound controls in Microsoft Visual Studio, and are able to use the IDE to manipulate Connections, DataAdapter
s, and DataSet
s.
Using the code
In order to use the code in this article, you will need to have the Microsoft Tablet PC SDK installed on your development machine. The Tablet PC SDK can be downloaded from Microsoft. It is not necessary to have a Tablet PC in order to use the SDK.
The Database and Serialized Ink
The Tablet PC SDK provides a method for serializing ink to an array of bytes. Ink can be serialized in the ISF format (normal or base 64 encoded) or GIF format (normal or base 64 encoded), but regardless of the format, it always comes out as an array of bytes. Because of this, it is ideal to use a field in the database that maps to an array of bytes. With Microsoft Access, this is the "OLE Object" type. The Memo
type will work as well, but it maps to a string, so an additional conversion is involved. This additional conversion can be a problem because it's difficult to find a character mapping which supports all of the values in the byte array. For example, many strings use the character with the numerical value of zero to signify the end of a string. If you create a string which contains a zero in it (which is very possible when the source is an array of bytes from ink), then you would end up with a string that doesn't represent the ink and cannot be converted back. Because of this, you will need to experiment with saving the ink in different formats and using different encoding formats for the string (look for System.Text.Encoding
in the online help for more information). When you have control over the database, it's much safer to just use a binary field type and save yourself the hassle.
It should go without saying, but the field in the database cannot be of a fixed size (unless that size is very large). You cannot anticipate how much data will be stored for any given ink object, so you need a field which can grow as needed.
Binding the Ink to the Control
The sample code binds the InkPicutre
control to an OLE Object field in an Access database. Normally, you would create a binding using the Properties window for your control, but since the Ink
property of the control is a special type, Visual Studio doesn't know how to map it. Therefore, the binding needs to be created manually in code using the DataBindings.Add()
method on the control. The first parameter is the name of the property to bind to, the second is the name of your DataSet
object, and the third parameter is the name of the field in the database. I prefer to specify the table name with the field name, but what you use in your own application will depend on how you build your DataSet
.
Binding binding = inkPicture1.DataBindings.Add ("Ink", myDS1, "MyTable.InkField");
Binding the control doesn't magically make your application understand what to do with the data it's receiving from the database. Remember, it's coming through as a byte array from the database and is being assigned to an Ink
-type property. The binding object has a Format
event which we can use to perform the conversion.
binding.Format += new ConvertEventHandler(binding_Format);
Variant datatypes don't exist in .NET but the event needs to be generic enough to support any obscure datatype we throw at it. Therefore, it uses an object
variable to hold the data to be converted. The event parameters tell us what type to convert the data to and, once complete, we assign that value back to the object
variable. The sample code below verifies the incoming and requested outgoing datatype before making the conversion. It uses the Load
method of the Ink
object to convert the byte
array to Ink
.
private void binding_Format(object sender, ConvertEventArgs e)
{
Ink ink = new Ink();
if (e.DesiredType.Equals(typeof(Ink)))
{
if (e.Value.GetType().Equals(typeof(byte[])))
{
byte[] bytes = (byte[])e.Value;
if (bytes.Length>0)
{
ink.Load(bytes);
}
}
e.Value = ink;
}
}
Other Considerations
For the code in this article to function properly, the InkPicture
control needs to have its InkEnabled
property set to false
. This is important because the Ink
property of the control cannot be modified in code while the InkEnabled
property is set to true
. The result is that you cannot edit the ink in the InkPicture
control using the pen. However, editing the ink with the pen and saving it to a database using bound controls is possible, and will be addressed in another article.