piątek, 27 kwietnia 2012

Notatki o Windows 8 Consumer Preview - odc. 16

W tym odcinku trochę o komponentach WinRT oraz globalizacji aplikacji.

Tym razem moją uwagę zwróciły atrybuty dla komponentów WinRT i ich metod wpływające na ich widzialność i sposób używania z poziomu Java Script (np. jeśli dwie metody z poziomu JS są uznawane za nieróżniące się, to jedną z nich można oznaczyć atrybutem DefaultOverloadAttribute lub oznaczyć więcej metod atrybutami Overload z alternatywnymi unikalnymi nazwami). Pewną ciekawostką jest że typy z przestrzeni Platform::Collections w przeciwieństwie do wszystkich pozostałych nie są zdefiniowane w pliku .wmd, a w pliku nagłówkowym collections.h.

W przypadku komponentów WinRT pisanych w .NET w Windows Consumer Preview jesteśmy ostrzegani przed możliwym wyciekiem pamięci, jeśli zarządzany obiekt z konstruktorem zawierającym parametry zostanie utworzony z poziomu kodu niezarządzanego C++ lub Java Script. Dostajemy też stosunkowo prosty przepis na workaround.

Interesujący jest na pewno przykład demonstrujący migrację kontrolki ActiveX BingMaps wyznaczającej najkrótszą drogę do aplikacji Metro, która wykorzystuje JavaScript-ową wersję kontrolki, jednocześnie pokazując jak webcontext może komunikować się z localcontextem, który ma dostęp do komponentu WinRT w C++ odpowiedzialnego za komunikację.

Komponenty WinRT

C++

An activatable class (also known as a ref class) is one that can be instantiated from another language such as JavaScript. To be consumable from another language such as JavaScript, a component must contain at least one activatable class. An activatable class must inherit directly from Platform::Object. Apply the WebHostHidden[WebHostHidden] attribute to C++ types that are not intended to be visible to JavaScript.

An activatable class must be declared as public ref class sealed. The ref class keywords tell the compiler to create the class as a Windows Runtime compatible type.

In cases of ambiguity, you can ensure that JavaScript always calls a specific overload by applying the Windows::Metadata::DefaultOverload attribute to the method signature in the header file.

//C++ header file:
[Windows::Foundation::Metadata::DefaultOverloadAttribute]
int WinRTComponent::GetNumber(int i);

double WinRTComponent::GetNumber(double d);

To enable JavaScript to access multiple overloads that it would otherwise not be able to tell apart, you can apply the Windows::Metadata::Overload attribute to each overloaded method signature in the C++ header file. The Overload attribute takes a string parameter that specifies an alternate unique name for an overload. This enables JavaScript to call a specific overload by that name (after converting to camelCase of course).

Public ref class LangSample {
//…
// Backing store for propertyA.
int _propertyAValue;

// Property that has custom setter/getter
property int PropertyA
{
    int get() { return _propertyAValue; }
    void set(int propertyAValue)
    {
        if (propertyAValue != _propertyAValue)
        {
            _propertyAValue = propertyAValue;
            //fire event. (See event example below.)
            propertyChangedEvent(this, propertyAValue);
         }
    }
}

// Trivial get/set property that has a compiler-generated backing store.
property Platform::String^ PropertyB;
}

Enum values are passed between C++ and JavaScript as integers.

In the Object Browser, you can inspect all Windows Runtime types that are defined in .winmd files. This includes the types in the Platform namespace and the default namespace. However, the types in the Platform::Collections namespace are defined in the header file collections.h, not in a winmd file. Therefore, these types don’t appear in the Object Browser.

If you are creating a component for use only in Metro style apps with Visual Basic or C#, and the component does not contain Windows Metro style controls, consider making it a simple class library instead of a Windows Runtime component. There are fewer restrictions on a simple class library.

C#/VB.NET

A public class or interface cannot:

  • Be generic.

  • Implement an interface that is not a Windows Runtime interface. (However, you can create your own Windows Runtime interfaces and implement them.)

  • Override methods of Object other than ToString.

  • Declare protected constructors.

Public structures can't have any members other than public fields, and those fields must be value types or strings.

Because of an issue with .NET Framework support for the Windows Runtime in Windows 8 Consumer Preview, memory leaks can occur in the following scenario: A managed object is created from unmanaged code (such as JavaScript or C++) by using the new operator, and the constructor has at least one parameter. Managed objects that are created in this way currently are not reclaimed by garbage collection. The workaround for Windows Runtime Components that target Windows 8 Consumer Preview is to mark constructors that have parameters as internal (Friend in Visual Basic) and use static methods to return new instances of .NET Framework types.

int _id;
internal Example(int id)
{
    _id = id;
}
public static Example Create(int id) { return new Example(id); }

You can throw any exception type that is included in the .NET APIs for Metro style apps. You can't declare your own public exception types in a Windows Runtime component.

Visual Studio first compiles the class library, and then executes an MSBuild task that runs the WinMdExp.exe utility to create the WinMD file, turning your class library into a Windows Runtime component. The WinMD file contains both the managed code and the WinMD metadata that is used by JavaScript and the Windows Runtime. WinMdExp.exe generates build errors when you write code that's invalid in WinMD. Visual Studio adds your component to the package (.appx) file for your Metro style app, and generates the appropriate manifest.

Many Metro style apps use a dark presentation theme to help extend battery life in mobile devices.

Bing Maps Trip Optimizer

One important project setting in the WinRT Component DLL template is the /ZW option, which enables the program to use the Windows Runtime language extensions.

The application workflow

The app and web containers

Both contexts:

function OnLoad() {
    window.addEventListener("message", receiveMessage, false);
}

Bing Maps Trip Optimizer uses cross-document messaging because one context can't directly access the DOM of another context.

After the contexts register for messaging events, they can use the postMessage method to communicate. This method sends a cross-document message that's routed to the message event handler, the receiveMessage function, of the other context. Because cross-document messages are text-based, the app and web contexts use the JSON.stringify function to serialize messages into JSON text. When a context receives a message, it calls the JSON.parse function to deserialize the fields back from the JSON text.

function optimizerOptimizeTrip(locations,TravelMode, Optimize, BingMapsKey, alpha, beta, rho, iterations, parallel) {
    var message = {
        "invoke": "optimizeTrip", "locations": locations, "TravelMode": TravelMode, "Optimize": Optimize, "BingMapsKey": BingMapsKey,
        "alpha": alpha, "beta": beta, "rho": rho, "iterations": iterations, "parallel": parallel
    };
    window.parent.postMessage(JSON.stringify(message), '*');
}

We initiate the loading of the Windows Runtime component from the web context instead of directly from the local context to ensure that both contexts are loaded before the Windows Runtime component is created.

Local context:

function receiveMessage(event) {
    var data = JSON.parse(event.data)
    if (data.invoke == "load") {
        optimizerLoad();
    } else if (data.invoke == "optimizeTrip") {
        optimizerOptimizeTrip(data.locations, data.TravelMode, data.Optimize, data.BingMapsKey,
                              data.alpha, data.beta, data.rho, data.iterations, data.parallel);
    } else if (data.invoke == "cancel") {
        optimizerCancel();
    } else if (data.invoke == "alert") {
        // Show message dialog.
        new Windows.UI.Popups.MessageDialog(data.message).showAsync().then();
    }
}

Bing Maps AJAX Control, Version 7.0

Jeśli chodzi o globalizację, to w przypadku JS nie zauważyłem nic nowego. W przypadku C# i XAML moją uwagę zwróciła informacja jak obsługiwać attached property w pliku .resw. Należy dodatkowo oznaczać ją pełną przestrzenią nazw.

Globalizing

C#

Solution Explorer with localization resources.

You need to associate every control that needs localized text with the .resw file. You do this using the x:Uid attribute on your XAML elements:

<TextBlock x:Uid="HelloWorld" Text="Hello World" />

Resources.resw in Visual Studio.

Notice that for Name, you give the Uid attribute value plus you specify what property is to get the translated string (in this case the Text property). You could specify other property/values for different cultures such as HelloWorld.Width, but be careful with such layout-related properties.

Note that attached properties are handled differently in resw files such as AutomationPeer.Name. you need to explicitly write out the namespace:

MediumButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name

Brak komentarzy: