Sunday, September 26, 2010

No programmers in heaven

Pair programming parody


Saturday, September 18, 2010

WPF Circular Progress Control – Part 2

In the previous post (WPF Circular Progress Control – Part 1) I tried to explain about drawing a circular progress control.
In this post we will see how to animate the control so that we get the rotating behavior. Ohh there goes we need animation to rotate the control.
Before we apply the animation, we apply the rotate transform to the path we created earlier, RotateTransform rotates an object by a specified angle. We apply RotateTransform to the RenderTransform of path control, Render transforms are typically intended for animating or applying a temporary effect to an element. 
<Path Stretch="Fill" Name="pathCircular">
      <Path.Data>
            <GeometryGroup>
                  <EllipseGeometry Center="20,20" RadiusX="20" RadiusY="20"/>
                  <EllipseGeometry Center="20,20" RadiusX="15" RadiusY="15"/>
            </GeometryGroup>
      </Path.Data>
      <Path.RenderTransform>
            <TransformGroup>
                  <RotateTransform Angle="0"/>
            </TransformGroup>
      </Path.RenderTransform>
      <Path.Fill>
            <LinearGradientBrush StartPoint="0,0.3" EndPoint="0.3,1">
<GradientStop x:Name="GradientStop1" Color="Black" Offset="0"/>
<GradientStop x:Name="GradientStop2" Color="Transparent" Offset="1"/>
            </LinearGradientBrush>
      </Path.Fill>
</Path>

Now we need to define the animation, we need to animation on ‘pathCircular’ object and its ‘RotateTransform’ property.
We define a DoubleAnimation, which updates the value of a property over a period of time. But this would mean that we will be able to specify the value of the property only once, however what we need is to be able set the value of the property twice after certain time interval. For this we need to define DoubleAnimationUsingKeyFrames.
By using DoubleAnimationUsingKeyFrames not only can we have more than two of target values but we can also control the interpolation method of individual DoubleKeyFrame segments. There are three types of DoubleKeyFrame classes, one for each supported interpolation method: LinearDoubleKeyFrame, DiscreteDoubleKeyFrame, andSplineDoubleKeyFrame.
We use  SplineDoubleKeyFrame, Spline key frames like SplineDoubleKeyFrame create a variable transition between values according to the value of the KeySpline property. Each key frame has a target Value and a KeyTime. The KeyTimespecifies the time at which the key frame's Value should be reached. A key frame animates from the target value of the previous key frame to its own target value. It starts when the previous key frame ends and ends when its own key time is reached.
<Storyboard x:Key="rotateAnimation">
      <DoubleAnimationUsingKeyFrames
            BeginTime="00:00:00"
            Storyboard.TargetName="pathCircular"
            Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"
            RepeatBehavior="Forever">
            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
            <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="360"/>
      </DoubleAnimationUsingKeyFrames>
</Storyboard>

OK the animation is implemented and applied to the control, now we need the events to start and stop the animation. Here we can define a routed event and in XAML we can define trigger to begin and stop the animation.
<UserControl.Triggers>
      <EventTrigger RoutedEvent="local:CircularProgressControl.StopEvent">
            <StopStoryboard BeginStoryboardName="rotateAnimation"/>
      </EventTrigger>
      <EventTrigger RoutedEvent="local:CircularProgressControl.StartEvent">
<BeginStoryboard Name="rotateAnimation" Storyboard="{StaticResource rotateAnimation}"/>
      </EventTrigger>
</UserControl.Triggers>

Now we need to define these events in the code behind.
StartEvent =
    EventManager.RegisterRoutedEvent (
    "StartEvent",
    RoutingStrategy.Direct,
    typeof (RoutedEventHandler),
    typeof (CircularProgressControl));
StopEvent =
    EventManager.RegisterRoutedEvent (
    "StopEvent",
    RoutingStrategy.Direct,
    typeof (RoutedEventHandler),
    typeof (CircularProgressControl));
 
What we can also do is define a custom dependency property , on update of which we can raise start / stop animation events.
/// <summary>
/// Gets or sets a value indicating whether this instance is in progress.
/// </summary>
/// <value>
///   <c>true</c> if this instance is in progress; otherwise, <c>false</c>.
/// </value>
public bool IsInProgress {
    get { return (bool)GetValue (IsInProgressProperty); }
    set { SetValue (IsInProgressProperty, value); }
}

/// <summary>
/// Using a DependencyProperty as the backing store for IsInProgress.
/// This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty IsInProgressProperty =
    DependencyProperty.Register (
        "IsInProgress",
        typeof (bool),
        typeof (CircularProgressControl),
        new UIPropertyMetadata (
            false, new PropertyChangedCallback (OnIsInProgressChanged)));

/// <summary>
/// Called when IsInProgress is changed.
/// </summary>
/// <param name="inSender">The sender.</param>
/// <param name="inEventArgs">The
/// <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance
/// containing the event data.</param>
private static void OnIsInProgressChanged (
    DependencyObject inSender, DependencyPropertyChangedEventArgs inEventArgs) {
    bool newValue = (bool)inEventArgs.NewValue;
    if (newValue) {
        (inSender as CircularProgressControl).RaiseEvent (new RoutedEventArgs (StartEvent));
    }
    else {
        (inSender as CircularProgressControl).RaiseEvent (new RoutedEventArgs (StopEvent));
    }
}

There, we have the control. Now we can use it in other views, bind IsInProgress (Boolean) property with presenter property and it’s done.

