Archive

Archive for the ‘Metro’ Category

Windows 8 Sharing Contract

September 24, 2012 Leave a comment

Windows 8 Apps support a new type of sharing, which is kind of Clipboard 2.0.

Every app can share data to any other app.

The user is in charge of this sharing, because this enables apps to break out of their sandbox. This looks like this:

User selects an image in the photo app (green), then shares it to e.g. the mail app to send the image via mail.

image

The sharing target app displays a dialog (red), where the user can add some additional data and then close the dialog to go back to the share source app.

Sharing Source App:

In order to use this sharing contract the share source app must implement this:

private void btnShare_Click_1(object sender, RoutedEventArgs e)
{
    dtMgr = DataTransferManager.GetForCurrentView();
    dtMgr.DataRequested += dtMgr_DataRequested;
}

void dtMgr_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    args.Request.Data.Properties.Title = "Data from Sample Share app";
    args.Request.Data.Properties.Description = "Description from Sample Share app";
    args.Request.Data.Properties.Thumbnail = 
        RandomAccessStreamReference.CreateFromUri(
                new Uri("ms-appx:///Assets/SharingIcon.png"));

    // Share Text data
    args.Request.Data.SetText(txtShareText.Text);

    // Share Uri data
    args.Request.Data.SetUri(new Uri(txtUri.Text));
}

 

Sharing Target app:

The sharing target app needs a little bit more effort:

First, a new XAML Page of type "Share Target Contract" must be added.

When you add this page, then the wizard will also take care of adding the declaration "Share Target" to the Package.appxmanifest.

If you use any other page, then this must be done manually!

The declaration does also define, which type of data will be accepted for sharing, which can be any of the default formats in Windows.ApplicationModel.DataTransfer.StandardDataFormats, but also any custom data format

The dialog looks like this:

image

The wizard does also add the overload function OnShareTargetActivated to App.xaml.cs, which must be done manually, if some other XAML page will be added as target page.

 

/// <summary>
/// Invoked when the application is activated as the target of a sharing operation.
/// </summary>
/// <param name="args">Details about the activation request.</param>
protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
    var shareTargetPage = new ShareTargetPage();
    shareTargetPage.Activate(args);
}

 

 

When using any other XAML page as target page, then make sure, to add the statement

d:ExtensionType="ShareTarget">

to the top of the target page and make sure, that the UI is 645 px in width.

The header will always be displayed by the system and will take about 80 px in height.

Example for a sharing target page:

image

 

The sharing target page must include a function called Activate(), which will deliver the data from the source application, after the user has selected the target app.

args.ShareOperation does contain the data from the source app or allows to fetch data from the source app using e.g. Data.GetTextAsync(), Data.GetUriAsync(),….

After the app is finished with the shared data, it must report back to the source app.

Also this is done on top of the ShareOperation using ReportCompleted()

Optionally the app can return a so called quicklink, which will add a new entry to the share target list and allow the user to select the same target parameters very quickly the next time.

The mail app e.g does remember the last recipient and adds a quicklink to the target selection list.

Example where the list of quick links is marked in red:

image

 

/// <summary>
/// Invoked when another application wants to share content through this application.
/// </summary>
/// <param name="args">Activation data used to coordinate the process with Windows.</param>
public async void Activate(ShareTargetActivatedEventArgs args)
{
    this.shareOperation = args.ShareOperation;

    // Communicate metadata about the shared content through the view model
    
    var shareData = this.shareOperation.Data;
    var shareDataProperties = shareData.Properties;


    txtStatus.Text = "AppName: " + shareDataProperties.ApplicationName + 
                     "\nTitle: " + shareDataProperties.Title + "\n" +
                     "\nDescription: " + shareDataProperties.Description + 
                     "\nQuicklink: " + shareOperation.QuickLinkId + 
                     "\nAvailable Formats: ";

    for (int i = 0; i < shareData.AvailableFormats.Count; i++)
    {
        txtStatus.Text += "\n - " + shareData.AvailableFormats[i];
    }


    if (this.shareOperation.Data.Contains(StandardDataFormats.Uri))
    {
        txtStatus.Text += "\n\n";
        Uri uri = await this.shareOperation.Data.GetUriAsync();
        if (uri != null)
        {
            txtStatus.Text += "\nUri: " + uri.AbsoluteUri + Environment.NewLine;
        }
    }

    if (this.shareOperation.Data.Contains(StandardDataFormats.Text))
    {
        txtStatus.Text += "\n\n";
        string text = await this.shareOperation.Data.GetTextAsync();
        if (text != null)
        {
            txtStatus.Text += "\nText: " + text + Environment.NewLine;
        }
    }

    if (this.shareOperation.Data.Contains(StandardDataFormats.StorageItems))
    {
        txtStatus.Text += "\n\n";
        IReadOnlyList<IStorageItem> storageItems = null;
        storageItems = await this.shareOperation.Data.GetStorageItemsAsync();
        string fileList = String.Empty;
        for (int index = 0; index < storageItems.Count; index++)
        {
            fileList += storageItems[index].Name;
            if (index < storageItems.Count - 1)
            {
                fileList += ", ";

            }
        }
        txtStatus.Text += "StorageItems: " + fileList + Environment.NewLine;
    }


    if (this.shareOperation.Data.Contains(StandardDataFormats.Html))
    {
        txtStatus.Text += "\n\n";
        string htmlFormat = await this.shareOperation.Data.GetHtmlFormatAsync();
        string htmlFragment = HtmlFormatHelper.GetStaticFragment(htmlFormat);

        txtStatus.Text += "HTML: " + Environment.NewLine;
        this.MyWebView.Visibility = Visibility.Visible;
        this.MyWebView.NavigateToString("<html><body>" + htmlFragment + "</body></html>");
    }
    
    if (this.shareOperation.Data.Contains(StandardDataFormats.Bitmap))
    {
        txtStatus.Text += "\n\n";
        this.MyIMageLabel.Visibility = Visibility.Visible;
        MyImage.Visibility = Visibility.Visible;
        IRandomAccessStreamReference imageReceived = 
            await this.shareOperation.Data.GetBitmapAsync();
        
        IRandomAccessStreamWithContentType stream = 
            await imageReceived.OpenReadAsync();

        BitmapImage bitmapImage = new BitmapImage();
        bitmapImage.SetSource(stream);
        MyImage.Source = bitmapImage;
    }              

    Window.Current.Content = this;
    Window.Current.Activate();
}

private async void btnShare_Click_1(object sender, RoutedEventArgs e)
{
    QuickLink ql = new QuickLink()
    {
        Id = "HS42",
        Title = "MyQuicklink",
        SupportedFileTypes = { ".txt", ".jpg", ".bmp", ".gif", 
                               ".docx", ".pptx", ".xlsx", ".pdf", 
                               ".wmv", ".mp3", ".mp4", ".wma" },
        SupportedDataFormats = { StandardDataFormats.Text, 
                                 StandardDataFormats.Uri, 
                                 StandardDataFormats.Bitmap, 
                                 StandardDataFormats.StorageItems }
    };
   
    StorageFile iconFile = await Windows.ApplicationModel.Package.Current
        .InstalledLocation
        .CreateFileAsync("Assets\\Quicklink.png", CreationCollisionOption.OpenIfExists) 
        as StorageFile;
    ql.Thumbnail = RandomAccessStreamReference.CreateFromFile(iconFile);

    
    shareOperation.ReportCompleted(ql);            
}

 

 .

Categories: Metro, Windows 8

Windows 8 Theme changing between dark and light

September 23, 2012 Leave a comment

The new UI Style Windows 8 apps support a dark and light theme, which cannot be changed during runtime.

The developer can set it either in code, which must be very early during startup like in the App ctor:

 

public App()
{
    Application.Current.RequestedTheme = ApplicationTheme.Light; 
    this.InitializeComponent();
    //..
}

 

or directly in App.xaml like this:

 

<Application
x:Class="AppBarDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppBarDemo"
xmlns:localData="using:AppBarDemo.Data"
RequestedTheme="Light">

 

The Visual Studio designer preview also supports themes. They can be set in the Devices toolbar:

image

 

If there should be other values used for the different color settings, then you can add this settings to StandardStyles.xaml

<SolidColorBrush x:Key="ApplicationPageBackgroundThemeBrush" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ApplicationForegroundThemeBrush" Color="#333333"/>
<SolidColorBrush x:Key="ApplicationSecondaryForegroundThemeBrush" Color="#FF991100"/>
<SolidColorBrush x:Key="ApplicationPointerOverForegroundThemeBrush" Color="#AAAAAA"/>
<SolidColorBrush x:Key="BackButtonBackgroundThemeBrush" Color="#00000000"/>
<SolidColorBrush x:Key="BackButtonForegroundThemeBrush" Color="#FFFFFF"/>
<SolidColorBrush x:Key="BackButtonPressedBackgroundThemeBrush" Color="#00000000"/>
<SolidColorBrush x:Key="BackButtonPressedForegroundThemeBrush" Color="#CCCCCC"/>
<SolidColorBrush x:Key="BackButtonPointerOverBackgroundThemeBrush" Color="#00000000"/>

 

 .

Categories: Metro, Windows 8

Windows 8 Lockscreen app

September 18, 2012 Leave a comment

In the new app lifecycle model, all apps, which are not currently in use by the user will be terminated after 10 seconds in the background. This helps to preserve system resources like CPU cycles and battery life. However there are some cases, when apps should run in the background, even if there is no UI visible for the user.

There are different types of scenarios possible and the WinRT library has solutions for this scenarios, which i’ll describe in another post.

There is one special scenario for so called lockscreen apps.

When the user has locked his PC, then certain apps can still interact with the lockscreen and display some status information there.

Only the user can decide, which apps should be visible and active in the lockscreen and the number of apps is limited to seven plus one app for detailed information.

This app can update its tile, all others can only update the badge data. The user defines, which app are on the lockscreen and which single app can update the tile with a detailed status. For this, open the "Personalize" tab in the "Change PC settings" app.

image

 

All this apps are then visible in the red marked area in the lockscreen picture below.

The logo and the badge data, which can be a number from 1 to 99 or a symbol from here can be displayed.

The single app, which is able to display detailed information will be shown inside the yellow frame.

It can display tile data in any of this formats, which must be a wide template (2x)

image

 

Not every app can be used as lockscreen app. The apps must follow this pattern:

  • Add a badge logo with 24×24 pixel in png format to the Assets folder of the project
  • Add a wide logo with 310 x 150 pixel in png format to the Assets folder
  • Open Package.appxmanifest and select the badge and wide logo
  • Set Lock screen notification to "Badge" or "Badge and Tile text"

image

A red error logo will remind you, that you must add a background task for this kind of application. This can be done through the Declarations Tab in Package.appxmanifest

  • Select "Baxckground Tasks" from the dropdown and add it to the list.
  • Enter the fully qualified classname into the textbox labeled "Entry point"

image

On Startup we need to ask, whether the app is allowed to run as lockscreen app.

We can do this e.g. in the OnNavigatedTo() method of the MainPage:

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    BackgroundAccessStatus backgroundStatus = 
              await BackgroundExecutionManager.RequestAccessAsync();
    lblStatus.Text = backgroundStatus.ToString();
    base.OnNavigatedTo(e);
}

 

 

The user will get this question on the first startup of this app:

image

 

If the user allows to run the app in the background, then it will be added to one of the seven lockscreen slots, which can be customize by the user through opening "Personalize" tab from the "Change PC settings" app.

The app will the display one of this states from BackGroundAccessStatus enum:

public enum BackgroundAccessStatus
{
    // Summary:
    //     The user has not selected "allow" or "don't allow" or
    //     dismissed the dialog without making a choice.
    //     The app cannot perform background activity
    Unspecified = 0,
    //
    // Summary:
    //     The user chose "allow" in the dialog box. 
    //     The app is added to the lock screen,
    //     can set up background tasks, and, if it has the capability, 
    //     can use the real-time connectivity (RTC) broker. 
    //     This means that the app can function while the
    //     device is in the connected standby state.
    AllowedWithAlwaysOnRealTimeConnectivity = 1,
    //
    // Summary:
    //     The user chose "allow" in the dialog box. 
    //     The app is added to the lock screen
    //     and can set up background tasks, 
    //     but it cannot use the real-time connectivity (RTC) broker. 
    //     This means that the app might not function while the device
    //     is in connected standby.
    //     Note that apps that do not specify RTC in their manifest
    //     will always demonstrate this behavior.
    AllowedMayUseActiveRealTimeConnectivity = 2,
    //
    // Summary:
    //     The user chose "don't allow" in the dialog box. 
    //     The app is not added to the
    //     lock screen.After this value has been returned, 
    //     subsequent calls to the RequestAccessAsync method do not present 
    //     the dialog box to the user. They have stated their
    //     preference and it should be honored.
    Denied = 3,
}

When the app is running, it can update badge data using this code:

private void UpdateBadgeData(string badgeMessage)
{
    XmlDocument xmlDoc = 
      BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);

    // using BadgeTemplateType.BadgeNumber or .BadgeGlyph will return:
    //<badge value=""/>

    var node = xmlDoc.SelectSingleNode("/badge");
    node.Attributes[0].NodeValue = badgeMessage;

    BadgeNotification badgeNotification = new BadgeNotification(xmlDoc);
    BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badgeNotification);
    lblStatus.Text = "Set new badge data to Lockscreen!";
}

 

Updating the tile data must take into account, that the logo can be either small or wide.

If the app is on the lockscreen, then its always using the wide tile template, but on the regular start screen, the user can decicde which size should be used.

So both XML-templates must be contained in the update data. The code looks like this:

private void btnSendTile_Click_1(object sender, RoutedEventArgs e)
{
    //XmlDocument xmlDoc = 
    //   TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideBlockAndText01);

    string template = String.Format("<tile>" + 
    " <visual>" +
    "   <binding template='TileSquareText01'>" +
    "      <text id='1'>{0}</text>" +
    "      <text id='2'>{1}</text>" +
    "   </binding>" +
    "   <binding template='TileWideText01'>" +
              "<text id='1'>{0}</text>" +
              "<text id='2'>{1}</text>" +
    "   </binding>" +
    " </visual>" +
    "</tile>", DateTime.Now.Second.ToString(), txtMessage.Text);
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(template);

    TileNotification tileNotification = new TileNotification(xmlDoc);
    TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
    tileUpdater.Update(tileNotification);
    lblStatus.Text = "Set new Tile logo!";
}

 

 

 .

Categories: Metro, Windows 8

Windows 8 Supporting Snapped view and other views

September 10, 2012 Leave a comment

Windows 8 New UI Style apps are always fullscreen. Except for some special cases, which i’ll discuss here.

If the screen resolution is at least 1366 px in width, then the so called "Snapped View" can be used. To bring a app into snapped view, the user can drag the app from the top to left or right side of the screen, where the app will then snap.

The rest of the screen can be used from another app, which can be either any app from the store or any classic desktop app, even the desktop itself.

This feature is meant for having two applications side by side. Example for this would be the weather app (red), which can be arranged next to the mail app.

 

image

The "Snapped" view is defined as 320px in width and the total height of the screen. It can be either placed on the right or on the left side.

The rest of the screen is then called "Filled" view, which is total width – 320 px

And then there is the default view, called "FullscreenLandscape" view and if the device supports rotation, then there is the last view, called  "FullscreenPortrait"

Windows 8 UI certification guidelines require, that at least "Snapped" view is supported, which does not mean, there must be functionality. It would be good enough to display the app logo, just it should look nice and professional.

But even better is, if the app can provide real value in this view. That’s pretty much depending on the app and its usage scenario, but here i’ll show how to switch between theses views.

The real switching is of course done by the user. The app must then act on the users decission and provide the best possible view of its data.

In this example i’ll just use a very primitive UI, which will show how to handle the different view switches.

For this i’ve used the minimalistic app from here and added 4 StackPanels, each for one of the 4 views:

image

All but the first StackPanel’s are hidden using Visibility="collapsed" attribute.

Each StackPanel does have a unique color and some text, so that it’s easy to see, which one is active. The idea is, that every view does have his own StackPanel and all other Stackpanels are hidden. This change of visibility can be easily done using the VisualStateManager feature, which allows to declaratively define the visibility and lots of other things depending on the view.

We could do this all by hand, but it’s rather annoying and much better to do this in Blend. So let’s open this page in Blend using right click on the MainPage.xaml and select "Open in Blend". This will show the page in Blend like this:

(Make sure, so switch the Window mode to animation using the menu
"Window->Workspaces->Animation"

image

At the left bottom, we can see all possible views.

Now lets define the visibility changes for each of this views:

Click on Filled, then open the Grid next to the right, so that this will be shown:

image

When Filled view is active, then we want to make two changes:
– Hide the StackPanelFullscreen

– Show the StackPanelFilled

For this we first select the StackPanelFullscreen, then go to the top right edge of Blend, type in vi to filter for all properties starting with vi and then change the Visibility to Collapsed:

image

Then we click on StackPanelFilled and set its visibility to visible.

Now we repeat this steps for the view "FullScreenPortrait" and then do the same for the other views.

After this, stop recording by clicking on the top left edge of Blend:

image

 

Now Save and go back to Visual Studio, let it reload the changed files and test the application. Now each view should display a different color and text.

image image imageimage

All the magic in Blend is saved into the <VuslaStateManager.VisualStateGroups> section in XAML, where it’s now easy to understand…?

It defines per state all property changes against the default UI definition above

image

Categories: Metro, Windows 8

Windows 8 ApplicationSettings

September 10, 2012 Leave a comment

Every Windows 8 New UI Style App is sandboxed and by default cannot access the users filesystem.

The only locations where file access is possible, are below the install location of the app itself.

The install location can be fetched using this API:

Windows.ApplicationModel.Package.Current.InstalledLocation.Path

 

But this location is read only!

There are other locations for storing application data, local settings and roaming settings.

Roaming settings will be synced automatically to your Microsoft account and will be available on another machine when you log into this machine using the same account and start using the app there.

Syncing is not in realtime, in can take a few seconds. There is one special key, which can be used for a single setting or a composite setting. The special keyname must be "HighPriority". All settings behind this key will be synced with higher priority. This high priority setting can be a maximum of 4kb.

The maximum total size roaming settings (currently 100 kb) can be fetched using

ApplicationData.RoamingStorageQuota

This locations can be accessed using this API’s:

void DemoSettingsApi()
{
    // Get RoamingStorage Quota
    ulong roamingQuotaInKB = ApplicationData.Current.RoamingStorageQuota;

    // Write local setting
    ApplicationData.Current.LocalSettings.Values["key1"] = txtlocalKey1.Text;
    
    // Read local setting
    if (ApplicationData.Current.LocalSettings.Values["key1"] != null)
    {
        txtlocalKey1.Text = 
            ApplicationData.Current.LocalSettings.Values["key1"].ToString();
    }

    // Write roaming setting
    ApplicationData.Current.RoamingSettings.Values["key1"] = txtlocalKey1.Text;

    // Read roaming setting
    if (ApplicationData.Current.RoamingSettings.Values["key1"] != null)
    {
        txtRoamingKey1.Text = 
            ApplicationData.Current.RoamingSettings.Values["key1"].ToString();
    }

    // Write composite setting
    int zip = 0;
    Int32.TryParse(txtZipCode.Text, out zip);
    Customer c = new Customer() 
    { 
        FirstName = txtCustomerFirstname.Text, 
        LastName = txtCustomerLastname.Text, 
        ZipCode = zip 
    };

    ApplicationDataCompositeValue comp = new ApplicationDataCompositeValue();
    comp["LastName"] = c.LastName;
    comp["FirstName"] = c.FirstName;
    comp["ZipCode"] = c.ZipCode;
    ApplicationData.Current.LocalSettings.Values["CurrentCustomer"] = comp;

    // Read composite setting
    if (ApplicationData.Current.LocalSettings.Values["CurrentCustomer"] != null)
    {
        ApplicationDataCompositeValue comp2 = 
            ApplicationData.Current.LocalSettings.Values["CurrentCustomer"] as ApplicationDataCompositeValue;
        txtCustomerLastname.Text = comp2["LastName"] as string;
        txtCustomerFirstname.Text = comp2["FirstName"] as string;
        int? zipCode = comp2["ZipCode"] as int?;
        txtZipCode.Text = zipCode.ToString();
    }
}

 

 

 .

Categories: Metro, Windows 8

Windows 8 Using WebCam

September 10, 2012 1 comment

The new Windows Runtime object model makes it very easy to access all kind of sensors and devices. In this example i’ll show, how to get images and videos from a connected / builtin webcam.

There are two ways how to get images or videos from the device:

  • CameraCaptureUI

The easiest way is using the builtin CameraCaptureUI object, which displays the system provided dialog always fullscreen and looks like this: image
The disadvantage of this approach is, that your app is not visible during capturing, because the CameraCaptureUI always takes up the whole screen and the user can only use the system provided settings dialogs. This dialog allows the user to crop the taken image or video immediately after recording and then returns the resulting image or video to your app.

  • MediaCapture

    Using the MediaCapture object model the system does not show any UI, but your app must display its own UI for preview and capturing the image or video.
    Although it takes a few more steps, it also allows much more finer control
    For more info on this way, scroll down to "How to use MediaCapture"

I’ll start from the minimalistic template app, which i’ve described in this post.

Sample on how to use the CameraCaptureUI:
Then we’ll add two button in the content area and label the first button "Get Image" and the second one "Get Video". Below we will place a Border control and above this a Image Control.

Should look like like this:

image   image

Fetching images using CameraCaptureUI

Interaction with the Webcam can only be done, if the user allows to do so.

Therefore the developer has to request access to the webcam. This can be done by opening the Package.appxmanifest and check the WebCam capability:

This will prompt the user on the first startup of the app with this question:

image 

image

Now we need a few lines of code to fetch the image from the webcam, save it to a temp folder and then read it into memory to display it in the image control.

The easiest way would be this:

private async void GetImageFromWebCamSimple()
{
    CameraCaptureUI camUI = new CameraCaptureUI();
    StorageFile storageFile = await camUI.CaptureFileAsync(CameraCaptureUIMode.Photo);
    if (storageFile != null)
    {
        IRandomAccessStream stream = await storageFile.OpenReadAsync();
        BitmapImage bitmapImage = new BitmapImage();
        bitmapImage.SetSource(stream);
        MyImageControl.Source = bitmapImage;
    }
}


Fetching the image from the webcam and storing it to disk is just the first 2 lines!!

A real world app would of course wrap the call to CaptureFileAsync() with a try block, because if the user denies the permission to use the webcam, then this would crash the app.

 

Fetching a video using CameraCaptureUI

Almost the same logic is needed to fetch a video, save it to a temporary file and then play it back on a MediaElement control:

private async void GetVideoFromWebCam()
{
    CameraCaptureUI camUI = new CameraCaptureUI();
    StorageFile storageFile = await camUI.CaptureFileAsync(CameraCaptureUIMode.Video);
    if (storageFile != null)
    {
        IRandomAccessStream stream = await storageFile.OpenReadAsync();
        MyMediaElement.SetSource(stream, "image/jpeg");
        MyMediaElement.Play();
    }
}

 

Sample on how to use MediaCapture

 

Also for this sample, i’ll start from the minimalistic template app, which i’ve described in

this post.

As UI elements we need a ImageControl for displaying the captured image and a MediaElement for replaying the captured video stream.

For recording the image or video, a CaptureElement must be added to the XAML code.

To use the webCam, the capabilities WebCam and Microphone must be checked in the Package.AppxManifest.

 

image

 

  1. The first step is to find all the Cameradevices using DeviceInformation.FindAllAsync()
  2. The next step is to create a Storagefile for the image or video
  3. Then a new MediaCapture object must be created
  4. A new MediaCaptureInitializationSettings object must be created and the member VideoDeviceId must be set to one of the Ids of the found cameras.
  5. After this, the InitializeAsync(settings) call on the MediaCapture object must be made and then either one of this functions can be called:
  • CapturePhotoToStorageFileAsync
  • StartRecordToStorageFileAsync

Both will save the image or video into the given StorageFile.

Optionally the StartPreviewAsync() function can be called to get a preview of the current image.

MediaCapture mc = null;
StorageFile videoStorageFile = null;

private void btnStartVideo_Click_1(object sender, RoutedEventArgs e)
{
    StartRecordVideoUsingMediaCapture();
}

private void btnStopVideo_Click_1(object sender, RoutedEventArgs e)
{
    StopRecordVideoUsingMediaCapture();
}

private async void GetImageUsingMediaCapture()
{
    DeviceInformationCollection myDevices = 
        await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);

    if (myDevices.Count < 1)
    {
        await new MessageDialog("No Camera found!").ShowAsync();
        return;
    }
    // Create the image file
    StorageFolder localFolder = ApplicationData.Current.LocalFolder;
    string filename = "Pic_" + DateTime.Now.ToString("yyyy_MM_dd-hh_mm_ss") + ".jpg";
    StorageFile photoStorageFile = 
        await localFolder.CreateFileAsync(filename, 
                        CreationCollisionOption.ReplaceExisting);

    bool camAvailable = true;
    try
    {
        // Initialize MediaCapture
        mc = new MediaCapture();
        MediaCaptureInitializationSettings settings =
                    new MediaCaptureInitializationSettings();

        settings.VideoDeviceId = myDevices.ElementAt(0).Id;
        await mc.InitializeAsync(settings); // Will check for Capabilities here

        //// Define Image quality
        ImageEncodingProperties imageProperties = new ImageEncodingProperties();
        imageProperties.Subtype = "JPEG";
        imageProperties.Width = 640;
        imageProperties.Height = 400;
        
        await mc.CapturePhotoToStorageFileAsync(imageProperties, photoStorageFile);

        DisplayImageFromFileInImageControl(photoStorageFile);
    }
    catch (Exception ex)
    {
        camAvailable = false;
    }

    if (camAvailable == false)
    {
        await new Windows.UI.Popups.MessageDialog("No Webcam allowed?").ShowAsync();
    }

    if (photoStorageFile != null)
    {
        DisplayImageFromFileInImageControl(photoStorageFile);
    }
}

private async void StartRecordVideoUsingMediaCapture()
{
    DeviceInformationCollection myDevices = 
        await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);

    if (myDevices.Count < 1)
    {
        await new MessageDialog("No Camera found!").ShowAsync();
        return;
    }
    // Create the video file
    StorageFolder localFolder = ApplicationData.Current.LocalFolder;
    string filename = "Video_" + DateTime.Now.ToString("yyyy_MM_dd-hh_mm_ss") + ".mp4";
    videoStorageFile = await localFolder.CreateFileAsync(filename, 
                                        CreationCollisionOption.ReplaceExisting);

    bool camAvailable = true;
    try
    {
        btnStartVideo.IsEnabled = false;
        btnStopVideo.IsEnabled = true;

        // Initialize MediaCapture
        mc = new MediaCapture();
        MediaCaptureInitializationSettings settings = 
            new MediaCaptureInitializationSettings();

        settings.VideoDeviceId = myDevices.ElementAt(0).Id; // Use 1. camera
        await mc.InitializeAsync(settings); // Will check for Capabilities here

        // Also do Preview
        previewElement.Source = mc;
        await mc.StartPreviewAsync();

        MediaEncodingProfile recordProfile = 
            Windows.Media.MediaProperties.MediaEncodingProfile.CreateMp4(
                                                        VideoEncodingQuality.Auto);
        await mc.StartRecordToStorageFileAsync(recordProfile, videoStorageFile);
        lblStatus.Text = "Recording...";
    }
    catch (Exception ex)
    {
        camAvailable = false;
    }

    if (camAvailable == false)
    {
        await new Windows.UI.Popups.MessageDialog("No Webcam allowed?").ShowAsync();
    }
}

private async void StopRecordVideoUsingMediaCapture()
{
    btnStartVideo.IsEnabled = true;
    btnStopVideo.IsEnabled = false;

    await mc.StopRecordAsync();

    if (videoStorageFile != null)
    {
        lblStatus.Text = "Replay video...";
        DisplayVideo(videoStorageFile);
    }
}

#endregion Using MediaCapture 

#region Image / Video Display helpers
private async void DisplayImageFromFileInImageControl(StorageFile storageFile)
{
    IRandomAccessStream stream = await storageFile.OpenReadAsync();
    BitmapImage bitmapImage = new BitmapImage();

    bitmapImage.SetSource(stream);
    MyImageControl.Source = bitmapImage;
}

private async void DisplayVideo(StorageFile storageFile)
{
    IRandomAccessStream stream = await storageFile.OpenReadAsync();
    MyMediaElement.SetSource(stream, "image/jpeg");
    MyMediaElement.Play();
}        
#endregion

 .

Categories: Metro, Windows 8

Windows 8 New UI Apps FilePickerTarget

September 9, 2012 Leave a comment

The FilePicker concept is very flexible to provide apps with access to any filesystem location, while the user is still in charge of his data, because the app always requires user interaction before data from the filesystem can be accessed.

But there is more behind this concept. Developer can extend their app to not only use the FilePicker interface for loading and saving data, but also enable their app to act as source for any FilePicker operation.

This is what SkyDrive app by default does.

To see this concept in action, just use any app, e.g. tghe Music app and open a file.

Then open the list of provider from the left top and switch to SkyDrive.
This will then be on screen:
image  ==> image

After selecting a file, the original app can use this file as it would be on the local filesystem.

To enable your app to provide this kind of FilePickerTarget feature you must follow this steps:

Add the "File Open Picker" Declaration in the AppXManifest and either select "Supports any filetype" or add all filetypes you want to provide.

image

Add a new page of type  FileOpen Picker Contract to your project
image

Make sure, that the following function will be added to App.xaml.cs. The Add Page wizard does it for you., when adding the File Open Picker contract.

Inside this code make sure to activate the newly added page:

protected override void OnFileOpenPickerActivated(Windows.ApplicationModel.Activation.FileOpenPickerActivatedEventArgs args)
{
   var fileOpenPickerPage = new FilePickerTargetApp.Views.FileOpenPickerPage();
   fileOpenPickerPage.Activate(args);
}

 

I stripped down the code behind for the newly added FileOpenPickerPage to a minimum here:

The Activate function there looks like this:

public async void Activate(FileOpenPickerActivatedEventArgs args)
{
    this._fileOpenPickerUI = args.FileOpenPickerUI;
    Window.Current.Content = this;
    Window.Current.Activate();

    CreateAndReadDummyData();
}

 

CreateAndReadDummyData will create a few textfiles in the temp storage of the app and then add all this filenames to the left ListBox.

The user should then select one or more files, which will add the names to the right listbox and also add it to the given instance of _fileOpenPicker, where it will be given to the AddFile() method

 

The UI looks ugly, but the minimalistic view should help to understand the concept:

Only the grey part is coming from the FilePicker target app, the surrounding space is from the calling app, which is in this demo scenarion the same app, but usually a different one!

image

 

public sealed partial class MyFileOpenPickerPage : FilePickerTargetApp.Common.LayoutAwarePage
{
    private Windows.Storage.Pickers.Provider.FileOpenPickerUI _fileOpenPickerUI;

    public MyFileOpenPickerPage()
    {
        this.InitializeComponent();
    }

    public async void Activate(FileOpenPickerActivatedEventArgs args)
    {
        this._fileOpenPickerUI = args.FileOpenPickerUI;
        Window.Current.Content = this;
        Window.Current.Activate();

        CreateAndReadDummyData();
    }

    private async void ListFiles_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
    {
        var tempFolder = Windows.Storage.ApplicationData.Current.TemporaryFolder;
        for (int i = 0; i < e.AddedItems.Count; i++)
        {
            string filename = (e.AddedItems[i]) as String;

            // add selected file to FileOpenPicker
            StorageFile sf = await tempFolder.GetFileAsync(filename);
            this._fileOpenPickerUI.AddFile(filename, sf);

            // Update UI
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                SelectedFiles.Items.Add(filename);
            });
        }

    }

    private async void SelectedFiles_Tapped_1(object sender, TappedRoutedEventArgs e)
    {
        string filename = SelectedFiles.SelectedItem.ToString();

        // REMOVE selected file from FileOpenPicker
        this._fileOpenPickerUI.RemoveFile(filename);

        // Update UI
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            SelectedFiles.Items.Remove(filename);
        });
    }
}

 

 

The important detail is to add at least one file of the type, which was specified by the caller.

(See lines 28 and 44)

Otherwise the "Open" button at the bottom never gets active and the user can only cancel the FilePicker operation.

If the user selects a file and then  click "Open", then the selected files will be forwarded to the calling application and there they can be treated like any other regular files.

 

The nice thing is, that the target application can fake all the files, which will be displayed and only fetch the real files, when any file is selected. So the left listbox could be filled with just the filenames from a webservice and the real file could then be fetched and temporarily placed in the temporary storage, when the user has reallly selected the file.

 .

Categories: Metro, Windows 8