Silverlight 3.0 içerisinde görsel veri validasyonu.

0 dakikada yazıldı

6829 defa okundu

Düzenle

Silverlight içerisinde veri validasyonu konusunda validasyon
kontrollerimiz yok gibi gözükse de aslında arka planda işimize
yarayabilecek ilginç bir sistem var. Bu sistemi hem tasarımcı hem de
yazılımcı beraber kullanırlarsa ortaya çok güzel sonuçlar çıkabilir. Bu
yazımızda nesne bazlı veri validasyonunu nasıl yapabiliriz konusuna göz
atacağız.

Hemen örneğimize başlama yoluna uygulamamızın ekranına bir DataGrid
koyalım. Bir sonraki adımda uygun nesneleri yaratarak bu Grid'e
bağlayacağız sonrasında da Grid içerisinde hücre başına validasyon
yapmaya çalışacağız.

[XAML]

<UserControl
xmlns
:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

       x:Class="SilverlightApplication81.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400"
Height
="300">

    <Grid
x
:Name="LayoutRoot"
Background
="White">

        <data:DataGrid
x
:Name="myGrid"></data:DataGrid>

    </Grid>

</UserControl>

Görsel tarafı en azından şimdilik hallettiğimize göre hemen veri
tarafına geçip işimizi görecek kadar örnek veri yaratalım. İlk olarak
verimiz içerisinde her nesneyi tanımlayacak olan Urun sınıfını
hazırlıyoruz.

[VB]

    Public Class Urun

 

        Private PAdi As String

        Public Property Adi() As String

            Get

                Return PAdi

            End Get

            Set(ByVal value As String)

                PAdi = value

            End Set

        End Property

 

        Private PSatis As Integer

        Public Property Satis() As Integer

            Get

                Return PSatis

            End Get

            Set(ByVal value As Integer)

                PSatis = value

            End Set

        End Property

 

    End Class

Göreceğiniz üzere Urun sınıfının sadece iki tane Property'si var.
Bunlardan biri ürünün adını taşırken diğeri ise toplam satış rakamını
taşıyacak. Bu sınıfımızdan bir liste yaratarak rastgele urunler ekleyip
Grid'imize basit bir şekilde bağlayacağız.

[VB]

    Private Sub MainPage_Loaded(ByVal sender As Object,
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim Liste As New
List(Of Urun)

        For index As Integer
= 1 To 10

            Liste.Add(New Urun() With {.Adi = "Ürün Adi" & index, .Satis = Rnd() *
50})

        Next

 

        myGrid.ItemsSource = Liste

    End Sub

Bütün bu manzara içerisinde bir anda aklınızda "ürün isimleri boş
bırakılmasın" şeklinde bir validasyon geldiğini varsayalım. Peki ne
yapacağız? Aslında konu tahmin edeceğinizden çok daha basit. İlk olarak
validasyon işlemini yapabileceğimiz basit bir yer var, orası da Urun
sınıfımızın Property'lerinin Set durumu.

[VB]

       Private PAdi As String

        Public Property Adi() As String

            Get

                Return PAdi

            End Get

            Set(ByVal value As String)

                If String.IsNullOrEmpty(value) Then

                    Throw New ValidationException("isim boş geçilemez!")

                Else

                    PAdi = value

                End If

 

            End Set

        End Property

Yukarıdaki kod içerisinde sadece tek bir Property'nin set ve get
durumunu görüyorsunuz. İlk baştaki örneğimizden farklı olarak Set
işleminde gelen value'yu kontrol ediyoruz. Eğer Grid içerisinde bu
Property'e denk gelen TextBox boş bırakılırsa doğan olarak arka tarafa
grid boş değer döndürerek söz konusu nesnenin property'sini değiştirmeye
çalışacak. İşte tam da o notkada devreye girip kontrolleri yapıp
gerekiyorsa bir ValidationException fırlatıyoruz. Exception
fırlatırken kullanıcıya vermek istediğimiz mesajı da parametre olarak
constructor'a geçebiliriz.

Eğer projeyi bu hali ile Debug modunda açarsanız hatayı Debug tarafında
görürsünüz. Oysa projeyi Debug mode yerine normal şekilde
çalıştırırsanız aşağıdaki manzara ile karşılaşacaksınız.

Basit bir validasyon sonucu...
Basit bir validasyon sonucu...

Gördüğünüz gibi manzara gerçekten çok hoş. Görsel olarak da güzel bir
animasyon ile sahneye gelen bu tooltip çoğu validasyon ihtiyacınızı
giderecektir. Grid içerisinde herhangi bir ek işleme gerek kalmadan
validasyonu çalışır hale getirmiş olsak da farklı kontrollerde Binding
ayarlarken vermeniz gereken ek parametreler de mevcut.

[XAML]

<UserControl
xmlns
:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

       x:Class="SilverlightApplication81.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400"
Height
="300">

    <Grid
x
:Name="LayoutRoot"
Background
="White">

        <data:DataGrid
x
:Name="myGrid"
AutoGenerateColumns
="False">

            <data:DataGrid.Columns>

                <data:DataGridTemplateColumn>

                    <data:DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <TextBox
Text
="{Binding
Adi
}" />

                        </DataTemplate>

                    </data:DataGridTemplateColumn.CellTemplate>

                </data:DataGridTemplateColumn>

            </data:DataGrid.Columns>

        </data:DataGrid>

    </Grid>

</UserControl>

Gridimizi yukarıdaki şekli ile biraz değiştirdik. Artık DataGrid ile
beraber gelen hazır kolonları değil de özel kolon tasarımlarını
kullandığımızı düşünelim. Bizim örneğimizde sadece Adi kolonunu ekledik
ve bu kolonun tasarımına da bir TextBox koyduk. Böylece bu kolon sürekli
TextBox olarak gözükecek. Peki acaba bu TextBox'ı da boş bıraktığımızda
validasyon hatasını görebilecek miyiz? Maalesef hayır.

[XAML]

<UserControl
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

      
x:Class="SilverlightApplication81.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400"
Height="300">

    <Grid x:Name="LayoutRoot"
Background="White">

        <data:DataGrid x:Name="myGrid"
AutoGenerateColumns="False">

            <data:DataGrid.Columns>

               
<data:DataGridTemplateColumn>

                   
<data:DataGridTemplateColumn.CellTemplate
>

                        <DataTemplate>

                            <TextBox
Text
="{Binding
Mode
=TwoWay, NotifyOnValidationError=true,
Path
=Adi, ValidatesOnExceptions=true}" />

                        </DataTemplate>

                   
</data:DataGridTemplateColumn.CellTemplate>

               
</data:DataGridTemplateColumn>

           
</data:DataGrid.Columns>

        </data:DataGrid>

    </Grid>

</UserControl>

Bu noktada değiştirmemiz gereken şey Binding mekanizmamızla alakalı
ayarlar. İlk olarak Mode'u TwoWay yaparak buradaki
değişikliklerin de nesne tarafına yansımasını sağlıyoruz ki nesne
tarafındaki validasyon gerçekleşsin. Ayrıca NotifyOnValidationError
ve ValidatesOnExceptions özelliklerini de True yaparak gerekli
hataların gösterilmesini sağlıyoruz.

Özel binding ile validasyon.
Özel binding ile validasyon.

Görselliği değiştirelim...

Herhangi bir kontrolün validation işlemi sonrasında InValid olması
haline gösterilmek üzere InValid State'lerinin bulunması gerekir. Hali
hazırda Silverlight içerisindeki çoğu kontrolde bu state'ler zaten hazır
ve tasarımları yapılmış durumda. Eğer yukarıdaki örneğimizi Expression
Blend ile açar ve TextBox'ın Template'ini editlemek isterseniz söz
konusu menüde "Edit a copy" demeyi unutmayın.

TextBox kontolümüzü Template'ini kopyalayarak editleyelim.
TextBox kontolümüzü Template'ini kopyalayarak editleyelim.

Böylece Blend sizin yerinize standart şablonu kopyalayacaktır. Bunu
yaptıktan sonra söz konusu şablonun için VisualStateManager'da
InValid statelerini bulabilir ve bu stateler için hazırlanmış
tasarımları değiştirebilirsiniz.

Blend içerisinde TextBox'a ait ayrı stateler...
Blend içerisinde TextBox'a ait ayrı stateler...

State'ler arası tasarımları değiştirdikten sonra büyük ihtimal ile
InValid durumunde gösterilen Tooltip'in de tasarımını değiştirebilmek
isteyeceksiniz. Blend içerisinden şu an için (Blend 3 MIX Preview
sürümü) bir çözüm bulunmasa da kod tarafından gerekli özelleştirmeleri
yapabiliriz. Textbox'ımızın Template'inden bir kopya alıp editlerken
aslında beraberindeki Tooltip'in tasarımının da bir kopyası
uygulamamızın sayfasında Resource olarak saklanıyor.

[XAML]

<UserControl
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

      
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

      
x:Class="SilverlightApplication81.MainPage" Width="400" Height="300"
mc:Ignorable="d">

    <UserControl.Resources>

        <ControlTemplate
x
:Key="ValidationToolTipTemplate">

            <Grid
x
:Name="Root"
Margin
="5,0" Opacity="0"
RenderTransformOrigin
="0,0">

                <vsm:VisualStateManager.VisualStateGroups>

                    <vsm:VisualStateGroup x:Name="OpenStates">

                        <vsm:VisualStateGroup.Transitions>

                            <vsm:VisualTransition GeneratedDuration="0"/>

                            <vsm:VisualTransition GeneratedDuration="0:0:0.2"
To
="Open">

                               
<Storyboard>

                                   
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="xform"
Storyboard.TargetProperty
="X">

                                       
<SplineDoubleKeyFrame KeyTime="0:0:0.2"
Value
="0"/>

                                   
</DoubleAnimationUsingKeyFrames>

                                   
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Root"
Storyboard.TargetProperty
="Opacity">

                                       
<SplineDoubleKeyFrame KeyTime="0:0:0.2"
Value
="1"/>

                                   
</DoubleAnimationUsingKeyFrames>

                               
</Storyboard>

                            </vsm:VisualTransition>

                        </vsm:VisualStateGroup.Transitions>

                        <vsm:VisualState
x
:Name="Closed">

                            <Storyboard>

                               
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Root"
Storyboard.TargetProperty
="Opacity">

                                   
<SplineDoubleKeyFrame KeyTime="0"
Value
="0"/>

                               
</DoubleAnimationUsingKeyFrames>

                            </Storyboard>

                        </vsm:VisualState>

                        <vsm:VisualState
x
:Name="Open">

                            <Storyboard>

                               
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="xform"
Storyboard.TargetProperty
="X">

                                   
<SplineDoubleKeyFrame KeyTime="0"
Value
="0"/>

                               
</DoubleAnimationUsingKeyFrames>

                               
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Root"
Storyboard.TargetProperty
="Opacity">

                                   
<SplineDoubleKeyFrame KeyTime="0"
Value
="1"/>

                               
</DoubleAnimationUsingKeyFrames>

                            </Storyboard>

                        </vsm:VisualState>

                    </vsm:VisualStateGroup>

                </vsm:VisualStateManager.VisualStateGroups>

                <Grid.RenderTransform>

                    <TranslateTransform x:Name="xform"
X
="-25"/>

                </Grid.RenderTransform>

                <Border
Margin
="4,4,-4,-4" Background="#152A2E31"
CornerRadius
="4"/>

                <Border
Margin
="3,3,-3,-3" Background="#252A2E31"
CornerRadius
="4"/>

                <Border
Margin
="2,2,-2,-2" Background="#352A2E31"
CornerRadius
="4"/>

                <Border
Margin
="1,1,-1,-1" Background="#452A2E31"
CornerRadius
="4"/>

                <Border
Background
="#FFDC000C" CornerRadius="4"/>

                <Border
CornerRadius
="4" Background="Green">

                    <TextBlock
Margin
="8,3,8,4" MaxWidth="250"
UseLayoutRounding
="false" Foreground="White"
Text
="{Binding
Path
=(Validation.Errors)[0].Exception.Message}" TextWrapping="Wrap"/>

                </Border>

            </Grid>

        </ControlTemplate>

        <Style x:Key="TextBoxStyle1"
TargetType="TextBox">

            <Setter
Property="BorderThickness" Value="1"/>

            <Setter Property="Background"
Value="#FFFFFFFF"/>

            <Setter Property="Foreground"
Value="#FF000000"/>

            <Setter Property="Padding"
Value="2"/>

Yukarıdaki örnek uygulamaızın XAML kodunun ilk altmış satırını
görebilirsiniz. Bu altmış satır içerisinde önemli olan şey
ValidationToolTipTemplate adındaki ControlTemplate nesnesi. Söz
konusu nesne TextBox'ın Template'i içerisinde de kullanılıyor. Aşağıdaki
TextBox'ımızın Template'ine ait XAML kodu içerisinde yukarıdaki
ControlTemplate'in nasıl kullanıldığını görebilirsiniz.

[XAML]

<Border x:Name="ValidationErrorElement"
Visibility="Collapsed" BorderBrush="#FFDB000C" BorderThickness="1"
CornerRadius="1">

                               
<ToolTipService.ToolTip>

                                   
<ToolTip
x
:Name="validationTooltip" DataContext="{Binding
RelativeSource
={RelativeSource
TemplatedParent
}}" Template="{StaticResource ValidationToolTipTemplate}"
Placement
="Right" PlacementTarget="{Binding
RelativeSource
={RelativeSource
TemplatedParent
}}">

                                   
    <ToolTip.Triggers>

                                       
    <EventTrigger RoutedEvent="Canvas.Loaded">

                                       
        <BeginStoryboard>

                                       
            <Storyboard>

                                       
                <ObjectAnimationUsingKeyFrames
Storyboard.TargetName="validationTooltip"
Storyboard.TargetProperty="IsHitTestVisible">

                                       
                    <DiscreteObjectKeyFrame KeyTime="0">

                                       
                        <DiscreteObjectKeyFrame.Value>

                                       
                           
<System:Boolean>true</System:Boolean>

                                       
                        </DiscreteObjectKeyFrame.Value>

                                       
                    </DiscreteObjectKeyFrame>

                                       
                </ObjectAnimationUsingKeyFrames>

                                       
            </Storyboard>

                                       
        </BeginStoryboard>

                                       
    </EventTrigger>

                                       
</ToolTip.Triggers>

                                   
</ToolTip>

                               
</ToolTipService.ToolTip>

                                <Grid
Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0"
VerticalAlignment="Top" Width="12" Background="Transparent">

                                    <Path
Fill="#FFDC000C" Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2
L8,7 z"/>

                                    <Path
Fill="#ffffff" Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8"/>

                               
</Grid>

                           
</Border
>

TextBox'ın Template'i içerisinde bulunan bir Border nesnesine
Tooltip kontrolü atanmış. Söz konusu Tooltip kontrolünün şablonu
ise doğrudan ValidationToolTipTemplate adındaki şablonla
eşleştirilmiş durumda. Böylece biz doğrudan ValidationToolTipTemplate
nesnesini değiştirerek validasyon esnasında görülen Tooltip'i
değiştirebiliriz.

[XAML]

                <Border
CornerRadius
="4" Background="Green">

                    <TextBlock
Margin
="8,3,8,4" MaxWidth="250"
UseLayoutRounding
="false" Foreground="White"
Text
="{Binding
Path
=(Validation.Errors)[0].Exception.Message}" TextWrapping="Wrap"/>

                </Border>

Yukarıdaki kesiti ValidationToolTipTemplate içerisinden aldım.
Tooltip şablonu içerisinde buradaki Textblock'un Text'i görüldüğü üzere
bir Binding ile beraber geliyor. Arkaplanda yolladığımız
Exception'ların mesajlarının Tooltip içerisinde gözükmesini
sağlayan sistem bu bindingden ibaret. Eğer burayı silersiniz
mesajlarınız gözükmeyecektir o nedenle tasarımınızı değiştirirken bu
noktaya dikkat etmekte fayda var.

Hepinize kolay gelsin.