Archive

Archive for the ‘WinRT’ Category

Binding to Enum in Universal apps with localization

19/03/2015 1 comment

When we work with enums, we may want to display description messages associated to symbolic names. For example, suppose we have the following model in our app that uses the MVVM pattern:

public enum Status
{
    Free,
    NotAvailable,
    DoNotDisturb
}

public class Contact
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Status CurrentStatus { get; set; }
}

If we bind the CurrentStatus property of Contact class, the desired result is to have values such as Free, Not Available and Do Not Disturb.

This task can be simply accomplished using Custom Attributes and a Converter. First of all, create a Display attribute and use it to decorate the enum:

public class DisplayAttribute : Attribute
{
    public string Name { get; private set; }

    public DisplayAttribute(string name)
    {
        Name = name;
    }
}

public enum Status
{
    [Display("Free")]
    Free,
    [Display("Not Available")]
    NotAvailable,
    [Display("Do Not Disturb")]
    DoNotDisturb
}

Then, we define the following Converter:

public class EnumToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null || !(value is Enum))
            return null;

        var @enum = value as Enum;
        var description = @enum.ToString();

        var attrib = this.GetAttribute<DisplayAttribute>(@enum);
        if (attrib != null)
            description = attrib.Name;

        return description;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        string language)
    {
        throw new NotImplementedException();
    }

    private T GetAttribute<T>(Enum enumValue) where T : Attribute
    {
        return enumValue.GetType().GetTypeInfo()
            .GetDeclaredField(enumValue.ToString())
            .GetCustomAttribute<T>();
    }
}

At line 11 we check whether the enum value is decorated with the Display attribute. If so, we retrieve its Name property, that contains the actual string we want to use.

The last thing to do is to use the EnumToStringConverter in our binding declaration. For example:

<TextBlock Text="{Binding CurrentStatus, 
    Converter={StaticResource EnumToStringConverter}}" />

And what’s about localization? In a real world scenario we want strings localized according to user culture. Thanks to the built-in support in WinRT, this is straightforward. We just need to modify a bit our Converter:

public class EnumToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null || !(value is Enum))
            return null;

        var @enum = value as Enum;
        var description = @enum.ToString();

        var attrib = this.GetAttribute<DisplayAttribute>(@enum);
        if (attrib != null)
        {
            var resource = new ResourceLoader();
            if (!string.IsNullOrWhiteSpace(resource.GetString(attrib.Name)))
                description = resource.GetString(attrib.Name);
            else
                description = attrib.Name;
        }

        return description;
    }

    public object ConvertBack(object value, Type targetType, object parameter, 
        string language)
    {
        throw new NotImplementedException();
    }

    private T GetAttribute<T>(Enum enumValue) where T : Attribute
    {
        return enumValue.GetType().GetTypeInfo()
            .GetDeclaredField(enumValue.ToString())
            .GetCustomAttribute<T>();
    }
}

At lines 14-16 we create a ResourceLoader and try to get the string that corresponds to the Name property of the attribute. So, to enable localization of enum values we simply need to add a Resource file (resw) for each language and define resource names accordingly.

If, instead, the string isn’t found with the ResourceLoader, the original Display.Name property is used (line 18), as in the first version of the Converter.

Advertisements
Categories: .NET, C#, MVVM, Windows Phone, WinRT

An improved NavigationService for MVVM Light in Universal apps

24/02/2015 4 comments

Some times ago we talked about the new NavigationService that has been introduced with MVVM Light 5.

The implementation for Universal apps is very useful as it covers all the basic needs. However, at this time it lacks some important features, like a way to determine whether the navigation can go back and a method to remove the most recent available entry from the back stack.

So, we can extend the built-in NavigationService defining an interface like this:

public interface INavigationServiceEx : INavigationService
{
    bool CanGoBack { get; }
    
    bool RemoveBackEntry();       
}

And the corresponding implementation:

public class NavigationServiceEx : NavigationService, INavigationServiceEx
{
    public bool CanGoBack
    {
        get
        {
            var frame = this.GetMainFrame();
            if (frame != null)
                return frame.CanGoBack;

            return false;
        }
    }

    public bool RemoveBackEntry()
    {
        var frame = this.GetMainFrame();
        if (frame.CanGoBack)
        {
            frame.BackStack.RemoveAt(frame.BackStackDepth - 1);
            return true;
        }

        return false;
    }

