• Nie Znaleziono Wyników

Programowanie w technologii .NET wykład 9 – Dokumenty

N/A
N/A
Protected

Academic year: 2021

Share "Programowanie w technologii .NET wykład 9 – Dokumenty"

Copied!
48
0
0

Pełen tekst

(1)

Programowanie w technologii .NET

wykład 9 – Dokumenty

Kontrolki Label i TextBlock nie nadają się do wyświetlania dużej ilości formatowanego tekstu (np. dokumentów czy pomocy):

(2)

• Dokumenty pozwalają prezentować dużą ilość tekstu w czytelny, wygodny sposób:

obsługa kolumn, podziału na strony, dzielenia słów, ...

Dokumenty w WPF dzielą się na dwie kategorie:

Fixed documents – dokumenty przeznaczone do wydruku, o ustalonej pozycji całej zawartości, stronicowaniu; są odpowiednikiem plików PDF. Ważne, gdy chcemy wydrukować coś bez zmian.

Flow documents – przeznaczone do oglądania w okienku, ich zawartość układana jest automatycznie – dostosowując się do okna i sposobu wyświetlania;

odpowiadają dokumentom HTML.

Dokument musi być wyświetlany w kontenerze:

Fixed documents – w kontenerze DocumentViewer

Flow documents – do dyspozycji mamy kontenery FlowDocumentReader, FlowDocumentPageViewer i FlowDocumentScrollViewer

Wszystkie te kontenery są tylko do odczytu. Poza tym, mamy do dyspozycji API do tworzenia fixed documents oraz kontrolkę RichTextBox do edycji flow documents.

(3)

Flow Documents

• W tym typie dokumentów zawartość dostosowuje się do kontenera. Przeznaczone do wyświetlania na ekranie.

• Jest pozbawiony wielu wad HTMLa. Domyślnie zawartość HTML wypełnia okno – co może utrudniać czytanie, jeśli szerokość okienka (i długość linii) jest duża.

Rozwiązaniem jest odgórne ograniczenie szerokości, ale wiąże się to ze stratą miejsca (marginesy).

• Flow Document dodaje m. in. paginację (podział na strony), formaty

wielokolumnowe, dzielenie słów i możliwość zmiany ustawień użytkownika.

Do tworzenia dokumentu typu flow służy klasa FlowDocument.

Nowy dokument możemy tworzyć jako osobny plik lub też umieścić go w

istniejącym oknie, wykorzystując jeden z dostępnych kontenerów (w przykładzie użyjmy FlowDocumentScrollViewer).

<Window ...>

<FlowDocumentScrollViewer>

<FlowDocument>

...

</FlowDocument>

</FlowDocumentScrollViewer>

</Window>

(4)

Flow Elements

Nie możemy umieścić tekstu bezpośrednio w dokumencie. Flow document tworzony jest z flow elements.

• Nie dziedziczą one z klas UIElement i FrameworkElement, lecz tworzą własną, odrębną hierarchię klas ContentElement i FrameworkContentElement.

• Są prostsze, niż elementy poznawane dotąd, ale obsługują podobny zestaw zdarzeń i własności.

• Podstawowa różnica: nie odpowiadają za renderowanie samych siebie. Zajmuje się tym kontener, który je przechowuje (co pozwala na optymalizację).

• Domyślnie nie przyjmują też focusa (Focusable ustawione na false).

Dwa podstawowe typy Flow Elements:

Block elements (elementy blokowe) – służą do grupowania innych elementów. Np.

paragraf (Paragraph) może zawierać kilka sekcji różnie sformatowanego tekstu.

Inline elements (elementy liniowe) – są zagnieżdżane w elementach blokowych (lub innych elementach liniowych). Np. element Run zawiera pewien tekst, który możemy umieścić w paragrafie.

Ten model zawartości pozwala na wielokrotne poziomy zagnieżdżenia (np. element Bold w elemencie Underline – model bardzo podobny do HTMLa)

(5)

Elementem najwyższego poziomu musi być element blokowy (np. Paragraph):

<Window ...>

<FlowDocumentScrollViewer>

<FlowDocument>

<Paragraph>Hello, world!</Paragraph>

</FlowDocument>

</FlowDocumentScrollViewer>

</Window>

(6)

Nie ma ograniczenia, ilu elementów najwyższego poziomu użyjemy:

<Window ...>

<FlowDocumentScrollViewer>

<FlowDocument>

<Paragraph>Hello, world!</Paragraph>

<Paragraph>A to jest drugi paragraf.</Paragraph>

</FlowDocument>

</FlowDocumentScrollViewer>

</Window>

Pasek przewijania jest dodawany automatycznie, a czcionka jest pobrana z ustawień systemowych, a nie okna zawierającego kontener.

Domyślnie FlowDocumentScrollViewer.IsSelectionEnabled jest ustawione na true.

(7)

Formatowanie elementów

Foreground, Background – pędzle wypełniające pierwszy plan i tło elementu

FontFamily, FontSize, FontStretch, FontStyle, FontWeight – własności dotyczące kroju pisma

ToolTip – podpowiedź wyświetlana po zatrzymaniu się na elemencie

Style – styl definiujący wygląd elementu

BorderBrush, BorderThickness – obramowanie elementu

Margin – odległość między elementem a kontenerem lub elementem sąsiednim.

Domyślnie odległość między Block elements (np. dwoma paragrafami) to 18 jednostek (jeśli chcemy to zmniejszyć, należy zmniejszać z obu stron).

Padding – odstęp między krawędzią elementu a elementem zagnieżdżonym

TextAlignment – wyrównanie w poziomie (Left, Right, Center lub Justify)

LineHeight – odległość między liniami zagnieżdżonego tekstu.

LineStackingStrategy – określa, w jaki sposób linie są oddzielone, gdy zawierają tekstu o różnych wielkościach czcionki (MaxHeight wybiera najwyższy rozmiar czcionki, BlockLineHeight używa wartości LineHeight dla wszystkich linii) Ponadto:

TextDecorations (udostępniane przez Paragraph i wszystkie elementy inline) – Strikethrough, Overline, Underline lub dowolna ich kombinacja.

• Typography – szczegóły dotyczące sposobu renderowania tekstu.

(8)

Elementy blokowe Paragraph i Run

Paragraf nie zawiera tekstu, ale kolekcję elementów liniowych – Paragraph.Inlines (może on zatem zawierać nie tylko tekst).

Aby umieścić tekst w paragrafie, musi znaleźć się on w elemencie Run:

<Paragraph>

<Run>A to jest drugi paragraf.</Run>

</Paragraph>

Jeśli go nie podamy, zostanie utworzony automatycznie, ale należy o tym pamiętać, jeśli chcemy sięgnąć do tego tekstu programistycznie:

<Paragraph Name="paragraf">Hello, world!</Paragraph>

((Run)paragraf.Inlines.FirstInline).Text = "Dzień dobry!";

Lepszym pomysłem może być umieszczanie tekstu, który chcemy modyfikować w elemencie Span (aby móc go nazwać i sięgać do niego bezpośrednio).

(9)

Klasa Paragraph zawiera własność TextIndent, która pozwala ustawić rozmiar wcięcia pierwszego wiersza.

W odróżnieniu od HTML, w WPF nie ma elementów nagłówków (należy używać odpowiednio sformatowanych paragrafów).

List

Reprezentuje punktowaną lub numerowana listę – wybrać to można przy pomocy własności MarkerStyle:

• Disc

• Box

• Circle

• Square

• Decimal (domyślnie zaczyna się od 1, ale można wybrać inny StartingIndex)

• LowerLatin (a, b, c).

• UpperLatin (A, B, C).

• LowerRoman (i, ii, iii, iv).

• UpperRoman (I, II, III, IV).

• None – bez znacznika

MarkerOffset pozwala ustawić odstęp między znacznikiem a elementem listy.

(10)

Wewnątrz listy zagnieżdżamy elementy ListItem, reprezentujące poszczególne podpunkty.

Każdy ListItem musi zawierać element blokowy (np. Paragraph).

<FlowDocument>

<Paragraph>Tematy projektów</Paragraph>

<List>

<ListItem>

<Paragraph>Sklep</Paragraph>

</ListItem>

<ListItem>

<Paragraph>Miejski Dom Kultury</Paragraph>

</ListItem>

<ListItem>

<Paragraph>Lista zadań</Paragraph>

</ListItem>

<ListItem>

<Paragraph>Wizard klas</Paragraph>

</ListItem>

</List>

</FlowDocument>

(11)

Table

Przeznaczona do wyświetlania zawartości w postaci tabeli. Jest wzorowana na <table> z HTMLa.

Aby stworzyć tabelę, należy:

1. Umieścić element TableRowGroup w elemencie Table. TableRowGroup zawiera grupę wierszy. Pozwala nadać jedno formatowanie kilku wierszom jednocześnie.

2. Umieścić element TableRow w elemencie TableRowGroup. TableRow reprezentuje pojedynczy wiersz.

3. W TableRow umieścić element TableCell dla każdej kolumny wiersza.

4. W TableCell umieścić element blokowy (przeważnie Paragraph), do przechowywania zawartości komórki.

(12)

<FlowDocument>

<Paragraph FontSize="16pt">Oddanych zadań:</Paragraph>

<Table>

<TableRowGroup Paragraph.TextAlignment="Center">

<TableRow FontWeight="Bold">

<TableCell>

<Paragraph>Nr</Paragraph>

</TableCell>

<TableCell>

<Paragraph>Temat</Paragraph>

</TableCell>

<TableCell>

<Paragraph>Oddanych</Paragraph>

</TableCell>

</TableRow>

...

</TableRowGroup>

</Table>

</FlowDocument>

(13)

W przeciwieństwie do kontenera Grid, komórki w Table są wypełniane w kolejności (wg pozycji).

• Domyślnie kolumny rozmieszczane są równomiernie. Można samodzielnie określić rozmiar lub proporcje (ale nie oba naraz, nie możemy łączyć kolumn o ustalonym rozmiarze z proporcjonalnymi):

<Table.Columns>

<TableColumn Width="*"/>

<TableColumn Width="3*"/>

<TableColumn Width="*"/>

</Table.Columns>

Można również określić ColumnSpan lub/i RowSpan.

CellSpacing określa odstęp między komórkami.

• Każda komórka może być formatowana osobno.

Nie ma zbyt dobrego wsparcia dla obramowań: istnieją BorderThickness, BorderBrush (własności TableCell), ale rysują osobną krawędź wokół każdej komórki; BorderThickness i BorderBrush w klasie Table to krawędź wokół całej tabeli.

(14)

Section

Sekcja jest analogiczna do elementu <div> z HTMLa.

Służy do grupowania elementów blokowych, by móc nadać im jeden styl formatowania.

<Section FontFamily="Comic Sans MS"

Background="LightSteelBlue">

<Paragraph > ... </Paragraph>

<Paragraph > ... </Paragraph>

</Section>

(Oczywiście można też używać w połączeniu ze stylami.)

(działa to dla czcionek, bo są dziedziczone, a dla tła, bo domyślnie w paragrafie jest przezroczyste i widać tło sekcji)

(15)

BlockUIContainer

• Pozwala na umieszczenie w dokumencie (w miejscu elementu blokowego) elementów dziedziczących z UIElement.

Pozwala na dodawanie np. przycisków, pól wyboru, a nawet całych paneli czy gridów ze złożoną zawartością. Jedyne ograniczenie: pojedyncze dziecko.

• Jest to przydatne, gdy chcemy połączyć dokument z kontrolkami zapewniającymi pewną interakcję z użytkownikiem, np. przyciski w systemie pomocy lub pola tekstowe w formularzu.

<Paragraph>

Naciśnij, aby uzyskać więcej pomocy.

</Paragraph>

<BlockUIContainer>

<Button HorizontalAlignment="Left" Padding="5">

Pomoc </Button>

</BlockUIContainer>

(16)

Elementy liniowe

Run – zawiera zwykły tekst, można ustawić mu formatowanie, choć częściej stosuje się do tego Span; tworzony domyślnie np. w paragrafach

Span – (analogiczne do HTMLowego <span>) opakowuje dowolną liczbę elementów inline, zazwyczaj używany do formatowania fragmentu tekstu (w tym celu opakowujemy w niego tekstu lub np. element Run); przydatne do nazwania fragmentu dokumentu (aby móc odwołać się do niego w kodzie)

Bold, Italic, and Underline – umożliwiają nałożenie odpowiedniego formatowania na tekst (zazwyczaj lepiej jest jednak opakować tekst w Span i ustawić odpowiedni styl)

Hyperlink – reprezentuje odnośnik (podnosi zdarzenie Click)

LineBreak – dodaje złamanie linii

InlineUIContainer – pozwala na umieszczanie elementów dziedziczących po UIElement w miejscu elementów inline.

Floater, Figure – umożliwia wstawienie specjalnie traktowanego obszaru do wyświetlania obrazów, wyróżnionych informacji i innej zawartości

(17)

Białe znaki

Na początku lub końcu elementu są ignorowane, ale przed elementem inline – nie. Dlatego to działa źle:

<Paragraph>Do końca grudnia<Bold> należy </Bold>przygotować prototyp interfejsu.</Paragraph>

a to dobrze:

<Paragraph>Do końca grudnia <Bold>należy</Bold> przygotować prototyp interfejsu.</Paragraph>

Można też używać:

<Run xml:space="preserve"> a b c </Run>

(18)

Floater

Element, który pozwala umieścić pewną zawartość obok głównego dokumentu:

<Paragraph>

Przykładowy tekst.

<Floater Style="{StaticResource Cytat}">

<Paragraph>...cytat...</Paragraph>

</Floater>

Dalszy ciąg przykładowego tekstu..

</Paragraph>

<Paragraph>

Kolejny akapit.

</Paragraph>

(19)

Styl użyty w przykładzie:

<Style x:Key="Cytat">

<Setter Property="Paragraph.FontSize" Value="24"/>

<Setter Property="Paragraph.FontStyle" Value="Italic"/>

<Setter Property="Paragraph.Foreground" Value="SteelBlue"/>

<Setter Property="Paragraph.Padding" Value="5"/>

<Setter Property="Paragraph.Margin" Value="15,10,5,10"/>

</Style>

Możemy ograniczyć zajmowane przez niego miejsce i ręcznie określić położenie:

<Floater Style="{StaticResource Cytat}" Width="200"

HorizontalAlignment="Left">

<Paragraph>...</Paragraph>

</Floater>

Aby dostosować wygląd, możemy też ustawić własności Background, BorderBrush, BorderThickness, Margin i Padding (pozostałe elementy inline tego nie mają, tylko Floater i Figure).

(20)

Możemy go użyć do wyświetlenia obrazka (element Image wewnątrz BlockUIContainer lub InlineUIContainer). Niestety, Floater nie dostosowuje szerokości do zawartości.

<Paragraph>...</Paragraph>

<Paragraph>

...

<Floater Width="100" Padding="5,0,5,0"

HorizontalAlignment="Right">

<BlockUIContainer>

<Image Source="Masqueofthereddeath.jpg"></Image>

</BlockUIContainer>

</Floater>

...

</Paragraph>

(21)

Figure

Podobna do Floatera, ale daje więcej kontroli nad położeniem.

uwaga: wiele poniższych własności (np. HorizontalAnchor, VerticalOffset,

HorizontalOffset) nie jest wspieranych przez kontener FlowDocumentScrollViewer, dlatego w przykładzie użyjemy FlowDocumentReader.

Width – szerokość; poza stała wartością, jak w Floaterze, możemy podać wartość proporcjonalną (np. „0.25 content”, „2 Column”).

Height – wysokość – Floater dostosowywał ją do zawartości, tu możemy określić ją ręcznie

HorizontalAnchor – zamiast HorizontalAlignment z klasy Floatera; pozwala orientować również względem strony lub kolumny (poza np. ContentCenter mamy PageCenter i ColumnCenter).

VerticalAnchor – pozwala wyrównać do bieżącej linii, kolumny, strony

HorizontalOffset, VerticalOffset – pozwalają na przesunięcie figury względem miejsca zakotwiczenia

WrapDirection – określa, czy tekst może opływać figurę z jednej czy obu stron

(22)

<FlowDocumentReader>

<FlowDocument>

<Paragraph>...</Paragraph>

<Paragraph>...

<Figure Width="0.5 column" Padding="5,0,5,0"

HorizontalAnchor="ColumnLeft">

<BlockUIContainer>

<Image Source="Masqueofthereddeath.jpg" />

</BlockUIContainer>

</Figure>

...</Paragraph>

</FlowDocument>

</FlowDocumentReader>

(23)

Interakcja z elementami

(uwaga: tekst musimy ręcznie umieszczać w elementach Run) // Pierwszy fragment zdania

Run runFirst = new Run();

runFirst.Text = "Oto ";

// Wytłuszczony tekst Bold bold = new Bold();

Run runBold = new Run();

runBold.Text = "dynamicznie wygenerowany";

bold.Inlines.Add(runBold);

// Koniec zdania

Run runLast = new Run();

runLast.Text = " dokument.";

// Dodawanie fragmentów do paragrafu Paragraph paragraph = new Paragraph();

paragraph.Inlines.Add(runFirst);

paragraph.Inlines.Add(bold);

paragraph.Inlines.Add(runLast);

// Utworzenie dokumentu i dodanie paragrafu FlowDocument document = new FlowDocument();

document.Blocks.Add(paragraph);

// Wyświetlenie dokumentu

docViewer.Document = document;

(24)

A czego użyć, aby przeglądać (i modyfikować) gotowy dokument?

kolekcja FlowDocument.Blocks zwraca elementy blokowe;

FlowDocument.Blocks.FirstBlock zwraca pierwszy, a FlowDocument.Blocks.LastBlock – ostatni element kolekcji

w celu przeglądania elementów blokowych: Block.NextBlock (lub

Block.PreviousBlock). Ponadto kolekcja Block.SiblingBlocks pozwala przeglądać elementy tego samego poziomu co aktualny

