MVVM Light Telling the View to play Storyboards

Sometimes you want to tell the view to play an animation (Storyboard). One simple way to do this is to define a StartStoryboardMessage class, populate this with the name of a Storyboard to play, then send it to the Messenger.

public class StartStoryboardMessage
{
    public string StoryboardName { getset; }
    public bool LoopForever { getset; }
}

In the viewmodel when you want to tell the view to play an animation:

Messenger.Default.Send(new StartStoryboardMessage { StoryboardName = "TimerFinAnimation",LoopForever=true });

The view (i.e. in the code-behind) registers for these messages:

Messenger.Default.Register<StartStoryboardMessage>(this, x => StartStoryboard(x.StoryboardName, x.LoopForever));

...

private void StartStoryboard(string storyboardName, bool loopForever)
{
    var storyboard = FindName(storyboardName) as Storyboard;
    if (storyboard != null)
    {
        if (loopForever) 
            storyboard.RepeatBehavior = RepeatBehavior.Forever;
        else
            storyboard.RepeatBehavior = new RepeatBehavior(1);
        storyboard.Begin();
    }
}

SHARE:

MVVM Light Messenger Action Executing Multiple Times

With MVVM Light you can get into a situation where you code-behind's message handler gets called multiple times.

If the ctor for the view is registering for a message then the every time the view loads another subscription will be added; then when the message is sent the are effectively 2 'listeners' which end up executing the registered Action method multiple times.

One solution to this is to make sure you un-register when the view unloads.

The example below shows the code-behind for a simple settings page that registers for a DialogMessage in the ctor, and un-registers when the page is done with.

    public partial class Settings : PhoneApplicationPage
    {
        public Settings()
        {
            InitializeComponent();
            Messenger.Default.Register<DialogMessage>(this, DialogMessageHandler);
        }
        private void DialogMessageHandler(DialogMessage message)
        {
            var result = MessageBox.Show(message.Content, message.Caption, message.Button);
            
            message.ProcessCallback(result);
        }
        private void PhoneApplicationPage_Unloaded(object sender, RoutedEventArgs e)
        {
            Messenger.Default.Unregister(this);
        }
    }

SHARE:

Cleaner Code in Unit Tests

One thing that can quickly become messy when writing unit tests is the creation of test objects. If the test object is a simple int, string, etc then it's not as much of a problem as when you a list or object graph you need to create.

Even using object initializers you can end up taking a lot of lines of indented code, and gets in the way of the tests.

One solution is to use a 'builder' class which will construct the object for you.

For example, rather than lots of initializer code you could write:

            _sampleData = new HistoryBuilder()
                .WithTimer(false11new DateTime(200011))
                .WithTimer(false11new DateTime(200011))
                .WithTimer(true11new DateTime(200011))
                .Build()

 You can multiple overloads of WithTimer (for example one which create adds a default Timer).

 Implementation of HistoryBuilder:

    public class HistoryBuilder
    {
        private readonly History _history;
        public HistoryBuilder()
        {
            _history = new History();
        }
        public HistoryBuilder WithTimer()
        {
            _history.Timers.Add(new Timer());
            return this;
        }
        public HistoryBuilder WithTimer(bool completed, int internalInteruptions, int externalInteruptions,
                                        DateTime time)
        {
            _history.Timers.Add(new Timer
                                    {
                                        Completed = completed,
                                        InternalInteruptionsCount = internalInteruptions,
                                        ExternalInteruptionsCount = externalInteruptions,
                                        StartedTime = time
                                    });
            return this;
        }
        public History Build()
        {
            return _history;
        }
    }

If you want to fill in the gaps in your C# knowledge be sure to check out my C# Tips and Traps training course from Pluralsight – get started with a free trial.

SHARE:

MVVM Light Messenger Events Firing Multiple Times

If you register for a message in the ctor of your views code-behind, eg:

Messenger.Default.Register<DialogMessage>(this, DialogMessageHandler);

Every time you reload the view, i.e by navigating to it, the callback method (in this example DialogMessageHandler) can get called multiple times from the previous times ctor ran.

To fix this all you need to do create an event handler for the PhoneApplicationPage Unloaded and unregister the message (you can unregister more specifically using one of the other overloads):

Messenger.Default.Unregister(this);

SHARE:

Comparing 2 Locations in Windows Phone 7

If you are using Microsoft.Phone.Controls.Maps.Platform.Location in your WP7 application and you want to see if 2 Locations refer to the same place (i.e. the same altitude, latitude and longitude are all equal) then using Location.Equals or == will not work.

This is because Location inherits from Object and doesn't override Equals method, which results in a simple reference equality check.

The extension method below can be used instead:

using Microsoft.Phone.Controls.Maps.Platform;
namespace DontCodeTired
{
    public static class LocationExtensionMethods
    {
        public static bool IsSamePlaceAs(this Location me, Location them)
        {
            return me.Latitude == them.Latitude &&
                   me.Longitude == them.Longitude &&
                   me.Altitude == them.Altitude;
        }
    }
}

 

Example usage:

    [TestFixture]
    public class LocationExtensionMethodsTest
    {
        [Test]
        public void ShouldRecogniseWhenTwoLocationsReferToTheSamePlace()
        {
            var l1 = new Location {Latitude = double.MinValue, Longitude = double.MaxValue};
            var l2 = new Location { Latitude = double.MinValue, Longitude = double.MaxValue };
            Assert.IsTrue(l1.IsSamePlaceAs(l2));
        }
        [Test]
        public void ShouldRecogniseWhenTwoLocationsReferToDifferentPlacess()
        {
            var l1 = new Location { Latitude = double.MinValue - double.MinValue, Longitude = double.MaxValue };
            var l2 = new Location { Latitude = double.MinValue, Longitude = double.MaxValue };
            Assert.IsFalse(l1.IsSamePlaceAs(l2));
        }
    }

 

 

SHARE:

URL Routing in ASP.Net 4 Web Forms

ASP.Net 4 simplifies the routing experience introduced in ASP.NET 3.5 SP1. Routing allows the use of more meaningful or 'friendly' URLs (which may also benefit search engine ranking).

For example, rather than .../ShowCountryDetails.aspx?country=australia routing could be enabled so the url looked like .../countries/australia

Defining Routes

You can define routes using the MapPageRoute method of the RouteCollection class. The code below is an extract from Global.asax.cs, the method RegisterRoutes is called from application start, and it's here that a route is registered:

protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}



void RegisterRoutes(RouteCollection routes)
{
    /*  Create a routing which maps SayHello to ShowGreeting.aspx
        and defines 2 parameters, greeting and name. */
    routes.MapPageRoute("ShowGreetingRoute",
        "SayHello/{greeting}/{name}",
        "~/ShowGreeting.aspx");
}
 

Accessing URL Param Values In Markup

When routing is in use, RouteValue expressions can be used in markup:

<asp:Literal Text="<%$RouteValue:greeting%>" runat="server" />

Accessing URL Param Values In Code

In code we can use Page.RouteData to get the values of any routed URL parameter values; we can either check for a null before calling ToString() or as in the following example use ContainsKey to check first (in practice you wouldn't use 'magic' string literals for "greeting", rather you'd define these as constants, etc.):

string greetingText = "no greeting text specified";
       
if (Page.RouteData.Values.ContainsKey("greeting"))
        greetingText = Page.RouteData.Values["greeting"].ToString();

Creating Routed URLs in Markup

We can create routed URLs in markup by using RouteUrl expressions:

<asp:HyperLink ID="declarativeLinkToRoutedPage"
    NavigateUrl="<%$RouteUrl:greeting=bonjour, name=Bob, routename=ShowGreetingRoute%>"
    Text="Say hello to Bob in French" runat="server" />

Creating Routed URLs in Code

Instead of setting NavigateUrl using RouteUrl in markup, we could set it in code RouteValueDictionary & VirtualPathData:

/// <summary>
/// Example of setting a Hyperlink's NavigateUrl in code
/// </summary>
private void SetHyperlinkNavigateUrlProperty()
{
    RouteValueDictionary routeParamValues = new RouteValueDictionary
    {
        {"greeting", "goodnight"},
        {"name", "Fred"}
    };

    VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(null, routeParamValues);

    linkSetInCodeToRoutedPage.NavigateUrl = vpd.VirtualPath;
}

Using Routed Values as Parameters in Bound Data Sources

We can define RouteParameter in a data source that is being bound to.

The following markup shows a DetailsView being bound to an ObjectDataSource. The SelectParameters collection defines a RouteParameter which will pass the value of the 'name' URL routed parameter to the GetPersonsAge method of the MockDataBase class.

<asp:ObjectDataSource ID="odsPersonsAge" runat="server"
    SelectMethod="GetPersonsAge" TypeName="ASPNET4Routing.MockDataBase">
    <SelectParameters>
        <asp:RouteParameter Name="name" RouteKey="name" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>
      
<asp:DetailsView ID="DetailsView1" runat="server" DataSourceID="odsPersonsAge"
                AutoGenerateRows="true" /> 

<asp:ObjectDataSource ID="odsPersonsAge" runat="server"
    SelectMethod="GetPersonsAge" TypeName="ASPNET4Routing.MockDataBase">
    <SelectParameters>
        <asp:RouteParameter Name="name" RouteKey="name" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

SHARE:

Managing ViewState in ASP.Net 4

ASP.Net 4 allows us to use an opt-in viewstate approach rather than an opt-out approach in previous ASP.Net versions (EnableViewState was available in prior versions, but if you disable viewstate, all children of the page/container also inherited this with no one to override and opt-in).

To use the opt-in approach in ASP.Net 4, the new ViewStateMode property can be used. The following example disables viewstate for the whole page (all children will inherit this disabled value) but Label2 opts in to viewstate. The codebehind sets the values of the labels only on the first page load - not any subsequent postbacks.

<%@ Page Language="C#" AutoEventWireup="true"
 CodeFile="Default.aspx.cs" Inherits="_Default"
 ViewStateMode="Disabled"
 %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <p><asp:Label ID="Label1" Text="not set" runat="server" /></p>
        <p><asp:Label ID="Label2" Text="not set" ViewStateMode="Enabled" runat="server" /></p>
        <p><asp:Button Text="do a postback" runat="server" /></p>
    </div>
    </form>
</body>
</html>

Code behind:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        Label1.Text = "this text has been set from code behind only on the first load i.e. not set in any following postbacks.";
        Label2.Text = Label1.Text;
    }
}

The first time the page loads, the 2 text boxes show the text as set in the code behind:

 

 



Once a postback happens (in this case by clicking the button), no code is executed in the Page_Load event due to the if (!IsPostBack) check. This means that the Text property of the labels will be set to what's initially declared in the aspx mark-up (i.e. "not set"); however because we have set ViewStateMode="Enabled" on Label2, it's Text will be retained in viewstate between callbacks:

 

SHARE:

Setting Meta Tags in ASP.Net 4

ASP.Net 4 adds 2 new Meta tag related properties to the Page. They can be used to set meta tags for keywords and description.

You can set them in the code behind:

Page.MetaKeywords = "keyword1, keyword2, keyword3";
Page.MetaDescription = "Example of new meta tag support in ASP.Net 4";

You can also set in the @Page directive:

<%@ Page Language="C#" AutoEventWireup="true"
MetaKeywords="keyword1, keyword2, keyword3"
MetaDescription="Example of new meta tag support in ASP.Net 4"
CodeFile="Default.aspx.cs" Inherits="_Default" %>

The ouput of either of these methods renders html similar to the following:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>
    ASP.NET 4 Meta Tag Support
</title><meta name="description" content="Example of new meta tag support in ASP.Net 4" /><meta name="keywords" content="keyword1, keyword2, keyword3" /></head>
<body>
 </body>
</html>
 

SHARE:

Permanently Redirecting a Page in ASP.NET 4

Rather that using Response.Redirect("newpage.aspx"), ASP.NET 4 introduces the RedirectPermanent method.

To signal that the redirect is of a permanent nature (such as a url to a page changing permanently) you can now call:

Response.RedirectPermanent("newpage.aspx");

This will issue a http response HTTP 301 (Moved Permanently) rather than the normal HTTP 302 (Found). This can potentially remove a round trip to the server.

From msdn.com: "Search engines and other user agents that recognize permanent redirects will store the new URL that is associated with the content, which eliminates the unnecessary round trip made by the browser for temporary redirects."

SHARE:

Notification Window "toast" in Silverlight 4

To create "toast" notifications (small popup messages that appear at the bottom right on Windows) the NotificationWindow class can be used.

NotificationWindow will only work in out-of-browser mode, if you try to show a notification when running in-browser a System.InvalidOperationException is thrown.

The default size of the toast is 400 x 100, the size can be altered by setting NotificationWindow's Width & Height, but the max size is 400 x 100;

Example 1 - Creating Some Simple Content

The code below shows a notification that contains a  TextBlock with the text "hello" and show it for 2 seconds:

var toast = new NotificationWindow();

// Create some content to show
var tb = new TextBlock();
tb.Text = "hello";

// Set content of the toast before we show it
toast.Content = tb;

// Show notification for 2 seconds
toast.Show(2000);

Example 2 - Using a UserControl as Content

This example shows how a UserControl defined elsewhere in XAML can be used as the content of the notification:

var toast = new NotificationWindow();

// Create an instance of a UserControl called SampleToast
// that is defined in XAML
var toastContent = new SampleToast();

// Set the content of the notification window to that of the UserControl
toast.Content = toastContent;

// Optionally set size to be different from the
// default of 400 x 100
toast.Width = 200;
toast.Height = 50;

// Show notification for 2 seconds
toast.Show(5000);

MSDN Docs

 

SHARE: