13 Temmuz 2011 Çarşamba

Silverlight Web Part Tarih Problemi

Şirkette bir süredir çalıştığım Sharepoint 2010 projesinde, Silverlight Web Part'lar kullanmaya karar verdiğimizden beri çeşitli sıkıntılar yaşıyorum ve bunlardan en ilginci de birazdan anlatacağım tarih problemi.

Örnek olarak Sharepoint 2010 üzerindeki Tasks listesini vereceğim. Bu listede yer alan görev elemanlarının Date and Time tipinden bir "Start Date" özelliği mevcut ve görevin başlama tarihini işaret ediyor kendisi. Pekii.. O zaman bu Task listesinin elemanlarını Client Object Model kullanarak Silverlight Web Part'ımızda gösterelim. Öncelikle listemiz:








Bu da yazdığımız ufak uygulamanın ekran çıktısı:












Aaa, o ne yaa? Bu tarihler farklı?! Xaml'deki language ayarını bir Tr-tr yapın bakalım. Oldu mu? Olmadı.. Çekilen datetime değerlerini Türkçe region ayarlarına göre formatlayın, deneyin. Oldu mu? Yine olmadı. Çaresizce Sharepoint portalı, sitesi, kullandığınız istemci bilgisayarın tüm dil ayarlarını Türkçe yapın bakalım. Bu sefer olmuştur ama. Yine mi olmadı? Olmaz abi olmaz. Ben hepsini yaptım olmadı çünkü :)

Sorunun nedeni regional ayarlar ama haklısınız. Hatta dikkat ederseniz listedeki değerler ile Client Object Model'den gelen değerler arasında tam olarak 3 saatlik bir fark olduğunu göreceksiniz. Eee nasıl çözülecek bu? Şimdi nette uzun uğraşlar sonucunda şöyle bir makale buldum ve burada anlatılanların işe yaradığını gördüm. Yalnız burada arkadaş Console Application örneği üzerinden gitmiş ve bu nedenle yapılan çağrıların hepsi senkron. Ancak biz UI içeren Silverlight uygulaması kullandığımız için çağrılarımız asenkrondu hatırlarsanız. Ben de bu makalede asenkron çağrılar kullanarak tarih formatı düzeltme işleminin nasıl yapılacağını anlatacağım.

Yine bir örnek uygulama üzerinden gidelim. Tasks listesindeki Görevleri Silverlight Web Part'ımızda başlangıç tarihleri ile birlikte gösteren bir uygulama geliştirelim. Öncelikle önyüzümüz olan Xaml kodumuza gerekli ekran elemanlarını ekliyoruz.
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"  x:Class="SLWebPart.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" xml:lang="tr-TR"
d:DesignHeight="300" d:DesignWidth="400">

<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Vertical">
<Button x:Name="btnGorevGetir" Content="Tüm Görevleri Getir" Click="btnGorevGetir_Click"
Width="200" Height="50"></Button>
<sdk:DataGrid x:Name="taskGrid" AutoGenerateColumns="False">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Görev No" Binding="{Binding GorevID}"/>
<sdk:DataGridTextColumn Header="Görev Rolü" Binding="{Binding GorevAdi}" />
<sdk:DataGridTextColumn Header="Bağlı Talep" Binding="{Binding BaslangicTarihiAfter}" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</StackPanel>
</Grid>
</UserControl>
Görüldüğü gibi önyüzümüzde görevleri çekme komutunu veren bir buton ve görevleri listelediğimiz bir grid olacak.

Asıl iş olan code-behind'da normalden farklı olarak yapmamız gereken işlem şu; Asenkron bir çağrı ile çektiğimiz liste verilerinden tarih olanı formatlayarak tekrar bir asenkron çağrı yapmak. Bunu da tahmin edebileceğiniz üzere OnSuccess callback metodu içerisinde yapacağız. Görelim;
private void success()
{
taskList = new List<Task>();

foreach (ListItem item in taskCollection)
{
Task newTask = new Task();
newTask.GorevID = Convert.ToInt32(item["ID"]);
newTask.GorevAdi = Convert.ToString(item["Title"]);

DateTime baslangicTarihiWrong = Convert.ToDateTime(item["StartDate"]);
newTask.BaslangicTarihiBefore = Utility.FormatDateTime(spContext, spContext.Web, baslangicTarihiWrong, DateTimeFormat.DateTime);

taskList.Add(newTask);
}

spContext.ExecuteQueryAsync(TarihSuccess, OnFailure);
}
Burada can alıcı nokta Utility.FormatDateTime kullanımı. Bu metot ile Client Object Model'den yanlış olarak çekilen tarihi formatlama işlemini gerçekleştiriyoruz. Yalnız formatladıktan sonra işimiz bitiyor mu? Tabii ki hayır! Bu metot bize ClientResult tipinde bir değer dönüyor ve bu değer tahmin edebileceğiniz üzere hemen kullanılamıyor. Ne zaman kullanılabilir? Tabii ki asenkron bir çağrı yapıp ClientContext'e yüklendikten sonra. Eee ben kocaman bir listede her elemanın tarihini formatlamak için asenkron bir çağrı mı yapacağım?! Tabii ki hayır! Bunun için birkaç yöntem kullanılabilir. Benim uydurduklarımdan bir tanesi, list item tipini çevirdiğiniz kendi veri tipiniz içerisinde ClientResult tipinde bir Property yaratmanız ve örnekte de görüldüğü üzere ilk atama işlemini bu property'e yapmanız. Daha sonra yaptığınız asenkron çağrının Success'inde ise bu Property'nin datetime value'sunu okumanız. Görelim;
private void tarihSuccess()
{
foreach (Task item in taskList)
{
item.BaslangicTarihiAfter = Convert.ToDateTime(item.BaslangicTarihiBefore.Value);
}

taskGrid.ItemsSource = taskList;
}
ClientResult nesnesinin Value property'si bize formatlanmış tarih değerini verecektir.

Code behind'ın tümü:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Utilities;

namespace SLWebPart
{
public partial class MainPage : UserControl
{
ClientContext spContext;

ListItemCollection taskCollection;
List<Task> taskList;

private delegate void SuccessDelegate();
private delegate void FailDelegate(Exception ex);

public MainPage()
{
InitializeComponent();
}

private void btnGorevGetir_Click(object sender, RoutedEventArgs e)
{
spContext = new ClientContext("http://t1wsportal01/TalepYonetimi/");
List taskList = spContext.Web.Lists.GetByTitle("Tasks");

CamlQuery query = new CamlQuery();
query.ViewXml = String.Format(@"<View><Query></Query></View>");

taskCollection = taskList.GetItems(query);

spContext.Load(taskCollection);
spContext.ExecuteQueryAsync(OnSuccess, OnFailure);
}

private void OnFailure(object sender, ClientRequestFailedEventArgs e)
{
FailDelegate delegateMethod = fail;
this.Dispatcher.BeginInvoke(delegateMethod, e.Exception);
}

private void fail(Exception ex)
{
MessageBox.Show(ex.Message);
}

private void OnSuccess(object sender, ClientRequestSucceededEventArgs e)
{
SuccessDelegate delegateMethod = success;
this.Dispatcher.BeginInvoke(delegateMethod);
}

private void success()
{
taskList = new List<Task>();

foreach (ListItem item in taskCollection)
{
Task newTask = new Task();
newTask.GorevID = Convert.ToInt32(item["ID"]);
newTask.GorevAdi = Convert.ToString(item["Title"]);
DateTime baslangicTarihiWrong = Convert.ToDateTime(item["StartDate"]);
newTask.BaslangicTarihiBefore = Utility.FormatDateTime(spContext, spContext.Web, baslangicTarihiWrong, DateTimeFormat.DateTime);

taskList.Add(newTask);
}

spContext.ExecuteQueryAsync(TarihSuccess, OnFailure);
}

private void TarihSuccess(object sender, ClientRequestSucceededEventArgs e)
{
SuccessDelegate delegateMethod = tarihSuccess;
this.Dispatcher.BeginInvoke(delegateMethod);
}

private void tarihSuccess()
{
foreach (Task item in taskList)
{
item.BaslangicTarihiAfter = Convert.ToDateTime(item.BaslangicTarihiBefore.Value);
}

taskGrid.ItemsSource = taskList;
}
}
}

Task sınıfımız:
using System;
using Microsoft.SharePoint.Client;
namespace SLWebPart
{
public class Task
{
public int GorevID { get; set; }
public string GorevAdi { get; set; }

public ClientResult<string> BaslangicTarihiBefore { get; set; }
public DateTime BaslangicTarihiAfter { get; set; }
}
}
Uygulamamızı portalımıza web part olarak yükledikten sonra çalıştıralım:












Görüldüğü gibi tarihler artık doğru bir şekilde gösteriliyor.

Kolay gelsin,



Hiç yorum yok: