2 Comments
  •   Posted in: 
  • UWP

image

Callisto is excellent open source UI Control library for the Windows 8 Store apps. Caliburn.Micro is a powerful framework for building Windows Phone, Silverlight and Windows 8 Store apps. These helpers make it easier to combine Caliburn.Micro with the Callisto's dialogs (Flyouts).

Content

  • Helper for displaying settings dialogs on the Settings charm
  • Helper for displaying normal dialogs all around the screen

Install

The helpers are available as a source code through NuGet:

Install-Package CaliburnMicroWinRTCallistoHelpers

Installing the package will add a Dialogs-folder into your project. This folder contains the source code for the helpers.

Requirements

Your project must reference Callisto and the Caliburn.Micro must be set right. The helpers have been tested with Callisto 1.2.1.

To learn how to set up Caliburn.Micro for WinRT, you may refer to this tutorial.

Sample

A sample app showing the usage of the helpers is available from the GitHub repository.

Usage

  1. Create normal Caliburn.Micro view model (inheriting from Screen / Conductor) and the view for the view model
  2. Pass the type of the view model to the helper. The helper will create the view model and the view and it will display the dialog.

Normal dialog

Use DialogService.ShowDialog<TViewModel>() to display a dialog.

Parameters

  • PlacementMode (Required): The way the Callisto dialog works is that you provide it an UI control as a placement target. The dialog will open next to this UI control. PlacementMode defines if the dialog should be shown above, under, left or right of this UI control.
  • PlacementTarget (Required): The placement target.
  • onInitialize: Action which is executed before the dialog is shown.
  • onClose: Action which is executed after the dialog has been closed.

Example

Page's XAML:

<Button x:Name="ShowDialog" Content="Show Dialog" HorizontalAlignment="Center"/>

Page's View Model:

public void ShowDialog(FrameworkElement source) 
    { 
        DialogService.ShowDialog<DialogViewModel>(PlacementMode.Left, source); 
    }

Dialog's view model:

public class DialogViewModel : Screen
        { 
            public async void ShowMessage() 
            { 
                var dlg = new MessageDialog("Hello from Dialog"); 

                await dlg.ShowAsync(); 
            } 
        }

Dialog's View:

<UserControl 
        x:Class="caliburn_micro_winrt_getting_started.DialogView" 
        ... 
        Height="200" 
        Width="200"> 

        <Grid > 
            <TextBlock Text="Callisto Dialog" Foreground="Black" Style="{StaticResource SubheaderTextStyle}"/> 
            <Button x:Name="ShowMessage" Content="Show Message" Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"/> 
        </Grid> 
    </UserControl>
&nbsp;

Settings dialog

The settings dialog can be shown similar to a normal dialog:

DialogService.ShowSettings<SettingsViewModel>()

Here’s an example which shows how to initialize the view model before it’s shown (the initialization happen before the view model’s OnInitialize is called). It also shows how to to update the UI after the settings dialog has closed:

DialogService.ShowSettings<SettingsViewModel>(onInitialize: vm => vm.TestSetting = this.CurrentValue, onClosed: (vm, view) => CurrentValue = vm.TestSetting);

Adding the view model to settings pane

On the MainPage of your application, use the AddSetting-extension method:

private static bool initialized; 
        public MainPage() 
        { 
            this.InitializeComponent(); 

            if (initialized) return;

            SettingsPane.GetForCurrentView().CommandsRequested += CommandsRequested; 
            initialized = true; 
        }

        private void CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args) 
        { 
            args.AddSetting<SettingsViewModel>(); 
        }

Parameters

  • onInitialize: Action which is executed before the settings dialog is shown.
  • onClosed: Action which is execute after the dialog has been closed.
  • headerBrush: Can be used to control the color of the settings header
  • backgroundBrush: Can be used to control the background color of the settings page

Dialog's title is automatically determined from the ViewModel's DisplayName-property.

Example

View Model:

public sealed class SettingsViewModel : Screen 
    { 
        public SettingsViewModel() 
        { 
            this.DisplayName = "My Settings"; 
        }

        public bool TestSetting { get; set; } 
    }
&nbsp;

View:

<UserControl 
        x:Class="caliburn_micro_winrt_getting_started.SettingsView" 
        ... 
        > 

        <StackPanel> 
            <CheckBox Content="Test setting" x:Name="TestSetting" Margin="0 20 0 0" /> 
        </StackPanel> 
    </UserControl>
&nbsp;

Source code and sample

The source code for these helpers and the sample app are available from GitHub.

23 Comments
  •   Posted in: 
  • UWP
Note: This is a repost of my Stack Overflow question. Feel free to comment here or on the SO.

 

I'm having some problems in Windows 8 Metro apps (XAML & C#) regarding the user's regional settings. It seems that the apps won't respect user's regional settings, so even if your Windows 8 is set to display dates and times in Finnish format, the apps will still display them using US-formatting. But this is such a big problem that there must be something I'm missing?

To test this I started by creating a WPF-application. The application just prints out the CurrentCulture and the formatted DateTime.Now:

private void Culture_Loaded_1(object sender, RoutedEventArgs e) 
        { 
            this.Culture.Text = System.Globalization.CultureInfo.CurrentCulture.DisplayName; 
        }

        private void Date_Loaded_1(object sender, RoutedEventArgs e) 
        { 
            this.Date.Text = DateTime.Now.ToString(); 
        }

Here's my default regional settings:

 

When run, the app displayed the date in Finnish format:

Then I changed the regional settings to US:

And when the app was run again, the culture and formatting changed:

This is as I expected everything to work and this is also how I expected WinRT apps to work.

So as a next step, I created a WinRT (XAML & C#) app with the same code and reverted the regional settings back to Finnish. The problem: 

Even when I've defined through regional settings that the formatting should be "Finnish", the WinRT app displays the datetime with US-formatting. I then modified the app's project file and made fi-FI the default language:

This change also modified the app's culture: 

Strange. I changed the Default Language back to its default value and the formatting was restored to US. I then created folders "Strings - fi-FI" inside the project and added an empty "Resources.resw" to the project. This empty file seems to be enough, as I was now getting the Finnish formatting: 


As soon as I remove the empty resource file, the formattings reverts back to US: 


Very strange.

Any ideas? Is this a bug, or something that needs to be considered when building WinRT apps? Any ideas how thing work on the JavaScript side of things? Please chime by leaving a comment here or on the Stack Overflow.

28 Comments
  •   Posted in: 
  • UWP

imageOne of our Windows 8 WinRT apps requires a ListView with two features:

  1. "Auto-stick". The ListView must automatically scroll to the bottom when a new message is added to it.
  2. "Scroll detection". User can disable the "Auto-stick" by manually scrolling the ListView. If the user scrolls the ListView to bottom, the "Auto-stick" is enabled, otherwise it's disabled.

Here's some guidance on how you can achieve these features in a WinRT app using C# and XAML.

Automatically scrolling ListView to bottom

The ListView can be programmatically scrolled using the ScrollIntoView-method. You can pass in the item and the ListView jumps to show it. Here's how you can automatically scroll the ListView to show the last item when the list's ItemsSource is an ObservableCollection and a new item is added to the collection:

1. Register a listener to collection's CollectionChanged-event:
private readonly ObservableCollection<string> items = new ObservableCollection<string>(); 
...

        private void MyListviewLoaded(object sender, RoutedEventArgs e) 
        { 
            this.MyListView.ItemsSource = items;

            items.CollectionChanged += (s, args) => ScrollToBottom(); 
        }
2. In the ScrollToBottom-method, set the ListView's SelectedIndex to the last item in the collection, then use ScrollIntoView to scroll to the SelectedItem:
private void ScrollToBottom() 
        { 
            var selectedIndex = MyListView.Items.Count - 1; 
            if (selectedIndex < 0) 
                return;

            MyListView.SelectedIndex = selectedIndex; 
            MyListView.UpdateLayout();

            MyListView.ScrollIntoView(MyListView.SelectedItem); 
        }

Another option (used by the WinRT XAML Toolkit) is to get the ScrollViewer-control from inside the ListView, and call its ScrollToVerticalOffset-method:

private void ScrollToBottom() 
        { 
            var scrollViewer = MyListView.GetFirstDescendantOfType<ScrollViewer>(); 
            scrollViewer.ScrollToVerticalOffset(scrollViewer.ScrollableHeight); 
        }

GetFirstDescendantOfType is part of the WinRT XAML toolkit. Here's the VisualTreeHelperExtensions -class from the toolkit which includes this extension method:

public static class VisualTreeHelperExtensions 
    { 
        public static T GetFirstDescendantOfType<T>(this DependencyObject start) where T : DependencyObject 
        { 
            return start.GetDescendantsOfType<T>().FirstOrDefault(); 
        }

        public static IEnumerable<T> GetDescendantsOfType<T>(this DependencyObject start) where T : DependencyObject 
        { 
            return start.GetDescendants().OfType<T>(); 
        }

        public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject start) 
        { 
            var queue = new Queue<DependencyObject>(); 
            var count = VisualTreeHelper.GetChildrenCount(start);

            for (int i = 0; i < count; i++) 
            { 
                var child = VisualTreeHelper.GetChild(start, i); 
                yield return child; 
                queue.Enqueue(child); 
            }

            while (queue.Count > 0) 
            { 
                var parent = queue.Dequeue(); 
                var count2 = VisualTreeHelper.GetChildrenCount(parent);

                for (int i = 0; i < count2; i++) 
                { 
                    var child = VisualTreeHelper.GetChild(parent, i); 
                    yield return child; 
                    queue.Enqueue(child); 
                } 
            } 
        }

        public static T GetFirstAncestorOfType<T>(this DependencyObject start) where T : DependencyObject 
        { 
            return start.GetAncestorsOfType<T>().FirstOrDefault(); 
        }

        public static IEnumerable<T> GetAncestorsOfType<T>(this DependencyObject start) where T : DependencyObject 
        { 
            return start.GetAncestors().OfType<T>(); 
        }

        public static IEnumerable<DependencyObject> GetAncestors(this DependencyObject start) 
        { 
            var parent = VisualTreeHelper.GetParent(start);

            while (parent != null) 
            { 
                yield return parent; 
                parent = VisualTreeHelper.GetParent(parent); 
            } 
        }

        public static bool IsInVisualTree(this DependencyObject dob) 
        { 
            return Window.Current.Content != null && dob.GetAncestors().Contains(Window.Current.Content); 
        }

        public static Rect GetBoundingRect(this FrameworkElement dob, FrameworkElement relativeTo = null) 
        { 
            if (relativeTo == null) 
            { 
                relativeTo = Window.Current.Content as FrameworkElement; 
            }

            if (relativeTo == null) 
            { 
                throw new InvalidOperationException("Element not in visual tree."); 
            }

            if (dob == relativeTo) 
                return new Rect(0, 0, relativeTo.ActualWidth, relativeTo.ActualHeight);

            var ancestors = dob.GetAncestors().ToArray();

            if (!ancestors.Contains(relativeTo)) 
            { 
                throw new InvalidOperationException("Element not in visual tree."); 
            }

            var pos = 
                dob 
                    .TransformToVisual(relativeTo) 
                    .TransformPoint(new Point()); 
            var pos2 = 
                dob 
                    .TransformToVisual(relativeTo) 
                    .TransformPoint( 
                        new Point( 
                            dob.ActualWidth, 
                            dob.ActualHeight));

            return new Rect(pos, pos2); 
        } 
    }

Detecting when ListView is scrolled to the bottom

In our case the auto-stick is enabled or disabled based on where the user scrolls the ListView. This requires that we can detect when the ListView is scrolled (or actually, when the scrolling ends) and also we need to know if the ListView is scrolled to the bottom. In order to achieve these, we first need to access the ListView's ScrollViewer and then the ScrollViewer's vertical ScrollBar. We can use the GetFirstDescendantOfType and GetDescendantsOfType -extension methods from WinRT XAML Toolkit (shown above) to get these controls:

            var scrollViewer = MyListView.GetFirstDescendantOfType<ScrollViewer>();

            var scrollbars = scrollViewer.GetDescendantsOfType<ScrollBar>().ToList();

            var verticalBar = scrollbars.FirstOrDefault(x => x.Orientation == Orientation.Vertical);

Now that we have the vertical scroll bar, we can register an event handler to its Scroll-event:

if (verticalBar != null) 
                verticalBar.Scroll += BarScroll;

In the BarScroll-method we receive and event argument of type ScrollEventargs. This argument tells us when the scrolling has ended. After making sure that we have received the event we need, we can use the argument's NewValue-property to make sure if we have scrolled to the bottom:

void BarScroll(object sender, ScrollEventArgs e) 
        { 
            if (e.ScrollEventType != ScrollEventType.EndScroll) return;

            var bar = sender as ScrollBar; 
            if (bar == null) 
                return;

            System.Diagnostics.Debug.WriteLine("Scrolling ended");

            if (e.NewValue >= bar.Maximum) 
            { 
                System.Diagnostics.Debug.WriteLine("We are at the bottom"); 
                LockToBottom = true; 
            } 
            else 
            { 
                System.Diagnostics.Debug.WriteLine("We are away from the bottom"); 
                LockToBottom = false; 
            } 
        }

Now we just need to the edit the ScrollToBottom-method so that the automatic scrolling happens only if the LockToBottom-property is set to true:

private void ScrollToBottom() 
        { 
            if (!LockToBottom) 
                return;

            var selectedIndex = MyListView.Items.Count - 1; 
            if (selectedIndex < 0) 
                return;

            MyListView.SelectedIndex = selectedIndex; 
            MyListView.UpdateLayout();

            MyListView.ScrollIntoView(MyListView.SelectedItem); 
        }

Sample:

A sample app if available from the GitHub.

image

Add few messages to the list to see how it auto scrolls, because the “Auto Stick” is enabled by default. Then scroll the list up and auto stick is disabled.

Links:

WinRT XAML Toolkit

3 Comments
  •   Posted in: 
  • UWP

imageIt’s happy times for the WinRT developers: Caliburn.Micro for WinRT is here. The core parts (INPC, EventAggregator) have actually been available since the developer preview was released last year, but now Nigel Sampson and Keith Patton have done a great work and have ported the rest of the framework to WinRT. Here’s a short introduction about how to get started, aimed for those who have previous experience working with the framework.

Note: Caliburn.Micro is aimed to the XAML & C#-developers. It’s built as a class library so it cannot be used with C++.

Update 5.12.2012:The post and the sample has been updated to work with the Caliburn.Micro version 1.4.

Bootstrapper

This guide is based on the “Blank App XAML” –template. The project needs to reference the following assemblies:

  • Caliburn.Micro.WinRT
  • Caliburn.Micro.WinRT.Extensions
  • Windows.UI.Interactivity

After creating a new solution, the first thing we need is to make the Caliburn.Micro to bootstrap the application. With other supported platforms,  this is usually done with a dedicated Bootstrapper-class. But with WinRT we use a custom “Application”-class to make this happen.

Every WinRT XAML template has a class called App (in App.xaml and App.xaml.cs files). The built-in App-class inherits the Windows.UI.Xaml.Application. When Caliburn.Micro is used in WinRT, the App is replaced with a class which inherits Caliburn.Micro.CaliburnApplication. This requires two modifications. First, we change the XAML from this:

<Application 
    x:Class="caliburn_micro_winrt_getting_started.App" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="using:caliburn_micro_winrt_getting_started">

    <Application.Resources> 
        <ResourceDictionary> 
            <ResourceDictionary.MergedDictionaries>

                <!-- 
                    Styles that define common aspects of the platform look and feel 
                    Required by Visual Studio project and item templates 
                 --> 
                <ResourceDictionary Source="Common/StandardStyles.xaml"/> 
            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary> 
    </Application.Resources> 
</Application>

to this:

<cal:CaliburnApplication 
    x:Class="caliburn_micro_winrt_getting_started.App" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="using:caliburn_micro_winrt_getting_started" 
    xmlns:cal="using:Caliburn.Micro">

    <Application.Resources> 
        <ResourceDictionary> 
            <ResourceDictionary.MergedDictionaries>

                <!-- 
                    Styles that define common aspects of the platform look and feel 
                    Required by Visual Studio project and item templates 
                 --> 
                <ResourceDictionary Source="Common/StandardStyles.xaml"/> 
            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary> 
    </Application.Resources> 
</cal:CaliburnApplication>

Note that we have replaced the first and last line of the declaration. The same change must be done to code-behind:

sealed partial class App : Caliburn.Micro.CaliburnApplication 
{

Or alternatively:

sealed partial class App 
{

Our “bootstrapper” is now in place but as you can notice, the project doesn’t compile. What we need to do is to replace the code in App.xaml.cs with the initialization code required by Caliburn.Micro. As you can see, this class ends up looking like the “Bootstrapper” –class from other platforms. Here’s an example of a working App.xaml.cs:

using System;
using System.Collections.Generic;
using Caliburn.Micro;
using Windows.ApplicationModel.Activation;

namespace caliburn_micro_winrt_getting_started
{
    sealed partial class App
    {
        private WinRTContainer container;

        public App()
        {
            InitializeComponent();
        }

        protected override void Configure()
        {
            container = new WinRTContainer();
            container.RegisterWinRTServices();
        }

        protected override object GetInstance(Type service, string key)
        {
            return container.GetInstance(service, key);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return container.GetAllInstances(service);
        }

        protected override void BuildUp(object instance)
        {
            container.BuildUp(instance);
        }

        protected override void PrepareViewFirst(Windows.UI.Xaml.Controls.Frame rootFrame)
        {
            container.RegisterNavigationService(rootFrame); 
        }

        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            DisplayRootView<MainPage>();
        }
    }
}

The bootstrapper is now in place so it’s time to build the views and view models.

The view

In our bootstrapper we defined “MainPage” as the default view. The Visual Studio template already contains the MainPage so we don’t have to implement it. What is required is the view model.

The view model

Add a class called MainPageViewModel to the project. Caliburn.Micro will automatically bind the view against this class. An instance of MainPageViewModel is created and set as the data context of the MainPage. As with other supported platforms, MainPageViewModel can be inherited from a Screen-class and also the Conductor is available:

public class MainPageViewModel : Screen 
{ 
}

At this point we’re actually all set and can start adding features like we usually do with Caliburn.Micro. Our sample app still looks rather uninteresting so let’s add something simple to the view.

Conventions

Caliburn.Micro for WinRT supports conventions like the other supported Caliburn.Micro platforms. For example the framework can automatically bind a button against a view model’s method and a text box against the view model’s string-property. Let’s add a button and an empty textbox to the MainPage.xaml:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> 
    <Button x:Name="SayHello" Content="Say hello!" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
    <TextBlock x:Name="HelloText" HorizontalAlignment="Left" VerticalAlignment="Center"/> 
</Grid>

And then add the following to to MainPageViewModel:

public class MainPageViewModel : Screen 
{ 
    private string helloText; 
    public string HelloText 
    { 
        get { return helloText; } 
        set { helloText = value; NotifyOfPropertyChange(() => HelloText); } 
    }

    public void SayHello() 
    { 
        this.HelloText = "Hi from the Caliburn.Micro for WinRT!"; 
    } 
}

And we’re all set. The button named “SayHello” is automatically bound against the SayHello-method and the same happens with the textbox HelloText.

image

Conclusion

Caliburn.Micro has previously been easily the best framework to make you more productive when building apps for Windows Phone, Silverlight and WPF. And now we have the same powerful framework available for WinRT.

Links

11 Comments
  •   Posted in: 
  • UWP

image

In a Windows Phone 7 app, sending an email requires an instance of EmailComposeTask. EmailComposeTask is really easy to use and self-explanatory:

EmailComposeTask emailComposeTask = new EmailComposeTask();

emailComposeTask.Subject = "message subject"; 
emailComposeTask.Body = "message body"; 
emailComposeTask.To = "recipient@example.com"; 
emailComposeTask.Show();

In Windows 8 Metro app, there’s no EmailComposeTask. Instead, you create a “mailto” –uri and launch it:

var mailto = new Uri("mailto:recipient@example.com"); 
await Windows.System.Launcher.LaunchUriAsync(mailto);

But when compared to an EmailComposeTask, this only fills the “To” –part, but what about the “Subject” and “Body”? Well, the mailto-syntax allows you to define these as query parameters. For example, here’s an example of defining the “subject”, “body” and “to” –fields from code. The uri is automatically escaped by the platform:

var mailto = new Uri("mailto:?to=recipient@example.com&subject=The subject of an email&body=Hello from a Windows 8 Metro app."); 
await Windows.System.Launcher.LaunchUriAsync(mailto);

image

With this knowledge, it would be rather easy to build a custom EmailComposeTask for Windows 8.