Recently I wrapped up primary programming on a library for Windows Phone 7 development, which you can find here: http://btiphone.codeplex.com/ . I started it around 6 weeks ago as a quick way to learn Silverlight and replicate some controls that don't exist on WP7 (SaveFileDialog, OpenFileDialog, and FolderBrowserDialog). Quick it wasn’t, but I feel like I understand Silverlight now and I created some nice controls along the way so I’m happy with how it turned out, even if it took about 3-4 weeks longer than I was expecting.
The first version was a UserControl because that's what the tools include as a template and I didn't know any better. Then I learned all about how controls you intend to share with others or even use for anything other than a quick mockup should be put together.
The first useful bit of information I came across was Creating a New Control by Creating a ControlTemplate. This introduced me to the concept of the Parts and States model, though it didn’t really explain it in terms I understood. I ultimately found this set of articles by Karen Corby, a product manager at Microsoft, that explained how it all works. Essentially, parts are a declaration using of the different named controls and named layout elements your code is expecting to find (e.g. a TextBlock with an x:Name=”UserInput”, a Grid with x:Name=“LayoutRoot”) – these are the “parts” – and the different states of being that the control you are creating can be in (Pressed, Released, Disabled, … there are certain strongly recommended standards for naming and grouping these; have a look at the Control Styles and Templates of many of the standard Silverlight controls to learn what they are and how you only need to use what your control actually will use).
Armed with this knowledge, I completely rewrote the control as an ItemsControl, which inherits from Control and, along with ContentControl, is one of the two standard classes to derive from when writing a control. Along the way I learned about generic.xaml – a resource dictionary that you should use to define the default template for your control and which you should put in a folder called “Themes” that you should create in the base of your project (i.e. not within some other folder). All a “template” is, incidentally, is a bunch of XAML that lays out and positions the parts of your control relative to one another and defines the visual states and any Storyboard animations that accompany them within a VisualStateManager – this is explained well in the Control Authoring Overview, but when I first came across that page it looked like Greek (or worse, in my case, since I actually understand a bit of Attic Greek). I was smart enough to bookmark it though and coming back to it later it made a lot more sense. If you already know Silverlight, it’ll probably be much more accessible right from the get-go.
I also learned the foolishness of relying too much on a CTP. I had been working with the April CTP, and the way that TextBox behaved when disabled worked perfectly for me and looked good for my purposes (to represent file and directory items within the control). When the Beta came out this month and I upgraded… it still behaved mostly the way I wanted it to, but the look was different and no longer usable. At first I toyed around with trying to retemplate it, but I quickly came to my senses: I was trying to use TextBox for something that TextBox wasn’t really meant for. So I took a few days and created a BorderedTextBlock control, which is simply a TextBlock inside a Border. I then created keyed templates that included a transparent background brush so that it would register presses anywhere within the control rather than only on the TextBlock or along the border itself, and voila, the control I should’ve been using from the outset. I also reworked a control I had created to handle text overflow. The end result was the ScrollingTextBlock control, which tests the length of the string and if it’s larger than the space it has been allotted by the arrangement system, it uses some simple animation to ping-pong the text back and forth. Actually, the default behavior of the control just loop scrolls it like a news ticker; originally it had been a ping-pong only system but I realized that it’d be far more useful to expand its capabilities.
Which leads me to DependencyProperty objects. Whenever you right click on a control and choose “Properties” and you see things like Background, Foreground, MinHeight, etc., what you are seeing are dependency properties. Each control has their own, and yours need be no different. You’ll find that many are there automatically since they come along with the variant of Control that you’ve inherited from and everything that it inherits from, and even from some of the parts within your control. Sometimes you’ll have to do some internal connection using StaticResource to make sure that everything reflects changes properly – that part (for me, at least) is a trial-and-error process and will be for quite a bit of time. Rather than explain the nitty-gritty details of dependency properties here, I’ll direct you to Custom Dependency Properties, which explains and shows it at least as well as I can (you can also take a look at my project code using the CodePlex link at the top of this post). Just remember that you need to actually use the values that are set: simply adding a dependency property isn’t enough since all it does is set or change the value of a property – you need to subsequently do something with that value.
The last thing I’ll include here for now is one last important thing to know about Parts and States and controls in general. A key part of the Parts and States model is that the programming is divorceable from the design. A skilled programmer can write all the code to make the control work, and a skilled designer can develop a beautiful, intuitive, user-friendly interface. The designer isn’t limited only to what you include as parts (though states do require the code to recognize the underlying action that triggers the state and change states accordingly). Unfortunately, the designer isn’t compelled to include all of the parts you do list as mandatory or to properly connect things internally that need to be properly connected. As such, you need to runtime check if the named part actually exists and is of the type you are expecting (i.e. a TextBox and not a TextBlock or a ScrollViewer). If the parts all exist, great! If not, you are obligated to, as I’ve termed it, “fail silently”. It’s acceptable for the control to lose some or even all functionality. It’s not acceptable for the control to throw exceptions. This runs contrary to most programming you’ve learned in .NET. But controls get added to a XAML page often by dragging them into place, setting some properties, and moving on to the next part of the design. Later they might get retemplated in order to make them look prettier or fit within space confines or whatever. Nowhere in this process do any catch blocks get added nor is there such a thing as an easy way to add them since the layout process happens behind the scenes unless you decide to take a very complicated and time-consuming active role in it. So it’s very unacceptable for a control to ever cause an exception since it’s almost guaranteed to crash the app itself. Which is why I had to learn everything there is to know about how IsolatedStorage works on WP7 and why I ultimately ended up writing the FileOps class as a wrapper around IsolatedStorage in order to throw much more useful exceptions than “IsolatedStorageException: something bad happened somewhere” (paraphrasing, but only slightly). But FileOps is another topic for another time.