Tuesday, April 26, 2011

Building a Jounce RegionAdapter with the Accordion Control

Natively, Jounce supports two types of Region containers, the ContentControl and the ItemsControl.  You can add your own containers by inheriting from RegionAdapterBase.  In this post, I will show you how you can build a RegionAdapter using the Accordion control (from the Silverlight Toolkit) as a container:

Step 1.  Add a class that inherits from RegionAdapterBase<Accordion> and implement the required ActivateControl override.

Jounce.Regions.Adapters.RegionAdapterBase<System.Windows.Controls.Accordion>

[RegionAdapterFor(typeof(Accordion))] 
public
class AccordionRegionAdapter : RegionAdapterBase<Accordion> { public override void ActivateControl(string viewName, string targetRegion) { } }

Step 2.  Implement ActivateControl.  This step involves the following process.  First, you need to validate the region name and control name provided in the parameters of the call.  Jounce has two helper methods that make the validation an easy task.  The next step is to determine if we have already added the view to our region.  We can do this simply by maintaining an internal list of views.  If the view is already added, we simply make that view the selected view in our region.  If the view has not been previously added, we will go through the process of adding the view to our region (outlined in Step 3).

Step 3. In order to add our view to the region, we will need more information than the view itself.  This type of region has a header for each child view.  Luckily for us, when exporting a view, there is a parameter called MenuName.  We can use this parameter to inform the RegionManagerAdapter how we would like to title our view:

    [ExportAsView(typeof(CitiesView), MenuName = "Cities", IsShell = false)]

So in our RegionAdapter, we use MEF to bring in all of the views along with the associated metadata:

[ImportMany(AllowRecomposition = true)]
public Lazy<UserControl, IExportAsViewMetadata>[] Views { get; set; }
And then, when adding our views to the region, we simply match the viewName to obtain the MenuName.  Finally, this name is then applied to the Header property of the AccordionItem control.

private
void InitializeView(ItemsControl region, string viewName) { var metadata = (from v in Views where v.Metadata.ExportedViewType.Equals(viewName) select v.Metadata).FirstOrDefault(); var title = metadata == null ? viewName : metadata.MenuName; var accordionItem = new AccordionItem { Header = title, Content = Controls[viewName], HorizontalContentAlignment = HorizontalAlignment.Stretch, VerticalContentAlignment = VerticalAlignment.Stretch }; region.Items.Add(accordionItem); }

For your convenience, the complete RegionAdapter is defined below:

[RegionAdapterFor(typeof(Accordion))]
public class AccordionRegionAdapter : RegionAdapterBase<Accordion>
{
    private readonly List<string> _views;

    public AccordionRegionAdapter()
    {
        _views = new List<string>();
    }

    [ImportMany(AllowRecomposition = true)]
    public Lazy<UserControl, IExportAsViewMetadata>[] Views { get; set; }

    public override void ActivateControl(string viewName, string targetRegion)
    {
        ValidateRegionName(targetRegion);
        ValidateControlName(viewName);

        var region = Regions[targetRegion];

        if (!_views.Contains(viewName))
        {
            _views.Add(viewName);
            InitializeView(region, viewName);
        }

        region.SelectedIndex = _views.IndexOf(viewName);
    }

    private void InitializeView(ItemsControl region, string viewName)
    {
        var metadata = (from v in Views
                        where v.Metadata.ExportedViewType.Equals(viewName)
                        select v.Metadata).FirstOrDefault();

        var title = metadata == null ? viewName : metadata.MenuName;

        var accordionItem = new AccordionItem
        {
            Header = title,
            Content = Controls[viewName],
            HorizontalContentAlignment = HorizontalAlignment.Stretch,
            VerticalContentAlignment = VerticalAlignment.Stretch
        };

        region.Items.Add(accordionItem);
    }
}

