sobota, 10 marca 2012

Windows 8 Metro na żywo, ale w Internecie - odc. 2

Po ukazaniu się nowej odsłony Windows 8 postanowiłem nie tylko przenieść to co już było na nową wersję projektu Visual Studio, ale także równocześnie rozwijać poszczególne funkcjonalności i wprowadzać nowe pomysły do interfejsu użytkownika.

Wskutek tych działań pierwsza strona aplikacji prezentuje się obecnie następująco:

mks2_1

Wprowadziłem następujące zmiany:

  • usunąłem gradient z tła aplikacji (kierując się duchem prostoty)
  • przeniosłem przyciski paska aplikacji na prawą stronę (większość predefiniowanych aplikacji ma przyciski z tej strony)
  • zmieniłem wizualizację pobieranych plików na przycisk ze statusem otwierający popup (większa prostota i wygoda użytkowania)
  • zaimplementowałem pobieranie plików w tle z paskami postępu

Podzielę się teraz różnymi ciekawymi spostrzeżeniami/fragmentami kodu z ostatnich kilku dni:

  • Zainstalowane aplikacje Metro - obecnie znajdują się w C:\Program Files\WindowsApps
  • Słowniki z predefiniowanymi szablonami XAML – pliki .xaml w katalogu C:\Program Files (x86)\Windows Kits\8.0\Include\winrt\xaml\design (nie wszystkie szablony da się skopiować i otworzyć do edycji za pomocą Blenda)
  • Edytowanie szablonów XAML - analogicznie jak we wcześniejszych wersjach Blend, na poniższym zrzucie ekranu edytuję GridViewItem (element kontrolki GridView)

blend_3

  • Binding relatywny - można dostrzec w nowym oknie databindingu w Blend nowe pozycje
  • Strony - obecnie znacznie uległa zmianie ich implementacja w szablonach Visual Studio, wprowadzono typ bazowy, który zawiera w sobie obsługę zmiany layout’u w zależności od orientacji ekranu i stanu okna aplikacji
  • AppBar - obecnie są dwie properties w stronie dla paska na dole i górze, nie osadzamy go już bezpośrednio w layout
  • Przyciski AppBar - są predefionowane style dla sporej liczby przycisków, również w znakach czcionki Segoe UI Symbol odnajdziemy obecnie znacznie większą ilość symboli
  • Nawigacja - określanie typu za pomocą … typu zamiast stringa z nazwą typu (jak było wcześniej), w szablonie Visual Studio pojawił się predefiniowany Frame
  • Popup - analogicznie jak w Silverlight, także pozycjonowanie w oparciu o mierzenie rozmiaru obiektów
  • Notyfikacje
    • jeden interfejs INotifyPropertyChanged !
    • predefiniowana klasa BindableBase w szablonie Visual Studio wykorzystująca atrybuty parametrów metod z C#5

    [Windows.Foundation.Metadata.WebHostHidden]
    public abstract class BindableBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
        {
            if (object.Equals(storage, value)) return false;

            storage = value;
            this.OnPropertyChanged(propertyName);
            return true;
        }

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null)
            {
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

        Dzięki implementacji z atrybutem [CallerMemberName] nie musimy w klasie dziedziczącej podawać jawnie nazwy property.  Przykładowo w mojej klasie DownloadFileTask wygląda to tak:

        public class DownloadFileTask : BindableBase
        {

             ….

            private double _progress;
            public double Progress
           {
               get { return _progress; }
               set
              {
                  SetProperty<double>(ref _progress, value);
              }
          }
       }   

      Mamy więc prostszą implementację niż dotychczas, choć …. nie tak prostą, gdyby porównać to z podejściem aspektowym, jakie udało się wypracować w Silverlight

    • kolekcja ObservableCollection<T> obecnie już działa !  (u mnie lista pobieranych plików na popup)

  • Pobieranie plików w tle

sealed partial class App : Application
    {
        private CancellationTokenSource _cts;
        public ObservableCollection<DownloadFileTask> ActiveDownloads { get; private set; }

       ….


        private async void DiscoverActiveDownloads()
        {
            ActiveDownloads = new ObservableCollection<DownloadFileTask>();

            try
            {
                IReadOnlyList<DownloadOperation> downloads = await BackgroundDownloader.GetCurrentDownloadsAsync();              

                foreach (DownloadOperation download in downloads)
                {
                    HandleDownloadAsync(download, false);
                }
            }
            catch (Exception ex)
            {    }
        }

        public void StartDownload(IEnumerable<YouTubeEntry> items)
        {
            foreach (var item in items)
                StartDownload(item);
        }

        private async void StartDownload(YouTubeEntry item)
        {
            try
            {
                var client = new HttpClient() { MaxResponseContentBufferSize = 65536 * 4 };
                var respMsg = await client.GetAsync(YouTubeUtils.Convert(item.VideoUrl) + "&nomobile=1");

                if (respMsg.StatusCode != System.Net.HttpStatusCode.OK) return;

                var content = await respMsg.Content.ReadAsStringAsync();

                var str2 = YouTubeUtils.GetFromaturl(content, "720P");


                var bd = new BackgroundDownloader();

                var newFile = await KnownFolders.VideosLibrary.CreateFileAsync(item.Title + ".mp4", CreationCollisionOption.ReplaceExisting);

                var download = bd.CreateDownload(new Uri(str2, UriKind.Absolute), newFile);

                HandleDownloadAsync(download, true);
            }           
            catch (Exception)
            {
            }
        }      

        private async void HandleDownloadAsync(DownloadOperation download, bool start)
        {
            var downloadTask = new DownloadFileTask
            {
                Guid = download.Guid,
                FileName = download.ResultFile.Name.Replace(".mp4", ""),
                BytesReceived = download.Progress.BytesReceived,
                TotalBytesToReceive = download.Progress.TotalBytesToReceive,
                Status = download.Progress.Status
            };

            try
            {
                ActiveDownloads.Add(downloadTask);

                Progress<DownloadOperation> progressCallback = new Progress<DownloadOperation>(DownloadProgress);
                if (start)
                {
                    await download.StartAsync().AsTask(_cts.Token, progressCallback);
                }
                else
                {
                    await download.AttachAsync().AsTask(_cts.Token, progressCallback)  
                }
            }

            catch (TaskCanceledException)
            {

            }
            catch (Exception ex)
            {

            }
            finally
            {
                ActiveDownloads.Remove(downloadTask);
            }
        }

        private void DownloadProgress(DownloadOperation download)
        {
            MarshalUpdate(download.Guid, download.Progress.BytesReceived, download.Progress.TotalBytesToReceive, download.Progress.Status);
        }

        private void MarshalUpdate(Guid guid, ulong bytesReceived, ulong totalBytesToReceive, BackgroundTransferStatus status)
        {
            Window.Current.Content.Dispatcher.Invoke(Windows.UI.Core.CoreDispatcherPriority.Normal, (sender, state) =>
            {
                var item = ActiveDownloads.FirstOrDefault(it => it.Guid == guid);

                if (item != null)
                {

                  if (totalBytesToReceive > 0)
                        item.Progress = bytesReceived * 100 / totalBytesToReceive;
          }

            }, this, null);
        }      
    }

Tu taka ciekawostka - gdy podczas dłuższego pobierania całkowicie zamknę aplikację i potem ją otworzę zupełnie na nowo, w metodzie AttachAsync będzie wyjątek, tak samo w przykładzie MS na którym się wzorowałem.

  • Co nowego w obecnej wersji platformy i narzędzi ?

Z tą tematyką będzie związany niejeden post, który napiszę, dokonam też za jakiś czas szczegółowego podsumowania po zapoznaniu się z wszystkimi materiałami.  Dziś jedynie wspomnę, że twórcy napisali o tym trochę już postów:

Brak komentarzy: