Programowanie obiektowe i zdarzeniowe wykład 3 Okna i kontrolki <Window x:class="wpfapplication1.mainwindow"... Title="Okno" Width="300" Height="200"> <Grid> <Button Name="przycisk" Click="klik" Margin="3" Padding="10,5" HorizontalAlignment="Left" VerticalAlignment="Top"> Nie dotykać! </Button> </Grid> </Window> public partial class MainWindow : Window private void klik(object sender, RoutedEventArgs e) przycisk.content = "Bzzzt..."; Marek Tabędzki Programowanie obiektowe i zdarzeniowe 1/41
Content Controls kontrolki zawartości Mogą zawierać pojedynczy element. Ten sam mechanizm zagnieżdżania, który pozwalał układać nam elementy w kontenerach, służy do ustawienia zawartości kontrolki. <StackPanel> <Button Margin="3" Padding="5">Napis</Button> <Button Margin="3" Padding="5"> <Image Source="lolface.png" Stretch="None" /> </Button> <Button Margin="3" Padding="5"> <StackPanel> <Label Margin="3">Napis i obrazek</label> <Image Source="lolface.png" Stretch="None" /> </StackPanel> </Button> </StackPanel> Mają właściwość Content. Marek Tabędzki Programowanie obiektowe i zdarzeniowe 2/41
Marek Tabędzki Programowanie obiektowe i zdarzeniowe 3/41
uwaga: tylko część elementów umieszczanych na oknie to kontrolki (kontrolki to elementy z którymi użytkownik może wchodzić w interakcję) Podstawowe własności kontrolek: Dotyczące rozmieszczenia (Margin, Padding, HorizontalAlignment, VerticalContentAlignment, etc.) Marek Tabędzki Programowanie obiektowe i zdarzeniowe 4/41
Wybór czcionek: <WrapPanel Margin="3"> <Label FontFamily="Arial">Arial</Label> <Label FontFamily="Times New Roman">Times New Roman</Label> <Label FontFamily="Courier New">Courier New</Label> <Label FontFamily="Comic Sans MS">Comic Sans MS</Label> <Label FontSize="24">Size 24</Label> <Label FontSize="32">Size 36</Label> <Label FontStyle="Italic">Italic</Label> <Label FontWeight="Bold">Bold</Label> <Label FontStyle="Italic" FontWeight="Bold">Italic Bold</Label> </WrapPanel> Właściwości czcionek można ustawić dla okna, a wszystkie elementy je odziedziczą (o ile nie nadpiszą własnymi). Marek Tabędzki Programowanie obiektowe i zdarzeniowe 5/41
Wybór tła, kolorów, obramowania <WrapPanel Margin="2"> <Label Foreground="Red">Red</Label> <Label Foreground="BlueViolet">BlueViolet</Label> <Label Background="GreenYellow">GreenYellow</Label> <Label Background="SkyBlue">SkyBlue</Label> <Label Background="DarkGoldenrod" Foreground="Khaki">...</Label> <Label BorderBrush="Maroon" BorderThickness="1">Maroon 1</Label> <Label BorderBrush="Gold" BorderThickness="5">Gold 5</Label> </WrapPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 6/41
Widoczność: <WrapPanel Margin="2"> <Button>Pierwszy</Button> <Button Visibility="Hidden">Drugi</Button> <Button>Trzeci</Button> </WrapPanel> Przeźroczystość: <WrapPanel Background="GreenYellow"> <Button>Pełny</Button> <Button Opacity="0.5">Opacity</Button> <Button Background="#80FFFFFF">Kolor</Button> </WrapPanel> Dostępność wyszarzenie: <WrapPanel> <Button>Normalny</Button> <Button IsEnabled="False">Niedostępny</Button> </WrapPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 7/41
Przycisk Button <StackPanel> <DockPanel> <Label Margin="3">Podaj kod:</label> <TextBox Margin="3"></TextBox> </DockPanel> <WrapPanel HorizontalAlignment="Right"> <Button Margin="3" Padding="15,3" IsDefault="True" Click="OkClick">OK</Button> <Button Margin="3" Padding="15,3" IsCancel="True" Click="CancelClick">Anuluj</Button> </WrapPanel> </StackPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 8/41
private void CancelClick(object sender, RoutedEventArgs e) MessageBox.Show("Nacisnąłeś Anuluj"); private void OkClick(object sender, RoutedEventArgs e) MessageBox.Show("Nacisnąłeś OK"); Zdarzenie Click. Właściwości: o IsCancel (naciśnięcie Esc automatycznie go uruchomi) o IsDefault (ma specjalne graficzne oznaczenie, uruchomi go naciśnięcie Enter (o ile nie stoimy na innym przycisku)) o (ale zamknięciem okna musimy zająć się sami) Marek Tabędzki Programowanie obiektowe i zdarzeniowe 9/41
CheckBox i RadioButton są szczególnymi typami przycisków. CheckBox: właściwość IsChecked przyjmuje wartość true, false oraz null (stan niezdefiniowany) właściwość IsThreeState ustawiona na true oznacza, że użytkownik może wybrać stan niezdefiniowany <StackPanel> <CheckBox Margin="3" IsChecked="True">Tak</CheckBox> <CheckBox Margin="3" IsChecked="False">Nie</CheckBox> <CheckBox Margin="3" IsChecked="x:Null">Może?</CheckBox> </StackPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 10/41
RadioButton: wybór jednego z grupy (na podstawie zawierania elementów w kontenerze) możliwe definiowanie własnych grup (właściwość GroupName) <StackPanel> <RadioButton Margin="3" IsChecked="True">Opcja A</RadioButton> <RadioButton Margin="3">Opcja B</RadioButton> <RadioButton Margin="3">Opcja C</RadioButton> </StackPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 11/41
GroupBox Ramka z nagłówkiem, służy do grupowania innych kontrolek Zawiera pojedynczy element! <GroupBox Header="Wybierz opcję" Padding="5" Margin="5"> <StackPanel> <RadioButton Margin="3">Pierwsza</RadioButton> <RadioButton Margin="3">Druga</RadioButton> <RadioButton Margin="3">Trzecia</RadioButton> </StackPanel> </GroupBox> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 12/41
W nagłówku możemy umieścić również inne elementy. <GroupBox Padding="5" Margin="5"> <GroupBox.Header> <CheckBox Click="adresClick" Name="adresCheck" IsChecked="true"> Adres</CheckBox> </GroupBox.Header> <Grid Name="adresGrid"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label Margin="3">Imię i nazwisko:</label> <Label Margin="3" Grid.Row="1">Ulica:</Label> <Label Margin="3" Grid.Row="2">Kod i miejscowość:</label> <TextBox Margin="3" Grid.Column="1" /> <TextBox Margin="3" Grid.Column="1" Grid.Row="1"/> <TextBox Margin="3" Grid.Column="1" Grid.Row="2"/> </Grid> </GroupBox> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 13/41
private void adres(object sender, RoutedEventArgs e) if (adrescheck.ischecked == true) adresgrid.isenabled = true; else adresgrid.isenabled = false; Marek Tabędzki Programowanie obiektowe i zdarzeniowe 14/41
Przykład praktyczny: <StackPanel> <GroupBox Header="Wybierz dodatki" Padding="5" Margin="5"> <StackPanel Name="dodatki" CheckBox.Click="klik"> <CheckBox Margin="3" Tag="Pieczarki">Pieczarki</CheckBox> <CheckBox Margin="3" Tag="Salami">Salami</CheckBox> <CheckBox Margin="3" Tag="Oliwki">Oliwki</CheckBox> <CheckBox Margin="3" Tag="Ser">Ser</CheckBox> </StackPanel> </GroupBox> <Label Margin="3" Name="wybór"/> </StackPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 15/41
private void klik(object sender, RoutedEventArgs e) string str = "Wybrałeś: "; foreach (CheckBox cbx in dodatki.children) if(cbx.ischecked == true) str += cbx.tag + ", "; wybór.content = str; Marek Tabędzki Programowanie obiektowe i zdarzeniowe 16/41
Label etykieta To również jest kontrolka zawartości Pozwala na definiowanie mnemoników skrótów odsyłających do kontrolki <StackPanel> <CheckBox Margin="3">Wybór _pierwszy</checkbox> <CheckBox Margin="3">Wybór _drugi</checkbox> <CheckBox Margin="3">Wybór _trzeci</checkbox> <Label Target="pole" Margin="3">D_ane:</Label> <TextBox Name="pole" Margin="3"></TextBox> </StackPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 17/41
ToolTip podpowiedzi: Nie reagują na wejście użytkownika <StackPanel>... <Button ToolTip="Naciśnij mnie" Margin="3" Padding="3">... </Button>... </StackPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 18/41
Mogą być bardziej złożone:0 <Button Margin="3" Padding="3"> <Button.ToolTip> <StackPanel> <Label FontWeight="Bold" Background="Blue" Foreground="White"> Przycisk "Dwa" </Label> <TextBlock Padding="10" Width="200" TextWrapping="WrapWithOverflow"> Opis działania drugiego przycisku zajmuje parę linijek tekstu. </TextBlock> <Line Stroke="Black" StrokeThickness="1" X2="200"/> <Label FontWeight="Bold">F1 - dodatkowa pomoc.</label> </StackPanel> </Button.ToolTip> Dwa </Button> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 19/41
Marek Tabędzki Programowanie obiektowe i zdarzeniowe 20/41
Expander Ukrywa swoją zawartość, którą użytkownik może rozwinąć. IsExpanded sterujemy rozwinięciem/zwinięciem ExpandDirection kierunek rozwijania (domyślne Down lub Up, Left, Right) <StackPanel> <Expander Header="pierwsza">...</Expander> <Expander Header="druga"> <StackPanel>... </StackPanel> </Expander> <Expander Header="trzecia">...</Expander> </StackPanel> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 21/41
Kontrolki tekstowe: TextBox Wprowadzanie kilku linijek tekstu: AcceptsReturn ustawione na true pozwala na wprowadzanie Enterów AcceptsTab podobnie z Tabami TextWrapping (ustawiona na Wrap lub WrapWithOverflow) zawijanie zbyt długich wierszy (WrapWithOverflow pozwala wypływać poza kontrolkę zbyt długim liniom, Wrap zawsze łamie tekst) <TextBox TextWrapping="Wrap" Margin="3"> AlamakotaAlamakota Alamakota</TextBox> <TextBox TextWrapping="WrapWithOverflow" Margin="3"> AlamakotaAlamakota Alamakota</TextBox> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 22/41
Ponadto: MinLines i MaxLines pozwalają na ustawienie rozmiaru kontrolki aby była w stanie wyświetlić zadeklarowaną liczbę linii LineCount pozwala sprawdzić faktyczną liczbę linii VerticalScrollBarVisibility ustawiając na Visible lub Auto dodajemy pasek przewijania IsReadOnly tekst tylko do odczytu (ale pozwala wybrać go i skopiować w odróżnieniu od etykiety) Text zawartość tekstowa Focus() przejęcie fokusa <DockPanel> <Label DockPanel.Dock="Left">Dane</Label> <Button DockPanel.Dock="Right" Click="Clear">Wyczyść</Button> <TextBox Name="tekst"/> </DockPanel> private void Clear(object sender, RoutedEventArgs e) tekst.text = ""; tekst.focus(); Marek Tabędzki Programowanie obiektowe i zdarzeniowe 23/41
PasswordBox Password pozwala pobrać hasło (hasło przechowywane jest w pamięci w postaci zaszyfrowanej) <StackPanel> <DockPanel> <Label Margin="3">Hasło</Label> <PasswordBox Name="hasło" Margin="3"/> </DockPanel> <Button HorizontalAlignment="Center" Margin="3" Padding="15,3" Click="OkClick">OK</Button> </StackPanel> private void OkClick(object sender, RoutedEventArgs e) MessageBox.Show("Twoje hasło to " + hasło.password + "!"); Marek Tabędzki Programowanie obiektowe i zdarzeniowe 24/41
Range-Based Controls (ScrollBar, Slider, ProgressBar) Przyjmują pewną wartość z ustalonego zakresu Value (aktualna wartość) jej zmiana odpala ValueChanged Maximum i Minimum dolny i górny zakres Slider: Orientation w poziomie czy w pionie? TickPlacement None, TopLeft lub BottomRight TickFrequency odległość między tickami IsSnapToTickEnabled doklejanie do ticków <Slider Minimum="1" Value="15" Maximum="100" TickFrequency="10" TickPlacement="TopLeft"/> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 25/41
Ticks kolekcja położeń (jeśli np. mają być nieregularnie rozmieszczone) IsSelectionRangeEnabled wybór zakresu na ścieżce (użyteczne np. w media playerach) (przy pomocy SelectionStart i SelectionEnd) <Slider Margin="5" Value="1.5"/> <Slider Margin="5" Value="1.5" TickFrequency="1" TickPlacement="BottomRight"/> <Slider Margin="5" Value="1.5" Ticks="1,2,3,5,8" TickPlacement="BottomRight"/> <Slider Margin="5" Value="1.5" TickFrequency="2.5" TickPlacement="BottomRight" IsSelectionRangeEnabled="True" SelectionStart="3" SelectionEnd="7"/> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 26/41
Inny przykład: <StackPanel Margin="5"> <Slider Margin="5" Minimum="10" Maximum="50" Value="12" TickFrequency="5" TickPlacement="BottomRight" ValueChanged="slide"/> <Label HorizontalAlignment="Center"> To jest test slidera. </Label> </StackPanel> private void slide(object sender, RoutedPropertyChangedEventArgs<double> e) this.fontsize = e.newvalue; Marek Tabędzki Programowanie obiektowe i zdarzeniowe 27/41
ProgressBar Nie zapewnia interakcji, a jedynie wyświetla jakąś informację dla użytkownika. Pozwala też na stan nieokreślony (gdy nie wiemy ile czasu potrwa zdarzenie): <ProgressBar Value="50" Height="18"/> <ProgressBar Height="18" IsIndeterminate="True" /> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 28/41
Menu Mamy dostępne dwie kontrolki: Menu (menu aplikacji) i ContextMenu (menu kontekstowe). Menu: o Podlega zwyczajnym regułom rozmieszczania elementów (przeważnie jest na szczycie DockPanela lub w pierwszym wierszu Grida). o Można dodać dowolną liczbę menu. o Własność IsMainMenu ustawiona na true powoduje, że menu dostanie focusa po naciśnięciu klawisza Alt lub F10. ContextMenu: o Nie jest umieszczane w oknie, lecz ustawiane dla konkretnego elementu. MenuItem Menu zbudowane jest z elementów typu MenuItem oraz Separator. MenuItem to rozwijalne podmenu. 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ć). Marek Tabędzki Programowanie obiektowe i zdarzeniowe 29/41
<Menu DockPanel.Dock="Top"> <MenuItem Header="File"> <MenuItem Header="New"/> <MenuItem Header="Open"/ > <MenuItem Header="Save"/> <Separator></Separator> <MenuItem Header="Exit"/> </MenuItem> <MenuItem Header="Edit"> <MenuItem Header="Undo"/> <MenuItem Header="Redo"/> <Separator></Separator> <MenuItem Header="Cut"/> <MenuItem Header="Copy"/> <MenuItem Header="Paste"/> </MenuItem> </Menu> MenuItem reaguje na kliknięcie zdarzeniem Click. Wyświetla: tekst (Header), ikonę (Icon), znacznik wyboru (IsChecked wybranie dodatkowo IsCheckable kliknięcie zmienia stan wyboru), skrót klawiaturowy (InputGestureText to jest tylko tekstu skrótu, nie zapewnia to obsługi). Marek Tabędzki Programowanie obiektowe i zdarzeniowe 30/41
ToolBar i StatusBar <ToolBar DockPanel.Dock="Top"> <Button>New</Button> <Button>Open</Button> <Button>Save</Button> <Separator/> <Button>Cut</Button> <Button>Copy</Button> <Button>Paste</Button> </ToolBar> <StatusBar DockPanel.Dock="Bottom"> <Label>Ready</Label> </StatusBar> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 31/41
Items Controls kontrolki elementów Pozwalają na wyświetlanie listy wielu elementów. ListBox SelectionMode można ustawić na: Multiple (wybieramy kilka klikając na nich) lub Extended (klikając z Ctrl lub Shift) <ListBox SelectionMode="Multiple"> <ListBoxItem>jeden</ListBoxItem> <ListBoxItem>dwa</ListBoxItem> <ListBoxItem>trzy</ListBoxItem> <ListBoxItem>cztery</ListBoxItem> </ListBox> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 32/41
ComboBox działa podobnie jak ListBox, ale ukazuje jedynie jeden wybrany element <ComboBox IsEditable="True"> <ComboBoxItem>jeden</ComboBoxItem> <ComboBoxItem>dwa</ComboBoxItem> <ComboBoxItem>trzy</ComboBoxItem> <ComboBoxItem>cztery</ComboBoxItem> </ComboBox > Właściwość IsEditable ustawiona na true pozwala użytkownikowi wprowadzić własną wartość. Marek Tabędzki Programowanie obiektowe i zdarzeniowe 33/41
Programistyczny dostęp do listy elementów: <DockPanel Margin="3"> <ToolBar DockPanel.Dock="Top"> <Button Click="Dodaj">Dodaj</Button> <Button Click="Usuń">Usuń</Button> </ToolBar> <ListBox Margin="3" Name="lista"/> </DockPanel> private void Dodaj(object sender, RoutedEventArgs e) lista.items.add(new ListBoxItem Content="nowy element" ); private void Usuń(object sender, RoutedEventArgs e) if(lista.selectedindex >= 0) lista.items.removeat(lista.selectedindex); Marek Tabędzki Programowanie obiektowe i zdarzeniowe 34/41
Marek Tabędzki Programowanie obiektowe i zdarzeniowe 35/41
Powinniśmy pytać użytkownika o nazwę dodawanego elementu w tym celu stwórzmy drugie okno. <Window x:class="wpfapplication1.itemdialog"... Title="ItemDialog" Width="300" SizeToContent="Height"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label Margin="3">Nazwa:</Label> <TextBox Margin="3" Grid.Column="1" Name="nazwa"/> <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Right"> <Button IsDefault="True" Margin="3" Padding="15,3" Click="OkClick">OK</Button> <Button IsCancel="True" Margin="3" Padding="15,3">Anuluj</Button> </StackPanel> </Grid> </Window> Marek Tabędzki Programowanie obiektowe i zdarzeniowe 36/41
Jak wyświetlić okno użytkownikowi? Stworzyć okno i wywołać metodę ShowDialog(). Taki typ okna to okno modalne, blokuje ono dostęp do okna rodzica. public partial class MainWindow : Window private void Dodaj(object sender, RoutedEventArgs e) ItemDialog dlg = new ItemDialog(); dlg.showdialog(); // poniższy kod wykona się po zamknięciu okna dialogowego lista.items.add(new ListBoxItem Content = dlg.nazwa.text ); Marek Tabędzki Programowanie obiektowe i zdarzeniowe 37/41
Warto sprawdzić, czy okno zamknięto przy pomocy OK, czy Anuluj służy do tego DialogResult: public partial class ItemDialog : Window private void OkClick(object sender, RoutedEventArgs e) DialogResult = true; I teraz: public partial class MainWindow : Window private void Dodaj(object sender, RoutedEventArgs e) ItemDialog dlg = new ItemDialog(); if (true == dlg.showdialog()) lista.items.add(new ListBoxItem Content = dlg.nazwa.text ); Marek Tabędzki Programowanie obiektowe i zdarzeniowe 38/41
W podobny sposób możemy pytać przed usunięciem elementu ale tym razem wykorzystująca standardowe okno dialogowe: private void Usuń(object sender, RoutedEventArgs e) if (lista.selectedindex >= 0) if(messagebox.show( "Czy na pewno chcesz usunąć wskazany element?", "Usuń element", MessageBoxButton.YesNo, MessageBoxImage.Question)!= MessageBoxResult.Yes) return; lista.items.removeat(lista.selectedindex); Marek Tabędzki Programowanie obiektowe i zdarzeniowe 39/41
Dodajmy możliwość edycji elementów: <ToolBar DockPanel.Dock="Top">... <Button Click="Edytuj">Edytuj</Button> </ToolBar> Posłużymy się tym samym dialogiem: public partial class MainWindow : Window private void Edytuj(object sender, RoutedEventArgs e) if (lista.selectedindex >= 0) ItemDialog dlg = new ItemDialog(); dynamic it = lista.selecteditem; dlg.nazwa.text = it.content; if (true == dlg.showdialog()) it.content = dlg.nazwa.text; Marek Tabędzki Programowanie obiektowe i zdarzeniowe 40/41
Parę drobiazgów: <Window x:class="wpfapplication1.itemdialog"... Loaded="Window_Loaded"> public partial class ItemDialog : Window private void OkClick(object sender, RoutedEventArgs e) if (nazwa.text.length > 0) DialogResult = true; else MessageBox.Show("Podaj nazwę elementu.", "Edycja", MessageBoxButton.OK, MessageBoxImage.Warning); nazwa.focus(); private void Window_Loaded(object sender, RoutedEventArgs e) nazwa.selectionlength = nazwa.text.length; nazwa.focus(); Marek Tabędzki Programowanie obiektowe i zdarzeniowe 41/41