Once the region adapter has been constructed, we can assign a region to an instance of the Accordion control in one of our views:

    <Grid x:Name="LayoutRoot">
        <layoutToolkit:Accordion Regions:ExportAsRegion.RegionName="MainRegion"
                                 Width="400"
                                 Height="200" />
    </Grid>

Any time we export a view that targets this region, the view will appear as a child in the accordion control.  An example of the cities view follows:

[ExportAsView(typeof(CitiesView), MenuName = "Cities", IsShell = false)]
[ExportViewToRegion(typeof(CitiesView), "MainRegion")]
public partial class CitiesView : UserControl
{
    public CitiesView()
    {
        InitializeComponent();
    }
}

Finally, we can make this view appear by publishing the view using Jounce’s Event Aggregator:

EventAggregator.Publish(typeof(CitiesView).FullName.AsViewNavigationArgs());

image

That’s all there is to it!  I’ve posted a sample project if you would like to see more details.  Enjoy!

Tuesday, March 01, 2011

A Simple Focus Manager for Jounce Applications

When a view loads, it’s ideal to be able to set focus on a particular control so the user won’t need to actually click or tab to the control to start data entry. This is not an unreasonable expectation for any Silverlight application that has any sort of data entry. Normally, a FocusManager would handle this task, but in Silverlight, the FocusManager is trimmed down and there is no support for Setting Focus from the XAML side of things. Obviously, this is a UI related task and it makes a lot of sense to keep this responsibility as close to the UI as possible.

If you are using Jounce, it’s really easy to build a control that can tap into the messaging infrastructure (IEventSink<ViewNavigatedArgs>) so that when a view loads, a control of choice obtains a default focus.

So what would such an implementation look like? As I mentioned before, I’d like to keep this logic as close to the view as possible and at the same time, it needs to be easy to implement on multiple views. In the code snippet below, I’ve defined what I will call a JounceFocusManager.

<StackPanel x:Name="LayoutRoot" Background="White">
    <TextBox x:Name="Box1" Text="Box 1" />
    <TextBox x:Name="Box2" Text="Box 2" />
    <TextBox x:Name="Box3" Text="Box 3" />
    <TextBox x:Name="Box4" Text="Box 4" />
    <TextBox x:Name="Box5" Text="Box 5" />
    <local:JounceFocusManager ViewName="Page1" TargetControl="Box2" />
</StackPanel>

This particular snippet of code would cause the TextBox named Box2 to gain focus any time the view is activated.

As you can see, the JounceFocusManager requires a ViewName and a TargetControl. Using this information, the JounceFocusManager will subscribe to any ViewNavigatedArgs messages that are broadcasted. When it encounters a message that matches the view specified by ViewName, it will traverse the Visual Tree for a matching control specified by TargetControl. If it finds a match, it will call the Focus method for that particular control. It’s kind of a long-winded approach, but it gets the job done and it plays nicely with the MVVM pattern.

Below is the implementation of the JounceFocusManager:

public class JounceFocusManager : FrameworkElement, IEventSink<ViewNavigatedArgs>
{
    [Import]
    public IEventAggregator EventAggregator { get; set; }

    public JounceFocusManager()
    {
        if (!DesignerProperties.IsInDesignTool)
        {
            CompositionInitializer.SatisfyImports(this);
            EventAggregator.Subscribe(this);
        }
    }

    public static readonly DependencyProperty TargetControlProperty =
        DependencyProperty.Register("TargetControl", typeof(string),
        typeof(JounceFocusManager), null);

    public string TargetControl
    {
        get { return (string)GetValue(TargetControlProperty); }
        set { SetValue(TargetControlProperty, value); }
    }

    public static readonly DependencyProperty ViewNameProperty =
        DependencyProperty.Register("ViewName", typeof(string),
        typeof(JounceFocusManager), null);

    public string ViewName
    {
        get { return (string)GetValue(ViewNameProperty); }
        set { SetValue(ViewNameProperty, value); }
    }

    public void HandleEvent(ViewNavigatedArgs publishedEvent)
    {
        if (publishedEvent.ViewType.Equals(ViewName) && TargetControl != null)
        {
            Control control = 
                FindParent<UserControl>(this).FindName(TargetControl) as Control;
            if (control != null)
            {
                var txtBox = control as TextBox;
                if (txtBox != null)
                {
                    txtBox.Select(0, txtBox.Text.Length);
                }

                control.Focus();
            }
        }
    }

    public static T FindParent<T>(DependencyObject obj) where T : DependencyObject
    {
        while ((obj = VisualTreeHelper.GetParent(obj)) != null)
        {
            T p = obj as T;
            if (p != null)
            {
                return p;
            }
        }

        return null;
    }
}

There are some improvements that can be made on this control. First, instead of requiring the ViewName to be specified. We could use a ViewModelRouter to “look-up” the current view name for which the control instance currently resides. An enhancement like this make your view logic slightly less fragile in the event that you renamed your view and forgot to update the name in the FocusManager. Second, I my implementation has some additional logic for the case where the control is a TextBox. In that scenario, I select all of the text in addition to setting focus. This is a nice addition because it allows the user to just begin typing without having to select any text. It would be really easy to make this a configurable option.

image

Click here to see a live demo

Click here to download the source code

Friday, February 25, 2011

Please Vote for my CodeStock 2011 Speaker Submission

This year I’ve submitted a session titled The Silverlight Control Model to CodeStock 2011 on June 3rd - 4th.  If you are planning on attending (which you should), please vote for my session!  See you there!

codestock_lowres

43341e84-6729-4a20-9464-144be82ba4ee.jpg

The Silverlight Control Model

Track / Area:
Developer / Applications - Silverlight

Technology:
Silverlight

General / Specific Experience Level:
Beginner / Beginner

Start Time / Length
/ 70 min

Silverlight has a very powerful control model which you can use to achieve modularity in your applications. With so many options at hand, it can be difficult to know where to start. In this presentation, we will discover some of the differences between User Controls and Custom Controls and how to implement each type in your applications.

Thursday, February 24, 2011

Tiled Background Control in Silverlight

Need a tiled background control in your Silverlight application?  Unfortunately, there is no native tile control in Silverlight. However, you can easily obtain the source code for one if you install the JetPack theme.  Sure, there a few steps, but at least you don’t have to write it yourself!  A special thanks goes out to Tim Heuer for the tip!

1. Download the JetPack theme from Microsoft (SL4Themes-templates.zip).

2. Extract the zip archive and install the JetPack.vsix

image

3. Restart Visual Studio and create a new Silverlight application.

SilverlightApp

4.  Find the TiledBackground control in the Solution Explorer.

image

5.  Using the control is simple. 

<controls:TiledBackground SourceUri="/BusinessApplication1;component/Images/grass-texture.jpg" />

tiled
Tile Image from TutorialMagazine.com

Friday, February 18, 2011

Building a Radar Control in Silverlight–Part 2

In Part 1 of this series, I built the foundation for the Radar control.  In this post, I will enhance the control by adding blips to the Radar screen.  The diagram below illustrates the key players for this part of the series.

image

In the true spirit of a radar, we only want to update the blips when the arm passes over the individual blips.  This responsibility is delegated to the RadarBlipUpdateEventAggregator.  Since the arm is moving quite fast, there would be quite a few calls to notify.  To reduce this overhead, calls to notify only happen at a certain frequency.  In other words, each time Notify is called, the radar control fires Notify with a previous angle and a current angle:

if (_angle % 5 == 0)
{
    _updateAggregator.Notify(_previousAngle, _angle, _center, _passSerial);
    _previousAngle = _angle;
}

The RadarBlipUpdateEventAggregator will query each blip for their current angle (using GetAngleFromCenter) and if the angle falls between _previousAngle and _angle, the aggregator will call UpdatePosition on RadarBlipControl.

UpdatePosition on RadarBlipControl uses a BlipMovementFunction to determine what it’s new Location should be.  In the diagram above, we have a LinearBlipMovementFunction that has the ability to move a blip in a specific direction at a configurable velocity.  We can easily define other types of BlipMovementFunctions that do other cool things.  For example, you could define a center-seeking function that causes the blip to move towards the center. To see how the MovementFunction is used, I’ve included the UpdatePosition method on RadarBlipControl below:

public void UpdatePosition(int passSerial, Point center)
{
    // Only update positions once per sweep.
    if (passSerial == _passSerial)
    {
        return;
    }

    if (!DesignerProperties.IsInDesignTool)
    {
        Location = MovementFunction.UpdatePosition(center, Location);
    }

    _passSerial = passSerial;
    VisualStateManager.GoToState(this, "Default", true);
    VisualStateManager.GoToState(this, "RadarPass", true);
}

Notice how I am using the VisualStateManager to go from the Default state to the RadarPass state immediately?  This is a little trick to simulate an auto state revert.  Each time the radar arm passes over the blip, the blip control will go to the default state (which is a no-op) and then to the RadarPass state.  This is important because the VisualStateManager won’t go into a state if it is already in that state.  The RadarPass state is a simple flash and decay effect, see below:

<VisualState x:Name="Default" />
<VisualState x:Name="RadarPass">
    <Storyboard>
        <DoubleAnimationUsingKeyFrames AutoReverse="True" 
                        Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" 
                        Storyboard.TargetName="Blip">
            <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
            <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="2"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames AutoReverse="True" 
                        Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)" 
                        Storyboard.TargetName="Blip">
            <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
            <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="2"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Blip">
            <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
            <EasingDoubleKeyFrame KeyTime="0:0:9" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>

I skipped over a lot of neat features of this control.  In a future post, I will go over more aspects of the Radar control.  In the meantime, you can check out a live demo of the latest version.  Also, be sure to check out the source code!

image

Click here to see a live demo

Click here to download the source code

Sunday, January 23, 2011

Clipping to Bounds with Geometries and Attached Behaviors

While working on my Radar control, I found that I had the need to clip the contents of a rectangular Panel by a bounding ellipse.  In Silverlight, that’s a very trivial task:

<Canvas Background="DarkBlue" Width="100" Height="100">
    <Canvas.Clip>
        <EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50" />
    </Canvas.Clip>
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="0" Canvas.Left="0" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="10" Canvas.Left="10" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="20" Canvas.Left="20" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="30" Canvas.Left="30" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="40" Canvas.Left="40" />
</Canvas>

image

The XAML above clips every child inside the Canvas that is not contained by the EllipseGeometry.  But it’s a very common case that you won’t know the dimensions of the containing Canvas until runtime.  Even worse, your canvas dimensions may change multiple times during runtime.

We need some way to update the EllipseGeometry during runtime.  Sure, you could easily write some one-off code to do this, but it’s better if we can encapsulate the logic so that it can be re-used.  This is where Attached Behaviors come in handy.

Simply put, an attached behavior is a way for you to attach additional logic to an element.  In our example above, we need to adjust the values for the EllipseGeometry to be appropriate for the dimensions of its containing element. 

Colin Eberhardt has a nice article that is very close to what we need.  So instead of starting from scratch, let’s enhance his logic to do a little more.  Instead of simply clipping using a rectangle geometry when the attached behavior is applied, it would be nice if we could specify the geometry type.  Something like this would be really cool:

<Canvas Background="DarkBlue" Width="100" Height="100" local:AutoClip.Geometry="Ellipse">
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="0" Canvas.Left="0" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="10" Canvas.Left="10" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="20" Canvas.Left="20" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="30" Canvas.Left="30" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="40" Canvas.Left="40" />
</Canvas>

With our version (which I will dub AutoClip), we can specify different geometry types.  When the containing control is initialized or if the control’s size changes, we will update the clipping geometry. Below you will find the code for the AutoClip attached property:

