ailon's DevBlog: Development related stuff in my life

Unit Testing Combo WPF/Silverlight Projects

9/23/2010 8:24:52 PM

With release of WPF 4 and Silverlight 4 creating WPF and Silverlight applications sharing the same code became easier and less frustrating than it was before. The main techniques that I described in my series for WPF 3.5/Silverlight 3 (“Add as Link”, partial classes and preprocessor directives) are still the most reasonable approach for this task.

It is also commonly agreed that your ride will be smoother if you start with Silverlight. This doesn’t mean that you have to leave WPF for later. What this means is that you create files in your Silverlight project and “Add as Link” to your WPF project (not the other way around). The main benefit of this approach is that you get Intellisense , etc. for Silverlight and, since Silverlight doesn’t have all the classes, methods and overloads that full .NET framework has, you won’t be tempted to use them.

A very simple solution utilizing this approach would look something like this:

image

So What About Unit Testing?

If you are some unit testing fanatic you might want to unit test WPF part with MSTest (or other testing framework) and separately unit test Silverlight part with Silverlight Unit Test Framework. Now this would be good in test reliability but I can bet you won’t run both of them anyway and since most of your code is identical you would probably want to unit test that common code with only one of the testing suites.

It wouldn’t come as a surprise if you decide to use MSTest running inside Visual Studio over SilverlightUT running in browser. The problem here is that you are doing Silverlight project first and you can’t add a reference to your Silverlight project to the MSTest project.

image

So what you have to do if you want to go this route is constantly add new files to your WPF project and reference that project from the test project.

image

Problems with this Approach

Unfortunately this approach is far from perfect.

First of all it’s problematic to do TDD/tests first. When you write a call to a not yet defined method and try to generate method stub using Visual Studio you get this error:

image

This happens because you have MyControl.cs file open from a Silverlight project and the tool tries to generate the stub into WPF project and fails because the file is already open from another project. Now if you close the file as the message box suggests, the generation will succeed but it will open the file from the WPF project and you won’t be doing Silverlight-first unless you close the file again and reopen it from the Silverlight project again.

Then there’s a similar problem with refactoring/renaming. When you have your file open from the Silverlight project and you rename a method, class, property, etc. using Visual Studio refactoring tool it won’t be renamed in the test project. So you’ll have to go and do a bunch of manual finding and replacing.

These are obviously not critical issues but pretty annoying nevertheless. It’s very important that writing tests is as painless as it could be or you won’t write them. And pain is pretty much a part of the process considering these issues.

Hopefully there’s a better way to unit test such projects and I’m just not aware of it. If so, please, let me know in the comments.

Shout it kick it on DotNetKicks.com

Tags: , , ,

Side Effects of Silverlight Marketing

7/1/2010 9:47:07 AM

Note: this post is about marketing to developers (not general public).

Yesterday Tim Heuer published a blog post titled “Top issues for Silverlight” where he summarizes reported Silverlight issues. Apparently number one issue is “WPF and Silverlight feature (dis)parity”. Tim writes:

It’s a little weird to call this one an issue with Silverlight, but this was seen requested more often in these areas that it is worth calling attention to.  Some of the genesis of this angst comes from an expectation that WPF applications ‘just work’ in Silverlight and not understanding the areas of the subset in both XAML and the .NET base class libraries (BCL).

Now this is true but one has to wonder where this expectation comes from.

Silverlight is NOT a subset of WPF

The root of this problem (at least how I see it) is the initial marketing message of Silverlight for developers was that it is a subset of WPF. Now Microsoft is a serious and cautious company and you would hardly find any direct evidence of this but this is what gets stuck in minds of developers who know about WPF and/or Silverlight from various presentations before they dig deeply into development.

It’s hard for Microsoft insider to realize this. That is not that obvious even for someone who is working with both WPF and Silverlight. But if you take someone who does some real work with either WPF or Silverlight but not both and ask them what’s the relation between WPF and Silverlight, most of the time you are going to hear the mantra – “Silverlight is a subset of WPF”. I’ve heard developers, project managers, etc. with no experience in WPF and/or Silverlight repeat this numerous times. And that was exactly as I saw things before I started coding with both. This is encrusted deeply in the minds of developers and is the main source of frustration when they realize that this is not exactly the case.

