Programowanie w technologii.net wykład 8 Style, listy, drzewa, toolbary, menu

Podobne dokumenty
Modele zawartości. WPF wykorzystuje 4 modele zawartości kontrolek: ContentControl pojedyncza zawartość

Programownie w technologii.net wykład 6 Element Binding i Data Binding

Rozwój aplikacji modułowych Paweł Brudnicki. Dodanie modułu

Tworzenie prezentacji w MS PowerPoint

Budowa aplikacji w technologii.net wykład 7 konwersja, walidacja, szablony, widoki

Programowanie obiektowe i zdarzeniowe wykład 3 Okna i kontrolki

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Przed rozpoczęciem pracy otwórz nowy plik (Ctrl +N) wykorzystując szablon acadiso.dwt

1. Dockbar, CMS + wyszukiwarka aplikacji Dodawanie portletów Widok zawartości stron... 3

Informatyka II. Laboratorium Aplikacja okienkowa

Dodanie nowej formy do projektu polega na:

xmlns:prism= c. <ContentControl prism:regionmanager.regionname="mainregion" />

Programowanie obiektowe i zdarzeniowe wykład 1 Wprowadzenie do programowania zdarzeniowego

Zdarzenia i polecenia

Adobe InDesign lab.1 Jacek Wiślicki, Paweł Kośla. Spis treści: 1 Podstawy pracy z aplikacją Układ strony... 2.

Kurs programowania 2 - listy

Przewodnik krok po kroku:

Zadanie 1. Stosowanie stylów

Informatyka Edytor tekstów Word 2010 dla WINDOWS cz.3

Informatyka Edytor tekstów Word 2010 dla WINDOWS cz.3

Rys. 3. Kod elementów na stronie po dodaniu kontrolek podstawowych.

DODAJEMY TREŚĆ DO STRONY

Aby nadać jej pożądaną postać należy w pliku Window1.xaml stworzyć definicję swojego stylu modyfikując ręcznie postać zapisu XAML:

MVVM i XAML w Visual Studio 2015 / Jacek Matulewski. Gliwice, cop Spis treści

Moduł rozliczeń w WinUcz (od wersji 18.40)

Wykład III. dr Artur Bartoszewski Wydział Nauczycielski, Kierunek Pedagogika Wprowadzenie do baz danych

1. Umieść kursor w miejscu, w którym ma być wprowadzony ozdobny napis. 2. Na karcie Wstawianie w grupie Tekst kliknij przycisk WordArt.

Tworzenie bazy danych na przykładzie Access

Plan. Raport. Tworzenie raportu z kreatora (1/3)

6.4. Efekty specjalne

Programowanie w środowisku graficznym GUI

Informatyzacja Przedsiębiorstw

Lokalizacja jest to położenie geograficzne zajmowane przez aparat. Miejsce, w którym zainstalowane jest to urządzenie.

Leszek Stasiak Zastosowanie technologii LINQ w

Politechnika Poznańska Wydział Budowy Maszyn i Zarządzania

Podręczna pomoc Microsoft Power Point 2007

Dodawanie grafiki i obiektów

TabControl kontrolka odpowiedzialna za wyświetlenie zestawu zakładek. PageControl podobnie jak TabControl ale posiada wbudowane strony.

Budowa aplikacji ASP.NET współpracującej z bazą dany do przeprowadzania ankiet internetowych

Oficyna Wydawnicza UNIMEX ebook z zabezpieczeniami DRM

Budowa aplikacji ASP.NET współpracującej z bazą dany do obsługi przesyłania wiadomości

DARMOWA PRZEGLĄDARKA MODELI IFC

Informatyzacja Przedsiębiorstw

WinSkład / WinUcz 15.00

Utworzenie aplikacji mobilnej Po uruchomieniu Visual Studio pokazuje się ekran powitalny. Po lewej stronie odnośniki do otworzenia lub stworzenia

WYKONANIE APLIKACJI OKIENKOWEJ OBLICZAJĄCEJ SUMĘ DWÓCH LICZB W ŚRODOWISKU PROGRAMISTYCZNYM. NetBeans. Wykonał: Jacek Ventzke informatyka sem.

Klawisze funkcyjne w OpenOffice.org Writer

Przypisy i przypisy końcowe

ECDL/ICDL Przetwarzanie tekstów Moduł B3 Sylabus - wersja 5.0

Temat: Organizacja skoroszytów i arkuszy

Podstawowe elementy GUI cz. 2 i 3 - zadania

Programowanie w Javie

1. Wprowadzenie do WPF i XAML. Tworzenie interfejsu użytkownika.

etrader Pekao Podręcznik użytkownika Strumieniowanie Excel

Edytor tekstu OpenOffice Writer Podstawy

UONET+ moduł Dziennik

Windows Presentation Foundation

- Narzędzie Windows Forms. - Przykładowe aplikacje. Wyższa Metody Szkoła programowania Techniczno Ekonomiczna 1 w Świdnicy

Budowa aplikacji ASP.NET współpracującej z bazą dany do przeprowadzania ankiet internetowych

UNIWERSYTET RZESZOWSKI KATEDRA INFORMATYKI

Przewodnik Szybki start

Arkusz kalkulacyjny EXCEL

I. Program II. Opis głównych funkcji programu... 19

9 Zakup [ Zakup ] Zakup

Arkusz strona zawierająca informacje. Dokumenty Excela są jakby skoroszytami podzielonymi na pojedyncze arkusze.

ECDL/ICDL Zaawansowane przetwarzanie tekstów Moduł A1 Sylabus, wersja 2.0

Formularze w programie Word

Spis treści. Warto zapamiętać...2. Podstawy...3

Laboratorium programowania urządzeń mobilnych

MS Access formularze

Aplikacje mobilne. Pliki zasobów, grafiki, menu, podpinanie zdarzeń. dr Tomasz Jach Instytut Informatyki, Uniwersytet Śląski

Makropolecenia w Excelu

BAZY DANYCH MAKRA I PRZYCISKI. Microsoft Access. Adrian Horzyk. Akademia Górniczo-Hutnicza

TWORZENIE PREZENTACJI MS POWERPOINT

Komputery I (2) Panel sterowania:

Adobe InDesign lab. 2 Jacek Wiślicki, Paweł Kośla. Spis treści: 1 Dokument wielostronicowy Książka Eksport do PDF... 7.

Platforma VULCAN. Jak rozpocząć pracę na Platformie VULCAN? Logowanie administratora do Platformy

Zadanie 9. Projektowanie stron dokumentu

Java: otwórz okienko. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

PRZEWODNIK PO ETRADER ROZDZIAŁ XII. ALERTY SPIS TREŚCI

Kolory elementów. Kolory elementów

EXCEL TABELE PRZESTAWNE

BAZY DANYCH Formularze i raporty

ECDL/ICDL Przetwarzanie tekstów Moduł B3 Sylabus - wersja 6.0

I. Spis treści I. Spis treści... 2 II. Kreator szablonów Tworzenie szablonu Menu... 4 a. Opis ikon Dodanie nowego elementu...

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Formatowanie tekstu za pomocą zdefiniowanych stylów. Włączanie okna stylów. 1. zaznaczyć tekst, który chcemy formatować

Sylabus Moduł 4: Grafika menedżerska i prezentacyjna

Formatowanie c.d. Wyświetlanie formatowania

Kadry Optivum, Płace Optivum

Compas 2026 Vision Instrukcja obsługi do wersji 1.07

SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE

Dane w poniższej tabeli przedstawiają sprzedaż w dolarach i sztukach oraz marżę wyrażoną w dolarach dla:

Backend Administratora

Formatowanie komórek

ECDL/ICDL Grafika menedżerska i prezentacyjna Moduł S2 Sylabus - wersja 5.0

Optimed24 Instrukcja ogólna

4. Budowa prostych formularzy, stany sesji, tworzenie przycisków

Transkrypt:

Programowanie w technologii.net wykład 8 Style, listy, drzewa, toolbary, menu 1/70 Podstawy Chcąc wielokrotnie wykorzystać pewne ustawienia stylu, może zdefiniować je w zasobach: <Window.Resources> <FontWeight x:key="buttonfontweight"> Bold </FontWeight> <SolidColorBrush x:key="buttonbackground"> Orange </SolidColorBrush> </Window.Resources>

A następnie z nich skorzystać, ustawiając własności elementów: <Button Background="{StaticResource ButtonBackground}" FontWeight="{StaticResource ButtonFontWeight}"> OK </Button> <Button Background="{StaticResource ButtonBackground}" FontWeight="{StaticResource ButtonFontWeight}"> Anuluj </Button> Problemy: Nic (poza nazwą) nie wskazuje, że te zasoby są powiązane i że powinniśmy używać ich łącznie. Korzystanie z takich zasobów wymaga dodatkowego kodu XAMLa, przez co staje się nieczytelne. Lepsze rozwiązanie, to zdefiniowanie Stylu pojedynczy Style będzie zawierał w sobie wszystkie interesujące nas własności. 2/70

3/70 <Window.Resources> <Style x:key="orangebutton"> <Setter Property="Control.Background" Value="Orange"/> <Setter Property="Control.FontWeight" Value="Bold"/> </Style> </Window.Resources> Tworzymy obiekt klasy System.Windows.Style. Zawiera on kolekcję Setterów jeden na własność, której wartość chcemy ustawić. Setter określa nazwę własności i wartość, jaką ma jej nadać. Każdy element WPF może używać jednego Stylu (lub żadnego) wybieramy go przez własność Style. <Button Style="{StaticResource OrangeButton}"> OK </Button> <Button Style="{StaticResource OrangeButton}"> Anuluj </Button> Możemy też ustawić to programistycznie: btnexit.style = (Style)this.FindResource("OrangeButton");

4/70 Styl ustawia bazowy wygląd elementu możemy to nadpisać, ustawiając daną własność w tym elemencie (przeważnie jednak, zamiast na tym polegać, będziemy definiować różne warianty stylów). Styl pozwala stworzyć grupę powiązanych ustawień i w łatwy sposób nadać je jakiemuś elementowi. Nie musimy się martwić, jakie własności on ustawia, ani wiedzieć, gdy się zmienia: <Window.Resources> <Style x:key="orangebutton"> <Setter Property="Control.Background" Value="Orange"/> <Setter Property="Control.FontWeight" Value="Bold"/> <Setter Property="Control.FontSize" Value="16"/> <Setter Property="FrameworkElement.Margin" Value="3"/> <Setter Property="Control.Padding" Value="10,3"/> </Style> </Window.Resources>

Własności klasy Style: Setters kolekcja Setterów i EventSetterów, ustawiających wartości własności i dołączających obsługę zdarzeń. Triggers pozwalają na automatyczną modyfikację stylu, np. w reakcji na zmianę jakiejś własności lub zdarzenie. Resources zasoby używane przez style dobrze jest definiować wewnątrz niego. BasedOn pozwala na dziedziczenie stylów. TargetType określa typ elementu, jakiego dotyczy styl; pozwala stworzyć styl, który będzie stosowany automatycznie do elementów określonego typu. 5/70

6/70 Tworzenie obiektu stylu Można je definiować w zasobach (na dowolnym poziomie: okna, aplikacji, kontenera, kontrolki). Można też ustawiać od razu w miejscu użycia: <Button> <Button.Style> <Style> <Setter.../> <Setter.../> </Style> </Button.Style> OK </Button> Wydaje się to mało użyteczne w wypadku setterów, ale bywa przydatne z triggerami.

7/70 Setters Styl zawiera kolekcję obiektów klasy Setter. Każdy Setter ustawia pojedynczą własność (tylko zależnościową!) w elemencie. Aby ustawić złożoną wartość, możemy (jak zwykle) zastąpić atrybut zagnieżdżeniem: <Setter Property="Control.Background"> <Setter.Value> <LinearGradientBrush> <GradientStop Color="AliceBlue" Offset="0"/> <GradientStop Color="SteelBlue" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter>

8/70 Aby określić własność, którą chcemy ustawić, musimy podać zarówno jej nazwę jak i nazwę klasy, w której jest zawarta. Nie musi to być jednak nazwa klasy, w której jest ona zdefiniowana (może to być klasa potomna, która dziedziczy tę własność): <Style x:key="orangebutton"> <Setter Property="Button.FontWeight" Value="Bold"/> <Setter Property="Button.FontSize" Value="16"/> </Style> Działanie się nie zmieni, bo nadal odwołujemy się do tej samej własności. Pozwala to również ustawiać własności, które nie będą istnieć w elemencie używającym tego stylu.

9/70 Poniższy przykład nie ustawi innego stylu dla przycisku, a innego dla pola tekstowego. Własności będą ustawione dwukrotnie, zatem w efekcie otrzymamy wynik ostatniego ustawienia. Dzieje się tak, mimo iż FontSize jest deklarowane osobno w Button i TextBlock wskazują bowiem na tę samą własność zależnościową. <Style x:key="differentstyles"> <Setter Property="Button.Foreground" Value="Blue"/> <Setter Property="Button.FontSize" Value="18"/> <Setter Property="TextBox.Foreground" Value="Green"/> <Setter Property="TextBox.FontSize " Value="16"/> </Style> Jeśli wszystkie własności, które ustawiamy, dotyczą jednego typu elementów, możemy ustawić TargetType stylu: <Style x:key="orangebutton" TargetType="Button"> <Setter Property="Background" Value="Orange"/> <Setter Property="FontWeight" Value="Bold"/> </Style>

10/70 EventSetters Dowiązują do elementu obsługę zdarzeń. <Style x:key="orangebutton">... <EventSetter Event="UIElement.MouseEnter" Handler="element_MouseEnter"/> <EventSetter Event="UIElement.MouseLeave" Handler="element_MouseLeave"/> </Style> Metody obsługi zdarzeń: private void element_mouseenter(...) { ((UIElement)sender).FontStyle = FontStyles.Italic; } private void element_mouseleave(...) { ((Control)sender).ClearValue(Control.FontStyleProperty); }

Zdarzenia MouseEnter i MouseLeave nie wykorzystują tunneling i bubbling, dlatego nie moglibyśmy ustawić ich np. w kontenerze dla grupy elementów. EventSetter jest niezłym rozwiązaniem. EventSettery są rzadko wykorzystywane w WPF podobną funkcjonalność mogą zapewnić nam bowiem triggery. 11/70

12/70 Dziedziczenie stylów Możemy zdefiniować dowolną liczbę stylów na dowolnym poziomie. Ale jeden element może używać tylko jednego stylu. Z pomocą przychodzi dziedziczenie własności i dziedziczenie stylów. Niektóre własności są dziedziczone z kontenera (IsEnabled, IsVisible, Foreground, własności czcionek). Możemy też stworzyć nowy styl na podstawie innego (atrybut BasedOn): <Window.Resources> <Style x:key="orangebutton"> <Setter Property="Control.Background" Value="Orange"/> <Setter Property="Control.Margin " Value="3"/> <Setter Property="Control.Padding" Value="10,3"/> </Style> <Style x:key="emphasizedorangebutton" BasedOn="{StaticResource OrangeButton}"> <Setter Property="Control.FontWeight" Value="Bold"/> </Style> </Window.Resources>

Jeśli ponownie ustawimy tę samą własność, setter potomny nadpisze wartość z settera bazowego. Dziedziczenie stylów zwiększa złożoność, dlatego dobrze jest ograniczyć jego użycie do sytuacji gdy jeden styl jest pewnym wariantem drugiego. W pozostałych przypadkach lepiej jest tworzyć style z różnymi kombinacjami formatowania. 13/70

14/70 Automatyczne stosowanie stylów Aby automatycznie używać stylu do wszystkich elementów określonego typu, należy: ustawić własność TargetType, nie ustawiać klucza (WPF zrobi to za nas: x:key="{x:type Button}"). Styl będzie automatycznie używany przez wszystkie elementy określonego typu w dół drzewa elementów. <Window.Resources> <Style TargetType="Button"> <Setter Property="Background" Value="Orange"/> <Setter Property="FontWeight" Value="Bold"/> <Setter Property="Margin " Value="3"/> <Setter Property="Padding" Value="10,3"/> </Style> </Window.Resources> <WrapPanel> <Button>OK</Button> <Button Style="{x:Null}">Anuluj</Button> </WrapPanel>

15/70 Przycisk Anuluj zastępuje automatyczny styl swoim własnym ustawieniem (w tym wypadku nullem, co po prostu powoduje usunięcie stylu). W wypadku złożonych okien, kłopotliwe bywa śledzenie w jaki sposób ustawiana jest dana własność. Dlatego z automatycznych stylów lepiej korzystać oszczędnie, np. tylko aby ustawić jednakowy margines wszystkich Buttonów w kontenerze.

16/70 Triggers Pozwalają na automatyzację prostych zmian stylu (bez potrzeby pisania kodu) np. w reakcji na zdarzenie lub zmianę wartości. Każdy styl może mieć dowolną liczbę triggerów: obiektów klas dziedziczących z System.Windows.TriggerBase, zawartych w kolekcji Style.Triggers. Triggery mogą być użyte bezpośrednio w elementach, z pominięciem Stylu, dzięki kolekcji FrameworkElement.Triggers. Klasy dziedziczące z TriggerBase Trigger najprostszy z triggerów, reaguje na zmianę wartości własności zależnościowej. MultiTrigger jak wyżej, ale pozwala łączyć kilka warunków. DataTrigger podobny do Triggera, ale reaguje na zmianę w dowiązanych danych nei wymaga własności zależnościowej. MultiDataTrigger jak wyżej, ale dla kilku warunków. EventTrigger uruchamiają określone akcji w reakcji na zdarzenie, służą np. do wyświetlania animacji.

17/70 Trigger Może być dołączony do dowolnej własności zależnościwej (np. IsFocused, IsMouseOver, IsPressed). Określamy własność, która ma być obserwowana i wartość, jakiej oczekujemy. Gdy własność przyjmie tę wartość, uruchomione zostaną settery z kolekcji Trigger.Setters. Nie jest możliwe stworzenie bardziej złożonych reguł: tylko proste porównanie wartości. <Style TargetType="Button"> <Style.Setters> <Setter Property="Background" Value="Orange"/> <Setter Property="Margin " Value="3"/> <Setter Property="Padding" Value="10,3"/> </Style.Setters> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Foreground" Value="White"/> </Trigger> </Style.Triggers> </Style>

Kolor znaków zmieni się, gdy wskaźnik myszy znajduje się nad przyciskiem. Nie musimy deklarować powrotu do poprzedniej wartości : oryginalna wartość zostaje zapamiętana i własność do niej wraca, gdy trigger przestanie być aktywny. Możemy stworzyć kilka triggerów do jednego elementu. Jeśli ustawiają one tę samą własność, wygrywa zawsze ostatni na naszej liście (niezależnie od czasu zdarzeń). 18/70

19/70 <Style TargetType="Button"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Foreground" Value="White"/> </Trigger> <Trigger Property="IsFocused" Value="True"> <Setter Property="Foreground" Value="Yellow"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Foreground" Value="Magenta"/> </Trigger> </Style.Triggers> </Style> Gdy naciskamy przycisk, wygrywa IsPressed.

20/70 Triggery można wykorzystać do informowania o błędzie: Pola z wiązaniem danych (i walidacją!): <TextBox Text="{Binding Path=Imię, ValidatesOnDataErrors=True}"/> <TextBox Text="{Binding Path=Nazwisko, ValidatesOnDataErrors=True}"/> Trigger reaguje na Validation.HasError: <Style TargetType="{x:Type TextBox}"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="Background" Value="Red"/> <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/> </Trigger> </Style.Triggers> </Style>

21/70 MultiTrigger Kolekcja Conditions pozwala zdefiniować szereg oczekiwanych wartości własności. Trigger uruchamia się tylko wówczas, gdy wszystkie one są spełnione. <Style TargetType="Button"> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsMouseOver" Value="True"/> <Condition Property="IsFocused" Value="True"/> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Foreground" Value="White"/> </MultiTrigger.Setters> </MultiTrigger> </Style.Triggers> </Style> Kolor tekstu zostanie ustawiony tylko gdy przycisk ma focusa, a wskaźnik myszy znajduje się nad nim.

22/70 EventTrigger Oczekuje na odpalenie konkretnego zdarzenia i uruchamia przewidzianą akcję. Przeważnie służy to do włączania animacji. Przykład: <Style TargetType="Button">... <Style.Triggers> <EventTrigger RoutedEvent="Mouse.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="FontSize" To="22" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style>

Animacja zdefiniowana jest w Storyboardzie. Animacja sprowadza się do modyfikacji własności zależniościowych w określonym czasie. W przeciwieństwie do Triggerów, nie ma automatycznego powrotu i musimy zadeklarować to sami: <Style TargetType="Button">... <Style.Triggers>... <EventTrigger RoutedEvent="Mouse.MouseLeave"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:1" Storyboard.TargetProperty="FontSize" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> Tym razem jednak nie podajemy docelowego rozmiaru czcionki, WPF uznaje zatem, że wracamy do oryginalnej wartości. 23/70

24/70 DataTrigger Jak triggery, ale nie wymagają własności zależnościowych. <Window.Resources> <Style x:key="dangerstyle"> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="EX"> <Setter Property="Control.Background" Value="Orange"/> </DataTrigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel> <TextBox Margin="3" Style="{StaticResource DangerStyle}" /> </StackPanel>

25/70 Style w listach: przy pomocy ItemContainerStyle możemy wskazać styl, który będzie użyty do każdego elementu listy. <ListBox...> <ListBox.ItemContainerStyle> <Style> <Setter Property="ListBoxItem.Background" Value="LightSteelBlue" /> <Setter Property="ListBoxItem.Margin" Value="5" /> <Setter Property="ListBoxItem.Padding" Value="5" /> </Style> </ListBox.ItemContainerStyle> </ListBox>

26/70

27/70 Przy pomocy triggerów możemy formatować wygląd elementu wybranego: <ListBox.ItemContainerStyle> <Style TargetType="{x:Type ListBoxItem}">... <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter... /> <Setter... /> <Setter... /> </Trigger> </Style.Triggers> </Style> </ListBox.ItemContainerStyle>

28/70 Naprzemienne podświetlanie wierszy: AlternationCount i AlternationIndex. <ListBox AlternationCount="2"...> <ListBox.ItemContainerStyle> <Style TargetType="{x:Type ListBoxItem}"> <Setter Property="Background" Value="LightSteelBlue" /> <Setter Property="Margin" Value="5" /> <Setter Property="Padding" Value="5" /> <Style.Triggers> <Trigger Property="ItemsControl.AlternationIndex" Value="1"> <Setter Property="Background" Value="LightBlue" /> </Trigger> </Style.Triggers> </Style> </ListBox.ItemContainerStyle> </ListBox>

StyleSelector gdy chcemy nadać różny styl bazując na bardziej złożonych warunkach. Działa bardzo podobnie, jak DataTemplateSelector. 29/70

30/70 DataTriggery można wykorzystać również do warunkowego formatowania w szablonach danych: <DataTemplate x:key="defaultbooktemplate"> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=CategoryName}" Value="Horror"> <Setter Property="Control.Foreground" Value="Red" /> </DataTrigger> </DataTemplate.Triggers> <Border...> <Grid...>... </Grid> </Border> </DataTemplate>

Wady zmieniamy pojedyncze własności, testujemy tylko pojedyncze warunki. 31/70

DataTrigger pozwala utworzyć listę z elementami, które wyświetlają dodatkowe dane po wybraniu (a raczej ukrywają, gdy element nie jest wybrany). Należy sprawdzić wartość IsSelected elementu listy i ustawić wartość Visibility innego elementu. <DataTemplate x:key="defaultbooktemplate"> <Border..."> <Grid...> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <TextBlock... Text="{Binding Path=Title}" /> <TextBlock... Text="{Binding Path=Author}"> <TextBlock.Style> <Style>... </Style> </TextBlock.Style> </TextBlock> </Grid> </Border> </DataTemplate> 32/70

<Style> <Style.Triggers> <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="False"> <Setter Property="TextBlock.Visibility" Value="Collapsed" /> </DataTrigger> </Style.Triggers> </Style> 33/70

Kontrolki list 34/70

35/70 Własności klasy ItemsControl: ItemsSource źródło danych (kolekcja lub DataView) DisplayMemberPath wskazuje, którą własność elementu wyświetlić ItemTemplate szablon elementu ItemTemplateSelector klasa dokonująca wyboru szablonu elementu ItemContainerStyle styl kontenera otaczającego pojedynczy element ItemContainerStyleSelector ItemsPanel panel, który przechowuje wszystkie elementy listy (zazwyczaj: pionowy StackPanel) GroupStyle styl formatowania grupy GroupStyleSelector

Klasa Selector dodaje możliwość wybierania elementu (ToolBar czy Menu tego nie mają) własności: SelectedItem, SelectedIndex, SelectedValue (własność Value wybranego obiektu danych, zgodnie z SelectedValuePath) selector nie daje obsługi wielokrotnego wyboru to jest dodane w ListBox, przez SelectionMode i SelectedItems zdarzenie SelectionChanged TreeView też pozwala wybierać elementy, ale (z powodu hierarchicznej struktury) działa to inaczej, choć własności nazywają się tak samo: SelectedItem, SelectedValue, SelectedValue path properties. Do tego zdarzenie SelectedItemChanged. 36/70

37/70 ComboBox Składa się z dwóch części: okna wyboru (ukazuje aktualny wybór) i listy rozwijalnej (wszystkie możliwe wybory).

38/70 Ustawienie IsEditable na true zmienia działanie: w okienku wyboru wyświetlany jest jedynie tekst zwrócony przez ToString użytkownik może wpisać tu jakąś wartość autouzupełnianie <ComboBox IsEditable="True"... />

39/70 ListView Umożliwia wyświetlanie różnych widoków do tych samych danych. Zwłaszcza przydaje się do tworzenia widoku wielokolumnowego. Dziedziczy z ListBox, dodając jedną własność: View. Widok to obiekt dziedziczący z ViewBase klasy przechowującej wskazanie na style: styl kontrolki listy (DefaultStyleKey) i styl elementu listy (ItemContainerDefaultStyleKey). Wydzielenie informacji o widoku daje lepszą organizację, pozwala np. wykorzystywać ten sam widok w różnych listach lub używać zamienni kilku widoków w jednej liście. WPF dostarcza jeden gotowy widok: GridView. Mamy też możliwość tworzenia własnych.

40/70 Dodawanie kolumn: kolekcja GridView.Columns. Header to nagłówek kolumny, a DisplayMemberBinding wiązanie obiektem danych. <ListView Name="lstBooks" Margin="3"> <ListView.View> <GridView> <GridView.Columns> <GridViewColumn Header="Tytuł" DisplayMemberBinding="{Binding Path=Title}"/> <GridViewColumn Header="Autor" DisplayMemberBinding="{Binding Path=Author}"/> <GridViewColumn Header="Cena" DisplayMemberBinding="{Binding Path=Price}"/> </GridView.Columns> </GridView> </ListView.View> </ListView>

Oczywiście wszystkie rzeczy, które działały dla list (konwertery, szablony) mogą być tu stosowane. Użytkownik ma możliwość zmiany kolejności kolumn i zmiany ich rozmiaru (domyślnie rozmiar jest ustalany na podstawie zawartości, o ile nie określimy ręcznie własności Width). 41/70

42/70 Szablony komórek Podobnie jak szablony danych dla ListBoxa, ale działa tylko dla jednej kolumny. <GridViewColumn Header="Tytuł" Width="150"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Title}" TextWrapping="Wrap"></TextBlock> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> W ten sam sposób możemy umieścić w kolumnach dowolne elementy, np. obrazek z poprzedniego wykładu (ImagePathConverter). Ponieważ szablony kolumn nie mogą być używane w innym miejscu, przeważnie definiuje się je inline. GridViewColumn.CellTemplateSelector pozwala na ustawienie selectora szablonu. GridViewColumn.Header pozwala na zdefiniowanie wyglądu nagłówka; GridViewColumn.HeaderTemplate pozwala na wybór szablonu dla nagłówka lub GridView.ColumnHeaderTemplate na wybór jednego szablonu dla wszystkich.

43/70

44/70 DataGrid Oferuje wielokolumnowy widok z edycją. Przeznaczony do wyświetlania całych tabel danych. Podstawowe wykorzystanie jest bardzo proste: <DataGrid Name="data"/> I w kodzie: data.itemssource = books;

45/70 RowDetailsTemplate pozwala na prezentację dodatkowych informacji w wierszu: <DataGrid Name="data" SelectionMode="Single"> <DataGrid.RowDetailsTemplate> <DataTemplate> <TextBlock Margin="10" FontWeight="Bold" Text="{Binding Price}"/> </DataTemplate> </DataGrid.RowDetailsTemplate> </DataGrid> Wypełnienie kolekcji DataGrid.Columns pozwala na własne definicje kolumn: <DataGrid Name="data" SelectionMode="Single" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="Title" Binding="{Binding Title}"/> <DataGridTextColumn Header="Author" Binding="{Binding Author}"/> </DataGrid.Columns> </DataGrid>

46/70

47/70 TreeView Wspiera wiązanie danych. TreeView to specjalizowana kontrolka listy, która zawiera elementy TreeViewItem. TreeViewItem to nie kontrolka zawartości, ale kolejna kontrolka listy, która może zawierać kolejne elementy TreeViewItem. TreeViewItem dziedziczy z HeaderedItemsControl, która dodaje własność Header. Prosty przykład ręcznej definicji drzewa: <TreeView> <TreeViewItem Header="Filmy"> <TreeViewItem Header="Komedie"/> <TreeViewItem Header="Dramaty"/> <TreeViewItem Header="Seriale"/> </TreeViewItem> <TreeViewItem Header="Książki"> <TreeViewItem Header="Fantasy"/> <TreeViewItem Header="Science fiction"/> <TreeViewItem Header="Horror"/> </TreeViewItem> </TreeView>

Podobnie, jak w wypadku ListBox, nie musimy budować drzewa z elementów TreeViewItem. Jednak przeważnie najlepiej jest opakowywać swoje obiekty w TreeViewItem, a zawartość do wyświetlenia udostępniać przez TreeViewItem.Header. 48/70

49/70 TreeView i wiązanie danych Podobnie, jak wypadku pozostałych kontrolek listy, wystarczy ustawić własność ItemsSource. Wypełnia to jednak tylko pierwszy poziom drzewa: public class Category { public string Name { get; set; } public Category(string name) { Name = name; } } <Grid> <TreeView Name="drzewo" DisplayMemberPath="Name" /> </Grid>

50/70 private void Window_Loaded(object sender, RoutedEventArgs e) { List<Category> lst = new List<Category>(); lst.add(new Category("Fantasy")); lst.add(new Category("Science fiction")); lst.add(new Category("Horror")); } drzewo.itemssource = lst;

51/70 Dodajmy książki do kategorii: public class Category { public string Name { get; set; } public Category(string name) { Name = name; books = new List<Book>(); } private List<Book> books; public List<Book> Books { get { return books; } } }

52/70 private void Window_Loaded(object sender, RoutedEventArgs e) {... lst[0].books.add(new Book("Gra o tron", "George R.R. Martin", 45M)); lst[0].books.add(new Book("Straż nocna", "Terry Pratchett", 27.5M)); lst[0].books.add(new Book("Czarnoksiężnik z Archipelagu", "Ursula K. Le Guin", 19.9M));... } Aby wyświetlić zawartość kategorii, potrzebny jest szablon, który może obsłużyć dane hierarchiczne: <TreeView Name="drzewo"> <TreeView.ItemTemplate> <HierarchicalDataTemplate> <TextBlock Text="{Binding Path=Name}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>

53/70 To wyświetli tylko nazwę kategorii. HierarchicalDataTemplate może zawierać kolejny szablon a kolekcja elementów z pierwszego poziomu może być przekazana do kolejnego poziomu. Własność ItemsSource określa własność, która zawiera elementy dzieci. <TreeView Name="drzewo"> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Path=Books}"> <TextBlock Text="{Binding Path=Name}" /> <HierarchicalDataTemplate.ItemTemplate > <DataTemplate> <TextBlock Text="{Binding Path=Title}" /> </DataTemplate> </HierarchicalDataTemplate.ItemTemplate> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>

Drzewo posługuje się teraz dwoma osobnymi szablonami dla pierwszego i drugiego poziomu. Drugi szablon wykorzystuje wybrany element pierwszego, jako źródło danych. 54/70

55/70 Szablony możemy też przenieść do zasobów i wybierać na podstawie typów, a nie zagnieżdżenia: <Window.Resources> <HierarchicalDataTemplate DataType="{x:Type local:category}" ItemsSource="{Binding Path=Books}"> <TextBlock Text="{Binding Path=Name}"/> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="{x:Type local:book}"> <TextBlock Text="{Binding Path=Title}" /> </HierarchicalDataTemplate> </Window.Resources> <Grid> <TreeView Name="drzewo"> </TreeView> </Grid>

Pozwala to również na kolejne zagnieżdżenia elementów w drzewie. Np. tak: public class Category { public string Name { get; set; } public Category(string name) { Name = name; books = new List<object>(); } private List<object> books; public List<object> Books { get { return books; } } }... lst[1].books.add(new Category("podkategoria")); ((Category)lst[1].Books[3]).Books.Add(new Book(...)); ((Category)lst[1].Books[3]).Books.Add(new Book(...)); ((Category)lst[1].Books[3]).Books.Add(new Book(...)); 56/70

Tworzenie gałęzi na żądanie: Jeśli drzewo zawiera dużą ilość danych, możemy nie wypełniać od razu wszystkich podkategorii, a odwlec to do czasu rozwinięcia gałezi przez użytkownika. TreeViewItem odpala zdarzenia Expanded i Collapsed, które na o tym informują. Można to wykorzystać, by wypełnić listę danymi. Początkowo, każda kategoria powinna zawierać jakiś wypełniacz, aby wyświetlony został plus, który umożliwi jej rozwinięcie. W obsłudze zdarzenia Expanded możemy wczytać potrzebne dane i wypełnić nimi kategorię. 57/70

58/70 Menu Mamy dostępne dwie kontrolki: Menu (menu aplikacji) i ContextMenu (menu kontekstowe). Menu: Podlega zwyczajnym regułom rozmieszczania elementów (przeważnie jest na szczycie DockPanela lub w pierwszym wierszu Grida). Można dodać dowolną liczbę menu. Własność IsMainMenu ustawiona na true powoduje, że menu dostanie focusa po naciśnięciu klawisza Alt lub F10. Menu zachowało wszelkie cechy kontrolek listy (wiązanie danych, szablony danych, grupowanie, style itemów, etc.) MenuItem Menu składa się z obiektów klas MenuItem oraz Separator. Klasa MenuItem dziedziczy z HeaderedItemsControl. Separator to po prostu pozioma linia, nie reagująca na wejście użytkownika. Menu możemy (teoretycznie) wypełniać dowolnymi elementami (ale lepiej nie przesadzać).

<Menu DockPanel.Dock="Top"> <MenuItem Header="File"> <MenuItem Header="New"></MenuItem> <MenuItem Header="Open"></MenuItem> <MenuItem Header="Save"></MenuItem> <Separator></Separator> <MenuItem Header="Exit"></MenuItem> </MenuItem> <MenuItem Header="Edit"> <MenuItem Header="Undo"></MenuItem> <MenuItem Header="Redo"></MenuItem> <Separator></Separator> <MenuItem Header="Cut"></MenuItem> <MenuItem Header="Copy"></MenuItem> <MenuItem Header="Paste"></MenuItem> </MenuItem> </Menu> Można używać znaku podkreślenia, aby oznaczyć skrót z Altem. Reakcja na kliknięcie w element Menu: zdarzenie MenuItem.Click (osobno dla każdego elementu lub jeden handler w korzeniu), system poleceń (Commands) preferowane rozwiązanie. 59/70

60/70 MenuItem wyświetla: tekst z własności Header, ikonę: MenuItem.Icon (przyjmuje dowolny obiekt, co pozwala na dołączenie również grafiki wektorowej), checkmark: własność MenuItem.IsChecked; jeśli ustawimy dodatkowo IsCheckable, kliknięcie na menu zmieni ten stan (niestety, nie ma automatycznej obsługi grup), skrót klawiaturowy: można go wyświetlić podając MenuItem.InputGestureText (to jest tylko tekst, nie daje żadnej obsługi tu lepiej polegać na Commands, jeśli nie chcemy robić tego ręcznie). <MenuItem Command="Open"></MenuItem> MenuItem pozwala na sprawdzenie stanu: IsChecked, IsHighlighted, IsPressed, IsSubmenuOpen. Możemy wykorzystać to np. w triggerach.

61/70 ContextMenu Podobnie jak Menu, przechowuje kolekcję obiektów MenuItem. Jedyna różnica nie może być umieszczone w oknie. Służy do ustawienia własności ContextMenu innych elementów (pokazuje się na prawy przycisk myszy lub Shift+F10): <TreeView...> <TreeView.ContextMenu> <ContextMenu> <MenuItem Command="Undo"/> <MenuItem Command="Redo"/> </ContextMenu> </TreeView.ContextMenu> </TreeView>

Menu kontekstowe domyślnie nie pokazuje się, jeśli element jest wyszarzony (isenabled równe False), ale można to zmienić ustawiając ContextMenuService.ShowOnDisabled na True. 62/70

63/70 Separator Służy do dzielenia elementów na grupy. Zawartość separatora można zmieniać przy pomocy szablonów pozwala to definiować własne, nieklikalne elementy menu (nawet jeśli dodamy do menu wrzucimy element nie będący MenuItem, to będzie się zachowywał jak one; Separator nie np. nie jest podświetlany). <Separator> <Separator.Template> <ControlTemplate> <Border CornerRadius="4" Padding="5" Background="Black"> <TextBlock FontWeight="Bold"> Editing Commands </TextBlock> </Border> </ControlTemplate> </Separator.Template> </Separator>

64/70 Niestety, Separator to nie kontrolka zawartości, zatem nie można oddzielić zawartości od formatowania i zawartość musi siedzieć w szablonie. Toolbar i StatusBar Standardowo pasek narzędzi przechowuje przyciski, a pasek statusu tekst i kontrolki, z ktorymi użytkownik nie wchodzi w interakcję (np. pasek postępu). Nie ma elementów przeznaczonych specjalnie dla paska narzędzi czy statusu można w nim umieszczać wszelkie podstawowe elementy WPF.

ToolBar Przeważnie zawiera obiekty typu Button, ComboBox, CheckBox, RadioButton i Separator. Button w toolbarze wygląda inaczej niż w oknie, gdyż toolbar nadpisuje domyślny styl przycisku. Własność Orientation pozwala na stworzenie pionowego toolbara. <ToolBar DockPanel.Dock="Top"> <CheckBox FontWeight="Bold">Bold</CheckBox> <CheckBox FontStyle="Italic">Italic</CheckBox> <CheckBox> <TextBlock TextDecorations="Underline"> Underline </TextBlock> </CheckBox> <Separator></Separator> <ComboBox SelectedIndex="1"> <ComboBoxItem>120%</ComboBoxItem> <ComboBoxItem>100%</ComboBoxItem> <ComboBoxItem>80%</ComboBoxItem> </ComboBox> <Separator></Separator> </ToolBar> 65/70

Gdy wszystkie elementy paska narzędzi nie mieszczą się w oknie, zostają przeniesione do rozwijalnego menu. Elementy trafiają automatycznie, ale możemy wskazać (przy pomocy własności dołączonej ToolBar.OverflowMode), jakie elementy nie powinny tam trafić (OverflowMode.Never), a jakie powinny być tam zawsze (OverflowMode.Always) 66/70

ToolBarTray Przechowuje kolekcję toolbarów (własność ToolBars) i daje możliwość ich przemieszczania przez użytkownika (o ile nie ustawiliśmy ToolBarTray.IsLocked na true). ToolBar przy pomocy własności Band może określić swoje położenie w ToolBarTray. <ToolBarTray DockPanel.Dock="Top"> <ToolBar> <Button>One</Button> <Button>Two</Button> <Button>Three</Button> </ToolBar> <ToolBar> <Button>A</Button> <Button>B</Button> <Button>C</Button> </ToolBar> <ToolBar Band="1"> <Button>Red</Button> <Button>Blue</Button> <Button>Green</Button> <Button>Black</Button> </ToolBar> </ToolBarTray> 67/70

68/70

StatusBar Może przechowywać dowolną zawartość (która zostaje automatycznie opakowana przez StatusBarItem). Nadpisuje domyślne style niektórych elementów. Używany głównie do wyświetlania statycznej informacji. Domyślnie rozmieszcza elementy od lewej do prawej. Można podmienić mu panel, aby rozmieszczać w inny sposób. <StatusBar DockPanel.Dock="Bottom"> <StatusBar.ItemsPanel> <ItemsPanelTemplate> <Grid><Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions></Grid> </ItemsPanelTemplate> </StatusBar.ItemsPanel> <TextBlock>2/10</TextBlock> <StatusBarItem Grid.Column="1"> <TextBlock>120%</TextBlock> </StatusBarItem> </StatusBar> 69/70

70/70