piątek, maj 29, 2009

70-502 (WPF) Przygotowania...nr 16.2 - Walidacja

Czasami podczas wprowadzania danych zachodzi potrzeba ich walidacji (a w zasadzie zawsze jest). W tym odcinku zobaczymy jak możemy takie reguły walidacyjne do naszego interface'u. Możemy to zrobić na dwa sposoby. Pierwszy z nich to napisanie własnej metody walidacji, drugi to przekazanie wszystkich wystąpionych wyjątków jako elementów systemu walidacji. Zobaczmy to na przykładach. Aby napisać nową klasę walidacji jedyne co musimy zrobić to napisać nową klasę, która dziedziczy po klasie ValidationRule. Najprostsza klasa ma postać:


public class AlwaysFailingValidationRule : ValidationRule


{


    public override ValidationResult Validate(object value, CultureInfo cultureInfo)


    {


        return new ValidationResult(false, "Always failing");


    }


}


Ta prosta reguła zawsze będzie zwracać fałsz przy walidacji danych. Jak tego użyć w XAMLu?


<TextBox x:Name="f" />


<TextBox x:Name="fileName">


    <TextBox.Text>


        <Binding Path="Text" ElementName="f" Mode="TwoWay">


            <Binding.ValidationRules>


                <local:AlwaysFailingValidationRule />


            </Binding.ValidationRules>


        </Binding>


    </TextBox.Text>           


</TextBox>


Metoda ta jest wywoływana jeszcze przed przekazaniem danej do ewentualnego konwertera. Jeśli chcemy "upiększyć" wygląd kontrolki w przypadku błędu, możemy to zrobić definiując wygląd tego stanu w elemencie Validation.ErrorTemplate.

Wyjątki

Możemy też skorzystać w dostarczanego wraz z WPF elementu


<ExceptionValidationRule />


W takim przypadku wysztkie wyjątki jakie się pojawią będą traktowane jako złamanie reguł walidacji.

Klasa Binding

Jeśli tworzymy binding z kodu, możemy posłużyć się właściwością UpdateSourceExceptionFilter i w nim wpiąć trochę kodu, odpowiedzialnego za logikę.

Następnym razem będzie o stylach w WPF.

C2C was potrzebuje

Byłeś/byłaś na C2C'09? Podobała ci się konferencja i popierasz ideę za nią stojącą?


Wspomóż C2C!



Oficjalna notka organizatorów poniżej:

Przygotowania do konferencji C2C 2009 rozpoczęły się w październiku 2008 roku. Od strony finansowej całe przedsięwzięcie obsługiwać miało stowarzyszenie PDG, które rozliczało również C2C 2008. Na początku 2009 roku PDG zrezygnowało ze współpracy z grupami off-line. Dla organizatorów C2C 2009 oznaczało to brak możliwości rozliczenia konferencji C2C. Na szczęście dzięki uprzejmości fundacji PROIDEA stan taki nie trwał długo. Konferencja szczęśliwie została zorganizowana. Niestety stowarzyszenie PDG do dnia dzisiejszego nie przelało wszystkich środków C2C na konto PROIDEI. Z tego powodu budżet konferencji posiada niedobór środków na kwotę ponad 6 000 PLN. Dlatego też zwracamy się do wszystkich osób, którym idea konferencji jest blisko o pomoc.

Każda kwota się liczy. Z góry dzięki. Ja już swoją cząstkę przelałem.

Edit: Żeby nie było, że wyciągam kasę to ta sama notka na stronie głównej konferencji.

czwartek, maj 28, 2009

Fun: Nowy system operacyjny Microsoftu

Zainteresowałem się ostatnio nowym (a może już nie nowym) notebookiem od Dell'a a mianowicie - Mini 10v. Stwierdziłem, że przyjrzę się mu bliżej tak więc zabrałem się za konfigurację maszynki. W pewnym momencie przyszło do konfigurowania systemu operacyjnego. Trochę się zdziwiłem widzą taką możliwość wyboru jak poniżej:



Innych opcji do wyboru nie ma. Wygląda zatem na to, że MS wchodzi z nowym systemem operacyjnym :). Micorosft XP SP 3 jest dostępny w innych wersjach notebooka, ale chyba powinni zastąpić ten tekst "System operacyjny firmy Microsoft" po prostu na "System operacyjny" :).

środa, maj 27, 2009

70-502 (WPF) Przygotowania...nr 16.1

W poprzednim odcinku omówiony (pobieżnie) został mechanizm DataBindingu. Dziś powiemy sobie co robić, abyśmy mogli w trochę większym stopniu niż dotychczas kontrolować to w jaki sposób wyświetlamy nasze dane.

ValueConverters

Załóżmy, że na potrzeby tego wpisu mamy prostą klasę:


public class Device


{


    public bool Active { get; set; }


}


Oczywiście chcemy jej właściwość Active wyświetlić na UI i oczywiście posługujemy się bindingiem.


<CheckBox Content="Active?" IsChecked="{Binding Active}" /> 


(źródło danych ustawiamy za pomocą DataContext). Co jednak, gdy wartości tej nie chcemy wyświetlić za pomocą klasy CheckBox a np. obrazka? Z pomocą przychodzi nam ValueConverter. Oczywiście nie będziemy zmieniać naszej zaawansowanej logiki, aby zwracała obrazek. Napiszemy odpowiednią klasę.


public class BoolToImageConverter : IValueConverter


{


    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)


    {


        if (targetType != typeof(ImageSource))


            throw new InvalidOperationException("The target must be a ImageSource");


 


        return (bool) value


                  ? new BitmapImage(new Uri("Yes.png", UriKind.Relative))


                  : new BitmapImage(new Uri("No.png", UriKind.Relative));


    }


 


    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)


    {


        throw new NotSupportedException();


    }


}


Nasza klasa konwertera jest niezwykle prosta. Implementujemy interface IValueConverter i tworzymy implementację metod Convert oraz ConvertBack. W naszym przypadku udostępniamy tylko konwersją w jedną stronę. Na podstawie wartości parametru zwracamy odpowiedni obrazek. Dodatkowo nasz metoda mogłaby brać pod uwagę aktualną kulturę i wyświetlać obrazek w zależności od tej. Jak tego użyć?
Deklarujemy nasz konwerter:


<Window.Resources>


    <local:BoolToImageConverter x:Key="BoolToImageConverter" />


</Window.Resources>


A następnie używamy przy bindingu


<Image Source="{Binding Path=Active, Converter={StaticResource BoolToImageConverter}}" Width="20" Height="20"/>


Efekt końcowy?

Ładniutki plusik :)

Templatki

Wiele z kontrolek udostępnia możliwość zdefiniowana wyglądu danych, które prezentują. Robi się to za pomocą definiowania nowego wyglądu w tagu DataTemplate. Zobaczmy jak to działa na przykładzie klasy ListBox. Stwórzmy sobie klasę do przechowywania zdjęć wraz z ich nazwą:


public class File


{


    public string Name { get; set; }


    public string Path { get; set; }


}


oraz wypełnijmy ją jakimiś danymi


const string path = @"C:\Users\pawlos\Pictures\Sample Images\Nature";


var files = from file in Directory.GetFiles(path, "*.jpg")


            select new File() {Name = Path.GetFileName(file), Path = file};


a nasz ListBox zdefiniujemy następująco


<ListBox x:Name="listbox">


    <ListBox.ItemTemplate>


        <DataTemplate>


            <StackPanel>


                <Label Content="{Binding Name}" />


                <Image Source="{Binding Path}" Height="50" />


            </StackPanel>


        </DataTemplate>


    </ListBox.ItemTemplate>


</ListBox>


W efekcie dostaniemy ładnie wyświetlone zdjęcia wraz z nazwą odpowiadającego pliku

Na dziś tyle. Następnym razem będzie o walidacji podczas bindowania.

poniedziałek, maj 25, 2009

70-502 (WPF) Przygotowania...nr 16 - Data Binding

Dzisiejszy odcinek poświęcony będzie zagadnieniu DataBindning. Zagadnienie to nie jest nowe - mamy je także w Win i WebForms, jednak w przypadku technologii WPF nabiera ono zupełnie nowego wymiaru.

Binding w kodzie

Możemy to zrobić w prosty sposób za pomocą elementy Binding


Binding binding = new Binding();


binding.Source = tekst;


binding.Path = new PropertyPath("Text");


label.SetBinding(TextBlock.TextProperty, binding);


Dzięki takiemu zabiegowi tekst, tekst który wprowadzimy pojawi się także w polu label. Binding taki będzie aktywny, aż do czasu, aż go nie wyczyścimy:


BindingOperations.ClearBinding(label, TextBlock.TextProperty);


To samo można osiągnąć, poprzez jawne podstawienia wartości pod docelową właściwość (w tym przypadku Text.

XAML

Podobny zabieg bindowania możemy oczywiście zdefiniować w języku XAML.


<TextBox x:Name="tekst" />


<TextBlock x:Name="label" Text="{Binding ElementName=tekst, Path=Text}" />


Warto zauważyć, iż posługujemy się właściwością ElementName. Możemy oczywiście użyć Source, ale wtedy nasz element musielibyśmy zdefiniować w słowniku ResourceDictionary i odwoływać się do niego za pomocą StaticResource.

Bindowanie do prostych właściwości w .NET

Jeśli chcemy umożliwić binding w naszych klasach musimy odpowiednio je przygotować. Mamy do wyboru dwa podejścia (preferowane pierwsze):

  • implementujemy w naszej klasie interface INotifyPropertyChanged, który zawiera jeden wspólny event PropertyChanged

  • implementujemy eventy XXXChanged dla każdej z właściwości, którą chcemy móc bindować

Dodatkowo jeśli nasza klasa jest kolekcją i chcemy do niej bindować warto skorzystać z klasy ObservableCollection<T>, która już ma zaimplementowane notyfikacje dla operacji na niej (Add, Remove, Clear). Bindowanie to obiektów, które nie dziedziczą po klasie UIElement spowoduje, że w wyniku dostaniemy zwrócony tekst z wykonania metody ToString().

Bindowanie kolekcji

Bindując kolekcję zapewne posłużymy się, jedną z kontrolek umożliwiająca wyświetlenie kolekcji elementów - np. ListBox. W przypadku tej kontrolki element źródłowy podpinamy do właściwości ItemsSource. Jeśli chcemy wyświetlać element inny niż wynik wykonania metody ToString powinniśmy ustawić właściwość DisplayMemberPath. Jeśli chcemy współdzielić dany obiekt źródłowy bindowania przez wiele kontrolek powinniśmy ustawić go jako wartość właściwości DataContext a na poszczególnych kontrolkach bindować do poszczególnych właściwości.

Następnym razem będzie trochę o kontrolowaniu tego jak wyglądają nasze zbindowane elementy - ValueConverter, DataTemplate.

sobota, maj 23, 2009

Visual Studio 2010 Beta 1 - Debugger

Wszyscy zabierają się za testowanie najnowszej wersji Visual Studio 2010 Beta 1, tak więc postanowiłem i ja się przyjrzeć co tam jeszcze ciekawego na nas czeka. Na pierwszy ogień poszła funkcjonalność debuggera.

VS 2010 umożliwia nam eksport i import zdefiniowanych e naszej aplikacji breakpointów.

Breakpointy zapisywane są w pliku XML, tak więc można je edytować ręcznie w pliku. Oczywiście breakpointy są zapamiętywane jako ustawione w danej linii tak więc gdy dodamy dodatkowe linie "psując" stary porządek linii, breakpointy gubią się (ale to raczej oczywiste). Dodatkowo breakpointom możemy nadawać etykiety.

Zastanawiam się nad potrzebą takich funkcjonalności. Jedyne jakie widzę (grubo naciągane) to szybkie przekazanie ustawienia skomplikowanych breakpointów w przypadku debuggowania skomplikowanego problemu. Jednak być może funkcjonalność ta wykorzystywana jest w przypadku "historycznego debuggera"

piątek, maj 22, 2009

70-502 (WPF) Przygotowania...nr 15 - Resources

Dziś będzie poruszony temat zasobów w WPF. A mówiąc ściślej zasobów binarnych. Zasoby te mogą być umieszczone w 3 różnych miejscach. Mogą być:

  • wewnątrz assembly

  • "luźne" pliki znane aplikacji w czasie kompilacji

  • "luźne" pliki nieznane aplikacji podczas kompilacji

Gdy dodajemy nowy zasób do wyboru mamy mnóstwo opcji:

Dla nas istotne są:

  • Resource - umieszcza zasób w assembly

  • Content - pozostawia zasób jako "luźny" plik, ale odnotowuje fakt jego istnienia poprzez dodanie odpowiedniego atrybutu (AssemblyAssociateContentFile)

Lista duża, ale warto zwrócić uwagę na to czego nie stosować. Embedded Resource - jest to sposób znany sprzed WPF i powinien być unikany w aplikacjach.
Zanim przejdziemy do omówienia jak dostawać się do zasobów, mała uwaga. Jeśli chcemy aby nasze zasoby były lokalizowalne, należy umieszczać je jako Resource

Dostęp do zasobów

Aby użyć zasobów zdefiniowanych jako Resource wystarczy napisać:


<Image Source="conan_doyle.gif" />


Prawdziwy problem z zasobami w WPF jest z rozszyfrowaniem ścieżki URI. Ta, bowiem może przyjmować wiele form. Spróbujmy pokrótce je omówić:

  • A/B/plik.jpg - zasób znajduje się w danym assembly, lub jest to "luźny" plik. A/B/ definiują strukturę podkatalogów (jeśli występują).

  • c:\folder\plik.jpg lub file://c:/folder/plik.jpg - "luźny" plik w folderze c:\folder

  • PlikDll;Component/A/B/plik.jpg - zasób zawarty w innym assembly (.dll lub .exe) znajdujący się w ewentualnym podkatalogu A/B/

  • pack://siteOfOrigin:,,,/A/B/plik.jpg - "luźny" plik znajdujący się w katalogu aplikacji (lub w podkatalogu A/B/ jeśli zdefiniowane)

Lokalizowanie zasobów

Lokalizowanie WPF'a to troszkę uciążliwy proces. Składa się wygenerowaniu unikalnych identyfikatorów dla każdego elementu naszego pliku XAML (można ro zrobić za pomocą narzędzia msbuild - msbuild.exe /t:updateuid NazwaProjektu.csproj). Następnie za pomocą narzędzia LocBaml generujemy plik .csv, w którym będziemy mogli przetłumaczyć na dowolny język. Dokładny opis można znaleźć w tym artykule.

Następnym razem będzie temat DataBindingu (co prawda na liście w kolejności jest temat obrazków - ale nie wiem co jeszcze w tym temacie można napisać).

sobota, maj 16, 2009

70-502 (WPF) Przygotowania...nr 14 - Multimedia

Dziś temat multimediów. WPF jako nastawiony na interface użytkownika ma dużo do powiedzenia w tej kwestii. Zacznijmy zatem po kolei przyglądać się co mamy do dyspozycji.

SoundPlayer

To najprostsza klasa, którą mamy do dyspozycji jeśli chcemy odtworzyć dźwięki.


SoundPlayer player = new SoundPlayer("plik.wav");


player.Play();


Ze względu na swoją prostotę ma też swoje ograniczenia.

  • odtwarza tylko pliki .wav

  • nie ma wsparcia dla wielu dźwięków odtwarzanych jednocześnie

  • nie ma możliwości zmiany głośności

SystemSounds

Jeśli chcemy odegrać proste dźwięki systemowe, możemy skorzystać z tej klasy. definiuje ona kilka podstawowych dźwięków, każdy z których udostępnia metodę Play.


SystemSounds.Asterisk.Play();


SoundPlayerAction

Aby umożliwić deklaratywne odtwarzanie dźwięków powstała ta klasa. Dzięki niej możemy bezpośrednio w XAMLu zdefiniować jaki dźwięk ma zostać odgrany, gdy jakieś zdarzenie zajdzie. Zdefiniujmy odtwarzanie pliku, gdy przycisk zostanie naciśnięty:


       <Button Content="play">


            <Button.Triggers>


                <EventTrigger RoutedEvent="Button.Click">


                    <EventTrigger.Actions>


                        <SoundPlayerAction Source="click.wav" />


                    </EventTrigger.Actions>


                </EventTrigger>


            </Button.Triggers>


        </Button>


MediaPlayer

To klasa dużo bardziej rozbudowana niż wspomniany wcześniej SoundPlayer. Nie tylko wspiera więcej formatów niż poprzednik (audio i wideo) to dodatkowo posiada funkcje umożliwiające sterowanie odtwarzanym zasobem jak i odpowiednią jego konfigurację - np. przewijanie, regulacja głośności, pobranie długości klipu itp. Jak tego użyć. Wyłącznie z kodu:


MediaPlayer player = new MediaPlayer();


player.Open(new Uri("roar.wma", UriKind.Relative));


player.Play();


Tak jak wspomniałem, MediaPlayer ma dosyć duże możliwości, ale jest używalna tylko z kodu. Aby wykorzystać pełną moc XAML'a musimy skorzystać z następnych klas.

MediaElement oraz MediaTimeline

MediaElement to XAMLowy odpowiednik klasy MediaPlayer. Umożliwia nam te sam właściwości a przy okazji jest dostępny jako element języka XAML.


<StackPanel>


    <MediaElement Source="roar.wma" />


</StackPanel>


MediaTimeline natomiast, jest tym dla klasy MediaElement czym SoundPlayerAction jest dla SoundPlayer. Możemy zatem użyć jej w eventach i animacjach.


<MediaElement x:Name="audio" />


<Button Content="Hover">


    <Button.Triggers>


        <EventTrigger RoutedEvent="Button.MouseEnter">


            <BeginStoryboard x:Name="playStoryboard">


                <Storyboard>


                    <MediaTimeline Source="roar.wma" Storyboard.TargetName="audio" />


                </Storyboard>


            </BeginStoryboard>


        </EventTrigger>


        <EventTrigger RoutedEvent="Button.Click">


            <StopStoryboard BeginStoryboardName="playStoryboard" />                   


        </EventTrigger>


    </Button.Triggers>


</Button>


Wideo

Do wideo używamy ostatnio omawianej klasy MediaElement jednak w przypadku wideo, mamy do dyspozycji kilka dodatkowych właściwości, które warto omówić.
Możemy dla przykładu przyciąć wideo oraz wyświetlać je z półprzeźroczystością.


<Button Content="I'm below the video" />


<MediaElement x:Name="video" Source="walker.wmv" Opacity="0.7" >


    <MediaElement.Clip>


        <EllipseGeometry Center="80,80" RadiusX="180" RadiusY="180" />


    </MediaElement.Clip>


</MediaElement>


Możemy też reagować na błędy związane z danymi za pomocą eventu MediaFailed.

Speech

WPF daje nam też możliwość zamiany tekstu w mowę oraz operację odwrotną czyli rozpoznawanie mowy i zamianę jej na formę tekstową. Aby skorzystać z tej funkcjonalności należy dodać do projektu System.Speech.dll.
Aby zmusić naszą aplikcję do wypowiedzenia pierwszych słów, należy skorzystać z klasy SpeechSynthesizer.


SpeechSynthesizer speech = new SpeechSynthesizer();


speech.Speak("I'm talking");


Można także robić to asynchronicznie za pomocą funkcji SpeakAsync. Oczywiście oprócz tego mamy jeszcze dużo więcej możliwości jak chociażby wybór głosu.


speech.SelectVoiceByHints(VoiceGender.Female, VoiceAge.Teen);


Oczywiście odpowidnie głosy musimy ściągnąć - są one dostępne w Speech SDK.
Oczywiście mamy dużo większe możliwości konfiguracyjne. Za pomocą klasy PromptBuilder. Umożliwia ona zbudowanie całej wypowiedzi wraz z odpowiednią intonacją: przerwami, przyśpieszaniem wypowiedzi lub jej zwalnianiem.


PromptBuilder builder = new PromptBuilder(CultureInfo.CurrentUICulture);


builder.AppendTextWithHint("WPF", SayAs.SpellOut);


builder.AppendText("jest");


builder.AppendBreak(PromptBreak.Small);


builder.AppendText("ekstra", PromptEmphasis.Strong);


 


SpeechSynthesizer speech = new SpeechSynthesizer();


speech.Speak(builder);


To tylko mały pokaz możliwości tej klasy.

Rozpoznawanie mowy

WPF udostępnia także rozpoznawanie głosu:


SpeechRecognizer recognizer = new SpeechRecognizer();


recognizer.SpeechRecognized += recognizer_SpeechRecognized;


Oczywiście i w tym przypadku możemy więcej kontrolować za pomocą gramatyki. Jednak jest to dosyć zaawansowana sprawa i nie sądzę, aby jej znajomość była bardzo wymagana na egzaminie - MSDN on GrammerBuilder

Na dziś tyle. Następnym razem będzie o zasobach w WPF.

Bug w WebFormsach?

Załóżmy, że mam sobie taką oto strukturę elementów na stronie WWW:


    <div id="outer1">


        <div id="inner" />


    </div>


    <div id="outer2"></div>


Wygląda, że wszystko jest w porządku, jednak gdy uruchomimy taką stronę efekt wynikowy będzie zupełnie nie taki, jakiego byśmy się mogli spodziewać. Wynik w Firebugu:


    <div id="outer1">


        <div id="inner"> </div>


        <div id="outer2"/>


    </div>


Co jest? Nasz zewnętrzny element div (outer2) został wciągnięty do środka pierwszego zewnętrznego elementu div (outer1).
Co powoduje takie dziwnie zachowanie? Po kilku chwilach zdziwienia, zwątpienia i rozczarowania udało się ustalić, że za złe zachowanie odpowiedzialny jest ten element:


    <div id="inner" />


Tak zamknięty element powodował to zachowanie. Wystarczy zmienić go na <div></div> a wszystko wróci do normy. Można powiedzieć, że mały to problem, ale często męczący. Ja np. praktycznie z automatu dodaję "niepoprawnie" zamknięty tag jak tworzę layout i potrzebuję mieć element, który będzie miał ustawiony clear:both.

Zawszę piszę po prostu:
<div class="clear" /> i oczywiście wpadam w tę pułapkę. Mam nadzieję, że może komuś to pomoże i nie straci za dużo czasu na zastanawianiu się gdzie jest błąd. A może to tylko u mnie występuje ten efekt? Ktoś potwierdzi powtarzalność?

piątek, maj 15, 2009

70-502 (WPF) Przygotowania...nr 13 - Documents

Dziś, zgodnie z zapowiedzią z poprzedniego posta, miało być o Materials. Jednak o tym nie będzie. Dlaczego? Posty te traktuję jako przygotowanie do egzaminu, i po przyjrzeniu się stwierdziłem, że tak wnikliwe opisywanie tematu nie jest konieczne przy egzaminie. Oby da decyzja się nie zemściła :).