I guess with all the advances in Silverlight over the recent years this disparity became a little more expected than before but the “subset” idea is pretty much still alive. Now I don’t know what needs to be done to kill this mantra and I’m not even sure Microsoft’s business departments want to kill it but I think this is the main source of this “issue” being number one.

Silverlight for Windows Phone 7 is NOT Silverlight

This subtitle is a little too drastic on my part but I’ll explain what I mean in the next few paragraphs.

One might think that WPF vs. Silverlight case was one of it’s kind but just this spring during MIX10 conference while unveiling Silverlight as one of the main developer platforms for Windows Phone 7 Scott Guthrie said this: “It’s not Silverlight Lite. It’s not Silverlight Mobile. It’s Silverlight.” (watch MIX10 Day 1 Keynote).

Now the wording is really clever. And later he explained that it’s Silverlight 3 with some parts of Silverlight 4 and phone specific stuff. He never said that you can run exactly the same code on the phone as you can on desktops and those who were interested in WP7 development understood that. But majority of the folks who just listened to this as high level overview have this notion stuck deeply in their minds. They believe that they can continue working on their Silverlight apps as they did before and then if they decide to move the app to WP7 they will only have to shuffle the UI a little to fit the smaller screen.

Do you think they are going to be frustrated when they find out that this is not the case? What do you think is going to be the top issue for Silverlight for Windows Phone 7? My guess is “Silverlight and Silverlight for WP7 (dis)parity”.

Shout it kick it on DotNetKicks.com

Tags: , , ,

Ghost Lines in Silverlight

4/7/2010 4:30:00 PM

Some pretty strange bug in amCharts has been reported to me. In one scenario user got 3 graph lines when zooming-in deeply on a single line graph.

While investigating this bug I found out that previous data point in that scenario was far apart from the one in view and a line was drawn from that previous data point to the current one. The calculated X-coordinate of the previous data point was something around –89000 in my test case so I became curious if this a little crazy coordinate value could be the source of the problem.

I made a very simple Silverlight 3 user control with this simple XAML:

   1: <Canvas>
   2:     <Line X1="-50000" Y1="10" X2="500" Y2="110" Stroke="Green" />
   3: </Canvas>

Here’s what you get when you run this app:

image

 

It looks like even though X1 is supposed to be of type double it rolls over what appears to be Int16.MinValue limit. Tried this in Silverlight 4 RC with exactly the same result. Surprisingly (actually not surprisingly at all) this exact code works as expected in WPF.

Searching around the web produced a couple of results dealing with something that appears to be manifestation of the same issue but in different scenarios and with different outcome.

I wasn’t able to locate any mentions of these limitations in the documentation but I must admit I wasn’t looking hard enough to say that it’s definitely not there.

I’m not sure if I should report this as a bug somewhere provided that one of the posts above is on official Silverlight forums and there are no official answers to it. What I know is that we – Silverlight developers – have to account for this issue in our code and make required approximations ourselves (as in my case) or impose limits on users (as in cases above). So just beware of this oddity.

Some official or just informed word on the issue wouldn't hurt either.

Shout it kick it on DotNetKicks.com

Tags: , ,

Writing WPF/Silverlight compatible code. Part 6: Adding XAML files as links

1/13/2010 5:05:40 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.

While developing WPF/Silverlight applications and controls one of the main code sharing techniques is adding shared code files as links to one of your projects.

Visual Studio - Add As Link

This works perfectly with C# or VB files but not as good with XAML. In this article I’ll show you a workaround to make this technique work with only a small overhead.

The Problem

Suppose you’ve created a WPF project and your own custom control named MyTemplatedControl. It does nothing in our example (just sets DefaultStyleKey property).

   1: public class MyTemplatedControl : Control
   2: {
   3:     public MyTemplatedControl()
   4:     {
   5:         this.DefaultStyleKey = typeof(MyTemplatedControl);
   6:     }
   7: }

Now we create a default ControlTemplate for our control. To do this we add Themes folder to our project and Generic.xaml flie in that folder.

image

Our template will just display a blue rectangle (just so we can see if it’s applied). Here’s the XAML in Generic.xaml file.

   1: <ResourceDictionary
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:local="clr-namespace:TestControl"
   5:     >
   6:  
   7:     <Style TargetType="local:MyTemplatedControl">
   8:         <Setter Property="Template">
   9:             <Setter.Value>
  10:                 <ControlTemplate TargetType="local:MyTemplatedControl">
  11:                     <Canvas>
  12:                         <Rectangle Canvas.Left="20" Canvas.Top="20" Width="100" Height="100" 
  13:                                    Fill="Blue" Stroke="Black" StrokeThickness="3" />
  14:                     </Canvas>
  15:                 </ControlTemplate>
  16:             </Setter.Value>
  17:         </Setter>
  18:     </Style>
  19: </ResourceDictionary>

We add our control to a test window and run the app we see something like this:

image

Now we want to create a Silverlight version of the control. We create a Silverlight custom control project, “Add As Link” our MyTemplatedControl.cs file, create Themes directory and “Add As Link” our Generic.xaml file. Everything compiles just fine. But when we add the control to a Silverlight application and run it we get an empty white screen. The template is not applied.

This happens because for some reason (I don’t know if it’s done for a reason or is it just a bug) XAML compiler compiles local and linked .xaml files differently. Here’s an article describing what happens. Short story is that resource key for local and linked xaml files are different and since the engine looks for a specific resource key for default styles it just can’t see the linked version because it’s key is not equal to what it looks for.

The Solution

The above mentioned LearnWPF.com article suggest a workaround of moving all resources to a separate assembly. I’m pretty sure that wouldn’t work with default styles and generally is not something you would want to do in simple scenarios.

I solved this issue using MergedDictionaries. The solution is to move you Generic.xaml content to other XAML file (or even better separate files for each control). This way you can “Add As Link” those specific files and add separate Generic.xaml files with only a list of MergedDictionaries addressing the resource key differences.

Let’s apply this technique to our example. So we move our XAML to a separate MyTemplatedControl.xaml file and reference it from MergedDictionaries in Generic.xaml.

MyTemplatedControl.xaml:

   1: <ResourceDictionary
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:local="clr-namespace:TestControl"
   5:     >
   6:     <Style TargetType="local:MyTemplatedControl">
   7:         <Setter Property="Template">
   8:             <Setter.Value>
   9:                 <ControlTemplate TargetType="local:MyTemplatedControl">
  10:                     <Canvas>
  11:                         <Rectangle Canvas.Left="20" Canvas.Top="20" Width="100" Height="100" 
  12:                                    Fill="Blue" Stroke="Black" StrokeThickness="3" />
  13:                     </Canvas>
  14:                 </ControlTemplate>
  15:             </Setter.Value>
  16:         </Setter>
  17:     </Style>
  18: </ResourceDictionary>

Generic.xaml

   1: <ResourceDictionary
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     >
   5:     <ResourceDictionary.MergedDictionaries>
   6:         <ResourceDictionary Source="/WpfControl;component/Themes/MyTemplatedControl.xaml" />
   7:     </ResourceDictionary.MergedDictionaries>
   8: </ResourceDictionary>

Now we “Add As Link” MyTemplatedControl.xaml to our Silverlight project and add a copy of Generic.xaml and make required changes.

Generic.xaml (Silverlight project version)

   1: <ResourceDictionary
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     >
   5:     <ResourceDictionary.MergedDictionaries>
   6:         <ResourceDictionary Source="/SlControl;component/MyTemplatedControl.xaml" />
   7:     </ResourceDictionary.MergedDictionaries>
   8: </ResourceDictionary>

Notice the differences in Source of ResourceDictionary:

  1. It refers to out Silverlight assembly (SlControl)
  2. It addresses linked XAML file by it’s “incorrect” resource key which is a side effect of linking the XAML file described above (notice that there’s no “Themes” portion in the path)

Now our Silverlight app runs as expected:

image

This way we can share elaborate XAML between WPF and Silverlight versions (or just between several WPF and/or Silverlight projects) without having to maintain 2 versions of the file. And the only price to pay for this is that we have to add a line to both Generic.xaml files every time we add a new XAML file.

kick it on DotNetKicks.com Shout it

Tags: , ,

Writing WPF/Silverlight compatible code. Part 5: Adding PropertyChangedCallback

11/30/2009 5:13:27 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.

No OverrideMetadata() Take 2

One thing I didn’t mention in the “No OverrideMetadata()” section of “Part 2: Dependency Properties” is how to add a handler to be called when value of inherited dependency property changes. In WPF you can use OverrideMetadata() method to do it like this:

   1: static MyControl()
   2: {
   3: #if !SILVERLIGHT
   4:     FrameworkPropertyMetadata newMetadata = new FrameworkPropertyMetadata();
   5:     newMetadata.PropertyChangedCallback += MyControl.VisibilityProperty_Changed;
   6:     Control.VisibilityProperty.OverrideMetadata(typeof(MyControl), newMetadata);
   7: #endif
   8: }

Then you do what you wanted to do in your handler which is called every time Visibility changes:

   1: private static void VisibilityProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
   2: {
   3:     // do something here
   4: }

As you know there’s no OverrideMetadata() in Silverlight. So what can we do? Unfortunately, to my knowledge, there’s no elegant way to do this. There are however a couple of not so pretty hacks/workarounds. One is described in an answer to a similar question on StackOverflow.

I’ve used another method which involves an extra DependencyProperty declaration and binding of the original dependency property to this one. In the above mentioned case a Silverlight portion for achieving the same result looks like this:

   1: #if SILVERLIGHT
   2:         // declare an extra DependencyProperty for the workaround
   3:         // It uses the same PropertyChangedCallback as WPF code for Visibility
   4:         private static readonly DependencyProperty VisibilityChangedWorkaroundProperty = DependencyProperty.Register(
   5:             "VisibilityChangedWorkaround", typeof(Visibility), typeof(MyControl),
   6:             new PropertyMetadata(MyControl.VisibilityProperty_Changed));
   7: #endif
   8:  
   9: public MyControl()
  10: {
  11: #if SILVERLIGHT
  12:     // visibility changed event workaround
  13:     Binding visibilityBnd = new Binding("Visibility");
  14:     visibilityBnd.Source = this;
  15:     visibilityBnd.Mode = BindingMode.TwoWay;
  16:     this.SetBinding(MyControl.VisibilityChangedWorkaroundProperty, visibilityBnd);
  17: #endif
  18: }

As you can see above, we’ve declared and extra dependency property called VisibilityChangedWorkaroundProperty and we’ve attached the same handler to it’s property changed event as the one used in the WPF code above. We don’t need a CLR property wrapper for it and we’ve declared it as private so it doesn’t show up anywhere outside of our class. Then in the constructor we bind our new workaround property to the real inherited Visibility property. So, when inherited property changes, our local property changes too, and the callback is called.

kick it on DotNetKicks.com Shout it

Tags: , ,

Writing WPF/Silverlight compatible code. Part 4: Geometries

11/23/2009 7:26: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.

Default Contstructors Only

The first thing you notice when you start working with geometries in Silverlight after you’ve dealt with them in WPF is absence of constructor overloads. So, if you’ve used to write constructs like this in WPF:

RectangleGeometry rectangle = new RectangleGeometry(new Rect(10, 10, 50, 40));


you should forget about them when you start caring about WPF/Silverlight compatibility. Use something like this instead:

   1: RectangleGeometry rectanble = new RectangleGeometry()
   2: {
   3:     Rect = new Rect(10, 10, 50, 40)
   4: };

This works in both and isn’t that much longer.

No PathSegment.IsStroked

IsStroked let’s you specify that a segment of some path shouldn’t be stroked. So, in WPF you can create a path like this

image

with XAML like this:

   1: <Path Stroke="Black" StrokeThickness="3">
   2:     <Path.Data>
   3:         <PathGeometry>
   4:             <PathFigure StartPoint="0,20">
   5:                 <LineSegment Point="20,0" />
   6:                 <LineSegment Point="40,20" />
   7:                 <LineSegment Point="60,20" IsStroked="False" />
   8:                 <LineSegment Point="80,0" />
   9:                 <LineSegment Point="100,20" />
  10:             </PathFigure>
  11:         </PathGeometry>
  12:     </Path.Data>
  13: </Path>

As you have probably guessed, it’s not going to work in Silverlight 3 (and as far as I’ve checked in Siverlight 4 beta either). For simple scenarios like the one above, you can overcome this limitation by simply dividing PathFigure into several figures (one for each continuos segment) like this

   1: <Path Stroke="Black" StrokeThickness="3">
   2:     <Path.Data>
   3:         <PathGeometry>
   4:             <PathFigure StartPoint="0,20">
   5:                 <LineSegment Point="20,0" />
   6:                 <LineSegment Point="40,20" />
   7:             </PathFigure>
   8:             <PathFigure StartPoint="60,20">
   9:                 <LineSegment Point="80,0" />
  10:                 <LineSegment Point="100,20" />
  11:             </PathFigure>
  12:         </PathGeometry>
  13:     </Path.Data>
  14: </Path>

I’ve created a simple helper extension method to do the same trick automatically in code depending on whether it’s being called in WPF or Silverlight:

   1: public static void AddLineSegment(this PathGeometry pathGeometry, Point point, bool isStroked)
   2: {
   3:     PathFigure figure = pathGeometry.Figures[pathGeometry.Figures.Count - 1];
   4:  
   5: #if SILVERLIGHT
   6:     if (isStroked)
   7:     {
   8:         figure.Segments.Add(new LineSegment() { Point = point });
   9:     }
  10:     else
  11:     {
  12:         // silverlight has no IsStroked properto on LineSegment
  13:         // so we create a new PathFigure to imitate non-stroked line segment
  14:         figure = new PathFigure();
  15:         figure.StartPoint = point;
  16:         pathGeometry.Figures.Add(figure);
  17:     }
  18: #else
  19:     figure.Segments.Add(new LineSegment(point, isStroked));
  20: #endif
  21: }

You can use it like this when constructing paths in your code:

myPathGeometry.AddLineSegment(myPoint, true);

The method can be generalized to support other types of PathSegments, but I needed it for lines so there you have lines-only version. You can generalize/adapt it yourself.

This saves us in cases like the one above. But here’s another simple case that above technique fails to solve:

image

In WPF a figure like this can be created with this XAML:

   1: <Path HorizontalAlignment="Center" VerticalAlignment="Center"
   2:     Stroke="Black" Fill="LightGreen" StrokeThickness="3">
   3:     <Path.Data>
   4:         <PathGeometry>
   5:             <PathFigure StartPoint="0,40">
   6:                 <LineSegment Point="0,20" IsStroked="False" />
   7:                 <LineSegment Point="20,0" />
   8:                 <LineSegment Point="40,20" />
   9:                 <LineSegment Point="60,20" IsStroked="False" />
  10:                 <LineSegment Point="80,0" />
  11:                 <LineSegment Point="100,20" />
  12:                 <LineSegment Point="100,40" IsStroked="False" />
  13:             </PathFigure>
  14:         </PathGeometry>
  15:     </Path.Data>
  16: </Path>

Unfortunately, I don’t know a way to achieve this in Silverlight other than overlaying 2 paths – one for the fill and one for the stroke like this:

   1: <Path Fill="LightGreen" StrokeThickness="3">
   2:     <Path.Data>
   3:         <PathGeometry>
   4:             <PathFigure StartPoint="0,40">
   5:                 <LineSegment Point="0,20"/>
   6:                 <LineSegment Point="20,0" />
   7:                 <LineSegment Point="40,20" />
   8:                 <LineSegment Point="60,20"/>
   9:                 <LineSegment Point="80,0" />
  10:                 <LineSegment Point="100,20" />
  11:                 <LineSegment Point="100,40" />
  12:             </PathFigure>
  13:         </PathGeometry>
  14:     </Path.Data>
  15: </Path>
  16:  
  17: <Path Stroke="Black" StrokeThickness="3">
  18:     <Path.Data>
  19:         <PathGeometry>
  20:             <PathFigure StartPoint="0,20">
  21:                 <LineSegment Point="20,0" />
  22:                 <LineSegment Point="40,20" />
  23:             </PathFigure>
  24:             <PathFigure StartPoint="60,20">
  25:                 <LineSegment Point="80,0" />
  26:                 <LineSegment Point="100,20" />
  27:             </PathFigure>
  28:         </PathGeometry>
  29:     </Path.Data>
  30: </Path>

And since we are talking about WPF/Silverlight reusable code here, this is the way we should do it to work in both. Let me know if you happen to know a better way of doing something like this without having to rely on 2 objects.

kick it on DotNetKicks.com Shout it

Tags: , ,

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: , ,

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