Grid.Row="0" Grid.Column="0"

Transkrypt

Grid.Row="0" Grid.Column="0"
Windows Presentation
Foundation – WPF (1)
Programowanie Wizualne
Paweł Wojciechowski
Instytut Informatyki, Politechniki Poznańskiej
2012
Architektura
Managed WPF API
PresentationFramework.dll
PresentationCore.dll
WindowsBase.dll
milcore.dll
WindowsCodecs.dll
Direct3D
User32
Media
Integration
Layer
Hierarchia klas
object
DispatcherObject
DependencyObject
Visual
UIElement
FrameworkElement
Control
realizacja modelu STA (single thread affinity)
model właściwości – cechy: informowanie o zmianach, dziedziczenie wartości domyślnej
obiekt, który jest „rysowany” – transformacje, przycinanie, przezroczystość, hit tests
layout, inputs, events, data binding, animation, styles
kluczowe właściwości obiektów
obiekty interaktywne. Właściwości: font, foreground, background colors
Jednostki wielkości w WPF (1)

jednostki wielkości uwzględniają DPI ekranu

zaleta: na wszystkich ekranach wielkość elementu jest taki sam

wada: ...
𝑙𝑖𝑐𝑧𝑏𝑎 𝑝𝑖𝑘𝑠𝑒𝑙𝑖 𝑛𝑎 𝑗𝑒𝑑𝑛𝑜𝑠𝑡𝑘ę 𝑊𝑃𝐹 =
𝑟𝑜𝑧𝑚𝑖𝑎𝑟 𝑒𝑙𝑒𝑚𝑒𝑛𝑡𝑢 𝑤 𝑝𝑖𝑘𝑠𝑒𝑙𝑎𝑐ℎ =

𝑟𝑜𝑧𝑚𝑖𝑎𝑟 𝑠𝑡𝑎ł𝑒𝑗 𝑗𝑒𝑑𝑛𝑜𝑠𝑡𝑘𝑖 𝑊𝑃𝐹
𝐷𝑃𝐼 𝑚𝑜𝑛𝑖𝑡𝑜𝑟𝑎
𝑙𝑖𝑐𝑧𝑏𝑎 𝑝𝑖𝑘𝑠𝑒𝑙𝑖 𝑛𝑎 𝑗𝑒𝑑𝑛𝑜𝑠𝑡𝑘ę 𝑊𝑃𝐹
𝑀𝑜𝑗𝐸𝑙𝑒𝑚𝑒𝑛𝑡. 𝐴𝑐𝑡𝑢𝑎𝑙𝑊𝑖𝑑𝑡ℎ
Jednostka WPF = 1/96
Gdzie w systemie Windows ustawia się DPI?
Jednostki wielkości w WPF (2)
WPF główne cechy

wspomaganie sprzętowe
wykorzystanie Direct3D

niezależność od rozdzielczości
wykorzystanie systemu DPI i ustawień systemowych

brak ustalonego wyglądu kontrolek
kontrolki są „lookless” – są w pełni konfigurowalne

deklarowane interfejsy użytkownika
interfejs użytkownika deklarowany jest w specjalnym języku XAML
XAML

XAML Extensible Application Markup Language

case sensitive

główny cel – oddzielenie logiki aplikacji od jej wyglądu

XAML nie jest niezbędny do działania WPFa

Kompilacja -> BAML (Binary Application Markup Language)

Podstawowe właściwości:

Każdy element zdefiniowany w XAMLu odpowiada klasie w .NET.

Jak w każdym dokumencie XML jeden element można osadzać w innym

Właściwości klas mogą zostać ustawione poprzez atrybuty.
Pierwsza aplikacja
Plik xaml:
<Window x:Class="WpfApplication1.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">
<Grid>
</Grid>
</Window>
Plik cs:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Przestrzenie nazw
„Namiary” na lokację klas .NET – atrybut xmlns

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
domyślny pakiet w aplikacji zawierający wszystkie klasy WPF.

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
pakiet zawierający różne narzędzia umożliwiające wpłynięcie na sposób
interpretacji dokumentu XAML

Odwołanie do własnych klas
xmlns:prefix="clr-namespace:Namespace;assembly=AssemblyName"
<Page x:Class="WPFApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:Sample;assembly=SampleLibrary">
...
<custom:ExampleClass/>
...
</Page>
Nazywanie elementów interfejsu

Nadawanie nazw elementom nie jest obowiązkowe.
<Grid>
...
</Grid>

Wymagane jest jednak jeśli wymagane są zmiany parametrów elementu
kontrolnego w kodzie programu
<Grid Name="Grid_1">
...
</Grid>

Dla tak zdefiniowanej nazwy zostanie wygenerowana automatycznie
następująca część kodu klasy MainWindow
#line 5 "..\..\..\MainWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.Grid Grid_1;
Właściwości komponentów
proste typy i konwertery
<Image Height="250" HorizontalAlignment="Left" Name="image1" Stretch="None"
VerticalAlignment="Top" Width="250"
Source="/WpfApplication1;component/Images/bitmap.jpg" />
Dla powyższego komponentu (Image) muszą być zdefiniowane następujące właściwości: Height,
HorizontalAlignment, Stretch, VerticalAlignment, Width, Soruce
Typy proste – prosta konwersja.
Typy złożone np. właściwość Background wymaga specjalnego konwertera.
Zaleca się nadawania wartości parametrów w XAMLu nie w kodzie aplikacji, ze względu na to,
iż XAML jest parsowany i sprawdzany w czasie kompilacji, dzięki czemu wcześniej można wykryć
potencjalne błędy.
Właściwości komponentów
właściwości złożone

niektóre właściwości mogą być obiektami – problem z kowersją

w XAML-u można definiować elementy zagnieżdżone w formie
Rodzic.NazwaWłaściwości
<Image Height="250" HorizontalAlignment="Left" Name="image1" Stretch="None"
VerticalAlignment="Top" Width="250"/>
<Image Name="image1" Stretch="None" VerticalAlignment="Top" Width="250">
<Image.Height>250</Image.Height>
<Image.HorizontalAlignment>Left</Image.HorizontalAlignment>
</Image>
Właściwości komponentów
właściwości złożone (2)
<Grid Name="Grid_1">
<Grid.Background>
<RadialGradientBrush>
<GradientStop Offset="0.00" Color="White" />
<GradientStop Offset="0.5" Color="Red"/>
<GradientStop Offset="0.75" Color="Indigo"/>
<GradientStop Offset="1" Color="Violet"/>
</RadialGradientBrush>
</Grid.Background>
</Grid>
RadialGradientBrush rgb = new RadialGradientBrush();
GradientStop gradientStop1 = new GradientStop();
gradientStop1.Color = Colors.White;
gradientStop1.Offset = 0;
rgb.GradientStops.Add(gradientStop1);
GradientStop gradientStop2 = new GradientStop();
gradientStop2.Color = Colors.Red;
gradientStop2.Offset = 0.5;
rgb.GradientStops.Add(gradientStop2);
GradientStop gradientStop3 = new GradientStop();
gradientStop3.Color = Colors.Indigo;
gradientStop3.Offset = 0.75;
rgb.GradientStops.Add(gradientStop3);
GradientStop gradientStop4 = new GradientStop();
gradientStop4.Color = Colors.Violet;
gradientStop4.Offset = 1;
rgb.GradientStops.Add(gradientStop4);
Grid_1.Background = rgb;
Właściwości komponentów
Dynamiczne wartości właściwości

w poprzednich przykładach wartości właściwości były statyczne

Specjalna składnia umożliwia nadawanie wartości dynamicznych przez
wiązanie jej z wartościami właściwości klas
<Label Name="label1" Content="Hello World" Background="{x:Static SystemColors.HighlightColor}"/>

wszystkie rozszerzone znaczniki (markup extensions) są zaimplementowane w
klasach dziedziczących po System.Windows.Markup.MarkupExtension
label1.Background = new SolidColorBrush(SystemColors.HighlightColor);
<Label Name="label1" Content="Hello World">
<Label.Background>
<SolidColorBrush>
<SolidColorBrush.Color>
<x:Static Member="SystemColors.HighlightColor"></x:Static>
</SolidColorBrush.Color>
</SolidColorBrush>
</Label.Background>
</Label>
Właściwości komponentów
właściwości powiązane

Attached Properties

właściwości zdefiniowane w jednym z komponentów, ale zdefiniowane w
innej klasie

używanie poprzez: TypDefniniujący.NazwaWłaściwości
<Label Name="label1" Content="Hello World" Grid.Row="0" Grid.Column="0"/>

są one przekształcane w wywołania metod
TypDefiniujący.SetNazwaWłaściwości
Grid.SetColumn(label1, 1);
Elementy zagnieżdżone

element musi zadecydować w jaki sposób obsługiwać elementy zagnieżdżone.
Dzieje się to za pomocą jednego z trzech mechanizmów:

Jeżeli rodzic implementuje interfejs IList, wywoływana jest metoda
IList.Add()

Jeżeli rodzic implementuje IDictionary, wywoływana jest metoda
IDictionary.Add() elementy muszą mieć nadaną wartość parametru x:Key

Jeżeli rodzicowi nadano atrybut ContentProperty, dziecko używane jest do
nadania wartości wskazanej właściwości.
<Button>
Ala ma kota
</Button>
Zdarzenia

w XAMLu można również definiować zdarzenia –
NazwaZdarzenia="NazwaMetodyObsługiZdarzenia"
<Button Content="Button" Name="button1" Click="button1_Click" />
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Ala ma kota");
}
Ładowanie i kompilacja XAML

Implementacja aplikacji w WPFie może być realizowana za pomocą
następujących metod:

tylko kodu – definicja całego interfejsu za pomocą kodu np. w C#

Kodu i plików XAML – podejście stosowane w przypadku dynamicznie zmieniających
się interfejsów. Plik XAML jest ładowany podczas uruchomienia programu za
pomocą klasy XamlReader z przestrzeni nazw System.Windows.Markup

Kodu i plików BAML – najczęściej stosowane podejście. Interfejs opisany jest w
plikach XAML, które są kompilowane do BAML i osadzane w pakiecie.
Rozmieszczenie komponentów
Layouts
Filozofia rozmieszczania komponentów

odejście od sztywnych ustawień pozycji i rozmiaru

okno WPF może zawierać tylko jeden komponent

zasady budowania interfejsów:

elementy kontrolne nie powinny mieć ustawionego rozmiaru – powinny one
wypełniać pojemnik w którym się znajdują

można ograniczać minimalny i maksymalny rozmiar komponentów

położenie elementów nie jest opisane współrzędnymi ekranu, ale rozmiarem,
rozkładem itp. pojemnika(ów) w których się znajdują. Przerwy pomiędzy
komponentami narzuca się poprzez wartości parametru Margin.

pojemniki dzielą swoją przestrzeń pomiędzy swoje dzieci

pojemniki mogą być zagnieżdżone
Podstawowe rodzaje pojemników

StackPanel

WrapPanel

DockPanel

Grid

UniformGrid

Canvas

TabPanel, ToolbarPanel, ToolbarOverflowPanel, VirtualizingStackPanel,
InkCanvas
Layout - właściwości

HorizontalAlignment: Center, Left, Right, Stretch

VerticalAlignment: Center, Top, Bottom, Stretch

Margin

MinWidth/MinHeight

MaxWidth/MaxHeight

Width/Height
Właściwość Background musi zostać ustawiona (domyślnie null) jeśli pojemnik ma
otrzymywać zdarzenia myszy! Naturalnie może to być kolor transparentny.
StackPanel
<StackPanel>
<Label>Stack Panel</Label>
<Button>Button 1</Button>
<Button>Button 2</Button>
<TextBox>TextBox 1</TextBox>
<Button>Button 3</Button>
<Button>Button 4</Button>
</StackPanel>
<StackPanel Orientation="Horizontal">
...
</StackPanel>
StackPanel (2)
<StackPanel Margin="3" Background="Aqua">
<Label HorizontalAlignment="Center" Margin="3">Stack Panel</Label>
<Button HorizontalAlignment="Left" Margin="3">Button 1</Button>
<Button HorizontalAlignment="Right" Margin="3">Button 2</Button>
<TextBox Margin="3">TextBox 1</TextBox>
<Button HorizontalAlignment="Center" Margin="3">Button 3</Button>
<Button Margin="3">Button 4</Button>
</StackPanel>
Wyznaczanie wielkości komponentów

Rozmieszczenie elementów pojemnika składa się z dwóch etapów:

pomiaru (measure stage) – pojemnik „pyta” swoje dzieci o ich preferowany rozmiar

rozmieszczenia (arrange stage) – dzieci są rozmieszczane w pojemniku na właściwych pozycjach

Pojemniki nie wspierają przewijania (scrolls). Jest za to komponent – ScrollViewer.

Informacje wykorzystywane przy określaniu rozmiaru komponentu to:


Minimalny rozmiar – rozmiar komponentu będzie co najmniej taki, jak jego minimalny rozmiar

Maksymalny rozmiar – rozmiar komponentu będzie mniejszy od jego maksymalnego rozmiaru (chyba,
że minSize > maxSize)

Zawartość – jeśli zawartość komponentu będzie wymagała większego rozmiaru, to zostanie on
powiększony (DesiredSize)

Wielkość pojemnika – jeżeli wielkość pojemnika jest mniejsza niż wielkość komponentu, to część
komponentu zostanie obcięta

(Horizontal|Vertical)Alignment – pojemnik zwiększy rozmiar komponentu jeśli dla tej
własności zostanie ustawiona wartość Stretch
Rozmiar komponentu może zostać odczytany z właściwości ActualWidth, ActualHeight
(Width i Height mogą być = null !)
Rozmiar okna głównego

WPF dopuszcza sztywne ustawienie rozmiaru okna. Kiedy i dlaczego?

Aby rozmiar okna dostosowywał się do zawartości należy:

Usunąć właściwości Width i Height okna,

Ustawić Window.SizeToContent na (WidthAndHeight, Width lub Height)
WrapPanel

rozmieszcza komponenty kolejno w jednym wierszu, a gdy zabraknie miejsca
przechodzi do kolejnego wiersza (oczywiście dla WrapPanel.Orientation =
Horizontal).
<WrapPanel >
<Label MinHeight="60">Stack Panel</Label>
<Button VerticalAlignment="Top" >Button 1</Button>
<Button >Button 2</Button>
<TextBox >TextBox 1</TextBox>
<Button VerticalAlignment="Bottom" >Button 3</Button>
<Button VerticalAlignment="Center">Button 4</Button>
</WrapPanel>
DockPanel

rozmieszcza komponenty wzdłuż krawędzi

kolejność dodawania komponentów jest istotna!

(Horizontal|Vertical)Alignment są brane pod uwagę
<DockPanel LastChildFill="True">
<Label DockPanel.Dock="Left">Stack Panel</Label>
<Button DockPanel.Dock="Top">Button 1</Button>
<Button DockPanel.Dock="Right">Button 2</Button>
<TextBox DockPanel.Dock="Top" >TextBox 1</TextBox>
<Button DockPanel.Dock="Bottom">Button 3</Button>
<Button >Button 4</Button>
</DockPanel>
Grid

najbardziej zaawansowany pojemnik

tworzenie rozkładu za pomocą tego pojemnika przebiega dwuetapowo

Definicja wierszy (Grid.RowDefinitions) i kolumn (Grid.ColumnDefinitions)

Rozmieszczenie komponentów – właściwości Grid.Row i Grid.Column

domyślnie 1 komórka – 1 komponent

włożenie dwóch elementów do komórki powoduje ich nałożenie

Komponent UniformGrid – uproszczony Grid
Grid (2)
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" >Grid</Label>
<Button Grid.Row="0" Grid.Column="1">Button 1</Button>
<Button Grid.Row="0" Grid.Column="2">Button 2</Button>
<TextBox Grid.Row="1" Grid.Column="1">TextBox 1</TextBox>
<Button Grid.Row="1" Grid.Column="2"
HorizontalAlignment="Center"
VerticalAlignment="Center">
Button 3
</Button>
<Button Grid.Row="1" Grid.Column="0">Button 4</Button>
</Grid>
Grid – rozmiary wierszy i kolumn

rozmiary wierszy i kolumn podlegają jednej z następujących reguł, od której
zależy sposób obsługi zmiany rozmiaru komponentu w przypadku zwiększenia
rozmiaru pojemnika:

stały rozmiar – nieelastyczne
<RowDefinition Height="80"/>

automatyczny – wiersz/kolumna dostają dokładnie tyle miejsca ile potrzebują
<RowDefinition Height="Auto"/>

proporcjonalny – cały dostępny rozmiar jest proporcjonalnie dzielony przez
wiersze/kolumny – wartość domyślna
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
Grid – rozmiary wierszy i kolumn
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
Grid – „Rozpiętość” (Span) kolumn i
wierszy
<Grid>
...
<Label Grid.Row="0" Grid.Column="0" >Grid</Label>
<Button Grid.Row="0" Grid.Column="1"
Grid.ColumnSpan="2">Button 1
</Button>
<TextBox Grid.Row="1" Grid.Column="1">
TextBox 1
</TextBox>
<Button Grid.Row="1" Grid.Column="2"
HorizontalAlignment="Center"
VerticalAlignment="Center">
Button 3
</Button>
<Button Grid.Row="1" Grid.Column="0">Button 4</Button>
</Grid>
Grid – „paski podziału” (splitter bars)

umożliwia zmianę rozmiaru wierszy/kolumn użytkownikowi aplikacji

pasek podziału musi być umieszczony w komórce

rozmiar wiersza/kolumny powinien zostać ustawiony na stałe lub
automatycznie

rozszerzana jest zawsze cała kolumna/wiersz, nawet jeśli GridSplitter
zajmuje tylko jedną komórkę

domyślny rozmiar GridSplitter-a jest 0. Należy ustawić następujące
właściwości:

Podział pionowy: Width, VerticalAlignment=Stretch i
HorizontalAlignment=Center

Podział poziomy: Height, HorizontalAlignment=Stretch i
VerticalAlignment=Center
GridSplitter
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" >Grid</Label>
<Button Grid.Row="0" Grid.Column="2">Button 1</Button>
<Button Grid.Row="1" Grid.Column="2"
HorizontalAlignment="Center"
VerticalAlignment="Center">Button 3</Button>
<Button Grid.Row="1" Grid.Column="0">Button 4</Button>
<GridSplitter Grid.Column="1" Grid.Row="0" Grid.RowSpan="1" Width="10„
VerticalAlignment="Stretch" HorizontalAlignment="Center"/>
</Grid>
Canvas

umożliwia ręczne umiejscowienie elementów

pozycję podaje się w parametrach Canvas.Top (Canvas.Bottom) oraz
Canvas.Left (Canvas.Right)

Jeżeli elementy nachodzą na siebie, to parametrem Canvas.ZIndex można
definiować ich kolejność – element z wyższą wartości zawsze będzie
znajdował się nad elementem z niższą wartością Zindex.
<Canvas>
<Button Canvas.Left="100" Canvas.Top="20">Button 1</Button>
<Button Canvas.Top="50" Canvas.Right="20" Width="100"
VerticalAlignment="Stretch"
Canvas.ZIndex="1">Button 2</Button>
<Button Canvas.Top="60" Canvas.Right="40" Height="80">
Button 3
</Button>
</Canvas>
Ramka - Border
<Border Margin="5"
Padding="5"
Background="LightYellow"
BorderBrush="SteelBlue"
BorderThickness="3,5,3,5"
CornerRadius="3" >
<Grid>
...
</Grid>
</Border>
DependencyProperties
Podstawowe informacje

Dependency Property stanowi rozszerzenie pojęcia właściwości z .NETa na
potrzeby interfejsów użytkownika w WPF

Jest to podstawowy mechanizm wykorzystywany w animacji elementów
interfejsu, wiązaniu danych (data binding) oraz obsłudze styli

DP umożliwiają:

informowanie o zmianie wartości (change notification)

dziedziczenie wartości właściwości

zmniejszają koszty przechowywania wartości
Tworzenie Depencency Property

można je dodać tylko do obiektów dziedziczących po DependencyObject
public static readonly DependencyProperty AquariumGraphicProperty;

obiekt DependencyProperty musi być zawsze statyczny i tylko do odczytu

obiekt ten należy zarejestrować przed jakimkolwiek użyciem klasy dla której
go definiujemy
static MainWindow()
{
AquariumGraphicProperty = DependencyProperty.Register(
"AquariumGraphic",
typeof(Uri),
typeof(MainWindow),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnUriChanged))
);
}
Tworzenie Dependency Property (2)


Rejestrując DP należy podać:

jej nazwę

typ danych używany przez tę właściwość

typ danych, który będzie taką właściwość posiadał

opcjonalnie: obiekt FrameworkPropertyMetadata z dodatkowymi ustawieniami

opcjonalnie: funkcję zwrotną do walidacji wartości
Ustawienia FrameworkPropertyMetadata:
AffectsArrange,AffectsMeasure, AffectsParrentArrange, AffectsParentMeasure,
AffectsRender, BindsTwoWayByDefault, Inherits, IsAnimationProhibited,
IsNotDataBindable, DefaultValue, CoerceValueCallback, PropertyChangedCallback
Tworzenie Dependency Property (3)

dodanie wrapper-a właściwości
public Uri AquariumGraphic
{
get { return (Uri)GetValue(AquariumGraphicProperty); }
set { SetValue(AquariumGraphicProperty, value); }
}

walidacja danych nie następuje w setterach/getterach!

właściwości mogą być współdzielone
TextBlock.FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));

Właściwości mogą być powiązane – użycie RegisterAttached()

Usuwanie wartości właściwości:
Window1.ClearValue(MainWindow.AquariumGraphicProperty);
Wykorzystanie DP przez WPF
informowanie o zmianach (change notification)

DP nie uruchamia automatycznie zdarzeń w momencie zmiany wartości

Uruchamia metodę OnPropertyChangedCallback(), która to przekazuje
informację o zmianie wartości do dwóch serwisów: wiązania danych i
triggerów – chcąc reagować na zmianę wartości można albo utworzyć
wiązanie albo napisać trigger

Niektóre komponenty udostępniają zdarzenia po zmianie wartości np.
ScrollBar – ValueChanged
Wykorzystanie DP przez WPF
wyznaczanie wartości (dynamic value resolution)


Określenie bazowej wartości właściwości odbywa się na podstawie:

Wartości domyślnej,

Wartości dziedziczonej,

Wartości dla styli

lokalnej wartości
Następnie wartość końcowa określana jest w następujący sposób:

Jeżeli wartość opisana jest jakimś wyrażeniem – wyznaczenie jej wartości (data
binding i zasoby)

Jeżeli wartość jest celem animacji – zastosowanie animacji

Wywołanie metody CoerceValueCallback w celu „naprawienia” wartości
Walidacja wartości

Ustawienie wartości DP przebiega następująco:

Dostarczana wartość trafia do metody CoerceValueCallback, gdzie może zostać
zmodyfikowana. Jeśli zmiana jest nieakceptowalna zwracana jest wartość
DependencyProperty.UnsetValue

Następnie uruchamiana jest metoda ValidateValueCallback, która zwraca
wartość true jeśli wartość jest akceptowana. Metoda ta nie ma dostępu do
pozostałych właściwości obiektu.

Jeśli poprzednie kroki zakończą się sukcesem, zostaje wywołana metoda
PropertyChangedCallback

ValidateValueCallback musi być statyczna i jest dostarczana jako
opcjonalna na etapie rejestracji DP

CoerceValueCallback ma następujący nagłówek
private static object CoerceMaximum( DependencyObject d, object value);
Przykład działania walidacji
ScrollBar bar = new ScrollBar();
bar.Value = 100;
//bar.Value = 1
bar.Minimum = 1;
//bar.Value = 1
bar.Maximum = 200;
//bar.Value = 100

Podobne dokumenty