Silverlight在MOSS2010上用作前端显示,给用户带来了更好的体验,也让开发者在作前端UI上有了更多的选择,是为前端的开发利器。
这里要记录的开发工作是这样的:需要在首页上显示一个Event的视图,用来显示一周内公司所有的Event,这些event的源头可能来自不同的站点,因此需要先在后台创建一个job,每天先从这些不同的站点下收集event,存放在一个list中,然后再将这个list数据用Silverlight显示出来。
为了以后更方便的访问SharePoint服务器端的对象模型,我们对底层的list数据访问进行了抽象,我们创建了common的类库,在其中创建了几个泛型接口:
public interface IListDataCreate<T>
{
bool InsertDataIntoList(T t);
}
public interface IListDataDelete
{
void DeleteListItems(int[] itemIDlist);
}
public interface IListDataRetrieve<T>
{
T ListDataItemRetrieve();
}
这些接口代表了对List的查询,增,删等等操作,然后创建抽象类
public abstract class BaseItemDataOperation<M, N> : IDisposable, IListDataCreate<M>, IListDataRetrieve<N>, IListDataDelete
{
protected SPSite currentSite = null;
protected SPWeb currentWeb = null;
protected SPList targetList = null;
protected string listName = String.Empty;
public BaseItemDataOperation(string currentSiteURL)
{
currentSite = new SPSite(currentSiteURL);
currentWeb = currentSite.OpenWeb();
}
public abstract bool InsertDataIntoList(M m);
public abstract N ListDataItemRetrieve();
public abstract void DeleteListItems(int[] itemIds);
public virtual void Dispose()
{
if (null != currentWeb)
{
currentWeb.Close();
}
if (null != currentSite)
{
currentSite.Close();
}
}
}
创建实现类,继承上面的父类,实现接口方法
public class SPListItemCollectionCRUD : BaseItemDataOperation<IList<Dictionary<string, object>>, SPListItemCollection>
{
private string camlQuery = String.Empty;
public string CamlQuery
{
get;
set;
}
public string ListName
{
get;
set;
}
public SPListItemCollectionCRUD(string currentSiteURL, string listName):base(currentSiteURL)
{
this.ListName = listName;
}
public SPListItemCollectionCRUD(string currentSiteURL, string camlQuery, string listName):this(currentSiteURL,listName)
{
CamlQuery = camlQuery;
}
#region //IListDataRetrieve method implement
/// <summary>
/// Retrieve data from target list
/// </summary>
/// <returns></returns>
public override SPListItemCollection ListDataItemRetrieve()
{
if (String.IsNullOrEmpty(CamlQuery))
{
throw new Exception("Please provide caml string.");
}
else
{
if (String.IsNullOrEmpty(ListName))
{
throw new Exception("Please provide target list name.");
}
else
{
targetList = currentWeb.Lists[ListName];
SPQuery query = new SPQuery();
query.Query = CamlQuery;
return targetList.GetItems(query);
}
}
}
#endregion
#region //IListDataCreate method implement
/// <summary>
/// Add one Item to target list
/// </summary>
/// <param name="fieldValuePairs">The source data, set as field value pairs</param>
/// <returns></returns>
public override bool InsertDataIntoList(IList<Dictionary<string, object>> itemList)
{
if (String.IsNullOrEmpty(ListName))
{
throw new Exception("Please provide target list name.");
}
else
{
try
{
targetList = currentWeb.Lists[ListName];
SPListItem newItem = null;
for (int i = 0; i < itemList.Count; i++)
{
newItem = targetList.AddItem();
Dictionary<string, object> fieldValuePairs = itemList[i];
foreach (KeyValuePair<string, object> item in fieldValuePairs)
{
newItem[item.Key] = item.Value;
}
newItem.Update();
}
return true;
}
catch
{
return false;
}
}
}
#endregion
#region//IListDataDelete method implement
/// <summary>
/// Delete item from list by id list
/// </summary>
/// <param name="itemIds"></param>
public override void DeleteListItems(int[] itemIds)
{
targetList = currentWeb.Lists[ListName];
foreach(int id in itemIds)
{
targetList.Items.DeleteItemById(id);
}
}
#endregion
public void DeleteAllItems()
{
targetList = currentWeb.Lists[ListName];
for (int i = targetList.ItemCount - 1; i >= 0; i--)
{
targetList.Items[i].Delete();
}
}
#region //IDispose method
public override void Dispose()
{
if (null != currentWeb)
{
currentWeb.Close();
}
if (null != currentSite)
{
currentSite.Close();
}
}
#endregion
}
注意在编译这个library时,确保你项目属性的.net framework为3.5,因为你引用的Microsoft.SharePoint .dll .Net Framework 3.5的,而VS2010默认是4.0。
这样一个Common的List数据操作类库就完成了,以后可以不断的重用它,并且加入新的方法,现在我们创建一个Console项目作为每天获取Event的Job程序,添加对Common library的引用,并且在main方法中获取来自别的Site的List中的event数据并写入我们创建的用于首页显示的List中:
static void Main(string[] args)
{
string siteURL = "Your target site";
string knowledgeSite = "Your source site";
string camlQuery = "";
string listName = "Your source list";
string insertTargetListName = "Your target list";
DateTime firstWeekday = DateTime.Now.AddDays(((double)DateTime.Now.DayOfWeek - 1.0) * -1.0);
DateTime endWeekDay = DateTime.Now.AddDays((7 - (double) DateTime.Now.DayOfWeek) * 1.0);
string isoFirstDate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(firstWeekday);
string isoEndDate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(endWeekDay);
camlQuery = @"<ViewFields>
<FieldRef Name='LinkTitle' />
<FieldRef Name='Start_x0020_Time' />
<FieldRef Name='End_x0020_Time' />
</ViewFields>
<OrderBy>
<FieldRef Name='Start_x0020_Time'/>
</OrderBy>
<Where>
<And>
<Geq>
<FieldRef Name= 'Start_x0020_Time' />
<Value Type='DateTime'>" + isoFirstDate + @"</Value>
</Geq>
<Leq>
<FieldRef Name= 'End_x0020_Time' />
<Value Type='DateTime'>" + isoEndDate + @"</Value>
</Leq>
</And>
</Where>
";
using (BaseItemDataOperation<IList<Dictionary<string, object>>, SPListItemCollection> listRetrieve = new SPListItemCollectionCRUD(knowledgeSite, camlQuery, listName))
{
SPListItemCollection itemCollection = listRetrieve.ListDataItemRetrieve();
using (BaseItemDataOperation<IList<Dictionary<string, object>>, SPListItemCollection> listCreate = new SPListItemCollectionCRUD(siteURL, insertTargetListName))
{
List<Dictionary<string, object>> dataCollection = new List<Dictionary<string, object>>();
foreach (SPListItem item in itemCollection)
{
Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
keyValuePairs["Title"] = item["Topic"];
keyValuePairs["Time Span"] = item["Start Time"].ToString() + " ~ " + item["End Time"].ToString();
keyValuePairs["Original Link"] = knowledgeSite + "/Lists/Training%20Schedule/DispForm.aspx?ID=" + item.ID.ToString();
dataCollection.Add(keyValuePairs);
}
(listCreate as SPListItemCollectionCRUD).DeleteAllItems();
bool isSuccess = listCreate.InsertDataIntoList(dataCollection);
}
}
}
每天运行这个Job,保证最终List中汇集了当前周的所以Event。在编译运行这个Job前,先确认此project属性中的target platform设为x64,因为moss2010运行在64位的平台上,不作此设置,运行将会出错。
现在开始介绍前端Silverlight显示的部分:
首先创建一个Silverlight library 用来完成所有底层访问SharePoint 客户端对象模型的操作,具体代码如下
public abstract class BaseClientListProxy <T>
{
protected string SiteURL { get; set; }
public abstract void GetListItemsAsync(string listName, string queryXML, out T listItems);
}
public class ClientOMProxy:BaseClientListProxy<ListItemCollection>, IDisposable
{
private ClientContext clientContext = null;
public ListItemCollection listItems = null;
public ClientOMProxy(string siteURL)
{
this.SiteURL = siteURL;
clientContext = new ClientContext(this.SiteURL);
}
public ClientRequestSucceededEventHandler successEventHandler = null;
public ClientRequestFailedEventHandler failEventHandler = null;
public override void GetListItemsAsync(string listName, string viewXML, out ListItemCollection listItems)
{
clientContext.Load(clientContext.Web);
List targetList = clientContext.Web.Lists.GetByTitle(listName);
clientContext.Load(targetList);
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml = viewXML;
listItems = targetList.GetItems(camlQuery);
clientContext.Load(listItems);
clientContext.ExecuteQueryAsync(successEventHandler, failEventHandler);
}
public void CreateListItemAsync(string listName, Dictionary<string, object> fieldValueDic, ClientRequestSucceededEventHandler onSuccess, ClientRequestFailedEventHandler onFail)
{
clientContext.Load(clientContext.Web);
List targetList = clientContext.Web.Lists.GetByTitle(listName);
clientContext.Load(targetList);
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
ListItem oListItem = targetList.AddItem(itemCreateInfo);
foreach (KeyValuePair<string, object> pair in fieldValueDic)
{
oListItem[pair.Key] = pair.Value;
}
oListItem.Update();
clientContext.Load(oListItem);
clientContext.ExecuteQueryAsync(onSuccess, onFail);
}
public void UpdateListItemAsync(string listName, ListItem item, Dictionary<string, object> fieldValueDic, ClientRequestSucceededEventHandler onSuccess, ClientRequestFailedEventHandler onFail)
{
clientContext.Load(clientContext.Web);
List targetList = clientContext.Web.Lists.GetByTitle(listName);
clientContext.Load(targetList);
ListItem oListItem = item;
foreach (KeyValuePair<string, object> pair in fieldValueDic)
{
oListItem[pair.Key] = pair.Value;
}
oListItem.Update();
clientContext.Load(oListItem);
clientContext.ExecuteQueryAsync(onSuccess, onFail);
}
public void Dispose()
{
if (null != clientContext)
clientContext.Dispose();
}
}
然后创建Silverlight Application project,在mainpage.xaml文件中添加datagrid,设置显示style和绑定列
<Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5,5,5,5" >
<Border BorderThickness="1" BorderBrush="#b8babd" CornerRadius="10" Width="280" Height="200">
<data:DataGrid Name="eventGrid" AutoGenerateColumns="False" Margin="5,5,5,5" BorderThickness="0" IsReadOnly="True" CanUserResizeColumns="False" AlternatingRowBackground="White" HorizontalAlignment="Center">
<data:DataGrid.ColumnHeaderStyle>
<Style
xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
TargetType="primitives:DataGridColumnHeader" >
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="primitives:DataGridColumnHeader" >
<Grid Name="Root">
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="SortStates" >
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition GeneratedDuration="00:00:0.1" />
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="Unsorted" />
<vsm:VisualState x:Name="SortAscending">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1.0" />
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="SortDescending">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1.0" />
<DoubleAnimation Storyboard.TargetName="SortIconTransform" Storyboard.TargetProperty="ScaleY" Duration="0" To="-.9" />
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Rectangle x:Name="BackgroundRectangle" Stretch="Fill" Grid.ColumnSpan="2" Grid.RowSpan="2">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#2A3A57" Offset="0.0" ></GradientStop>
<GradientStop Color="#6c8cbe" Offset="1.0"></GradientStop>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter Grid.RowSpan="2" Content="{TemplateBinding Content}" Cursor="{TemplateBinding Cursor}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}" />
<Rectangle Name="VerticalSeparator" Grid.RowSpan="2" Grid.Column="2" Width="1" VerticalAlignment="Stretch" Fill="{TemplateBinding SeparatorBrush}" Visibility="{TemplateBinding SeparatorVisibility}" />
<Path Grid.RowSpan="2" Name="SortIcon" RenderTransformOrigin=".5,.5" HorizontalAlignment="Left" VerticalAlignment="Center" Opacity="0" Grid.Column="1" Stretch="Uniform" Width="8" Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z ">
<Path.Fill>
<SolidColorBrush Color="#FF444444" />
</Path.Fill>
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="SortIconTransform" ScaleX=".9" ScaleY=".9" />
</TransformGroup>
</Path.RenderTransform>
</Path>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGrid.ColumnHeaderStyle>
<data:DataGrid.Columns>
<data:DataGridTemplateColumn Width="Auto" CanUserResize="False" Header="">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<HyperlinkButton HorizontalContentAlignment="Left" Grid.Row="0" FontWeight="Bold" Foreground="#0072bc" FontSize="10" VerticalContentAlignment="Center" Content="{Binding Event}" NavigateUri="{Binding Url}"></HyperlinkButton>
<TextBlock Text="{Binding Time}" VerticalAlignment="Center" Foreground="#0072bc" Grid.Row="1" FontSize="9" TextWrapping="Wrap" Margin="3,3,3,3"></TextBlock>
</Grid>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
</data:DataGrid>
</Border>
在项目中添加Event实体类,用作Datagrid数据绑定
public class EventEntity
{
public string Time { get; set; }
public string Event { get; set; }
public string Url { get; set; }
}
在mainpage.xaml.cs中添加显示数据的代码:
private ClientOMProxy clientProxy = null;
private string targetListName = null;
private ListItemCollection targetEventItems = null;
public MainPage(string siteUrl,string listName)
{
InitializeComponent();
this.targetListName = listName;
string currentPageURL = siteUrl;
string viewXML = @"<View><ViewFields>
<FieldRef Name='LinkTitle' />
<FieldRef Name='Time_x0020_Span' />
<FieldRef Name='Original_x0020_Link'/></ViewFields></View>";
clientProxy = new ClientOMProxy(currentPageURL);
clientProxy.successEventHandler += new ClientRequestSucceededEventHandler(OnRequestSucceeded);
clientProxy.failEventHandler += new ClientRequestFailedEventHandler(OnRequestFailed);
clientProxy.GetListItemsAsync(listName, viewXML, out targetEventItems);
}
private void OnRequestSucceeded(Object sender, ClientRequestSucceededEventArgs args)
{
Dispatcher.BeginInvoke(BindData);
clientProxy.Dispose();
}
private void OnRequestFailed(Object sender, ClientRequestFailedEventArgs args)
{
clientProxy.Dispose();
}
private string CutDateTime(string timespan)
{
string[] dateArray = timespan.Split(new string[]{"~"}, StringSplitOptions.RemoveEmptyEntries);
string result = String.Empty;
DateTime startTime = Convert.ToDateTime(dateArray[0]);
result += startTime.ToString(@"MM\/dd\/yyyy HH:mm");
result += " ~ ";
DateTime endTime = Convert.ToDateTime(dateArray[1]);
result += endTime.ToString(@"MM\/dd\/yyyy HH:mm");
return result;
}
private void BindData()
{
EventEntity aecEvent = null;
IList<EventEntity> eventsList = new List<EventEntity>();
foreach (ListItem item in targetEventItems)
{
aecEvent = new EventEntity();
aecEvent.Event = item["Title"].ToString();
aecEvent.Time = CutDateTime(item["Time_x0020_Span"].ToString());
aecEvent.Url = item["Original_x0020_Link"].ToString() + "&Source=" + HtmlPage.Document.DocumentUri.ToString();
eventsList.Add(aecEvent);
}
this.eventGrid.ItemsSource = eventsList;
}
同时修改App.xaml.cs中Application_Startup的方法,确保从外部获得设置的参数
private void Application_Startup(object sender, StartupEventArgs e)
{
string siteUrl = e.InitParams["SiteUrl"];
siteUrl = System.Windows.Browser.HttpUtility.UrlDecode(siteUrl);
string listName = e.InitParams["SourceListName"];
listName = System.Windows.Browser.HttpUtility.UrlDecode(listName);
this.RootVisual = new MainPage(siteUrl, listName);
}
编译后,上传到SharePoint site上,然后添加silverlight web part,设置xap的url地址,同时设置外部参数,添加参数如下SiteUrl = ”your site”, SourceListName="your list name” 保存后,silverlight就可以正常显示,界面如下
实现起来方便又不失美观。
欢迎大家讨论指正……