ailon's DevBlog: Development related stuff in my life

WPF, Silverlight and WP7 at MIX11 – My Picks

1/26/2011 6:31:57 PM

I’ve submitted a proposal to present a talk at MIX11 conference in Las Vegas called “Developing for WPF, Silverlight and Windows Phone 7 at Once”. The proposal has passed the first round of filtering and now it needs your votes to get me into one of the 10 spots. With more than 200 candidates that’s going to be a tough task. But it’s doable, right? RIGHT!?

image

If you like the subject of my talk you can vote for it by selecting all the cats in the CAPTCHA and pressing the “Vote for the session” button.

Obviously mine is not the only great proposal there. Here are some of my favorites I voted for:

  • First of all my virtual friend (devirtualization at MIX?), great developer, Silverlight MVP and overall nice guy René Schulte has 3 proposals: The World Is Not Enough - Silverlight Augmented Reality, Pictures Lab - How To Write A Silverlight Photo Effects Application For Windows Phone 7 and Silverlight 3D. I’m sure he can deliver and you should definitely vote for one of his proposals (if not all).
  • Then there’s The tale of two apps - Making a splash in the Windows Phone Marketplace by András Velvárt and Bálint Orosz. A story of 2 of the most popular WP7 apps made by developers from the “unsupported” Hungary who instead of whining and fighting for justice (like I do) found ways to make a splash in the Marketplace despite obstacles Microsoft put in front of them.
  • Then there’s PHP on Windows by fellow Lithuanian Juozas Kaziukėnas. I’ve seen him present an earlier version of this talk and it was great even though it feels a little out of place in my list. Juozas is a great presenter and the content is interesting for those who want to keep their eye on what’s happening in the area.
  • Then I chose Exploring a Blendable Windows Phone 7 Application by Laurent Bugnion out of 2 of his proposals. I believe Deep dive MVVM will get through without my help. Laurent is probably the most knowledgeable and influential Silverlight MVP in Europe and probably the whole world. It’s always interesting to see him speak.
  • One talk that has a chance to be overlooked but seems very interesting and important is Introduction to Maps by Colin Blair. Mapping becomes more common in all sorts of apps but quite a few developers understand the terms and concepts involved.

My other votes in no particular order:

As you can see these are only a few of the proposals and they are well worth their own conference and they need your votes.

And most importantly (shameless plug and I’m not even sorry) I need your votes. Thanks!

P.S. In case you don’t like cats think of the CAPTCHA as the one were you have to eliminate all the cats.

Tags: , , ,

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

Quick Charts for WPF & Silverlight v.1.0 Beta Released

5/10/2010 3:48:46 PM

We are happy to announce availability of feature complete version of amCharts Quick Charts for WPF & Silverlight.

Quick Charts is an easy to use, fast set of charting controls for WPF & Silverlight. And it's FREE and Open Source (Ms-PL)!

Quick Charts for WPF & Silverlight

Quick Charts motto is: Ease of Use, Speed, Small Footprint. It is amCharts Bundle's little brother. It has less features, but concentrates on what's important for the majority of users. In order to achieve this we didn't just strip the features off of amCharts Bundle, but created Quick Charts from scratch.

Check them out and, please, spread the word. Thanks!

Shout it kick it on DotNetKicks.com

Tags: , ,

VisualStudio.NET vNext Feature Request: Multi-target IntelliSense

4/8/2010 6:44:04 PM

Visual Studio is great. IntelliSense is great. But here’s one thing that has been slowing me down ever since I started developing for several .NET based platforms.

The Problem

WPF, Silverlight and now Silverlight for Windows Phone 7 are very similar frameworks, but they are far from being identical. Lots of code written for one can be re-used in another, but quite often it requires small modifications and it’s frustrating. It’s especially frustrating when you realize that those modifications wouldn’t be necessary if you’ve used another overload of some method or used another method altogether in the first place.

Take a look at screenshots of the IntelliSense dropdown for the same class in different environments:

Here’s one for WPF 3.5:

wpf_dropdown

And this is on for Silverlight 3:

silverlight_dropdown

Notice that there are lots of methods, properties and events that are not in Silverlight and I guess you can find some that are not in WPF (but that is not the point here). Sometimes you want to use WPF-only stuff for better performance or other very valid reasons but quite often you just use it unconsciously because finding out if something is supported in Silverlight or vice versa takes too much time and effort.

Now I wanted to add a LineSegment to the PathFigure and it has 3 constructor overloads in WPF

wpf_overloads

but only only the default constructor in Silverlight

silverlight_overloads

If I haven’t checked for the difference manually (or knew it by heart) I would end up rewriting this code once I’ve switched to a Silverlight project. So, you get the picture. The fact that I constantly have to recompile for all the platforms and/or manually check for differences takes a significant amount of my development time. I would like this to be addressed somehow and if some third party tool does something good in this area, please, let me know in the comments, but for now I have this idea/feature request for future version of Visual Studio .NET

Multi-target IntelliSense

There are numerous ways how this could be implemented and I’m not the best expert in things like this, but I’ll try to explain my vision of this anyway.

I think a project could have a set of secondary Target Framework settings. When these are set, IntelliSense would somehow highlight members or overloads that are in main target framework but not in the secondary. For example WPF methods that aren’t in Silverlight could be grayed out in the dropdown above. Same goes for method overloads. Plus some text explanation in the tooltip wouldn’t hurt too.

Something like this would be a huge time saver for me and I guess for other developers too. Especially with all the extra framework partitioning related to release of Silverlight for Windows Phone 7 which will be another very close yet not completely identical addition to the current mix of WPF & Silverlight.

I’ve ran this idea by Jesse Liberty at MIX10 and he thought it wasn’t a bad idea at all, so I decided I need to submit it to Microsoft via MS Connect. So if you like this and would like something along these lines implemented in the future version of Visual Studio, please, go there and vote it up.

Shout it

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

Setting Pens on Shapes in WPF

11/18/2009 2:42:36 PM

Sometimes you need to draw various shapes, drawings and objects using the same elaborate pen. Like the rectangle and the star in this picture.

pentostroke

Problem is that some WPF elements like GeometryDrawing (represented by star in this picture) take Pen object for their pen settings and classes derived from Shape (like Rectangle, Ellipse, etc.) expose all the pen settings directly in the form of StrokeXxx properties. So when you want to draw several GeometryDrawings and several shapes using the same pen settings you end up defining 2 sets of these settings in your Resources: one in the form of a Pen object and 1 for StrokeXxx properties.

In this article we will rectify this problem with the help of one simple extension method and an attached property.

ApplyPen() Method

First we create ApplyPen() extension method for the Shape class. All it does is assigns Pen properties to the appropriate StrokeXxx properties of the Shape. Here's the code:

   1: public static void ApplyPen(this Shape shape, Pen pen)
   2: {
   3:     if (pen != null)
   4:     {
   5:         shape.Stroke = pen.Brush;
   6:         shape.StrokeThickness = pen.Thickness;
   7:         shape.StrokeDashCap = pen.DashCap;
   8:         if (pen.DashStyle != null)
   9:         {
  10:             shape.StrokeDashArray = pen.DashStyle.Dashes;
  11:             shape.StrokeDashOffset = pen.DashStyle.Offset;
  12:         }
  13:         shape.StrokeStartLineCap = pen.StartLineCap;
  14:         shape.StrokeEndLineCap = pen.EndLineCap;
  15:         shape.StrokeLineJoin = pen.LineJoin;
  16:         shape.StrokeMiterLimit = pen.MiterLimit;
  17:     }
  18: }

Attached Property – Pen

Using ApplyPen() method we can apply any pen to any shape from code but we want to be able to do this from XAML too. So we define an attached property called Pen which when set calls our extension method to apply the pen. Here's how it's implemented:

   1: public static readonly DependencyProperty PenProperty = DependencyProperty.RegisterAttached(
   2:     "Pen", typeof(Pen), typeof(ShapeExtender),
   3:     new FrameworkPropertyMetadata(null, new PropertyChangedCallback(ShapeExtender.PenProperty_Changed))
   4:     );
   5:  
   6: public static void SetPen(Shape shape, Pen pen)
   7: {
   8:     shape.SetValue(PenProperty, pen);
   9: }
  10:  
  11: public static Pen GetPen(Shape shape)
  12: {
  13:     return (Pen)shape.GetValue(PenProperty);
  14: }
  15:  
  16: private static void PenProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
  17: {
  18:     Shape shape = d as Shape;
  19:     if (shape != null)
  20:     {
  21:         shape.ApplyPen(e.NewValue as Pen);
  22:     }
  23: }

Now we can define a single Pen in our resources and use it for both Rectangle and star (GeometryDrawing) in our sample.

Pen definition:

   1: <Window.Resources>
   2:     <Pen x:Key="MyPen" Thickness="10" LineJoin="Bevel">
   3:         <Pen.Brush>
   4:             <LinearGradientBrush>
   5:                 <GradientStop Color="#FF5555" Offset="0" />
   6:                 <GradientStop Color="#FFFF55" Offset="0.2" />
   7:                 <GradientStop Color="#55FFFF" Offset="0.4" />
   8:                 <GradientStop Color="#FF55FF" Offset="0.6" />
   9:                 <GradientStop Color="#5555FF" Offset="0.8" />
  10:                 <GradientStop Color="#55FF55" Offset="1" />
  11:             </LinearGradientBrush>
  12:         </Pen.Brush>
  13:         <Pen.DashStyle>
  14:             <DashStyle Dashes="0.2 2 2 0.2" />
  15:         </Pen.DashStyle>
  16:     </Pen>
  17: </Window.Resources>

Pen applied to both GeometryDrawing and a Rectangle:

   1: <Rectangle Name="Rectangle1" Canvas.Left="20" Canvas.Top="20" Width="200" Height="100"
   2:            local:ShapeExtender.Pen="{StaticResource MyPen}"
   3:            >
   4: </Rectangle>
   5:  
   6: <Image Canvas.Left="250" Canvas.Top="20" Height="100" Width="100">
   7:     <Image.Source>
   8:         <DrawingImage>
   9:             <DrawingImage.Drawing>
  10:                 <GeometryDrawing Pen="{StaticResource MyPen}">
  11:                     <GeometryDrawing.Geometry>
  12:                         <PathGeometry>
  13:                             <PathFigure StartPoint="20,100" IsClosed="True">
  14:                                 <LineSegment Point="100,40" />
  15:                                 <LineSegment Point="0,40" />
  16:                                 <LineSegment Point="80,100" />
  17:                                 <LineSegment Point="50,0" />
  18:                             </PathFigure>
  19:                         </PathGeometry>
  20:                     </GeometryDrawing.Geometry>
  21:                 </GeometryDrawing>
  22:             </DrawingImage.Drawing>
  23:         </DrawingImage>
  24:     </Image.Source>
  25: </Image>

Bonus: GetPenFromStroke() Method

Using ApplyPen() method and Pen attached property we can apply a Pen to a Shape. But what if we need to go the other way around? For example if you draw something from code using DrawingContext.DrawXxx() methods you need a Pen. And sometimes you want this pen to match settings of some Shape. So you need the opposite of ApplyPen() method – a method that takes a Shape and returns a Pen based on it's StrokeXxx settings. Here is a super-simple but nevertheless useful GetPenFromStroke() extension method:

   1: public static Pen GetPenFromStroke(this Shape shape)
   2: {
   3:     return new Pen()
   4:     {
   5:         Brush = shape.Stroke,
   6:         Thickness = shape.StrokeThickness,
   7:         DashCap = shape.StrokeDashCap,
   8:         DashStyle = new DashStyle()
   9:         {
  10:             Dashes = shape.StrokeDashArray,
  11:             Offset = shape.StrokeDashOffset
  12:         },
  13:         StartLineCap = shape.StrokeStartLineCap,
  14:         EndLineCap = shape.StrokeEndLineCap,
  15:         LineJoin = shape.StrokeLineJoin,
  16:         MiterLimit = shape.StrokeMiterLimit
  17:     };
  18: }

kick it on DotNetKicks.com Shout it

Tags:

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