• wiele elementów blokowych zawiera inne elementy (np. List zawiera elementy ListItem, Section zawiera kolekcję Blocks, Paragraph zawiera Inlines.

Jeśli celem jest wymienienie fragmentu tekstu w dokumencie, najlepiej jest wydzielić ten fragment w postaci elementu Span. Możemy rozpoznawać je po nazwie, albo np. po Tagu (jeśli szukamy elementu, który występuje więcej niż raz).

(25)

Justowanie

Domyślnie dokumenty są wyjustowane (TextAlignment – Justify).

Ustawienie FlowDocument.IsOptimalParagraphEnabled na true włącza opcję optymalnego dzielenia wierszy (podziały linii są ustawiane tak, by jak najlepiej rozłożyć odstępy w dokumencie)

Ustawiając

FlowDocument.IsHyphenationEnabled na true włączamy dzielenie słów (do ustalenia podziału wykorzystywany jest słownik).

(26)

Kontenery Flow Document

Są tylko do odczytu. Wszystkie obsługują drukowanie i zoom.

FlowDocumentScrollViewer – cały dokument z paskiem przewijania. Nie obsługuje paginacji, ani wielu kolumn.

FlowDocumentPageViewer – dzieli dokument na wiele stron.

FlowDocumentReader – łączy cechy obu powyższych: użytkownik wybiera który rodzaj widoku go interesuje.

Zmiana kontenera jest prosta:

<FlowDocumentPageViewer>

<FlowDocument>

<Paragraph>Jeden akapit tekstu.</Paragraph>

</FlowDocument>

</FlowDocumentPageViewer>

Jak prosty kontener może też służyć TextBlock – może przechowywać elementy liniowe (inline), oferuje zawijanie (TextWrapping) i ucinanie (TextTrimming) tekstu. To ostatnie może przyjmować wartość: None, WordEllipse (niemieszczące się słowa zastępuje „...”), CharacterEllipse (niemieszczące się znaki zastępuje „...”).

(27)

Zoom

Ustawiany przy pomocy własności Zoom kontenera. Jest to wartość w procentach.

Można ustawiać ją ręcznie, używać metod IncreaseZoom() i DecreaseZoom() (zwiększają lub zmniejszają o ZoomIncrement) lub pozwolić wybrać

użytkownikowi (FlowDocumentScrollViewer daje do dyspozycji suwak powiększenia):

<FlowDocumentScrollViewer MinZoom="50" MaxZoom="1000"

Zoom="100" ZoomIncrement="5" IsToolBarVisible="True">

...

</FlowDocumentScrollViewer>

Elementy Floater lub Figure o ustalonym rozmiarze też są powiększane proporcjonalnie.

(28)

Strony i kolumny

• Kontener FlowDocumentPageViewer może dzielić długi dokument na strony – zastępuje to scrollowanie.

• A jeśli okno będzie dość szerokie, tekst zostanie podzielony na kolumny.

Uwaga: Floater domyślnie przyjmuje szerokość całej kolumny, Można go zmniejszyć, ale nie powiększyć. Z kolei Figure może zajmować kilka kolumn.

• FlowDocumentReader również obsługuje podział na strony: można wybrać widok jednej strony (jak we FlowDocumentPageViewer) lub dwóch stron obok siebie.

(29)

Podział na strony i kolumny możemy kontrolować w klasie FlowDocument lub Paragraph.

FlowDocument:

ColumnWidth – preferowany rozmiar kolumny (działa jako rozmiar minimalny)

IsColumnWidthFlexible – decyduje, czy kontener może dopasować rozmiar kolumny (jeśli false, użyta jest dokładna wartość ColumnWidth, jeśli true – ColumnWidth to rozmiar minimalny). Domyślnie: true;

ColumnGap – odstęp między kolumnami

ColumnRuleWidth i ColumnRuleBrush – szerokość i wypełnienie pionowej kreski między kolumnami

Paragraph:

KeepTogether – czy paragraf może być podzielony na strony (gdy true: cały paragraf znajdzie się na jednej stronie)

KeepWithNext – czy można oddzielić końcem strony ten paragraf od następnego (przeważnie: do nagłówków)

MinOrphanLines – jak paragraf jest dzielony na strony; minimalna liczba linii, jaka ma pozostać na poprzedniej stronie (gdy się nie mieszczą – przenoszony jest cały paragraf)

MinWidowLines – minimalna liczba linii, jaka ma być przeniesiona na następną stronę

(30)

Odczyt dokumentu z pliku

• Do ładowania zawartości do kontenera służy klasa XamlReader (z

System.Windows.Markup). W poniższym kodzie pominięto obsługę błędów:

using (FileStream fs = File.Open(documentFile, FileMode.Open)) { FlowDocument document = XamlReader.Load(fs) as FlowDocument;

if (document == null) {

MessageBox.Show("Nie udało się odczytać dokumentu.");

} else {

flowContainer.Document = document;

} }

• Podobnie łatwo zapisać dokument z kontenera:

using (FileStream fs = File.Open(documentFile, FileMode.Create)) {

XamlWriter.Save(flowContainer.Document, fs);

}

(31)

Drukowanie

• Wystarczy wywołać metodę Print() z kontenera. Wyświetla on okno drukowania z wyborem drukarki i ustawień.

• Drukowanie działa przez system poleceń, zatem wystarczy dodać przycisk (lub skorzystać z Ctrl+P):

<Button Command="ApplicationCommands.Print"

CommandTarget="docViewer">Print</Button>

W podobny sposób obsługiwane jest np. wyszukiwanie i powiększenie.

(32)

Edycja Flow Document

Kontrolka RichTextBox umożliwia edycję flow documents.

Ładowanie pliku – można zadeklarować dokument od razu wewnątrz RichTextBox:

<RichTextBox>

<FlowDocument>

<Paragraph>Dokument do edycji.</Paragraph>

</FlowDocument>

</RichTextBox>

Można też wczytać go przy pomocy

XamlReader.Load(). Jeśli chcemy czytać inne formaty, pomocna jest klasa

System.Windows.Documents.TextRange.

Dozwolone typy:

• DataFormat.Xaml – zawartość XAML

• DataFormats.Rtf – rich text (*.rtf)

• DataFormats.XamlPackage – zawartość XAML z osadzonymi obrazami

• DataFormats.Text – zwykły tekst

(33)

OpenFileDialog openFile = new OpenFileDialog();

openFile.Filter = "RichText Files (*.rtf)|*.rtf|All Files (*.*)|

*.*";

if (openFile.ShowDialog() == true) {

TextRange documentTextRange = new TextRange(

richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);

using (FileStream fs = File.Open(openFile.FileName, FileMode.Open)) {

// po rozszerzeniu rozpoznajemy typ pliku:

if (Path.GetExtension(openFile.FileName).ToLower() == ".rtf") {

documentTextRange.Load(fs, DataFormats.Rtf);

} else {

documentTextRange.Load(fs, DataFormats.Xaml);

} } }

(najpierw tworzymy TextRange obejmujący zawartość dokumentu, którą chcemy wymienić)

(34)

Zapis pliku:

SaveFileDialog saveFile = new SaveFileDialog();

saveFile.Filter =

"XAML Files (*.xaml)|*.xaml|RichText Files (*.rtf)|*.rtf|All Files (*.*)|*.*";

if (saveFile.ShowDialog() == true) {

// cały zakres dokumentu:

TextRange documentTextRange = new TextRange(

richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);

// Jeśli plik istnieje, zostanie nadpisany

using (FileStream fs = File.Create(saveFile.FileName)) {

if (Path.GetExtension(saveFile.FileName).ToLower() == ".rtf") {

documentTextRange.Save(fs, DataFormats.Rtf);

} else {

documentTextRange.Save(fs, DataFormats.Xaml);

} } }

(35)

Elementem najwyższego poziomu będzie Section (tylko takie pliki XAML są akceptowane).

Formatowanie tekstu: Kontrolka RichTextBox obsługuje polecenia związane z edycją i formatowaniem dokumentów, dlatego aby obsługiwać np. zmiany formatu, wystarczy dodać przyciski na pasku narzędzi.

(36)

Fixed Documents

• Fixed documents są mnie elastyczne niż Flow documents.

• Służą jako dokument gotowy do wydruku, który można przenosić i drukować w niezmienny formacie (na wzór PDF).

• Wykorzystują pliki XPS (XML Paper Specification).

Sterowniki drukarki w Viście i Windows 7 pozwalają na drukowanie dowolnych dokumentów do formatu XPS.

Format XPF to archiwum zip zawierające dokument w postaci XML, wraz z obrazkami, osadzonymi czcionkami, etc.

Dokument XPS możemy wyświetlić w kontenerze DocumentViewer.

XpsDocument doc = new XpsDocument("filename.xps", FileAccess.Read);

docViewer.Document = doc.GetFixedDocumentSequence();

doc.Close();

W dokumentach XPS układ i rozmiar strony są ustalone, dokument przeznaczony jest od razu do wydruku.

(37)

Annotations

Adnotacje. Pozwalają dodać komentarz lub wyróżnić fragment tekstu w dokumencie flow lub fixed.

Dwa rodzaje:

Highlighting – wybranie części tekstu i wyróżnienie go kolorem (zakreślacz).

Sticky notes – dołączenie do fragmentu tekstu pływającej ramki z informacją.

(38)

• Wszystkie cztery rodzaje kontenerów obsługują adnotacje.

• Przedtem jednak musimy wykonać dwa kroki: włączyć możliwość stosowania adnotacji i dodać kontrolki, przy pomocy których użytkownik będzie je dodawał.

Klasy adnotacji

AnnotationService – klasa zajmująca się usługą adnotacji (aby je udostępnić, należy stworzyć jej obiekt)

AnnotationStore – zajmuje się składowanie dodanych adnotacji. Udostępnia metody pozwalające na dodawanie i usuwanie pojedynczych adnotacji oraz zdarzenia podnoszone w momencie ich dodawania i zmiany. Jest to klasa

abstrakcyjna, dziedziczy z niej klasa XmlStreamStore, która składuje adnotacje w formacie XML.

AnnotationHelper – udostępnia kilka statycznych metod do posługiwania się adnotacjami.

(39)

Włączanie obsługi

// A stream for storing annotation.

private MemoryStream annotationStream;

// The service that manages annotations.

private AnnotationService service;

protected void window_Loaded(object sender, RoutedEventArgs e) { // Create the AnnotationService for your document container.

service = new AnnotationService(docReader);

// Create the annotation storage.

annotationStream = new MemoryStream();

AnnotationStore store = new XmlStreamStore(annotationStream);

// Enable annotations.

service.Enable(store);

}

• Zamiast MemoryStream (który zniknie po zakończeniu aplikacji) możemy użyć FileStream, co spowoduje zapisanie utworzonych adnotacji do pliku.

• Jeśli już były jakieś adnotacje w strumieniu, gdy wywołano AnnotationService.Enable(), wówczas od razu się one pojawią.

(40)

protected void window_Unloaded(object sender, RoutedEventArgs e) {

if (service != null && service.IsEnabled) {

// Flush annotations to stream.

service.Store.Flush();

// Disable annotations.

service.Disable();

annotationStream.Close();

} }

Uwaga: Każdy kontener dokumentu może mieć jedną instancję AnnotationService. Każdy dokument powinien mieć własną instancję AnnotationStore (przy otwieraniu nowego dokumentu, należy wyłączyć usługę, zamknąć strumień, stworzyć nowy Store i ponownie uruchomić usługę).

(41)

Dodawanie adnotacji (dwa sposoby)

Metody klasy AnnotationHelper:

• CreateTextStickyNoteForSelection()

• CreateInkStickyNoteForSelection()

• DeleteTextStickyNotesForSelection()

• DeleteInkStickyNotesForSelection()

• CreateHighlightsForSelection()

• ClearHighlightsForSelection()

Wszystkie działają dla aktualnego zaznaczenia.

Lub odpowiadające im polecenia klasy z AnnotationService.

(42)

<Window ... xmlns:annot=

"clr-namespace:System.Windows.Annotations;assembly=PresentationFramework"

...>

...

<ToolBar DockPanel.Dock="Top">

<Button

Command="annot:AnnotationService.CreateTextStickyNoteCommand"

>Text Note</Button>

</ToolBar>

• (Naciśnięcie przycisku spowoduje dodanie okienka, w którym użytkownik może pisać tekst adnotacji.)

(uwaga: gdy Button jest na toolbarze, możemy pominąć CommandTarget, gdyż polecenie samo znajdzie swój cel.)

(43)

Możemy dostarczyć dodatkowych informacji dodawanej adnotacji: np. nazwę użytkownika, który ją dodał.

<Button

Command="annot:AnnotationService.CreateTextStickyNoteCommand"

CommandParameter="{StaticResource AuthorName}">

Text Note

</Button>

Z nazwą pobraną z zasobów:

<Window.Resources>

<sys:String x:Key="AuthorName">[Anonymous]</sys:String>

</Window.Resources>

Lub wczytaną z ustawień systemowych:

(System.Security.Principal.WindowsIdentity)

WindowsIdentity identity = WindowsIdentity.GetCurrent();

this.Resources["AuthorName"] = identity.Name;

(44)

Pozostałe:

<Button

Command="annot:AnnotationService.CreateInkStickyNoteCommand"

CommandParameter="{StaticResource AuthorName}">

Ink Note

</Button>

<Button

Command="annot:AnnotationService.DeleteStickyNotesCommand">

Delete Note(s)

</Button>

Dodawanie wyróżnienia wymaga podania koloru. Jest on nakładany na tekst, a zatem powinien być półprzeźroczysty (dwie pierwsze cyfry koloru, to alpha).

<Button Background="Yellow" Width="15" Height="15"

Margin="2,0"

Command="annot:AnnotationService.CreateHighlightCommand">

<Button.CommandParameter>

<SolidColorBrush Color="#54FFFF00"></SolidColorBrush>

</Button.CommandParameter>

</Button>

(45)

<Button Background="LimeGreen" Width="15" Height="15"

Margin="2,0"

Command="annot:AnnotationService.CreateHighlightCommand">

<Button.CommandParameter>

<SolidColorBrush Color="#5432CD32"></SolidColorBrush>

</Button.CommandParameter>

</Button>

I usunięcie:

<Button

Command="annot:AnnotationService.ClearHighlightsCommand">

Clear Highlights

</Button>

Uwaga: adnotacje są drukowane, zatem przed rozpoczęciem drukowania dobrze jest wyłączyć usługę adnotacji.

(46)

Przeglądanie adnotacji

Klasa AnnotationStore pozwala również przeglądać stworzone adnotacje.

IList<Annotation> annotations = service.Store.GetAnnotations();

foreach (Annotation annotation in annotations) {...

}

Własności klasy Annotation

Id – globalny identyfikator

AnnotationType – rodzaj adnotacji

Anchors – wskazanie na adnotowany tekst (można też skorzystać z metody GetAnchorInfo() klasy AnnotationHelper).

Cargos – wskazanie na dane użytkownika dołączone do adnotacji (np. tekst notki) (niskopoziomowe)

Authors – kolekcja stringów identyfikujących autorów adnotacji

CreationTime – data i czas utworzenia

LastModificationTime – data i czas ostatniej modyfikacji

Własności adnotacji są tylko do odczytu, nie ma łatwego sposobu programistycznej manipulacji adnotacjami.

(47)

Reakcja na zmianę adnotacji Cztery zdarzenia:

AnchorChanged (gdy adnotacja jest przenoszona)

AuthorChanged (gdy zmieniana jest informacja o autorze)

CargoChanged (gdy zmieniane są dane, np. tekst)

StoreContentChanged (gdy jest tworzona, usuwana, etc.)

Dostosowanie wyglądu notatek Można ustawić styl:

<Style TargetType="{x:Type StickyNoteControl}">

<Setter Property="Background" Value="LightGoldenrodYellow"/>

</Style>

Więcej możliwości dadzą nam później szablony kontrolek.

(48)

Zachowanie adnotacji w Fixed Document

W wypadku flow documents adnotacje muszą być składowane w osobnym pliku.

W wypadku fixed documents możemy przechowywać adnotacje w pliku XPS (a nawet kilka niezależnych od siebie zbiorów adnotacji).

Uri annotationUri = PackUriHelper.CreatePartUri(

new Uri("AnnotationStream", UriKind.Relative));

Package package = PackageStore.GetPackage(doc.Uri);

PackagePart annotationPart = null;

if (package.PartExists(annotationUri))

annotationPart = package.GetPart(annotationUri);

else

annotationPart = package.CreatePart(annotationUri,

"Annotations/Stream");

AnnotationStore store =

new XmlStreamStore(annotationPart.GetStream());

service = new AnnotationService(docViewer);

service.Enable(store);

Cytaty

Powiązane dokumenty

zyka niż człowieka, wtedy jednak powoływałoby się do istnienia nową total ­ ność, na gruncie której możliwa byłaby ciągła historia, historia dyskursu jako nauka

Miernik Mocy Złącze Sondy do Inspekcji Wideo Quad / PON OTDR Quad / PON OTDR /.

• Jeśli chcemy aby szablony były używane w obrębie wielu aplikacji należy stworzyć osobny słownik zasobów (Resource Dictionary) – Solution Explorer -&gt; Add -&gt;. New

Zapis rozpoczyna się w sposób typowy dla bloku przedsionkowo­komorowego II stopnia typu I, po którym zamiast skrócenia widać wydłużenie odstępu PQ, czyli od razu

W 2011 roku, już z mniejszą „śmiałością”, opisałbym ten zapis jako „normę” — pojawiło się opracowanie wskazujące na to, że obecność sko- śnych ku górze uniesień

Profesor Krzysztof Simon, kierownik Kliniki Chorób Zakaźnych i Hepatologii Uniwersytetu Medycznego we Wrocławiu, przyznaje, że młodzi ludzie w stolicy województwa

W związku z obowiązkową przerwą w funkcjonowaniu szkoły w celu zapobiegania szerzenia się koronowirusa zmuszeni jesteśmy do pracy przez Internet i za pomocą maila..

2 Na szczękę chwytną potrzeba jednego zestawu części luźnych szczęki wymiennej 3 oraz części stałej szczęki wymiennej. Mocowanie chwytaka za pomocą