PwT.N W14

Transkrypt

PwT.N W14
Aplikacja oparta na stronach
Model tradycyjnej aplikacji okienkowej: okno lub okna (okna dokumentów, okna
dialogowe), menu, paski zadań. Praca wykonywana jest przeważnie w obrębie
pojedynczego okna, dodatkowe okna służą wykonaniu pobocznych zadań – i albo po ich
wykonaniu następuje powrót do okna macierzystego, albo wykonują swe zadanie
niezależnie i równolegle.
Model aplikacji opartej na stronach. Pojedynczym elementem aplikacji jest strona
(przeważnie jedna). Wykonywanie zadań opiera się na przemieszczaniu się z jednej strony
do innej – liniowo. Kolejne kroki lub kolejne czynności przenoszą nas do następnych
stron.
Interfejs oparty na stronach
Zmienia się tylko element główny – zamiast klasy Window klasa Page.
Jest jednak istotna różnica – strona musi znajdować się w kontenerze. Może nim być:
● NavigationWindow
● element Frame wewnątrz innego okna
● element Frame wewnątrz innej strony
● Internet Explorer lub Firefox
1/45
Dodawanie stron do projektu:
menu Project -> Add Page...
Przykładowa zawartość strony:
<Page x:Class="Wpf6.Page1"
...
WindowTitle="To jest moja strona"
Title="Moja strona" FontSize="16">
<StackPanel Margin="3">
<TextBlock Margin="3">
To jest prosta strona.
</TextBlock>
<Button Margin="2" Padding="2">OK</Button>
<Button Margin="2" Padding="2">Zamknij</Button>
</StackPanel>
</Page>
WindowTitle – jako nagłówek okienka
Title – nazwa strony do zapisania w historii przeglądania
(uwaga: nie ustawia się tu żadnego “rozmiaru strony” czy “rozmiaru okna”)
2/45
plik App.xaml:
<Application x:Class="Wpf6.App"
...
StartupUri="Page1.xaml" >
<Application.Resources>
</Application.Resources>
</Application>
(WPF sam rozpozna, że wskazujemy na stronę a nie na okno i utworzy kontener –
NavigationWindow – w którym wyświetli stronę)
3/45
4/45
Ręczne stworzenie strony i kontenera:
NavigationWindow win = new NavigationWindow()
win.Content = new Page1();
win.Show();
Uzyskanie dostępu do okna kontenera z poziomu strony (najwcześniej możemy to zrobić w
zdarzeniu ładowania strony):
private void Page_Loaded(object sender, RoutedEventArgs e)
{
NavigationWindow win =
(NavigationWindow)Window.GetWindow(this);
}
5/45
Page
Własności:
Background, Foreground, FontFamily, FontSize – podobnie jak w wypadku okna
Content – zawartość strony
WindowWidth, WindowHeight, WindowTitle – ustawienie parametrów okna, ma
znaczenie tylko dla stron wyświetlanych w oknie (a nie np. ramce)
NavigationService – referencja na obiekt pozwalający na nawigację (przenoszenie się do
innych stron)
KeepAlive – informuje, że obiekt strony powinien zachowywać swą zawartość gdy
użytkownik przejdzie do innej strony
ShowsNavigationUI – czy kontener zawierający stronę ma wyświetlać przyciski
nawigacji
Title – nazwa strony wyświetlana w historii nawigacji
(strona nie ma metod typu Show czy Hide – jest wyświetlana dopóki jej nie opuścimy przy
pomocy nawigacji)
6/45
Hyperlinks
<TextBlock Margin="3">
To jest prosta strona.
Kliknij
<Hyperlink NavigateUri="Page2.xaml">
tutaj
</Hyperlink>
aby przejść dalej.
</TextBlock>
7/45
Kliknięcie (zdarzenie Click) możemy przetwarzać ręcznie (i np. wykonać jakieś zadanie
lub przenieść użytkownika do innej strony):
NavigationService nav;
nav = NavigationService.GetNavigationService(this);
nav.Navigate(new System.Uri("Page1.xaml",
UriKind.RelativeOrAbsolute));
// lub:
Page1 nextPage = new Page1();
nav.Navigate(nextPage);
Możemy również podać tylko stronę docelową i nawigacją zajmie się WPF:
<Hyperlink NavigateUri="Page2.xaml">...</Hyperlink>
Uwaga: ta automatyczna nawigacja działa tylko jeśli link znajduje się na stronie, a nie np.
zwykłym oknie.
8/45
9/45
Możliwa jest również nawigacja do lokacji zewnętrznych:
<Hyperlink NavigateUri="http://wi.pb.edu.pl/">
Strona wydziału
</Hyperlink>
Wskazanie do którego elementu przewinąć docelową stronę:
<Hyperlink NavigateUri="Page2.xaml#myButton">
tutaj
</Hyperlink>
Zawartość docelowej strony:
<Page x:Class="Wpf6.Page2"
...>
<StackPanel>
<Button Name="myButton">Zamknij</Button>
</StackPanel>
</Page>
10/45
Obsługiwanie błędów nawigacji: najlepiej w klasie aplikacji
<Application x:Class="Wpf6.App"
...
StartupUri="Page1.xaml"
NavigationFailed="Application_NavigationFailed" >
<Application.Resources>
</Application.Resources>
</Application>
public partial class App : Application
{
private void Application_NavigationFailed(...)
{
if (e.Exception is System.Net.WebException)
{
MessageBox.Show("Nie udało się wejść na " +
e.Uri.ToString());
e.Handled = true;
}
}
}
11/45
Strona w zwykłym oknie: element Frame
<Window x:Class="Wpf6.Window1"
... Title="Zwykłe okno">
<Grid Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Margin="3" TextWrapping="Wrap">
Zwykłe okno
</TextBlock>
</StackPanel>
<Frame Grid.Column="1" Source="Page1.xaml"
BorderBrush="Blue" BorderThickness="1" />
</Grid>
</Window>
12/45
Można samodzielnie ustawić widoczność przycisków nawigacji:
<Frame NavigationUIVisibility="Hidden" ... />
13/45
Strony zagnieżdżone:
<Page x:Class="Wpf6.Page2"
... Title="Page2" >
<StackPanel Margin="3">
<TextBlock Margin="3">
Witamy na drugiej stronie.
</TextBlock>
<Frame Source="Page3.xaml" BorderBrush="Blue"
BorderThickness="1" />
</StackPanel>
</Page>
14/45
15/45
16/45
alternatywne rozwiązanie:
<Frame JournalOwnership="OwnsJournal" .../>
17/45
Historia nawigacji
18/45
19/45
W historii został zapisany cały poprzedni stan odwiedzanej strony.
Uwaga: ponowne nawigowanie do tej samej strony (np. przez link) tworzy zupełnie nową instancję
strony i stan nie zostanie odtworzony
Zachowywane są tylko te właściwości zależnościowe elementów strony, które mają ustawioną
(podczas rejestracji właściwości) flagę Journal. Jeśli chcemy aby historia zachowywała jakieś
nasze własne dane powinniśmy stworzyć właściwość zależnościową (w klasie strony):
private static DependencyProperty MojeDaneProperty;
static Page1()
{
FrameworkPropertyMetadata metadata =
new FrameworkPropertyMetadata();
metadata.Journal = true;
MojeDaneProperty = DependencyProperty.Register(
"MojeDaneProperty", typeof(string),
typeof(Page1), metadata, null);
}
private string MojeDane
{
set { SetValue(MojeDaneProperty, value); }
get { return (string)GetValue(MojeDaneProperty); }
}
Możemy również wymóc zatrzymanie całego obiektu w pamięci ustawiając właściwość strony
KeepAlive.
20/45
Zdarzenia nawigacji:
Navigating – zaraz rozpocznie się nawigacja, można to zatrzymać
Navigated – rozpoczęto nawigację (ale strona jeszcze nie została uzyskana)
NavigationProgress – podnoszone co 1KB danych informuje o postępie nawigacji
(NavigationProgressEventArgs.BytesRead i NavigationProgressEventArgs.MaxBytes)
LoadCompleted – strona została pobrana i przetworzona (ale jeszcze nie zainicjowana i
wyświetlona)
FragmentNavigation – przed przescrollowaniem do żądanego elementu
NavigationStopped – nawigacja zatrzymana metodą StopLoading()
NavigationFailed – niepowodzenie (być może nie udało się zlokalizować strony
docelowej)
21/45
Journal
Model aplikacji – wizard:
22/45
a co z historią?
NavigationService nav =
NavigationService.GetNavigationService(this);
string pageName = "";
while (pageName != "Page1.xaml")
{
JournalEntry entry = nav.RemoveBackEntry();
pageName =
System.IO.Path.GetFileName(entry.Source.ToString());
}
23/45
efekt:
24/45
Dodawanie własnych elementów do historii
<DockPanel Margin="3">
<TextBlock DockPanel.Dock="Top">Składniki</TextBlock>
<Button DockPanel.Dock="Bottom" Click="Add_Click">
Dodaj
</Button>
<ListBox Name="listBox"></ListBox>
</DockPanel>
25/45
public partial class Page1 : Page
{
...
// dodawanie elementów do list
static char i = 'A';
private void Add_Click(...)
{
string item = i.ToString();
i = (char)((int)i+1);
listBox.Items.Add(item);
}
...
}
A jak zapamiętywać w Dzienniku historię operacji?
26/45
// klasa do zapamiętania stanu w historii:
[Serializable()]
public class ListaJournalEntry : CustomContentState
{
// tu zapamiętamy dane strony
private List<string> items;
public List<string> Items
{
get { return items; }
}
// nazwa pod jaką element pojawi się w historii:
private string _journalName;
public override string JournalEntryName
{
get { return _journalName; }
}
// (tylko gettery, bo wartości ustawimy w konstruktorze)
27/45
// pozwólmy aby strona sama odtworzyła swą zawartość
// (metoda callback)
public delegate void ReplayListChange(ListaJournalEntry o);
private ReplayListChange replayListChange;
public override void Replay(
NavigationService navigationService, NavigationMode mode)
{
this.replayListChange(this);
}
// konstruktor
public ListaJournalEntry(
List<string> items, string journalName,
ReplayListChange replayListChange)
{
this.items = items;
this._journalName = journalName;
this.replayListChange = replayListChange;
}
} // end of ListaJournalEntry
28/45
// dodanie poniższego interfejsu jest ważne!
public partial class Page1 : Page, IprovideCustomContentState
{
...
// dodawanie elementów do list
static char i = 'A';
private void Add_Click(...)
{
string item = i.ToString();
string journalName = "Dodano " + item;
NavigationService nav =
NavigationService.GetNavigationService(this);
nav.AddBackEntry(GetJournalEntry(journalName));
i = (char)((int)i+1);
listBox.Items.Add(item);
}
29/45
// składowanie zawartości
private ListaJournalEntry GetJournalEntry(string name)
{
// kopiowanie zawartości listBoxa
List<string> items = new List<string>();
foreach(object o in listBox.Items)
items.Add((string)o);
// obiekt stanu
return new ListaJournalEntry(items, name, Replay);
}
// odtworzenie zawartości
private void Replay(ListaJournalEntry state)
{
listBox.Items.Clear();
// kopiowanie z historii do listBoxa
foreach (string item in state.Items)
listBox.Items.Add(item);
// przy odtworzeniu stanu zapamiętamy jego nazwę z historii
restoredName = state.JournalEntryName;
}
private string restoredName;
30/45
// dodawanie historii, gdy nastąpiło wyjście ze strony
public CustomContentState GetContentState()
{
return GetJournalEntry(restoredName);
}
...
} // end of Page1
31/45
Page Functions
odpowiednik okien dialogowych – strony, które mogą zwracać wartość
menu Add → New Item → Page Function (WPF) template
<PageFunction
...
xmlns:local="clr-namespace:MojaAplikacja"
x:Class="NavigationApplication.PageFunction1"
x:TypeArguments="local:Dodatek"
Title="Wybór dodatku"
>
...
</PageFunction>
public partial class PageFunction1 : PageFunction<Dodatek>
{ /*...*/ }
Określamy typ zwracanej wartości.
32/45
// otwieranie PageFunction:
<Page ...>
<StackPanel>
<TextBlock>Wybierz dodatki</TextBlock>
<ListBox .../>
<TextBlock HorizontalAlignment="Right">
<Hyperlink Click="Nowy_Click">
Dodaj nowy
</Hyperlink>
</TextBlock>
</StackPanel>
</Page>
// zawsze ręczne tworzenie:
private void Nowy_Click(object sender, RoutedEventArgs e)
{
PageFunction1 pageFunction = new PageFunction1();
// callback wołany po powrocie z PageFunction
pageFunction.Return += Returned;
this.NavigationService.Navigate(pageFunction);
}
private void Returned(object sender, ReturnEventArgs<Dodatek> e)
33/45
// odczyt po powrocie:
private void Returned(object sender, ReturnEventArgs<Dodatek> e)
{
Dodatek dodatek = (Dodatek)e.Result;
/* przetwarzamy uzyskane dane... */
}
(warto też oczyścić historię, aby nie pozwolić na powrót do page function)
// jak strona zwraca wynik:
private void lnkOK_Click(object sender, RoutedEventArgs e)
{
// zwrócenie wartości...
OnReturn(new ReturnEventArgs<Dodatek>(/*...*/));
}
private void lnkCancel_Click(object sender, ...)
{
// lub nie...
OnReturn(null);
}
34/45
<PageFunction ...>
<StackPanel>
<TextBox .../>
<TextBlock HorizontalAlignment="Right">
<Hyperlink Click="lnkOK_Click">Ok</Hyperlink>
<Hyperlink Click="lnkCancel_Click">Anuluj</Hyperlink>
</TextBlock>
</StackPanel>
</PageFunction>
35/45
XAML Browser Applications – XBAP
Aplikacje oparte na stronach uruchamiane w przeglądarce internetowej:
• uruchamiane w oknie przeglądarki
• nie wymagają instalacji
• mają ograniczone uprawnienia
• komputer, na którym aplikacja ma być uruchomiona musi mieć zainstalowany
.NET Framework przynajmniej w wersji 3.0
• obsługiwane przeglądarki: Internet Explorer 6+, FireFox 2+
• jest to osobny rodzaj projektu w VS
Kluczowe pliki wynikowe:
• ApplicationName.exe – skompilowany kod
• ApplicationName.exe.manifest – wymagania aplikacji
• ApplicationName.xbap – startowy punkt aplikacji; to ten plik będzie otwierany w
przeglądarce (zdalnie bądź lokalnie)
36/45
•
uwaga: wraz z projektem powstają pliki kluczy ważne do aktualizowania aplikacji:
ApplicationName_TemporaryKey.pfx – można też podać własny klucz: Properties w
oknie Solution Explorer i zakładka Signing
•
aby aktualizować aplikację musimy pilnować numeru wersji – Solution Explorer
→ Properties → Publish → Publish Version
Albo łatwiejsza metoda – automatyczne zwiększanie numeru wersji – menu Build
→ Publish i opcja Automatically Increment Revision with Each Publish
•
37/45
Isolated Storage
// czy możemy zapisać dane w fizycznym pliku?
string filePath = System.IO.Path.Combine(appPath,"dane.txt");
FileIOPermission permission = new FileIOPermission(
FileIOPermissionAccess.Write, filePath);
// sprawdzanie uprawnień
if (CheckPermission(permission))
{
// zapis lokalny
try
{
using (FileStream fs = File.Create(filePath))
{
/*...zapis danych...*/
}
}
catch { /*...*/ }
}
Sprawdzamy możliwość zapisu, by móc uruchamiać aplikację lokalnie (w innych
uprawnieniach).
38/45
else
{
// zapis do isolated storage
try
{
IsolatedStorageFile store =
IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream fs =
new IsolatedStorageFileStream(
"dane.txt", FileMode.Create, store))
{
/*...zapis danych...*/
}
}
catch { /*...*/ }
}
Jest to odpowiednik ciasteczek (niewielka porcja informacji zapisywana u klienta).
39/45
// metoda użyta w powyższym przykładzie:
private bool CheckPermission(
CodeAccessPermission requestedPermission)
{
try
{
// próbujemy uzyskać uprawnienie
requestedPermission.Demand();
return true;
}
catch
{
return false;
}
}
40/45
Okna popup – odpowiednik okien dialogowych (Popup dodajemy jako element strony).
<Page ...><StackPanel>
... <TextBlock><Hyperlink Click="cmdStart_Click">
Pokaż
</Hyperlink></TextBlock> <Label Name="lblName"/> ...
<Popup StaysOpen="True" Placement="Center" MaxWidth="200"
Name="dialogPopUp">
<Border ...>
<StackPanel Margin="5" ...>
<TextBlock Margin="10" TextWrapping="Wrap">
Podaj nazwę użytkownika.
</TextBlock>
<TextBox Name="txtName" Margin="10"/>
<StackPanel Orientation="Horizontal" Margin="10">
<Button Click="dialog_cmdOK_Click"
Padding="3" Margin="0,0,5,0">OK</Button>
<Button Click="dialog_cmdCancel_Click"
Padding="3">Anuluj</Button>
</StackPanel>
</StackPanel>
</Border>
</Popup>
</StackPanel></Page>
41/45
42/45
private void cmdStart_Click(object sender, RoutedEventArgs e)
{
ShowPopup(true);
}
private void dialog_cmdOK_Click(object sender, ...)
{
// pobranie nazwy z popupu do okna głównego
lblName.Content = "Podałeś: " + txtName.Text;
ShowPopup(false);
}
private void dialog_cmdCancel_Click(object sender, ...)
{
ShowPopup(false);
}
private void ShowPopup(bool show)
{
this.IsEnabled = !show;
this.Background = show ? Brushes.LightGray : null;
dialogPopUp.IsOpen = show;
}
43/45
44/45
Zagnieżdżanie aplikacji XBAP wewnątrz html
•
•
Aplikacje XBAP mogą być uruchamiane poprzez odwołanie do pliku .xbap w
pasku adresu przeglądarki
Mogą być również umieszczone jako część strony, np.:
<html>
<head>
<title>Strona zawierająca XBAP</title>
</head>
<body>
<h1>Zwykła zawartość strony HTML.</h1>
<iframe src="BrowserApplication.xbap"></iframe>
<p>Więcej zwykłej zawartości.</p>
</body>
</html>
45/45

Podobne dokumenty