ailon's DevBlog: Development related stuff in my life

Writing WPF/Silverlight compatible code. Part 3: Brushes, Strokes and Dashes

11/12/2009 7:14:43 PM

Note for future readers: These series discuss WPF and Silverlight versions that are current stable versions at the time of this writing – WPF 3.5 and Silverlight 3.

These are ongoing series of posts on the subject of WPF/Silverlight compatibility. New posts will be added to the Table of Contents post as they are written so bookmark that post or just subscribe to my RSS feed.

In this part I’m going to outline my findings about the differences in one of the fundamental visual aspects of WPF/Silverlight user interface visual appearance – Brush and related issues.

No Brushes in Silverlight

That title sounds scary. It’s not that bad as it may look at the first glance though. I’m talking about Brushes class here (not Brush). Brushes class contains a set of predefined solid color brushes which are very handy shortcuts for specifying solid most common brushes in your code.

In pure WPF you are most probably accustomed to using something like this:

   1: border.Background = Brushes.White;

Well, I have bad news for you. You should forget about this, if you are going to write code that works in both WPF and Silverlight. Fortunately both WPF and Silverlight have Colors class which is almost the same as Brushes except rather than predefined brushes it contains predefined colors. So the above code could be universally rewritten this way:

   1: border.Background = new SolidColorBrush(Colors.White);

A little longer but still better than forking your code for such a small issue.

There’s no difference in setting named brushes in XAML though:

   1: <Border Name="border" Background="White">
   2: </Border>

This works in both.

No Clone() in Silverlight

Well, this is not directly related to brushes, but it’s were I got hit by the absence of object cloning in Silverlight. I’ve cloned my stroke brush in WPF, made it semi-transparent and used that brush as my fill brush. This worked with any type of brush with minimal effort on my side. Unfortunately that happened to be quite a difficult issue in Silverlight. Fortunately Justin Angel has posted a universal Clone() method to Silverlight forums. So, the day is saved once again.

Stroke vs. Pen

WPF has Pen class, Silverlight doesn’t. Pen is a nice class encapsulating all the aspects of a stroke in one reusable object. It’s used in WPF in GeometryDrawing (which is not in Silverlight, too, but I’ll leave it for another installment) and other places. Annoying thing is that it’s dropped in most places (like all the shapes) in favor of separate StrokeXxx properties. I even wrote an extension method and an attached property for the Shape derived classes to use Pen. I’ll share it in a separate post since it’s irrelevant in Silverlight anyway.

In Silverlight you don’t have Pen. Most likely it’s for a simple reason – you don’t have any of the classes that use it anyway. So, if you use Pen somewhere in your WPF code you can be sure that you’ll have to rewrite that code in some greater scope than just changing Pen to StrokeXxx.

No DashStyle in Silverlight

Since there’s no Pen, it’s only logical that there’s no DashStyle, too. You just use StrokeDashArray, StrokeDashCap and StrokeDashOffset instead.

kick it on DotNetKicks.com Shout it

Tags: , ,

Writing WPF/Silverlight compatible code. Part 2: Dependency Properties

11/5/2009 7:33:43 PM

Note for future readers: These series discuss WPF and Silverlight versions that are current stable versions at the time of this writing – WPF 3.5 and Silverlight 3.

These are ongoing series of posts on the subject of WPF/Silverlight compatibility. New posts will be added to the Table of Contents post as they are written so bookmark that post or just subscribe to my RSS feed.

DependencyProperty.Register

Dependency properties are one of the core concepts of both WPF and Silverlight. To register a dependency property you use DependencyProperty.Register() method. WPF 3.5 has 3 overloads for that method while Silverlight has only one.

   1: public static DependencyProperty Register(
   2:     string name,
   3:     Type propertyType,
   4:     Type ownerType,
   5:     PropertyMetadata typeMetadata
   6: )

Obviously if you want to create cross-platform code you should use that one method all the time in WPF.

No FrameworkPropertyMetadata

For most WPF framework-level application development purposes, FrameworkPropertyMetadata is the type used for dependency property metadata, rather than the base metadata types PropertyMetadata or UIPropertyMetadata.

from MSDN.

Unfortunately there’s no FrameworkPropertyMetadata class in Silverlight.

One of the most used improvements of FrameworkPropertyMetadata over PropertyMetadata are Boolean settings that control invalidation of various layout aspects of the object such as AffectsMeasure, AffectsArrange, etc. If some of these settings are set to true, corresponding aspect is invalidated when property value changes.