public class AutoClip
{
    public enum GeometryType { Rectangle, Ellipse, Diamond, None };

    public static GeometryType GetGeometry(DependencyObject depObj)
    {
        return (GeometryType)depObj.GetValue(GeometryProperty);
    }

    public static void SetGeometry(DependencyObject depObj, GeometryType clipToBounds)
    {
        depObj.SetValue(GeometryProperty, clipToBounds);
    }

    public static readonly DependencyProperty GeometryProperty =
        DependencyProperty.RegisterAttached("Geometry", typeof(GeometryType),
        typeof(AutoClip), new PropertyMetadata(GeometryType.None, OnGeometryPropertyChanged));

    private static void OnGeometryPropertyChanged(DependencyObject d, 
DependencyPropertyChangedEventArgs e) { FrameworkElement fe = d as FrameworkElement; if (fe != null) { ClipToGeometry(fe); fe.Loaded += (s, args) => ClipToGeometry(s as FrameworkElement); fe.SizeChanged += (s, args) => ClipToGeometry(s as FrameworkElement); ; } } private static void ClipToGeometry(FrameworkElement fe) { var type = GetGeometry(fe); switch (type) { case GeometryType.Ellipse: fe.Clip = new EllipseGeometry { Center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2), RadiusX = fe.ActualWidth / 2, RadiusY = fe.ActualHeight / 2 }; break; case GeometryType.Rectangle: fe.Clip = new RectangleGeometry { Rect = new Rect(0, 0, fe.ActualWidth, fe.ActualHeight) }; break; case GeometryType.Diamond: PathGeometry p = new PathGeometry(); PathFigure pf = new PathFigure(); pf.IsClosed = true; pf.StartPoint = new Point(fe.ActualWidth/2, 0); pf.Segments.Add(new LineSegment {Point=new Point(fe.ActualWidth/2, 0)}); pf.Segments.Add(new LineSegment {Point=new Point(fe.ActualWidth, fe.ActualHeight/2)}); pf.Segments.Add(new LineSegment {Point=new Point(fe.ActualWidth/2, fe.ActualHeight)}); pf.Segments.Add(new LineSegment {Point=new Point(0, fe.ActualHeight/2)}); p.Figures.Add(pf); fe.Clip = p; break; default: fe.Clip = null; break; } } }

Note that we can easily add new geometries to our attached behavior and have a nice separation of concerns.  Here is an example that uses the diamond geometry:

<Canvas Background="DarkBlue" Width="100" Height="100" local:AutoClip.Geometry="Diamond">
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="0" Canvas.Left="0" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="10" Canvas.Left="10" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="20" Canvas.Left="20" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="30" Canvas.Left="30" />
    <Ellipse Fill="LightBlue" Width="10" Height="10" Canvas.Top="40" Canvas.Left="40" />
</Canvas>

image

Wednesday, January 12, 2011

Building a Radar Control in Silverlight - Part 1

Introduction

submarineI had an idea recently about building a Silverlight Radar control; you know, similar to the kind you see in Submarine movies. Such a control could have a variety of applications from displaying incoming opponents in a game to helping you visualize the relative locations of free WiFi access points (assuming you had the GPS data available for making such calculations). 

In this series of posts, I will walk through my journey of building this control.  Along the way, I’ll document some of the more interesting aspects of the control.  Let’s get started!

Building the Radar Grid and Line

I usually start off by sketching out my idea on paper so that I have something to look at.  It’s not anything fancy, but it works for me. 

sketch

So looking at my sketch, the most characteristic part of the radar control will be the fluid gradient that will rotate about the center point. 

There are different ways you could achieve this effect, but maybe the easiest way would be to take advantage of the CompositionTarget.Rendering event.  This event basically gives you a hook to make changes right before objects are rendered for each frame.

One of my favorite examples of this effect was the Fluid Dynamics demo built by Rick Barraza.

So, in incremental fashion, let’s write some code that will rotate a line about a single point, using the CompositionTarget.Rendering event.

First, some XAML.  Our initial control template will look like the following:

<Style TargetType="local:Radar">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:Radar">
                <Grid x:Name="LayoutRoot" Background="Black">
                    <Line x:Name="RadarLine" 
                            Height="2"
                            Width="150" 
                            Margin="150,0,0,0" 
                            X1="0" X2="300" 
                            Stroke="Red" 
                            StrokeThickness="2" />
                    <Image x:Name="ImgOut" Stretch="Fill" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Obviously, RadarLine is the line that will rotate about the center point.  Less obviously, ImgOut will be used to render a composition of frames.  On each call to CompositionTarget_Rendering, the angle will be incremented and a rotation transformation will be applied to the line.  We then clear the contents of ImgOut by drawing a black rectangle and then the line.

Let’s look at the code in the Compositiontarget.Rendering event:

public override void OnApplyTemplate()
{
    _layoutRoot = GetTemplateChild("LayoutRoot") as Panel;
    _imgOut = _layoutRoot.FindName("ImgOut") as Image;
    _radarLine = _layoutRoot.FindName("RadarLine") as Line;

    _writableBitmap = new WriteableBitmap((int)this.Width, (int)this.Height);
    _imgOut.Source = _writableBitmap;

    CompositionTarget.Rendering += CompositionTarget_Rendering;

    base.OnApplyTemplate();
}

void CompositionTarget_Rendering(object sender, System.EventArgs e)
{
    _angle += 1;
    if (_angle > 360)
    {
        _angle = 0;
    }

    // Rotate the line.
    CompositeTransform ct = new CompositeTransform
                                {
                                    Rotation = _angle,
                                    TranslateY = this.Height / 2,
                                    TranslateX = this.Width / 2
                                };

    Rectangle blackRectangle = new Rectangle
                                    {
                                        Width = this.Width,
                                        Height = this.Height,
                                        Fill = new SolidColorBrush(Colors.Black)
                                    };

    _writableBitmap.Invalidate();

    // Hide the previous frame.
    _writableBitmap.Render(blackRectangle, null);

    // Render the line at a new angle.
    _writableBitmap.Render(_radarLine, ct); 
}

This code results in a rotating line.  Not very exciting, is it?
A few small tweaks to the code will give us a much more dramatic result.  Instead of rendering a solid black rectangle on each call, we can render a rectangle with that is only subtly opaque.  Adding a Blur effect to the image will smooth out the lines and cause the older frames to progressively fade out.

Radar-line

The code to achieve this is included below:

<Style TargetType="local:Radar">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:Radar">
                <Grid x:Name="LayoutRoot" Background="Black">
                    <Line x:Name="RadarLine" 
                            Height="2"
                            Width="150" 
                            Margin="150,0,0,0" 
                            X1="0" X2="300" 
                            Stroke="Red" 
                            StrokeThickness="2" />
                        <Image x:Name="ImgOut" Stretch="Fill">
                            <!-- Blur Effect -->
                            <Image.Effect>
                                <BlurEffect Radius="5" />
                            </Image.Effect>
                        </Image>
                    </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
void CompositionTarget_Rendering(object sender, System.EventArgs e)
{
    _angle += 1;
    if (_angle > 360)
    {
        _angle = 0;
    }

    // Rotate the line.
    CompositeTransform ct = new CompositeTransform
                                {
                                    Rotation = _angle,
                                    TranslateY = this.Height / 2,
                                    TranslateX = this.Width / 2
                                };

    Rectangle blackRectangle = new Rectangle
                                    {
                                        Width = this.Width,
                                        Height = this.Height,
                                        Fill = new SolidColorBrush(Color.FromArgb(2, 0, 0, 0))
                                    };

    _writableBitmap.Invalidate();

    // Render the previous frame.
    _writableBitmap.Render(_imgOut, null);

    // Fade the previous frame.
    _writableBitmap.Render(blackRectangle, null);

    // Render the line at a new angle.
    _writableBitmap.Render(_radarLine, ct); 
}

Finally, we can add the concentric ellipses and quadrants to complete the effect.

radar-red

Click here to see a live demo

Click here to download the source code

Conclusion

In Part 1 of this series, we built the foundation for this control.  In the next part of the series, we will enhance the control by adding the ability to customize various visual aspects.  Additionally, we will add blips that will refresh on each pass of the radar line.  Stay tuned!

Sunday, January 09, 2011

Custom Content Properties in Silverlight

Sometimes features get added to Silverlight without much fanfare.  One of those little hidden features is the ContentPropertyAttribute.  While this feature was added in Silverlight 3, I just noticed that it was there while working on a new control. 

Suppose you are building a control that contains a collection of items. In Silverlight 2, you had to do something like this:

    <PB:Radar x:Name="MyRadar">
        <PB:Radar.Blips>
            <PB:BlipCollection>
                <PB:RadarBlip Location="150,100" />
                <PB:RadarBlip Location="10,20" />
                <PB:RadarBlip Location="190,190" />
            </PB:BlipCollection>
        </PB:Radar.Blips>
    </PB:Radar>

In Silverlight 3+, with the ContentPropertyAttribute, you can omit the explicit Blips and BlipCollection elements and simply inject the children as the content for the Radar element.

    <PB:Radar x:Name="MyRadar">
        <PB:RadarBlip Location="150,100" />
        <PB:RadarBlip Location="10,20" />
        <PB:RadarBlip Location="190,190" />
    </PB:Radar>
              

Notice, the arbitrary number of RadarBlip clildren?  To enable this from your control, simply decorate your control with the ContentProperty attribute.

...  
    [ContentProperty("Blips")]
    public class Radar : Control
    {
       ...
       ...

The name Blips matches the property on the Radar control which happens to be of type BlipCollection:

        
public static DependencyProperty BlipsProperty = DependencyProperty.Register(
"Blips",
typeof(BlipCollection),
typeof(Radar),
null);

public BlipCollection Blips
{
   get { return (BlipCollection)GetValue(BlipsProperty); }
   set { SetValue(BlipsProperty, value); }
}

Simple huh? 

Sunday, March 21, 2010

Custom Splash Screen for Windows Phone

The WP7 platform has native support for static splash screens.  In the following post, I’ll show you how just how easy it is:

  • Create an image of size 480 x 800 in your favorite image editor.
  • Save the image as a JPEG. At the moment it looks like the file type must be a JPEG to work.

    image 

  • Include the image in your solution and set the Build Action as Content.

    image  image

     
  • Run the application and TaaDaa!  The splash screen will display for a brief period while the application loads.

image

I've also posted a video tutorial of this if you would like to see the splash screen in action.

Saturday, October 24, 2009

Performance Improvement Tip for Large Projects in Visual Studio

While working on solutions in Visual Studio, it is often the case that you have many projects contained within the solution.  Even on fast machines, Visual Studio performance can start to drag when you begin working on the solution.  In my past experience, I have found that I typically work on only a few projects at a time.  I found that unloading the  unnecessary projects can help to conserve memory.  So how do you unload a project from a solution in Visual Studio?  Simple, just right-click on the project name and click Unload Project.

image

The unloaded projects will appear grayed out along with the text (unavailable).

image

This trick also works well if your code is under source control.  As far as I can tell, the unload/load state of any given project is stored in the corresponding *.SUO file for a solution so this change won’t be tracked by your source control client.

If you are a ReSharper user (and you should be), you know that ReSharper can consume lots of memory while indexing the various symbols.  When you unload a project, ReSharper will ignore files from these projects.  I also find this particularly useful because files with similar names will no longer show up in your quick search lists.

This tip is fairly trivial, but I have never once seen someone do this in practice.  I hope this helps!