Documents

Dziś zatem będzie o dokumentach. Dokumenty definiuje się za pomocą znacznika FlowDocument.


    <FlowDocument>


        <Paragraph FontSize="22">70-502 (WPF) Przygotowania...nr. 13</Paragraph>


        <Paragraph>Dziś, zgodnie z zapowiedzią z poprzedniego posta, miało być o


            <Span FontFamily="Consolas">Material</Span> s. Jednak o tym nie będzie. Dlaczego? Posty te traktuję jako przygotowanie do egzaminu, i po przyjrzeniu się stwierdziłem, że tak wnikliwe opisywanie tematu nie jest konieczne przy egzaminie. Oby da decyzja się nie zemściła :).


        </Paragraph>


        <Paragraph FontSize="22">Documents</Paragraph>


        <Paragraph>Dziś zatem będzie o dokumentach.</Paragraph>


    </FlowDocument>


Jak widzimy definicja dokumentu jest banalna. Definiujemy po prostu kolejne paragrafy, które zawierają tekst, który ma się pojawić w dokumencie. Po uruchomieniu naszym oczom ukaże się takie okno.

Okno standardowo zawiera przyciski do manipulacji dokumentem takie jak zmiana strony, powiększanie czy pomniejszanie. Jakie elementy mamy dostępne do budowania dokumentów?

Klocki

  • Paragraph - podstawowy element tworzący dokumenty. Składa się z podrzędnych elementów. W przypadku, gdy paragraf zawiera tylko tekst, tworzony jest element Run, który zawiera nasz tekst.

  • Section - grupuje elementy bez definiowania odgórnej ich struktury. Przydatne jeśli chcemy nadać identyczne właściwości większej ilości elementów w dokumencie.

  • List - umożliwia utworzenie listy elementów w dokumencie. Dużo możliwości ustalenia numeracji elementów - właściwość MarkStyle.

  • Table - odpowiednik znacznika TABLE z HTML

  • BlockUIContainer - umożliwia hostowanie elementu UIElement wewnątrz dokumentu

Zobaczmy jak to wygląda w praktyce:


    <FlowDocument>


        <Section FontFamily="Consolas" FontSize="12">


            <Paragraph>


                <Span>Jakiś tekst w pierwszej linii.</Span>


                <Span>Ta linia będzie miała takie same parametry jak poprzednia linia.</Span>


            </Paragraph>


        </Section>


        <List MarkerStyle="Box">


            <ListItem>


                <Paragraph>Pozycja 1</Paragraph>


            </ListItem>


            <ListItem>


                <Paragraph>Pozycja 2</Paragraph>


            </ListItem>


            <ListItem>


                <Paragraph>Pozycja 3</Paragraph>


            </ListItem>


        </List>


        <Table>


            <TableRowGroup>


                <TableRow>


                    <TableCell>


                        <Paragraph>Cell 1,1</Paragraph>


                    </TableCell>


                    <TableCell>


                        <Paragraph>Cell 1,2</Paragraph>


                    </TableCell>


                </TableRow>


                <TableRow>


                    <TableCell>


                        <Paragraph>Cell 2,1</Paragraph>


                    </TableCell>


                    <TableCell>


                        <Paragraph>Cell 2,2</Paragraph>


                    </TableCell>


                </TableRow>


            </TableRowGroup>


        </Table>


        <BlockUIContainer>


            <Button Content="Przycisk w dokumencie" />


        </BlockUIContainer>


    </FlowDocument>


Przycisk oczywiście nadal działa, tak jak w normalnej aplikacji.

Elementy inline

Prócz elementu Span mamy do dyspozycji kilka predefiniowanych elementów, które przyśpieszają pisanie dokumentu.


        <Paragraph>


            <Bold>Jakiś tekst boldem</Bold>


            <Italic>Kursywa</Italic>


            <Underline>Podkreślenie</Underline>


            <Hyperlink>Hyperlink</Hyperlink>


            <Span BaselineAlignment="Subscript">indeks dolny</Span>


            <Span BaselineAlignment="Superscript">indeks górny</Span>


            <Span>


                <Span.TextDecorations>


                    <TextDecoration Location="Strikethrough" />


                </Span.TextDecorations>


                przekreślenie


            </Span>


        </Paragraph>


W przypadku dokumentów mamy możliwość umieszczenia "dokowalnych" elementów. Robimy to za pomocą Figure oraz Floater. Figure umożliwia przypięcie elementu do odpowiedniej części dokumentu.


            <Figure VerticalAnchor="ContentTop">


                <BlockUIContainer>


                    <Image Source="braille.gif" />


                </BlockUIContainer>


            </Figure>


Wyświetlanie dokumentu

Jak możemy wyświetlić taki dokument? Do dyspozycji mamy trzy klasy.

  • FlowDocumentScrollViewer - wyświetla dokument jako dokument wraz z paskami przewijania.

  • FlowDocumentPageViewer - dokument wyświetlany jako pojedyncze strony z możliwością nawigacji pomiędzy nimi.

  • FlowDocumentReader - łączy możliwości poprzednich dwóch sposobów wyświetlania dodając kika zaawansowanych właściwości jak np. możliwość wyszukiwania tekstu.

Adnotacje

WPF umożliwia tworzenie adnotacji, do umieszczenia w dokumencie. WPF udostępnia kilka poleceń, które umożliwiają tworzenie notatek.

  • CreateTextStickyNoteCommand - umożliwia stworzenie tekstowej notatki dla zaznaczanego tekstu

  • CreateInkStickyNoteCommand - tworzy notatkę, na której możemy pisać ręcznie.

  • DeleteStickyNotestCommand - usuwa aktualnie zaznaczoną notatkę

  • CreateHightlightCommand - zaznacza tekst na kolor przekazany jako parametr.

  • ClearHighlightsCommand - usuwa zaznaczenie


        <StackPanel Orientation="Horizontal">


            <Button Command="a:AnnotationService.CreateInkStickyNoteCommand" CommandTarget="{Binding ElementName=reader}">Nowa notatka</Button>


            <Button Command="a:AnnotationService.DeleteStickyNotesCommand" CommandTarget="{Binding ElementName=reader}">Usuń notatkę</Button>


            <Button Command="a:AnnotationService.CreateHighlightCommand" CommandTarget="{Binding ElementName=reader}"


                   CommandParameter="{x:Static Brushes.Yellow}">Zaznacz tekst na zielono</Button>


            <Button Command="a:AnnotationService.ClearHighlightsCommand" CommandTarget="{Binding ElementName=reader}">Usuń zaznaczenie</Button>


        </StackPanel>


Musimy jeszcze aktywować adnotacje.


            AnnotationService service = AnnotationService.GetService(reader);


            if (service == null)


            {


                stream = new FileStream("storage.xml", FileMode.OpenOrCreate);


                service = new AnnotationService(reader);


                AnnotationStore store = new XmlStreamStore(stream);


                store.AutoFlush = true;


                service.Enable(store);


            }


Przy wychodzeniu z aplikacji, należy jeszcze zamknąć strumień.

Na dziś tyle, następnym razem będzie trochę o multimediach.

środa, maj 13, 2009

70-502 (WPF) Przygotowania...nr 12.9

Dziś będzie trochę informacji odnośnie światła w scenach 3D w WPF. Światło jest dosyć ważne, gdyż to od niego zależy jak nasz obiekt będzie się prezentował. W WPF dostępnych mamy kilka ich rodzajów:

  • DirectionalLight

  • PointLight

  • SpotLight

  • AmbientLight

Jak ich używać? Światło definiujemy jak element w tagach ModelVisual3D.Content


<ModelVisual3D>


    <ModelVisual3D.Content>


        <AmbientLight />


    </ModelVisual3D.Content>


</ModelVisual3D>


DirectionalLight

Światło to symuluje źródło światła oddalone tak bardzo, że promienie są praktycznie równoległe.


<DirectionalLight Direction="-0.612372,-0.5,-0.612372" Color="Red" />


W tym przypadku wystarczy zdefiniować kierunek "świecenia" oraz kolor.

PointLight

W tym przypadku definiujemy światło punktowe rozchodzące się we wszystkich kierunkach.


<PointLight ConstantAttenuation=".1"


          LinearAttenuation="0"


          QuadraticAttenuation="0.0125"


          Color="Red"


          Position="2,2,2" />



Ponownie pozycję i kolor definiujemy przez właściwości Position oraz Color. Jednak w tym przypadku definiujemy dodatkowo jak bardzo światło ma przygasać wraz z odległością. Dokładny wzór można znaleźć tu. Tak zdefiniowane światło da nam ładny gradient na naszej powierzchni.

SpotLight

Ten typ światła to można powiedzieć typ "lampki". Skupione w wiązkę światła typu PointLights. Prócz właściwości dostępnych jak przy PointLight mamy dostępne InnerConeAngle oraz OuterConeAngle definiujące odpowiednio wewnętrzny i zewnętrzny kąt rozpraszania naszej wiązki.

AmbientLight

Ostatni typ światła. Stosowany zwykle jako dodatkowe jego źródło. Pozwala utworzyć efekt światła rozproszonego na różnych powierzchniach. Dzięki jego dodaniu uzyskujemy ładniejszy wygląd elementów na naszej scenie poprzez bardziej naturalne jej oświetlenie. W tym przypadku definiujemy tylko kolor.


<AmbientLight Color="Pink"/>



Na dziś tyle. W następnej części będzie o materiałach, z których są tworzone nasze elementy na scenie 3D. klasie FlowDocument

wtorek, maj 12, 2009

70-502 (WPF) Przygotowania...nr 12.8

Po kolejnej przerwie powracamy do kursu o WPF a konkretniej dokończenia fragmentu o grafice 3D.

Przekształcenia 3D

Dostępne mamy te same transformacje co w przypadku dwuwymiarowych grafik: TranslateTransform3D, ScaleTransform3D, RotateTransform3D, MatrixTransform3D oraz Transform3DGroup.

Jak ich użyć?


<ModelVisual3D>


    <ModelVisual3D.Transform>


        <x:Static Member="Transform3D.Identity" />


    </ModelVisual3D.Transform>


Tak więc po prostu tworzymy podrzędny tag o nazwie Transform, w którym będziemy definiować nasze przekształcenia. Dodatkowo widzimy jak możemy użyć przekształcenia tożsamościowego.

Myślę, że nad większością transformat nie ma co się rozpisywać. Dochodzi dodatkowo koordynata Z. (ScaleZ, OffsetZ, CenterZ). Ciekawy natomiast jest Obrót. Możemy go definiować za pomocą obrotu wokół osi (AxisAngleRotation3D) lub jako kwaternion (QuaternionRotation3D). Obrót definiujemy za pomocą:


<RotateTransform3D>


    <RotateTransform3D.Rotation>


        <AxisAngleRotation3D Angle="40" Axis="0,1,0" />


    </RotateTransform3D.Rotation>


</RotateTransform3D>


gdzie Axis definiujemy wokół, której osi obracamy. Z kwaternionem będzie tak:


<RotateTransform3D.Rotation>


    <QuaternionRotation3D Quaternion="0.3,0.1,0,0.1" />


</RotateTransform3D.Rotation>


definiując odpowiednie wartości kwaternionu.

Na dziś tyle o przekształceniach 3D. W następnym wpisie będzie o świetle.