Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

[WP7] Comment avoir une applicationbar bindable ?

L’API Windows Phone 7 permet d’afficher une barre de menu standard dans vos applications. Malheureusement, il n'est pas possible de faire du binding dessus, car l'object n'est pas un FrameworkElements, mais pourquoi ? Voici la réponse de Peter Torrs:

Firstly, the ApplicationBar is not a Silverlight element and thus not all Silverlight concepts apply to it. For example, you would expect that the Background could be set to any kind of brush - SolidColorBrush, LinearGradientBrush, and so on - but because the AppBar is actually rendered by the Windows Phone shell, it only supports a solid colour. You might also expect to be able to put arbitrary Content inside the buttons, or to apply RenderTransforms to the menu items, and so on. And you should be able to put it anywhere on the screen with HorizontalAlignment / VerticalAlignment, right? Again, none of these things are supported by the underlying shell UX, so they would all have to throw exceptions or no-op when called. And where's the fun in that?

Secondly, the content models in Silverlight 3 do not support controls with multiple collections of items. Some of the built-in elements have multiple collections - for example, Grid has the Children collection in addition to the RowDefinitions and ColumnDefinitions collections - but these are not actually usable outside of the "core" (they rely on PresentationFrameworkCollection, which is an internal class, and in order to maintain portability across platforms we are not adding anything phone-specific to the "core"). For the AppBar, we want both a Buttons collection and a MenuItems collection, and that's just not doable in a clean, strongly-typed API (there are hacks, such as making the AppBar a generic container and then hoping that developers only put buttons and menu items into it, but they're not good design). Silverlight 4 has addressed this limitation with the DependencyObjectCollection class, but Windows Phone is based on version 3, not 4.

Pour résumer, l’applicationBar n’est pas un objet SilverLight…voila qui est bien embêtant…

Pour répondre au besoin de binding, j’ai développé un wrapper qui le permet, il est ainsi possible d’associer des commandes sur le click d’un bouton et ainsi de respecter les patterns MVVM.

Son utilisation est très simple, prenons l’exemple d’un bouton et d’un item de menu:

<Controls:BindableApplicationBar x:Name="AppBar" BarOpacity="1.0" IsVisible="{Binding IsBarVisible}" >
  <Controls:BindableApplicationBarIconButton Command="{Binding AddCommand}" Text="Add" IconUri="/images/appbar.add.rest.png" />
  <Controls:BindableApplicationBar.MenuItems>
    <Controls:BindableApplicationBarMenuItem  Text="Settings" Command="{Binding SettingsCommand}" />
  </Controls:BindableApplicationBar.MenuItems>
</Controls:BindableApplicationBar>

Regardons maintenant les rouages du wrapper. Le wrapper est un control héritant de ItemsControl et implémentant l’interface standard IApplicationBar. Je l’ai nommé BindableApplicationBar.

[ContentProperty("Buttons")]
public class BindableApplicationBar : ItemsControl, IApplicationBar
{
    // ApplicationBar wrappé
    private readonly ApplicationBar _applicationBar;

    public BindableApplicationBar()
    {
        _applicationBar = new ApplicationBar();
        this.Loaded += BindableApplicationBar_Loaded;
    }

    void BindableApplicationBar_Loaded(object sender, RoutedEventArgs e)
    {
        // AU chargement du control, on recherche la page ou est situé le control pour lui associé notre applicationbar
        var page =
            this.GetVisualAncestors().Where(c => c is PhoneApplicationPage).FirstOrDefault() as PhoneApplicationPage;
        if (page != null) page.ApplicationBar = _applicationBar;
    }
...

L’étape suivant est d’ajouter les boutons et menuItems XAML à l’applicationbar wrappé.

protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    base.OnItemsChanged(e);
    // On efface tout, pour être sur...
    _applicationBar.Buttons.Clear();
    _applicationBar.MenuItems.Clear();
    // On récupère tous les boutons défini dans les items de l'ItemsControl
    foreach (BindableApplicationBarIconButton button in Items.Where(c => c is BindableApplicationBarIconButton))
    {
        // on les affectes a l'application bar
        _applicationBar.Buttons.Add(button.Button);
    }
    foreach (BindableApplicationBarMenuItem button in Items.Where(c => c is BindableApplicationBarMenuItem))
    {
        _applicationBar.MenuItems.Add(button.MenuItem);
    }
}

Ensuite, pour que le binding soit effectif, il suffit de rajouter les DependencyProperty.

public static readonly DependencyProperty IsVisibleProperty =
    DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(BindableApplicationBar), new PropertyMetadata(true, OnVisibleChanged));

private static void OnVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue != e.OldValue)
    {
        ((BindableApplicationBar) d)._applicationBar.IsVisible = (bool) e.NewValue;
    }
}

public bool IsVisible
{
    get { return (bool)GetValue(IsVisibleProperty); }
    set { SetValue(IsVisibleProperty, value); }
}

Le mécanisme du wrapper est plus ou moins identique pour BindableApplicationBarIconButton et BindableApplicationBarMenuItem.

Le binding peut ainsi s’effectuer sur les propriétés (Visible, Text, …), et sur les commands et commandparameter ! Je précise que le binding sur la propriété text est très pratique dans le cas d’une application multilingue. On peut ainsi appliqué du binding sur des fichiers de resources, comme expliqué dans cet article MSDN :http://msdn.microsoft.com/en-us/library/ff637520%28VS.92%29.aspx

Ce code est utilisé pour le projet Warnygo, et est bien sur disponible : Phone7.Fx.Preview.zip

Si vous avez des retours, n’hésitez pas !

Publié jeudi 19 août 2010 11:46 par Nicolas
Classé sous : ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

Pas de commentaires
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Comment utiliser MVVM avec Windows Forms par .net is good... C# is better ;) le il y a 12 heures et 21 minutes

- [Setup] Tu reboot ou tu reboot pas? par Blog de Jérémy Jeanson le il y a 16 heures et 53 minutes

- Nouveau blog par Blog Technique d'Audrey PETIT le 02-21-2012, 15:59

- Bienvenue sur le Blog de Make a Sign ! par Le Blog (Vert) d'Arnaud JUND le 02-21-2012, 10:38

- [SharePoint] Arrêter l’activation d’une feature depuis un Event Receiver par Blog de SPBrouillet (Pierrick BROUILLET) le 02-20-2012, 16:40

- Les API REST d'Azure avec ASP .NET Web API par Richard Clark le 02-20-2012, 14:31

- [Office 365] Sauvegarder et restaurer un site SharePoint grâce aux Web Services par Blog de SPBrouillet (Pierrick BROUILLET) le 02-19-2012, 19:32

- [Azure] Migrez votre base SQL Server 2008 sur SQL Azure par ALM .Net with Scrum le 02-17-2012, 22:56

- Encapsuler un appel de web service avec les Tasks par Richard Clark le 02-17-2012, 17:58

- Pattern d'appel asynchrone dans vos services par Richard Clark le 02-17-2012, 14:33