Archive for September, 2009

For several days, I looked for hooks into the Windows Presentation Foundation (WPF) System.Windows.Controls.TextBox control to display shadowed text:

image

WPF does not make the shadow effect easy. Changing the Foreground property can change the text color to any brush (even to a video or an image brush), but it does not achieve the shadow effect.

Using Snoop, I looked at the visual composition of a TextBox:

image

The TextBoxView control shown in Snoop does not have a public API. Lester Lobo says in this WPF forum post, “It is an internal lightweight class handling the rendering of content in the textbox.” If you re-templated a TextBox to try to replace the TextBoxView, you would have to implement all of the text-management functions provided by a TextBox—no small undertaking.

I then found a blog entry by Ken Johnson on CodeProject.com describing a clever hack for tricking a TextBox into doing what I wanted. His article is called CodeBox 2, and it uses the following algorithm:

  1. Derive a class from TextBox.
  2. Set the Foreground property to Brushes.Transparent. This hides the TextBox’s built-in text rendering.
  3. Override OnRender().
  4. Create a FormattedText object with the TextBox.Text string and set some properties to make it align and wrap just like the TextBoxView would normally do.
  5. Call DrawingContext.DrawText() to output the FormattedText such that it draws in the exact same position as the TextBoxView would normally do. To achieve the shadowed text effect, first use DrawingContext.DrawText() to draw the shadow offset a couple of units from the primary text, and then call it again to draw the primary text.
  6. What you now see is your shadowed text in a fully-functional TextBox. If you highlight text, you are actually interacting with the transparent text that is drawn in the TextBox.

image

It may be a hack, but it’s a clever hack, and aside from some scrolling and margin adjustments described well in Ken Johnson’s article and code, you can implement it relatively painlessly.

WPF’s in-depth text formatting features pay a price in CPU performance. This custom TextBox pays an even bigger price. First, drawing transparent text likely goes through all the formatting and rendering steps for no obvious visual effect. The text formatting likely provides some hit-testing and/or control sizing (layout) features even when formatting invisible text. Second, in addition to drawing transparent text, constructing a new FormattedText object and calling DrawingContext.DrawText() twice (once for the shadow and once for the primary text) adds overhead.

While I have not tested performance of my shadowed-text custom TextBox as shown in the screenshots above, I imagine that it will perform well enough on mid-level computers even with large amounts of text. However, if your application used these shadowed text effects in many places, the accumulation of CPU overhead would likely add up to significant performance problems.

I would appreciate any comments about other ways to draw shadowed text in a TextBox or to gain hooks into TextBox’s inner workings.

Good luck!

Dale

Technorati Tags: ,,
Advertisements

Some developers pride themselves on writing flexible classes, making as few assumptions as possible about how other objects would use them. This sounds good, but is there such a thing as excessive flexibility?

“Excessive flexibility” implies that a class fails to do enough decision-making or encapsulation on its own, so you have to do it in the calling code. In an effort to provide a flexible class, a developer unintentionally provides a class that requires more work to use and allows less room for performance optimization.

I encounter business classes similar to the one below. This class opens a text file and provides a List<string> where each item in the list represents one line of text in the file.

public class SomeDataClass: IDisposable
{
string path;
StreamReader streamReader;

public SomeDataClass()
{
}

public SomeDataClass(string path)
{
Initialize(path);
}

public void Initialize(string path)
{
this.path = path;
}

public void Open()
{
if (streamReader != null)
streamReader.Close();

streamReader = new StreamReader(path);
}

public void Close()
{
streamReader.Close();
streamReader = null;
}

public void Dispose()
{
if (streamReader != null)
{
streamReader.Close();
streamReader = null;
}
}

public List<string> GetLineData()
{
var newList = new List<string>();

while (!streamReader.EndOfStream)
{
newList.Add(streamReader.ReadLine());
}

return newList;
}
}

The public API includes a constructor, Initialize, Open, Close, Dispose, and GetLineData. This class provides some flexibility features:

  1. You can create one using a convenient parameterless constructor.
  2. You can use the constructor overload that automatically initializes the object.
  3. You can open the file only when ready to read it.
  4. You can close and then reopen the file without creating a new class instance.
  5. You can call Dispose instead of Close if you prefer.

I would have some questions for a developer who wrote that code:

  1. Will you provide documentation on best practices when using this flexible class?
  2. Why does it need the complexity of two constructors instead of one?
  3. Does the object provide a useful service when constructed by not initialized?
  4. Does the object provide a useful service when initialized, but not opened?
  5. If Close and Dispose do the same thing, why have both?
  6. Could dispose-and-new-again replace the close-and-then-reopen functionality?

I simplified this class:

public class SomeDataClassSimplified
{
string path;

public SomeDataClassSimplified(string path)
{
this.path = path;
}

public List<string> GetLineData()
{
var newList = new List<string>();

var streamReader = new StreamReader(path);

while (!streamReader.EndOfStream)
{
newList.Add(streamReader.ReadLine());
}

streamReader.Close();

return newList;
}
}

This version provides the same functionality with only two methods. Does this class require documentation? Notice that instead of creating the StreamReader in the constructor, Initialize, or Open, it creates it only as needed. This optimization results from having more implementation flexibility inside the class whereas the first version provided more flexibility outside the class.

While I contrived this over-simplified example, the same concepts apply to large business objects.

I worked with an architect who likes the term “appropriate.” In the case of business-object flexibility, I am looking for “appropriate flexibility” in the public interface, which in most cases means, “as simple as appropriate.”

I offer a few guidelines when writing business objects:

  1. Avoid overloading constructors. A constructor should require those parameters it needs in order to reach a usable state to avoid the need for Initialize types of public methods.
  2. Avoid providing developers convenience methods that add no real value. In the first example above, just as in the StreamReader class written by Microsoft, the class provides two ways to do the same thing: Close and Dispose. A developer trying to use your class will need to read documentation to figure out how to use your class.
  3. Reduce the public interface to your class in order to provide more “wiggle room” inside the class for optimization.
  4. Write self-documenting public interfaces. The first example does not make clear the difference between the constructor, Initialize, and Open. The second example self-documents.
  5. Carefully consider the number of object states you allow. The first example has at least four valid states: (1) constructed, but not initialized, (2) constructed and initialized, but not opened, (3) constructed and initialized and opened, and (4) constructed and initialized, but closed/disposed. The second example has two states: (1) constructed and (2) disposed.
  6. Avoid exposing invalid object states. In the first example, constructing an object and then calling GetLineData would fail.  Similarly, closing the object and then calling GetLineData would fail. In complex applications, the more you expose opportunities to use classes incorrectly, the more often it will happen.
  7. If you do not immediately need a business object, do not construct it. Wait until you need it to consume the memory. Generally, dispose it as soon as you no longer need it to free resources. Write code that allows business object references to remain null most of the time.

Please add comments to refine these ideas for appropriate business-object flexibility or to show your support for these ideas.

Good luck,

Dale

Technorati Tags: ,

For ideas on when to implement IDisposable, see another of my blog posts, Implement IDisposable More.

If you decide that your class should implement IDisposable, you have done your part to be sure that resources can be cleaned up immediately with a call you your Dispose method.

However, what if another object fails to call your Dispose method? Your Dispose method will never run.

In most cases, if a Dispose method does not run, I consider it a coding error. To catch it, I developed this simple enforcement trick:

public class SomeDisposableClass: IDisposable
{
bool isDisposed;
StreamReader myStreamReader;

public SomeDisposableClass()
{
myStreamReader = new StreamReader(“c:\foo.txt”);
}

public void Dispose()
{
isDisposed = true;

if (myStreamReader != null)
myStreamReader.Dispose();
}

#if DEBUG
~SomeDisposableClass()
{
if (!isDisposed)
Console.WriteLine(“An instance of SomeDisposableClass was not disposed before garbage collection.”);
}
#endif
}

A private field called isDisposed defaults to false, but is set to true if the Dispose method runs. If the Dispose method does not run, a finalizer catches the coding error by outputting a message to the console window.

Objects with finalizers pay a garbage collection performance penalty. To avoid that penalty, the above code only compiles in the finalizer in debug mode.

Typically, you can spot these error messages in the console window when garbage collection runs at application shut-down. However, they can show up at any time if your object is garbage collected sooner.

If you really want to enforce that your Dispose methods get called, throw an exception instead of outputting a message to the console window.

Be careful what you do in your finalizer. The finalizer will run on a garbage-collection thread, so avoid the temptation to reference any non-thread-safe fields. While it would be nice to pop up a message box to report not-disposed errors, during application shutdown, you may or may not catch a glimpse of it before shutdown completes.

Please add comments to refine the use of IDisposable or to show support for this enforcement trick.

Enjoy,

Dale

Technorati Tags: ,

The following code snippet demonstrates the only way that I know of that the C# language specification treats IDisposable differently than any other interface:

// StreamReader implements IDisposable.
using (var myStreamReader = new StreamReader(“c:\foo.txt”))
{
Console.WriteLine(myStreamReader.ReadLine());
}

StreamReader implements the IDisposable interface and can therefore be used with the using C# keyword. At the end of the using block, the myStreamReader object’s Dispose() method will run automatically.

You may speculate that an object implementing IDisposable somehow pays a penalty, perhaps by demotion to a slower garbage-collection tier, but according to Microsoft, IDisposable works like any other interface in garbage collection.

Although C# does not penalize objects implementing IDisposable, the usual recommendation to let the common language runtime (CLR) garbage-collect them without worrying about calling Dispose methods makes good sense.

I read somewhere that you should implement IDisposable only if your class references unmanaged resources. I think implementing IDisposable can improve quality in several ways:

  1. For performance reasons, you wish to free a resource sooner rather than later. For example, while a managed database connection or file handle will close when the garbage collector collects the object, you may wish to free the resources as soon as your application no longer needs them.
  2. You need to call the Dispose method on sub-objects that you own.
  3. Ease verifying correctness when working with complex object relationships. If, in a Dispose method, you clear values for all or most of your fields, then if some other object is mistakenly continuing to reference a field on the disposed object, you will likely catch the error quickly as a null reference or out-of-range error. I have found this to be especially helpful when forgetting to unregister from an event. When the event handler runs, it usually references a cleared out field and breaks, thus alerting me to my coding error.
  4. And of course, if you have unmanaged resources, you should implement IDisposable to close/free/release them.
  5. ?
  6. ?
  7. ?

Do you use IDisposable for reasons not mentioned above? Please add comments to describe those cases.

Please add comments to help refine the use of IDisposable or to show your support of this post.

Thanks,

Dale

Technorati Tags: ,