Статьи

31 день Windows 8 | День № 7: Поделиться контрактом

Эта статья является 7-м днем ​​в серии под названием «  31 день Windows 8» . Каждая из статей этой серии будет опубликована как для  HTML5 / JS, так  и для  XAML / C # . Вы можете найти дополнительные ресурсы, загрузки и исходный код на нашем  сайте .

advertisementsample

day5-амулеты сторонаЗа последние два урока мы познакомились с этой новой функцией Windows 8: контракты. Мы ввели это понятие контрактов, исследуя, как включить Настройки в наши приложения. Затем мы рассмотрели вопрос о расширении присутствия наших приложений для пользователей с помощью поиска. Сегодня мы собираемся сделать следующий шаг и исследовать одну из особенностей, которая по-настоящему взволновала нас с Кларком, Шаринг.

До Windows 8 встраивать «социальные» в приложения было сложно. Вам нужно было не только изучить API для платформы, на которую нацелено ваше приложение, но вы также должны были изучить API для Facebook, Twitter и многих других социальных сетей, которые вы хотели бы включить.

Это практически подавляет, если не почти невозможно сделать эффективно.

В Windows 8 нам остается только беспокоиться о создании нашего приложения. На самом деле, использование в вашем приложении кнопок с надписью «Поделиться в социальной сети X» на самом деле не рекомендуется. Зачем предоставлять пользователю кнопку Twitter, если он не использует Twitter? То же самое касается Google+, Facebook, Flickr, GitHub или любой другой социальной платформы.

С Договором о разделе, пользователь получает полный контроль. Вы делаете свой контент общедоступным, и ОНИ сами решают, где и как он будет распространяться. Это вдохновляющее чувство, которое действительно является для меня одним из основных моментов Windows 8.

Совместное использование происходит двумя способами:  тот,  кто делится (исходное приложение), и  тот, кто получает (целевое приложение). Это происходит через брокера.

Изображение через:  http://msdn.microsoft.com/en-us/library/windows/apps/hh758314.aspx 

Какие типы файлов вы можете поделиться? Существует 6 различных типов контента, которым вы можете поделиться с брокером:

  • Неформатированный простой текст
  • Ссылка на сайт
  • Форматированный контент / HTML
  • файлы
  • Одиночное изображение
  • Пользовательский формат данных

Данные передаются через объект, называемый  DataPackage . В этой статье мы собираемся разделить Share Source и Share Target на два отдельных раздела.

Мы собираемся начать с более простого из двух сценариев, Share Source. В этом сценарии мы покажем, как вы можете поделиться каждым из 7 типов контента, перечисленных выше. Во-первых, нам нужно создать новый пустой шаблон приложения, поэтому начните с него. В примере кода для этой статьи вы заметите, что я создал отдельную страницу для каждого типа данных. Вам, конечно, не нужно делать это в вашем приложении, но имейте в виду, что вы можете одновременно передавать только один тип данных с любой одной страницы. Начнем с неформатированного текста.

Совместное использование простого неформатированного текста

Это будет самый длинный пример для Share Source, потому что большая часть кода одинакова для каждого примера. Я только собираюсь выделить все это здесь, а затем сосредоточусь на вещах, которые отличаются в последовательных примерах.

Во-первых, нам нужна ссылка на DataTransferManager. Вам также необходимо добавить пространство имен Windows.ApplicationModel.DataTransfer на свою страницу. В приведенном ниже коде вы увидите, что я создаю экземпляр ссылки на DataTransferManager, а затем создаю обработчик событий (в методе OnNavigatedTo ()) для случаев, когда данные запрашиваются для совместного использования с этой страницы с помощью обработчика событий DataRequested () ,

DataTransferManager dtm;
 
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    dtm = DataTransferManager.GetForCurrentView();
    dtm.DataRequested += dtm_DataRequested;
}
 
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
    base.OnNavigatingFrom(e);
    dtm.DataRequested -= dtm_DataRequested;
}

Вы также должны заметить, что мы настроили метод OnNavigatingFrom для удаления события DataRequested, когда мы покидаем страницу. Это лучшая практика, которую вы должны включить в каждую из своих страниц. Обязательно отмените все обработчики событий, которые вы зарегистрировали.

Наконец, нам нужно на самом деле поделиться некоторым текстом, и мы делаем это с помощью нашего метода dtm_DataRequested ():

void dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    string textSource = "Testing the Share Source functionality in Windows 8. #31daysofwin8";
    string textTitle = "31 Days of Windows 8!";
    string textDescription = "This just explains what we're sharing.";  //This is an optional value.
 
    DataPackage data = args.Request.Data;
    data.Properties.Title = textTitle;
    data.Properties.Description = textDescription;
    data.SetText(textSource);
}

Как видите, мы просто создаем объект DataPackage и соответствующим образом устанавливаем его свойства. Используя значения, которые я указал в коде, вот подсказка, которую увидит пользователь (нажмите, чтобы увеличить):

7-XAML-UnformattedTextPrompt

Как только они выберут приложение (в данном случае приложение Почта), они увидят, что данные заполнят выбранное приложение (нажмите, чтобы увеличить):

7-XAML-UnformattedTextResult

Для остальных типов данных я покажу вам содержимое метода dtm_DataRequested (), но все остальное в основном остается прежним.

Обмен ссылками

Единственная разница между ссылкой и неформатированным текстом заключается в том, что вы должны указать действительный объект Uri для DataPackage. Вот код:

void dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    Uri linkSource = new Uri("http://31daysofwindows8.com");
    string linkTitle = "31 Days of Windows 8!";
    string linkDescription = "This just explains what we're sharing.";  //This is an optional value.
 
    DataPackage data = args.Request.Data;
    data.Properties.Title = linkTitle;
    data.Properties.Description = linkDescription;
    data.SetUri(linkSource);
}

Где реальные различия отображаются в пользовательском интерфейсе. Вот начальная подсказка для обмена:

7-XAML-LinkPrompt

Вы заметите, что они продвинули приложение «Люди» над строкой и предложили опубликовать его в Twitter. Если я на самом деле открою приложение People, у меня все равно будет возможность выбрать Facebook, поэтому мне не совсем понятно, как они выбирают Twitter вместо Facebook здесь. Вот что почтовое приложение делает с этими данными при открытии:

7-XAML-LinkResult

Что здорово в этой реализации, так это несколько функций:

  • Почтовое приложение фактически выскочило в Интернет и взяло пару изображений, которые, по его мнению, были бы полезными миниатюрами. Мне придется поговорить с Кларком о том, почему его изображение по умолчанию. :)
  • Почтовое приложение также вышло и захватило фактический <title> с веб-сайта и использовало это в листинге. Очень полезно.
  • В неформатированном текстовом примере свойство «Описание» не использовалось приложением «Почта». В данном случае он был встроен по ссылке.

Давайте теперь посмотрим на совместное использование форматированного контента как HTML.

Обмен отформатированным контентом / HTML

Вот посмотрите на нашу реализацию кода:

void dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    string HTMLSource = "<strong>This is bold text,</strong> and this is not.  <a href='http://31daysofwindows8.com'>Check out the 31 Days of Windows 8!</a>";
    string HTMLTitle = "31 Days of Windows 8!";
    string HTMLDescription = "This just explains what we're sharing.";  //This is an optional value.
 
    DataPackage data = args.Request.Data;
    data.Properties.Title = HTMLTitle;
    data.Properties.Description = HTMLDescription;
    data.SetHtmlFormat(HtmlFormatHelper.CreateHtmlFormat(HTMLSource));
}

Нам пришлось относиться к этому контенту немного по-другому. Вы заметите, что когда мы на этот раз установили наш контент для нашего DataPackage, нам пришлось вызвать HtmlFormatHelper. Это берет нашу обычную строку символов и преобразует ее в активную рабочую разметку HTML, которую можно анализировать и использовать. Первоначальное приглашение к обмену выглядит точно так же, как и раньше, но вы можете видеть, что наш отформатированный HTML-код отображается в теле нашего сообщения электронной почты.

7-XAML-HTMLPrompt

7-XAML-HTMLResult

Отличное применение этого было бы для создания сообщений электронной почты в формате HTML. Создание таких сообщений чрезвычайно сложно для тех людей, которые еще не знают HTML. Представьте себе простое приложение, которое позволяет им визуально создавать свой HTML-интерфейс, а затем они могут поделиться им со своим почтовым клиентом и отправить его. Кто-то это построит.

Обмен файлами

В этой ситуации один или несколько файлов обрабатываются одинаково, но поскольку мы читаем файлы из хранилища, необходимо также написать немного дополнительного кода. Если у вас есть дополнительные файлы, вы просто добавите дополнительную логику для асинхронного захвата дополнительных файлов и добавите их в коллекцию List <IStorageItem>. Мы будем асинхронно захватывать файл (ы), и когда операция будет завершена, мы поделимся ею.

async void dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    string FileTitle = "31 Days of Windows 8!";
    string FileDescription = "This just explains what we're sharing.";  //This is an optional value.
 
    DataPackage data = args.Request.Data;
    data.Properties.Title = FileTitle;
    data.Properties.Description = FileDescription;
 
    DataRequestDeferral waiter = args.Request.GetDeferral();
 
    try
    {
        StorageFile textFile = await Package.Current.InstalledLocation.GetFileAsync("FileForSharing.txt");
        List<IStorageItem> files = new List<IStorageItem>();
        files.Add(textFile);
        data.SetStorageItems(files);
    }
    finally
    {
        waiter.Complete();
    }
}

Note that the entire method needed to be decorated with the “async” keyword in order to make this happen.  You will see this over and over in Windows 8 development as the way to handle asynchronous events.  It just makes life so much easier.  If it’s not entirely clear, I created a simple .txt file and added it to my project.  This is the file that I grab and share.  Again, the sharing prompt looks identical to every other instance we’ve had so far, but here’s what it looks like opened in the Mail app:

7-XAML-FileResult

The interesting thing to note in this situation is that previously the Mail app used our Title property as the subject of the email message.  In this example, it doesn’t.  I’m sure that the product team has a list of reasons why this is, but I’d vote for consistency on this one.

Sharing Images

When sharing an image file, you might want to treat it differently than just a basic file, but you also might not.  In this example, we’re actually going to share our image file two different ways, so that the Target apps can use the data that they are interested in.  Apps that are looking for files will be available, as will apps that are just specifically looking for Images.  You can use this handy trick when you’ve got multiple types of data to share as well, not just when the same thing could be shared two different ways.

async void dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    string FileTitle = "31 Days of Windows 8!";
    string FileDescription = "This just explains what we're sharing.";  //This is an optional value.
 
    DataPackage data = args.Request.Data;
    data.Properties.Title = FileTitle;
    data.Properties.Description = FileDescription;
 
    DataRequestDeferral waiter = args.Request.GetDeferral();
 
    try
    {
        StorageFile image = await Package.Current.InstalledLocation.GetFileAsync("Assets\\0.png");
        data.Properties.Thumbnail = RandomAccessStreamReference.CreateFromFile(image);
        data.SetBitmap(RandomAccessStreamReference.CreateFromFile(image));
 
        List<IStorageItem> files = new List<IStorageItem>();
        files.Add(image);
        data.SetStorageItems(files);
    }
    finally
    {
        waiter.Complete();
    }
}

You can see that we’ve called both the SetBitmap method, as well as the SetStorageItems method.  In either case, the Share prompt looks identical, but the list of apps available are definitely different in each case.  For example, the Mail app doesn’t accept raw images, but it does accept images as StorageItems.  Give this sample code a try.  I was also surprised to see that the People app doesn’t accept either type of sharing.  We’ll talk later in this article about being a Share Target, and how we can accept different kinds of data.

Sharing a Custom Data Format

There are going to be many times where you want to share some kind of custom data.  99% of the time, it’s going to fit a standard schema, like those found on http://schema.org.  You’re certainly not locked into a pre-determined schema, but make sure you understand theguidance Microsoft offers for creating your own custom data formats.

The short story on sharing custom data is that you’re going to specify a DataPackageFormat, but it can be absolutely anything you want.  For most purposes, you’re only likely to share custom data formats between two of your own apps, but standardizing on a standard schema certainly increases your odds of being able to share with other apps as well.  Ultimately, you will specify a string that represents your data schema, and if any other app is a Target for that same specific string of characters, they will show up in the list of potential Targets.

void dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    string customData = @"{
        ""type"" : ""http://schema.org/Person"",
        ""properties"" :
        {
        ""image"" : ""http://jeffblankenburg.com/images/200x200.png"",
        ""name"" : ""Jeff Blankenburg"",
        ""affiliation"" : ""Microsoft"",
        ""birthDate"" : ""07/22/1976"",
        ""jobTitle"" : ""Senior Technical Evangelist"",
        ""nationality"" : ""United States of America"",
        ""gender"" : ""Male""
        }
    }";
    string linkTitle = "31 Days of Windows 8!";
    string linkDescription = "This just explains what we're sharing.";
 
    DataPackage data = args.Request.Data;
    data.Properties.Title = linkTitle;
    data.Properties.Description = linkDescription;
    data.SetData("http://schema.org/Person", customData);
}

In my example above, I am using the schema from http://schema.org/Person, and since none of the other apps on my machine recognize that schema, I get a Share prompt that looks like this:

7-XAML-CustomDataPrompt

As you’ve seen throughout this section, our Title and Description continue to show up in the same place, but this time, there aren’t any apps available to share with.  In the next section, we’re going to look at how we make our app capable of receiving these data types.

When I first sat down to write this article, I made Share Target last because I thought that was going the be the more difficult (and lengthy) of the two.  As it turns out, I’m wrong.  Much of the work for being a Share Target falls in your Package.appmanifest file.

Open yours to the Declarations section, and you’re likely to find a barren wasteland.  What we need to do is add the Share Target declaration, by choosing it from the dropdown and clicking the Add button.

7-XAML-AppManifest

Once you add the Share Target, it’s going to light up with a bunch of red X symbols.

7-XAML-AppManifestBroken

Adding a Share Target declaration isn’t enough.  You also need to specify what type of Target your app is going to be.  Thankfully, we can choose any/all of the formats we’ve already covered in this article.  The next screenshot shows my Share Target declarations completely filled out, making my app a Target for nearly everything.

7-XAML-AppManifestComplete

Now, as we get into the code, there’s WAY more going on here than you’ll probably need for your application.  I’ve built a sample application that shares and consumes every possible data type, and as such, it’s got many more lines of code than you will.

First, we need to open our App.xaml.cs file and add a new method to handle when the Share Target is activated.  It is called, not surprisingly, OnShareTargetActivated.

protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
    var rootFrame = new Frame();
    rootFrame.Navigate(typeof(MainPage), args.ShareOperation);
    Window.Current.Content = rootFrame;
    Window.Current.Activate();
}

All this really indicates to the app is which page you want to have loaded when your application is targeted for sharing.  This will likely be a separate page for you (not MainPage), but for my example, that’s exactly what I’ve used.

Now for the big chunk of logic. I’ve added comments so that you can find the pieces you’re specifically interested in.  Here’s the entire OnNavigatedTo method in my MainPage.xaml file.  Remember, this logic will likely go in a seperate “SearchTarget” page in your application that has the sole responsibility of catching shared data.

ShareOperation share;
string title;
string description;
string text;
string customData;
string customDataFormat = "http://schema.org/People/";
string formattedText;
Uri uri;
IReadOnlyList<IStorageItem> storageItems;
IRandomAccessStreamReference bitmapImage;
IRandomAccessStreamReference thumbImage;
        
 
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    share = e.Parameter as ShareOperation;
 
    await Task.Factory.StartNew(async () =>
    {
        title = share.Data.Properties.Title;
        description = share.Data.Properties.Description;
        thumbImage = share.Data.Properties.Thumbnail;
 
        //IF THERE WAS FORMATTED TEXT SHARED
        if (share.Data.Contains(StandardDataFormats.Html))
        {
            formattedText = await share.Data.GetHtmlFormatAsync();
        }
        //IF THERE WAS A URI SHARED
        if (share.Data.Contains(StandardDataFormats.Uri))
        {
            uri = await share.Data.GetUriAsync();
        }
        //IF THERE WAS UNFORMATTED TEXT SHARED
        if (share.Data.Contains(StandardDataFormats.Text))
        {
            text = await share.Data.GetTextAsync();
        }
        //IF THERE WERE FILES SHARED
        if (share.Data.Contains(StandardDataFormats.StorageItems))
        {
            storageItems = await share.Data.GetStorageItemsAsync();
        }
        //IF THERE WAS CUSTOM DATA SHARED
        if (share.Data.Contains(customDataFormat))
        {
            customData = await share.Data.GetTextAsync(customDataFormat);
        }
        //IF THERE WERE IMAGES SHARED.
        if (share.Data.Contains(StandardDataFormats.Bitmap))
        {
            bitmapImage = await share.Data.GetBitmapAsync();
        }
 
        //MOVING BACK TO THE UI THREAD, THIS IS WHERE WE POPULATE OUR INTERFACE.
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
            {
                TitleBox.Text = title;
                DescriptionBox.Text = description;
 
                if (text != null)
                    UnformattedTextBox.Text = text;
                if (uri != null)
                {
                    UriButton.Content = uri.ToString();
                    UriButton.NavigateUri = uri;
                }
 
                if (formattedText != null)
                    HTMLTextBox.NavigateToString(HtmlFormatHelper.GetStaticFragment(formattedText));
 
                if (bitmapImage != null)
                {
                    IRandomAccessStreamWithContentType bitmapStream = await this.bitmapImage.OpenReadAsync();
                    BitmapImage bi = new BitmapImage();
                    bi.SetSource(bitmapStream);
                    WholeImage.Source = bi;
 
                    bitmapStream = await this.thumbImage.OpenReadAsync();
                    bi = new BitmapImage();
                    bi.SetSource(bitmapStream);
                    ThumbImage.Source = bi;
                }
 
                if (customData != null)
                {
                    StringBuilder receivedStrings = new StringBuilder();
                    JsonObject customObject = JsonObject.Parse(customData);
                    if (customObject.ContainsKey("type"))
                    {
                        if (customObject["type"].GetString() == "http://schema.org/Person")
                        {
                            receivedStrings.AppendLine("Type: " + customObject["type"].Stringify());
                            JsonObject properties = customObject["properties"].GetObject();
                            receivedStrings.AppendLine("Image: " + properties["image"].Stringify());
                            receivedStrings.AppendLine("Name: " + properties["name"].Stringify());
                            receivedStrings.AppendLine("Affiliation: " + properties["affiliation"].Stringify());
                            receivedStrings.AppendLine("Birth Date: " + properties["birthDate"].Stringify());
                            receivedStrings.AppendLine("Job Title: " + properties["jobTitle"].Stringify());
                            receivedStrings.AppendLine("Nationality: " + properties["Nationality"].Stringify());
                            receivedStrings.AppendLine("Gender: " + properties["gender"].Stringify());
                        }
                        CustomDataBox.Text = receivedStrings.ToString();
                    }
                }
            });
        });
 
}

As you can see, each example is slightly different, depending on the type of data.

Today, we took took a very extensive look into adding Sharing into your application. The Search contract provides an amazing new paradigm for applications to interact with one another.  There’s something incredibly refreshing about knowing that applications that have no idea mine exists can still communicate with one another.  I hope you’ll take the opportunity to share and consume the data that makes sense for your application.

You can download the entire sample solution from this article here:

downloadXAML

Tomorrow, we’re going to focus on storing data, both locally on the user’s device, as well as roaming that data in the cloud.  See you then!

downloadTheTools