Merhabalar, daha önceki yazımda Dynamic Data Web Site'ını özelleştirebileceğimiz konusundan bahsetmiştim. Bu yazı özelleştirme konusuna giriş amacı taşımaktadır.
ASP.NET Dynamic Data Web Site'ın oluşturulmasıyla birlikte gelen sayfa şablonları üzerinden yazıma devam ediyorum.
Bazı veritabanlarında tablo isimleri kolon isimleri vb. alanlar firmanın kullandığı standartlar kapsamında belirleniyor. Bir tablo adlandırması çok farklı prefixler alabilir, kısaltmalardan faydalanarak kullanılıyor olabilir yada okunuşu çok anlamsız bir şekilde olabilir. Bu temelde yazılımsal olarak bir sııntı oluşturmuyor ancak bu isimlendirme standartlarından kullanıcıların anlamasını beklemek çok yanlış olacaktır. Dynamic Data Web Site'ın çözüm için üzerinde modifikasyon(lar) yapılacak tabloya partial olan farklı bir class yazıyoruz ve class'a MetaDataType özniteliğini atıyoruz. Zor gibi görünen bu yapı kodlaması oldukca basittir.
Employee tablosunun List.aspx şablonunda görüntüsü:
Bu tablo modifikasyonlar yapmaya başlayalım. Tanımalayacağımız class partial olduğundan LINQ to SQL Class'ında generate edilmiş tablo adı ile aynı isimi taşıması gerekiyor. Tablonun atandığı class adını LINQ to SQL Class'ından tespit edilebilir.
Employee tablosu için yazılmış örnek MetaData sınıfı:
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
[MetadataType(typeof(EmployeeMetaData))]
public partial class Employee
{
}
public class EmployeeMetaData
{
/* Tanımlamalar burada yapılacak. */
}
Üzerinde değişiklik yapılmak istenen kolon için MetaData sınıfı içinde propery tanımlaması yapılmalı. Değişken adının aynı olması gerektiğini tekrar hatırlatıyım. Örneğin FirstName'in Name olarak gösterilmesi istiyorsak:
[DisplayName("Name")]
public string FirstName { get; set; }
Dikkat edilmesi gereken en önemli noktalardan birisi yapılan isim değişikliğinin FirstName alanının kullanıldığı tüm şablonlara yansıtılacağıdır.DisplayName özniteliği dışında bir çok özniteliği kullanarak modifikasyon işlemleri zenginleştirilebilir.
Görünmesini istemenmeyen herhangi bir alanı gizmelek için kullanılan öznitelik ataması
[ScaffoldColumn(false)]
Format ataması bildirimleri için DisplayFormat özniteliği kullanılabilir.
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime HireDate { get; set; }
Bazı durumlarda özelleştirilmiş FieldTemplate'ler kullanılması gerekiyor. Bu gibi durumlarda UIHint özniteliği kullanılabilir. Edit modu için herhangi bir tanımlama yapılmaz. [KontrolAdı]_Edit.ascx aranır yoksa varsayılan field ile çalışır. PhotoPath alanının gösterimi için Image kontrolünü kullanmayı düşünyorum. Varsayılan olarak Text şablonunu kullanmaması için aşağıdaki bildirim yapılmalıdır.
[UIHint("ThumbImage")]
public string PhotoPath { get; set; }
ThumbImage şablonumun kaynak kodları şöyledir
ThumbImage.ascx
<%@ Control Language="C#" CodeFile="ThumbImage.ascx.cs" Inherits="ThumbImageField" %>
<asp:Image runat="server" ID="imgThumb" ImageUrl="<%# FieldValueString %>" />
ThumbImage.ascx.cs
using System.Web.UI;
public partial class ThumbImageField : System.Web.DynamicData.FieldTemplateUserControl
{
public override Control DataControl {
get {
return imgThumb;
}
}
}
Path'i alınan resimin thumbnail halini göstermem gerekiyor. Aksi halde görüntü kirliliği oluşabilir. Bu nedenle resimler için Generic Handler yazmamız sorunu çözecektir.
Edit şablonlarına örnek olması amacıyla farklı tipteki bir alana özel şablon geliştirelim. DateTime özel şablonunda DateTime Picker kullanımına örnek olarak aşağıdaki tanımlamayı yapıyorum. Projeye AjaxControlToolkit'i eklemeyi unutmayın.
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}"),UIHint("CustomDateTime")]
public DateTime HireDate { get; set; }
CustomDateTime_Edit.ascx
<%@ Control Language="C#" CodeFile="CustomDateTime_Edit.ascx.cs" Inherits="CustomDateTime_EditField" %>
<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" tagprefix="cc1" %>
<asp:TextBox ID="TextBox1" runat="server" CssClass="droplist" Text='<%# FieldValueEditString %>' Columns="20"></asp:TextBox>
<cc1:CalendarExtender ID="TextBox1_CalendarExtender" runat="server"
TargetControlID="TextBox1">
</cc1:CalendarExtender>
<asp:RequiredFieldValidator runat="server" ID="RequiredFieldValidator1" CssClass="droplist" ControlToValidate="TextBox1" Display="Dynamic" Enabled="false" />
<asp:RegularExpressionValidator runat="server" ID="RegularExpressionValidator1" CssClass="droplist" ControlToValidate="TextBox1" Display="Dynamic" Enabled="false" />
<asp:DynamicValidator runat="server" ID="DynamicValidator1" CssClass="droplist" ControlToValidate="TextBox1" Display="Dynamic" />
CustomDateTime_Edit.ascx.cs
using System;
using System.Collections.Specialized;
using System.Web.UI;
public partial class CustomDateTime_EditField : System.Web.DynamicData.FieldTemplateUserControl
{
protected void Page_Load(object sender, EventArgs e) {
TextBox1.ToolTip = Column.Description;
SetUpValidator(RequiredFieldValidator1);
SetUpValidator(RegularExpressionValidator1);
SetUpValidator(DynamicValidator1);
}
protected override void ExtractValues(IOrderedDictionary dictionary) {
dictionary[Column.Name] = ConvertEditedValue(TextBox1.Text);
}
public override Control DataControl {
get {
return TextBox1;
}
}
}
İşlem DateTime varsayılan şablonu üzerinden yapılırsa tüm DateTime alanları bundan etkilenir. FieldTemplate'lerin zenginleştirmesi daha sonraki projelerde de kullanılabileceğinden iyi bir yatırımdır. Dynamic Data'nın Routing yapısı üzerine kurulu olmasından dolayı FileUpload vb. işlemleri yapılabilmesi için farklı yöntemler izlemek gerekiyor. Bu konuyu da farklı bir yazı konusu olarak düşünüyorum.
Gelelim kullanıcı ve sunucu taraflı doğrulama işlemlerine. FieldTemplate'ler üzerinde yapılabileceği gibi olay tabanlı doğrulama işlemlerinin yapılmasıda mümkün oluyor. Ekleme, güncelleme, silme gibi işlemleri MetaData ile çok kolay bir şekilde denetleyebiliyoruz ve gerek duyulan durumlarda hata fırlatma yöntemi ile Dynamic Data'nın durumu farketmesini sağlayabiliyoruz. Varsayılan alan şablonlarında veri tipi tanımlamasına uygun veri girdileri sağlamak adına standart doğrulama işlemleri mevcut. Fakat bazı durumlarda bazı alanlar için özel tanımlama yapılması gerekebilir.
Aralık bildirimi için kullanılan Range özniteliğine bir örnek:
[Range(0,30,ErrorMessage="This area should be a value between 0 to 30.")]
public System.Nullable<short> UnitsInStock { get; set; }
Sunucu tabanlı doğrulama işlemleri tanımlamak bazı durumlarda zorunludur. Product tablosundaki stok alanı ile siparişteki ürün sayısını tutan alanın birbiriyle tutarlı çalışması gerekiyorsa yani güncelleme olayında girilmiş stok sayısının verilen sipariş sayısından küçük olmasını engelleme veya tam tersi gibi işlemler için öznitelik ataması yapmak(özelleştirilmiş öznitelik de olabilir) mantıklı değildir. Bu yüzden güncelleme olayı için aşağıdaki kod yazılabilir. Yazacağımız method partial olacağından MetaData sınıfına yazmamız mümkün olmuyor. Method doğrudan MetaDataType özniteliğini atadığımız sınıf içinde tanımlanmalı.
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
[MetadataType(typeof(ProductMetaData))]
public partial class Product
{
// Depodaki ürünün stok miktarını tutan alanın değişim olayı
partial void OnUnitsInStockChanging(short? value)
{
if (value.HasValue)
{
// Yeni deper ürüne verilen sipariş sayısından az ise
if (value < this._UnitsOnOrder)
{
throw new ValidationException("
Bu üründen daha fazla sipariş vermelisiniz");
}
}
}
}
public class ProductMetaData
{
[Range(0, 30,
ErrorMessage = "Bu alan 0 ile 30 arasında değer alabilir")]
public System.Nullable<short> UnitsInStock { get; set; }
}
Sayfa şablonlarını özelleştirmek
Dynamic Data Web sitesinin varsayılan şablonlarını bazı sayfalar için kullanmak istemeyebiliriz. Herhangi bir şablon için özel sayfa tasarlamak istiyorsak varsayılan Dynamic Data Web sitesi ile gelen ~/DynamicData/CustomPages klasörü içine TabloAdı/ŞablonAdı.aspx oluşturulması yeterli. Daha iyi anlaşılması için Product tablosu için özel sayfamızı tasarlamaya başlayalım.
Standart şablondan kopyala yapıştır ile ~/DynamicData/CustomPages/Products/ konumuna yapıştırdığım List.aspx şablonu kullanımdadır. Şablon üzerinde özelleştirme yapalım. Product tablosundaki tüm alanların görünmesini istemiyorum. Bunun için MetaData'da yazabiliriz ancak DynamicDataField kullanımını da gösterebilmek adına GridView1 nesnesinin AutoGenerateColumns özelliğini false yapıyorum.
Daha sonra görüntülenmesini istediğim alanları ekliyorum.
<asp:GridView ID="GridView1" runat="server" DataSourceID="GridDataSource"
AllowPaging="True" AllowSorting="True" AutoGenerateColumns="false" CssClass="gridview">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:HyperLink ID="EditHyperLink" runat="server"
NavigateUrl='<%# table.GetActionPath(PageAction.Edit, GetDataItem()) %>'
Text="Edit" /> <asp:LinkButton ID="DeleteLinkButton" runat="server" CommandName="Delete"
CausesValidation="false" Text="Delete"
OnClientClick='return confirm("Are you sure you want to delete this item?");'
/> <asp:HyperLink ID="DetailsHyperLink" runat="server"
NavigateUrl='<%# table.GetActionPath(PageAction.Details, GetDataItem()) %>'
Text="Details" />
</ItemTemplate>
</asp:TemplateField>
<asp:DynamicField runat="server" DataField="ProductID" />
<asp:DynamicField runat="server" DataField="ProductName" />
<asp:DynamicField runat="server" DataField="UnitPrice" />
<asp:DynamicField runat="server" DataField="Discontinued" />
</Columns>
<PagerStyle CssClass="footer"/>
<PagerTemplate>
<asp:GridViewPager runat="server" />
</PagerTemplate>
<EmptyDataTemplate>
There are currently no items in this table.
</EmptyDataTemplate>
</asp:GridView>
Tüm sayfa istenilen şekilde düzenlenebilir. Aynı durumu diğer sayfa şablonları içinde yapabiliriz. Projedeki bazı veri girişi ekranlarının vb. özelleştirilmesi gerekiyorsa bu tekniğin kullanılmasında fayda vardır ancak zorunlu olmadığı sürece kullanılması projenin geliştirme sürecinde daha sonrası için sorun olabilir. Bağımsız sayfalar olduğunu düşünürseniz dinamik altyapı ile kontrol ettiğiniz bazı işlevleri özel sayfalarda gerçekleştirmekte sıkıntı yaşayabilirsiniz.
Bir sonraki makalede görüşmek üzere. Yaşasın dinamizm!
Ömer Faruk ZORLU