środa, 16 stycznia 2013

70-516 DataView

DataView umożliwia sortowanie i filtrowanie danych zawartych w obiekcie DataTable. Jeden obiekt DataTable może posiadać wiele widoków (DataView), dzięki czemu nie ma potrzeby za każdym razem pobierać danych z źródła danych jakim np. jest baza danych.

Sortowanie
Sortowanie konfigurujemy w bardzo prosty sposób. Do zmiennej Sort przypisujemy kolumny po których chcemy przeprowadzić sortowanie i podajemy kierunek: rosnąco - ASC, malejąco - DSC. Jeżeli chcemy sortować po więcej niż jednej kolumnie, kolejne definicje sortowania wprowadzamy po przecinku:

Code:
dataView.Sort = "tytul ASC, rezyser DESC";


Filtrowanie wyników
Wyniki możemy filtrować zarówno pod względem zawartości, jak i stanu wiersza.
Właściwość, która pozwala filtrować wiersze pod względem zawartości danych jest RowFilter. Składnia właściwi jest taka sama jak w SQLu. Przykład:

Code:
dataView.RowFilter = "nosnik = 'dvd' and tytul like '%a%'";

Właściwość RowStateFilter pozwala przeszukiwać dane według stanu wiersza obiektu DataTable. Właściwość ta przyjmuje wartości zawarte w typie wyliczeniowym DataViewRowState.
Możliwe wartości:
  • Added 
  • CurrentRows
  • Deleted
  • ModifiedCurrent
  • ModifiedOriginal
  • None - czyści filtr
  • OriginalRows
  • Unchanged

Przykład:

Code:
dataView.RowStateFilter = DataViewRowState.Unchanged;


Iteracja po obiekcie DataView
Jest bardzo podobna do iteracji po obiekcie DataTable:

Code:
            foreach (DataRowView row in dataView)
            {
                for (int i = 0; i < dataView.Table.Columns.Count; i++)
                {
                    Console.Write(row[i] + "  ");
                }
                Console.WriteLine();
            }



Eksport DataView do DataTable
Operacja ta może się przydać, gdy chcemy stworzyć nowy obiekt DataTable np. na podstawie zawężonych danych (użytkownik ustawi filtrowanie, sortowanie itd.). Dodatkowo możemy zdefiniować kolumny które będą zawarte w nowej tabeli. Przeprowadzamy ją za pomocą metody ToTable().
Przykład:

Code:
var newTable = dataView.ToTable("NewTable", false, "nosnik", "tytul");

sobota, 12 stycznia 2013

70-516 Bezpołączeniowy model cz. 2

W poprzednim poście rozpocząłem omawianie obiektu DataTable. W tym poście kontynuuje rozpoczęty temat.

Stan wiersza
Wiersze w obiekcie DataTable zmieniają swoje podczas całego życia obiektu. Sprawdzając właściwość RowState możemy dowiedzieć w jakim stanie jest obecnie wiersz. Wartości tego typu wyliczeniowego:
  • Detached - wiersz został stworzony, ale nie dodany do tabeli
  • Added - wiersz został stworzony i dodany do tabeli
  • Unchanged - stan wiersza nie zmienił się od ostatniego wywołania metody AcceptChanges; w momencie wywołania metody AcceptChanges wiersz przyjmuje ten stan
  • Modified - wiersz zmienia się w ten stan tylko jeżeli poprzednio miał stan Unchanged; po wywołaniu AcceptChanges wiersz zmienia swój stan na Unchanged
  • Deleted - wiersz przyjmuje ten stan gdy zostanie usunięty z tabeli
Przykład obrazujący zmianę stanów wiersza:

