Programowanie .NET

Archive for the category “Wszystkie”

Projektowanie dotyku Metro 3/3

Windows 8 jest systemem który będzie również dostępny na tabletach. Tymi urządzeniami sterujemy przy pomocy dotyku dlatego projektując aplikację metro trzeba wziąć pod uwagę iż nie zawsze będzie obsługiwana przy pomocy myszki a za pomocą dotyku.

System Windows 8 posiada zwiększony zestaw interakcji dotykowych, który jest używany w całym systemie. stosując ten zestaw w swoich aplikacjach sprawimy że stanie się znajoma dla użytkownika.

Przy projektowaniu interfejsu użytkownika trzeba wsiąść pod uwagę zasady:

  1. Używaj palców do tego w czym są dobre – myszka i pióro są dokładne natomiast palce nie. Projektując interfejs musimy stosować duże elementy które wspierają bezpośrednią manipulację oraz dostarczają bogaty zestaw interakcji dotykowych.
  2. Przeglądaj zawartość za pomocą dotyku – stosuj zoom semantyczny i panoramę które dobrze radzą sobie z prezentowaniem dużych treści, zamiast wielu kart i stron.
  3. Pokaż reakcję – Zwiększaj zaufanie użytkownika poprzez zapewnienie natychmiastowej informacji, gdy ekran jest dotykany. Możesz tego dokonać stosując zmianę koloru, rozmiaru lub przesuwać element.
  4. Zawartość podążająca za palcem – elementy które można przesuwać powinny krążyć za palcem. Natomiast elementy które nie poruszają się powinny powrócić do stany domyślnego po opuszczeniu palca z ich obrębu.
  5. Zachowuj interakcje odwracalne – Interakcje dotykowe powinny być odwracalne, zapewniając wizualną reakcję na to, co się stanie, gdy użytkownik podniesie palec. Spowoduje to, że Twoja aplikacja będzie korzystała bezpiecznie z dotyku.
  6. Zezwalaj na dowolną liczbę palców – Ludzie korzystają często z więcej niż jednego palca, nie zdając sobie z tego sprawy. Dlatego interakcje dotykowe nie powinny się zmieniać radykalnie, w zależności od liczby palców dotykających ekran.
  7. Nie ograniczaj czasowo interakcji – Interakcje, które wymagają złożonych gestów, takich jak podwójne stuknięcie lub naciśnięcie i przytrzymanie, musza być wykonane w określonym czasie. Należy unikać interakcji czasowych, ponieważ są trudne i często wyzwalane przypadkowo.

Dostępne interakcje w Windows 8.

  • wciśnij i przytrzymaj

  • stuknij

  • przesuń

  • swipe do zaznaczenia, wydania polecenia i przesunięcia

  • ściągnij i rozciągnij, aby zmienić rozmiar

  • obróć

  • swipe od krawędzi – pasek aplikacji

  • swipe od krawędzi – pasek poleceń systemowych

Zakres dotyku

Tablety w przeciwieństwie do monitorów użytkownik nie będzie widział dobrze całego ekranu ponieważ część ekranu odejdzie na trzymanie tableta. Spróbuj zoptymalizować aplikację dla różnych rodzajów chwytów, ale jeśli interakcja naturalnie nadaje się do konkretnego chwytu, zoptymalizuj ją pod tym kątem.

Obszary interakcji: Ponieważ tablety najczęściej trzymane są za boki rogu, więc boki są idealną lokalizacją elementów interaktywnych.

Obszary czytania:Zawartość w górnej połowie ekranu jest łatwiejsza do zobaczenia niż w dolnej, która często blokowana jest przez ręce lub ignorowana.

Cztery najczęstsze chwyty.

  • jedna ręka trzyma tablet, druga prowadzi interakcję

    • prawa lub dolna krawędź zapewnia szybką interakcję.
    • prawy dolny róg może zostać zablokowany przez nadgarstek
    • dotykanie jest bardziej dokładne.
    • czytanie, wyszukiwanie, pisanie.
  • dwie ręce trzymają tablet, kciuki prowadzą interakcję.
    • lewy i prawy dolny narożnik zapewniają szybką interakcję.
    • umiejscowione kciuki zwiększają dokładność dotyku.
    • element znajdujący się na środku jest trudny do osiągnięcia.
    • dotknięcie środka ekranu wymaga zmiany postawy.
    • czytanie, przeglądanie, lekkie pisanie, granie.
  • urządzenie leży na kolanach lub stole, obie ręce są w stanie prowadzić interakcję.

    • dół ekranu zapewnia szybką interakcję.
    • dolne narożniki mogą być blokowane przez nadgarstki.
    • dotyk jest bardzo dokładny.
    • czytanie, przeglądanie, pisanie długich tekstów.
  • urządzenie stoi, możliwa jest interakcja lub jej brak.
    • dolna część ekranu zapewnia szybką interakcję.
    • dotknięcie górnej części ekranu pochłania zawartość.
    • dotknięcie górnej części ekranu może wytrącić urządzenie z równowagi.
    • interakcja w odległości zmniejsza czytelność i dokładność.
    • należy zwiększyć rozmiar elementu docelowego, aby poprawić czytelność i precyzję.
    • oglądanie filmu, słuchanie muzyki.

Celność dotyku

Rozmiar elementu ma durzy wpływ na jego celność. Nie ma konkretnych zasad w tej kwestii. Choć elementy z poważnymi akcjami (usuwanie, zamykanie, dodawanie) powinny być duże natomiast elementy z rzadziej stosowanymi akcjami mogą być mniejsze.

Ludzie często obwiniają się za to, że mają „tłuste palce”, ale nawet palce niemowląt są szersze niż większość celów dotykowych.

Obraz pokazuje szerokość palca przeciętnej dorosłej osoby, wynoszącą około 11 mm szerokości, choć niektórzy koszykarze mają palce szersze niż 19 mm, podczas gdy dziecko ma 8 mm.

Oto kilka informacji, jakiej wielkości powinny być cele dotykowe. Wytyczne rozmiarów celów.

  • Zalecany minimalny rozmiar – 7×7 mm
    7×7 mm – jeśli błędny dotyk może zostać skorygowany w jednym lub dwóch gestach, lub w ciągu pięciu sekund to jest to dobra, minimalna wielkość. Odległość pomiędzy celami jest tak samo ważna, jak docelowa wielkość.
  • Gdy ważna jest dokładność
    Zamykanie, usuwanie i inne działania z poważnymi konsekwencjami nie mogą pozwolić sobie na przypadkowe stuknięcia. Użyj wielkości 9×9 mm – jeśli dotyk złego elementu wymaga więcej niż dwóch gestów, jest dłuższy niż pięć sekund lub wymaga zmiany dużej części zawartości.
  • Gdy, to po prostu się nie zmieści
    Możesz użyć wielkości 5×5 mm, jeśli akcje można skorygować za pomocą jednego gestu, w tym przypadku bardzo ważny jest 2 mm odstęp.

StorageFolder & StorageFile API

Dziś zajmę się częścią WinRT odpowiedzialną za obsługę plików i folderów. W tym artykule skupie się na dwóch klasach mianowicie StorageFolder i StorageFile które służą do operacji na plikach i folderach.

Wybieranie folderów

Zacznę od klasy KnownFolders, która służy do określenia miejsca możemy wybrać z kilki podstawowych bibliotek takich jak:

  • Documents Library
  • Home Group
  • Media Server Devices
  • Music Library
  • Pictures Library
  • Removable Devices
  • Videos Library

Te typy zwracają obiekt tylu StorageFolder, który reprezentuje wybrany przez nas folder. Oczywiście jest możliwość wskazania innego folderu najczęściej będą to foldery samej aplikacji. Każda aplikacja metro posiada trzy foldery o specyficznych dla siebie możliwościach są to:

  • LocalFolder – służy do przechowywania folderów i plików lokalnie.
  • RoaamingFolder – służy przechowywania folderów i plików, które są synchronizowane z chmura i dzięki temu są dostępne dla aplikacji np. na tablecie i komputerze.
  • TemporaryFolder – folder do tymczasowego przechowywania danych.

Aby skorzystać z tych katalogów należy użyć ApplicationData jak poniżej:

var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;

 

Do tych trzech folderów można również odwołać się z poziomu XAML dzięki zastosowani odpowiednich przedrostków:

  • dla lokalnego folderu to będzie ms-appdata:///local/
  • dla folderu zdalnego natomiast ms-appdata:///roaming/
  • dla folderu tymczasowego będzie ms-appdata:///temp/

Tworzenie plików i folderów

Do tworzenia folderów posługujemy się metodą CreateFolderAsync(). Ma ona dwie przeciążone formy w pierwszej podajemy tylko nazwę folderu a w drugiej dodatkowo czynność, jaka ma być wykonana w przypadku jak folder o tej nazwie już istnieje.

var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("folder", CreationCollisionOption.OpenIfExists);

 

Należy pamiętać o stosowaniu słów await i async, ponieważ wszystkie operacje są asynchroniczne. Tworzenie plików odbywa się dokładnie tak samo tylko wywołujemy metodę CreateFileAsync(), jest ona również przeciążona.

Pobieranie plików i folderów

Mamy trzy możliwości pobierania zawartość lokalizacji, w której aktualnie się znajdujemy. Możemy pobrać same foldery, same pliku lub wszystkie elementy, czyli razem pliki i foldery.
Pobieranie samych folderów odbywa się dzięki metodzie GetFolderAsync(String) lub GetFoldersAsync(). Pierwsza metoda zwraca tylko jeden folder i jest to konkretny folder o podanej nazwie, jako parametr. Natomiast druga zwraca listę wszystkich folderów, jakie znajdują się w danej lokalizacji.

StorageFolder folder = await KnownFolders.VideosLibrary.GetFolderAsync("MyFolder");

 

Pobieranie plików odbywa się podobnie mamy metody GetFileAsync(String) i GetFilesAsync(). Tak samo jak w przypadku folderów pierwsza zwraca plik o konkretnej nazwie a druga listę wszystkich plików, jakie się znajdują w lokalizacji.
Ostatnim sposobem przeszukiwania i zwracania jest pobranie wszystkich elementów folderów i plików. Dokonujemy tego przy pomocy GetItemAsync(String) lub GetItemsAsync(). Tak jak poprzednio i tu pierwsza metoda zwraca jeden element o podanej nazwie jednak tu może to być folder lub plik. Aby określić, z czym mamy odczynienia zwracamy obiekt klasy StorageItem posiada właściwość IsOfType, która określa czy po folder czy plik a może jeszcze coś innego.

Odczyt z pliku

Jest parę sposobów odczytania pliku jak odczyt tekst, strumieni czy bufora. Na pierwszy ogień niech pójdzie najłatwiejsza metoda, czyli odczyt tekst.

StorageFolder storageFolder = KnownFolders.DocumentsLibrary;
StorageFile sampleFile = await storageFolder.GetFileAsync("sample.txt");
String text = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);

 

Odczyt z bufora

var buffer = await Windows.Storage.FileIO.ReadBufferAsync(sampleFile);
DataReader dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer);
string text = dataReader.ReadString(buffer.Length);

 

Odczyt za pomocą strumieni

var stream = await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
var size = stream.Size;
using (var inputStream = stream.GetInputStreamAt(0))
{
    DataReader dataReader = new DataReader(inputStream);
    uint numBytesLoaded = await dataReader.LoadAsync((uint)size);
    string text = dataReader.ReadString(numBytesLoaded);
}

 

Zapis do pliku

Ostatnim elementem, jaki dziś pokażę to zapis do pliku. Do tego celu można również używać tekstu, bufora i strumieni.

Zapis tekstu

await Windows.Storage.FileIO.WriteTextAsync(sampleFile, "Swift as a shadow");

 

Zapis bufora

var buffer = Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary("What fools these mortals be", Windows.Security.Cryptography.BinaryStringEncoding.Utf8);
await Windows.Storage.FileIO.WriteBufferAsync(sampleFile, buffer);

 

Zapis strumieni

var stream = await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
using (var outputStream = stream.GetOutputStreamAt(0))
{
    DataWriter dataWriter = new DataWriter(outputStream);
    dataWriter.WriteString("The DataWriter provides method to write to various types, such as DataTimeOffset.");
}

 

Tips & Tricks–handling the virtual keyboard

Jakiś czas temu pokazałem jak można utworzyć w łatwy sposób pływające okno. Jednak w przypadku, jeśli takie okno jest używane do wprowadzania danych należy rozważyć obsługę wirtualnej klawiatury, aby menu użytkowania przesunęło się do góry.screenshot_08012012_081005

WinRT posiada odpowiednie zdarzenie do tego celu.

Windows.UI.ViewManagement.InputPane.GetForCurrentView().Showing

Natomiast kod do unoszenia menu nad wirtualną klawiaturę prezentuje się następująco.

int flyoutOffset = 0;
Windows.UI.ViewManagement.InputPane.GetForCurrentView().Showing += (s, args) =>
    {
        flyoutOffset = (int)args.OccludedRect.Height;
        flyout.VerticalOffset -= flyoutOffset;
    };
Windows.UI.ViewManagement.InputPane.GetForCurrentView().Hiding += (s, args) =>
    {
        flyout.VerticalOffset += flyoutOffset;
    };

Kod powyżej wstawiamy do metody którą zaprezentowałem w poście o Flyout Control.

public static Popup ShowPopup(FrameworkElement source, UserControl control)
{
    Popup flyout = new Popup();

    var windowBounds = Window.Current.Bounds;
    var rootVisual = Window.Current.Content;

    GeneralTransform gt = source.TransformToVisual(rootVisual);

    var absolutePosition = gt.TransformPoint(new Point(0, 0));

    control.Measure(new Size(Double.PositiveInfinity, double.PositiveInfinity));

    flyout.VerticalOffset = windowBounds.Height - control.Height - 120;
    flyout.HorizontalOffset = (absolutePosition.X + source.ActualWidth / 2) - control.Width / 2;
    flyout.IsLightDismissEnabled = true;

    flyout.Child = control;
    var transitions = new TransitionCollection();
    transitions.Add(new PopupThemeTransition() { FromHorizontalOffset = 0, FromVerticalOffset = 100 });
    flyout.ChildTransitions = transitions;
    flyout.IsOpen = true;

    // Handling the virtual keyboard
    int flyoutOffset = 0;
    Windows.UI.ViewManagement.InputPane.GetForCurrentView().Showing += (s, args) =>
        {
            flyoutOffset = (int)args.OccludedRect.Height;
            flyout.VerticalOffset -= flyoutOffset;
        };
    Windows.UI.ViewManagement.InputPane.GetForCurrentView().Hiding += (s, args) =>
        {
            flyout.VerticalOffset += flyoutOffset;
        };

    return flyout;
}

Polecenia w Metro 2/3

W tym poście opiszę jak powinno wyglądać ułożenie poleceń na AppBar. Na początku chcę przypomnieć o charms bar jest to wysuwany z prawej strony parek. Wspominam o nim ponieważ operacje jak szukanie, udostępnianie, ustawienia czy też komunikacja pomiędzy urządzeniami powinny znaleźć się właśnie w charms bar unikniemy w ten sposób duplikowania funkcji.

Pasek aplikacji czyli kontrolka AppBar służy do przechowywania akcji jakie w danym miejscu aplikacji może wykonać użytkownik. Jednak istnieje kilka zasad którymi trzeba się kierować dodając akcje do paska. Trzymając się tych zasad nasze aplikacje będą wpisywać się bardzo dobrze w styl metro ale również ergonomia użytkowania będzie znacznie większa. Dodatkowo użytkownik będzie miał miłe doznania ponieważ przechodząc pomiędzy aplikacjami znajdzie szukane opcje szybciej.

Pierwszym krokiem jaki musimy zrobić to wypisać sobie wszystkie polecenia jakie występują w aplikacji. Poniżej prezentuję przykładowe akcje.

Mając wypisane akcje trzeba odpowiedzieć sobie na trzy ważne i podstawowe pytania.

  • Które polecenia powinny pojawiać się w całej aplikacji?
  • Które polecenia powinny pokazywać się na określonych stronach?
  • Które polecenia powinny zostać przeniesione do charms oraz ustawień?

Po zastanowieniu się nad powyższymi pytaniami kolejnym krokiem jest pogrupowanie poleceń. Przy grupowania odpowiadamy sobie na takie pytania.

  • Które komendy są funkcjonalnie powiązane?
  • Które polecenia przełączają typy widoków?
  • Które polecenia powinny pojawić się po zaznaczeniu elementu?

Akcje które znajdują się wyżej można pogrupować w ten sposób.

Polecenia widoku
Polecenia filtrowania
Polecenia sortowania
Polecenia wyboru
Polecenia wyboru
Polecenie Nowy element

Kolejnym krokiem jest rozważenie czy zestawy komend będzie lepiej działał w formie menu komend:

  • Czy pasek aplikacji nie jest zbyt zatłoczony, czy nie ma tam zbyt wielu poleceń, czy będą do siebie pasowały?
  • Czy istnieje zbiór, który korzysta z dłuższych etykiet lub interaktywnej kontrolki?

Dzięki menu można pokazać więcej opcji na mniejszej pozycji używając interaktywnych kontrolek.

W prezentowanym prze zemnie przykładzie można połączyć polecenia sortowania jak i filtrowania w jedno. Efektem tej operacji jest większa przejrzystość paska jak również zwiększona ergonomia pracy.

Ostatnim elementem jaki poruszę to sposób umieszczania poleceń na pasku. Ponieważ jest parę zasad które określają jak układać polecenia na pasku. Przy projektowaniu wyglądu paska powinniśmy kierować się trzema zasadami.

  • Przewidywalność: W miarę możliwości należy użyć spójnej interakcji oraz umieścić polecenia we wszystkich widokach.
  • Ergonomia: Zastanów się, w jaki sposób rozmieszczenie poszczególnych poleceń może poprawić szybkość i łatwość ich wykorzystania.
  • Estetyka: Ogranicz liczbę poleceń. Wybierz ikony, które są łatwe do zrozumienia i przewidzenia. Stosuj krótkie etykiety.

Po prawej stronie paska umieszczamy polecenia które przewijają się przez cały system (domyślne polecenia). W przypadku jak mamy zaledwie kilka komend możemy wszystkie przypiąć do prawej krawędzi paska aplikacji.

Jednak jeśli poleceń jest więcej do rozgrupowujemy je do lewej i prawej krawędzi. Uzyskamy dzięki temu łatwiejsze korzystanie z paska.

Komendy które nie są potrzebne w niektórych momentach powinny być ukryte natomiast pokazywane tylko wtedy gdy faktycznie jest możliwość ich użycia.

Polecenia które pokazują się jako rezultat akcji użytkownika (np. zaznaczenie). Są przesuwane skrajnie do lewej krawędzi prowadzi to do łatwiejszego wyboru tych opcji.

Niektóre polecenia występują w większości aplikacjach i dla zachowania spójności pomiędzy nimi należy stosować się do poniższych zasad.

Polecenia wyboru, związane z dokonaniem wyboru, zawsze pojawiają się skrajnie na lewo. Kiedy są kontekstowe, pojawiają się przy danej sekcji.

W tym przykładzie, zanim użytkownik coś zaznaczy, polecenie select all pojawi się po lewej. Po zaznaczeniu czegoś przez użytkownika pozostałe polecenia pojawiają się po lewej stronie.

Polecenie Nowy element pojawia się po prawej stronie. Dzięki temu polecenie Nowy(New) staje się łatwo dostępne dla użytkownika.
Polecenie Nowe nie powinno pojawiać się w innym miejscu.

Polecenie Usuń/Nowy powinno być zawsze usytuowane w przedstawionej na rysunku kolejności.

Polecenie Usuń/Dodaj powinno być zawsze usytuowane w zaprezentowanej na rysunku kolejności.

Jasne polecenia – użyj etykiety, aby polecenie było czytelne, szczególnie dla operacji destrukcyjnych.

Projektowanie interfejsu 1/3

Dziś nie będzie kody w tym i kolejnych postach skupie się na zasadach jakie obowiązują przy projektowaniu interfejsu dla aplikacji Metro. W dzisiejszy poście skupie się na temacie nawigacji pomiędzy stronami i elementami jakie wchodzą w skład nawigacji.

Pisząc na temat nawigowania w aplikacjach metro dla systemu Windows 8 trzeba rozpocząć od przedstawienia dwóch podstawowych wzorców nawigacyjnych.

Układ hierarchiczny

Jest jednym z najczęściej używanych systemów nawigacji. Ten system nawigacji najlepiej nadaje się do aplikacji gdzie mamy dużo treści lub zróżnicowanych zawartości.
Hierarchical system of navigation in a Metro stye spp
Najważniejsze w układzie hierarchicznym jest podział na sekcje i różne poziomy szczegółowości. Charakterystycznym elementem jest przycisk wstecz który ułatwia nawigowanie. Układ hierarchiczny składa się z 3 głównych typów stron które reprezentują różne poziomy szczegółowości.

Strona główna – hub
Hub jest pierwszym poziomem aplikacji. Hub zawiera wszystkie elementy aplikacji a zarazem jest najmniej szczegółowy. Zawartość jest wyświetlana panoramiczne dzięki czemu użytkownik ma łatwy dostęp.
Hub zawiera odnośniki do poszczególnych sekcji w różnych kategoriach. Na tym poziomie nie ma sztywnych wizualnych form projektowania. Przeciwnie zaleca się aby strona wizualna była różnorodna dzięki czemu ułatwi to projektantowi rozdzielenie poszczególnych sekcji a użytkownikowi pozwoli na łatwiejsze poruszanie.

Sekcje
Sekcja to kolejny poziom szczegółowości. Strona ta zawiera więcej szczegółów ale i tu nie ma sztywnych reguł do wyglądu. Strona powinna jak najlepiej dopasowana do wyświetlanego materiału. Ta strona składa się z pojedynczych elementów które przekierowują do strony z detalami.

Szczegóły
Ten poziom jest to  najbardziej szczegółowym i znajdują się tutaj informacje na temat jednego elementu. Ta strona może zawierać wiele informacji jak również  elementów wideo lub obrazy. Format wyświetlania powinien być dostosowany do zawartości.

Układ płaski

Jest to kolejny często wykorzystywany układ nawigacji. Wykorzystuje się go tam gdzie elementy znajdują się na tym samym poziomie hierarchii. Najlepiej nadaje się do aplikacji gdzie liczba stron jest niewielka a użytkownik przemieszka się pomiędzy stronami lub kartami które są dostępne pod prawym przyciskiem.

Pasek nawigacyjny
Pasek nawigacyjny służy do przełączania się pomiędzy stronami. Element jest zlokalizowany przy górnej krawędzi. Wyświetlany jest po kliknięciu prawym klawiszem myszki lub szybkim przeciągnięciu do górnej lub dolnej krawędzi ekranu.

Przełączanie
Nie ma tu przycisku wstecz a nawigacja pomiędzy stronami odbywa się przy pomocy bezpośrednich linków lub wykorzystując pasek nawigacyjny.

Podsumujmy teraz sobie wszystkie elementy jakie wchodzą w skład nawigacji.

  1. Nagłówek oraz przycisk wstecz.
  2. Hub.
  3. Sekcja lub kategorie.
  4. Zoom semantyczny.
  5. Pasek nawigacyjny.
  6. Menu nagłówka.
  7. Link do strony głównej.
  8. Pasek aplikacji.
  9. Widok/sortowanie/filtrowanie.
  10. Swiping do krawędzi.

Nawigowanie za pomocą etykiet menu i sekcji nagłówka

Nagłówek jest elementem który znajduje się na każdym poziomie w naszej aplikacji. Możemy go wykorzystać do nawigowania umieszczając w nim łącza do każdej sekcji na danym poziomie oraz łącze do Huba aplikacji.

Warto również przy nagłówkach sekcji dodawać informacje o liczbie elementów. Poinformuje to użytkownika ze jest więcej elementów niż te co widzi.

Poniżej znajduje się diagram który przedstawia przykłady nawigacji wraz z przedstawieniem wszystkiego co jest interaktywne.

Filtrowanie, sortowanie i widok

Na koniec tego postu skupie się na elementach które dają użytkownikowi większą kontrolę nad prezentacją danych.

Sortowanie i filtrowanie stron
Elementy filtrowania i sortowania można umieścić pomiędzy nagłówkiem na treścią.

Te elementów mogą również zawierać rozwijalną listę w przypadku jak opcji jest za dużo aby pokazać je wszystkie na raz.

Pasek aplikacji – przełączanie widoku
Pasek aplikacji jest używany do przechowywania akcji jakie mogą być wykonane na aktualnym elemencie. W tym miejscu powinny znajdować się i wyłącznie akcje jakie można wykonać na aktualnym elemencie. Operacje jakie mogą się tutaj między innymi znaleźć to sterowanie, przełączanie widoku, przestawianie, filtrowanie i sortowanie. Nie powinno się umieszczać tutaj elementów nawigacyjnych do innych części aplikacji. Na przykładzie kalendarza można łatwo zaobserwować zmianę widoku.

Na stronie All Restaurants, przykładowej aplikacji Food with Friends, dostępne jest przeglądanie elementów w widoku listy lub mapy, z możliwością sortowania widocznych elementów, na podstawie takich kryteriów jak: koszty, lokalizacja i ocena. Opcje filtrowania są dostępne jako kontrolki w menu wysuwanym.

Trips & tricks–Flyout control

Dziś zaprezentuję jak stworzyć flyout control. Flyout control jest niczym innym jak popup colntrol, który możemy wywołać po kliknięciu np. na przycisk. Przykład takigo flyout to przycisk “Opinia” w SkyDrive.

Untitled

Akurat to okno jest mocno rozbudowane jednak flyout nie musi nieć aż tylko opcji.

A więc jak można stworzyć taką funkcjonalność a wystarczy użyć poniższej metody.

public static Popup ShowPopup(FrameworkElement source, UserControl control)
{
    Popup flyout = new Popup();

    var windowBounds = Window.Current.Bounds;
    var rootVisual = Window.Current.Content;

    GeneralTransform gt = source.TransformToVisual(rootVisual);

    var absolutePosition = gt.TransformPoint(new Point(0, 0));

    control.Measure(new Size(Double.PositiveInfinity, double.PositiveInfinity));

    flyout.VerticalOffset = absolutePosition.Y  - control.Height - 10;
    flyout.HorizontalOffset = (absolutePosition.X + source.ActualWidth / 2) - control.Width / 2;
    flyout.IsLightDismissEnabled = true;

    flyout.Child = control;
    var transitions = new TransitionCollection();
    transitions.Add(new PopupThemeTransition() { FromHorizontalOffset = 0, FromVerticalOffset = 100 });
    flyout.ChildTransitions = transitions;
    flyout.IsOpen = true;

    return flyout;
}

 

Przyjmuje ona dwa parametry pierwszym jest źródło, do którego będzie dodawać popup. Natomiast drugi parametr to UserControl. Tak, więc naszą funkcjonalność, którą chcemy pokazać nad przyciskiem tworzymy w formie kontrolki i dodajemy na popup.

Tips & tricks – FileExistsAsync

Dziś podzielę się kodem metody, która sprawdza czy istnieje plik w zadanym katalogu. Niby nic, ale jednak WinRT jak na razie nie posiada takiej funkcji i trzeba samemu coś takiego napisać.

Metoda nie jest skomplikowana jak widać poniżej.

public async static Task<bool> FileExistsAsync(this StorageFolder folder, string name)
{
    var files = await folder.GetFilesAsync();

    return files.Any((f)=>
    {
        return f.Name == name;
    });
}

Należy przekazać jej folder, w jakim ma szukać i nazwę pliku do szukania. Jak znaczna część metod operujących na plikach (albo i wszystkie) moja również jest asynchroniczna.

Natomiast wykorzystać można np. tak.

if (!await ApplicationData.Current.LocalFolder.FileExistsAsync(filename))
    return;

StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(filename);

Trial i Windows Store API

Cały czas rozpisuję się o nowościach i możliwościach jakie mają aplikacje metro wszystko fajnie jednak nie zapominajmy o rynku na jakim będą dostępne nasze aplikacje a mianowicie Windows Store. Obecnie na świecie jest ok 500 milionów komputerów PC z Windows 7. Niech nawet tylko 10%  z nich przejdzie na Windows 8 to i tak dostęp będziemy mieć do 50 milionów potencjalnych klientów. Nie ma co robi warzenie (przynajmniej na mnie).

Microsoft daje nam narzędzia do łatwiejszego testowania aplikacji w wersji płatnej i próbnej. W przeciwieństwie do Windows Phone 7 tutaj mamy możliwość testowania kupna aplikacji jak również co jest nowością symulowania kupna konkretnych elementów w aplikacji lub grze. Ta druga opcja przyda się szczególnie przy grach.

Tak więc przejdźmy do o kodowania funkcjonalności kupowania. Wszystkie informacje o naszej aplikacji znajdują się w specjalnie przygotowanym pliku którego używamy do symulowania połączenia się ze sklepem. Plik prezentuję poniżej.

<?xml version="1.0" encoding="utf-16" ?>
<CurrentApp>
  <ListingInformation>
    <App>
      <AppId>2B14D306-D8F8-4066-A45B-0FB3464C67F2</AppId>
      <LinkUri>http://apps.microsoft.com/app/2B14D306-D8F8-4066-A45B-0FB3464C67F2</LinkUri>
      <CurrentMarket>en-US</CurrentMarket>
      <AgeRating>3</AgeRating>
      <MarketData xml:lang="en-us">
        <Name>Contoso Cookbook</Name>
        <Description>Metro recipes for Metro developers</Description>
        <Price>08.99</Price>
        <CurrencySymbol>$</CurrencySymbol>
      </MarketData>
    </App>
    <Product ProductId="ItalianRecipes">
      <MarketData xml:lang="en-us">
        <Name>Italian Recipes</Name>
        <Price>0.99</Price>
        <CurrencySymbol>$</CurrencySymbol>
      </MarketData>
    </Product>
  </ListingInformation>
  
  <LicenseInformation>
    <App>
      <IsActive>true</IsActive>
      <IsTrial>true</IsTrial>
      <ExpirationDate>2022-12-31T23:59:59.00Z</ExpirationDate>
    </App>
    <Product ProductId="ItalianRecipes">
      <IsActive>false</IsActive>
    </Product>
  </LicenseInformation>
</CurrentApp>

 

Jak widać znajdują się tutaj dwie główne sekcje. Pierwsza <ListingInformation> przechowuje informacje na temat aplikacji i dodatkowych produktów jakie można kupić w ramach aplikacji. Natomiast drugi element <LicenseInformation> zawiera informacje na temat licencjonowania aplikacji i produktów. Normalnie te informacje będą pochodzić ze sklepu. Jednak w naszym przypadku symulacji będą kopiowane do pliku WindowsStoreProxy.xml (nazwa może być dowolna) z którego system z czyta informacje. W pliku App.xaml.cs odnajdujemy metodę OnLaunched do której wstawiamy następujący kawałek kodu.

// Inocjowanie WindowsStoreProxy.xml
var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Microsoft\\Windows Store\\ApiData", CreationCollisionOption.OpenIfExists);
var file = await Package.Current.InstalledLocation.GetFileAsync("Data\\license.xml");
var newfile = await folder.CreateFileAsync("WindowsStoreProxy.xml", CreationCollisionOption.ReplaceExisting);
await file.CopyAndReplaceAsync(newfile);

 

Te cztery linijki otwierają plik licencji jaki mamy w projekcie potem tworzą plik który będzie używany do symulowania zakupi i kopiują dane z pierwszego do drugiego.
Kolejnym krokiem jest stworzenie klasy która będzie przechowywać informacjie na temat licencji tak samo jak się robiło dla aplikacji WP7. Przykładową implementację prezentuję poniżej.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Store;
using Windows.Foundation;

namespace ContosoCookbook
{
    class AppLicenseDataSource : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private bool _licensed = false;
        private string _price;

        public AppLicenseDataSource()
        {
            if (CurrentAppSimulator.LicenseInformation.IsTrial)
            {
                CurrentAppSimulator.LicenseInformation.LicenseChanged += OnLicenseChanged;
                GetListingInformationAsync();
            }
            else
                _licensed = true;
        }

        private async void GetListingInformationAsync()
        {
            var listing = await CurrentAppSimulator.LoadListingInformationAsync();
            _price = listing.FormattedPrice;
        }
        
        private void OnLicenseChanged()
        {
            if (!CurrentAppSimulator.LicenseInformation.IsTrial)
            {
                _licensed = true;
                CurrentAppSimulator.LicenseInformation.LicenseChanged -= OnLicenseChanged;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("IsLicensed"));
                    PropertyChanged(this, new PropertyChangedEventArgs("IsTrial"));
                    PropertyChanged(this, new PropertyChangedEventArgs("LicenseInfo"));
                }
            }
        }

        public bool IsLicensed
        {
            get { return _licensed; }
        }

        public bool IsTrial
        {
            get { return !_licensed; }
        }

        public string LicenseInfo
        {
            get
            {
                if (!_licensed)
                    return "Trial Version";
                else
                    return ("Valid until " + CurrentAppSimulator.LicenseInformation.ExpirationDate.LocalDateTime.ToString("dddd, MMMM d, yyyy"));
            }
        }
        
        public string FormattedPrice
        {
            get
            {
                if (!String.IsNullOrEmpty(_price))
                    return "Upgrade to the Full Version for " + _price;
                else
                    return "Upgrade to the Full Version";
            }
        }
    }
}

 

W konstruktorze klasy pobierane są informacje z symulatora zakupu. Między innymi sprawdzana jest wersja w przypadku wersji trial dodawane jest zdarzenie zmiany wersji. Nie będę dokładnie opisywał ponieważ klasa jest prosta. Natomiast kod odpowiedzialny za zakup naszej aplikacji prezentuje się bardzo prosto.

using Windows.ApplicationModel.Store;

CurrentAppSimulator.RequestAppPurchaseAsync();

 

Potrzebujemy dodać jedną przestrzeń nazw i uruchomić jedną metodę. Zostało napisać wygląd prezentowania tych informacji ale to już zależy do każdej aplikacji indywidualnie. Po napisaniu wyglądu jesteśmy gotowi do uruchomienia. U mnie prezentuje się następująco.

Wersja trial
Untitled1

Okno symulujące zakup jak widać mamy kilka scenariuszy do wyboru.Untitled2

I wersja płatna
Untitled3

Ostatnim elementem symulacji zakupów o jakim chcę opowiedzieć jest kupowanie pojedynczych produktów które zwiększają funkcjonalność naszej aplikacji lub gry. Odbywa się to bardzo podobnie jak kupno aplikacji. Znów musimy stworzyć klasę która będzie informować o stanie danego produktu. Przykładowa implementacja.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Store;

namespace ContosoCookbook
{
    class ProductLicenseDataSource : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private const string _name = "ItalianRecipes";
        private bool _licensed = false;
        private string _price;

        public string GroupTitle
        {
            set
            {
                if (value != "Italian")
                    _licensed = true;
                else if (CurrentAppSimulator.LicenseInformation.ProductLicenses[_name].IsActive)
                    _licensed = true;
                else
                {
                    CurrentAppSimulator.LicenseInformation.LicenseChanged += OnLicenseChanged;
                    GetListingInformationAsync();
                }
            }
        }

        private async void GetListingInformationAsync()
        {
            var listing = await CurrentAppSimulator.LoadListingInformationAsync();
            _price = listing.ProductListings[_name].FormattedPrice;
        }
        
        private void OnLicenseChanged()
        {
            if (CurrentAppSimulator.LicenseInformation.ProductLicenses[_name].IsActive)
            {
                _licensed = true;
                CurrentAppSimulator.LicenseInformation.LicenseChanged -= OnLicenseChanged;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("IsLicensed"));
                    PropertyChanged(this, new PropertyChangedEventArgs("IsTrial"));
                }
            }
        }
        
        public bool IsLicensed
        {
            get { return _licensed; }
        }

        public bool IsTrial
        {
            get { return !_licensed; }
        }

        public string FormattedPrice
        {
            get
            {
                if (!String.IsNullOrEmpty(_price))
                    return "Purchase Italian Recipes for " + _price;
                else
                    return "Purchase Italian Recipes";
            }
        }
    }
}

 

W tej implementacji właściwość GroupTitle steruje tym czy dany element trzeba kupić. Jest tak ponieważ kod pochodzi od aplikacji kucharskiej a kupić można przepisy włoskie a inne są darmowe. Kod odpowiedzialny za uruchomienie kupna tego produktu jest następujący.

using Windows.ApplicationModel.Store;

CurrentAppSimulator.RequestProductPurchaseAsync("ItalianRecipes");

 

Jak widać Microsoft dał nam spory silnik do testowania kupowania aplikacji.

Suspended

Dzisiaj opiszę czym jest suspended. Pod tym tajemniczym wyrazem kryje się stan aplikacji która nie jest na pierwszym planie czyli użytkownik aktualnie z niej nie korzysta. Podobny stan posiadają aplikacje pisane na smartfony z Windows Phone 7. Po co wspominam o tym stanie? Ponieważ gdy aplikacja znajduje się w stanie zamrożenia (suspended) może nastąpić sytuacja, że system bez pytania zamknie aplikację a użytkownik gdy będzie chciał powrócić do niej z powrotem zdziwi się, że uruchamia się na nowo. Microsoft wprowadził ten stan aby lepiej kontrolować zasoby komputera. Ponieważ gdy aplikacja znajduje się w stanie suspended nie korzysta z żadnych zasobów jak np.: CPU, karta graficzna, kata sieciowa itd.. Korzysta jedynie z pamięci RAM. Natomiast aktualnie działająca aplikacja moze potrzebować więcej pamięci RAM i w tedy system przydzieli jej pamięć zajmowaną przez aplikacje w stanie zamrożenia.

W tym poście pokażę w jakich miejscach należy umieszczać kod aby móc reagować na takie sytuacje. Oczywiście sama reakcja będzie wyglądała inaczej w każdej aplikacji jednak miejsca osadzenia kodu są te same. W moim przypadku będę zapamiętywał stronę na której aktualnie znajduje się użytkownik.

Przed przystąpieniem do pracy warto zapoznać się jak aplikacja reaguje na sytuację  zamrożona i gwałtownego zakończenia. W tym celu wystarczy uruchomić spod Visual Studio aplikację w trybie debug. Po uruchomieniu wracamy do Visual Studio i naciskamy przycisk.

image

Jeśli go nie widzimy to trzeba dodać pasek o nazwie Debug Location.

Po tym jak umiemy już wywołać interesującą nas sytuację nadszedł czas na zapoznanie się gdzie możemy przechowywać informacje tak aby ich nie utracić. Platforma Windows Runtime daje nam dostęp do Windows.Storage.ApplicationData. Jest to klasa która umożliwia przechowywanie danych w następujący sposób:

  • Lokalny folder (LocalFoldet)
  • Lokalne ustawienia (LocatSettings)
  • Zdalny folder (RoamingFolder)
  • Zdalne ustawienia (RoamingSettings)
  • Folder czasowy (TemporaryFolder)

W przypadku zapisu danych na nośnikach zdalnych (przez Internet) Aplikacja może przywracać swój stan pomiędzy urządzeniami. Dodatkowo jeśli użytkownik nie jest zalogowany na koncie Live lub nie ma podłączenia do Internetu system przechowuje dane lokalnie. Aktualnie jest tylko jeden minus w wersji Windows Consumer Preview można przechowywać jedynie 100K danych.

Przejdźmy więc do kodu. W konstruktorze klasy App zobaczycie że jest już dodana obsługa zdarzenia Suspending tak więc w tej metodzie następuje dodanie kodu odpowiedzialnego za zapisanie stanu. W moim przypadku wygląda tak.

ApplicationData.Current.RoamingSettings.Values["Location"] = _location;

Zmienna _location przechowuje aktualną stronę i jest to zapisywane jako zdalne ustawienie. Ustawienia to para klucz/wartość. Tutaj klucz to: “Location” natomiast wartością jest to co przechowuje zmienna _location.

Ostatnim krokiem jest przywrócenie odpowiedniego stanu w przypadku zamrożenia i zamknięcia aplikacji. Dokonujemy tego w metodzie OnLaunched() poprzez dodanie jednego warunku if.

if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
    //TODO: Kod do przywrócenia stanu. U mnie taki
    var location = (string)ApplicationData.Current.RoamingSettings.Values["Location"];
    NavigateToLocation(location);

}

Tiles

Do tej pory cały czas pisałem o tematach cięższych wymagających więcej uwagi dlatego dziś postanowiłem opisać krótki i przyjemny temat. A jest to temat kafelka aplikacji. Spotykam się u wielu osób z podejściem iż kafelek aplikacji traktują po macoszemu to znaczy wstawiają obrazek który mniej lub bardziej pasuje do aplikacji. A trzeba pamiętać że kafelek aplikacji jest pierwszą rzeczą jaką widzi użytkownik i zarazem jedyną rzeczą jaka zachęci do wejścia w szczegóły aplikacji. Tak więc trzeba pamiętać aby projekt kafelka był przemyślany i przykuwający uwagę. Aplikacja może mieć kafelek w dwóch trybach.

  1. o wymiarach 150×150 pikseli
  2. o wymiarach 310×150 pikseli

Aby można było przełączać się pomiędzy małą a dużą ikonką wystarczy w pliku Package.appxmanifest ustawić pola dla małego i dużego kafelka.

image

Przejdźmy teraz do tworzenia dodatkowego kafelka. Tak samo jak na Windows Phone w Windows 8 można oprócz kafelka głównego można dodać sobie dodatkowe kafelki które przenoszą nas bezpośrednio do określonej części aplikacji. kod wstawiania takiego kafelka prezentuje się następująco.

var tile = new SecondaryTile(
    _item.UniqueId,             // Tile ID
    _item.ShortTitle,           // Tile short name
    _item.Title,                // Tile display name
    _item.UniqueId,             // Activation argument
    TileOptions.ShowNameOnLogo, // Tile options
    logo                        // Tile logo
);

tile.RequestCreateAsync();

 

Tak jak w WP7 i tu kafelek ma dwie strony tak więc można było by uzupełnić powyższy kod o drugą stronę. Jeśli ktoś pisał aplikacje na WP7 i tworzył tam dodatkowe kafelki widzi pewnie różnice. Nie ma podawania adresu Url do jakiej części aplikacji ma zostać użytkownik przekierowany. Taki kod musimy sami napisać. i umieszczamy go w metodzie OnLaunched z pliku App.xaml.cs. A tak wygląda.

if (!String.IsNullOrEmpty(args.Arguments))
    _args = args.Arguments;
else
{
    //kod w przypadku jeśli uruchomiliśmy z głównego kafelka.
}

 

Argument args metody OnLaunched ma właściwość Arguments jeśli ta właściwość nie jest pusta to znaczy że uruchomiono z kafelka dodatkowego.

Post Navigation