Assigning adorners with dynamically created controls
I was trying to add an adorner to the WPF TextBox to add a cue banner. I was creating the controls at runtime and I am relatively clueless with WPF. I wanted my text boxes to show a grayed out text prompt in the edit fields when they are empty. In HTML5, that would be the placeholder attribute. In Win32, it’s called a cue banner.
While Win32 (since XP) supports the cue banner for text input controls (single line input only, everybody else can go away), WPF has no built-in support for the cue banner. There are a few ways to add this behavior. I read one suggestion that described how to a style to the control, with triggers to handle the showing and hiding of the cue banner.
That looked a little messy to me and would be specific to a single type of control. I found an article by Jason Kemp that described how to implement a cue banner as an adorner to the control. He provides a service class with a static method to assign a cue banner object to a control. That object could be a string, an image, a form, it doesn’t matter. That method wraps up the code to assign an adorner to the destination object.
I was adding TextBox contols to a grid using the following code
var tb = new TextBox { Margin = new Thickness(8.0, 0.0, 4.0, 4.0) }; tb.SetValue(Grid.ColumnProperty, 1); tb.SetValue(Grid.RowProperty, i); grid.Children.Add(tb); CueBannerService.SetCueBanner(tb, SomeCueValue);
The call to SetCueBanner was throwing an exception trying to add the adorner to the control. To add an adorner in code you follow the following pattern:
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control); // code here for creating a new adorner layer.Add(newAdorner);
This code was blowing up because GetAdornerLayer was returning null. Because I was building up the controls dynamically, I missing the AdornerLayer that GetAdornerLayer is trying to get a hold of. The cue banner adorner needs an AdornerLayer to render the controls on to.
The solution was to create an instance of
var decorator = new AdornerDecorator(); var tb = new TextBox { Margin = new Thickness(8.0, 0.0, 4.0, 4.0) }; decorator.SetValue(Grid.ColumnProperty, 1); decorator.SetValue(Grid.RowProperty, i); decorator.Child = tb; grid.Children.Add(decorator); CueBannerService.SetCueBanner(tb, SomeCueValue);
Once I did that, everything fell into place.
Comments