    private Frame GetMainFrame()
    {
        return (Frame)Window.Current.Content;
    }
}

In this class, we simply get the current Frame and then access its CanGoBack and BackStack property.

Now we can use the new NavigationServiceEx in the usual way:

static ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    var navigationService = CreateNavigationService();
    SimpleIoc.Default.Register<INavigationServiceEx>(() => navigationService);

    // ...
}

private static INavigationServiceEx CreateNavigationService()
{
    var navigationService = new NavigationServiceEx();
    navigationService.Configure(Constants.SecondPage, typeof(SecondPage));

    return navigationService;
}

Of course, all ViewModels need to refer the new INavigationServiceEx interface (instead of the default INavigationService provided by MVVM Light).

Categories: C#, MVVM, Windows Phone, WinRT

How to open and close Flyouts in Universal apps using MVVM

15/01/2015 6 comments

Some times ago we talked about how to open attached Flyouts and close them using custom behaviors and MVVM. This was necessary because the Flyout control doesn’t expose properties to control its visibility, so we need to use its ShowAt and Hide methods explicitly.

While this approach works correctly, we can obtain the same result in a more elegant way with two simple Attached Properties. The following is a generic version of the implementation shown in the article Using Windows 8.1 Flyout with MVVM and works with every control, not only buttons (remember, in fact, that we can attach a Flyout on any FrameworkElement).

Let’s start creating a FlyoutHelper class:

public static class FlyoutHelper
{
    public static readonly DependencyProperty IsVisibleProperty = 
        DependencyProperty.RegisterAttached(
        "IsOpen", typeof(bool), typeof(FlyoutHelper), 
        new PropertyMetadata(true, IsOpenChangedCallback));

    public static readonly DependencyProperty ParentProperty = 
        DependencyProperty.RegisterAttached(
        "Parent", typeof(FrameworkElement), typeof(FlyoutHelper), null);

    public static void SetIsOpen(DependencyObject element, bool value)
    {
        element.SetValue(IsVisibleProperty, value);
    }

    public static bool GetIsOpen(DependencyObject element)
    {
        return (bool)element.GetValue(IsVisibleProperty);
    }

    private static void IsOpenChangedCallback(DependencyObject d, 
        DependencyPropertyChangedEventArgs e)
    {
        var fb = d as FlyoutBase;
        if (fb == null)
            return;

        if ((bool)e.NewValue)
        {
            fb.Closed += flyout_Closed;
            fb.ShowAt(GetParent(d));
        }
        else
        {
            fb.Closed -= flyout_Closed;
            fb.Hide();
        }
    }

    private static void flyout_Closed(object sender, object e)
    {
        // When the flyout is closed, sets its IsOpen attached property to false.
        SetIsOpen(sender as DependencyObject, false);
    }

    public static void SetParent(DependencyObject element, FrameworkElement value)
    {
        element.SetValue(ParentProperty, value);
    }

    public static FrameworkElement GetParent(DependencyObject element)
    {
        return (FrameworkElement)element.GetValue(ParentProperty);
    }
}

This code adds an IsOpen Attached Property to flyout objects. When it changes (lines 22-39), we check whether we want to open or close the Flyout: in the first case, we call the ShowAt method (lines 32), that actually displays it. Since this method requires a FrameworkElement that represents the flyout’s placement target, we need also to specify the target control for the popup, so we have defined a Dependecy Property named Parent that holds the container of the Flyout. If, instead, we are about to dismiss the control, when IsOpen is set to false, we invoke its Hide method (line 37).

Note that we also need to register for the Closed event (line 31), that occurs when the flyout is hidden. The Flyout, in fact, implements the light-dismiss behavior: it can be closed simply touching anywhere on the screen outside of it, so it is necessary to handle the Closing event and set the IsOpen property to false, to reflect the correct state of the Flyout.

Now we can define a ViewModel like this:

public class MainViewModel : ViewModelBase
{
    private bool isOpen;
    public bool IsOpen
    {
        get { return isOpen; }
        set { this.Set(ref isOpen, value); }
    }

    public RelayCommand OpenCommand { get; set; }
    public RelayCommand CloseCommand { get; set; }

    public MainViewModel()
    {
        OpenCommand = new RelayCommand(() => IsOpen = true);
        CloseCommand = new RelayCommand(() => IsOpen = false);
    }
}

We have a command to open the Flyout (setting the IsOpen variable to true, line 15) and, similarly, a command to close it (line 16). In this way, in the View we can use the following XAML:

<Button Content="Open Flyout" x:Name="button" Command="{Binding OpenCommand}">
    <FlyoutBase.AttachedFlyout>
        <Flyout controls:FlyoutHelper.IsOpen="{Binding IsOpen, Mode=TwoWay}" 
                controls:FlyoutHelper.Parent="{Binding ElementName=button}">
            <StackPanel>
                <TextBlock Text="Awesome Flyout!" Margin="10" />
                <Button Content="Close" Command="{Binding CloseCommand}" 
                        HorizontalAlignment="Right" />
            </StackPanel>
        </Flyout>
    </FlyoutBase.AttachedFlyout>
</Button>

Moveover, using the Behavior SDK, we can use the same approach to attach a Flyout to any control:

<Page
    ...
    xmlns:i="using:Microsoft.Xaml.Interactivity"
    xmlns:core="using:Microsoft.Xaml.Interactions.Core"
    ...>

    ...
    <Image x:Name="image" Source="/Assets/Logo.scale-100.png" 
           Height="100" Width="100">
        <FlyoutBase.AttachedFlyout>
            <Flyout controls:FlyoutHelper.IsOpen="{Binding IsOpen, Mode=TwoWay}" 
                    controls:FlyoutHelper.Parent="{Binding ElementName=image}">
                <StackPanel>
                    <TextBlock Text="Awesome Flyout!" Margin="10" />
                    <Button Content="Close" Command="{Binding CloseCommand}" 
                            HorizontalAlignment="Right" />
                </StackPanel>
            </Flyout>
        </FlyoutBase.AttachedFlyout>
        <i:Interaction.Behaviors>
            <core:EventTriggerBehavior EventName="Tapped">
                <core:InvokeCommandAction Command="{Binding OpenCommand}" />
            </core:EventTriggerBehavior>
        </i:Interaction.Behaviors>
    </Image>
    ...
</Page>

The EventTriggerBehavior declared at lines 21-23 invokes the OpenCommand action as soon as the Tapped event raises. As seen before, this sets the IsOpen property of the ViewModel to true, so the Flyout attached property displays the popup.

Categories: .NET, C#, MVVM, Windows Phone, WinRT

DialogService in MVVM Light V5

14/10/2014 Comments off

Some days ago we talked about the new release of MVVM Light Toolkit and, in particular, we presented the new NavigationService.

The other service that is shipped with MVVM Light V5 is DialogService, that is able to show message dialogs in our apps. To use it, we simply need to register this type in the ViewModelLocator class:

public ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    SimpleIoc.Default.Register<IDialogService, DialogService>();
    // Other registrations...

    SimpleIoc.Default.Register<MainViewModel>();
}

Basically, the IDialogService interface contains the ShowMessage method, with some overloads. For example:

public class MainViewModel : ViewModelBase
{
    private IDialogService dialogService;

    public RelayCommand TestDialogServiceCommand { get; set; }

    public MainViewModel(IDialogService dialogService)
    {
        this.dialogService = dialogService;

        TestDialogServiceCommand = new RelayCommand(async () =>
        {
            // Simple message without callback.
            await dialogService.ShowMessage("Simple message text", "Title");

            // Message with custom buttons and callback action.
            await dialogService.ShowMessage("Are you sure you want to continue?", 
                "Confirmation",
                buttonConfirmText: "Continue", buttonCancelText: "Discard", 
                afterHideCallback: (confirmed) =>
                {
                    if (confirmed)
                    {
                        // User has pressed the "confirm" button.
                        // ...
                    }
                    else
                    {
                        // User has pressed the "cancel" button
                        // (or has discared the dialog box).
                        // ...
                    }
                });
        });
    }
}

At line 15, we use the simplest version of ShowMessage: we just show a dialog box with a message and a title. Instead, the second example (lines 18-25) shows a message box with two buttons, Continue and Cancel; when the user taps one of them (or discard the question), the afterHideCallback action will be invoked. It will get a boolean parameter indicating if the “confirm” button (true) or the “cancel” button (false) was pressed (or if the dialog was discarded).

The IDialogService interface exposes also the ShowError method, that can be used to display information about an error (it accepts an Exception as first argument). However, we must notice that on some platforms, like Windows Store and Windows Phone, ShowError has the same output of ShowMessage (because these platforms don’t have the concept of error message box).

Categories: C#, MVVM, Windows Phone, WinRT

NavigationService in MVVM Light V5

10/10/2014 20 comments

Two days ago, at Xamarin Evolve conference in Atlanta, has been announced the availability of MVVM Light V5. This release contains interesting updates for Xamarin, as we can read in the official blog post.

Another important news is the inclusion of the INavigationService and IDialogService interfaces with corresponding implementations on supported platforms. They are objects that very often are created in each project, so now we can use the existing classes instead of creating new ones.

Let’s see how to use the INavigationService interface in our Universal Windows apps. First of all, we need to add a reference to MVVM Light V5 using NuGet:

Adding MVVM Light V5 to the project

Adding MVVM Light V5 to the project

Creating the NavigationService is straightforward. In the ViewModelLocator class we write something like this:

public ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    var navigationService = this.CreateNavigationService();
    SimpleIoc.Default.Register<INavigationService>(() => navigationService);

    SimpleIoc.Default.Register<IDialogService, DialogService>();

    SimpleIoc.Default.Register<MainViewModel>();
    SimpleIoc.Default.Register<DetailsViewModel>();
}

private INavigationService CreateNavigationService()
{
    var navigationService = new NavigationService();
    navigationService.Configure("Details", typeof(DetailsPage));
    // navigationService.Configure("key1", typeof(OtherPage1));
    // navigationService.Configure("key2", typeof(OtherPage2));

    return navigationService;
}

In the CreateNavigationService method (lines 14-22), we create a new instance of the NavigationService class (that is contained in the new GalaSoft.MvvmLight.Views namespace). We need to call its Configure method to associate each page type with a key. For example, at line 17 we associate the Details key with the DetailsPage page. Then, in the class constructor, we register the INavigationService interface specifying the concrete object that must be returned when it is requested (line 6).

Now, we can define the MainViewModel as follows:

public class MainViewModel : ViewModelBase
{
    private INavigationService navigationService;

    public RelayCommand DetailsCommand { get; set; }

    public MainViewModel(INavigationService navigationService)
    {
        this.navigationService = navigationService;

        DetailsCommand = new RelayCommand(() =>
        {
            navigationService.NavigateTo("Details", "My data");
        });
    }
}

When the DetailsCommand command is executed, at line 13, we invoke the NavigateTo method on the INavigationService reference, specifying the key that corresponds to the page to navigate to (DetailsPage, in this example). We’re using simple strings to control the navigation, so ViewModels don’t need to know the Views that will be actually opened (and thus without breaking the MVVM pattern).

We’re calling an overload of the NavigateTo method that accepts an object parameter that will be passed to the OnNavigatedTo method of the requested page (in the Parameter property of NavigationEventArgs):

public sealed partial class DetailsPage : Page
{
    // ...

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        var parameter = e.Parameter as string;  // "My data"
        base.OnNavigatedTo(e);
    }
}

Of course, as we’re following the MVVM pattern, we don’t want to use code-behind. So, the preferred approach to get navigation parameters is the one presented in the article Calling ViewModel methods in response to Page navigation events using MVVM Light in WinRT to automatically pass this parameter to the ViewModel.

Lastly, the INavigationService interface exposes also the GoBack method, that allows to return to the previous page in the navigation stack.

Categories: C#, MVVM, Windows Phone, WinRT

How to correctly use the Mobile Services SDK with MVVM in Universal Windows apps

07/10/2014 Comments off

If we want to use an Azure Mobile Service in our Universal apps, we can take advantage of the Add | Connected Service menu command. In this way, a static reference to a MobileServiceClient instance is added to the App.xaml.cs file.

However, if we’re following the MVVM pattern, this approach isn’t correct: we surely need to access the client from ViewModels, and so in this case we should reference the App class.

The solution is simple. Suppose we have added the MVVM Light Toolkit to the project. We can leverage the ViewModelLocator class to register an instance of the client:

SimpleIoc.Default.Register<IMobileServiceClient>(() =>
{
    var client = new MobileServiceClient(
        applicationUrl: "",
        applicationKey: "");

    return client;
});

We use an overload of the Register method that is able to create the instance that must be returned when the given type is resolved. In this case, we create the MobileServiceClient specifying the Service Url and the Application Key.

Doing so, we can pass a reference to the client to our ViewModels:

private IMobileServiceClient client;

public MainViewModel(IMobileServiceClient client)
{
    this.client = client;
    // ...
}

In this way, the MVVM pattern is respected and we can use the Mobile Service in our ViewModels as we expect to do.

ScrollToBottom Behavior for ListView in MVVM based Universal Windows apps

30/09/2014 Comments off

In some cases, we may need to automatically scroll the content of a ListView as items are added to it (for example, in a chat-like app). To do this, we can use the ScrollIntoView method of the control.

However, if we’re following the MVVM pattern, this approach ins’t correct. We should instead use a Behavior that automatically scrolls the content when a new item is added:

public class ScrollToBottomBehavior : DependencyObject, IBehavior
{
    public DependencyObject AssociatedObject { get; private set; }

    public object ItemsSource
    {
        get { return (object)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(object),
        typeof(ScrollToBottomBehavior),
        new PropertyMetadata(null, ItemsSourcePropertyChanged));

    private static void ItemsSourcePropertyChanged(object sender,
        DependencyPropertyChangedEventArgs e)
    {
        var behavior = sender as ScrollToBottomBehavior;
        if (behavior.AssociatedObject == null || e.NewValue == null) return;

        var collection = behavior.ItemsSource as INotifyCollectionChanged;
        if (collection != null)
        {
            collection.CollectionChanged += (s, args) =>
            {
                var scrollViewer = behavior.AssociatedObject
                                           .GetFirstDescendantOfType<ScrollViewer>();
                scrollViewer.ChangeView(null, scrollViewer.ActualHeight, null);
            };
        }
    }

    public void Attach(DependencyObject associatedObject)
    {
        var control = associatedObject as ListView;
        if (control == null)
            throw new ArgumentException(
                "ScrollToBottomBehavior can be attached only to ListView.");

        AssociatedObject = associatedObject;
    }

    public void Detach()
    {
        AssociatedObject = null;
    }
}

The ScrollToBottomBehavior expects that the ItemsSource property is bound to an object that impletements the INotifyCollectionChanged interface (like ObservableCollection) and then registers for its CollectionChanged event (lines 22-25).

When such event occurs, we must scroll the ListView. Instead of using its ScrollIntoView method, like said before, we get the ScrollViewer that is contained in the ListView control (lines 27-28). GetFirstDescendantOfType is an extension method of the VisualTreeHelperExtensions class, part of the WinRT XAML Toolkit (you can grab only this class and add it to your project). We invoke the ChangeView method specifying the vertical offset to use: with ActualHeight, we scroll to the bottom of the control.

Now suppose we have the following ViewModel (we’re using MVVM Light Toolkit):

public class Question
{
    public string Name { get; set; }
    public string Text { get; set; }
    public DateTimeOffset Date { get; set; }
}

public class MainViewModel : ViewModelBase
{
    private ObservableCollection<Question> questions;
    public ObservableCollection<Question> Questions
    {
        get { return questions; }
        set { this.Set(ref questions, value); }
    }

    public RelayCommand AddQuestionCommand { get; set; }

    public MainViewModel()
    {
        Questions = new ObservableCollection<Question>();

        AddQuestionCommand = new RelayCommand(() =>
        {
            Questions.Add(new Question
            {
                Date = DateTime.Now,
                Name = "Name " + questions.Count,
                Text = "Question " + questions.Count
            });
        });
    }
}

We have an ObservableCollection and a Command to add items to it. We want to show the questions in a ListView that scrolls to bottom everytime a new item is added. So, we define the following XAML:

<Page
    ...
    xmlns:behaviors="using:QuestionsManager.Behaviors"
    xmlns:i="using:Microsoft.Xaml.Interactivity"
    ...>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        ...

        <ListView ItemsSource="{Binding Questions}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="0,5,0,0">
                        <TextBlock Text="{Binding Date}" Margin="0,0,5,0" />
                        <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                            <TextBlock Text="{Binding Name}" FontWeight="Bold"
                                       Margin="0,0,5,0" />
                            <TextBlock Text="{Binding Text}"  />
                        </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
            <i:Interaction.Behaviors>
                <behaviors:ScrollToBottomBehavior ItemsSource="{Binding Questions}"/>
            </i:Interaction.Behaviors>
        </ListView>
        <StackPanel Grid.Row="1">
            <Button Content="Add dummy question" Command="{Binding AddQuestionCommand}" />
        </StackPanel>
    </Grid>
</Page>

At lines 23-25, we have bound the ItemsSource property of ScrollToBottomBehavior to Questions (the same of the cointainer ListView). This is all we need to do in order to activate the automatic scrolling.

Categories: C#, MVVM, Windows Phone, WinRT