piątek, 6 kwietnia 2012

Notatki o Windows 8 Consumer Preview - odc. 8

W odcinku 8 pierwsza część zagadnień związanych z danymi i plikami.

W przypadku JS dostajemy więcej informacji na temat bindingu z praktycznymi przykładami jak go realizować na złożonych obiektach, kolekcjach. Element stylu także może być zbindowany.

W przypadku ustawień aplikacji oprócz wartości prostych mogą też być obiekty złożone zapisywane w sposób atomowy oraz kontenery będące słownikami klucz-wartość. Pliki mogą być zapisywane w postaci tekstowej i binarnej.

Dostajemy również odświeżone informacje na temat obsługi kontraktów typu FilePicker. Oprócz kontraktów odczytu i zapisu, moją uwagę zwrócił trzeci kontrakt Cached File Updater - służący do synchronizacji lokalnych kopii plików z ich źródłem!

Kolejnymi rzeczami, które zwróciły moją uwagę, są listy StorageItemMostRecentlyUsedList  i StorageItemAccessList. Dzięki pierwszej z nich możemy zapamiętywać do 25 ostatnio używanych przez aplikację plików i folderów. Jeśli aplikacja nie ma uprawnień do odczytu danej lokalizacji, dostęp do pliku można osiągnąć za pomocą FilePicker’a. Co jednak jeśli chcemy później skorzystać z takiego pliku w aplikacji bez ponownego otwierania FilePicker’a. Przychodzi nam wtedy z pomocą druga lista StorageItemAccessList, w której możemy zapamiętać do 1000 takich plików lub folderów!

Dowiadujemy się jednego szczegółu o serwisie do przesyłania plików w tle. Został zaprojektowany z myślą o dużych plikach multimedialnych – zdjęciach, nagraniach, filmach. W przypadku bardzo małych danych lepiej użyć API do stosu HTTP.

JS

Working with data and files

<script src="text/javascript"> var person = {name: "Fran"};</script>

<div id="basicBinding"> Welcome, <span id="nameSpan" data-win-bind="innerText: name"></span></div>

<script type="text/javascript"> var person = { name: "Fran", birthday: "02/11/1999" }; var personDiv = document.getElementById("nameSpan"); WinJS.Binding.processAll(personDiv, person);</script>

Right now the binding is one-time only, which means that the text won't change when the data changes. By itself, a JavaScript object can't notify the application when it changes, but you can convert the object to a binding context by using WinJS.Binding.as.

var bindingSource = WinJS.Binding.as(person);

Binding a style

var person = { name: "Fran", color: "red" };

<div id="basicBinding"> Welcome, <span id="nameSpan" data-win-bind="innerHTML: name; style.background: color"></span></div>

Complex object

You can use the WinJS.Class.define method.

<script type="text/javascript"> var Person = WinJS.Class.define( function() { this.name = "Harry"; this.color = "blue"; this.timeout; }, { _nameArray: new Array("Sally", "Jack", "Hal", "Heather", "Fred", "Paula", "Rick", "Megan", "Ann", "Sam"), _colorArray: new Array("lime", "lavender", "yellow", "orange", "pink", "greenyellow", "white", "lightblue", "lightgreen", "lightyellow"), _newName: function () { this.name = this._nameArray[this._randomizeValue()]); this.color = this._colorArray[this._randomizeValue()]); }, _randomizeValue: function () { var value = Math.floor((Math.random() * 1000) % 8); if (value < 0) value = 0; else if (value > 9) value = 9; return value; } }); </script>

Right now the Person object is not observable; that is, it does not provide notifications when it changes. We can make it observable by mixing the Person object with the correct binding functionality. The WinJS.Class.mix function adds to the Person object the functionality of the WinJS.Binding.mixin object, which includes a bind method, and the functionality of the WinJS.Binding.expandProperties function, which prepares the fields of the object for binding

WinJS.Class.mix(Person, WinJS.Binding.mixin, WinJS.Binding.expandProperties(Person));

The bind method takes two parameters: the name of the property or field that is to be bound, and a function that specifies how it is to be bound. This function has two parameters: the new value and the old value. Because bind is an instance method, for now we'll just instantiate a Person and call bind on its name and color fields.

