Pull to refresh for WinRT

'Pull to refresh' has been a standard UX pattern which is expected in all apps, on all platforms and I've made it available for Windows Phone 8.1 and Windows 8.1 (Universal) apps. 

The solution has been wrapped in a single control based on a ScrollViewer which is ready to use right out of the box.

<controls:PullToRefreshScrollViewer Grid.Row="1">
 ..Content goes here..
</controls:PullToRefreshScrollViewer>

Download and try the source

Pro tip: The sample works for Windows 8.1 apps as well! 

Should you need Pull to refresh for Listviews and gridviews it modifying this implementation should be fairly easy. So don't hesitate giving it a shot! 



 

Pull to refresh implementation explained

<controls:PullToRefreshScrollViewer
Grid.Row="1"
RefreshContent="PullToRefreshScrollViewer_RefreshContent"
RefreshCommand="{Binding RefreshCommand}"
ArrowColor="#004050"
RefreshText="Release to refresh"
PullText="Pull to refresh" />

This is the available properties in the control (PullToRefreshScrollViewer.cs)

  • RefreshContent (event)
  • RefreshCommand (Command)
  • ArrowColor (Brush)
  • RefreshText (string)
  • PullText (string)

The Command and event is invoked when the user pulls down and releases appropriately. 
ArrowColor is the brush of the arrow that animates once the user pulls down. During the pull there's two different texts - RefreshText and PullText
 

Deep dive

As far as I know there isn't a smooth and easy way to learn from the scrollviewer if the scrollviewer offset is negative.
After endless amounts of googling I couldn't figure out a way to do it so I decided to tweak the scrollviewer using two timers, negative margin, composite translate transform and some code to figure out whether the scrolling has a negative offset and hereby means to trigger the pull to refresh.
This solution is far from ideal (bit of a hack really) but get's the job done.


Loading the control and the timers

The control requires some timers in order to check whether the user scrolls into negative space or not. 
Note: The timers will only run as long as the scrolling offset is 0.

private void PullToRefreshScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
    timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromMilliseconds(100);
    timer.Tick += Timer_Tick;

    compressionTimer = new DispatcherTimer();
    compressionTimer.Interval = TimeSpan.FromSeconds(1);
    compressionTimer.Tick += CompressionTimer_Tick;

    timer.Start();
}


Working with the offset

Most of the magic comes from the ScrollViewers ViewChanged event which is invoked when the user scrolls. The event is fired continuously, multiple times in the second, and it tells us about the current offset. This is event looks whether we're at the top (offset 0) or not.
If we're at the top we start some timers used to determine if the users decides to pull to refresh.

private void ScrollViewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
    if (e.NextView.VerticalOffset == 0)
    {
        timer.Start();
    }
    else
    {
        if (timer != null)
        {
            timer.Stop();
        }

        if (compressionTimer != null)
        {
            compressionTimer.Stop();
        }

        isCompressionTimerRunning = false;
        isCompressedEnough = false;
        isReadyToRefresh = false;

        VisualStateManager.GoToState(this, VisualStateNormal, true);
    }
}

Determining pull to refresh by using the timers

The timer will tick every 100 ms and will use the TransformToVisual method to detect it's bound and compare it with the expected offset. If the user scrolls far enough and stays there for 1 second - which is checked by using another timer (compression timer) - a boolean property, IsReadyToRefresh, is set to true.
Once the offset goes back to 0 we check IsReadyToRefresh is true and invoke the refresh events to the world. 

private void Timer_Tick(object sender, object e)
{
    if (containerGrid != null)
    {
        Rect elementBounds = pullToRefreshIndicator.TransformToVisual(containerGrid).TransformBounds(new Rect(0.0, 0.0, pullToRefreshIndicator.Height, RefreshHeaderHeight));

        var compressionOffset = elementBounds.Bottom;
        Debug.WriteLine(compressionOffset);

        if (compressionOffset > offsetTreshhold)
        {
            if (isCompressionTimerRunning == false)
            {
                isCompressionTimerRunning = true;
                compressionTimer.Start();
            }

            isCompressedEnough = true;
        }
        else if (compressionOffset == 0 && isReadyToRefresh == true)
        {
            InvokeRefresh();
        }
        else
        {
            isCompressedEnough = false;
            isCompressionTimerRunning = false;
        }
    }
}

We use a visual state to change the text and rotate the arrow. If we after a second stayed long enough within the right bounds, we've set isReadyToRefresh to true and show the "release ready" visual state telling the user it will refresh once released.

private void CompressionTimer_Tick(object sender, object e)
{
    if (isCompressedEnough)
    {
        VisualStateManager.GoToState(this, VisualStateReadyToRefresh, true);
        isReadyToRefresh = true;
    }
    else
    {
        isCompressedEnough = false;
        compressionTimer.Stop();
    }
}


Telling the page and ViewModel about the refresh

The final part is telling the world that it should refresh the content. The world outside the control can listen by using an event or a command. If either is available they'll be raised once the InvokeRefresh is called and the control returns to it's original state.

private void InvokeRefresh()
{
    isReadyToRefresh = false;
    VisualStateManager.GoToState(this, VisualStateNormal, true);

    if (RefreshContent != null)
    {
        RefreshContent(this, EventArgs.Empty);
    }

    if (RefreshCommand != null && RefreshCommand.CanExecute(null) == true)
    {
        RefreshCommand.Execute(null);
    }
}

Final words

This is a quite hacky way to do it, but I couldn't find any other way when working with a scrollviewer. The solution is plug an play and works right out of the box. It get's the job done and hopefully this will be a lot simpler with Windows 10.

I'd love to hear your thoughts, improvement and feedback in the comments below - so don't hesitate writing something! 

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

A solution to an 'Unspecified error' when calling GetNavigationState via the SuspensionManager on Windows Phone 8.1

A common approach to communicate between page-navigations is to send data as a parameter when navigating. This parameter can all an object of all kinds - both value and reference types. However there's one gotcha: If you call Frame.GetNavigationState() and you've passed anything other than a simple type like guid, string or numerics you'll get an unspecified error and a COM exception like this:

{System.Runtime.InteropServices.COMException (0x80004005): Unspecified error

GetNavigationState doesn't support serialization of a parameter type which was passed to Frame.Navigate.
   at Windows.UI.Xaml.Controls.Frame.GetNavigationState()
   at xx.Common.SuspensionManager.SaveFrameNavigationState(Frame frame)
   at xx.Common.SuspensionManager.<SaveAsync>d__11.MoveNext()}

I passed the SelectedItem from a List as a parameter to a details page and that caused the exception when I tried to tombstone and save the page state using the SuspensionManager.

I looked up the documentation from MSDN and it's all there in black and white:

Note: The serialization format used by these methods is for internal use only. Your app should not form any dependencies on it. Additionally, this format supports serialization only for basic types like string, char, numeric and GUID types.
— https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.frame.getnavigationstate

My solution to this was to serialize my object as json using the Json.NET nuget package and sending the object as a string and deserializing it on the details page.

public void NavigateToDetails(Author author)
{
    string json = JsonConvert.SerializeObject(author, Formatting.Indented);
    NavigationHelper.NavigateTo<AuthorPage>(false, json);
}

And on the details page I simply deserialize it: 

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
     base.OnNavigatedTo(e);
     Author author = JsonConvert.DeserializeObject<Author>(e.Parameter.ToString());
     //// Do something with author
}

This is a simple work around that will work out of the box with the SuspensionManager shipped with Windows Phone 8.1. You might still run into trouble if you serialize huge lists as json and pass that as a parameter, but in those cases I'd say you're most likely doing something wrong. 

Feel free to comment and reach out if you have any comments or additions.

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

Natural User Interface implementation

Filling out forms can sometimes be a quite boring and in some cases even confusing experience for your users. One way to ease up the experience is to use a UX and interface design principle called 'Natural User Interface' (NUI) which basically means to use a familiar and real life environment which the user is already familiar with from the everyday life. 
There's a lot of other aspects in NUI but the core idea is to make the interaction between user and computer more seamless.

This sample demonstrates one approach to this principle where the user have to fill out a form by doing two selections from a list. Rather than having a static standard form with a header and a few combobox'es this approach uses a longer sentence with buttons inside which allows the user to choose a selection from a list of items. 
The buttons is highlighted using color differences and should feel completely intuitive.
 

Download source & sample


The sample both works for Windows Phone and Windows Store. 

 

Source code explained

The UI is created based on a sentence and a home made syntax indicating where we need the lists: 

var text = "I would like to see blendrocks focus more on #1 and less on #2"

First we break up the sentence into words create a list of words that should either be displayed as text or as a selection input. The hashtag indicates that we need a list of options. 
The list will be bound to an itemscontrol that will populate what looks like a textblock with some inline functionality. 


Viewmodel

public MainViewModel()
{
    ContentList = new ObservableCollection<ContentBase>();
    LoadData();
}

private void LoadData()
{
    var text = "I would like to see blendrocks focus more on #1 and less on #2";
    var words = text.Split(' ');
    foreach (var word in words)
    {
        if (word.StartsWith("#"))
        {
            var items = new List<string>();

            if (word == "#1")
            {
                items.Add("controls styling");
                items.Add("C# code samples");
                items.Add("complete designs");
                items.Add("custom animations");
            }
            else if (word == "#2")
            {
                items.Add("kitty pictures");
                items.Add("movie quotes");
                items.Add("gardening tips");
                items.Add("indoor swim guides");
            }

            ContentList.Add(new ContentSelection() { Items = items, SelectedItem = items[0], Type = ContentType.ComboBox });
        }
        else
        {
            ContentList.Add(new ContentText() { Text = word, Type = ContentType.Text });
        }
    }
}

 

Itemscontrol and the resources

As said in the UI we'll have an itemsControl which uses a template selector to show either a textblock or a button depending whether we're showing a ContentText or ContentSelection object. 
The itemscontrol will wrap it nicely like a normal sentence because it uses a custom ItemsPanel which my friend Daniel Vistisen created.

<Page.Resources>
    <DataTemplate
        x:Key="ContentSelectionTemplate">
        <local:ContentSelectionUserControl />
    </DataTemplate>
    <DataTemplate
        x:Key="TextTemplate">
        <TextBlock
            Text="{Binding Text}"
            Style="{StaticResource CustomTextBlockStyle}" />
    </DataTemplate>
    <local:MainPageTemplateSelector
        TextTemplate="{StaticResource TextTemplate}"
        ContentSelectionTemplate="{StaticResource ContentSelectionTemplate}"
        x:Name="MainPageTemplateSelector" />
</Page.Resources>

-----
<ItemsControl
    Grid.Row="1"
    Margin="0"
    ItemTemplateSelector="{StaticResource MainPageTemplateSelector}"
    ItemsSource="{Binding ContentList}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <local:WrapPanel
                HorizontalAlignment="Left"
                VerticalAlignment="Center" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>


Displaying the selection options

We use a regular button and a flyout to show the selections - everything bundled in a usercontrol called ContentSelectionUserControl.
I tried mutiple approaches including a combobox to show the selection options next to the text. After trying multiple things I concluded the flyout-button approach offered the most flexibility and extendibility so I chose to go with that one.

<UserControl
    x:Class="Natural_input.ContentSelectionUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Natural_input"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">
    <Grid>
        <Button
            Content="{Binding SelectedItem}">
            <Button.Flyout>
                <Flyout
                    x:Name="flyout"
                    FlyoutPresenterStyle="{StaticResource CustomFlyoutPresenter}">
                    <ListBox
                       x:Name="list"
                       SelectionChanged="list_SelectionChanged"
                       ItemsSource="{Binding Items}"
                       SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
                       ItemContainerStyle="{StaticResource CustomListBoxItemStyle}"
                       Style="{StaticResource CustomListBoxStyle}">
                    </ListBox>
                </Flyout>
            </Button.Flyout>
        </Button>
    </Grid>
</UserControl>

Note: To get everything to look good I created some styles and modified the templates. All this is not subject to explanation in this blogpost, but you're more than welcome to ask about it in the comments should you have any questions.


Conclusion

This is a quite new UX pattern on the app platform and it was a lot of fun to create it. It uses a lot of styles and templates to get the right visual look and feel. 
The pattern itself offers a lot of possibilities that I hope I'll investigate in the future.
I hope this has inspired you consider creating user input in new ways.
Feel free to let me know what you think in the comments or on twitter. I'd love to hear from you.
Happy coding! 

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

Facebook-like 'scroll to top' functionality in a ScrollViewer

Facebook did it some time ago! - Added this nifty button that makes you go to the top whenever you scroll upwards on Windows Phone.
A true UX gem which is almost expected by users because of its simple and intuitive nature. 

Yesterday I saw Tim Heuer tweeting how it's almost standard and something that the users expects.
My immediate thought was let's build it and bring it out there! 

 

Download the source and sample

 

 

 

Behavior and code explained

The solution is pretty straight forward. The solution is created using a custom behavior which injects a button and the necessary functionality into a scrollviewer. 
(Big shoutout to Joost van Schaik and Morten Nielsen for sharing their amazing insights on creating custom behaviors).

Using the behavior

Using the behavior is pretty straight forward. You simply set it on the scrollviewer and set the ScrollToTopButton property on the behavior with the button you want to inject and appear on top of your scrollviewer while scrolling backwards towards the top.

<ScrollViewer
    Grid.Row="1">
    <Interactivity:Interaction.Behaviors>
        <local:ScrollToTopBehavior>
            <local:ScrollToTopBehavior.ScrollToTopButton>
                <Button
                    x:Name="GoToTopButton"
                    Grid.RowSpan="2"
                    VerticalAlignment="Top"
                    HorizontalAlignment="Right"
                    Style="{StaticResource CleanButtonStyle}"
                    Margin="0,20,20,0">
                    <Image
                        x:Name="image"
                        Width="100"
                        Height="100"
                        Source="ms-appx:///Images/ToTop.png"
                        Stretch="None"
                        HorizontalAlignment="Right" />
                </Button>
            </local:ScrollToTopBehavior.ScrollToTopButton>
        </local:ScrollToTopBehavior>
    </Interactivity:Interaction.Behaviors>
</ScrollViewer>

Inside the behavior

To create a behavior we have to implement the IBehavior interface located inside the Behaviors SDK which is an extension shipped with Visual Studio and installed per default. 
The interface gives us an attach (hook into events and functionality) and detach (unhook events and functionality to allow GC) method which automatically is called when the behavior is attacted and detached to the scrollviewer.

In the attach method we hook into on following:

  • ScrollViewer.ViewChanging which is called continously while scrolling. This event is used to get offsets and see if inertia is enabled while scrolling.
  • ScrollViewer.Loaded to find the appropriate container inside the template of the scrollviewer and inject our button into.
public void Attach(DependencyObject associatedObject)
{
    offsets = new List<double>();
    isHidden = true;
    buttonAdded = false;

    if (!DesignMode.DesignModeEnabled)
    {
        _associatedObject = associatedObject;

        scrollviewer = _associatedObject as ScrollViewer;

        if (scrollviewer != null)
        {
            scrollviewer.ViewChanging += Scrollviewer_ViewChanging;
            scrollviewer.Unloaded += Scrollviewer_Unloaded;
        }

        if (ScrollToTopButton != null)
        {
            ScrollToTopButton.Tapped += ScrollToTopButton_Tapped;
            ScrollToTopButton.Visibility = Visibility.Collapsed;
            ScrollToTopButton.Name = "ScrollToTopButton";
        }
    }
}


Injecting the button

To find the right container in the scrollviewer template we create a recursive method to traverse down the visual tree until we find our grid. We create a container from our button and add it to our located grid. The container-grid is added to ensure we can animate the button. (Thanks @danielvistisen for the tip)

private void Scrollviewer_Loaded(object sender, RoutedEventArgs e)
{
    FindParts(scrollviewer);
}

private void FindParts(DependencyObject dp)
{
    if (buttonAdded == false)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dp); i++)
        {
            var control = VisualTreeHelper.GetChild(dp, i);

            if (buttonAdded == false && control is Grid)
            {
                container = new Grid();
                container.Children.Add(ScrollToTopButton);
                container.Visibility = Visibility.Collapsed;

                grid = control as Grid;
                grid.Children.Add(container);
                buttonAdded = true;
                break;
            }
            else
            {
                FindParts(control);
            }
        }
    }
    else
    {
        return;
    }
}

Determine scrolldirection and showing the button

By hooking into the ViewChanging event on the scrollviewer we'll continuously get the new offset as long as the user scrolls vertically and the there's not inertia included.

Inertia will be false while the user scrolls real slow eg. in reading scenarios. We only want to show it if the user actually shows towards the top. no

Each offset is added to a list which is used to determine our direction and our proximity towards the top. 

If the right conditions is meet we'll either show or hide the Button which allows us to scroll to the top.

private void Scrollviewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
    var offset = scrollviewer.VerticalOffset;

    if (e.IsInertial)
    {
        Debug.WriteLine(offset);
        offsets.Add(offset);
        count = count + 1;

        DetermineVisualstateChange();
    }
}

private void DetermineVisualstateChange()
{
    if (count > 4)
    {
        if (offsets[count - 1] > 250.0 && offsets[count - 2] < offsets[count - 3]
            && offsets[count - 1] < offsets[count - 2])
        {
            ShowGoToTopButton();
        }
        else if ((offsets[count - 1] > 250.0 && offsets[count - 3] < offsets[count - 2]
            && offsets[count - 2] < offsets[count - 1]) || offsets[count - 1] < 250.0)
        {
            HideGoTopTopButton();
        }
    }
}

Showing and hiding the button

Hiding and showing the button is done by a combination of using the built in FadeInThemeAnimation and setting visibility on the button.
The themeanimation animates the border inside the button template and when that's done we collapse the button visibility.

Important: If you modify the button template you need to have a control with the Name set to "border" otherwise the behavior wont to work!

private void HideGoTopTopButton()
{
    if (!isHidden)
    {
        isHidden = true;

        var storyboard = new Storyboard();
        var animation = new FadeOutThemeAnimation();
        animation.SetValue(Storyboard.TargetNameProperty, "ScrollToTopButton");
        Storyboard.SetTarget(animation, ScrollToTopButton);
        storyboard.Children.Add(animation);
        storyboard.Completed += (e,a) => { container.Visibility = Visibility.Collapsed; };
        storyboard.Begin();
    }
}

private void ShowGoToTopButton()
{
    if (isHidden)
    {
        container.Visibility = Visibility.Visible;
        isHidden = false;
        var storyboard = new Storyboard();
        var animation = new FadeInThemeAnimation();
        animation.SetValue(Storyboard.TargetNameProperty, "ScrollToTopButton");
        Storyboard.SetTarget(animation, ScrollToTopButton);
        storyboard.Children.Add(animation);
        storyboard.Begin();
    }
}

Scrolling to the top

When the button is visibile and the user invokes the Tapped event we call the ChangeView event which will set the scrollviewers vertical offset to 0 - which is at the top.
Calling the ChangeView method will trigger a offset change but that's not detected by detected by the ViewChanging event.  We call the HideGoToTopButton method to hide the button afterwards.

private void ScrollToTopButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
    scrollviewer.ChangeView(null, 0, null);
    HideGoTopTopButton();
}

 

Final words

This was infact my first custom behavior - ever. I created it after Morten Nielsen pointed out that my inspirational textbox styles implementation could have been done using behaviors.
After a few code iterations based on input from @skendrot, @dotmorten and @scottisafool I think the result is rather great. 

The final finish could be added by using @xyzzer's WinRT toolkit and have the scrolling animate all the way to the top using his scrollviewer extension but I omitted that part to avoid 3rd party dependencies. 

I hope you joined the post and find it useful. Feel free to share your thoughts, comments and maybe even improvements. I'd love to hear from you.

Happy coding!

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

Inspirational TextBox styles for Windows Phone & Store

Inspired by a post on text input effect for Web on Codrops I decided to create similar effects in xaml for Windows Store and Phone demonstrating how we can make input fields look great with a few simple animations. 

I've created different effects for the TextBox named Split, Flip, Iconic and Slide.
 

Split

The Split Style keeps the header label between the two lines. Once the textbox gets focus the header transitions upwards and gives room for the actual content. The header stays in place until the textbox loses focus and the text is removed.


Flip

When Flip gets focused the border goes to zero while the background flips up from behind. As long as there's text and focus this stays in place. When removed and focus is lost the background slides backwards out and the border tips back up.


Iconic

Iconic has a nice toned down icon. Once the TextBox gets focused the lights and scales up a bit to bring attention to the input field. Once the field is cleared it tones back down.


Slide

Slide got an overlay per default which is ready to slide out as soon as the TextBox gets focus. As long as there's focus and content it will remain in the right side. If content and focus is cleared is slides back out to the left.


Download the source and sample

The control and styles is made for proof of concept purposes and
it's not recommended for "plug and play" in a production environment.
 

 

Using the control and styles

To create these visual effects I had to create a custom TextBox (BrTextBox.cs). Applying these visual effects is rather easy once you use the custom TextBox. 
Just use the control instead of a normal textbox, set the header and apply style for the effect you want.

 <local:BrTextBox Header="First name" Style="{StaticResource SplitBrTextBoxStyle}"  />
 <local:BrTextBox Header="First name" Style="{StaticResource FlipBrTextBoxStyle}"  />
 <local:BrTextBox Header="First name" Style="{StaticResource SlideBrTextBoxStyle}"  />
 <local:BrTextBox Header="First name" Style="{StaticResource IconicBrTextBoxStyle}"  />

 

Custom control

Creating a custom control was necessary because I had to hooks into the Loaded, GotFocus and LostFocus event. In these events we check on whether we have Text or not. Based on this we set a one of two visual states: Expand or Collapse.

Each visual state will then perform a series of animations to transform the textbox visuals.

Code:

public sealed class BrTextBox : TextBox
{
    private const string StateCollapse = "Collapse";
    private const string StateExpand = "Expand";
    private bool hasFocus;

    public BrTextBox()
    {
        DefaultStyleKey = typeof(BrTextBox);
        GotFocus += BrTextBox_GotFocus;
        LostFocus += BrTextBox_LostFocus;
        Loaded += BrTextBox_Loaded;
        TextChanged += BrTextBox_TextChanged;
    }

    private void BrTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        // In case viewmodels clear the string
        if (string.IsNullOrWhiteSpace(Text) && hasFocus == false)
        {
            VisualStateManager.GoToState(this, StateCollapse, true);
        }
    }

    private void BrTextBox_Loaded(object sender, RoutedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(Text))
        {
            VisualStateManager.GoToState(this, StateCollapse, true);
        }
        else
        {
            VisualStateManager.GoToState(this, StateExpand, true);
        }
    }

    private void BrTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        hasFocus = false;

        if (string.IsNullOrWhiteSpace(Text) == true)
        {
            VisualStateManager.GoToState(this, StateCollapse, true);
        }
    }

    private void BrTextBox_GotFocus(object sender, RoutedEventArgs e)
    {
        hasFocus = true;

        if (string.IsNullOrWhiteSpace(Text) == true)
        {
            VisualStateManager.GoToState(this, StateExpand, true);
        }
    }
}

 

Animation using the UIThread

Some of the animations (SplitBrTextBoxStyle & SlideBrTextBoxStyle) requires the UIThread for to create the visual transition through animation. For this these the two styles will have the Property EnableDependentAnimation="True" in some of their animations.
This means the animation will use the UIThread rather than the compositor to animate the value change in the properties.
This is done because e.g. the width / height because these properties can't be visually transformed without the UIThread redrawing the control for each frame of the animation.
Ultimately the use of UIThread for animations can lead to a decrease in UI performance while animating and should be avoided at all costs unless you have no other option. 

 

Final words

Creating the control was rather fun and there's a lot of unexplored possibilities. The control as it is right now doesn't contain any other visual states other than Collapse and Expanded. For the same reason it's not ready for a production environment but was created to give you a general idea of the possibilities.

Feel free to reach out to me with your thoughts on the control and the styles. I'd love to hear from you in the comments. 

/Deani

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

Slide in menu control for Windows Phone

One of the most used menu navigational patterns right now is the hamburger icon / slide in menu.

The pattern is nothing something I'm particular fond of. Mainly because the icon doesn't look good, the icon takes up space and it's highly overused.
I've always felt the same way about the appbar. It doesn't look good but it's effective.

I've thought for some days how the concept two concepts could be merged together effectively and while having the full benefits of making it look good and maintaining the vertical scroll functionality. 

The solution is a two sided hidden menu wrapped in a easy to use control.
I present to you: 
SlideInMenuContentControl.
It contains one menu for navigation and one for context.

The control is free to use and available for download.

 

Download source and demo


 

 

Download source with buttons

UPDATE - SAMPLE WITH BUTTONS TO TRIGGER MENU
I've added a sample with buttons to slide out the menu's! Important: The trigger buttons need ManipulationMode="All" in order to keep the focus away from the flipvew. If the ManipulationMode isn't set the flipview keeps focus and the SelectedIndex will get reset.


Using the SlideInMenuContentControl

Using the control is simple. Simply replace the ". . ." with your xaml. 

  <local:SlideInMenuContentControl>
        <local:SlideInMenuContentControl.LeftMenuContent>
            <Grid>
                   ...
            </Grid>
        </local:SlideInMenuContentControl.LeftMenuContent>
        <local:SlideInMenuContentControl.RightMenuContent>
            <Grid>
                   ...
            </Grid>
        </local:SlideInMenuContentControl.RightMenuContent>
        <local:SlideInMenuContentControl.Content>
            <Grid>
                   ...
            </Grid>
        </local:SlideInMenuContentControl.Content>
    </local:SlideInMenuContentControl>


SlideInMenuContentControl configuration options:

Menu state (Both, Right, Left): Property to determine which sides of the menu should be visible in. Default is both.
RightSideMenuWidth: Property to determine the width of the slidein. Less than 200 is not recommended. Default is 250.0 px.
LeftSideMenuWidth: Property to determine the width of the slidein. Less than 200 is not recommended. Default is 250.0 px.
GoToMenuState(ActiveState state): Method for forcing the control to go to a specific menu.


A huge shout out to my friend Daniel Vistisen for helping and providing technical insights about building custom controls. He's one of those guys who can built anything in xaml and C#.
He's one to follow for sure! 

Feel free to tell me about your experience using the control and ask if you have any questions. 

P.S Make sure you follow me on twitter @deanihansen for more controls, design tips, articles and how-to's.

Implementing Image blur and gaussian blur in a Windows universal app

Image blur and gaussian blur can give a nice visual effect if used right . Blurred images for backgrounds can give a nice subtle effect and for the same reason it has been used by designers for years. However it's not supported out of the box on Windows Universal apps platform. Luckily there's atleast  two different nuget packages which can help us get started. 
I tried both the Lumia Imaging SDK 2.0 and Win2D. 

Between the two I see Win2D as the winner because the rendering quality, speed and amount of documentation. The downside is  that you don't have the same layout flexibility out of the box as with the Lumia Image SDK. 
Below you'll find source for both implementations.

Win2D Gaussian Blur

Tap to see the Gaussian Blur rendering. The rendering quality way better than the Lumia Image SDK.

Win2D is an easy-to-use Windows Runtime API for immediate mode 2D graphics rendering. It makes use of the Direct2D engine and utilizes the GPU to create the new blur effect. 
Win2D comes with a great documentation that gets you started in seconds and it works on both Windows Phone and Windows Store. 
The rendering is smooth, crisp and it's really really fast. Only downside is that you have to work the with the Win2D CanvasControl and CanvasBitmap rather than a regular Bitmap or WriteableBitmap.

 

Download demo and sample source

 

Lumia Image SDK 2.0

Tap to see the Blur rendering. The rendering quality not quite as good as the Win2D rendering.

The Lumia Image SDK is a Nokia and now Microsoft owned SDK created to provide easy-to-use filtering and image manipulation capabilities. The Image SDK is known for being fast - especially when working with live camera feeds and it has been a part of the platform for a while. I assume it makes use of the GPU but didn't find any material on the core of the Lumia Image SDK.

The rendering is not nearly as smooth as the Win2D though. If you compare the images it looks like the Lumia Image SDK makes use of a blur algorithm that blurs in squares. Not nearly is neat and good looking as Win2D but on the upside you get to work directly with Bitmaps and Writeable bitmaps with offers layout flexibility compared to Win2D where you have to some of the calculations yourself to get the image in the right size. 

Download source and sample

 

Wrapup

Win2D is the clear winner here though the Lumia SDK is a bit easier to implement but offers less flexibility and quality. A huge shout out to Rodrigo Diaz (@r2d2rigo) for introducing me to Win2D! He's a absolutely brilliant and clever guy. One to follow for sure! 

If you prefer not downloading the source you can find it below this section.

Feel free to contact contact me or write in the comments below if you have any questions or problems getting the sample to work.

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.


Code snippets for Lumia Image SDK blur and Win2D Gaussian Blur

Lumia Image SDK implementation:

1) This code snippet is a part of a templated control. The template contains an Image named "imagePresenter":

private async Task RenderImage()
{
    if (!string.IsNullOrWhiteSpace(ImageUrl))
    {
        var imageControl = GetTemplateChild("imagePresenter") as Image;

        if (imageControl != null)
        {
            var client = new HttpClient();

            // or get stream locally
            var stream = await client.GetStreamAsync(ImageUrl);

            var source = new Lumia.Imaging.StreamImageSource(stream, ImageFormat.Undefined);

            var source = new Lumia.Imaging.StreamImageSource(stream, ImageFormat.Undefined);

            var info = await source.GetInfoAsync();

            double displayScaling = DisplayInformation.GetForCurrentView().LogicalDpi / 96.0;


            double pixelWidth = Window.Current.Bounds.Width * displayScaling;

            var scalefactor = pixelWidth / info.ImageSize.Width;

            using (var filterEffect = new FilterEffect(source))
            {
                WriteableBitmap target = new WriteableBitmap((int)pixelWidth, (int)(info.ImageSize.Height * scalefactor));

                var filter = new BlurFilter(45);

                filterEffect.Filters = new IFilter[] { filter };

                using (var renderer = new WriteableBitmapRenderer(filterEffect, target, OutputOption.PreserveAspectRatio))
                {
                    await renderer.RenderAsync();

                    imageControl.Source = target;
                }
            }
        }
    }
}

Win2D Gaussian blur implementation

1) Create the CanvasControl which will host our Image and hook into the two events. Finally set the CanvasControl as Content on a contentpresenter or control.

control = new CanvasControl();
control.Draw += OnDraw;
control.CreateResources += OnCreateResources;
contentPresenter.Content = control;

2) Create and load the image from a URL

private async void OnCreateResources(CanvasControl sender, object args)
{
    if (!string.IsNullOrWhiteSpace(ImageUrl))
    {
        scaleEffect = new ScaleEffect();
        blurEffect = new GaussianBlurEffect();

        image = await CanvasBitmap.LoadAsync(sender.Device,
          new Uri(ImageUrl));

        imageLoaded = true;

        sender.Invalidate();
    }
}

3) On Draw we take the draw session and apply a scale effect to resize to the appropriate siize for the screen and DPI. Afterwards the blur effect is applied and finally the draw is called to render the image onto the Canvas.

private void OnDraw(CanvasControl sender, CanvasDrawEventArgs args)
{
    if (imageLoaded)
    {
        using (var session = args.DrawingSession)
        {
            session.Units = CanvasUnits.Pixels;

            double displayScaling = DisplayInformation.GetForCurrentView().LogicalDpi / 96.0;

            double pixelWidth = sender.ActualWidth * displayScaling;

            var scalefactor = pixelWidth / image.Size.Width;

            scaleEffect.Source = this.image;
            scaleEffect.Scale = new Vector2()
            {
                X = (float)scalefactor,
                Y = (float)scalefactor
            };

            blurEffect.Source = scaleEffect;
            blurEffect.BlurAmount = Blur;

            session.DrawImage(blurEffect, 0.0f, 0.0f);
        }
    }
}

HorizontalScrollViewerStyle and VerticalScrollViewerStyle for Windows apps

Back in the days when Windows Store 8.0 was shipped Microsoft bundled a nice set of standard styles in the file StandardStyles.xaml. While I never used most included styles there was two styles I used repeatedly through out almost all projects and that was HorizontalScrollViewerStyle and VerticalScrollViewerStyle.

The benefits of the two styles was that they disabled all the properties you would expect to be disabled. With the styles your scrollviewer disables zoom and only scrolls the appropriate direction. 

HorizontalScrollViewerStyle

 <Style
        x:Key="HorizontalScrollViewerStyle"
        TargetType="ScrollViewer">
        <Setter
            Property="HorizontalScrollBarVisibility"
            Value="Auto" />
        <Setter
            Property="VerticalScrollBarVisibility"
            Value="Disabled" />
        <Setter
            Property="ScrollViewer.HorizontalScrollMode"
            Value="Enabled" />
        <Setter
            Property="ScrollViewer.VerticalScrollMode"
            Value="Disabled" />
        <Setter
            Property="ScrollViewer.ZoomMode"
            Value="Disabled" />
    </Style>


VerticalScrollViewerStyle

    <Style
        x:Key="VerticalScrollViewerStyle"
        TargetType="ScrollViewer">
        <Setter
            Property="HorizontalScrollBarVisibility"
            Value="Disabled" />
        <Setter
            Property="VerticalScrollBarVisibility"
            Value="Auto" />
        <Setter
            Property="ScrollViewer.HorizontalScrollMode"
            Value="Disabled" />
        <Setter
            Property="ScrollViewer.VerticalScrollMode"
            Value="Enabled" />
        <Setter
            Property="ScrollViewer.ZoomMode"
            Value="Disabled" />
    </Style>

Feel free to let me know in the comments if you have any comments or improvements to the code above.

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

A complete guide to working with custom fonts in your Windows and Windows Phone app

One thing that really make an app stand out is using a special font or typeface (set of fonts in different weights and styles). It's not without reason that it's said that the font / typeface is one of the corner stones in visual indentity branding.
The Windows apps use Segoe UI and Segoe WP per default. While they are both very modern, simplistic, highly modern and current they are used by almost all apps out there. 
That alone is a reason for you to choose a custom font. No body wan'ts a product that looks just like all the others on the shelf.

A lot of wildly popular fonts such as Proxima Nova and Helvetica comes with a pretty high cost (+1,000$) for a independant developer. 
Fortunately there's free and absolutely beautiful alternatives out there!  
A great place to start is at Google Fonts. They got a lot of nice and free fonts mostly used in webdevelopment but they work perfectly in your app too. Find a typeface you like, download it and let's implement it in our app.

Implementing a custom font in Windows or Windows Phone app

1) First, you need to include the font or fonts in the project. Simply add the font to the project as a file.

 

 

 

 

2) Secondly, you need to change the build setting for the font files.
The setting should say:
Build action - Content
Copy to Output directory - Copy if newer

You can change the build settings by selecting the files, right click and say properties to see the properties panel, and change the settings.

 

 

3) Refer to the new font wherever you want to use it.
The easiest way to do that is by using the designer in Blend and Visual Studio 

You'll find your fonts which is added to the project at the very top of the items list.  If they are not there, something is wrong at you might want to fall back to the way it's done in VS2013. More about that later. 

 

4) In the code you should see one of these two in your code: 

 <TextBlock
FontFamily="ms-appx:///Fonts/HelveticaNeueLTStd-Md.otf#Helvetica Neue LT Std"
FontSize="16"
 Text="Hello stranger! Thank you for visiting blendrocks" />

or this:  FontFamily="Fonts/HelveticaNeueLTStd-Md.otf#Helvetica Neue LT Std"

Here's a few very important things that you need to know when working with custom fonts:

  • The font weight property should never be used. Always refer the correct font and set the weight using the file instead. 
  • If you add the font familiy to a resource dictionary you might want to reference the absolute path ms-appx:/// rather that using a relative path.
  • This part "#Helvetica Neue LT Std" is a suffix containing the Preferred Family. It's required in order for the custom font to work. The suffix has to be absolutely correct and it's case sensitive.
    • The easiest way to find it is to use a tool called dp4 font viewer. It's free and can be found downloaded here. Find your font through the program, go to the info tap copy the Preferred Family.
    • Do NOT use Windows built in font viewer as it does NOT show the correct preferred font family.

Using this technique, you should be able to use all open type and true type fonts inside your Windows and Windows Phone app.

Pro tip: To make it easier for your self, you can create a FontFamily resource inside your resource dictionary and just reference it wherever you want to use the custom font.
Your xaml could look something like this:

<FontFamily x:Key="HelveticNeueMediumFont">
Fonts/HelveticaNeueLTStd-Md.otf#Helvetica Neue LT Std</FontFamily>
nd 
<TextBlock
FontFamily="{StaticResource HelveticNeueMediumFont}"
FontSize="16"
Text="Hello stranger! Thank you for visiting blendrocks" />

For Visual Studio and Blend 2013 users

Unfortunately the dropdown containing fonts doesn't work nearly as good. So you have to write it in xaml instead. But if you use the dp4 font viewer tool and you copy the xaml shown above, you should be good to go. If not please reach out to me and I'll do my best to help you.

For Windows Phone 8 developers

To implement a custom font on Windows Phone 8 you have to reference the font a bit different. You should not use ms-appx://. Instead you should write "/AssemblyNameHere;component/path".
The assemblyname is usually the same as the project. But to be sure, remember to check it by right clicking the project in the solution explore and selecting properties. In the popup you'll find the Assembly name right away. In this sample the Assembly name is AwesomeFontSample:

<TextBlock 
Text="&#xF004;"
FontFamily="/AwesomeFontSample;component/Fonts/fontawesome-webfont.ttf#FontAwesome" 
/>

You can download a Windows Phone 8 sample here.


Wrap up

Working with custom fonts is easy when working with TextBlocks  But if you want to use is through out the app you might have to modify the template of certain controls suchs a ComboBox and Buttons.
Feel free to reach out to me if you need help on implementing your custom fonts or if you have comments or feedback to the article above. 

Happy coding!

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

Custom Page Template for Windows apps

In almost all Windows 8 or phone project involved in we use a create custom page class which derives from the Page class. Doing this allows you centralize functionality such as:

  • Providing a global appbar across your entire app
  • Centralized isloading indicator management
  • Setting different visual states depending on screen resolution

It's very simple to do. Almost too simple. You create a class in your solution inheriting the Page class:

namespace MyApp.Base
{
public abstract class PageBase : Page
{
 protected PageBase()
 {
 // Create global appbar 
 // Hook into this.SizeChanged to set visual states depending on screen width // Create centralized loading mechanism
 }
}
}

To use the new class called PageBase you need to inherit and use that on your xaml.cs and xaml pages. Replace your code with this;

<base:PageBase
xmlns:base="using:MyApp.Base"
....

and in you code behind you should have this instead;

public sealed partial class MainPage : PageBase

That's all it takes guys! - Using his simple maneuver you can now extend the functionality as much as you like and save your self a ton of duplicated code across pages inside your app and it only takes 5 minutes to set up. 

Pro tip: You can do the exact same thing for UserControl's!

Feel free to let me know if you have any issues with the implementation.
 

Extra: More on OTF and TTF fonts in Windows Phone and Store apps

Working with custom fonts in Windows Store and Windows Phone projects requires you to know the 'full font name'. Make sure you get the name completely right, otherwise it wont work! Most of times the name in the character map tool built in to Windows works. - How ever sometimes it does not display the right one, depending on how you have installed the font.

To aviod having a TTF font not working or a OTF font not working in Windows Store or Windows Phone, I recommend using this tool http://us.fontviewer.de/download.html to the right full font name.

In case everything else fails: Remember you always enable 'copy always' for the files, and set the build properties to Content

In xaml you can create font families and reference them on your page:

<Page.Resources>
<FontFamily
x:Key="FontBook">ms-appx:////Styles/Fonts/FuturaStd-Book.otf#Futura Std</FontFamily>
<FontFamily
x:Key="FontHeavy">ms-appx:////Styles/Fonts/FuturaStd-Heavy.otf#Futura Std</FontFamily></Page.Resources>

If you are facing any problems working with fonts - let me know on twitter, and I'll help you out.

Guides in Blend now available for Windows Store, Windows Phone and WPF projects

Guys, great news has arrived. The guide functionality in Blend first demoed by Unni Ravindranathan at the 2013 //build/ conference is now available for all xaml platforms in Blend with the release of Visual Studio 2013 RTM. If you haven't checked out Unni's session, make sure you do - It's really good and give a great insight into what's new for xaml in Visual Studio 2013 and Blend. You can find the session right here; http://channel9.msdn.com/Events/Build/2013/3-321 Dante Gagne wrote a nice blog post covering this topic as well, make sure you check it out.

This small tool guide tool included in Blend will help you achieving pixel perfect design and make the process much easier.

And hey - you can actually export the guides and use them on multiple pages - how cool is that!

Happy coding and thanks for reading, Deani

Convert Hexcodes to Color

Converting a color code string, e.g. "#FFB4DA55" to a Windows.UI.Color proved to be not that simple. Atleast there isn't any buildin way of doing so, so I decided to create a little helper class to do so.

The HelperClass both converts ARBG (colors with alpha values included and RGB colors to Windows.UI.Color.

 

 public class ColorConverterHelper
 {
 public Color HexToColor(String hexString)
 {
 Color outputColor;

 if ((hexString.StartsWith("#")) && (hexString.Length == 9))
 {
 byte a = Byte.Parse(hexString.Substring(1, 2), System.Globalization.NumberStyles.HexNumber);
 byte r = Byte.Parse(hexString.Substring(3, 2), System.Globalization.NumberStyles.HexNumber);
 byte g = Byte.Parse(hexString.Substring(5, 2), System.Globalization.NumberStyles.HexNumber);
 byte b = Byte.Parse(hexString.Substring(7, 2), System.Globalization.NumberStyles.HexNumber);
 outputColor = Color.FromArgb(a, r, g, b);
 }
 else if ((hexString.StartsWith("#")) && (hexString.Length == 7))
 {
 byte r = Byte.Parse(hexString.Substring(1, 2), System.Globalization.NumberStyles.HexNumber);
 byte g = Byte.Parse(hexString.Substring(3, 2), System.Globalization.NumberStyles.HexNumber);
 byte b = Byte.Parse(hexString.Substring(5, 2), System.Globalization.NumberStyles.HexNumber);
 outputColor = Color.FromArgb(255, r, g, b);
 }
 else
 {
 outputColor = Colors.White;
 }

 return outputColor;
 }
 }

I hope the source helps you solving problems like it did for me. Feel free to reach out to me if you have any questions or problems with the implementation.

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

Resources used in WDID Talk in Aarhus, 10/05/13

The 10th of may I talked about "The power of design and animation for Windows Phone" on behalf of the Windows Phone Developers Group in Denmark. Thanks to everyone that attended. I hope you've enjoyed the session.

The slides and the complete source is available here:

SLIDES: http://sdrv.ms/16lAYjQ

SOURCE: http://sdrv.ms/10mCASa

 

Thanks again for joining in.

@deanihansen

Two free online talks this month!

Windows Developers in Denmark is hosting two talks this month, and the first one is already TONIGHT! Tonight!!!: ONLINE TALK WITH FILIP SKAKUN FROM THE FRESH PAINT TEAM

In this talk Filip Shakun, from the Microsoft Fresh Paint team, talk about how it is to come back to Microsoft Corp. for the 3rd time. Filip Shakun tried different positions both in and outside Microsoft hunting for the perfect UX job. He’s highly specialized in the XAML technology and is they one behind the WinRT XAML Toolkit.

http://windev02.eventbrite.com

MOVE YOUR WINDOWS PHONE APP TO WINDOWS 8 The second talk is our own Thomas Martinsen, from Bluefragments, that talks about porting apps from Windows Phone to Windows 8. The talk focuses on Portable Class Libraries and the patterns that should be used when working with code across multiple platforms. The talk faces some of the challenges as well as different degrees of portability that can be achieved using Portable Class Libraries.

Both talks is held online and requires the Lync 2010 Attendee Client. You can download the client on http://lync2010.windowsdev.dk

CollectionViewSource and design time data in Blend

The CollectionViewSource is nothing new - it exists both in WPF and Silverlight and in Windows Store applications. This sample will be based on using it in a Windows Store app, but the basic principles applies to all platforms.

The CollectionViewSource works as a Proxy over a collection classes and enables grouping. This is extremely powerful if we are working with a GridView and we want to display groups of collections.

Example on group of collections:

WorkshopViewModel that contains a list of groups - List<Group> Groups - and each group contains a list of members - List<GroupMembers> Members

And instead of having a ItemsControl that first creates all the groups for us, and then inside each item, having a new items control that then displays all the members, we can use the CollectionViewSource and a GridView and voilà – job now done.

 

Creating a CollectionViewSource with designtime data in Blend

Sorry, too good to be true - it can’t be done. At least not by using the UI, as far as I know. But what we can do instead is typing in the XAML we need for it, and placing it inside the resource dictionary of the page.

<common:LayoutAwarePage.Resources>

<CollectionViewSource
x:Name="csv"
Source="{Binding Groups}"
ItemsPath="Members" 
IsSourceGrouped="true"
d:Source="{BindingGroups,Source="{d:DesignInstance Type=viewmodel:WorkshopViewModel, IsDesignTimeCreatable=True}}" />
 </common:LayoutAwarePage.Resources>

Here’s works:

x:Name: This is the name of the collection, we’ll use this when we are referencing it from our GridView

Source: This is where we create the binding to the parent groups in the ViewModel.

ItemsPath: This is the name of the collection inside the parent. So we have a group, and this contains a collection of members called Members.

IsSourceGrouped: Indicates whether our source, is already grouped, which it is, since it is a collection containing items, where each item has its own collection.

If you just put in the following XAML:

<CollectionViewSource x:Name="csv" />

You can see the the CollectionViewSource is available through the resource tab inside Blend.

Here we can set our bindings, and put in the ItemsPath, and IsSourceGrouped.

 

One last line that’s VERY VERY important, is this one:

d:Source="{Binding Groups, Source={d:DesignInstance Type=viewmodel:WorkshopViewModel, IsDesignTimeCreatable=True}}"

Because the CollectionViewSource works as a proxy and is referenced as a StaticResource is does not get the your design time data even if you’ve set the d:DataContext on the Page.

Therefore you’ll need to set your design time data explicitly on the CollectionViewSource itself too.

And voilà – you got design time data.
 

Making it work with a GridView.

I’ve used the CollectionViewSource on a GridView

One thing here you should note is, when you are creating your binding for the GridView. Do NOT drag the resource onto the GridView, from the Resource tab in Blend. This will create an incorrect binding!

Make sure your binding looks like this:

<GridView ItemsSource="{Binding Source={StaticResource csv}}" />

With the Binding source being a StaticResource.

You can download my complete sample with the CollectionViewSource, Design time data and everything, right here.

Feel free to reach out to me if using a CollectionViewSource gives you any problems.

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

Video: Increase your XAML Productivity with Blend for Visual Studio (1h 27 min)

The 26th of March I held a webtalk online on increasing the XAML productivity with Blend for Visual Studio.

Per request the webtalk was recorded and is now available.

The slides and the source code is available through this link http://sdrv.ms/ZgFMoL

The talk was hosted by the Windows Developers in Denmark usergroup.

Thanks for reading.
I hope you’ll enjoy the video. If you have any comments or feedback feel free to reach out to me in comments or on twitter @deanihansen.