Porting da iPhone a netbook: Oltre le basi

Oltre le basi

Lo sviluppo per iPhone ha registrato una rapida crescita grazie all'app store ma anche a causa del numero di caratteristiche specifiche del dispositivo e delle interfacce di alto livello rese disponibili da Apple per utilizzarle. Con l'aumento della diffusione dei netbook, molti dei componenti hardware in precedenza relegati agli smarpthone iniziano a diventare disponibili anche in ambiente netbook. Infatti, quasi ogni netbook fornito oggi include una webcam, una caratteristica raramente disponibile nei PC Windows di alcuni anni fa.

Di seguito approfondiremo le differenze hardware tra iPhone e netbook e illustreremo alcuni esempi di codice per l'accesso a tali caratteristiche in ogni piattaforma (se e quando disponibili). Oltre a questo articolo, la raccolta di articoli ADP include diverse risorse valide in cui vengono descritte le tecniche più complesse utilizzate durante il porting. Nell'intervista rilasciata per l'articolo ADP, “Da iPhone a AppUp: porting di "Smiles"”, Mike Kasprzack, CEO di Syhkronics, parla di molte delle differenze hardware quando spiega come ha trasferito il suo gioco per iPhone in AppUp.

La Apple iPhone OS Reference Library e Microsoft MSDN Library rappresentano risorse complete in cui trovare materiale di riferimento, tutorial e discussioni della comunità su ogni piattaforma.



Rotazione e accelerometro

Riferimento rapido

API per iPhone

API per Windows

Rotazione dello schermo

didRotateFromInterfaceOrientation

DisplaySettingsChanged

Accelerometro

UIAccelerometer

Sensore API di Windows

L'iPhone utilizza l'accelerometro integrato per rilevare cambiamenti di orientamento. L'API per iPhone rende disponibili due metodi per consentire e agire sui cambiamenti di orientamento. shouldAutorotateToInterfaceOrientation indica al framework se la UI deve essere automaticamente ruotata quando viene rilevato che il dispositivo è stato ruotato. Se si consente la rotazione, l'SDK iPhone attiva didRotateFromInterfaceOrientation per permettere all'applicazione di agire sul cambiamento di orientamento.

Objective C dell'iPhone

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}


- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    NSMutableString *newOrientation = [[[NSMutableString alloc] init] autorelease];
    switch ([[UIDevice currentDevice] orientation]) {
        case UIDeviceOrientationLandscapeLeft:
            [newOrientation setString:@"LandscapeLeft"];
            break;
        case UIDeviceOrientationLandscapeRight:
            [newOrientation setString:@"LandscapeRight"];
            break;
        case UIDeviceOrientationPortrait:
            [newOrientation setString:@"Portrait"];
            break;
        default:
            [newOrientation setString:@"(unknown)"];
            break;
    }

    [self setDiagInfo:@"Orientation" value:newOrientation];
}

Come afferma Kasprzack, “nei PC il gioco viene solitamente bloccato in un singolo orientamento”. Alcuni netbook (in particolare i tablet) supportano la rotazione dello schermo e avvertono il sistema quando lo schermo viene ruotato. 

È possibile rilevare la rotazione dello schermo assegnando un gestore eventi in C# (o altro codice gestito) all'evento di sistema DisplaySettingsChanged, come illustrato nel seguente esempio di applicazione Windows Presentation Foundation (WPF).

Windows XAML

<Window x:Class="Rotation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Rotation " Height="246" Width="431" Loaded="Window_Loaded" Unloaded="Window_Unloaded">
    <Grid>
        <Label Name="label1" Content="Orientation:" Height="28"  Width="131" 
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" 
               Margin="12,44,0,0" FontSize="14" FontWeight="Bold" />
        <Label Name="labelOrientation" Content="(starting)" Height="28"  Width="230"
               HorizontalAlignment="Right" HorizontalContentAlignment="Left" VerticalAlignment="Top"
               Margin="0,44,30,0" FontSize="14" FontWeight="Bold" />
    </Grid>
</Window>

Windows C#

using System;
using System.Windows;
using Microsoft.Win32;

namespace Rotation
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void UpdateOrientation(object sender, EventArgs e)
        {
            double width = System.Windows.SystemParameters.PrimaryScreenWidth;
            double height = System.Windows.SystemParameters.PrimaryScreenHeight;

            string orientation;
            if (height > width)
                orientation = "Portrait";
            else
                orientation = "Landscape";

            string description = orientation + " (" + width.ToString() + "x" + height.ToString() + ")";
            labelOrientation.Content = description;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            SystemEvents.DisplaySettingsChanged += new EventHandler(UpdateOrientation);
            UpdateOrientation(sender, e);
        }

        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            SystemEvents.DisplaySettingsChanged -= UpdateOrientation;
        }
    }

}

In C++ per applicazioni native, l'utente resta in ascolto e risponde al messaggio WM_DISPLAYCHANGE.

Windows C++

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM 
  lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    switch (message) 
    {
//. . .
//. . .
    case WM_DISPLAYCHANGE:
        {
        //Separate the screen width and height and then
        //check to see if screen width is greater than screen height
        if ((LOWORD(lParam) > HIWORD(lParam)) 
            //Run the application in landscape, for example:
            MessageBox(NULL,"Run in landscape.","Landscape",MB_OK);
        else
            //Run the application in portrait, as in:
            MessageBox(NULL,"Run in portrait.","Portrait",MB_OK);
        }
        break;

I dettagli completi, incluso l'esempio di codice, sono reperibili nell'articolo MSDN, “Detecting Screen Orientation and Screen Rotation in Tablet PC Applications”.

Se l'applicazione dipende dall'accelerometro come input, ad esempio per un gioco, il porting in un netbook può non essere fattibile, in quanto i netbook non includono un accelerometro. Tuttavia, con l'ingresso sul mercato di altri tablet tra la metà e la fine del 2010, prevediamo che gli accelerometri saranno più diffusi. 

Nell'iPhone è possibile ottenere eventi dell'accelerometro registrandosi nella classe UIAccelerometer come delegato e consumando gli eventi.

Objective C dell'iPhone

-(void)viewDidLoad
{
    [super viewDidLoad];

    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:1.0 / 60.0];
    [[UIAccelerometer sharedAccelerometer] setDelegate:self];
}

// UIAccelerometerDelegate method, called when the device accelerates.
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration

{
    // Do something with the data
    // acceleration.x, acceleration.y, acceleration.z
}

Windows 7 prevede il supporto per gli accelerometri come tipo di “sensore” tramite Sensor API, un'interfaccia COM accessibile da codice C++ nativo. Per accedere ai sensori tramite codice gestito, è necessario ottenere il Windows API Code Pack da MSDN. A differenza dell'iPhone, Windows non fornisce un'API di alto livello che estrae i dati dell'accelerometro da dispositivi distinti. Attualmente, gli accelerometri vengono utilizzati prevalentemente nei palmari, e Windows Mobile supporta due dispositivi di HTC e Samsung, in entrambi i casi tramite interfacce hardware dirette1

Per supportare gli accelerometri nei tablet o nei netbook, è necessario esaminare l'SDK o le interfacce pubblicate da ogni produttore.

[1] Per ulteriori dettagli e codice sorgente, vedere il documento non ufficiale Windows Mobile Unified Sensor API di Koushik Dutta.

Da touch a mouse

A meno che non vengano utilizzati comandi gestuali, il comportamento del mouse è simile a quello touch, con la differenza che le azioni del mouse generano un evento Hover non supportato dagli ambienti touch. Per questo motivo, il passaggio dall'interazione basata su touch al mouse dovrebbe essere semplice. Considerando le aspettative degli utenti Windows, può essere preferibile aggiungere effetti di passaggio del mouse se si dispone di componenti della UI personalizzati (come avviene spesso per i giochi). Analogamente ai collegamenti ipertestuali nei browser, la maggior parte dei controlli Windows fornisce oggi un feedback visivo al passaggio del mouse per avvertire l'utente se l'elemento risponderà a un evento Click. 

Se l'applicazione supporta comandi gestuali o dipende da alcuni comandi gestuali specifici dell'iPhone, è necessario valutare come ottenere questo funzionamento nell'applicazione Windows. 

Windows Touch

I tablet e i netbook touch screen spesso supportano i comandi gestuali multi-touch, e Windows 7 Touch include il supporto integrato per funzionalità quali trascinamento, scorrimento, zoom, gesto rapido, rotazione e altro ancora. Questo aspetto viene descritto efficacemente nell'articolo MSDN, “Getting Started with Windows Touch Gestures”. Per i dispositivi non touch, di seguito sono riportati alcuni approcci alla gestione di tali eventi.

Scorrimento

Riferimento rapido

API per iPhone

API per Windows

Scorrimento

UIScrollView

CWnd::OnMouseWheel

Control.OnMouseWheel

Il comando gestuale di scorrimento sull'iPhone è simile alla rotellina del mouse supportata dalle applicazioni Windows, ai controlli della barra di scorrimento e a qualsiasi controllo con contenuto che può scorrere. In una situazione con UI personalizzata, può essere necessario acquisire l'evento Scrollwheel per replicare questo comportamento. Ad esempio, Google Maps in un browser risponde alla rotellina di scorrimento ingrandendo o riducendo la mappa. In un'applicazione per iPhone, è probabile che non sia necessario prevedere lo scorrimento perché viene gestito automaticamente dai componenti della UI o implementato tramite una classe UIScrollView.

Si noti che nei controlli Windows standard non esiste un equivalente del comportamento "gesto rapido", ma è possibile paragonarlo a una chiamata di pagina su e pagina giù in un evento Scrollbar oppure ricavarlo dall'accelerazione dell'evento Scrollwheel. È possibile acquisire l'evento eseguendo l'override del metodo CWnd::OnMouseWheel in C++/MFC o del metodo Control.OnMouseWheel in .NET

Windows C++

BOOL CWndEx::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)    
{      
    if( zDelta < 0 )   
    {   
        if( zDelta < -PAGE_DELTA ) {
            // Do action to "scroll" down one "line"
        } else {
            // Do action to "scroll" down one "page"
        }
    }   
    else   
    {   
        if( zDelta < PAGE_DELTA ) {
            // Do action to "scroll" up one "line"
        } else {
            // Do action to "scroll" up one "page"
        }
    }     
   
    return CWnd::OnMouseWheel(nFlags, zDelta, pt);   
}

Windows C#

protected override void OnMouseWheel(MouseWheelEventArgs e)
{ 
    if( e.Delta < 0 ) {
        if( e.Delta < -PAGE_DELTA ) {
            // Do action to "scroll" down one "line"
        } else {
            // Do action to "scroll" down one "page"
        }
    } else {
        if( e.Delta < PAGE_DELTA ) {
            // Do action to "scroll" up one "line"
        } else {
            // Do action to "scroll" up one "page"
        }
    }

}

Zoom

Non essendo disponibile supporto nativo per l'azione zoom con pizzico o zoom nei dispositivi non touch, è necessario decidere se si desidera inserire questa azione nell'applicazione. È possibile fornire funzionalità zoom analoghe in altri modi, magari aggiungendo controlli all'interfaccia utente. Molte applicazioni Windows includono un menu "Visualizza" con le opzioni "Zoom indietro", "Zoom avanti" e "Dimensioni normali". Solitamente, queste opzioni sono mappate rispettivamente alle combinazioni di tasti Ctrl +, Ctrl - e Ctrl 0, e Windows 7 prevede le combinazioni di tasti Windows + e Windows - globali che consentono di applicare lo zoom all'intero schermo. 

