.NET i takie tam

Archive for Styczeń 2008

OAC – Orientation Aware Control

leave a comment »

Stworzenie przejrzystego i funkcjonalnego interfejsu dla aplikacji mobilnej nie jest prostym zadaniem. Szczególnie obecnie gdy dynamika rynku urządzeń mobilnych cały czas się zwiększa i co rusz spotykamy się z nowymi rodzajami wyświetlaczy. Projektując interfejs aplikacji mobilnej musimy mieć na uwadze trzy parametry wyświetlania: rozdzielczość, rozmiar ekranu, i tryb wyświetlania. Rozdzielczość określana jest w DPI (ang. dot per inch) im większa tym więcej możemy zmieścić na ekranie. Typowymi rozdzielczościami są QVGA (Quater – VGA) która wynosi 96 DPI i VGA wynosząca 192 DPI. Rozmiar ekranu, podawany jest w pikselach i typowo wynosi 240×320 (szerokość/wysokość). Tryb wyświetlania określa czy nasze okno wyświetlane jest pionowo (vertical/portrait) bądź poziomo (horizontal/landscape).

Rozdzielczością, automatycznie zajmuje się Visual Studio, po za tymi sporadycznymi przypadkami gdy ręcznie tworzymy kontrolki. Musimy wtedy rozmiary komponentu przemnożyć przez, że tak to nazwę, współczynnik skalujący – dostosowujący wymiary do aktualnej rozdzielczości. Skalę możemy określić na przykład tak:


static class Program
{

[MTAThread]
static void Main()
{
  frmScaling f = new frmScaling();

  // rozdzielczość dla której projektowaliśmy aplikację
  const float designResolution = 96.0f;
  System.Drawing.Graphics g = f.CreateGraphics();
  float runningResolution = g.DpiX;

  ScaleFactor = runningResolution / designResoluion;
  g.Dispose();

  Application.Run(f);
}

  // Przemnóż wszystkie 'ręcznie' tworzone kontrolki przez
  // Program.ScaleFactor,
  // Przez większość czasu będą one przyjmowały jedną z
  // trzech wartości:
  // 1 - gdy zaprojektowaliśmy aplikację dla rozdzielczości
  //     na której pracujemy
  // 2 - gdy zaprojektowaliśmy aplikację dla 96dpi ale
  //     pracujemy na 192 dpi
  // 1.365 - gdy zaprojektowaliśmy aplikację dla 96dpi ale
  //     pracujemy na 131dpi (QVGA Smartphone)
  internal static float ScaleFactor;
}

Więcej na ten temat można znaleźć w książce Microsoft Mobile Development Handbook na strony 63 – 66.

Niestety wymiarami ekranów i trybem pracy musimy zająć się sami. Jest kilka sposobów aby ułatwić sobie, życie. Jednym z nich jest projektowanie interfejsu z myślą o kwadratowym ekranie. Mato swoje zalety – interfejs zawsze będzie taki sam niezależnie czy aplikacja pracuje w trybie pionowym czy poziomym. Wadą tego rozwiązania jest to, że na ekranach prostokątnych, marnujemy całkiem sporą ilość wolnej przestrzeni. Myślę, że można wziąć to rozwiązanie pod uwagę dla prostych aplikacji bez wyszukanego interfejsu użytkownika.

Innym rozwiązaniem jest stworzenie osobnych kontrolek – reprezentujących zawartość danego okna – i dynamicznej ich zamianie w zależności od wymaganego położenia. Rozwiązanie to wymaga jednak większego nakładu pracy gdyż nie dość, że musimy zaprojektować dwa różne ekrany i obsłużyć zdarzenie zmiany położenia ekranu. Prosty przykład tego rozwiązania można znaleźć w jednym z wcześniejszych moich postów. Przykład ten prezentuje dodatkowo jak wykorzystać wzorzec MVP (Model-View-Presenter) aby nie duplikować kodu wewnątrz kontrolek.

Kolejnym rozwiązaniem, obecnie chyba najłatwiejszym, jest skorzystanie w Orientation Aware Control w skrócie OAC. OAC jest kontrolką kontenerem i odpowiada funkcjonalności standardowej UserControl. Jednak posiada szereg nowych właściwości dzięki którym, możemy, na tylko jednej (!) kontrolce zaprojektować layout’y dla różnych trybów pracy ekranu. Czyli w sumie cała ciężka praca jest zrobiona za nas – jedyne co nam pozostaje to rozplanować prawidłowe położenie kontrolek.

Komponent OAC możemy pobrać z dwóch źródeł. Pierwszym jest pakiet Microsoft Patterns & Practises – Mobile Client Software Factory (MCSF). Niestety został on wydany prawie dwa lata temu (lipiec 2006) i od tego czasu nie był uaktualniany. Drugim źródłem jest strona www.orientationaware.net – tutaj możemy znaleźć najnowszą wersję tejże kontrolki, niestety jest już to kontrolka komercyjna. Fakt na stronie jest udostępniona wersja Community Edition ale jest ona dość poważnie ograniczona tzn. obsługuje tylko jedną rozdzielczość (QVGA).

Z moich doświadczeń wynika, że wersja kontrolki dostarczona MCSF w zupełności wystarcza i nie powoduje większych problemów. Jednakże trzeba mieć na uwadze, że w przypadku jakichkolwiek kłopotów, możemy mieć problem z otrzymaniem pomocy (ostatnie tematy z forum są datowane na rok 2006), i albo kupimy komercyjną wersję kontrolki, albo sami będziemy musieli zgłębić jej kod źródłowy.

Jak to wszystko działa? Otóż, przede wszystkim musimy dodać referencje do assembly:
Microsoft.Practices.Mobile.UI.OrientationAware.dll
i
Microsoft.WindowsMobile.Status.dll
lub tylko
Clarius.UI.OrientationAware.dll
w przypadku wersji komercyjnej. Kolejnym krokiem będzie utworzenie nowej UserControl i zmianie klasy z której ona dziedziczy na OrientationAwareControl.

 

Teraz możemy otworzyć naszą kontrolkę w designerze, właściwościami na które powinniśmy zwrócić uwagę to Orientation i Resolution. Jak nazwy wskazują Orientation określa jak nasze okno jest położone (vertical/horizontal) a Resolution pozwala nam wybrać rozdzielczość dla jakiej projektujemy nasz interfejs. Kontrolka dostarczona nam przez MS Pattern & Practises pozwala nam na wybór pomiędzy: 240×320 (QVGA), 480×640 (VGA), 240×240 (kwadratowa QVGA), 480×480 (kwadratowa VGA) co w większości przypadków jest wystarczające. Kontrolka dostarczona nam przez Clarius Consulting (www.orientationaware.net) – rozszerza ten zakres i pozwala na definiowanie własnych rozmiarów. Następnie na powierzchni naszej kontrolki umieszczamy resztę elementów, np.:

Gdy jesteśmy zadowoleni z wyglądu kontrolki pora utworzyć interfejs dla trybu landscape. W tym celu możemy zmienić właściwość Orientation z okna Properties bądź skorzystać z menu kontekstowego kontrolki i wybrać ‚Rotate‚. Teraz nie pozostaje nam nic innego jak dostosować wielkość kontrolki i odpowiednio rozmieścić elementy na niej.

Nie jesteśmy bynajmniej ograniczeni tylko do zmiany położenia elementów naszej kontrolki – możemy również zmieniać pozostałe właściwości jak np. ich rozmiary, właściwość Text czy Image. Ograniczeniem z jakim się spotykamy jest niemożliwość dodawania nowych elementów na widokach innych niż domyślny. Jeśli chcemy aby jakaś kontrolka była widoczna tylko w trybie landscape – musimy najpierw przejść do widoku domyślnego domyślnego, tzn. nie tylko położenia horizontal ale także domyślnej rozdzielczości, możemy tego dokonać poprzez wybranie opcji z menu kontekstowego kontrolki ‚Switch to Default Layout‚. Teraz dodajemy nową kontrolkę i ustawiamy jej właściwość Visible na false i zmieniamy ją w kodzie w momencie wykrycia zmiany orientacji ekranu. Właściwości Orientation Aware Control, dla wszystkich trybów wyświetlania, oprócz domyślnego są przechowywane jako zasoby. Dlatego też, jeśli przyjrzymy się pozycji Resources naszej kontrolki zobaczymy coś takiego:

Pojedynczy plik określa właściwości jednego trybu wyświetlania i aby zmniejszyć rozmiar zasobów przechowuje tylko wartości które uległy zmianie z widoku domyślnego.

Problem jaki wiąże się z przechowywaniem właściwości w zewnętrznych zasobach jest to, że są one odczytywane przy każdej zmianie widoku i nie zachowują aktualnego stanu poszczególnych kontrolek. Weźmy na przykład aplikację korzystającą z OAC w którym znajduje się kontrolka TextBox, użytkownik wpisuje do niego dane i zmienia orientację ekranu – w tym momencie właściwości kontrolki OAC są odczytywane z zasobów i zawartość naszej kontrolki zostaje zastąpiona domyślną wartością. Dlatego też musimy sami zadbać o przechowywanie zawartości elementów naszej OAC.

Na koniec kilka szczegółów o których powinniśmy wiedzieć:

  • Forma na której zamierzamy umieścić naszą kontrolkę pochodzącą od OAC powinna mieć ustawioną właściwość AutoScaleMode na None, gdyż skalowanie odbywa się trakcie projektowania i prawidłowe rozmiary są zapisane w zasobach.
  • Gdy projektujemy interfejs dla trybu VGA, wszystkie kontrolki są dwa razy większe niż zazwyczaj. Nie jest to błąd – Visual Studio 2005 (i zapewne VS 2008 też, ale nie testowałem tego) odwzorowuje kontrolki w ich rzeczywistych rozmiarach a ponieważ VGA jest dwa razy lepsza od QVGA na ekranach naszych monitorów otrzymujemy nieco większe niż przywykliśmy. Niestety taka jest specyfika Visual Studio i nie jesteśmy wstanie nic z tym zrobić.

Dodatkowych informacji można szukać na stronach Clarius Consulting bądź w udostępnionym przez Microsoft webcast’cie ‚Designing Zero-Code Adaptive UIs Using the Orientation Aware Control’. Źródła przykładowej aplikacji jest dostępne na tutaj.

Reklamy

Written by sakowicz

Styczeń 30, 2008 at 9:40 pm

Napisane w .NET CF, Devices

Nowe ‚mobilne’ screencast’y

leave a comment »

Written by sakowicz

Styczeń 24, 2008 at 9:29 am

Napisane w .NET CF, Devices, Screencasts

MEDC odwołane :(

4 Komentarze

Microsoft oficjalnie potwierdził, że w tym roku konferencja Mobile and Embedded Developers Conference, w skrócie MEDC, się nie odbędzie, a prezentowane na niej zagadnienia zostaną zawarte w ramach konferencji TechEd. Przynajmniej tak ma się to odbyć w Stanach, co do wydarzenia Europejskiego należy podejrzewać, że przebiegnie ono podobnie, (chociaż jak to zwykle bywa na nieco mniejszą skalę).
Tą wiadomość mogę skomentować jednym słowem: szkoda! Generalnie zauważyłem, że im większa konferencja – to, w tym większej ilości interesujących prezentacji, nie mogę uczestniczyć, gdyż akurat w tym samym czasie a innym miejscu odbywa się coś ciekawszego. Z oczywistych względów nie lubię dokonywać takich wyborów. Również trudniej jest nawiązać kontakty z innymi developerami z tej samej dziedziny, a podczas wieczornych dyskusji przy piwie i zakąskach jesteśmy zagrożeni wciągnięciem w sam wir bitwy o wyższość C# nad Visual Basic’em czy na odwrót – zamiast np. dyskutować o możliwościach oferowanych przez MS Synchronization Services pod kątem urządzeń przenośnych.
Istnieje jeszcze inna obawa, Microsoft, w mojej opinii, nie przykłada się do tworzenia rozwiązań mobilnych. Fakt robi zdecydowanie więcej niż jeszcze jakiś czas temu, ale imho to nie wystarczy, aby zapewnić mu pozycję niekwestowanego lidera. Zresztą nie tyle ilość, co jakość się liczy – a i tu chłopaki z Redmond się nie popisują. Na poparcie moich słów wspomnę tylko system Windows Mobile 6 – który w zapowiedziach miał być kamieniem milowym oprogramowania urządzeń mobilnych, a w efekcie powstał system na bazie wcześniejszego usuwający, co poniektóre jego bolączki, ale już z tym samym, mało wygodnym interfejsem i tymi samymi ograniczeniami architektonicznymi, co jego protoplasta.
A pamiętać trzeba, że konkurencja nie śpi – Apple przygotowuje programistyczne SDK dla iPhone’a (te chłopaki to dopiero zrobili dobrą robotę projektując UI), Google wypuścił swojego Androida i jest on implementowany w pierwszych urządzeniach, (o czym możemy przeczytać tutaj).
Ja rozumiem, że Microsoft celuje głównie w klientów korporacyjnych i wierzy w potęgę swojego zaplecza backend’owego jak i niskie ceny urządzeń oferowanych przez jego partnerów dla odbiorców biznesowych. Ale czy to wystarczy, ceny urządzeń miarowo idą w dół, przy towarzyszącym mi wzroście możliwości tych, że urządzeń a potęga zaplecza, wydaje się dość złudna, bo kto nas zapewni, że jutro ktoś nie zaoferuje jakiś nowych tańszych/efektywniejszych rozwiązań.

 

Written by sakowicz

Styczeń 21, 2008 at 10:56 am

Napisane w Misc

Kompatybilność i SQL Server Compact Edition

leave a comment »

Na blogu SQL Server Compact Team Blog pojawił się artykuł omawiający wzajemną kompatybilność poszczególnych wersji SQL Server Compact Edition. I tak np. można się dowiedzieć, że np. designer z Visual Studio 2008 będzie pracował tylko z wersją 3.5, co jest nieco zaskakujące bo po produkcie takim jak Visual Studio można by się spodziewać nieco lepszej kompatybilności wstecznej.

Written by sakowicz

Styczeń 10, 2008 at 11:32 am

Napisane w .NET CF, SQL

LINQ ciąg dalszy

8 Komentarzy

Tym razem napiszę o problemie, na jaki natknąłem się korzystając z LINQ, otóż załóżmy, że mamy kolekcję zawierającą jako elementy obiekty typu np. Item. Teraz wykonuję jakąś kwerendzie LINQ i jako rezultat zwracam kolekcję niektórych właściwości klasy Item. Następnie otrzymaną listę bindujemy, do powiedzmy, kontrolki ComboBox. Wykonujemy jakieś operacje etc. a następnie chcemy pobrać zaznaczony obiekt poprzez ComboBox.SelectedItem. I tu pojawia się problem na jaki typ rzutować? Dla zobrazowania przykładowy kod:

class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Value { get; set; }
    public double Scale { get; set; }

    // etc. jakieś inne właściwości

    public Item(int id, string name, int value, double scale)
    {
        Id = id;
        Name = name;
        Value = value;
        Scale = scale;
    }
}

List<Item> list = new List<Item>();
list.Add(new Item(1, "Item1", 101, 5.99));
...
list.Add(new Item(9, "Item9", 909, 7));

var query = from o in list
            where o.Id < 10 && o.Id > 4
            select new { o.Id, o.Name, o.Value };

cboTest.DataSource = query.ToList();
cboTest.DisplayMember = "Name";
cboTest.ValueMember = "Id";

Ponieważ w kwerendzie LINQ, zwracam typ generowany przez IL (tzw. anonymous type) nie mogę jawnie na niego rzutować, i tym samym zastosowanie typów object czy var nic mi nie daje (tj. dostępu do właściwości typu). Zawsze można skorzystać z mechanizmu refleksji, jednak nie jest to ścieżka którą chciałbym podążyć. Szukam raczej jakiegoś ‚schludnego’ podejścia do tego ograniczenia/ciekawostki. Jakieś pomysły?

Written by sakowicz

Styczeń 9, 2008 at 11:07 am

Napisane w .NET

.NET Compact Framework 3.5 Redistributable

leave a comment »

Microsoft udostępnił wersję redistributable .NET Compact Framework 3.5 – do pobrania tutaj.

Written by sakowicz

Styczeń 8, 2008 at 10:13 am

Napisane w .NET CF

Lambda Expression

leave a comment »

Aby zaktualizować interfejs naszej aplikacji z innego wątku musimy się uciec na przykład do metod Invoke/BeginInvoke. Korzystając z .NET 2.0 musimy przykładowo:

private delegate void UpdateStringValue(string value); 

UpdateStringValue SetLabelStatus = delegate(string value)
{
    tsStatusLabel.Text = value;
};

BeginInvoke(SetLabelStatus, new object[] { "Generating ..." });

Jeśli jednak skorzystamy z Lambda Expression jednego z dobrodziejstw najnowszego Frameworka .NET 3.5, wystarczy tylko jedna linia:

BeginInvoke(new Action(() => tsStatusLabel.Text = "Generating ..."));
To się nazywa postęp! 😉

Written by sakowicz

Styczeń 7, 2008 at 10:20 am

Napisane w .NET