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就可以正常显示,界面如下
实现起来方便又不失美观。
欢迎大家讨论指正……