Rotazione

Il comando gestuale di rotazione a due dita riconosciuto dall'iPhone non è supportato in modo nativo nei dispositivi non touch per Windows. Anche in questo caso, solitamente le applicazioni Windows che consentono la rotazione includono un menu "Visualizza" con le opzioni "Ruota in senso orario" e "Ruota in senso antiorario".

Trascinamento

Riferimento rapido

API per iPhone

API per Windows

Trascinamento della selezione

touchesMoved

DragDrop

Mentre il trascinamento della selezione non è supportato in modo nativo nell'iPhone (è necessario implementarlo tramite il metodo touchesMoved), gli eventi Drag sono completamente supportati e ben documentati nelle API per Windows. È disponibile un video da 30 minuti in MSDN in cui viene descritto come implementare il trascinamento della selezione nelle applicazioni MFC. Per .NET vedere l'articolo MSDN “Performing Drag-and-Drop Operations on Windows” in cui viene fornito il seguente esempio di codice:

Windows C#

private void button1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
   button1.DoDragDrop(button1.Text, DragDropEffects.Copy | 
      DragDropEffects.Move);
}

private void textBox1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
   if (e.Data.GetDataPresent(DataFormats.Text)) 
      e.Effect = DragDropEffects.Copy;
   else
      e.Effect = DragDropEffects.None;
}

private void textBox1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
   textBox1.Text = e.Data.GetData(DataFormats.Text).ToString();
}

Ridimensionamento immagini

Le dimensioni dello schermo di un netbook sono maggiori rispetto a quelle di un iPhone. Anche se tali dimensioni variano, nella maggior parte dei casi sono pari a 1024 x 600 pixel. Per supportare questa risoluzione dello schermo maggiore e le risoluzioni dello schermo variate, è consigliabile regolare le dimensioni e la posizione degli oggetti in base alle dimensioni della finestra o dello schermo. Se si utilizza WPF per le applicazioni Windows, è possibile impostare il posizionamento automatico degli oggetti uno rispetto all'altro e adattarli alla finestra. Per le applicazioni ricche di grafica, ad esempio i videogame, nella sua intervista Kasprzack consiglia di ridimensionare le immagini partendo da una risoluzione di riferimento maggiore di qualsiasi risoluzione di destinazione. Creando le immagini con queste dimensioni, è possibile ridurle appropriatamente per ogni piattaforma. Inoltre, il posizionamento o il movimento di oggetti (come nei videogame) può essere ridimensionato in base alla risoluzione di ogni dispositivo dalla risoluzione di riferimento.

Geolocalizzazione/GPS

Riferimento rapido

API per iPhone

API per Windows 7

Servizi di localizzazione

Core Location Framework

API Location

Nelle app per iPhone, Apple fornisce il Core Location Framework, che consente agli sviluppatori di utilizzare l'hardware disponibile per localizzare e tenere traccia dei cambiamenti di posizione dell'utente, indipendentemente dall'hardware sottostante. Il campione dell'app LocateMe nella libreria di riferimento per iPhone fornisce un esempio di raccolta di informazioni sull'attuale posizione e di ricezione di notifiche in caso di variazioni. 

Per ottenere una singola posizione sull'iPhone è necessario creare un controller o delegato che implementi CLLocationManagerDelegate

Objective C dell'iPhone

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface LocationViewController : UIViewController <CLLocationManagerDelegate> {
    UILabel *labelLatitude;
    UILabel *labelLongitude;
    UILabel *labelStatus;
    CLLocationManager *locationManager;
}

@property(nonatomic,retain) IBOutlet UILabel *labelLatitude;
@property(nonatomic,retain) IBOutlet UILabel *labelLongitude;
@property(nonatomic,retain) IBOutlet UILabel *labelStatus;
@property(nonatomic,retain) CLLocationManager *locationManager;

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation;

- (void)locationManager:(CLLocationManager *)manager
       didFailWithError:(NSError *)error;

@end

È quindi necessario definire i metodi per avviare il servizio di localizzazione, raccogliere il risultato e gestire eventuali errori.

@implementation LocationViewController
@synthesize labelLatitude,labelLongitude,labelStatus,locationManager;

- (void)viewDidLoad {
    [super viewDidLoad];
    [labelStatus setText:@"Started"];

    locationManager = [[[CLLocationManager alloc] init] autorelease];
    locationManager.delegate = self;

    [locationManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation {
    [labelStatus setText:@"Got location"];
    [labelLatitude setText:[NSString stringWithFormat:@"%.6f",newLocation.coordinate.latitude]];
    [labelLongitude setText:[NSString stringWithFormat:@"%6f",newLocation.coordinate.longitude]];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    [labelStatus setText:[error description]];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
}

- (void)dealloc {
    [super dealloc];
}

@end

Con Windows 7 Location Platform, questa procedura è ormai quasi altrettanto semplice da eseguire nei netbook abilitati per la localizzazione. Si noti che molti netbook attualmente non offrono la possibilità di individuare la posizione perché non dispongono di wireless basato su cella o GPS. Nel Catalogo di componenti del sito Web ADP sarà tra breve disponibile un componente che consente di individuare la posizione del netbook in base a un servizio GPS integrato (o altro servizio di localizzazione) o di accedere al Web e tentare di determinare la posizione in base all'indirizzo IP del dispositivo. È inoltre possibile esaminare i prodotti disponibili su licenza di Skyhook Wireless, che forniscono un servizio di localizzazione basato su Wi-Fi utilizzato nell'iPod touch.

Se si desidera aggiungere manualmente il supporto per la localizzazione nelle applicazioni, è possibile utilizzare la Windows Location API per rilevare e ottenere dati dai dispositivi con identificazione della posizione. Ecco un frammento di codice del componente citato in precedenza per iniziare.

Windows C++

#include <LocationApi.h>

GeoLocation::GeoLocation() : Component(id) 
{    
    //Setup COM Location interface 
    HRESULT hr; 
    GeoCOM = false; 
    LOCATION_REPORT_STATUS locationReportStatus = REPORT_NOT_SUPPORTED;
    IID REPORTS[] = { IID_ILatLongReport, IID_ICivicAddressReport };
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);

    if(SUCCEEDED(hr))
    {
        hr = pLocation.CoCreateInstance(CLSID_Location);

        if (SUCCEEDED(hr))
        {
            hr = pLocation->RequestPermissions(NULL, REPORTS, ARRAYSIZE(REPORTS), TRUE);
            if (SUCCEEDED(hr)) 
            {
                hr = pLocation->GetReportStatus(IID_ILatLongReport, &locationReportStatus);

                if (SUCCEEDED(hr))
                {
                    switch (locationReportStatus) 
                    {
                        case REPORT_NOT_SUPPORTED: 
                        case REPORT_ERROR:
                        case REPORT_ACCESS_DENIED:
                            GeoCOM = false; 
                        default: 
                            GeoCOM = true; 
                    }
                }
            }
        }
    }


    http = new HTTPRequest(); 
    locationData = new Location(); 

}

/** GeoLocation destructor
    frees the current location data and HTTPRequest class.  
*/ 
GeoLocation::~GeoLocation()
{

    //tear down com interface 
    CoUninitialize();

    if(locationData)
        delete locationData; 
    locationData = NULL; 
}

DWORD GeoLocation::GetCOMLocation()
{
    HRESULT hr = ERROR_SUCCESS; 

    CComPtr<ILocationReport> pLocationReport; 
    CComPtr<ILatLongReport> pLatLongReport; 
    DOUBLE dValue; 

    hr = pLocation->GetReport(IID_ILatLongReport, &pLocationReport);

    if(SUCCEEDED(hr))
    {

        if(pLocationReport)
            hr = pLocationReport->QueryInterface(&pLatLongReport);

        if(SUCCEEDED(hr))
        {
        
            hr = pLatLongReport->GetLongitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dLongitude = dValue; 

            hr = pLatLongReport->GetLatitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dLatitude = dValue; 

            hr = pLatLongReport->GetAltitude(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dAltitude = dValue; 

            hr = pLatLongReport->GetAltitudeError(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dAltitudeError = dValue; 

            hr = pLatLongReport->GetErrorRadius(&dValue); 
            if(SUCCEEDED(hr))
                locationData->dErrorRadius = dValue; 
        }
    }

    return hr; 
}

La Location API è inoltre accessibile da .NET ed è molto più semplice. Ecco un'applicazione completa per Windows basata su .NET e WPF che sfrutta la classe GeoCoordinateWatcher per aggiornare il display quando la posizione cambia.

Windows XAML

<Window x:Class="Location.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Location" Height="238" Width="400">
    <Grid Height="177" Width="360">
        <Label Name="label1" Content="Latitude:"
               FontSize="18" FontWeight="Bold"
               Margin="12,23,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="label2" Content="Longitude:"
               FontSize="18" FontWeight="Bold"
               Margin="12,63,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="label3" Content="Status:"
               FontSize="18" FontWeight="Bold"
               Margin="12,103,0,0"
               HorizontalAlignment="Left" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="124" />

        <Label Name="labelLatitude" Content=""
               FontSize="18" FontWeight="Bold"
               Margin="142,23,0,0"
               HorizontalAlignment="Left" VerticalAlignment="Top" Width="206" />

        <Label Name="labelLongitude" Content=""
               FontSize="18" FontWeight="Bold"
               Margin="142,63,0,0"
               HorizontalAlignment="Left" VerticalAlignment="Top" Width="206" />

        <Label Name="labelStatus" Content="(starting)"
               FontSize="18" FontWeight="Bold"
               Margin="142,103,12,0" VerticalAlignment="Top" />


    </Grid>
</Window>

Windows C#

using System;
using System.Windows;
using System.Device.Location;

namespace Location
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            GeoCoordinateWatcher watcher = new GeoCoordinateWatcher();

            watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(LocationUpdate);
            if (watcher.TryStart(false, TimeSpan.FromMilliseconds(1000)))
                labelStatus.Content = "Started";
            else
                labelStatus.Content = "Couldn't start location";
        }

        public void LocationUpdate(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
            labelStatus.Content = "Got location";
            labelLatitude.Content = e.Position.Location.Latitude.ToString();
            labelLongitude.Content = e.Position.Location.Longitude.ToString();
        }
    }
}

Archivio dati

Quasi tutte le applicazioni devono conservare lo stato tra una sessione e l'altra. Parte di questa funzionalità viene fornita sotto forma di impostazioni dell'applicazione, ad esempio audio on/off, mentre in altri casi l'archivio dati può essere più grande, ad esempio file multimediali, stato di un videogame, dati raccolti. 

Dati di piccole dimensioni

Riferimento rapido

API per iPhone

API per Windows

Impostazioni

Preferenze

Impostazioni applicazione

L'iPhone fornisce le Preferenze per l'archiviazione di dati di configurazione di piccole dimensioni, che includono un'API di alto livello che consente di archiviare un semplice valore denominato per l'utente corrente sull'host corrente.

Objective C dell'iPhone

@implementation SmallDataViewController
@synthesize binarySetting,stringSetting;

// Load settings from NSUserDefaults and update the display
- (void)viewDidLoad {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [binarySetting setOn:[settings boolForKey:@"binarySetting"]];
    [stringSetting setText:[settings stringForKey:@"stringSetting"]];
    [super viewDidLoad];
}

// When the text box value is changed, update the settings and synchronize
- (IBAction) updateText:(id) sender {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [settings setObject:stringSetting.text forKey:@"stringSetting"];
    [settings synchronize];
    [sender resignFirstResponder];
}

// When the switch is changed, update the settings and synchronize
- (IBAction) switchValueChanged:(id) sender {
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    [settings setBool:binarySetting.on forKey:@"binarySetting"];
    [settings synchronize];
}

@end

Windows prevede una gestione delle impostazioni di alto livello simile nelle applicazioni .NET, tramite Impostazioni applicazione. È possibile agire direttamente in Visual Studio tramite la finestra di progettazione oppure scegliere di scrivere e inserire nel codice impostazioni personalizzate, come illustrato nell'esempio seguente.

Windows XAML

<Window x:Class="SmallData.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SmallData" Height="182" Width="398" 
    >
    <Grid>
        <Label Name="label1" Content="Binary setting:" 
               HorizontalAlignment="Left"  VerticalAlignment="Top" Margin="25,23,0,0" 
               FontSize="16" FontWeight="Bold" />
        <Label Name="label2" Content="String setting:" 
               HorizontalAlignment="Left" VerticalAlignment="Top" Margin="27,78,0,0"
               FontSize="16" FontWeight="Bold" />
        <CheckBox Name="CBBinarySetting" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="164,32,0,0" />
        <TextBox Name="TBStringSetting" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="164,84,0,0" Width="175" />
    </Grid>
</Window>

Windows C#

using System;
using System.Configuration;

namespace SmallData
{
    class MyUserSettings : ApplicationSettingsBase
    {
        [UserScopedSetting()]
        [DefaultSettingValue("False")]
        public bool binarySetting
        {
            get { return ((bool)this["binarySetting"]); }

            set { this["binarySetting"] = (bool)value; }
        }

        [UserScopedSetting()]
        [DefaultSettingValue("")]
        public string stringSetting
        {
            get { return ((string)this["stringSetting"]); }

            set { this["stringSetting"] = (string)value; }
        }
    }
}

Windows C#

    
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SmallData
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            MyUserSettings settings = new MyUserSettings();

            TBStringSetting.Text = settings.stringSetting;
            CBBinarySetting.IsChecked = settings.binarySetting;

            CBBinarySetting.Checked += CBBinarySetting_Changed;
            CBBinarySetting.Unchecked += CBBinarySetting_Changed;

            TBStringSetting.TextChanged += TBStringSetting_TextChanged;
        }

        private void CBBinarySetting_Changed(object sender, RoutedEventArgs e)
        {
            MyUserSettings settings = new MyUserSettings();
            settings.binarySetting = (bool)CBBinarySetting.IsChecked;
            settings.Save();
        }

        private void TBStringSetting_TextChanged(object sender, TextChangedEventArgs e)
        {
            MyUserSettings settings = new MyUserSettings();
            settings.stringSetting = TBStringSetting.Text;
            settings.Save();
        }
    }
}

Dati di grandi dimensioni

I dati di grandi dimensioni, ad esempio database o file, possono essere archiviati localmente sull'iPhone in uno spazio di storage dedicato e riservato per un'applicazione iPhone. Si utilizza la funzione NSSearchPathForDirectoriesInDomains per individuare la posizione di storage e quindi si effettuano chiamate di I/O standard per le operazioni di lettura e scrittura. Se è necessario l'accesso in sola lettura a un file o database, è possibile includerlo nell'applicazione come risorsa in Xcode.

Windows prevede meno limitazioni in merito alla posizione di archiviazione dei file (alcune parti del sistema sono ad accesso limitato in Windows 7). Tuttavia, oggi è consigliabile memorizzare i file di sola lettura nella cartella dei file di programma dell'applicazione, installata con il package di installazione. I file scrivibili e i dati raccolti o generati dall'utente vengono solitamente archiviati nella cartella AppData dell'applicazione. Esistono numerose API di alto livello che consentono di gestire i vari casi di storage dei dati. 

Fortunatamente, se nell'app per iPhone sono stati inclusi file di dati, è probabile che sia possibile analizzarli o accedervi in modo analogo in Windows. Ad esempio, un database SQLite potrebbe essere letto utilizzando le interfacce C native dell'iPhone che possono essere copiate direttamente nell'applicazione Windows e consumate come libreria C nativa. Se si passa a .NET, è possibile utilizzare la libreria System.Data.SQLite di terze parti per mappare le chiamate a .NET. A partire dalla versione 2.6.3, il motore SQLite è in grado di leggere i propri file di database anche se sono stati creati in un computer con un Endian diverso.

Consumo di servizi Web

Riferimento rapido

API per iPhone

API per Windows

Servizi HTTP

NSURLConnection

WinHTTP

HttpWebRequest

Un discreto numero di app per iPhone si collegano a servizi Web tramite i quali consumano dati o pubblicano aggiornamenti. Questi servizi possono essere servizi Web basati su SOAP, ma più spesso si tratta di interfacce RESTful o anche di semplici connessioni HTTP che restituiscono e accettano dati JSON, XML, HTML o strutturati.

Nel frammento seguente viene utilizzata la classe NSURLConnection per leggere un URL e quindi viene utilizzato il json-framework per Objective C di terze parti per analizzare la risposta.

Objective C dell'iPhone

- (void)viewWillAppear:(BOOL)animated {
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
    [NSURLConnection connectionWithRequest:request delegate:self]
}

...

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    
    NSString* out = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
    
    if ( out ) {
        SBJSON* jsonObject = [[SBJSON alloc] init];
        NSError* error;
        NSDictionary* outputDictionary = [jsonObject objectWithString:out error:&error];
        
        // Do something with the JSON response data
    } else {
        // Give an error or warning message
    }

    
    [_receivedData release];
    _receivedData = nil;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog([NSString stringWithFormat:@"Request failed. Reason: %@", error.localizedDescription]);

    // Give an error or warning message
    
    [_receivedData release];
    _receivedData = nil;
}

Anche i servizi Web basati su SOAP, sebbene non siano supportati in modo nativo nell'SDK per iPhone, possono essere consumati in un'app per iPhone, utilizzando uno strumento di terze parti come wsdl2objc, che genera classi Objective C per l'accesso a servizi Web SOAP da una definizione di servizio WSDL.

In ambiente Windows nativo, la libreria WinHTTP fornisce accesso a un client per la connessione a un server Web. Poiché le interazioni di rete sono soggette a errore, è in genere preferibile includere una funzionalità di acquisizione errori, che testa tutti i valori restituiti ed esegue l'azione appropriata e regolare in modo da non ostacolare eccessivamente l'esperienza utente nel caso in cui parte della richiesta HTTP non riesca. Detto questo, ecco uno pseudocodice per iniziare, che deve essere incapsulato nella gestione errori.

Windows C++

void HTTPRequest::Request(const std::wstring wsUrl, 
                const std::wstring wsHeaders, 
                const std::wstring wsContent,
                std::string &sOutBuffer)
{ 
    WinHttpCrackUrl( wsUrl.c_str(), wsUrl.length(), 0, &urlComponents );
    Url.assign(urlComponents.lpszHostName, urlComponents.dwHostNameLength);

    hConnect = WinHttpConnect( hSession, Url.c_str(), urlComponents.nPort, 0 );

    hRequest = WinHttpOpenRequest(
        hConnect, 
        "GET", 
        urlComponents.lpszUrlPath, 
        HTTP_VERSION.c_str(),
        WINHTTP_NO_REFERER, 
        WINHTTP_DEFAULT_ACCEPT_TYPES,
        dwOpenFlags);

    WinHttpSendRequest( hRequest, 
        wsHeaders.c_str(), 
        wsHeaders.length(),    
        WINHTTP_NO_REQUEST_DATA, 
        0, 
        0,
        0 ); 

    WinHttpReceiveResponse( hRequest, NULL );

    GetResponseData( hRequest, sOutBuffer); 
         
    if(hRequest)
        WinHttpCloseHandle(hRequest); 
    hRequest = NULL; 
    if(hConnect)
        WinHttpCloseHandle(hConnect); 
    hConnect = NULL; 
}

DWORD HTTPRequest::GetResponseData( HINTERNET hRequest, string &data) 
{
    DWORD dwSize; 
    DWORD dwStatus = ERROR_SUCCESS; 
    DWORD dwDownloaded;
    LPSTR pszOutBuffer;
        
    dwSize = 0;
    do {
        WinHttpQueryDataAvailable( hRequest, &dwSize); 
        pszOutBuffer = new char[dwSize+1];
        ZeroMemory(pszOutBuffer, dwSize + 1);
        WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded);

        if(pszOutBuffer)
        {
            data.append(pszOutBuffer);
            delete [] pszOutBuffer;
            pszOutBuffer = NULL;
        }
    } while (dwSize > 0);

    
    if(pszOutBuffer)
        delete [] pszOutBuffer; 
    pszOutBuffer = NULL; 

    return dwStatus; 
}

In .NET tutto questo è reso molto più semplice dalla classe HttpWebRequest.

Windows C#

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlString);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Encoding encoding = Encoding.GetEncoding(1252);  // Windows default Code Page
StreamReader reader = new StreamReader(response.GetResponseStream(),encoding);

string data = reader.ReadToEnd();

response.Close();
reader.Close();

Si noti che nel caso di Windows C++ nativo, il meccanismo HTTP è sincrono. La chiamata al metodo blocca la continuazione del codice finché non viene ricevuta la risposta o non è scaduto il periodo di timeout. Anche se nell'esempio .NET precedente viene utilizzato un approccio sincrono, la classe prevede una richiesta/risposta asincrona. In ambiente iPhone la chiamata è asincrona, per cui non bisogna preoccuparsi del threading. Ai fini delle prestazioni e dei tempi di risposta, è consigliabile utilizzare sempre chiamate asincrone o inserire le richieste di rete in un thread separato in modo che non blocchino il thread dell'interfaccia utente. Questo aspetto verrà trattato nella sezione seguente.

Threading e simultaneità

Riferimento rapido

API per iPhone

API per Windows

Threading

Thread tradizionali

Simultaneità avanzata

AfxBeginThread

ThreadStart

Molte attività a esecuzione prolungata esposte dalla libreria iPhone sono asincrone. Si dichiara un delegato, si avvia un evento e si attende una risposta. In questo modo, la gestione del threading o della simultaneità di questi eventi viene lasciata all'API, con una conseguente programmazione più veloce e un codice più stabile. Pertanto, la maggior parte degli sviluppatori per iPhone non deve preoccuparsi del threading, anche se Apple supporta sia i thread tradizionali sia le opzioni avanzate (consigliate) come gli oggetti operazione e Grand Central Dispatch (GCD) sull'iPhone.

Quando le applicazioni vengono trasferite in Windows, è possibile implementare thread personalizzati per gestire alcuni eventi in modo asincrono. Ad esempio, una richiesta HTTP dovrebbe essere gestita su un thread separato in modo da non compromettere i tempi di risposta dell'interfaccia utente. È possibile avviare tale thread da un'applicazione MFC con la funzione AfxBeginThread.

