Windows Presentation Foundation WPF (2) Programowanie Wizualne Paweł Wojciechowski Instytut Informatyki, Politechniki Poznańskiej 2012
Model zdarzeń zdarzenia (ang. events), czyli wiadomości wysłane przez obiekty informujące o istotnych zmianach model zdarzeń WPF zmianie znaczenie zdarzeń.net - nazywa się on routed event w strukturze hierarchicznej który z komponentów powinien obsłużyć zdarzenie np. kliknięcia przyciskiem myszy? zdarzenie wędruje wzdłuż hierarchii, co pozwala obsłużyć je w najbardziej dogodnym miejscu w WPFie 4 dodano obsługę zdarzeń związanych z urządzeniami dotykowymi (multitouch)
Definiowanie zdarzenia Definiowanie zdarzenia wygląda podobnie do definiowania dependency properties private static readonly RoutedEvent MyEvent; rejestracja zdarzenia: MyEvent = EventManager.RegisterRoutedEvent( "MyEvent", //nazwa zdarzenia RoutingStrategy.Bubble, //rodzaj typeof( RoutedEventHandler), //delegat metody obsługi typeof(mainwindow)); //właściciel Wrapper: public event RoutedEventHandler My { add { base.addhandler(myevent, value);} remove { base.removehandler(myevent, value);} }
Definiowanie zdarzeń (2) Zdarzenie współdzielone pomiędzy klasami MyEvent = Mouse.MouseDownEvent.AddOwner(typeof(MainWindow)); Wywołanie zdarzenia RoutedEventArgs rea = new RoutedEventArgs(Mouse.MouseDownEvent, this); base.raiseevent(rea); spowoduje to wywołanie zdarzenia dla wszystkich metod zarejestrowanych metodą AddHandler(). konwencja jest taka, że pierwszym parametrem metody obsługi zdarzenia jest obiekt, który to zdarzenie wywołał, a drugim jest obiekt EventArgs z dodatkowymi informacjami. private void button1_click(object sender, RoutedEventArgs e) { }
Dodanie/usunięcie metody obsługi zdarzenia XAML <Button Name="button1" Click="button1_Click">Button 1</Button> Kod button1.click += new RoutedEventHandler(button1_Click); button1.click += button1_click; UIElement.AddHandler() button1.addhandler(buttonbase.clickevent, new RoutedEventHandler(button1_Click)); Usunięcie metody: button1.click -= button1_click; button1.removehandler(buttonbase.clickevent, new RoutedEventHandler(button1_Click));
Rodzaje zdarzeń bezpośrednie (direct events) trafiają do jednego obiektu i nie wędrują po hierarchii komponentów Click, MouseEnter bulgoczące (bubbling events) podróżują w górę hierarchii np. MouseDown tunelowane (tunneling events) podróżują w dół hierarchii np. PreviewMouseDown
Typy zdarzeń i ich działanie tunneling event bubbling event Window Grid Label Label Grid Image TextBox
Przykład zdarzenia bubbling <Window x:class="wpfapplication1.mainwindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:wpfapplication1" Title="MainWindow" Name="Window1" MouseDown="MouseDownHandler"> <Border Margin="5" Padding="5" Background="LightYellow" BorderBrush="SteelBlue BorderThickness="3,5,3,5" CornerRadius="3" MouseDown="MouseDownHandler" > <Grid MouseDown="MouseDownHandler">... <Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"> <Image Source="/WpfApplication1;component/Images/Chrysanthemum.jpg" MouseDown="MouseDownHandler"/> </Label> <ListBox Name="listBox" Grid.Row="1" Grid.Column="0" /> </Grid> </Border> </Window>
Klasa RoutedEventArgs metoda obsługi zdarzenia: private void MouseDownHandler(object sender, RoutedEventArgs rea) { string message = "Nadawca: " + sender.tostring() + "\t źródło: " + rea.source; listbox.items.add(message); } klasa RoutedEventArgs ma następujące właściwości: Source obiekt który wywołał zdarzenie (tutaj Image) OriginalSource najczęściej to samo co wyżej, ale ma zastosowanie w pewnych przypadkach RoutedEvent zdarzenie, które zostało wywołane Handled ustawienie wartości na true pozwala zatrzymać proces tunelowania lub (bulgotania ;))
Zdarzenia tunelowane Łatwo rozpoznawalne przedrostek Preview W WPF-ie oba rodzaje zdarzeń są łączone w pary MouseDown PreviewMouseDown obłużone zdarzenie tunelowe (Handled = true) powoduje brak wywołania zdarzenia bubbling! współdzielenie obiektu RoutedEventArgs
Obsługa obsłużonych zdarzeń ustawienie parametrów zdarzenia Handled na true powoduje zatrzymania procesu jego przekazywania Istnieje jednak możliwość wymuszenia obsługi również obsłużonych zdarzeń Window1.AddHandler(MouseDownEvent, new MouseButtonEventHandler(MouseDownHandler), true); pytanie: czy takie postępowanie ma sens?
Zdarzenia łączone (attached) <StackPanel Name="stackPanel" Button.Click="button1_Click"> <Button Name="button1">Button 1</Button> <Button Name="button2">Button 2</Button> <Button Name="button3">Button 3</Button> </StackPanel>
Zdarzenia zdefiniowane w WPFie Można wyróżnić następujące rodzaje zdarzeń: związane z cyklem życia obiektu (lifetime) zdarzenia wejściowe: myszy (mouse) klawiatury (keyboard) rysika (stylus) multitouch
Zdarzenia cylku obiektów (lifetime events) Initialized zostaje wywołane w momencie, gdy obiekt jest już zainicjalizowany i jego właściwości ustawione w XAML zostały nadane (ale nie style) Loaded zostaje wywołane, gdy całe okno zostało już zainicjalizowane oraz zostały zaaplikowane style i wiązania danych Unloaded wywołane gdy komponent zostaje zwolniony kolejność inicjalizowania obiektów jest od najbardziej wewnętrznego do zewnętrznych
Cykl życia obiektu klasy Window Dodatkowe zdarzenia dla okna: SourceInitialized wywoływane gdy uchwyt okna (HwndSource) zostaje ustawiony ContentRendered okno zostało wyrenderowane po raz pierwszy Activated gdy okno staje się aktywne (w wyniku przełączania między oknami) Deactivated analogicznie jak wyżej Closing wywoływane, gdy okno jest zamykane. Jest to ostatni moment, żeby anulować jego zamknięcie (CancelEventArgs.Cancel = true) Closed wywołane po tym, gdy okno jest już zamknięte (elementy są wciąż dostępne przed wywołaniem Unloaded)
Zdarzenia obsługi klawiatury Zdarzenia pojawiają się w następującej kolejności: PreviewKeyDown KeyDown PreviewTextInput TextInput PreviewKeyUp KeyUp Stosowanie niskopoziomowej obsługi zalecana jest tylko w specjalnych przypadkach. private void TextBox_KeyDown(object sender, KeyEventArgs e) { KeyConverter converter = new KeyConverter(); MessageBox.Show( "Key: " + converter.converttostring(e.key)); }
Klasa KeyEventArgs Właściwości: Handled Key IsDown sprawdzenie, czy klawisz Key jest wciśnięty IsUp IsRepeat IsToggled KeyStates KeyboardDevice: Klasa Keyboard IsKeyDown() IsKeyUp() IsKeyToggled() GetKeyStates()
Zdarzenia obsługi myszy Zdarzenia: MouseEnter MouseLeave MouseMove (PreviewMouseMove) (Preview)Mouse(Left Right)ButtonDown(Up) MouseWheel (PreviewMouseWheel) MouseDoubleClick (Control class) Click (Button class)
Klasa MouseButtonEventArgs i MouseWheelEventArgs Właściwości MouseButtonEventArgs: ButtonState stan przycisków myszy ClickCount liczba kliknięć ChangedButton który przycisk zmienił stan LeftButton MiddleButton RightButton Właściwości MouseWheelEventArgs: Delta wskazanie zmiany położenia kółka myszy
Zdarzenia myszy - przykłady private void Window1_MouseMove(object sender, MouseEventArgs e) { label1.content = e.getposition(button1).tostring(); } private void Window1_MouseDown(object sender, MouseButtonEventArgs e) { string message = "ChangedButton: " + e.changedbutton.tostring() + "\tbuttonstate: " + e.buttonstate.tostring() + "\tclickcount: " + e.clickcount.tostring(); Mouse.Capture(label1, CaptureMode.Element); } label1.content = message; private void Window1_MouseWheel(object sender, MouseWheelEventArgs e) { string message = "Delta: "+ e.delta; label1.content = message; }
Obsługa Drag&Drop Wyróżnia się: źródło danych i miejsce przeznaczenia Komponent będący miejscem przeznaczenia musi mieć ustawioną właściwość AllowDrop na true. aby rozpocząć operację należy wywołać metodę DragDrop.DoDragDrop(source, data, DragDropEffects) Poprawność danych można sprawdzić obsługując zdarzenie DragEnter Miejsce przeznaczenia powinno obsługiwać zdarzenie Drop
private void TextBox_Drop(object sender, DragEventArgs e) { TextBox txtbox = (TextBox)sender; txtbox.text = (string) e.data.getdata(dataformats.text); } if (e.data is System.Windows.DataObject && ((System.Windows.DataObject)e.Data).ContainsFileDropList()) { int numberoffiles = ((System.Windows.DataObject)e.Data).GetFileDropList().Count; foreach (string filepath in ((System.Windows.DataObject)e.Data).GetFileDropList()) { System.IO.FileInfo fi = new System.IO.FileInfo(filePath); if ((fi.attributes & System.IO.FileAttributes.Directory)!= System.IO.FileAttributes.Directory && (fi.attributes & System.IO.FileAttributes.System)!= System.IO.FileAttributes.System) { listbox.items.add(fi.name); } } } Drag&Drop - przykład <TextBox KeyDown="TextBox_KeyDown" AllowDrop="True" Drop="TextBox_Drop" DragEnter="TextBox_DragEnter"></TextBox> <Label Name="label1" MouseDown="label1_MouseDown"></Label> private void label1_mousedown(object sender, MouseButtonEventArgs e) { Label lbl = (Label) sender; DragDrop.DoDragDrop(lbl, lbl.content, DragDropEffects.Copy); }
Multitouch events Obsługa następujących zdarzeń: zwykłe dotknięcia: gesty (Preview)Touch(Down Up) (Preview)TouchMove TouchEnter/TouchLeave ManipulationStarting ManipulationStarted ManipulationDelta ManipulationCompleted wsparcie dla wybranych elementów
Elementy kontrolne
Elementy kontrolne w WPF-ie Wyróżnia się następujące elementy kontrolne: Zawierające zagnieżdżone elementy (Content controls) Label, Button, ToopTip Elementy z nagłówkiem i zawartością (Headered content controls) Expander, GroupBox, TabItem Służące do wprowadzania tekstu (Text controls) TextBox, PasswordBox, RichTextBox Prezentujące kolekcje obiektów (List controls) ListBox, ComboBox Sterowania wartościami/zakresem (Range-based controls) Slider, ProgressBar Wyboru daty (Date controls) Calendar, DatePicker Powyższa lista nie obejmuje wszystkich elementów (Menu, Toolbar, Tree, Grid)
Klasa Control Opisuje elementy interaktywne czyli mogą otrzymać focus i akceptują zdarzenia generowane przez urządzenia zewnętrzne Właściwości obiektów kontrolnych umożliwiają: ustawienie wyrównania zawartości obiektu ustawienie kolejności przechodzenia po komponentach(tab order) rysowanie tła (background), pierwszego planu (foreground) oraz ramki (border) formatowanie rozmiaru i kroju czcionki
Kolory ustawienie koloru button1.background = new SolidColorBrush(Colors.Red); button1.background = new SolidColorBrush(Color.FromArgb( 122, 255, 0, 0)); <Button Name="button1" Background="Red">Button 1</Button> <Button Name="button2">Button 2 <Button.Background> <SolidColorBrush Color="Red"/> </Button.Background> </Button> <Button Name="button3" Background="#FFFF0000">Button 3</Button>
Czcionki Do ustawienia czcionek w klasie Control służą następujące właściwości: FontFamily nazwa czcionki FontSize rozmiar FontStyle wartości FontStyle.Norml, FontStyle.Italic, FontStyle.Oblique FontWeight wartości Thin, ExtraLight, Light, Normal, Medium, SemiBold, Bold, ExtraBold, Black, ExtraBlack FontStretch wartości UltraCondensed, ExtraCondensed, Condensed, SemiCondensed, Normal, SemiExpanded, Expanded, ExtraExpanded, UltraExpanded ale to nie działa Czcionki są dziedziczone czyli ustawienie parametrów czcionki w komponencie jest przenoszone na jego dzieci.
Czcionki (2) wybór czcionek alternatywnych <Button Name="button2" FontFamily="Times New Roman, Arial, Comic Sans MS"> Button 2 </Button> brak okienka dialogowego wyboru czcionki osadzanie czcionek dodanie pliku do projektu (Build Action -> Resource) <Button Name="button3 FontFamily="./#Baroque Script">Button 3</Button>
Kursor myszy ustawienie właściwości Cursor klasy FrameworkElement Kursory zdefiniowane w klasie Cursors: AppStarting, Arrow, ArrowCD, Cross, Hand, Help, Ibeam, No, None, Pen, ScrollAll, ScrollE, ScrollN, ScrollNE, ScrollNS, ScrollNW, ScrollS, ScrollSE, ScrollSW, ScrollW, ScrollWE, SizeAll, SizeNESW, SizeNS, SizeNWSE, SizeWE, UpArrow, Wait Ustawienie zewnętrznego kursora dodanie pliku do projektu (Build Action -> Resource) StreamResourceInfo sri = Application.GetResourceStream( new Uri("Maliwan pistol reload.ani", UriKind.Relative)); Cursor cursor = new Cursor(sri.Stream); button2.cursor = cursor;
Klasa ContentControl Obiekty tej klasy mają właściwość Content, do której można przypisać dowolny obiekt: Dla obiektów dziedziczących po UIElement zawartość obiektu zostanie wyświetlona w zależności od rodzaju obiektu (dokładniej metodą UIElement.OnRender()) Dla pozostałych obiektów zostanie wyświetlony tekst pochodzący z wywołania metody ToString() Inne zagadnienia wyrównanie zawartości (alignment) Mnemoniki <Button Name="button3">Button _A</Button> <Label Name="label2" Target="{Binding ElementName=button2}">Wybierz _C</Label>
Przyciski (Buttons) Zachowanie przycisków opisuje klasa ButtonBase, a dziedziczą po niej: Button, CheckBox, RadioButton, GridViewColumnHeader, RepeatButton, ToggleButton Cechy klasy ButtonBase: zdarzenie Click Właściwość ClickMode: Release, Press, Hover Cechy klasy Button: IsCancel wartość true oznacza, że przycisk zostanie automatycznie wciśnięty, gdy zostanie naciśnięty klawisz ESC IsDefault wartość true oznacza, że przycisk jest domyślnym przyciskiem zostanie wywołany jeśli zostanie naciśnięty klawisz ENTER
Przyciski (2) CheckBox właściwość bool? IsChecked właściwość IsThreeStated zdarzenia: Checked, Unchecked, Indeterminate RadioButton właściwość GroupName Z reguły umieszczane w pojemniku GroupBox
Podpowiedzi (ToolTip) Właściwości: Placement wartość z typu wyliczeniowego PlacementMode (Horizontal Vertical)Offset PlacementTarget umiejscowienie podpowiedzi względem innego obiektu IsEnabled IsOpen Klasa ToolTipService: Umożliwia sterowanie pewnymi właściwościami podpowiedzi Właściwości: Klasa PopUp InitalShowDelay czas po którym podpowiedź się pojawi ShowDuration czas wyświetlania BetweenShowDelay czas w którym użytkownik może się przemieszczać między podpowiedziami, bez konieczności oczekiwania
Obiekty kontrolne z nagłówkiem
Klasa HeaderedContentControl Elementy kontrolne, które mogą zawierać pojedynczy element, ale dodatkowo mają nagłówek Zaliczamy do nich następujące komponenty: GroupBox TabItem Expander
GroupBox <GroupBox Header="GroupBox Test" Margin="3"> <StackPanel > <Button>Button 1</Button> <RadioButton>Radio button 1</RadioButton> <RadioButton>Radio button 2</RadioButton> <RadioButton Margin="3">Radio button 3</RadioButton> </StackPanel> </GroupBox>
TabItem, TabControl <TabControl> <TabItem Header="Tab 1"> <StackPanel > <Button>Button 1</Button> <RadioButton>Radio button 1</RadioButton> <RadioButton>Radio button 2</RadioButton> <RadioButton Margin="3">Radio button 3</RadioButton> </StackPanel> </TabItem> <TabItem Header="Tab 2"></TabItem> </TabControl>
Expander <StackPanel Orientation="Horizontal"> <Expander > <Expander.Header> <StackPanel Orientation="Horizontal"> <Image Source="/WpfApplication2;component/Images/Chrysanthemum.jpg Width="30" Height="30"></Image> <Label>Beautiful header</label> </StackPanel> </Expander.Header> <StackPanel > <Button>Button 1</Button> <RadioButton>Radio button 1</RadioButton> <RadioButton>Radio button 2</RadioButton> <RadioButton Margin="3">Radio button 3</RadioButton> </StackPanel> </Expander> <Expander ExpandDirection="Left" Header="Expander 2"> <TextBlock TextWrapping="WrapWithOverflow" MaxWidth="200"> </TextBlock> </Expander> </StackPanel>
Komponenty wyświetlające tekst
Elementy wyświetlające tekst TextBox, RichTextBox, PasswordBox Każde należy rozpatrywać oddzielnie: TextBox przechowuje obiekt klasy string (właściwość Text) PasswordBox przechowuje obiekt klasy SecureString (właściwość Password) RichTextBox przechowuje obiekt klasy FlowDocument
Klasa TextBox domyślnie jedna linia tekstu właściwości MinLines i MaxLines określają minimalną (maksymalną) liczbę linii wyświetlanego tekstu właściwość LineCount mówi o tym ile linii tekstu rzeczywiście znajduje się w komponencie ograniczenie rozmiaru tekstu: MaxLength właściwości Vertical(Horizontal)ScrollBarVisibility Właściwości AcceptReturn i AcceptTab Metody LineUp(), LineDown(), PageUp(), PageDown(), ScrollToHome(), ScrollToEnd() IsReadOnly zaznaczenie tekstu poprzez właściwości: SelectionStart, SelectionLength, SelectedText, AutoWordSelection. Metody: Undo() (CanUndo) sprawdzanie pisowni SpellCheck.IsEnabled
Komponenty dla list obiektów
ListBox <ListBox> <StackPanel Orientation="Horizontal"> <Label>1.</Label> <Label>First element</label> </StackPanel> <StackPanel Orientation="Horizontal"> <Label>2.</Label> <Label>Second element</label> </StackPanel> <StackPanel Orientation="Horizontal"> <Label>3.</Label> <Label>Third element</label> </StackPanel> </ListBox>
ListBox (2) Kolekcja elementów Items Wybór wielu elementów: właściwość SelectionMode i SelectedItems Single pojedynczy element Multiple pozwala na zaznaczanie/odznaczanie elementów Extended pozwala na zaznaczanie/odznaczanie elementów oraz zaznaczenie pewnego zakresu elementów listy z klawiszem Shift Obsługa zdarzenia ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) Właściwości SelectionChangedEventArgs: AddedItems RemovedItems
Komponenty sterowania zakresami/wartościami
Range-Based Controls Komponenty dziedziczące po klasie RangeBase mają następujące właściwości: Value bieżąca wartość Maximum maksymalna wartość Minimum minimalna wartość Klasa ProgressBar jej wysokość (Height) jest ustawiona na 4 trzeba to ręcznie ustawić właściwość IsIndeterminate pozwala pokazać trwający proces, gdy nie wiadomo ile jeszcze czasu/operacji zostało do końca
Klasa Slider Właściwości: Orientation Delay/Interval czas po którym suwak przesunie się przy wciśniętym przycisku TickPlacement TickFrequency co jaki przyrost pojawia się kreska Ticks kolekcja umożliwiająca ustawienie kresek w różnych odstępach IsSnapToTickEnabled wartość true mówi, że suwak będzie się przesuwał wyłącznie po kreskach IsSelectionRangeEnabled true mówi o tym, że można zaznaczyć pewien zakres przez ustawienie SelectionStart i SelectionEnd. SmallChange wartość o którą zostanie zmieniona wartość Value przy małym kroku LargeChange analogicznie j.w.
Komponenty wyboru daty
Wybór daty Komponenty DatePicker i Calendar DisplayDateStart(End) zakres wyświetlanych dat BlackoutDates kolekcja dat, których nie można wybrać SelectedDate(s) FirstDayOfWeek DatePicker: IsDropDownOpen SelectedDateFormat Calendar: DisplayMode Decade, Year, Month SelectionMode None, SingleDate, SingleRange, MultipleRange