Статьи

Написание поведения в PCL для Windows Phone 8.1 и Windows 8.1

Я с радостью добавил поддержку  Windows Phone  8.1 в  WpWinNl  — или фактически поддержку универсальных приложений Windows — когда заметил, что в основном все, что я делал, это связывал файлы из проекта Windows 8.1 — без каких-либо изменений. Сходимость FTW! Хотя это делало добавление поддержки довольно простой, я также предполагал, что ко мне придет кошмар обслуживания, так как мне потребуется не только поддерживать связь между Windows 8.1 и Windows Phone 8.0, но также между Windows 8.1 и Windows Phone 8.1. Может быть, пришло время перейти на PCL. Это шло довольно хорошо, пока я не столкнулся с серьезным препятствием.

Этот блокпост, друзья мои, очень прост: SDK для поведения и, следовательно, все, что есть в Microsoft.Xaml.Interactivity , недоступно для PCL. Вероятно, это связано с тем, что SDK для Windows 8 для поведения является отдельной сущностью, а не частью базовой структуры, которая была добавлена ​​позже. Эта же характеристика перешла на Windows Phone 8.1. Это в основном сводится к следующему: каждое поведение должно реализовывать  Microsoft.Xaml.Interactivity.IBehavior и это сидит в двух отдельных сборках — одна для Windows, другая для Windows Phone. На моем Surface Pro они находятся в C: \ Program Files (x86) \ Microsoft SDKs \ Windows \ v8.1 \ ExtensionSDKs \ BehaviorsXamlSDKManaged \ 12.0 \ для Windows 8.1 и в C: \ Program Files (x86) \ Microsoft SDKs \ WindowsPhoneApp \ v8.1 \ ExtensionSDKs \ BehaviorsXamlSDKManaged \ 12.0 \ для Windows Phone 8.1

Что теперь?

Конечно, как я писал ранее, вы можете стонать и жаловаться на это и посылать ядовитые твиты Джо Бельфиоре, Клиффу Симпкинсу или просто первому Windows Phone MVP, которого вы можете найти — или вы можете остановиться и подумать, есть ли способ обойти это. Это то, что я люблю делать — исследовать, мыслить, изобретать — и прийти к решению. Потому что, как оказалось, мои друзья, есть один.

образКлючом к решению является  эта довольно странная статья,  которая, верьте или нет, была отправлена ​​в закрытый список рассылки MVP Windows Phone для того, чтобы узнать мнение не кого иного, как  Мэттис Хоекстра , всего за день до того, как я написал это. Тогда я думал, что это сумасшедшая идея — сегодня я нахожу это бесценной подсказкой. Говоря о совпадениях. Это как фундаментальное исследование — вы никогда не знаете, откуда придет следующая жизненно важная идея ;-).

It’s called the “Bait and switch PCL trick” and it basically makes use of a very odd NuGet trick – it will always prefer a native platform library over a PCL. If you make a NuGet package that contains both a PCL and a native version, the PCL is then only used at compile time. So what I did was create this crazy NuGet package called CrossPlatformBehaviorBase (I think I am going to change that later) that basically has three projects in it: once PCL, one Windows 8.1, and one Windows 8.1.

The key thing is that the implementation for Windows Phone and Windows are the same (in fact, the Windows Phone 8.1 implementation is linked from the Windows implementation, but the PCL is slightly different. The PCL version is:

namespace IBehaviorBase
{
  public interface IBehaviorBase 
  {
  }
}

indeed, an interface that does totally nothing. Whereas the Windows Phone and Windows implementations look like this:

using Microsoft.Xaml.Interactivity;

namespace IBehaviorBase
{
  public interface IBehaviorBase : IBehavior
  {
  }
}

образIf you build for release this will create three projects neatly conforming to NuGet naming specifications and finally run a little command file that creates a NuGet package in the output folder directly under the solution folder.

To be able to install the package, you need to specify that folder as a NuGet source:

образ

образMind you, in the future this will all be on NuGet so you won’t have to put up with this. For now,  am just explaining how it’s done.

Anyway, I went ahead and created an Universal Windows App DemoApp, and added a Windows Phone 8.1/Windows 8.1 PCL “WpWinNl” to the solution.

The newly created NuGet package needs to be installed in all three projects: 

образAnd then I started in copying stuff from WpWinNl,starting with the Behavior base class that I created to maintain compatibility with ‘Silverlight style’ behaviors:

using Windows.UI.Xaml;

namespace WpWinNl.Behaviors
{
  public abstract class Behavior : DependencyObject, IBehaviorBase.IBehaviorBase
  {
    public DependencyObject AssociatedObject { get; set; }

    public virtual void Attach(DependencyObject associatedObject)
    {
      AssociatedObject = associatedObject;
    }

    public virtual void Detach()
    {
    }
  }
}

The key part is highlighted in red and underlined – in stead of directly implementing IBehavior, the class implements my go-between IBehaviorBase.IBehaviorBase. The name is a bit of a kludge, I’ll be the first one to admit that, but it does the trick. And you won’t see it ever anymore, because it’s only used a a base class for Behavior<T>:

namespace WpWinNl.Behaviors
{
  public abstract class Behavior<T> : Behavior where T: DependencyObject
  {
    [System.ComponentModel.EditorBrowsable(
       System.ComponentModel.EditorBrowsableState.Never)]
    public new T AssociatedObject { get; set; }

    public override void Attach(DependencyObject associatedObject)
    {
      base.Attach(associatedObject);
      this.AssociatedObject = (T)associatedObject;
      OnAttached();
    }

    public override  void Detach()
    {
      base.Detach();
      OnDetaching();
    }

    protected virtual void OnAttached()
    {
    }

    protected virtual void OnDetaching()
    {
    }
  }
}

образWhich basically comes over from the existing WpWinNl implementation unchanged. For kickers, I then put my infamous DragFlickBehavior, and all my animation code that has been featured on this blog multiple times on top of it and lo and behold – with a minor addition it just works. And I was able to delete all #ifdef statements for Windows Phone out, leaving only the former ‘just windows’ code. All in PCL. And all usable both on Windows Phone and Windows.

образ
As coupe de grace I put the whole of MainPage.xaml in the shared portion, opened Blend and sure enough – there was my DragFlickBehavior:

So I dropped a date picker, a text box and a button on the screen and dragged my PCL-DragFlickBehavior on top on all three:

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    
    xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
    xmlns:behaviors="using:WpWinNl.Behaviors"

    x:Class="DemoApp.MainPage"
    mc:Ignorable="d">

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Button Content="Button" HorizontalAlignment="Left" Height="37" 
      Margin="101,114,0,0" VerticalAlignment="Top" Width="108">
      <Interactivity:Interaction.Behaviors>
        <behaviors:DragFlickBehavior/>
      </Interactivity:Interaction.Behaviors>
    </Button>
    <DatePicker HorizontalAlignment="Left" Margin="48,230,0,0"
                VerticalAlignment="Top">
      <Interactivity:Interaction.Behaviors>
        <behaviors:DragFlickBehavior/>
      </Interactivity:Interaction.Behaviors>
    </DatePicker>
    <TextBlock HorizontalAlignment="Left" Height="42" Margin="132,310,0,0" 
        TextWrapping="Wrap" Text="Blabla" VerticalAlignment="Top" Width="121">
      <Interactivity:Interaction.Behaviors>
        <behaviors:DragFlickBehavior/>
      </Interactivity:Interaction.Behaviors>
    </TextBlock >
  </Grid>
</Page>

And sure enough:образобраз

The screen shows up and you can happily drag the items along, although dragging a Windows Phone date picker is quite a challenge.

So there you have it – behaviors completely defined in PCL, courtesy of a little NuGet trick. Could the Windows Phone and/or Windows Team have done this themselves? Possibly – but don’t forget I just sailed past anything that is not managed, like C++. And if there ain’t any challenges for MVPs to meet, fat lot of use we would be, eh? 😉

A zip file with both the NuGet package and the Universal Windows App can be found here. For now it’s a kind of proof of concept, soon I will be putting this NuGet package out on NuGet itself and use it for the new version of WpWinNl. After some rigorous testing ;).