Code:
        static void Main(string[] args)
        {
            var clientTable = new DataTable("Client");
            var idColumn = new DataColumn("Id");
            idColumn.DataType = typeof (int);
            idColumn.Unique = true;
            idColumn.AllowDBNull = false;
            idColumn.Caption = "ID";
            idColumn.AutoIncrement = true;
            idColumn.AutoIncrementSeed = 1;
            idColumn.AutoIncrementStep = 1;
            clientTable.Columns.Add(idColumn);

            var firstNameColumn = new DataColumn("FirstName");
            firstNameColumn.DataType = typeof (string);
            firstNameColumn.MaxLength = 50;
            firstNameColumn.AllowDBNull = true;
            firstNameColumn.Caption = "Imię";
            clientTable.Columns.Add(firstNameColumn);

            clientTable.PrimaryKey = new DataColumn[] {idColumn};

            //Add new row by creating them first
            var row1 = clientTable.NewRow();
            row1["FirstName"] = "Jacek";
            Console.WriteLine(row1.RowState);
            clientTable.Rows.Add(row1);
            Console.WriteLine(row1.RowState);
            row1["FirstName"] = "Marek";
            Console.WriteLine(row1.RowState);
            row1.AcceptChanges();
            Console.WriteLine(row1.RowState);
            row1["FirstName"] = "Sebastian";
            Console.WriteLine(row1.RowState);
            row1.RejectChanges();
            Console.WriteLine(row1.RowState);
            Console.WriteLine(row1["FirstName"]);
            row1.Delete();
            Console.WriteLine(row1.RowState);
        }



Zarządzanie danymi w wierszu za pomocą wersji wiersza
DataTable może posiadać do trzech wersji danego wiersza: oryginalny (Original), obecny (Current) oraz zaplanowany (Proposed). Możliwe wartości przechowywane są w enumie DataRowVersion:
  • Current - obecna wartość; istnieje zawsze oprócz sytuacji w której usuniemy wiersz (próba pobrania zakończy się wyjątkiem)
  • Default - jeżeli DataRowState jest ustawiony na Added lub Modified - domyślna wartość to Current; jeżeli Deleted - rzucony zostanie wyjątek; jeżeli zostanie wywołana metoda BeginEdit - Proposed
  • Original - wartość załadowana do wiersza, lub ta od ostatniego wywołania metody AcceptChanges. Wartość ta nie jest ustawiana dopóki wiersz nie znajdzie się w stanie Modified, Unchanged, lub Deleted. Jeżeli wiersz jest w stanie Added, zostanie rzucony wyjątek VersionNotFound
  • Proposed - wartość w trakcie edycji wiersza. Jeżeli wiersz jest w stanie Deleted, zostanie rzucony wyjątek. Jeżeli metoda BeginEdit nie została wywołana, bądź też została wywołana na wierszu, który nie należy do DataTable, zostanie rzucony wyjątek VersionNotFound
Za pomocą metody HasVersion możemy w bezpieczny sposób sprawdzić czy dana wersja jest dostępna. Przykładowy kawałek kodu pozwalający na bezpieczne pobranie danej wersji wiersza:

Code:
            foreach (var rowVersion in Enum.GetNames(typeof(DataRowVersion)))
            {
                var enumValue = (DataRowVersion)Enum.Parse(typeof (DataRowVersion), rowVersion);
                if (row1.HasVersion(enumValue))
                {
                    Console.WriteLine(enumValue + " " + row1["FirstName", enumValue]);
                }
            }


Metody AcceptChanges oraz RejectChanges
Metody te, jak same ich nazwy mówią służą do akceptacji lub odrzucenia danych. Metody te są potrzebne, aby zmienić stan wiersza na Unchanged.
Po dokonaniu zmian wierszy, które zostały pobrane z źródła danych (np. bazy danych) należy wybrać tylko te wiersze, których stan to Modified. Wiesze które zostały zmodyfikowane możemy pobrać za pomocą metody GetChanges. Po zapisaniu zmian do źródła danych należy wywołać metodę AcceptChanges w celu zresetowania stanu wierszy przechowywanych w obiekcie DataTable.
Wywołanie AcceptChanges dodatkowo powoduje skopiowanie danych z wersji Current do wersji Original.
Wywołanie RejectChanges powoduje skopiowanie danych z wersji Original do Current.

Stan wiersza możemy także zmienić za pomocą dwóch dodatkowych metod:
  • SetAdded - dodany
  • SetModified - zmodyfikowany
Operacja taka może być przydatna gdy chcemy dane zapisać do innego źródła danych niż z którego oryginalnie zostały pobrane. Wystarczy w takim przypadku wywołać metodę SetAdded i przesłać taki obiekt DataTable do DataAdapter-a.
Metody te mają ograniczenie do wywoływania ich na wierszach, które są w stanie Unchanged. Jeżeli wiersz jest w innym stanie, zostanie rzucony wyjątek InvalidOperationExcetpion.
Dodatkowo metody te:
  • SetAdded - usuwa oryginalną wersjędanych
  • SetModified - nie modyfikuje wersji wiersza