Fortunately it’s not too difficult to mimic this behavior using PropertyMetadata and PropertyChangedCallback. So if you would write something like this for WPF-only application:

   1: public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register(
   2:     "Something", typeof(string), typeof(Window1),
   3:     new FrameworkPropertyMetadata(string.Empty,
   4:         FrameworkPropertyMetadataOptions.AffectsMeasure)
   5:         );

you can rewrite it this way to work in both WPF and Silverlight:

   1: public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register(
   2:     "Something", typeof(string), typeof(Window1),
   3:     new PropertyMetadata(string.Empty,
   4:         new PropertyChangedCallback(Window1.SomethingProperty_Changed))
   5:         );
   6:  
   7: private static void SomethingProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
   8: {
   9:     ((FrameworkElement)d).InvalidateMeasure();
  10: }

To make life easier you can create a helper class with methods for invalidating various aspects and combinations and use methods from that class for properties that do nothing else on change.

No Value Coercion

WPF includes a powerful mechanism for value coercion.

Coerce value callbacks do pass the specific DependencyObject instance for properties, as do PropertyChangedCallback implementations that are invoked by the property system whenever the value of a dependency property changes. Using these two callbacks in combination, you can create a series of properties on elements where changes in one property will force a coercion or reevaluation of another property.

A typical scenario for using a linkage of dependency properties is when you have a user interface driven property where the element holds one property each for the minimum and maximum value, and a third property for the actual or current value. Here, if the maximum was adjusted in such a way that the current value exceeded the new maximum, you would want to coerce the current value to be no greater than the new maximum, and a similar relationship for minimum to current.

from MSDN.

Unfortunately there’s no such mechanism in Silverlight. You’ll have to deal with scenarios like the one above by just using a combination of PropertyChangedCallbacks and regular methods. In some complicated but not critical scenarios you might find it reasonable to use value coercion in WPF and just leave uncoerced values in Silverlight. You can do this by forking your code as described in part 1 of these series.

No OverrideMetadata()

WPF let’s you override property metadata. You can do this using OverrideMetadata method of DependencyProperty. In the code below we override the default width of our control inherited from Control class.

   1: static MyControl()
   2: {
   3:     FrameworkPropertyMetadata newMetadata = new FrameworkPropertyMetadata();
   4:     newMetadata.DefaultValue = 180.0;
   5:     Control.WidthProperty.OverrideMetadata(typeof(MyControl), newMetadata);
   6: }

Again, there’s no OverrideMetadata in Silverlight. When you just want to change the default value of some property you can simply set the value in your default public constructor.

   1: public MyControl()
   2: {
   3:     this.Width = 180;
   4: }

However I'm not sure if or how you can set new PropertyChangedCallback on inherited property in Silverlight. Let me know in the comments if you do.

No read-only dependency properties

One of the really nice features in WPF are read-only dependency properties. These are very useful when you need to have a property reflecting some internally determined state of your object (like IsMouseOver property) and don’t want other developers to be able to change the value from the outside. You can easily do this with regular .NET properties but what if you want a read-only property while still enjoying all the perks of dependency property mechanism?

Unfortunately I have no workaround for Silverlight. Sure, you can create a regular CLR property wrapper for dependency property having only the get accessor, but this wont stop anyone from setting the value using SetValue() method.

So you’ll have to make a choice here (unless you find some really working solution): either forget about read-only dependency properties altogether or use separate code (either using pre-processor directives or partial classes) for WPF and Silverlight. Just remember that you’ll have to use different code for not only your dependency property declarations, but SetValue calls, too (you use SetValue() on DependencyPropertyKey with read-only properties). So things can really get messy with constant #if SILVERLIGHT forking.

kick it on DotNetKicks.com Shout it

Tags: , ,

Writing WPF/Silverlight compatible code. Table of Contents

11/5/2009 5:25:00 PM

Note for future readers: These series discuss WPF and Silverlight versions that are current stable versions at the time of this writing – WPF 3.5 and Silverlight 3.

Table of Contents

These are ongoing series of posts on the subject of WPF/Silverlight compatibility. New posts will be added to this ToC as they are written so bookmark this post or just subscribe to my RSS feed.

  1. The Big Picture
  2. Dependency Properties
  3. Brushes, Strokes and Dashes
  4. Geometries
  5. Adding PropertyChangedCallback
  6. Adding XAML files as links
  7. Ghost Lines in Silverlight
  8. … more to come…

Tags: , ,

Writing WPF/Silverlight compatible code. Part 1: The Big Picture

11/2/2009 11:53:59 AM

Note for future readers: These series discuss WPF and Silverlight versions that are current stable versions at the time of this writing – WPF 3.5 and Silverlight 3.

