Lab8 WPF Zad1 : - lista z językami i flagami + wiązanie danych z obiektu przerabiana na ComboBox - fisheye przegląd obrazków lupa (obrazki umieszczone na sztywno) - wyświetlenie galerii zdjęć zapisanej w pliku xml owym jako listy przewijanej poziomo, z wyświetlaniem opisu zdjęcia aktualnie wybranego z listy Postać: Kroki do wykonania: Etap1 - lista języków z flagami: 1. Zakładamy nowy projekt file->new Project->Visual C#-> WPF Application 2. W solution: utworzyć podkatalog flagi (Add->New Folder) i umieścić w nim pliki flaga_(x).jpg (Add->Existing Item) podobnie utworzyć podkatalog zdjecia i umieścić w nim pliki (1-10).jpg 3. Zdefiniujemy klasy Country, Countries jako źródła danych (język i ścieżka do pliku z flagą). Kod behind w Window1.xaml.cs powinien wyglądać następująco: namespace WPF_test public partial class Window1 : System.Windows.Window public Window1() InitializeComponent(); this.datacontext = Countries.GetCountries(); //jako źródło //lista z wartościami (nazwa kraju, scieżka flaga do pliku z flaga) 1
//od klasy Window1 public class Country public Country(string name, string imgpath) this.name = name; this.imgpath = imgpath; private string name; public string Name get return name; set name = value; private string imgpath; public string ImgPath get return imgpath; set imgpath = value; public override string ToString() return name; public static class Countries public static Country[] GetCountries() List<Country> panstwa = new List<Country>(); panstwa.add(new Country("PL", "flagi/flaga_pl.jpg")); panstwa.add(new Country("US", "flagi/flaga_us.jpg")); panstwa.add(new Country("KANADA", "flagi/flaga_kanada.jpg")); return panstwa.toarray(); 4. Modyfikacja interfejsu zawartego w pliku Window1.xaml: Dla istniejącego elementu Grid ustawić atrybuty: <Grid x:name="layoutroot" Background="Coral" TextBlock.TextAlignment="Left" > Jako element potomny wstawić z ToolBox a z grupy Controls element ListBox: <ListBox Name="wybor_jezyka" ItemsSource="Binding" Margin="0,2,59,0" Height="139" VerticalAlignment="Top" /> Properties ItemsSource="Binding" wiążącą kontrolkę do źródła danych należy dopisać ręcznie w kodzie. Po skompilowaniu i uruchomieniu otrzymamy zwykłą listę z wypisanymi językami: Aby nadać jej pożądaną postać należy w pliku Window1.xaml stworzyć definicję swojego stylu modyfikując ręcznie postać zapisu XAML: 2
<Window.Resources> //blok ten umieszczamy po elemencie <Window > i przed otwarciem głównego znacznika <Grid> <Style x:key="mojlistbox1" TargetType="x:Type ListBox"> <!-- ComboBox--> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*" SharedSizeGroup="MiddleColumn" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="60" /> </Grid.RowDefinitions> <TextBlock FontSize="16" VerticalAlignment="Center" Margin="5" FontStyle ="Italic" Grid.Column="0">Język:</TextBlock> <TextBlock FontSize="16" VerticalAlignment="Center" Margin="5" Text="Binding Name" FontWeight="DemiBold" Grid.Column="1" /> <Border Margin="4.0" Grid.Column="2" BorderThickness="2" CornerRadius="5" > <Border.BorderBrush> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <GradientStop Offset="0" Color="BlanchedAlmond" /> <GradientStop Offset="1" Color="#222" /> </LinearGradientBrush> </Border.BorderBrush> <Grid> <Rectangle> <Rectangle.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <GradientStop Offset="0" Color="#444" /> <GradientStop Offset="1" Color="#fff" /> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> <Image Width="48" Margin="2,2,2,1" Source="Binding ImgPath" /> </Grid> </Border> </Grid> </DataTemplate> </Setter.Value> </Setter> <Setter Property="Grid.IsSharedSizeScope" Value="True" /> </Style> </Window.Resources> Utworzony styl zastosowany do naszej listy, ale bez nałożonego obrazka flagi wygląda następująco: 5. Dla naszego elementu ListBox Name="wybor_jezyka" należy dodać atrybut przypisania stylu: Style="StaticResource mojlistbox1" 3
Otrzymamy listę w postaci: 6. Należy samodzielnie zmodyfikować kod tak aby listę zastąpić ComboBox em styl dalej ten sam (wymaga drobnej korekty gdzie?) spodziewany efekt: Etap2 - Wstawienie rybiego oka : W solution: dodać referencje do pliku FishEyeGrid.dll dodać definicje namespace w pliku window1.xaml (na samej górze tam gdzie reszta namespaców): xmlns:fisheyegrid="clr-namespace:fisheyegrid;assembly=fisheyegrid" Dodać do kodu behind w klasie Window1 ustawienie parametrów wyświetlania dla rybiego oka. private void HyperGrid_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e) MyHyperGrid.Zoom = 1.5; MyHyperGrid.Distribution = 0.25; MyHyperGrid.UseTransparency = false; Wstawić w pliku Window1.xaml w ramach głównego Grid poniżej ComboBox a <TextBlock Margin="5,161,39,0" Height="15" VerticalAlignment="Top">Oto moje zdjęcia:</textblock> <FishEyeGrid:HyperGrid x:name="myhypergrid" Margin="10,181,13,215" MouseEnter="HyperGrid_MouseEnter" Background="#00FFFFFF" Zoom="3.0" MouseLeaveResetDistribution="True" MouseLeaveResetCenter="True" MouseLeaveResetZoom="True" MouseLeaveResetTransparency="True" > <FishEyeGrid:HyperGrid.ColumnDefinitions> 4
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </FishEyeGrid:HyperGrid.ColumnDefinitions> <Image Source="/zdjecia/1.jpg" Grid.Column="0" Panel.ZIndex="1"/> <Image Source="/zdjecia/2.jpg" Grid.Column="1" Panel.ZIndex="1"/> <Image Source="/zdjecia/3.jpg" Grid.Column="2" Panel.ZIndex="1"/> <Image Source="/zdjecia/4.jpg" Grid.Column="3" Panel.ZIndex="1"/> <Image Source="/zdjecia/5.jpg" Grid.Column="4" Panel.ZIndex="1"/> </FishEyeGrid:HyperGrid> Metodą drag&drip ustaw rozmiary FishEyeGrid.HyperGrid o nazwie MyHyperGrid. Skompiluj projekt i zobacz efekt. Etap 3 - Wstawienie Listy ze zdjęciami z pliku XML, tytułu zdjęcia, i opisu aktualnie wybranego zdjęcia z listy zdjęć zaczytanych z pliku xml o nazwie galeria.xml 1. Dodać do projektu plik galeria.xml; 2. Dodać do <Window.Resources> zasób : <XmlDataProvider x:key="moja_galeria" Source="galeria.xml" XPath="galeria" /> 3. Dodać do zasobów definicję stylu listy poziomej jako tabeli(w górym wierszu obraz, w dolnym tytuł zdjęcia) z definicją wiązania danych z pliku xml <Style x:key="lista_poziom" TargetType="x:Type ListBox"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="x:Type ListBox"> <ScrollViewer HorizontalScrollBarVisibility="Auto"> <StackPanel Name="StackPanel1" IsItemsHost="True" Orientation="Horizontal" /> </ScrollViewer> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate > <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> 5
<Grid.RowDefinitions> <RowDefinition Height="80" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Image Source="Binding XPath=@src" Height="80" Grid.Column="0" Grid.Row="0" Margin="2,1,2,1" /> <Label Content="Binding XPath=tytul" Width="110" Height="30" VerticalAlignment="Center" TextBlock.TextAlignment="Center" Grid.Column="1" Grid.Row="1" </Grid> </DataTemplate> </Setter.Value> </Setter> </Style> HorizontalAlignment="Center"></Label> 4. W głównym gridzie okna po rybim oku dodać StackPanel wskazujący na wykorzystywany w nim obiekt moja_galeria jako źródło danych, w środku umieścić ListBox wyświetlany według zdefiniowanego szablonu lista_poziom oraz TextBox wyświetlający opis bieżącego elementu listy: <StackPanel x:name="szczeg2" DataContext="Binding Source=StaticResource moja_galeria, XPath=zdj " Margin="8,0,10,19" Height="195" VerticalAlignment="Bottom"> <ListBox Height="Auto" IsSynchronizedWithCurrentItem="True" Margin="5,5,5,5" ItemsSource="Binding" Style="StaticResource lista_poziom" /> <TextBox x:name="ogolny_opis" Text="Binding XPath=opis" FontSize="12" FontStyle="Italic" TextChanged="ogolny_opis_TextChanged" ScrollViewer.HorizontalScrollBarVisibility="Visible" /> </StackPanel> 5. dla TextBox a przypisano w klasie Window1 metodę ogolny_opis_textchanged która wymusza zapis zmodyfikowanych danych do pliku galeria.xml private void ogolny_opis_textchanged(object sender, System.Windows.Controls.TextChangedEventArgs e) XmlDataProvider k = ((XmlDataProvider)this.Resources["moja_galeria"]); k.document.save("..\\..\\galeria.xml"); Zaimmplementować następujące funkcjonalności: A. Opisy zdjęć prezentowanych w stackpanelu o nazwie szczeg2 prezentować zgodnie z językiem wybranym z listy/coboboxa wybor_jezyka potrzebne klasy: Binding, jej właściwość Xpath oraz dla elementu, dla którego zmieniamy wiązanie do źródła danych, metoda SetBinding (ustawia wiązanie wybranej właściwości np. TextBox.TextProperty z nowym źródłem danych utworzonym jako obiekt klasy Binding) B. Dodanie identyfikatora (atrybut id z pliku xml) ulubionego zdjęcia do pliku xml owego o nazwie ulubione.xml przy podwójmym kliknięciu myszą na ulubionym zdjęciu w obiekcie stackpanel o nazwie szczeg2. Jeżeli nie ma pliku tworzymy go i wypełniamy, 6
jeżeli jest dopisujemy kolejny element do pliku. Powienien on przybierać postać: <ulubione> <zdj>z1</zdj> <zdj>z6</zdj> </ulubione> W przypadku gdy wybrano zdjęcie, które jest już w pliku ulubione.xml nie należy go ponownie dodawać. Podać informację do użytkownika odpowiednio o dodaniu bądź nie. potrzebne klasy: XmlDocument, XmlElement, XmlNodeList, XmlNode, XmlText Zadanie 2 Utworzenie CustomControlLibrary (WPF) z animacją i wykorzystanie jej w zwykłej aplikacji windows owej. 1. Nowy projekt-> C#->Windows -> WPF User Control Library 2. Stworzyć kanwę z obrazkiem oraz dwa przyciski do uruchomienia I zatrzymania animacji ruchu oka. Uzupełnić xml ową definicję kontrolki o poniższy zapis (cały element DockPanel wstawiamy do elementu <UserControl> zamiast standardowo wstawionego elementu <Grid>. <DockPanel> <Grid DockPanel.Dock="Top"> <!-- Funny Face --> <Canvas Width="200" Height="200"> <Ellipse Canvas.Left="50" Canvas.Top="50" Width="100" Height="100" Stroke="Blue" StrokeThickness="4" Fill="Yellow" /> <Ellipse Canvas.Left="60" Canvas.Top="65" Width="25" Height="25" Stroke="Blue" StrokeThickness="3" Fill="White" /> <Ellipse Name="eye" Canvas.Left="67" Canvas.Top="72" Width="5" Height="5" Fill="Black" /> <Path Name="mouth" Stroke="Blue" StrokeThickness="4" Data="M 62,125 Q 95,122 102,108" /> <Line Name="LeftLeg" X1="92" X2="82" Y1="146" Y2="168" Stroke="Blue" StrokeThickness="4" /> <Line Name="LeftFoot" X1="68" X2="83" Y1="160" Y2="169" Stroke="Blue" StrokeThickness="4" /> <Line Name="RightLeg" X1="124" X2="132" Y1="144" Y2="166" Stroke="Blue" StrokeThickness="4" /> <Line Name="RightFoot" X1="114" X2="133" Y1="169" Y2="166" Stroke="Blue" StrokeThickness="4" /> </Canvas> </Grid> <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" > <Button Width="80" Height="40" Margin="5,5,5,5" Name="startButtonXAML">Start</Button> <Button Width="80" Height="40" Margin="5,5,5,5" Name="stopButtonXAML">Stop</Button> </StackPanel> <DockPanel.Triggers> <EventTrigger RoutedEvent="Button.Click" SourceName="startButtonXAML"> <BeginStoryboard Name="beginMoveEye"> 7
<Storyboard Name="moveEye"> <DoubleAnimation RepeatBehavior="Forever" DecelerationRatio=".8" AutoReverse="True" By="8" Duration="0:0:1" Storyboard.TargetName="eye" Storyboard.TargetProperty="(Canvas.Left)" /> <DoubleAnimation RepeatBehavior="Forever" AutoReverse="True" By="8" Duration="0:0:5" Storyboard.TargetName="eye" Storyboard.TargetProperty="(Canvas.Top)" /> </Storyboard> </BeginStoryboard> </EventTrigger> <EventTrigger RoutedEvent="Button.Click" SourceName="stopButtonXAML"> <StopStoryboard BeginStoryboardName="beginMoveEye" /> </EventTrigger> </DockPanel.Triggers> </DockPanel> Skompilować wytworzoną kontrolkę do postaci dll. (Build->Build Solution). Od bieżącego katalogu projektu w podkatalogu bin\debug powstanie plik dll. Aby zaobserwować działanie wytworzonej kontrolki należy ją osadzić w aplikacji. W tym celu wykonaj dalsze kroki. 3. Otworzyć nowy projekt zwykłej aplikacji windowsowej (File->New->Project->Windows -> Windows Forms Application) W Solution Explorerze na References klik prawym przyciskiem myszy i Add Reference - >zakładka.net ->dodać referencje do: WindowsFormsIntegration, WindowsBase, PresentationFramework, PresentationCore. Oraz do pliku z wytworzoną dll ką Add Reference ->zakładka Browse->wskazujemy naszą dll kę ->OK. 4. Dopisać namespace: using System.Windows.Forms.Integration; 5. Zmodyfikować zawartość namespace w Form1.Designer.cs do postaci: partial class Form1 /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) if (disposing && (components!= null)) components.dispose(); base.dispose(disposing); #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. 8
/// </summary> private ElementHost elthost = null; private void InitializeComponent() this.components = new System.ComponentModel.Container(); this.autoscalemode = System.Windows.Forms.AutoScaleMode.Font; this.text = "Form1"; //dodanie do formy Form1 kontrolki WPF elthost = new ElementHost();//Stworzenie elementu // hostującego (musi być dla kontrolek innego typu niż zwykłe // windowsowe - a nasza jest kontrolką WPF a nie windowsową) //WPF_Zad2_lib jest nazwa pliku dodanego w referencjach ktory zawiera // stworzoną WPF'ową kontrokę uzytkownika WPF_Zad2_lib.UserControl1 kontroka = new WPF_Zad2_lib.UserControl1(); //powołanie do życia instancji naszej kontrolki elthost.child = kontroka; //wskazujemy,ze nasza kontrolka ma // być hostowana w aplikacji windowsowej elthost.width = 230; elthost.height = 260; this.controls.add(elthost); // dodanie elementu hostujacego do // kontrolek zwiazanych z formą // tu osadzamy jeszcze zwykłą kontrolkę windowsową #endregion Teraz można skompilować aplikację (F6) i ją uruchomić (F5). Otrzymujemy: gdzie stworek z animacją oczu i przyciskami Start i Stop jest w całości zrobiony jako kontrolka WPF i osadzony w zwykłej aplikacji windowsowej. Zwykłe kontrolki windowsowe możemy umieszczać przez przeciągnięcie z ToolBoxa i ustawienie im właściwości w oknie Properties lub całokowicie tworzyć je w kodzie: // tu osadzamy jeszcze zwykłą kontrolkę windowsową Button winformsbutton = new Button(); winformsbutton.text = "Click Me!"; winformsbutton.width = 100; winformsbutton.height = 100; winformsbutton.top = 250; winformsbutton.left = 40; this.controls.add(winformsbutton); Należy jeszcze dopisać: using System.Windows.Forms; Skompilować projekt, uruchomić i sprawdzić działanie kontrolki WPF i zwykłej kontrolki windowsowej w aplikacji windowsowej. 9