Usuwanie wierszy i cofanie tej operacji
Wywołując metodę Delete na wierszu oznaczamy go do usunięcia. Następstwem jest usunięcie wersji danych Current oraz Proposed.
Jeżeli okaże się, że dany wiersz ma nie być usunięty, można użyć metody RejectChanges. Dane Original zostaną skopiowane do Current. Wiesz wraca do stanu sprzed ostatniego wywołania metody AcceptChanges, jednak wszystkie operacje wykonane po tej metodzie nie zostaną przywrócone.

Iteracja po danych zawartych w DataTable
Po obiekcie DataTable można iterować za pomocą pętli:

Code:
            var productTable = new DataTable("Products");
            productTable.Columns.Add("Id", typeof (int));
            productTable.Columns.Add("Name", typeof(string));
            productTable.Columns.Add("Price", typeof(decimal));
            productTable.Columns.Add("Amount", typeof(int));
            productTable.Rows.Add(new object[] { 1, "Laptops", 1678, 100 });
            productTable.Rows.Add(new object[] { 2, "Printers", 234.6d, 40 });
            productTable.Rows.Add(new object[] { 3, "Keybords", 70.30d, 130 });

            foreach (DataRow row in productTable.Rows)
            {
                foreach (DataColumn column in productTable.Columns)
                {
                    Console.Write(row[column.ColumnName] + "  ");
                }
                Console.WriteLine();
            }



Kopiowanie obiektów DataTable
Obiekty DataTable można kopiować za pomocą dwóch metod:
  • Copy - tworzy kopię zawierającą schemat i dane
  • Clone - kopiuje tylko schemę
Jeżeli zdecydujemy się na metodę Clone, możemy za pomocą metody ImportRow skopiować do niej wybrane rekordy (należy pamiętać, że schema tabel musi być taka sama). W przypadku gdy do tabeli docelowej będziemy próbowali skopiować rekord z kluczem głównym, który już w niej występuje, zostanie wyrzucony wyjątek ConstraintException.


Przykładowy kod do pobrania: http://sdrv.ms/X0ZixZ

piątek, 11 stycznia 2013

70-516 Bezpołączeniowy model

Zacznę od wyjaśnienia samego tytułu, gdyż może być mylący. Stwierdzenie "bezpołączeniowy model" mogłoby sugerować, iż między źródłem danych a samymi klasami nie występuje zależność. Nic bardziej mylnego. Bezpołączeniowy model oznacza, iż klasy odpowiedzialne za obsługę danych są w stanie działać bez ciągłego połączenia z źródłem danych. Jeżeli będziemy chcieli pobrać świeżą paczkę danych, klasy te nawiążą połączenie, pobiorą potrzebne dane, a następnie zakończą połączenie.

Spójrzmy na diagram:


Przedstawiony powyżej diagram przedstawia klasy, wchodzące w skład bezpołączeniowego i połączeniowego modelu.
W tym poście zajmiemy się bezpołączeniowym modelem danych.

DataTable
Obiekt klasy DataTable przedstawia tabelę (np. bazy danych) jako obiekt w pamięci. Obiekt ten przechowuje kolumny jako instancje klasy DataColumn oraz wiersze zawierające dane - DataRow. Tabeli możemy nadać nazwę, co w późniejszym czasie ułatwia jej pobranie z kolekcji tabel.
Każda kolumna przechowywana w DataTable może posiadać dodatkowe ograniczenia związane z rodzajem przechowywanych w niej danych. Dzięki temu, zapewniamy integralność przechowywanych danych.
Niektóre właściwości oraz ich domyślne wartości dla obiektu DataColumn:
  • DataType - typ przechowywanych danych - domyślnie string
  • MaxLength - maksymalna długość w przypadku łańcuchów tekstowych - domyślnie -1 co oznacza brak sprawdzania tego parametru
  • Unique - wartości unikalne - domyślnie false czyli brak kontroli przed wstawianiem duplikatów wartości
  • AllowDBNull - wartości DBNull - domyślnie true czyli nie trzeba specyfikować dla danej kolumny wartości
  • Caption - tytuł kolumny wykorzystywany np. w kontrolkach graficznych typu DataGridView - domyślnie przyjmuje wartość ColumnName
Przykład stworzenia obiektu DataTable:

Code:
            var clientTable = new DataTable("Client");
            var idColumn = new DataColumn("Id");
            idColumn.DataType = typeof (int);
            idColumn.Unique = true;
            idColumn.AllowDBNull = false;
            idColumn.Caption = "ID";
            clientTable.Columns.Add(idColumn);

            var firstNameColumn = new DataColumn("FirstName");
            firstNameColumn.DataType = typeof (string);
            firstNameColumn.MaxLength = 50;
            firstNameColumn.AllowDBNull = true;
            firstNameColumn.Caption = "Imię";
            clientTable.Columns.Add(firstNameColumn);

Do tabeli możemy także dodać informacje mówiące która kolumna/kolumny tworzą klucz główny (Primary Key) oraz ustawić automatycznie numerowane kolumny. Klucz główny wskazujemy za pomocą właściwości PrimaryKey:

Code:
clientTable.PrimaryKey = new DataColumn[] {idColumn};

Auto numerowanie konfigurujemy za pomocą trzech właściwości:
  • AutoIncrement - flaga mówiąca czy auto numerowanie jest włączone dla danej kolumny
  • AutoIncrementSeed - wartość od której się rozpocznie auto numerowanie
  • AutoIncrementStep - krok, o który zwiększy się kolejna wartość w stosunku do poprzedniej
Dla kolumny id, auto numerowanie możemy włączyć w następujący sposób:

Code:
            idColumn.AutoIncrement = true;
            idColumn.AutoIncrementSeed = 1;
            idColumn.AutoIncrementStep = 1;

Nasuwa się pytanie: co się stanie jeżeli w bazie danych także dane pole jest ustawione jako auto numerowane? Otóż wartości automatycznie wyliczane nie są przekazywane do bazy. Baza danych sama wyznaczy kolejne wartości dla pola auto numerowanego. Następnie zostanie uaktualnione pole w obiekcie DataTable wartością pobraną z bazy danych. Istnieje jednak ryzyko: jeżeli nasz DataTable jest numerowany np. od 1 do 50 a w tabeli bazodanowej jest już 10 będziemy mieli do czynienia z następującym scenariuszem:
  • do bazy danych zostanie dodany nowy rekord
  • baza danych wyznaczy dla niego wartość id = 11
  • obiekt DataTable będzie chciał uaktualnić wartość pola kolumny id na 11
  • z powodu istnienia w tabeli rekordu z id = 11, zostanie rzucony wyjątek
Jak zabezpieczyć się na taką ewentualność? Można ustawić wartość właściwości AutoIncrementSeed oraz AutoIncrementStep na -1. Dzięki temu będą generowane wartości ujemne. W większości baz danych identyfikatory dla wierszy są wartościami dodatnimi więc nie pojawi się konflikt z istniejącymi wartościami w bazie, a uaktualnianie wartości w DataTable nie spowoduje konfliktów związanych z auto numerowaniem.

Dodawać dane do DataTable możemy za pomocą metody Add, która została przeładowana i przyjmuje dane w dwóch postaciach:
  • jako obiekt typu DataRow
  • jako tablicę wartości dla poszczególnych kolumn - należy wtedy podać wartość dla każdej kolumny
Innymi metodami umożliwiającymi import wierszy do obiektu DataTable są:
  • ImportDataRow - pozwala na import wiersza z zachowaniem jego stanu
  • Load - pozwala zaimportować nowe wiersze bądź uaktualnić wartości w istniejących
  • LoadDataRow - pozwala wygenerować nowy wiersz na podstawie tablicy wartości dla poszczególnych kolumn
Metoda LoadDataRow oraz Load pozwala wybrać sposób w jaki zostaną dodane/uaktualnione dane w wierszu - odpowiada za to enum  LoadOption, który ma następujące możliwości:
  • OverwriteChanges - nadpisuje dane i zmienia stan wiersza na Unchanged; jeżeli zostanie dodany nowy wiersz - także otrzyma stan Unchanged
  • PreserveChanges - domyślny tryb - nadpisuje oryginalne dane nowymi, ale nie zmienia wersji wiersza. Nowe wiersze będą w stanie Unchanged
  • Upsert - zmienia aktualną wersję, ale nie oryginalną. Nowe wiersze będą mieć stan Added; wiersze ze stanem Unchanged pozostaną w takim stanie jeżeli aktualna wersja jest taka sama jak oryginalna, w przeciwnym wypadku wiersze będą w stanie Modified

Poniżej znajduje się przykład dodawania wierszy do obiektu DataTable za pomocą wcześniej omówionych metod.
Code:
            //Add new row by creating them first
            var row1 = clientTable.NewRow();
            row1["FirstName"] = "Jacek";
            var row2 = clientTable.NewRow();
            row2["FirstName"] = "Patryk";
            clientTable.Rows.Add(row1);
            clientTable.Rows.Add(row2);

            //Using add with values
            clientTable.Rows.Add(new object[] {3, "Sebastian"});

            //LoadData
            clientTable.LoadDataRow(new object[] { 4, "Marek" }, LoadOption.OverwriteChanges);

czwartek, 10 stycznia 2013

70-516 Wstęp

Egzamin 70-516 dotyczy wiedzy o dostępie do źródeł danych. Nie ogranicza się on tyko do baz danych, ale skupia swoją uwagę także na XML-u oraz popularnych obecnie webserwisach.
Podstawowym źródłem danych, które zamierzam wykorzystać w celu przygotowania się do egzaminu to przede wszystkim Training Kit 70-516 Accessing Data with Microsoft .NET Framework 4.
Książa przedstawia następujące aspekty dotyczące pracy z danymi:
  • połączeniowy model danych
  • bezpołączeniowy model danych
  • zapytania LINQ
  • LINQ2SQL
  • LINQ2XML
  • Entity Framework
  • WCF Data Services
  • zbieranie informacji o wydajności w ADO.NET
  • synchronizacja danych
  • tworzenie i udostępnianie aplikacji korzystających z źródeł danych
Jak widać książka zawiera bardzo dużo ciekawego materiału.
Jako, że w tematyce dostępu i zarządzaniu danymi czuję się bardzo swobodnie, niektórym tematom poświęcę mniej miejsca. Postaram się jednak pisać jak najbardziej zrozumiale i streszczać długie rozdziały, wybierając z nich najważniejsze informacje. Miłego czytania!

środa, 9 stycznia 2013

70 - 511 Podsumowanie i wrażenia po egzaminie

Do egzaminu przygotowałem się już od dłuższego czasu. Artykuły, które w tym czasie tutaj umieszczałem miały na celu zapisanie notatek, aby na parę godzin przed egzaminem powtórzyć sobie najważniejsze rzeczy.
Pytania dotyczyły głównie możliwości które oferuje WPF - bindowanie, walidacja, szablony, style. Nie zabrakło także pytań dotyczących bezpieczeństwa tworzonych aplikacji.
Z mojej perspektywy pytania były ciekawe i pozwalały zweryfikować, czy ktoś ma do czynienia z programowanie w WPF czy może tylko jest oczytany w tematach.
Przystępowanie do egzaminu po przeczytaniu tylko artykułów na temat WPF nie ma sensu. Należy poświęcić wiele czasu na praktyczną część i dużo ćwiczyć i jeszcze raz ćwiczyć praktycznie pisząc kod.
Czasu na egzamin jest wystarczająco dużo, aby można było go dwukrotnie odbyć. Można więc na spokojnie przemyśleć sobie kawałki kodu i nawet jeżeli nie jest się czegoś pewnym, drogą dedukcji dojść do prawidłowego rozwiązania.
Czy taka forma egzaminu w pełni odzwierciedla wiedzę i umiejętności programisty? Nie mam zamiaru tutaj polemizować z kimkolwiek na ten temat. W każdym razie sama wiedza teoretyczna nie jest wystarczająca, a znajomość i wiedza, że takie narzędzie przydaje się do tego a tamto do tamtego - nie zastąpi praktycznych umiejętności posługiwania się nimi.
Egzamin zaliczony :) czas na kolejne wyzwania. W planach mam zdanie 5 egzaminów do maja. W tym momencie mam już pierwszy za sobą.
Kolejny który zamierzam podjąć to 70 -516 Accessing Data with Microsoft .NET Framework 4 - możecie się spodziewać następnych notatek ;)

poniedziałek, 7 stycznia 2013

Wyszukiwanie w kolumnie zawierającej XML

Jeżeli w tabeli jedna z kolumn zawiera XML, możemy łatwo wybierać dane z tej kolumny (struktury xml) jak i szukać po elementach XML-a. Nie ma potrzeby pobierania całej zawartości tabeli do pamięci w programie, a następnie wybieranie danych za pomocą np. LINQ2XML.

Przykład:

Struktura tabeli w bazie ma następującą postać:

pole detale filmu jest XML-em następującej postaci:

Code:
<Szczegoly>
 <WersjaJezykowa>EN/PL</WersjaJezykowa>
 <audio>5.1</audio>
 <lektor>EN/PL</lektor>
 <rok>2004</rok>
</Szczegoly>

Jeżeli chcemy wyszukać np. film z 1990 roku wystarczy napisać następującego SQL-a:


SELECT * FROM film WHERE detale_filmu.value('(Szczegoly/rok/node())[1]', 'int') = 1990


Język zapytań jaki tutaj został użyty w celu wyciągnięcia zawartości XMLa to XQuery

Bindowanie do zasobów Dynamic vs Static

Tym razem krótko, o czymś co może się przydać nie tylko na egzaminie, ale także w codziennej pracy z zasobami i bindowaniem do nich.

Rozważmy cztery przypadki bindowania do zasobów w kodzie.
Kod wyjściowy dla wszystkich przypadków:

Code:
<Window x:Class="DynamicStatic.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <SolidColorBrush x:Key="Background" Color="Blue" />
        <SolidColorBrush x:Key="Foreground" Color="Red" />
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>
        <Button Content="Przycisk"></Button>
        <Button Name="btnDoSomething" Content="Wykonaj" Grid.Row="1" Click="BtnDoSomething_OnClick" />
    </Grid>
</Window>



Dwa zasoby dla tła i koloru czcionki, będziemy przypisywać do przycisku, a następnie w kodzie pobierzemy i zmienimy ich wartość. Zmieniać będzie się tylko definicja klawisza i metody podpiętej pod zdarzenie drugiego klawisza.

1. Przypisanie statyczne i zmiana koloru pędzla:

Code:
<Button Background="{StaticResource Background}" Foreground="{StaticResource Foreground}" Content="Przycisk"></Button>

private void BtnDoSomething_OnClick(object sender, RoutedEventArgs e)
        {
            var background = (SolidColorBrush)this.FindResource("Background");
            background.Color = Colors.Yellow;

            var foreground = (SolidColorBrush)this.FindResource("Foreground");
            foreground.Color = Colors.Black;
        }

Efekt? Zmiana kolorów:


2. Przypisanie statyczne i zmiana pędzla:
Różnica polega na zmianie pędzla - czyli podmieniamy cały obiekt zasobów (XAML definiujący przycisk nie zmienia się):

Code:
        private void BtnDoSomething_OnClick(object sender, RoutedEventArgs e)
        {
            this.Resources["Background"] = new SolidColorBrush(Colors.Yellow);

            this.Resources["Foreground"] = new SolidColorBrush(Colors.Black);
        }

Efekt?





Właściwie w tym przypadku możemy mówić o braku jakiegokolwiek efektu. Dzieje się tak dlatego, że statyczny zasób pobierany jest tylko raz. Obiekt pędzla został pobrany i nie zmienia się, jednak jeżeli zmienimy właściwość - zobaczymy efekt (dotyczy to pierwszego przykładu).

3. Przypisanie dynamiczne i zmiana koloru pędzla

Code:
<Button Background="{DynamicResource Background}" Foreground="{DynamicResource Foreground}" Content="Przycisk"></Button>

        private void BtnDoSomething_OnClick(object sender, RoutedEventArgs e)
        {
            ((SolidColorBrush) this.Resources["Background"]).Color = Colors.Yellow;

            ((SolidColorBrush)this.Resources["Foreground"]).Color = Colors.Black;
        }

Efekt:


4. Przypisanie dynamiczne i zmiana pędzla
Kod XAML przycisku taki jak poprzednio

Code:
        private void BtnDoSomething_OnClick(object sender, RoutedEventArgs e)
        {
            this.Resources["Background"] = new SolidColorBrush(Colors.Yellow);

            this.Resources["Foreground"] = new SolidColorBrush(Colors.Black);
        }

Efekt:


Warto zapamiętać te przypadki i wiedzieć w jaki sposób są pobierane zasoby, gdyż możemy dzięki temu uniknąć częstych błędów.