netmind - Primer Contacto con el Desarrollo de Aplicaciones para Windows 8

Preview:

DESCRIPTION

Conferencia de introducción al desarollo de aplicaciones para Windows 8.

Citation preview

F O C U S Q U A L I T Y E X P E R I E N C E

Barcelona, Noviembre 2012

Xavier Saladié

xavisf@expert.netmind.es

Desarrollo Windows 8

Desarrollo Windows 8

Plataforma Aplicaciones Windows 8

Tools

UI Controls

Appbar

FlipView

ListView, Grouping y Semantic zoom

Contratos

Notificaciones

Puntos claves para publicar en el Store

Contenidos

Co

re O

S

Broker

Llamadas Directas

Llamadas Delegadas

Contenedor+ Código Firmado y Verificado

Tu aplicación

WinRT APIs

Process.exe (C#,VB.Net, C++)

o

WWAHost.exe (HTML/JS)

AppXManifest

La app toma 5s para

entrar en suspensión

La App no es

notificada antes de

terminarse

Las apps son informadas

cuando se las ha reanudado

Usuario

Lanza

App

Pantalla

Splash

Local

Storage

La información se

sincroniza desde la

cuenta Microsoft del

usuario

• App usan sus datos y los almacena localmente

The development tools are FREE!

If you use a higher SKU, it just works!

AppXManifest.xml

BlockMap

Signature

Zip Central

Directory

Files / Assets

.appx package

Visual Studio 2012

Clear Button Spell Checking

Reveal Button

<!– Paso 1: declarar el control -->

<div id=“list” data-win-control="WinJS.UI.ListView" data-win-options="{ selectionMode: 'none' }"></div>

/* Paso 2: Llamar a WinJS.UI.processAll() */

/* Paso 3: Usar el control */

document.getElementById(‘list’).winControl.addEventListener (‘selectionchanged’, onSelChanged );

<Page

x:Class="CSharpWinRT.BlankPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:local="using:CSharpWinRT"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

mc:Ignorable="d">

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

<Button Content="La caja de Pandora" Click="Button_Click_1" Height="81" Margin="0,365,0,322" Width="248" HorizontalAlignment="Center" FontWeight="Normal" FontSize="22" />

<Image x:Name="selectedImage" Width="600" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="Uniform" Margin="383,384,383,383" > </Image>

</Grid>

</Page>

Se utiliza para mostrar las acciones de una ventana.

Se mantiene oculta.

Puede cambiar en función de la pantalla.

Se activa con el botón derecho

Iconos: http://msdn.microsoft.com/en-us/library/windows/apps/hh770557.aspx

<Page.BottomAppBar> <AppBar x:Name="BottomAppBar1" Padding="10,0,10,0" AutomationProperties.Name="Bottom App Bar"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="50*"/> <ColumnDefinition Width="50*"/> </Grid.ColumnDefinitions> <StackPanel x:Name="LeftPanel" Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Left"> <Button x:Name="Edit" Style="{StaticResource EditAppBarButtonStyle}" Tag="Edit"/> <Button x:Name="Save" Style="{StaticResource SaveAppBarButtonStyle}" Tag="Save"/> <Button x:Name="Delete" Style="{StaticResource DeleteAppBarButtonStyle}" Tag="Delete"/> </StackPanel> <StackPanel x:Name="RightPanel" Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right"> <Button x:Name="Refresh" Style="{StaticResource RefreshAppBarButtonStyle}" Tag="Refresh"/> <Button x:Name="Previous" Style="{StaticResource PreviousAppBarButtonStyle}" Tag="Previous"/> <Button x:Name="Next" Style="{StaticResource NextAppBarButtonStyle}" Tag="Next"/> <Button x:Name="Help" Style="{StaticResource HelpAppBarButtonStyle}" Tag="Help"/> </StackPanel> </Grid> </AppBar> </Page.BottomAppBar>

public MyPage() { this.InitializeComponent(); Back.Click += Back_Click; } private void Back_Click(object sender, RoutedEventArgs e) { if (this.Frame.CanGoBack) { Frame.GoBack(); } }

Muestra una colección de elementos.

Genera un avance y retroceso por defecto.

Se pueden utilizar plantillas para el contenido.

<FlipView x:Name="flipView1" Width="480" Height="270" BorderBrush="Black" BorderThickness="1"> <FlipView.ItemTemplate> <DataTemplate> <Grid> <Image Width="480" Height="270" Source="{Binding Image}" Stretch="UniformToFill"/> <Border Background="#A5000000" Height="80" VerticalAlignment="Bottom"> <TextBlock Text="{Binding Name}" FontFamily="Segoe UI" FontSize="26.667" Foreground="#CCFFFFFF" Padding="15,20"/> </Border> </Grid> </DataTemplate> </FlipView.ItemTemplate> </FlipView>

Permite mostrar listados de información.

Hace uso de plantillas para configurar el contenido.

<ListView x:Name="ItemListView" Width="Auto" Height="Auto" HorizontalAlignment="Left" Background="{StaticResource ApplicationPageBackgroundThemeBrush}" ItemTemplate="{StaticResource StoreFrontTileTemplate}" ItemContainerStyle="{StaticResource StoreFrontLVTileStyle}" BorderBrush="LightGray" BorderThickness="1" VerticalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" SelectionMode="None"/>

<!– List View Resources --> <DataTemplate x:Key="StoreFrontTileTemplate"> <Grid HorizontalAlignment="Left" Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <StackPanel Orientation="Horizontal" Margin="10,10,0,0"> <Image Source="{Binding Image}" Height="60" Width="60" VerticalAlignment="Center" Margin="0,0,10,0"/> <StackPanel Margin="0,0,0,0" Orientation="Vertical"> <TextBlock TextWrapping="Wrap" Foreground="{StaticResource ApplicationForegroundThemeBrush}" Style="{StaticResource ItemTitleStyle}" Width="200“ VerticalAlignment="Center" Text="{Binding Title}" HorizontalAlignment="Left" FontFamily="Segoe UI" /> <TextBlock TextWrapping="Wrap" Foreground="{StaticResource ApplicationForegroundThemeBrush}" Style="{StaticResource ItemSubtitleStyle}“

Width="200" MaxHeight="20" VerticalAlignment="Center" Text="{Binding Category}" HorizontalAlignment="Left"/> </StackPanel> </StackPanel> </Grid> </DataTemplate> <Style x:Key="StoreFrontLVTileStyle" TargetType="ListViewItem"> <Setter Property="FontFamily" Value="Segoe UI" /> <Setter Property="Height" Value="80" /> <Setter Property="Width" Value="292" /> <Setter Property="Padding" Value="0" /> <Setter Property="Margin" Value="0,0,8,8" /> <Setter Property="HorizontalContentAlignment" Value="Left" /> <Setter Property="VerticalContentAlignment" Value="Top" /> <Setter Property="BorderThickness" Value="0"/> <Setter Property="TabNavigation" Value="Local" /> </Style>

Nos permite agrupar la información.

La forma de crear grupos es personalizada.

Se utiliza el control ListView como base.

<Page.Resources> <CollectionViewSource x:Name="cvs1" IsSourceGrouped="true" /> </Page.Resources> <GridView x:Name="ItemsByCategory" VerticalAlignment="Bottom" ItemsSource="{Binding Source={StaticResource cvs1}}" BorderBrush="{StaticResource ApplicationForegroundThemeBrush}" BorderThickness="1"> <GridView.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </GridView.ItemsPanel> <GridView.ItemTemplate> <DataTemplate> <Grid HorizontalAlignment="Left" Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <StackPanel Orientation="Horizontal" Margin="10,10,0,0"> <Image Source="{Binding Image}" Height="60" Width="60" VerticalAlignment="Center" Margin="0,0,10,0"/> <StackPanel Margin="0,0,0,0" Orientation="Vertical"> <TextBlock TextWrapping="Wrap" Width="200" VerticalAlignment="Center" Text="{Binding Title}" HorizontalAlignment="Left" FontFamily="Segoe UI" /> </StackPanel> </StackPanel> </Grid> </DataTemplate> </GridView.ItemTemplate> <GridView.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" Margin="0"> <TextBlock Text='{Binding Key}' Foreground="Gray" FontSize="25" Margin="5" /> </Grid> </DataTemplate> </GroupStyle.HeaderTemplate> <GroupStyle.Panel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" Height="400" /> </ItemsPanelTemplate> </GroupStyle.Panel> </GroupStyle> </GridView.GroupStyle> </GridView>

internal List<GroupInfoList<object>> GetGroupsByCategory() { List<GroupInfoList<object>> groups = new List<GroupInfoList<object>>(); var query = from item in Collection orderby ((Item)item).Category group item by ((Item)item).Category into g select new { GroupName = g.Key, Items = g }; foreach (var g in query) { GroupInfoList<object> info = new GroupInfoList<object>(); info.Key = g.GroupName; foreach (var item in g.Items) { info.Add(item); } groups.Add(info); } return groups; }

public class GroupInfoList<T> : List<object> { public object Key { get; set; } public new IEnumerator<object> GetEnumerator() { return (System.Collections.Generic.IEnumerator<object>)base.GetEnumerator();} }

List<GroupInfoList<object>> dataCategory = _storeData.GetGroupsByCategory(); cvs1.Source = dataCategory;

<Grid x:Name="ContentPanel" VerticalAlignment="Top" HorizontalAlignment="Left"> <SemanticZoom x:Name="semanticZoom" VerticalAlignment="Bottom"> <SemanticZoom.ZoomedOutView> <GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False"> <GridView.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Group.Key}" FontFamily="Segoe UI Light" FontSize="24"/> </DataTemplate> </GridView.ItemTemplate> <GridView.ItemsPanel> <ItemsPanelTemplate> <WrapGrid ItemWidth="75" ItemHeight="75" MaximumRowsOrColumns="1“ VerticalChildrenAlignment="Center" /> </ItemsPanelTemplate> </GridView.ItemsPanel> <GridView.ItemContainerStyle> <Style TargetType="GridViewItem"> <Setter Property="Margin" Value="4" /> <Setter Property="Padding" Value="10" /> <Setter Property="BorderBrush" Value="Gray" /> <Setter Property="BorderThickness" Value="1" /> <Setter Property="HorizontalContentAlignment" Value="Center" /> <Setter Property="VerticalContentAlignment" Value="Center" /> </Style> </GridView.ItemContainerStyle> </GridView> </SemanticZoom.ZoomedOutView> List<GroupInfoList<object>> dataLetter = _storeData.GetGroupsByLetter();

cvs2.Source = dataLetter; (semanticZoom.ZoomedOutView as ListViewBase).ItemsSource = cvs2.View.CollectionGroups;

<SemanticZoom.ZoomedInView> <GridView ItemsSource="{Binding Source={StaticResource cvs2}}" IsSwipeEnabled="True" ScrollViewer.IsHorizontalScrollChainingEnabled="False"> <GridView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Margin="10,10,0,0" HorizontalAlignment="Left" Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Image Source="{Binding Image}" Height="60" Width="60" VerticalAlignment="Center" Margin="0,0,10,0"/> <TextBlock TextWrapping="Wrap" Foreground="{StaticResource ApplicationForegroundThemeBrush}" Width="200“ VerticalAlignment="Center“ Text="{Binding Title}" HorizontalAlignment="Left" FontFamily="Segoe UI" /> </StackPanel> </DataTemplate> </GridView.ItemTemplate> <GridView.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text='{Binding Key}' Foreground="{StaticResource ApplicationForegroundThemeBrush}" Margin="5" FontSize="18“ FontFamily="Segoe UI Light" /> </DataTemplate> </GroupStyle.HeaderTemplate> <GroupStyle.ContainerStyle> <Style TargetType="GroupItem"> <Setter Property="Template"> ….

//override in app the handler to OnWindowCreated protected override void OnWindowCreated(WindowCreatedEventArgs args) { // Register QuerySubmitted handler for the window at window creation time SearchPane.GetForCurrentView().QuerySubmitted += new TypedEventHandler<SearchPane, SearchPaneQuerySubmittedEventArgs>(OnQuerySubmitted);}

private void OnQuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args) { // Process the query if (MainPage.Current != null) { MainPage.Current.ProcessQueryText(args.QueryText);} }

protected override void OnNavigatedTo(NavigationEventArgs e) { // Register the current page as a share source. this.dataTransferManager = DataTransferManager.GetForCurrentView(); this.dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.OnDataRequested); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // Unregister the current page as a share source. this.dataTransferManager.DataRequested -= new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.OnDataRequested); } // When share is invoked the event handler will be called to populate the datapackage. private void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs e) { bool succeeded = false; string dataPackageText = TextToShare.Text; if (!String.IsNullOrEmpty(dataPackageText)) { DataPackage requestData = e.request.Data; requestData.Properties.Title = TitleInputBox.Text; requestData.Properties.Description = DescriptionInputBox.Text; requestData.SetText(dataPackageText); succeeded = true; } else { e.request.FailWithDisplayText("Enter the text you would like to share and try again."); } return succeeded; }

Permite recibir información de otras apps.

Puede permitir distintos formatos.

Se activa al seleccionar la app dentro de Share.

//On App we override the event for Share Targeting Activated and navigate to the Page receiving data protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args) { var rootFrame = new Frame(); rootFrame.Navigate(typeof(MainPage), args.ShareOperation); Window.Current.Content = rootFrame; Window.Current.Activate(); }

//On NavigateTo event of the page receiving data, we just do it asynchronously protected override async void OnNavigatedTo(NavigationEventArgs e) { // It is recommended to only retrieve the ShareOperation object in the activation handler, return as // quickly as possible, and retrieve all data from the share target asynchronously. this.shareOperation = (ShareOperation)e.Parameter; await Task.Factory.StartNew(async () => { // Retrieve the data package properties. this.sharedDataTitle = this.shareOperation.Data.Properties.Title; this.sharedDataDescription = this.shareOperation.Data.Properties.Description; this.sharedThumbnailStreamRef = this.shareOperation.Data.Properties.Thumbnail; this.shareQuickLinkId = this.shareOperation.QuickLinkId; // Retrieve the data package content. if (this.shareOperation.Data.Contains(StandardDataFormats.Uri)) { // The GetUriAsync() API will throw if there was an error retrieving data. try { this.sharedUri = await this.shareOperation.Data.GetUriAsync(); } catch (Exception ex) { … } }

Square (1x1) Wide (2x1)

IWideTileNotificationContent tileContent = null; ITileWideImageAndText01 wideContent = TileContentFactory.CreateTileWideImageAndText01(); wideContent.RequireSquareContent = false; wideContent.TextCaptionWrap.Text = "The image is in the appx package"; wideContent.Image.Src = "ms-appx:///images/redWide.png"; wideContent.Image.Alt = "Red image"; tileContent = wideContent; // Users can resize tiles to square or wide. // Apps can choose to include only square assets (meaning the app's tile can never be wide), or // include both wide and square assets (the user can resize the tile to square or wide). // Apps cannot include only wide assets. Apps that support being wide should include square tile // notifications since users determine the size of the tile. // create the square template and attach it to the wide template ITileSquareText04 squareContent = TileContentFactory.CreateTileSquareText04(); squareContent.TextBodyWrap.Text = "Hello World! My very own tile notification"; tileContent.SquareContent = squareContent; TileUpdateManager.CreateTileUpdaterForApplication().Update(tileContent.CreateNotification());

http://msdn.microsoft.com/en-us/library/windows/apps/hh913756.aspx

http://msdn.microsoft.com/en-us/library/windows/apps/xaml/Hh868255(v=win.10).aspx

Permite añadir segundos accesos a una app.

El usuario siempre debe aprobar este segundo tile.

Es posible actualizar su contenido.

// Prepare package images for use as the Tile Logo and small Logo in our tile to be pinned Uri logo = new Uri("ms-appx:///Assets/squareTile-sdk.png"); Uri smallLogo = new Uri("ms-appx:///Assets/smallTile-sdk.png"); // In this sample, we'll pass in the date and time the secondary tile was pinned. public const string SecTileId = "SecondaryTileId" string tArguments = SecTileId + " WasPinnedAt=" + DateTime.Now.ToLocalTime().ToString(); // Create a 1x1 Secondary tile SecondaryTile secTile = new SecondaryTile(SecTileId, "Title text", "Name when searching", tArguments, TileOptions.ShowNameOnLogo, logo); secTile.ForegroundText = ForegroundText.Dark; secTile.SmallLogo = smallLogo; // OK, the tile is created and we can now attempt to pin the tile. bool isPinned = await SecTile.RequestCreateForSelectionAsync( MainPage.GetElementRect((FrameworkElement)sender),Windows.UI.Popups.Placement.Below);

// Typical OnLaunched override Method of App.xaml.cs async protected override void OnLaunched(LaunchActivatedEventArgs args) { if (args.PreviousExecutionState == ApplicationExecutionState.Terminated) { // Do an asynchronous restore await RestoreData() } if (Window.Current.Content == null) { var rootFrame = new Frame(); rootFrame.Navigate(typeof(MainPage)); ((MainPage)rootFrame.Content).LaunchArgs = args; Window.Current.Content = rootFrame; } else { if (args.Arguments != “”) { //Inspect content of args and navigate where is needed } } Window.Current.Activate(); }

IToastNotificationContent toastContent = null; IToastImageAndText03 templateContent = ToastContentFactory.CreateToastImageAndText03(); templateContent.TextHeadingWrap.Text = "Heading text that wraps"; templateContent.TextBody.Text = "Body text"; templateContent.Image.Src = "images/toastImageAndText.png"; templateContent.Image.Alt = "Placeholder image"; toastContent = templateContent; // Set the launch activation context parameter on the toast. toastContent.Launch = "Context123"; // Create a toast from content ToastNotification toast = toastContent.CreateNotification(); // create a ToastNotifier object to show the toast ToastNotificationManager.CreateToastNotifier().Show(toast);

// In app.OnLaunched and in app.OnSearchActivated we register event for managing settings // Register handler for CommandsRequested events from the settings pane SettingsPane.GetForCurrentView().CommandsRequested += OnCommandsRequested; void OnCommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args) { // Add an About command var about = new SettingsCommand("about", "About", (handler) => { var settings = new SettingsFlyout(); settings.Content = new AboutUserControl(); settings.HeaderBrush = new SolidColorBrush(_background); settings.Background = new SolidColorBrush(_background); settings.HeaderText = "About"; settings.IsOpen = true; }); args.Request.ApplicationCommands.Add(about); // Add a Preferences command var preferences = new SettingsCommand("preferences", "Preferences", (handler) => { var settings = new SettingsFlyout(); settings.Content = new PreferencesUserControl(); settings.HeaderBrush = new SolidColorBrush(_background); settings.Background = new SolidColorBrush(_background); settings.HeaderText = "Preferences"; settings.IsOpen = true; }); args.Request.ApplicationCommands.Add(preferences); }

<UserControl x:Class="ContosoCookbook.PreferencesUserControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ContosoCookbook" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid> <ToggleSwitch x:Name="Remember" Header="Remember where I was" Toggled="OnToggled" /> </Grid> </UserControl>

private void Button_Click(object sender, RoutedEventArgs e) { Flyout flyOut = new Flyout(); flyOut.PlacementTarget = sender as UIElement; flyOut.Placement = PlacementMode.Top; flyOut.Content = new FlyoutUserControl(); flyOut.IsOpen = true; }

// Create the message dialog and set its content and title var msgDlg = new MessageDialog(“Updates found. Would you like to install?", "Updates"); // Add commands and set their callbacks msgDlg.Commands.Add(new UICommand("Don't install", (command) => { rootPage.NotifyUser("'Don't install' command has been selected.", NotifyType.StatusMessage); })); msgDlg.Commands.Add(new UICommand("Install updates", (command) => { rootPage.NotifyUser("'Install updates' command has been selected.", NotifyType.StatusMessage); })); // Set the command that will be invoked by default msgDlg.DefaultCommandIndex = 1; // Show the message dialog await msgDlg .ShowAsync();

Escenario Deberías….

using Windows.Networking.Connectivity;

...

public static bool isConnected()

{ var p=NetworkInformation.GetInternetConnectionProfile();

if (p!=null) {

if (p.GetNetworkConnectivityLevel()==NetworkConnectivityLevel.InternetAccess)

{ return true }

else

{ return false; } }

else { return false; } }

121

Anuncia con Microsoft Advertising o tu ad vendor

favorito

Introducción al desarrollo de aplicaciones Metro para Windows 8

http://blogs.msdn.com/b/esmsdn/archive/2012/07/18/introducci-243-n-al-desarrollo-de-aplicaciones-metro-para-windows-8.aspx

Cursos de Formación de Windows 8

http://blogs.msdn.com/b/esmsdn/archive/2012/09/04/cursos-de-formaci-243-n-de-windows-8.aspx

Tips & Tricks de desarrollo para Windows 8

http://blogs.msdn.com/b/esmsdn/archive/2012/08/08/tips-amp-tricks-de-desarrollo-para-windows-8.aspx

Windows 8 Metro style app samples

http://code.msdn.microsoft.com/windowsapps

Problemas comunes para pasar la certificación de Windows 8

http://blogs.msdn.com/b/esmsdn/archive/2012/07/31/problemas-comunes-para-pasar-la-certificaci-243-n-de-windows-8.aspx

V3.0 Certification requirements

http://msdn.microsoft.com/en-us/library/windows/apps/jj128432.aspx

F O C U S Q U A L I T Y E X P E R I E N C E

Preguntas y aclaraciones

Muchas gracias por su atención Xavier Saladié

xavisf@expert.netmind.es

Recommended