czwartek, 12 stycznia 2012

Notatki o Windows 8 - odc. 19

Aplikacje Metro w C#:  różnice w stosunku do Silverlight i WPF, animacje (transition, theme, enabling), multimedia, UI Automation, PlayTo, zbliżanie i dotykanie urządzeń, drukowanie, AutoPlay, publikowanie.

The different types of brushes are: SolidColorBrush, LinearGradientBrush, and ImageBrush.

Currently non-rectangular clipping is not supported.

In general, creating animations in a Metro style app using C++, C#, or Visual Basic is the same as Silverlight or WPF, however, there are a number of important differences:

  • You can create custom animations like before, but you can also use theme animations and transition animations to quickly create Metro style app recommended UI animations.
  • Not all custom animations you create will run by default (called "dependent animations"). If your animation will cause a layout change or otherwise potentially impact performance, you need to explicitly enable the animation in code.
  • Custom easing functions are not currently supported.

A list of some controls with built in animations:

Transition animations

<Button Content="Transitioning Button">

     <Button.Transitions>

         <TransitionCollection>

             <EntranceThemeTransition/>

         </TransitionCollection>

     </Button.Transitions>

 </Button>

Now, when the button is first rendered, it will swiftly slide into view rather than just appear. Note that the event handling and timing of the animation is handled for you. You can set a few properties on the animation object in order to tweak how far it slides and from what direction, but it's really meant to be a simple API for a specific scenario--that is, to make an eye-catching entrance.

    <Style x:Key="DefaultButtonStyle" TargetType="Button">

         <Setter Property="Transitions">

             <Setter.Value>

                 <TransitionCollection>

                     <EntranceThemeTransition/>

                 </TransitionCollection>

             </Setter.Value>

        </Setter>

    </Style>

There are several other transition effects you can apply to your UI elements as they are added, removed, re-ordered, etc.

  • AddDeleteThemeTransition  - provides the animated transition behavior for when controls add or delete children or content. Typically the control involved is an item container.
  • ContentThemeTransition - provides the animated transition behavior for when the content of a control is changing. This might be applied in addition to AddDeleteThemeTransition.
  • EntranceThemeTransition - provides the animated transition behavior for when controls first appear.
  • ReorderThemeTransition - provides the animated transition behavior for when list-view controls items change order. Typically this is due to a drag-drop operation. Different controls and themes potentially have varying characteristics for the animations involved.
Theme animations

You may want to have a bit more control over when and in what order your animation effects take place. You can use theme animations to enable more control while still using a consistent Windows 'theme' for how your animation behaves. Theme animations also require less code than custom animations.

A rectangle fades out of view

<Storyboard x:Name="myStoryboard">

     <FadeOutThemeAnimation Storyboard.TargetName="myRectangle"  />

</Storyboard>

You must use a Storyboard to contain the animation and you need to use an event handler to start the Storyboard. In addition, you can change the default behavior of the animation. For example, you can slow down the fade out by increasing the Duration on the FadeOutThemeAnimation.

There are several other theme animations you can apply to your UI elements to create a Windows-style animation effects.

  • CrossSlideBackThemeAnimation - represents the preconfigured animation that applies to controls when an element slides back into its layout slot after a CrossSlide.
  • CrossSlideHintThemeAnimation - represents the preconfigured animation that indicates that a CrossSlide is now possible.
  • DropTargetItemThemeAnimation - represents the preconfigured animation that applies to potential drop target elements.
  • FadeInThemeAnimation - represents the preconfigured opacity animation that applies to controls when they are first shown.
  • FadeOutThemeAnimation - represents the preconfigured opacity animation that applies to controls when they are removed from UI or hidden.
  • PopInThemeAnimation - represents the preconfigured animation that applies to pop-in components of controls as they appear. This animation combines opacity and translation.
  • PopOutThemeAnimation - represents the preconfigured animation that applies to pop-in components of controls as they are closed/removed. This animation combines opacity and translation.
  • RepositionThemeAnimation - represents the preconfigured animation for an object as it is repositioned.
  • SelectDownThemeAnimation - represents the preconfigured animation that applies to selection of an item.
  • SelectUpThemeAnimation - represents the preconfigured animation that applies to deselection of an item.
  • SplitCloseThemeAnimation - represents the preconfigured animation that reveals a target UI using a "split" animation.
  • TapDownThemeAnimation - represents a preconfigured animation for user action that taps an item or element.
  • TapUpThemeAnimation - represents a preconfigured animation for user action that runs after an item or element is tapped (pointer no longer on and over).
  • SplitOpenThemeAnimation - represents the preconfigured animation that reveals a target UI using a "split" animation.

Enable your animation

Not all custom animations will run by default. Animations that can have a performance impact on your app (particularly layout-effecting animations) are called 'dependent animations' and need to be enabled before they run. To enable your animation, set the EnableDependentAnimation to True on the animation object.

        <Storyboard x:Name="myStoryboard">

            <!-- Animate the center point of the ellipse. -->

            <PointAnimation EnableDependentAnimation="True" Storyboard.TargetProperty="Center"

              Storyboard.TargetName="MyAnimatedEllipseGeometry"

              Duration="0:0:5"

              From="20,200"

              To="400,100"

              RepeatBehavior="Forever" />

        </Storyboard>

In addition to using the easing functions included in the run-time, you can create your own custom easing functions by inheriting from EasingFunctionBase.

Multimedia

There are two ways to play audio and video media in your Metro style app using C++, C#, or Visual Basic: using the MediaElement object and using the MediaPlayer object.

<MediaPlayer Name="mediaPlayer" Width="400" Loaded="mediaPlayer_Loaded"  />

private void mediaPlayer_Loaded(object sender, RoutedEventArgs e)

{

    mediaPlayer.Source = new Uri(mediaPlayer.BaseUri, "Videos/video.wmv");

}

Devices

        // Sensor and dispatcher variables

        private Accelerometer _accelerometer;

        private CoreDispatcher _cd;

 

        // This event handler writes the current accelerometer reading to

        // the three acceleration text blocks on the application's main page.

 

        private void ReadingChanged(object sender, AccelerometerReadingChangedEventArgs e)

        {

            _cd.InvokeAsync(CoreDispatcherPriority.Normal, (s, a) =>

            {

                AccelerometerReading reading = (a.Context as AccelerometerReadingChangedEventArgs).Reading;

                txtXAxis.Text = String.Format("{0,5:0.00}", reading.AccelerationX);

                txtYAxis.Text = String.Format("{0,5:0.00}", reading.AccelerationY);

                txtZAxis.Text = String.Format("{0,5:0.00}", reading.AccelerationZ);

 

            }, this, e);

        }

 

        public MainPage()

        {

            InitializeComponent();

 

            _accelerometer = Accelerometer.GetDefault();

            _cd = Window.Current.CoreWindow.Dispatcher;

 

            // Assign an event handler for the accelerometer reading-changed event

            if (_accelerometer != null)

            {

                _accelerometer.ReadingChanged += new TypedEventHandler<Accelerometer, AccelerometerReadingChangedEventArgs>(ReadingChanged);

            } 

        }

Accessibility

Controls provide built-in keyboard support and support for screen readers, which take advantage of accessibility frameworks that already support HTML and other UI technologies. If necessary, you can also add similar support to your own custom controls, using a concept of "Automation peer". The Metro style app is treated as a top-level window by UI Automation, and all the content within that app window are reported and are available to a UI Automation client.

Keyboard

To provide good keyboard support, you must ensure that every part of your application can be used with a keyboard. You can ensure that the tab order matches the visual order by adjusting the XAML, or you can override the default tab order by setting the TabIndex property. If you want to exclude a control from the tab order, you can set the IsTabStop property to false. Only classes that derive from Control support input focus and tab navigation.

You can document access keys through screen readers by setting the AutomationProperties.AccessKey attached property to a string that describes the shortcut key. There is also an AutomationProperties.AcceleratorKey attached property for documenting non-mnemonic shortcut keys, although screen readers generally treat both properties the same way. In general, you should document shortcut keys in multiple ways, using tooltips, automation properties, and written help documentation.

<Button x:Name="Play" Margin="1,2"

    ToolTipService.ToolTip="shortcut key: P"

    AutomationProperties.AccessKey="P">

    <TextBlock><Underline>P</Underline>lay</TextBlock>

</Button>

Screen reader

To support screen readers, you sometimes must provide text alternatives to non-textual information in the UI, such as images and charts, excluding any purely decorative or structural elements. You can do this by setting the AutomationProperties.Name attached property.

The TextBox is labeled by both the TextBlock and the AutomationProperties.Name value. You can avoid the redundant labeling by setting the AutomationProperties.LabeledBy property instead, which indicates where the AutomationProperties.Name should get its value.

<TextBlock x:Name="QuantityLabel">Quantity</TextBlock>

<TextBox x:Name="Quantity" Width="25"

  AutomationProperties.LabeledBy="{Binding ElementName=QuantityLabel}"/>

If a brief description is insufficient to explain the control, you can set the AutomationProperties.HelpText attached property in addition to AutomationProperties.Name.

When you create a custom control, you should also implement or extend one or more AutomationPeer subclasses to provide accessibility support.

Media

If your application includes audio or audiovisual media, you should provide text alternatives or captions for the hearing impaired.

<MediaElement x:Name="media" Width="300" Height="200"

    MarkerReached="OnMarkerReached" Source="media.wmv"/>

 

public void OnMarkerReached(object sender, TimelineMarkerRoutedEventArgs e)

{

    CaptionTextBlock.Text = e.Marker.Text;

}

PlayTo

async private void LoadVideo()

{

    try

    {

        IReadOnlyList<Windows.Storage.IStorageFile> resultsLibrary =

    await Windows.Storage.KnownFolders.VideosLibrary.GetFilesAsync();

        MessageBlock.Text += "Play video: " + resultsLibrary[0].FileName + "\n";

        Windows.Storage.Streams.IRandomAccessStream videoStream =

    await resultsLibrary[0].OpenAsync(Windows.Storage.FileAccessMode.Read);

        VideoPlayer.SetSource(videoStream, resultsLibrary[0].FileType);

        VideoPlayer.Play();

    }

    catch (Exception ex)

    {

        MessageBlock.Text += "Exception encountered: " + ex.Message + "\n";

    }

}

 

private Windows.Media.PlayTo.PlayToManager ptm;

 

private void GridLoaded(object sender, RoutedEventArgs e)

{

    ptm = Windows.Media.PlayTo.PlayToManager.GetForCurrentView();

    ptm.SourceRequested += SourceRequested;

 

    ShowMedia("VideoPlayer");

    LoadVideo();

}

 

private void SourceRequested(Windows.Media.PlayTo.PlayToManager sender,

                             Windows.Media.PlayTo.PlayToSourceRequestedEventArgs e)

{

    try

    {

        ClearConnections();

 

        Windows.Media.PlayTo.PlayToSourceRequest sr = e.SourceRequest;

        Windows.Media.PlayTo.PlayToSourceDeferral deferral = sr.GetDeferral();

        Windows.Media.PlayTo.PlayToSource controller = null;

 

        try

        {

            if (mediaElement is Image)

            {

                controller = ((Image)mediaElement).PlayToSource;

            }

            else

            {

                controller = ((MediaElement)mediaElement).PlayToSource;

            }

 

        }

        catch (Exception ex)

        {

            MessageBlock.Text += "Exception encountered: " + ex.Message + "\n";

        }

 

        sr.SetSource(controller);

        deferral.Complete();

    }

    catch (Exception ex)

    {

        MessageBlock.Text += "Exception encountered: " + ex.Message + "\n";

    }

}

 

private void ClearConnections()

{

    if (VideoPlayer.PlayToSource != null)

        VideoPlayer.PlayToSource.Connection.Disconnect();

 

    if (AudioPlayer.PlayToSource != null)

        AudioPlayer.PlayToSource.Connection.Disconnect();

 

    if (ImagePlayer.PlayToSource != null)

        ImagePlayer.PlayToSource.Connection.Disconnect();

}

Proximity and tapping

Always use the Windows prefix for proximity message types.

Windows.Networking.Proximity.ProximityDevice proximityDevice = null;

 

bool InitializeProximityDevice()

{

    try

    {

        if (proximityDevice == null)

            proximityDevice =

    Windows.Networking.Proximity.ProximityDevice.GetDefault();

    }

    catch

    {

        MessageTextBlock.Text += "Failed to get default proximity device.\n";

        return false;

    }

    return true;

}

 

long publishedMessageId = -1;

long receivedMessageId = -1;

 

private void PublishMessageButton_Click(object sender, RoutedEventArgs e)

{

    if (publishedMessageId != -1)

        proximityDevice.StopPublishingMessage(publishedMessageId);

 

    publishedMessageId = proximityDevice.PublishMessage("Windows.SampleMessageType",

                                                        PublishMessageTextBox.Text);

    MessageTextBlock.Text +=

"Proximity message published, enter proximity to transmit.\n";

}

 

private void SubscribeForMessageButton_Click(object sender, RoutedEventArgs e)

{

    if (receivedMessageId != -1)

        proximityDevice.StopSubscribingForMessage(receivedMessageId);

 

    receivedMessageId =

proximityDevice.SubscribeForMessage("Windows.SampleMessageType",

                                            MessageReceived);

}

 

private void MessageReceived(Windows.Networking.Proximity.ProximityDevice device,

                             Windows.Networking.Proximity.ProximityMessage message)

{

    ReceivedMessageTextBlock.Text = "Message received: " + message.DataAsString;

}

Printing

In a Metro style app, printing might mean printing data to a physical printer, or sending a "printable document" to another device or file.

You use the PrintDocument to prepare the pages that you pass to the PrintTask as the DocumentSource. You prepare the pages to print in the GetPreviewPage, Paginate, and AddPages events.

In the Paginate event handler, you create a collection of pages to be printed. For each page, you add a named UIElement that you want to print. The UIElement can be any named UIElement in the application The specified UIElement does not have to be a part of the visual tree. To print the entire page, you can specify the root UserControl or Page element.

        private Windows.Graphics.Printing.PrintManager printManager;      

        private Windows.UI.Xaml.Printing.PrintDocument printDocument;

        private object pdDocumentSource;

        private List<UIElement> printPages = null;

 

        public MainPage()

        {

            InitializeComponent()

            IntializePrinting();

        }

 

        private void IntializePrinting()

        {

            //Create new print document

            printDocument = new PrintDocument();

            // Printing happens off-thread so cache the DocumentSource to use later.

            pdDocumentSource = printDocument.DocumentSource;

            printDocument.AddPages += new AddPagesEventHandler(printDoc_AddPages);

            printDocument.Paginate += new PaginateEventHandler(printDoc_Paginate);

            printDocument.GetPreviewPage += new GetPreviewPageEventHandler(printDoc_GetPreviewPage);

 

            printManager = PrintManager.GetForCurrentView();

            printManager.PrintTaskInitializing += new Windows.Foundation.TypedEventHandler<PrintManager, PrintTaskInitializingEventArgs>(printManager_PrintTaskInitializing);

        }

 

        void printManager_PrintTaskInitializing(PrintManager sender, PrintTaskInitializingEventArgs args)

        {

            //This happens off-thread so you must cache the DocumentSource first before passing it to avoid a COM exception.

            PrintTask printTask = args.Request.InitializePrintTask(pdDocumentSource, "Print Page Title");

        }

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            PrintManager.ShowPrintUI();

        }

 

        void printDoc_GetPreviewPage(object sender, GetPreviewPageEventArgs e)

        {

            printDocument.SetPreviewPage(e.PageNumber, printPages[e.PageNumber - 1]);

        }

 

        void printDoc_Paginate(object sender, PaginateEventArgs e)

        {

            if (printPages == null)

            {

                printPages = new List<UIElement>();

                printPages.Add(this);

                printPages.Add(PageContentPanel);

            }

 

            printDocument.SetPreviewPageCount(printPages.Count);

        }

 

        void printDoc_AddPages(object sender, AddPagesEventArgs e)

        {

            for (int i = 0; i < printPages.Count; i++)

            {

                printDocument.AddPage(printPages[i]);

            }

 

            printDocument.AddPagesComplete();

        }

AutoPlay

protected override void OnFileActivated(FileActivatedEventArgs args)

{

    if (args.Verb == "show")

    {

        MainPage page = (MainPage)Window.Current.Content;

 

        // Call DisplayImages with root folder from camera storage.

        page.DisplayImages((Windows.Storage.StorageFolder)args.Files[0]);

    }

 

    if (args.Verb == "copy")

    {

        MainPage page = (MainPage)Window.Current.Content;

 

        // Call CopyImages with root folder from camera storage.

        page.CopyImages((Windows.Storage.StorageFolder)args.Files[0]);

    }

 

    base.OnFileActivated(args);

}

 

async internal void DisplayImages(Windows.Storage.StorageFolder rootFolder)

{

    // Display images from first folder in root\DCIM.

    var dcimFolder = await rootFolder.GetFolderAsync("DCIM");

    var folderList = await dcimFolder.GetFoldersAsync();

    var cameraFolder = folderList[0];

    var fileList = await cameraFolder.GetFilesAsync();

    for (int i = 0; i < fileList.Count; i++)

    {

        var file = (Windows.Storage.StorageFile)fileList[i];

        FilesBlock.Text += file.Name + "\n";

        DisplayImage(file, i);

    }

}

 

async private void DisplayImage(Windows.Storage.IStorageItem file, int index)

{

    try

    {

        var sFile = (Windows.Storage.StorageFile)file;

        Windows.Storage.Streams.IRandomAccessStream imageStream =

    await sFile.OpenAsync(Windows.Storage.FileAccessMode.Read);

        Windows.UI.Xaml.Media.Imaging.BitmapImage imageBitmap =

    new Windows.UI.Xaml.Media.Imaging.BitmapImage();

        imageBitmap.SetSource(imageStream);

        var element = new Image();

        element.Source = imageBitmap;

        element.Height = 100;

        Thickness margin = new Thickness();

        margin.Top = index * 100;

        element.Margin = margin;

        FilesCanvas.Children.Add(element);

    }

    catch (Exception e)

    {

        FilesBlock.Text += e.Message + "\n";

    }

}

 

async internal void CopyImages(Windows.Storage.StorageFolder rootFolder)

{

    // Copy images from first folder in root\DCIM.

    var dcimFolder = await rootFolder.GetFolderAsync("DCIM");

    var folderList = await dcimFolder.GetFoldersAsync();

    var cameraFolder = folderList[0];

    var fileList = await cameraFolder.GetFilesAsync();

 

    try

    {

        var folderName = "Images " + DateTime.Now.ToString("yyyy-MM-dd HHmmss");

        Windows.Storage.StorageFolder imageFolder = await

            Windows.Storage.KnownFolders.PicturesLibrary.

            CreateFolderAsync(folderName);

 

        foreach (Windows.Storage.IStorageItem file in fileList)

        {

            CopyImage(file, imageFolder);

        }

    }

    catch (Exception e)

    {

        FilesBlock.Text += "Failed to copy images.\n" + e.Message + "\n";

    }

}

 

async internal void CopyImage(Windows.Storage.IStorageItem file,

                              Windows.Storage.StorageFolder imageFolder)

{

    try

    {

        Windows.Storage.StorageFile sFile = (Windows.Storage.StorageFile)file;

        await sFile.CopyAsync(imageFolder, sFile.FileName);

        FilesBlock.Text += sFile.FileName + " copied.\n";

    }

    catch (Exception e)

    {

        FilesBlock.Text += "Failed to copy file.\n" + e.Message + "\n";

    }

}

Publishing

Submitting your app to us for certification is not currently supported by Windows Developer Preview. You can, however, publish your app and send the resulting package to other developers with Windows Developer Preview and have installed a developer license on their computer.

Build your app's packages using Visual Studio
  • In the Solution Explorer of Visual Studio 11 Express for Windows Developer Preview, select the app in your project.
  • From the Store menu item, select Create App Package...
  • In the Build Package dialog box, select Build a package to use locally only, and then click Next.
  • Select the folder for the resulting package and set the version number that you want to assign to the package.
  • Review the summary information, and then click Build to build the package.
  • Review the results and the path to the package file in the Output window.
Build your app's packages using command-line tools
  • Review the entries in your package's AppxManifest.xml file to make sure they are correct.
  • Into a single folder, collect the files that you want to include in your app's package.
  • Call MakeAppx, passing the path to the folder described in the previous step.

c.d.n

Brak komentarzy: