Showing posts with label Converter. Show all posts
Showing posts with label Converter. Show all posts

25 June 2014

Building multi-resolution popups in an Universal App

With the advent of Windows Phone 8.1 multiple resolution are now also becoming a fact of life for Windows Phone developers (as they already were for Windows Store developers). This has a few side effects, which will require you to think about the size of things that need to cover part of the screen. Particularly you can get into trouble with animations built with visual states, which tend to have fixed number in them. I already dabbled into this when I ported Two Phone Pong to Windows 8.1 and had to make a screen-size independent scroll-into-view pane.

Now, for a demo app I have been working on, I wanted to make a popup that always used 1/3th of the screen. That used to be easy, as we had only one resolution, more or less, and for the rest Windows Phone 8 did some auto scaling. Now it needs a little more work. Very little, as it turned out I almost had all the pieces in place:

  • I already had the UnfoldBehavior, which can be used to let UI elements appear and disappear by ‘folding’ them in or out.
  • I already had the SizeListenerBehavior, which allows me to data bind to the actual width and height of an element

The only thing that was missing was this very simple converter:

using System;
using System.Globalization;

namespace WpWinNl.Converters
{
  public class PercentageConverter : BaseValueConverter
  {
    public override object Convert(object value, Type targetType, 
      object parameter, CultureInfo culture)
    {
      double parmValue;
      if (value is double && double.TryParse(parameter.ToString(), 
                                            out parmValue))
      {
        value = (double)value * parmValue / 100;
      }
      return value;
    }

    public override object ConvertBack(object value, Type targetType, 
      object parameter, CultureInfo culture)
    {
      if (value is double && (parameter is double || parameter is int))
      {
        value = (double)value / 100 / (double)parameter;
      }
      return value;
    }
  }
}

Basically all it does is dividing the incoming value by 100 and multiply it with the parameter value, so if you supply “33” as ConverterParameter is will multiply the incoming value by 0.33, effectively dividing it by 3 (ish).

So now I can define a simple app with a map (for instance), and when the popup is displayed, it always takes the bottom third of the screen. Regardless of screen resolution.

On the 4” WVGA (800x480) emulator this looks like this:

imageimage

While on a 1080p 6” emulator it looks like this:

imageimage

Added bonus: the popup resizes automatically as you rotate the phone. It always takes the bottom third of the screen.

The way to achieve this is pretty simple:

<!-- some stuff snipped -->
<Grid>
  <interactivity:Interaction.Behaviors>
    <behaviors:SizeListenerBehavior x:Name="ContentRootListener"/>
  </interactivity:Interaction.Behaviors>
  <!-- some stuff snipped -->
  
  <Grid Grid.Row="1" Margin="19,9.5,19,0">
    <maps:MapControl>
    </maps:MapControl>
    <!-- the Grid below is the actual popup window -->
    <Grid Height="{Binding WatchedObjectHeight, 
      ElementName=ContentRootListener, Converter={StaticResource PercentageConverter}, 
      ConverterParameter=33}" VerticalAlignment="Bottom" Background="Blue" >
      <interactivity:Interaction.Behaviors>
        <behaviors:UnfoldBehavior RenderTransformY="1" Direction="Vertical" 
                                  Activated="{Binding IsOpen}"/>
      </interactivity:Interaction.Behaviors>
      <StackPanel>
        <TextBlock Text="This is my popup" FontSize="22"></TextBlock>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="It's height is:" FontSize="22"></TextBlock>
          <TextBlock Text="{Binding WatchedObjectHeight, 
            ElementName=ContentRootListener, Converter={StaticResource PercentageConverter}, 
            ConverterParameter=33}" 
            FontSize="22"></TextBlock>
        </StackPanel>
      </StackPanel>
    </Grid>
  </Grid>
</Grid>
  • The top grid has a SizeListenerBehavior. This enables me to bind to the ActualHeight and of the top grid as it changes by binding to WatchedObjectHeight of the SizeListenerBehavior.
  • The height of the popup is set like by actually binding to said value. By using the converter with a parameter of 33 it will – when activated – always use 1/3rd of the screen – or 1/3rd of whatever element your SizeListenerBehavior is sitting on.
  • By setting the UnfoldBehavior’s RenderTransformY to 1 and the Direction to Vertical the popup will open from the bottom up. This is not new, that was already in the original article about UnfoldBehavior.

I have included a small demo solution to show off this little trick. It uses my pre-release version of WpWinNl 2.0.x and a little MVVMLight, but you could use this just as easy from a non-MVVM app. But I still think the beauty of this all is that can be initiated from a viewmodel ;)

In addition, although the demo solution only contains a Windows Phone app, this trick works just as well as on Windows Store apps, as both UnfoldBehavior and SizeListenerBehavior are already in the new WpWinNl NuGet package.

07 November 2013

Build for both-Converters for Windows Phone 8 and Windows 8.1 Store apps

Our Dutch DPE Rajen Kishna has been quite heavily banging the ‘build for both’ drum for some time now, and after having spent quite some time on exclusive Windows Phone development I decided to see if I could rewrite the code for my wp7nl library on codeplex so that it would easily work for Windows Phone 8 and Windows 8.1 store apps – of course in order to be able to port my apps for Windows 8.1 as well.

At the moment of this writing I am still very much in the process of converting and rewriting code, but here is already the first part one of my yet-to-be-decided-number-of-parts of a build-for-both series – about how to write a ValueConverter that runs on both platforms. Since this handles about UI stuff, I could not go PCL, so I chose the shared code route.

ValueConverters need to implement IValueConverter, a simple two-method interface. In Windows Phone (and Silverlight and WPF) this interface is

Convert(object value, Type targetType, object parameter, CultureInfo culture);
ConvertBack(object value, Type targetType, object parameter, 
CultureInfo culture);
So far so good, but for some reason I not have fathomed yet Microsoft have decided to change the IValueconverter signature to
Convert(object value, Type targetType, object parameter, string culture);
ConvertBack(object value, Type targetType, object parameter, string culture);

This is something you can moan about, or something you can solve. I choose the latter part. First of all, I implemented an abstract  base class BaseValueConverter

using System;
using System.Globalization;
#if WINDOWS_PHONE
using System.Windows.Data;
#else
using Windows.UI.Xaml.Data;
#endif

namespace WpWinNl.Converters
{
  public abstract class BaseValueConverter : IValueConverter
  {
    public abstract object Convert(object value, Type targetType, 
object parameter, CultureInfo culture); public abstract object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture); public object Convert(object value, Type targetType, object parameter,
string language) { return Convert(value, targetType, parameter, new CultureInfo(language)); }
public object ConvertBack(object value, Type targetType, object parameter,
string language) { return ConvertBack(value, targetType, parameter,
new CultureInfo(language)); } } }

Important to know is that the IValueConverter interface is defined in namespace System.Windows.Data in Windows Phone, and in Windows.UI.Xaml.Data in Windows Store apps. Fortunately, every Windows Phone project automatically defines the WINDOWS_PHONE conditional compilation symbol, so we can put these #if-#else-#endif preprocessor directives in code.

Then I took this little converter, that translates bound text to uppercase (written by my felllow MVP Timmy Kokke and added to wp7nl by him some months ago), and rewrote it using BaseValueConverter:

using System;
using System.Globalization;
using System.Windows;
#if !WINDOWS_PHONE
using Windows.UI.Xaml;
#endif

namespace WpWinNl.Converters
{
  /// <summary>
  /// Converts a string to all uppercase letters
  /// </summary>
  public class ToUpperValueConverter : BaseValueConverter
  {
    public override object Convert(object value, Type targetType, 
                           object parameter, CultureInfo culture)
    {
      if (value == null) return string.Empty;

      return value.ToString().ToUpperInvariant();
    }

    public override object ConvertBack(object value, Type targetType, 
                                       object parameter, CultureInfo culture)
    {
      return DependencyProperty.UnsetValue;
    }
  }
}

And that is all. So how does this work? In Windows Store apps, the methods with the string language parameters are called, but these make a CultureInfo from the string, and then call the methods using a CultureInfo. In Windows Phone, the CultureInfo using methods are directly called, and the string language parameters are simply not used. I could have put these between preprocessor directives as well, but I am not very fond of too much directives in the code itself.

I have included a sample Windows Phone and Windows Store app, that both show a button with the text HELLO in capitals, using this converter. The shared source code comes from a separate directory and is added as link to both solutions. When my library is finished you will of course simply make a reference to the right library, but in the mean time you know how you can write cross platform converters yourself.

Update: irony has it that Rajen itself, in the article I link to, refers to his October starter template which incorporates a BaseConverter class. His approach is a little bit different from mine, but the general idea - using shared code for a base class - is the same. As I apparently downloaded the wrong zip file from his SkyDrive, I did not see it before I wrote this. I don't know if this is prior art, but I like to be thorough in this. Great minds apparently think alike, and I sure was inspired by his (older) template to use shared code.

19 February 2011

Converter to convert a single item to a list to enable Bing Maps binding

Some things are so simple that I wonder if I am missing something. I tried to bind a single GeoPosition to Bing Maps’ MapItemsControl ItemsSource on Windows Phone 7 and it did not work. Apparently Bing Maps want a list. So I wrote the following converter:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Data;

namespace LocalJoost.Convertors
{
  public class ToListConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, 
                          CultureInfo culture)
    {
      var l = new List<object> ();
      if (value != null) l.Add(value);
      return l;
    }

    public object ConvertBack(object value, Type targetType, object parameter, 
                              CultureInfo culture)
    {
      return null;
    }
  }
}
A masterpiece of complicated coding, right :-/ ? Anyway, in stead of
<Microsoft_Phone_Controls_Maps:MapItemsControl 
  ItemsSource="{Binding GpsLocation}">
I used
<Microsoft_Phone_Controls_Maps:MapItemsControl 
  ItemsSource="{Binding GpsLocation, Converter={StaticResource ToListConverter}}">

and Lo and Behold – now I did get my single icon on the map.

I have no idea if this is a quirk of the Bing Maps control or that ItemsSource attributes in general only accept lists – but either way, this works.

28 November 2010

Converter for showing/hiding Silverlight/WP7 GUI elements with boolean model values

Typically, when you are writing Silverlight or Windows Phone 7 applications using the MVVM pattern, your viewmodels expose boolean values indicating something is going on – for instance, your background thread is loading data and thus you want to display a progress bar. For some reason the designers of XAML choose the Visibility property of a GUI element to be of type “Visibility”, an enum with values “Visible” and “Collapsed”.

Anyway, you could of course make your viewmodel expose properties of type Visibility but as an architect I would call that “bad form”. It’s so much easier to create a simple converter for this:

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace LocalJoost.Converters
{
  public class VisibilityConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, 
                   CultureInfo culture)
    {
      if (value != null && value is bool && parameter != null)
      {
        var bValue = (bool) value;
        var visibility = (Visibility)Enum.Parse(
             typeof (Visibility), parameter.ToString(),true);
        if (bValue) return visibility;
        return visibility == 
           Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
      }
      
      return null;
    }

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

This convertor assumes a boolean input value and a parameter of type Visibility. If the input value is true, it returns the value in the parameter, if it is false, it returns the opposite. So it’s basically a converter that converts one type of boolean in another, with the possibility to specify what is true and what is false ;-)

Typical usage snippet:

Visibility="{Binding IsDownloading, Converter={StaticResource VisibilityConverter}, ConverterParameter=Visible}"

which says that if the “IsDownloading” property of the model is true, the GUI object in case must be visible. Since I actually used this snippet on a Windows Phone 7 ProgressBar, this makes perfect sense.