Showing posts with label WPF. Show all posts
Showing posts with label WPF. Show all posts

Wednesday, May 27, 2009

Why doesn’t my WPF Trigger work? (... a peek into the Dependency Property evaluation process)

Imagine a simple situation where I have a blue rectangle:

<Grid>
<Rectangle Height="200" Width="300" Fill="Blue"/>
</Grid>



Now when I do a mouse over the rectangle, I want the color to turn to red - simple! The obvious choice in WPF is to add a property trigger. So that’s what I do.


<Grid>
<Grid.Resources>
<Style TargetType="Rectangle" x:Key="rectStyle">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Rectangle Height="200" Width="300" Fill="Blue"
Style="{StaticResource rectStyle}"/>
</Grid>



But it doesn’t work! What could be wrong? Perhaps the mouse over event isn’t firing on the rectangle for some reason? I debug the trigger using this cool technique and find that the trigger is firing alright. Then what on earth is the problem?

A closer look reveals the real issue. The culprit was the Fill="Blue" assignment! When evaluating the value of a dependency property, the dependency property system takes into consideration a few factors. The whole dependency property value evaluation process is shown below. Certain factors have priority over others as shown in Step 1 below.

Step 1: Determine Base Value
In the order of diminishing priority:
(a) Local Value
(b) Style triggers
(c) Template Triggers
(d) Style Setters
(e) Theme Style Triggers
(f) Theme Style Setters
(g) Inheritance
(h) Default Value

Step 2: Evaluate
– If Step 1 returns expression (DataBinding or DynamicResource), evaluate

Step 3: Apply Animation

Step 4: Coerce
- If CoerceValueCallback defined

Step 5: Validate
- If ValidateValueCallback defined

In the above example of the rectangle, the trigger (see Step 1b) that I set in the style, had lower priority when compared to the local value (see Step 1a), that is the Fill="Blue" assignment. That was the reason why the trigger value was ignored. So the result/output of Step 1 as far as the Fill property was concerned was the local value “Blue”. In this example, Steps 2-5 do not apply.

So how do I get my trigger to work? If I remove the local value assignment for Fill, and instead set that value within the style, then everything will work as expected because triggers (see Step 1b) have higher priority over style setters (see Step 1d).


<Grid>
<Grid.Resources>
<Style TargetType="Rectangle" x:Key="rectStyle">
<Setter Property="Fill" Value="Blue" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Rectangle Height="200" Width="300"
Style="{StaticResource rectStyle}"/>
</Grid>



Now the trigger works as expected!

Monday, April 27, 2009

WPF Text to Animated Path

Not sure what to call this - slate control? Or Text Scribbler? May be, or perhaps just "text to animated path control" :). Anyway, I was playing around with the possibility of animating text in such a way that you can see the text being written as though with a pen or a piece of chalk. This is the end result...

You can get the source here. Just run the application, enter any text in the TextBox, and hit the button to see the text being scribbled down in an animated fashion. You can also choose the Font Family, and the Foreground of the text from the combo boxes.

This is what the application does when the button is clicked:
1) The text from the TextBox is converted into a PathGeometry using the FormattedText class (System.Windows.Media namespace). You can specify the Font Family, Font Size etc. and get the corresponding path.

2) A MatrixAnimationUsingPath is applied on the pen (the red color object in the screen shot above). The PathGeometry property of this animation is set to the value obtained from Step 1).

3) At this point, the pen will animate over the path, but nothing will be scribbled down. While the pen is being animated, a timer runs concurrently with an interval in milliseconds. Whenever the timer ticks, the current position of the pen is calculated, and a small dot (ellipse) is drawn at that point. This gives you the illusion of text being continuously written down with the pen.

That's it! Enjoy scribbling!

(Note: The sample supports multi-line scribbling as well. To test this out, just set AcceptsReturn to true on the TextBox (you may also need to set IsDefault to False on the Start Button so that it does not interfere with the return key meant for the TextBox). Now if you enter multiple lines within the TextBox and start scribbling, you get multi-line scribbling !)

Source: Get it here.

Thursday, April 23, 2009

Debugging Data Binding in WPF

What do you do when you suspect that a data binding is broken in your WPF application? Looking at the Output window for data binding exceptions can be helpful in many cases. However, there are cases where this does not help.

For example, below I have TextBox.Text bound to Slider.Value. But notice that the binding is OneWay.

<TextBox Text="{Binding ElementName=slider, Path=Value, Mode=OneWay}" Height="30" Width="100" Margin="10" />

<Slider Minimum="0" Maximum="100" Value="20" Margin="10" x:Name="slider" />

When I move the slider, the text in the TextBox shows the slider value as expected. Now I enter some text in the TextBox(which causes the binding to break since it is OneWay binding; but assume that I haven't realized that). I move the slider now, but the text in TextBox does not update. I suspect that the binding is broken. But how do I confirm that?

Simply looking at the Output Window in this case reveals nothing. What I've been doing until now is to add a dummy converter and set a break point in the Convert method. If the break point is not hit I know that the binding is broken. But that is really a long way of doing something that should be really done in a simpler way....

WPF 3.5 has a new property called PresentationTraceSources.TraceLevel that you can set on your data binding to get more detailed information on the binding in the Output Window. In fact, if you set the trace level to High you can get details on every step in the process of the binding.

So to the above code, I make two changes

  • Add a namespace reference to System.Diagnostics
  • Set PresentationTraceSources.TraceLevel=High on the binding I want to inspect.

<Window x:Class="DebugBindings.Window1"... xmlns:trace="clr-namespace:System.Diagnostics;assembly=WindowsBase">

<StackPanel>

<TextBox Text="{Binding ElementName=slider, Path=Value, Mode=OneWay, trace:PresentationTraceSources.TraceLevel=High}" Height="30" Width="100" Margin="10"/>

<Slider Minimum="0" Maximum="100" Value="20" Margin="10" x:Name="slider" />

StackPanel>

Window>

Now when I inspect the Output Window after running the application, I can see step-by-step details of the data binding process.

Created BindingExpression (hash=17654054) for Binding (hash=44624228)
Path: 'Value'
BindingExpression (hash=17654054): Default update trigger resolved to LostFocus
BindingExpression (hash=17654054): Attach to System.Windows.Controls.TextBox.Text (hash=52727599)
BindingExpression (hash=17654054): Resolving source
BindingExpression (hash=17654054): Found data context element: (OK)
Lookup name slider: queried TextBox (hash=52727599)
BindingExpression (hash=17654054): Resolve source deferred

BindingExpression (hash=17654054): Resolving source
BindingExpression (hash=17654054): Found data context element: (OK)
Lookup name slider: queried TextBox (hash=52727599)
BindingExpression (hash=17654054): Activate with root item Slider (hash=54371668)
BindingExpression (hash=17654054): At level 0 - for Slider.Value found accessor DependencyProperty(Value)
BindingExpression (hash=17654054): Replace item at level 0 with Slider (hash=54371668), using accessor DependencyProperty(Value)
BindingExpression (hash=17654054): GetValue at level 0 from Slider (hash=54371668) using DependencyProperty(Value): '20'
BindingExpression (hash=17654054): TransferValue - got raw value '20'
BindingExpression (hash=17654054): TransferValue - implicit converter produced '20'
BindingExpression (hash=17654054): TransferValue - using final value '20'

If I now enter text in the TextBox, I can check the Output Window to confirm that the binding was indeed broken!

BindingExpression (hash=17654054): Deactivate
BindingExpression (hash=17654054): Replace item at level 0 with {NullDataItem}
BindingExpression (hash=17654054): Detach

The only down side is that you have to manually add this property to each binding that you want to inspect.

Monday, December 29, 2008

Discovering RelativeSource.PreviousData

When working with Data Binding in WPF, I have found the RelativeSource class very useful. This class is useful when you want to bind to a source relative to the binding target. For example you may want to bind the Width of a control to its Height. In this case RelativeSource.Self can be used. Or you may want to bind to an element that is an ancestor of the binding target. Here you would use RelativeSource.FindAncestor.

But there was another use of RelativeSource that I was not aware of until recently - RelativeSource.PreviousData. Let me explain where this might be useful. Say you have data bound an ItemsControl like a ListBox to a collection of data items. You have written a DataTemplate to define how each item must be displayed within the ItemsControl. Within the DataTemplate you have access to the current data item. But what if you also want access to the previous data item?



For example, you may be displaying the stock values for a company as a chart as shown below.



Each bar in the chart represents one data item within the ItemsControl. But within each item(above the bar), you also want to show the increase/decrease of the stock value when compared to the previous stock value. This is where RelativeSource.PreviousData is useful as shown below.


<DataTemplate x:Key="RecordTemplate">


...........


...........


<TextBlock Margin="3,0,0,3">


<TextBlock.Text>


<MultiBinding Converter="{StaticResource RecordsDifferenceConverter}">


<Binding/>


<Binding RelativeSource="{RelativeSource PreviousData}"/>


MultiBinding>


TextBlock.Text>


TextBlock>


...........


...........


</ DataTemplate


In the above XAML snippet, the MultiValueConverter accepts the value of the current item and the value of the previous item, and returns the difference between the two. This is displayed above each bar in the chart.



The source code for this sample may be found here. Keep in mind that this is not a fully implemented chart – there are so many things missing. I just wanted to illustrate where RelativeSource.PreviousData may be useful.

Friday, April 18, 2008

Differences between the WPF and Silverlight 2 model

What are the differences between the WPF and Silverlight 2 model? If you have worked with WPF before you started working with Silverlight (like me!), then this is no doubt a key question lingering in your mind. I understand that Microsoft is planning to publish such a list, and that would be great when it comes. But until that is published, below is a list of differences that I have picked so far. Remember this is a comparision between WPF and Silverlight 2 Beta 1. Some of the gaps between these two related technologies may be filled in future releases of Silverlight.

The observations below are what apply to Silverlight 2 Beta 1.

Styles
  • Once you apply a style to a FrameworkElement, you cannot reassign a new style. This is bad news because this means that the user cannot pick a theme. Having said that, you can still reassign properties that have been once assigned. This means that you can reassign the control template since it is a property as well.
  • Styles cannot inherit from other styles - the 'BasedOn' property cannot be used.
  • Styles have to be referenced by a key. This means that you cannot drop a style for a button in your 'application.resources' hoping that all buttons in the application will pick up the style. You need to explicitly reference it by its key in all buttons in the application. Sigh!

Triggers

See my earlier post on this. There are no triggers in Silverlight Beta 1 for styles, control templates and data templates. Sigh, sigh and more sigh!

Say you are changing the control template of a button. When doing that, as part of the template you would want to define an animation that should fire when the mouse is over it. In WPF, you would just add a trigger in the control template that looks for the IsMouseOver property - which is a neat way of doing things. But in Silverlight, the developer who creates the control has to create event handlers for the MouseEnter and MouseLeave events, and in these event handlers he has to load storyboards with well known names. Then hope that the designer/developer skinning the control will supply storyboards with the same names!

Control Templates

Keeping aside the limitation control templates have that I explained above (in the triggers section), I think this is one area that Silverlight is better than WPF. Silverlight follows the parts model. This means that any control (controls that ship with Silverlight and custom controls) publish a well defined control contract. This control contract will define

  1. the minimum UIElements (and their names) that should be present in a control template for that control
  2. the storyboards (and their names) that should be present.

So this means that a designer/developer who is going to skin (or define a new control template for) the control needs to only look at the control contract to understand what the control expects to function properly.

In WPF, you have to search for the "PART_" prefix in the default control template. There is no well-defined control contract. I understand from the MIX talks that this is one area that WPF, the big brother, will listen to Silverlight and emulate it in future WPF releases.

Also, a custom control in Silverlight cannot inherit from UIElement or FrameworkElement classes though these classes are present (by the way, there is no Visual class in the class hierarchy in Silverlight). Custom controls should extend the Control class, or other classes like TextBox or Button that extend the Control class.

What makes skinning a control (read changing its control template) really complicated now is that at the moment Blend does not support templating. I'm sure that is coming. But until then, your XAML editing skills will be put to the test!

Resources

Dynamic resource references are not available. All are static references.

Data Binding

The following apply to Silverlight 2 Beta 1:

  • No binding the property of a UI control to the property of another UI control. There is no 'ElementName' property in the binding syntax.
  • No 'RelativeSource'
  • No 'OneWayToSource' - who needed it anyway?
  • No UpdateSourceTrigger property. All updates to the source (for two way binding) happen immediately, except in the case of TextBox in which case changes are propogated to the source when the focus is lost.
  • Cannot bind directly to XML data. Need to convert the XML to CLR objects, and then bind to the CLR object.
  • No ReadOnlyObservableCollection. Fortunately, the ObservableCollection is available which is more important.
  • No CollectionView for sorting and filtering.
  • No validation rules.
  • No DataTemplateSelector for dynamically selecting data templates based on data.
  • No XMLDataProvider and ObjectDataProvider.

Commanding

  • Commands and input bindings are not available in Silverlight 2 Beta 1.

These are a few things I observed about Silverlight 2. I plan to keep this list updated so that this will be a ready reference for someone looking for the differences between the WPF and Silverlight 2 model.

Wednesday, April 9, 2008

XAML Tip - How do I escape curly braces?

Ever wondered how you would get a TextBlock to display some text with the curly braces? For example, say you wanted to display the text {Hello}. If you just say, <TextBlock Text="{Hello}"/> you will not get the desired result since the parser will assume that it is a markup extension.

This is what you should do to trick the parser: <TextBlock Text="{}{Hello}"/>

Simple, eh?

Sunday, March 30, 2008

We told you so!

Last December, we at IdentityMine did a Silverlight 1.0 training at Mumbai and Bangalore in which we had a wonderful audience - a total of more than 50 from various companies around India participating. Though the training was primarily on Silverlight, we also covered some 'then-WPF-only concepts' like Styles, Templates and Data Binding.

While most of the feedback was very positive, I still remember a few questioning the rationale behind including these "WPF concepts" in a Silverlight training session. Of course, the reason was, as we mentioned during the course, that these concepts would be included in Silverlight 2 later. And included they are! I'm sure they now realize that they have had a great sneak peek into these important concepts months ahead of the Silverlight 2 Beta release.


We told you so!

Thursday, March 27, 2008

No Triggers in Silverlight 2 Beta!

Being a WPF developer, I have been hoping that the Silverlight model would resemble WPF closely. After all, Silverlight is supposed to be a 'subset' of WPF (remember the code name for Silverlight was WPF/E - 'WPF Everywhere').

Now that Silverlight 2 Beta is out, I have been looking at it very closely. As expected a lot of features are common to WPF - styles, templates, databinding etc. But what on earth happened to triggers? Triggers are missing! C'mon Microsoft don't tell us there are no triggers in Silverlight. I'm hoping that triggers will arrive before the final release of Silverlight.

By the way, let me clarify that when I say that there are no triggers in Silverlight 2 Beta 1 what I mean is no triggers where they matter most - in styles, control templates and data templates. The FrameworkElement class does have a trigger collection, but that is not very useful is it, without triggers in styles and templates?

Update: For a full discussion on the differences between the WPF and Silverlight 2 model, see this post.

Also enjoy this geeky duel on the new Silverlight templating model (sans triggers) between two of my esteemed colleagues at IdentityMine - Robby Ingebretsen and Dr. WPF.