Cheers

Thursday, September 16, 2010

WPF Circular Progress Control - Part 1

There are various ways to show progress on status bar, with new trendy ways the user wants to see visual progress not only with the status text but with something more. Here is where WPF animation comes to picture.
I came across such a requirement where the operation is in process has to be notified to the user in terms of black arc rotating, this Is what I termed as Circular Progress Control. Off course circular progress control can be anything where the progress indication is circular (like ‘dancing lights’ arranged in round fashion).
First this was to develop the control which looks like

There are many ways to do this but on higher level there are two,
  1. Either to create an image
  2. Draw the control using WPF (uses vector graphics)

I was thinking to draw the control using path as it would have vector processing but I decided to read pros and cons.  Following is what I understood (source http://msdn.microsoft.com/en-us/library/ms748373.aspx)
Vector Graphics
WPF uses vector graphics as its rendering data format. Vector graphics—which include Scalable Vector Graphics (SVG), Windows metafiles (.wmf), and TrueType fonts—store rendering data and transmit it as a list of instructions that describe how to recreate an image using graphics primitives. For example, TrueType fonts are outline fonts that describe a set of lines, curves, and commands, rather than an array of pixels. One of the key benefits of vector graphics is the ability to scale to any size and resolution.
Unlike vector graphics, bitmap graphics store rendering data as a pixel-by-pixel representation of an image, pre-rendered for a specific resolution. One of the key differences between bitmap and vector graphic formats is fidelity to the original source image. For example, when the size of a source image is modified, bitmap graphics systems stretch the image, whereas vector graphics systems scale the image, preserving the image fidelity.
The following illustration shows a source image that has been resized by 300%. Notice the distortions that appear when the source image is stretched as a bitmap graphics image rather than scaled as a vector graphics image.
Differences between raster and vector graphics


Thus the way to first step was clear, create the control using WPF, I decided to use Path geometry for this. WPF provides a number of ready to use “Shape” objects, Path is one of them. Each Shape object supports
  • Stroke: Describes how the shape's outline is painted.
  • StrokeThickness: Describes the thickness of the shape's outline.
  • Fill: Describes how the interior of the shape is painted.
  • Data properties: to specify coordinates and vertices, measured in device-independent pixels.


Path class enables us to draw complex shapes; we can describe the layout using geometry objects. Thus for our case we needed an arc, a thick arc. We can have two concentric circles and then use LinearGradientBrush to have two colors gradients in it, black and transparent. For all this we need to update Path’s Data and Fill property, such as.
<Path Stretch="Fill" Name="pathCircular">
      <Path.Data>
            <GeometryGroup>
                  <EllipseGeometry Center="20,20" RadiusX="20" RadiusY="20"/>
                  <EllipseGeometry Center="20,20" RadiusX="15" RadiusY="15"/>
            </GeometryGroup>
      </Path.Data>
      <Path.Fill>
            <LinearGradientBrush StartPoint="0,0.3" EndPoint="0.3,1">
<GradientStop x:Name="GradientStop1" Color="Black" Offset="0"/>
<GradientStop x:Name="GradientStop2" Color="Transparent" Offset="1"/>
            </LinearGradientBrush>
      </Path.Fill>
</Path>

Here in the path geometry we drew two circles using EcllipseGeometry which are concentric: this becomes the data for Path object. Now in order to get black and transparent color gradients we ‘Fill’ it construct a LinearGradientBrush with the colors and specify the start point and end point.

After this what we get is something like this,


So that’s it we have got the control we needed using vector graphics.
To animate it, in next post.

Monday, September 13, 2010

WPF DataGrid columns are not part of visual tree

Sometime we have a requirement where 2 columns out of total 4 had to be hidden based on the presenter logic.
As per the “usual” WPF knowledge I bound the Visibility property of Datagrid column property to presenter property which threw an exception “System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element”.
This led to suspicion that the column is not able to inherit Datagrid’s datacontext, we tried binding using relative source and find ancestor but nothing seemed to work. It is then that it clicked that Datagrid columns are not part of Datagrid visual tree.
So how do we achieve the desired behavior? There are 3 ways I have tried to solve it,
  1. Attached property behavior:
    Create a Dependency property; bind this dependency property with the presenter property. Override the property metadata of the dependency property with attaching a function to be called whenever the property’s source is changed. In this function we can do operation on the columns.
  2. Reverse the binding direction:
    Up until now we have considered presenter as source and view as target. But we can also make presenter as target and view as source, like to solve the above problem we can do binding at runtime for the datagrid columns with column’s Visibility as source and presenter’s property as target and two way binding.
  3. Forward the Datagrid’s datacontext to its columns:
    Using the WPF property system we can add Framework.DataContext property to DataGridColumn,and then override Datagrid’s DataContext metadata to listen for its changes. In the callback method we can then set the DataContext (one we got by adding before) of each column with the new DataContext.

Sunday, September 12, 2010

WPF DataGrid and grouping issues

In software applications there are numerous places where we use WPF Datagrid and applying grouping which is dependent on presenter logic. 

Ideally as per MVP architecture what we do is that we change the source (presenter) property with another record which is bound to target’s (view / in this case mostly DataGrid) ItemsSource. Now changing the ItemsSource member of the datagrid with another value or null does not update its associated grouping and group descriptions which in turns gives an exception that “The specified Visual is not an ancestor of this Visual”. 


One of the workaround for this is that whenever we change the presenter bound ItemsSource we should update the grouping as well.