These are ongoing series of posts on the subject of WPF/Silverlight compatibility. New posts will be added to the Table of Contents post as they are written so bookmark that post or just subsribe to my RSS feed.

Introduction

Silverlight is a subset of WPF. This is the short official version. In reality this might be close to true featurewise, but as developers of cross-platform (here and later in these series "platform" should mean WPF or Silverlight) solutions we care not only about what benefits some feature provides to the end user, but about it's API for us (developers) and quite often even about it's internal implementation.

This is how it looks to me at this point (please excuse my artistic “skills”):

wpf_silverlight_theory

At the time of this writing I'm in the middle of converting of one of our WPF charting controls to WPF/Silverlight  compatible code. It took me more than a week of intense work just to get it to compile and display something under Silverlight and I have yet a lot to do before everything works as expected. During this journey I've been writing down all the differences I encounter and I'm going to share my findings, solutions and workarounds in these series.

There's great high-level conceptual information on the differences in Silverlight documentation and Wintillect’s whitepaper, but it's often the details that cost (or save) you time and cause headaches.

The Approach

I assume that anyone reading this plans to develop a cross-platform application or control or at least wants to be prepared for the future. Such project could be approached in two ways:

  1. Develop cross-platform project right from the start
  2. Develop for one platform and adopt for the other later.

Develop cross-platform project right from the start
This is the way to go if your only concern is the quality of the end result. Unfortunately in real life quality is often offset by the other very important metric – time to market. It's hard to argue that one will see some real tangible results faster when concentrating all attention on one platform.

Develop for one platform and adopt for the other later
As was said above you will most likely go this route. Here you have another 2 options: start from WPF or Silverlight. There might be some business, religious or other reasons to start from one or the other, but we will set all of these aside and look only on technical aspects.

From my point of view life would be easier if you start with Silverlight and ease into WPF later. With that said it’s not necessary that easier equals better. You will most likely settle for lowest common denominator which could be minor problem in most cases but could mean a lot in some. I’ll post a separate part on one of the differences were taking a special route for WPF version could make a difference.

If you start with WPF, however, you will most likely end-up doing quite some rewriting when time comes to step into Silverlight, but you wont have to break what’s working just to get a better performing WPF version (considering there’s a reason to care about performance in your project).

The Toolset

There are several methods to facilitate code reuse between Silverlight and WPF versions of your poject while handling the differences at the same time. Personally I use linked code files for common parts and two methods to tame the differences – preprocessor directives (where just a small portion of code differs) and partial classes (for serious differences).

Linked code files in Visual Studio

To add a linked code file to your WPF or Silverlight project in Visual Studio you just go to Project –> “Add Existing Item…” navigate to your other (Silverlight or WPF) project directory, locate the file and use “Add As Link” item in the “Add” dropdown button.

vs_add_as_link

Preprocessor (compiler) directives

When you have a small amount of code differing between the versions you can use preprocessor directives to include one or the other chunk of code depending on the type of project being compiled. For your convinience Silverlight project template predefines “SILVERLIGHT” constant. You can define something shorter and more mistyping proof but I find it’s safer to go with this default (you never know when and how you are goint to reuse this code). Here’s how you go about this:

   1: private TextBox _dataTextBox;
   2: public override void OnApplyTemplate()
   3: {
   4: #if SILVERLIGHT
   5:     _dataTextBox = this.GetTemplateChild("PART_DataTextBox") as TextBox;
   6: #else
   7:     _dataTextBox = this.Template.FindName("PART_DataTextBox", this) as TextBox;
   8: #endif
   9: }

Partial classes

While preprocessor directives are ok for ducttaping small differences, the code becomes quite difficult to digest pretty quickly. Enter partial classes. You define common code in one partial class file and link that file from one project to the other. Then you create a separate partical class files for methods that differ in each project.

Here’s how a simplistic Visual Studio solution implementing this approach looks like:

vs_partial_classes

MyControlWPF is a WPF project. It contains MyControl.cs file which contains a partial MyControl class. That file contains code that is identical for both WPF and Silverlight. It is added to MyControlSL (our Silverlight project) as link. So there’s only one copy of this file to maintain and it’s used in both projects. MyControl.WPF.cs and MyControl.SL.cs on the other hand contain members of MyControl that differ in implementation between WPF and Silverlight.

What about XAML?

Unfortunatelly there’s no standard way to use preprocessor directives or something like that to isolate the differences. There are some custom hacks however. While this is definitely cool from enthusiasts point of view or when you have really large XAML files to maintain, I think it’s easier to simply handle 2 files where differences are unavoidable. So you either make your XAML work for both WPF and Silverlight and link that XAML file from one project or you maintain 2 different files.

That’s it for part 1. Starting with the the next part I’ll show the differences I’ve stumbled upon and a ways to solve or workaround them. Stay tuned…

kick it on DotNetKicks.com Shout it

Tags: , ,

Stock Chart 1.0 Final

10/20/2009 1:34:44 PM

DataSet comparison in Stock Chart

We have released final 1.0 version of the amCharts Stock Chart for WPF. Thanks to everyone who helps us spread the word!

kick it on DotNetKicks.com Shout it

Tags: ,

Oh, The Dramz: Mozilla blocks WPF

10/19/2009 8:52:31 AM

As someone who has been unpleasantly (but not critically) affected by the drama that ensued during the weekend after Mozilla’s decision to forcefully block .NET Assistant add-on and WPF plug-in on ALL Windows/Firefox installations around the world, I’ve been following a Bugzilla thread on the subject. Quite a fascinating reading.

A comment from Peter Schaefer perefectly summarizes how I feel about this unfortunate situation:

As understand the issue, Mozilla could have used its blocklist mechanism right if Microsoft had been smart enough to update the version number or name of the installed plugins along with their security patch. A solution would be to do this now and thus make the functionality work again for those who want it.

If you want to assign blame, which doesn't help anyone, Microsoft first messed things up, but now when trying to fix it both Mozilla and Microsoft have made suboptimal decisions, IMO.

As I see it the only reasonable way to resolve this satisfatorily and ASAP (which is really needed in the world utilizing affected technologies) is for Microsoft to release a dummy WPF plugin update with an updated version number that wouldn’t install on unpatched Windows and for Mozilla to unblock that version and leave the old one blocked.

I can only imagine what admins and helpdesks of affected companies are going through Today. My deepest condolences to you guys.

Update: the block on .NET Assistant (ClickOnce enabling add-on) has been removed on Sunday, so the problem is partially resolved. However WPF in-browser (XBAP) apps are still affected.

kick it on DotNetKicks.com

Tags: ,

Stock Chart for WPF

10/12/2009 11:37:39 AM

WPF Stock Chart

We’ve published a release candidate of amCharts Stock Chart for WPF. Stock Chart’s main purpose is visualization of financial data but it’s suitable and very efficent at displaying any date/time based data. It’s free under linkware license even for commercial use.

Check out Stock Chart for WPF demo.

Download amCharts Stock Chart for WPF.

Btw, through the month of October 2009 we are running a 50% discount on Stock Chart licenses. Click here for details.

Tags: ,

amCharts for WPF with Scatter &amp; Bubble Chart Support

7/10/2009 9:16:27 AM

bubble chart for WPF

We’ve released a new version of amCharts for WPF with support for Scatter (XY) & Bubble charting. Check it out and help us spread the word. Thanks!

kick it on DotNetKicks.com Shout it

Tags: , ,

Creating Gapped and Bulleted Shapes in WPF/Silverlight

5/26/2009 10:08:37 AM

I’ve released a small control for creating bulleted paths in WPF. You can get it in download section of this blog. The main trick used in this control is described in my article titled “Creating Gapped and Bulleted Shapes in WPF/Silverlight” and published by dev102.com.

Tags: ,

Name scopes within templates are not supported

5/8/2009 3:03:59 PM

I’ve got an issue reported on amCharts for WPF forum suggesting that it’s impossible to use the charts in DataTemplate. I’ve verified that it was true and exception message reads as follows:

"NameScopeProperty found within the content of a FrameworkTemplate, on a 'AmCharts.Windows.LineChart' object. Name scopes within templates are not supported."

The exception was caused by the call to SetNameSpace method in the constructor of the chart base class:

NameScope.SetNameScope(this, new NameScope());

I’ve re-read documentation on name scopes and made an assumption that I have to find a way to use template’s own name scope to register my names with it and…  spent almost a day trying to figure out how to do it universally.

Finally I gave up on that branch of thought and decided to try something else. To my surprise the problem was solved by simply moving the same call to SetNameScope() from constructor to a place right before it is needed. Namely where I make the first call to RegisterName() which happens after my controls have been loaded, templates applied, etc.

Now I’m not completely sure why this worked but my understanding is that if you do it in the constructor SetNameScope() get’s called in the context of the Template (producing an exception above) and when it is called later it’s already in context of the control to which the Template is applied. Please, correct me if I’m wrong.

kick it on DotNetKicks.com Shout it

Tags: ,

Copyright © 2003 - 2014 Alan Mendelevich
Powered by BlogEngine.NET 2.5.0.6