Windows C++

typedef struct THREADINFOSTRUCT {
    HWND hWnd;
    bool bPush;
    std::wstring strUrl;
    int nApi;
} THREADINFOSTRUCT;

UINT __cdecl FnThreadedHttpRequest(LPVOID lParam) {
    THREADINFOSTRUCT* tis = (THREADINFOSTRUCT*)lParam;
    // Make the HTTP request and handle response
}

...

void App::CallApi(url) {
    THREADINFOSTRUCT* tis = new THREADINFOSTRUCT;
    tis->hWnd = hWnd;
    tis->bPush = false;
    tis->strUrl = url;
    tis->nApi = API_LOGIN;

    m_pHttpThread = AfxBeginThread(FnThreadedHttpRequest, tis, THREAD_PRIORITY_NORMAL, 0, 0);
}

Se si crea un'applicazione .NET, questa situazione viene gestita facilmente tramite il delegato ThreadStart, tra diversi altri approcci.

Windows C#

static void CallApi(string url) 
{
    ThreadedHttpRequest worker = new ThreadedHttpRequest();
    worker.Push = false;
    worker.Url = url;
    worker.Api = API_LOGIN;

    ThreadStart threadDelegate = new ThreadStart(worker.DoRequest());    
    Thread thread = new Thread(threadDelegate);
    thread.Start();
}

class ThreadedHttpRequest
{
    public boolean Push; 
    public string Url;
    public int Api;

    public void DoRequest() {
        // Make Http request and handle response
    }
}

Tenere presente che la programmazione multi-threaded è complicata. Quando più thread accedono alle stesse parti di codice o apportano modifiche a dati condivisi, possono verificarsi problemi di race condition e di concorrenza. Il threading rappresenta uno strumento efficace per migliorare i tempi di risposta e l'efficacia delle applicazioni, purché il codice venga sincronizzato con attenzione. Quando vengono fornite interfacce asincrone, è consigliabile utilizzarle per migliorare la thread safety.

Elenco di contatti

Riferimento rapido

API per iPhone

API per Windows

Rubrica

ABPeoplePickerNavigationController

Contacts API di Windows Live

Gli utenti dell'iPhone dispongono di una rubrica con tutti i loro contatti archiviata nell'apparecchio. Se l'app per iPhone include funzionalità di condivisione, è probabile che sia stata utilizzata questa rubrica. Con la rubrica e i servizi correlati, è possibile acquisire informazioni sui contatti per l'invio di e-mail, SMS o la condivisione di altre informazioni. 

Ad esempio, per ottenere il nome di un contatto selezionato, è necessario utilizzare la classe ABPeoplePickerNavigationController e implementare il protocollo ABPeoplePickerNavigationControllerDelegate nella classe.

Objective C dell'iPhone

@interface AddressBookQuickStartViewController : UIViewController <ABPeoplePickerNavigationControllerDelegate> {
    IBOutlet UILabel *firstName;
    IBOutlet UILabel *lastName;
}

@property (nonatomic, retain) UILabel *firstName;
@property (nonatomic, retain) UILabel *lastName;


- (IBAction) showPicker:(id) sender;

@end

@implementation AddressBookQuickStartViewController

@synthesize firstName;
@synthesize lastName;

-(IBAction) showPicker: (id) sender {
    ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
    picker.peoplePickerDelegate = self;
    
    [self presentModalViewController:picker animated:YES];
    [picker    release];
}


 
- (BOOL)peoplePickerNavigationController:
            (ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
 
    NSString* name = (NSString *)ABRecordCopyValue(person,
                                             kABPersonFirstNameProperty);
    self.firstName.text = name;
    [name release];
 
    name = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
    self.lastName.text = name;
    [name release];
 
    [self dismissModalViewControllerAnimated:YES];
 
    return NO;
}

Per ulteriori informazioni, vedere Address Book UI Framework. Per l'accesso diretto all'elenco di contatti, è possibile utilizzare l'Address Book Framework.

Per le applicazioni Windows, è possibile utilizzare la Contacts API di Windows, che tuttavia è stata deprecata in Windows 7 e sostituita dalla Contacts API di Windows Live. Per accedere alla rubrica Live Contact, è necessaria una connessione Internet attiva. L'API utilizza l'interfaccia REST per lo scambio di file XML. In MSDN è disponibile un esempio di applicazione con i dettagli.

Videocamera

Riferimento rapido

API per iPhone

API per Windows

Videocamera

UIImagePickerController

DirectShow

La maggior parte dei netbook viene fornita con una videocamera integrata. Si tratta di un aspetto positivo se è stata creata un'app per iPhone che utilizza la videocamera e si decide di eseguirne il porting in Windows. Tuttavia, la videocamera dei netbook è rivolta verso gli utenti e spesso viene definita webcam nelle specifiche di prodotto. Rispetto alla videocamera situata sul retro dell'iPhone (finora su iPhone, iPhone 3G e iPhone 3GS) questo significa che è necessario valutare con attenzione l'esperienza utente dell'applicazione sulla nuova piattaforma. Ad esempio, è possibile utilizzare la videocamera rivolta verso il retro dell'iPhone per la scansione di un codice a barre tenendo l'apparecchio sopra tale codice. Con il netbook, invece, il codice a barra deve essere posto di fronte alla videocamera. La videocamera di un netbook può, tuttavia, essere utilizzata per videoconferenze o per altre applicazioni in cui l'utente scatta fotografie a se stesso. Questa funzionalità non è possibile con la terza generazione di iPhone.

L'iPhone consente di acquisire immagini dalla videocamera e di registrare video (nella versione 3.0 e successive) con la classe UIImagePickerController e implementando un protocollo UIImagePickerControllerDelegate nella classe.

Objective C dell'iPhone

-(IBAction) recordVideo:(id) sender {
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        UIImagePickerController * picker = [[UIImagePickerController alloc] init];
        picker.delegate = self;
        picker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
        picker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:picker animated:YES]
    }
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    [picker dismissModalViewControllerAnimated:YES];
    movieFileUrl = [info objectForKey:@"UIImagePickerControllerMediaURL"];
}

In Windows è possibile utilizzare la libreria DirectShow per acquisire contenuti video dalla videocamera e manipolarli nel processo. È necessario innanzitutto creare un grafico di acquisizione con cui configurare il flusso di file e avviare il processo di rendering video.

Windows C++

IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C:\\Example.avi", &pMux, NULL);

hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, NULL,  pMux);

pMux->Release();

Per una descrizione più approfondita, vedere la sezione Video Capture della documentazione su DirectShow in MSDN.

Riproduzione multimediale

I media, sia video che audio, sono ora estremamente diffusi e accessibili da dispositivi palmari tramite reti 3G. L'iPhone fornisce supporto per la riproduzione e lo streaming di video e audio e prevede interfacce di alto livello per abilitare tutte queste opzioni. Con il trasferimento dell'applicazione in Windows, sono disponibili numerose opzioni tra cui scegliere per la riproduzione multimediale, dal lettore integrato di alto livello ai flussi, che permettono di apportare modifiche all'esperienza. A seconda di specifiche esigenze, è possibile valutare la scelta della tecnologia se lo streaming o la riproduzione di video o audio sono requisiti strategici. Utilizzando WPF si ottiene accesso rapido e immediato per ospitare il Windows Media Player nell'applicazione. In C++, è possibile incorporare il Windows Media Player con un po' di supporto, ma si ottiene anche un controllo più capillare.

Riproduzione di video

Riferimento rapido

API per iPhone

API per Windows

Riproduzione di video

UIImagePickerController

MediaElement

Uno degli aspetti positivi dei media sull'iPhone è l'estrema semplicità con cui si avvia e si riproduce un video.

Objective C dell'iPhone

MPMoviePlayerController *mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
if (mp)
{
    // save the movie player object
    self.moviePlayer = mp;
    [mp release];
        
    // Apply the user specified settings to the movie player object
    [self setMoviePlayerUserSettings];
        
    // Play the movie!
    [self.moviePlayer play];
}

Se si ha la possibilità di creare l'applicazione in C# tramite WPF, è possibile aggiungere facilmente un oggetto MediaElement al layout e impostare l'URL per l'origine dei media nell'XAML o nel code-behind, come illustrato in questo esempio.

Windows XAML

<MediaElement Name="myMediaElement" Width="450" Height="250" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Fill" MediaOpened="Element_MediaOpened" MediaEnded="Element_MediaEnded"/>

Windows C#

private void PlayContentUrl( Uri movieURL )
{
    myMediaElement.Source = movieURL;
    myMediaElement.Play();
}

L'esperienza è leggermente diversa in Windows, un aspetto da tenere presente nella progettazione. Per l'iPhone, con OS 3.1 e versioni precedenti, MPMoviePlayerController viene avviato per impostazione predefinita in modalità a schermo intero. Con l'oggetto MediaElement in WPF, il lettore viene incorporato nell'applicazione, circondato da controlli personalizzati.

Se l'applicazione Windows è scritta in C++, è possibile incorporare e controllare un controllo ActiveX di Windows Media Player nell'applicazione, ma è necessario completare diversi passaggi. È possibile leggere un breve riepilogo di ognuno di questi passaggi nell'articolo MSDN “Hosting the Windows Media Player Control in a Windows Application”.

Riproduzione di audio

Riferimento rapido

API per iPhone

API per Windows

Riproduzione di audio

Framework AV Foundation

SoundPlayer

PlaySound

Se l'app per iPhone incorpora audio, è probabile che si abbia familiarità con gli strumenti completi disponibili sull'iPhone per gestire i contenuti audio. Anche se Media Player Framework consente di riprodurre contenuti dalla libreria dell'iPod dell'utente, l'aggiunta di suoni all'applicazione può essere semplice come AV Foundation Framework.

Objective C dell'iPhone

self._player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
if (self._player)
{
    [self._player play]
}

Se si desidera riprodurre file audio o streaming nell'applicazione WPF Windows, è possibile incorporare l'oggetto MediaElement, come descritto in precedenza per la riproduzione di video. In questo modo verrà visualizzato un lettore multimediale. Se si desidera riprodurre suoni direttamente nell'applicazione .NET, senza elementi visivi, è possibile utilizzare la classe SoundPlayer, se i file audio sono in formato wav.

Windows C#

private void playSound() {
    SoundPlayer player = new SoundPlayer();
    player.SoundsLocation = pathToSoundFile;
    player.Play();
}

Per altri formati di file in WPF, è necessario incorporare Windows Media Player tramite la classe MediaPlayer. Il risultato è diverso da quello ottenuto con MediaElement, in quanto non include alcuna rappresentazione visiva. Tuttavia, l'API è simile.

Se l'applicazione Windows viene scritta in C++, è possibile incorporare il Windows Media Player, seguendo la descrizione precedente sui video, che offre un elemento visivo per la riproduzione. Se si desidera un suono di sottofondo, è possibile utilizzare la funzione PlaySound .

Windows C++

#include <msystem.h>

void MyClass::playSound() {
    PlaySound(fileUrl, NULL, SND_FILENAME | SND_ASYNC);
}

Si noti che in questo caso è necessario un suono che possa essere riprodotto dal dispositivo a forma d'onda del netbook, quindi è probabilmente più sicuro riprodurre file .wav.

0