myPerson = new Person();myPerson.bind("name", function onNameChange (newValue) { var span = document.getElementById("bindableSpan"); span.innerText = newValue; });

Now we'll get more control over the lifetime of the Person object by instantiating and binding it in an activation handler and unbinding and destroying it in an unload handler.

You add the activated and unload event handlers as part of the WinJS.Utilities.ready function. This function is called immediately after the DOMContentLoaded event.

WinJS.Utilities.ready(function () { WinJS.Application.addEventListener("activated", activatedHandler); WinJS.Application.addEventListener("unload", unloadHandler); }, false);

function activatedHandler(e) { myPerson = new Person(); myPerson.bind("name", function onNameChange (newValue) { var span = document.getElementById("bindableSpan"); span.innerText = newValue; }); myPerson.bind("color", function onColorChange (newValue) { var span = document.getElementById("bindableSpan"); span.style.color = newValue; }); myPerson.start(); }

function unloadHandler(e) { myPerson.stop(); myPerson.unbind("name", onNameChange); myPerson.unbind("color", onColorChange); myPerson = null;}

Templates to bind data

You can define a template declaratively as a Windows Library for JavaScript control and specify its internal structure and style. Even though they are declared as part of DIV elements, templates are not processed as part of the DOM and are not returned as part of DOM search results. You can specify the DIV element in which the template should appear or allow the render method to create its own DIV element.

<script type="text/javascript"> var Person = WinJS.Binding.define({ name: "", color: "", birthday: "", petname: "", dessert: "" });</script>

<div id="templateDiv" data-win-control="WinJS.Binding.Template"> <div class="templateItem" data-win-bind="style.background: color"> <ol> <li><span>Name :</span><span data-win-bind="textContent: name"></span></li> <li><span>Birthday:</span><span data-win-bind="textContent: birthday"></span></li> <li><span>Pet's name: </span><span data-win-bind="textContent: petname"></span></li> <li><span>Dessert: </span><span data-win-bind="textContent: dessert"></span></li> </ol> </div></div>

<div id="templateControlRenderTarget"></div>

<fieldset id="templateControlObject"> <legend>Pick a name:</legend> <select id="templateControlObjectSelector"> <option value="0">Show one</option> <option value="1">Show two</option> <option value="2">Show three</option> </select></fieldset>

var people = [ new Person({name:"Bob", color:"red", birthday:"2/2/2002", petname:"Spot", dessert:"chocolate cake"}), new Person({name:"Sally", color:"green", birthday:"3/3/2003", petname:"Xena", dessert:"cherry pie"}), new Person({name:"Fred", color:"blue", birthday:"2/2/2002", petname:"Pablo", dessert:"ice cream"}), ];

WinJS.Utilities.ready(function () { var selector = document.getElementById("templateControlObjectSelector"); selector.addEventListener("change", handleChange, false); }, false);

In the change event handler, find the DIV element that contains the template and the DIV element that specifies where to display the data, and then call WinJS.UI.process on the template DIV element. Because the WinJS.UI.process function returns a Promise, you can use the then method of the Promise to render the data. In this case, the then method takes a function whose parameter is the name of template control. When you call render on the template, the relevant fields of the data object are bound to the template's list items.

function handleChange(evt) { var templateElement = document.getElementById("templateDiv"); var renderHere = document.getElementById("templateControlRenderTarget"); renderHere.innerHTML = ""; var selected = evt.target.selectedIndex; WinJS.UI.process(templateElement).then(function (templateControl) { while (selected >= 0) { templateControl.render(people[selected--], renderHere); } });}

Managing application data

An ApplicationDataCompositeValue object contains settings that must be accessed atomically. This example creates a composite setting named exampleCompositeSetting and adds it to the localSettings container.

var composite = new Windows.Storage.ApplicationDataCompositeValue();composite["intVal"] = 1;composite["strVal"] = "string";localSettings.values["exampleCompositeSetting"] = composite;

Call the ApplicationDataContainer.CreateContainer method to create a settings container. This example creates a settings container named exampleContainer and adds a setting named exampleSetting. The Always value from the ApplicationDataCreateDisposition enumeration indicates that the container should be created if it does not already exist.

var container = localSettings.createContainer("exampleContainer", Windows.Storage.ApplicationDataCreateDisposition.Always);if (localSettings.containers.hasKey("exampleContainer")){ localSettings.containers.lookup("exampleContainer").values["exampleSetting"] = "Hello Windows";}

Write data to a file

Use the file APIs, such as Windows.Storage.StorageFolder.createFileAsync and Windows.Storage.FileIO.writeTextAsync, to create and update a file in the local app data store.

function writeTimestamp() { localFolder.createFileAsync("dataFile.txt", Windows.Storage.CreationCollisionOption.replaceExisting) .then(function (sampleFile) { var formatter = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("longtime"); var timestamp = formatter.format(new Date()); return Windows.Storage.FileIO.writeTextAsync(sampleFile, timestamp); }).done(function () { });}

Accessing data and files

Reading and writing a file

var buffer = Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary( 'What fools these mortals be', Windows.Security.Cryptography.BinaryStringEncoding['']);

Windows.Storage.FileIO.writeBufferAsync(sampleFile, buffer).then(function () { // Add code to do something after the text is written to the file});

Accessing files with file pickers

A daigram that shows the process of one app getting a file to open from another app using the file picker as an interface bewteen the two apps.

When your app gets files or a folder from a file picker, you should add the items to your futureAccessList (in the Windows.Storage.AccessCache namespace) so that your app can easily access the items again later.

Providing services using file pickers

Your app can use file pickers as an interface to provide three services to other apps: access to files, a save location, and/or updates for particular files. To provide one (or more) of these services, you must participate in the associated app contract, which includes declaring the contract in your app manifest, responding to the corresponding activated event, and, in some cases, creating a page that will be loaded in the center area of a file picker window.

Providing a save location

Consider using the file picker to provide your app as a location where the user can save files if your app connects the user to a service that hosts their files or if you expect the user to want to save a number of files in order to open and work with them in your app later. You can let users save files to your app by participating in the File Save Picker contract. When the user selects your app, the file picker a page that you will design to display your app's save location to the user (include existing files and/or subfolders if they exist).

If your app acts as a save location, you should also provide access to your app's files by participating in the File Open Picker contract.

Providing real-time updates for files

Consider providing file updates if your app is used to connect to and/or manage a central repository of the user's files. Participating in the Cached File Updater contract lets your app both perform updates on files in your app's repository and provide updates to local versions of the files in your repository. If your app participates in this contract and encounters an update that requires user intervention, the file picker will load a page that you will design to gather the needed information from the user.

A remote storage app like SkyDrive or DropBox might provide updates to local versions of a file to ensure that the user sees the latest version of the file in its repository, or might perform updates on a file in the repository if the user saves a newer version.

Tip  If your app provides file updates, it should also provide a save location, and access to files by participating in the File Save Picker contract and the File Open Picker contract, respectively.

Locations that your app has access to can include (but are not limited to):

Locations that your app has a capability for

You can get a storageFolder that represents one of these locations by using the property on the knownFolders class that corresponds to the capability. Learn more about capabilities in Access to user resources using the Windows Runtime.

Locations that contain your app's data

You can get a storageFolder that represents an app data folder by using a corresponding property on the applicationData class.

Locations in your app's most recently used (MRU) and future access lists

You can get a storageFolder that represents a location in the MRU or future access list by using methods of the StorageItemMostRecentlyUsedList or StorageItemAccessList class, respectively.

MRU list can store up to 25 items. While the app must add items to the MRU in order to track them, Windows maintains the 25-item limit by removing stale items if necessary.

var openPicker = new Windows.Storage.Pickers.FileOpenPicker(); openPicker.pickSingleFileAsync().then(function (pickedFile) { if (pickedFile) { // Store the file to access again later var listToken = Windows.Storage.AccessCache.StorageApplicationPermissions.mostRecentlyUsedList.add(pickedFile); // Add code to process the file that the user picked } else { // No file }});

Future access list represents a list that an app maintains so that the app can store files and/or locations (like folders) and easily access these items in the future.

Use this future-access list to preserve access to files and locations that may not be included with the accessible locations specified by the capabilities in your app manifest. For example, if your app uses a file picker to access a file (or location), we recommend that you store the StorageFile that is returned from the file picker in this future-access list.

This list can store up to 1000 items and must be maintained by the app.

var openPicker = new Windows.Storage.Pickers.FileOpenPicker(); openPicker.pickSingleFileAsync().then(function (pickedFile) { if (pickedFile) { // Store the file to access again later var listToken = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.add(pickedFile); // Add code to process the file that the user picked } else { // No file }});

Providing services from a file picker

Decide which services your app will provide from file picker windows

Your app can provide the following services to other apps by participating in the associated app contract. To participate in a contract, you must declare the contract in your app manifest, respond to the corresponding activated event, and create a specialized app view.

Provide access to files

Make files accessible from a file picker if your app has a unique and/or valuable view of those files or if users cannot easily access the files another way

App contract: File Open Picker contract

Activated event information: fileOpenPickerActivatedEventArgs

Provide a save location

Provide a save location through a file picker if you expect your users to want to save files in order to open and work with them in your app later.

App contract: File Save Picker contract

Activated event information: fileSavePickerActivatedEventArgs

Provide real-time updates for files

Track and update files through a file picker if you expect users to use your app as a central repository of files that your app provides access to, or that are saved using your app as the save location.

App contract: Cached File Updater contract

Activated event information: cachedFileUpdaterActivatedEventArgs

Add an app contract by bringing up the Add a New Item window for your project, selecting the contract from the center pane, and clicking the Add button.

You can bring up the Add a New Item window in Visual Studio 11 Express Beta for Windows 8 in several ways:

  • By selecting the Project > Add New Item... menu option
  • By selecting the Add > New Item... menu option from the project's context menu
  • By using the Crtl+Shift+A keyboard shortcut

Tip  When you add these contracts, Visual Studio 11 Express Beta for Windows 8 automatically updates your "package.appmanifest" manifest file and adds files to your project that you will use to define the layout and behavior of the view that will be hosted in the file picker window when your app's service is called.

Don't use a file picker to explore, consume, or manage file content.

Don't use a file picker to save a file if a unique, user-specified file name or location is not needed.

Whether picking or saving files and folders, customize the file picker by setting the commit button text appropriately for the user's current task. For example, if the user is wants to pick a file to open in your app, set the commit button text to "Open". Similarly, if the user wants to update a file from your app, set the commit button text to "Upload".

All file locations that are accessible to your app should be accessible from your file picker page.

For webcam, photography, and camera apps, design the UI of your file picker page around taking pictures.

Make sure users can get back into the app they were using (the calling app or caller) by simplifying your app's UI for your file picker page. Limit the controls you provide on your file picker page to controls that let the user take a picture, and let the user apply a few pre-processing effects (like toggling the flash and zooming).

All available controls must be visible on your file picker page because your app bar is not accessible to the user from the file picker. We recommend that you organize these controls on your file picker page similarly to the way they are organized in your app bar, and position them on your file picker page as close as possible (at the top/bottom of the page) to where they appear in your app bar.

Additional UX guidelines: providing file updates
  • Provide a repository that can track and update files for users.

    If users use your app as a primary storage location where they regularly save and access files, you may want your app to track some files to provide real-time updates for users.

  • Design your app and file picker page to present a robust repository.

    If users use your app as a primary storage location for their files, design your app and your associated file picker view to protect against data loss, which could be caused by frequent file updates or conflicting file versions.

  • Let users resolve issues encountered during updates.

    To help ensure a successful update, your app should to notify users in real time (using UIRequested) when a file is being updated or saved and user intervention is needed to resolve an issue effectively. It is especially important that your app help users resolve issues with credentials, file version conflicts, and disk capacity.

    If the problem cannot be solved in real time by the user, or if you simply need let the user know what happened (perhaps an error occurred that the user can't resolve), we recommend that you notify the user of the problem the next time your app is launched instead of immediately when the problem occurs.

  • Provide additional information about update and save operations from your normal app pages.

    Your main app UI should let users manage settings for in-progress and future operations, get information about in-progress and previous operations, and get information about any errors that have occurred.

Working with data sources

The Windows Library for JavaScript provides several types of IListDataSource objects:

You can also create your own custom data source that connects to some other type of data provider, such as a web service or database. For instructions, see How to create a custom data source.

Transferring a file from a network resource

Use the Background Transfer feature provided in the Windows Runtime to enhance your app with advanced download/upload features that allow for transfers to be recovered, resumed, and managed in the background. Additionally, this feature automatically leverages network-cost information provided during transfer operations. This feature supports file transfers using the HTTP, HTTPS, and FTP protocols.

Note  Background Transfer is primarily designed for the transfer of resources like video, music, and large images. For smaller content transfers (i.e. a couple KB), the use of the HTTP stack (XHR in JavaScript, HttpClient in .NET, and IXHR2 in C++) is recommended.

W przypadku C# i XAML mamy doprecyzowane kilka szczegółów odnośnie data bindingu - co może być jego źródłem, co odbiorcą (podobnie jak w Silverlight 3 i WP7 mamy FrameworkElement), jak notyfikować zmianę całego obiektu (jako nazwę właściwości podajemy string.Empty, nie można użyć null jak w Silverlight i WPF) czy indeksu we właściwości, różnice między C#/VB a C++.

Mamy pokazane jak sobie zorganizować wskazywanie bieżącego elementu i grupowanie (znana kolekcja CollectionViewSource). Podobnie jak w każdej wersji Silverlight o błędach w bindingach można dowiedzieć się podczas debugowania, są wtedy logowane. W przypadku Windows 8 trzeba dodatkowo włączyć w Visual Studio opcję debugowania kodu niezarządzanego.

C# & XAML

Data binding with XAML 

data binding basics

The source can be:

  • Any common language runtime (CLR) object, including the target element itself or other UI elements. If the target is in a data template, the source can be the UI element to which the template is applied. Classes that you define in C# and Visual Basic produce CLR objects, so they are bindable by default.
  • Any Windows Runtime object of a type that has a BindableAttribute or implements ICustomPropertyProvider. Classes that you define in C++ produce Windows Runtime objects, so they require one of these approaches to be bindable.

The target can be any DependencyProperty of a FrameworkElement.

You can raise the PropertyChanged event to indicate that all properties on the object have changed by using String.Empty for the PropertyName property of the PropertyChangedEventArgs. Note that you cannot use null (Nothing in Visual Basic) for this like you can in Windows Presentation Foundation (WPF) and Microsoft Silverlight.

You can also use change notification with indexer properties. You can raise the PropertyChanged event to indicate that indexer properties on the object have changed by using a PropertyName value of "Item[indexer]" for specific indexers (where indexer is the index value) or "Item[]" for all indexers. Note that C++ does not currently support binding to indexers. For a workaround, see the Data Binding sample.

Get property change updates from a bound object.

In C++ code, you can populate and bind to Vector<T> instances for both changing and unchanging collections.

Implement a collection that supports binding

  • C#:   Extend IList(Of T) or implement IList, IList<object>, IEnumerable, or IEnumerable<object>. Binding to generic IList<T> and IEnumerable<T> is not supported.
  • C++:   Implement IBindableVector, IBindableIterable, IVector<Object^>, IIterable<Object^>, IVector<IInspectable*>, or IIterable<IInspectable*>. Binding to generic IVector<T> and IIterable<T> is not supported.

Implement a collection that supports collection change updates.

Grouping data and tracking the current item

If the items in the collection are collections themselves, or are objects that contain collections, you can display the collections as groups within the larger collection. To do this, set the IsSourceGrouped property to true. If the items contain collections but are not collections themselves, you must also set the ItemsPath property to the name of the collection property. You can access the groups programmatically through the CollectionGroups property.

<CollectionViewSource x:Name="groupInfoCVS" IsSourceGrouped="true"/>

Teams teams = new Teams();var result = from t in teams group t by t.City into g orderby g.Key select g;groupInfoCVS.Source = result;

The converter also has optional parameters: ConverterLanguage, which allows specifying the language to be used in the conversion, and ConverterParameter, which allows passing a parameter for the conversion logic.

You can debug data bindings by enabling binding tracing in Microsoft Visual Studio. To do this, select Enable unmanaged code debugging in Visual Studio on the Debug page of the project designer. When binding tracing is enabled and you run your app with the debugger attached, any binding errors appear in the Output window in Visual Studio.

Managing app data

Windows.Storage.ApplicationData.Current.DataChanged += new TypedEventHandler<ApplicationData, object>(DataChangeHandler);

Brak komentarzy: