Upload
others
View
4
Download
0
Embed Size (px)
Citation preview
Windows Presentation
Foundation – WPF (1)Programowanie Wizualne
Paweł Wojciechowski
Instytut Informatyki, Politechniki Poznańskiej
2012
Architektura
Managed WPF API
Media
Integration
Layer
Direct3D
milcore.dll WindowsCodecs.dll
PresentationFramework.dll
PresentationCore.dll WindowsBase.dll
User32
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
<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>
namespace WpfApplication1{
/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{
public MainWindow(){
InitializeComponent();}
}}
Plik xaml:
Plik cs:
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.
Wymagane jest jednak jeśli wymagane są zmiany parametrów elementu
kontrolnego w kodzie programu
Dla tak zdefiniowanej nazwy zostanie wygenerowana automatycznie następująca część kodu klasy MainWindow
<Grid>...
</Grid>
<Grid Name="Grid_1">...
</Grid>
#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ówwł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 Name="image1" Stretch="None" VerticalAlignment="Top" Width="250"><Image.Height>250</Image.Height><Image.HorizontalAlignment>Left</Image.HorizontalAlignment>
</Image>
<Image Height="250" HorizontalAlignment="Left" Name="image1" Stretch="None"VerticalAlignment="Top" Width="250"/>
Właściwości komponentówwł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ówDynamiczne 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
wszystkie rozszerzone znaczniki (markup extensions) są zaimplementowane w klasach dziedziczących po System.Windows.Markup.MarkupExtension
<Label Name="label1" Content="Hello World" Background="{x:Static SystemColors.HighlightColor}"/>
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ówwł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
są one przekształcane w wywołania metod
TypDefiniujący.SetNazwaWłaściwości
<Label Name="label1" Content="Hello World" Grid.Row="0" Grid.Column="0"/>
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ówLayouts
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
automatyczny – wiersz/kolumna dostają dokładnie tyle miejsca ile potrzebują
proporcjonalny – cały dostępny rozmiar jest proporcjonalnie dzielony przez
wiersze/kolumny – wartość domyślna
<RowDefinition Height="80"/>
<RowDefinition Height="Auto"/>
<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 GridSplitterzajmuje 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
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
public static readonly DependencyProperty AquariumGraphicProperty;
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
walidacja danych nie następuje w setterach/getterach!
właściwości mogą być współdzielone
Właściwości mogą być powiązane – użycie RegisterAttached()
Usuwanie wartości właściwości:
public Uri AquariumGraphic{
get { return (Uri)GetValue(AquariumGraphicProperty); }set { SetValue(AquariumGraphicProperty, value); }
}
Window1.ClearValue(MainWindow.AquariumGraphicProperty);
TextBlock.FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));
Wykorzystanie DP przez WPFinformowanie 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 WPFwyznaczanie 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 = 1bar.Minimum = 1;//bar.Value = 1bar.Maximum = 200;//bar.Value = 100