估计大家看到这个标题,可能还无法理解我这个系统是用来干什么的,先说一个场景,犹如一些咖啡厅的心语墙,客户可以找服务员索取便签纸,写下自己的心语,然后在心语墙上找一个合适的位置贴下。对于这个便签纸,自己在将来的某一天再次光顾这家咖啡厅还可以看到自己以前贴下的心语。当然,其他的顾客也可以浏览,说了这么多,如果您感兴趣,可以猛击:http://fendouzl.s2.jutuo.net 。如果不登录,还有很多功能看不到哦。不过由于时间有限,又要工作,又要做这个毕设系统,还得写论文,因此,目前这个系统还是比较粗糙的,有时间我会去完善之,因为这个系统是我自己在生活中觉得的确需要,正好以前学了silverlight,便想把这个生活模型使用silverlight来模拟,尽管模拟的不好,大家不要见笑,可以直接给我留言指正,我会加以改正的哈。
下面先看看其中一个功能模块的截图

作为一个技术性文章,还是来说说本系统技术方便的事情,不然真会被其他的同学说我在这打广告了。
1 系统架构设计
首先,我想说的是系统的架构,个人觉得这个做一个系统的时候,最好有一个架构图,有了这个架构图,便可以通过这个图导出一个合理的解决方案以及解决方案里面的项目,之后的工作便是在各个项目中填充代码,相对来说还是比较容易的。
1.1 服务器端架构设计
服务器端架构设计的出发点便是如何高效快捷的访问数据库。如图下图所示,可以看到本系统服务端分为四层和三个辅助性的类库,分别是业务逻辑层、工厂层、数据链路接口层、数据链路层、数据访问工具层、公共层、数据库模板层。客户端通过WCF访问业务逻辑层的方法,由于业务逻辑层继承工厂层,因此这个工厂层通过反射技术,动态加载数据链路层的方法,并且辅助层的配合下完成数据库读写,最后把结果通过JSON序列化之后返回给客户端。

1.2 客户端架构设计
客户端主要是silverlight的实现,为了提高客户端代码的重构性和易移植性,决定在客户端使用MVVM模式,架构图如图28所示。其中View层便是UI界面的实现,Model层便是数据源,而ViewModel层便是为了让数据在View层显示的中介。客户端通过WCF请求数据,服务端把数据通过JSON序列化之后返回给客户端。

2.系统解决方案图
通过系统架构图,可以很容易的映射出系统解决方案,系统解决方案列表图如下图所示。目前,自己的技术还不怎么好,不过大型项目,为了避免逻辑混乱,是有必要去分层实现,使用必要的模式,如我这个客户端使用MVVM模式,服务器分为四层。

3 如何连接silverlight客户端上和服务器端
对于这个问题,大家一般都会只用WCF,当然,我也选择这个方式,因为相对比较容易,并且很方便。
View Code
public void Get**FromDB() { string query = “condition” Proxy.GetListAsync(JsonConvert.SerializeObject(query)); Proxy.GetListCompleted += new EventHandler<UserOwnWallServiceReference.GetListCompletedEventArgs>(Proxy_GetListCompleted); } } void Proxy_GetListCompleted(object sender, UserOwnWallServiceReference.GetListCompletedEventArgs e) { Deployment.Current.Dispatcher.BeginInvoke(delegate { if (e.Error == null) { _UserOwnWallCollection = JsonConvert.DeserializeObject<ObservableCollection<UserOwnWall>>(e.Result.ToString()); InitUserUI(); } }); }}
这段代码大致展示了用户如何使用WCF服务代理契约去访问远程数据,至于如何去编写这个服务契约,使用是需要注意什么问题,我就不详细讲了,因为在使用的时候的确会遇到各种奇奇怪怪的问题,不过主要应该是配置文件的改动。
4 用户控件移动代码详解
大家如何查看了http://fendouzl.s2.jutuo.net,那么就应该知道,系统很多地方用到了便签纸在Canvas上移动的体验,为此,我写了一个类来管理便签纸移动,其实就是一个用户的控件在canvas上移动,代码如下:
View Code
1 public class MouseMoveHelper 2 { 3 #region move 4 static Point _curPoint; //当前点 5 static bool _mouseMoving = false; //定义是否移动中的布尔变量 6 public static void MouseButtonUpHandler(object sender, MouseButtonEventArgs e) 7 { 8 FrameworkElement element = sender as FrameworkElement; 9 element.SetValue(CustomCanvasControl.ZIndexProperty, 0); 10 //放开鼠标之后修改变量值 11 _mouseMoving = false; 12 //停止捕捉鼠标位置 13 element.ReleaseMouseCapture(); 14 //设置当期X Y坐标为0 15 _curPoint.X = 0; 16 _curPoint.Y = 0; 17 //恢复鼠标指针样式 18 element.Cursor = null; 19 } 20 21 public static void MouseButtonDownHandler(object sender, MouseButtonEventArgs e) 22 { 23 FrameworkElement element = sender as FrameworkElement; 24 element.SetValue(CustomCanvasControl.ZIndexProperty, 1); 25 //获得当前鼠标的位置 26 _curPoint = e.GetPosition(element.Parent as CustomCanvasControl); 27 //设置开始拖动的值 28 _mouseMoving = true; 29 if (null != element) { 30 //捕捉鼠标位置 31 element.CaptureMouse(); 32 element.Cursor = Cursors.Hand; 33 } 34 } 35 36 public static void MouseMoveHandler(object sender, MouseEventArgs e) 37 { 38 FrameworkElement element = sender as FrameworkElement; 39 if (_mouseMoving) { 40 try { 41 double currX = e.GetPosition(element.Parent as CustomCanvasControl).X - _curPoint.X; 42 double currY = e.GetPosition(element.Parent as CustomCanvasControl).Y - _curPoint.Y; 43 44 //得到当前画布的宽度和高度 45 double currCanvasWidth = (element.Parent as Canvas).ActualWidth; 46 double currCanvasHeight = (element.Parent as Canvas).ActualHeight; 47 48 //得到当前移动目标的宽度高度 49 double currElementWidth = element.ActualWidth; 50 double currElementHeight = element.ActualHeight; 51 52 if ((currY + (double)element.GetValue(CustomCanvasControl.TopProperty) < 0) || 53 (currX + (double)element.GetValue(CustomCanvasControl.LeftProperty) < 0) 54 || (currY + (double)element.GetValue(CustomCanvasControl.TopProperty) > currCanvasHeight - currElementHeight) || 55 (currX + (double)element.GetValue(CustomCanvasControl.LeftProperty) > currCanvasWidth - currElementWidth)) { 56 //不作任务操作,不移动 57 } else { 58 59 //设置对象坐标 60 element.SetValue(CustomCanvasControl.TopProperty, currY + (double)element.GetValue(CustomCanvasControl.TopProperty)); 61 element.SetValue(CustomCanvasControl.LeftProperty, currX + (double)element.GetValue(CustomCanvasControl.LeftProperty)); 62 } 63 } catch { 64 } 65 } 66 //保存当前坐标值 67 _curPoint = e.GetPosition(element.Parent as CustomCanvasControl); 68 } 69 70 #endregion 71 }
5 Json序列化
目前用来保存和传递数据一般使用Json和XML.使用这两个技术,相对比较容易,重要的很多平台都使用这个两中方式,因此比较通过,Json的使用方式基本是就是序列化和反序列化,使用比较多的是以下两个方法:
View Code
JsonConvert.SerializeObject()
JsonConvert.DeserializeObject<>()
6 HTML 5 GelLocation API
因为系统需要定位,因为使用了HTML 5 的定位API,这个相对来说还是比较容易的,就是一个API,自己的去调用就行,还是给出代码:
View Code
1 function getUserLocation() { 2 if (navigator.geolocation) { 3 navigator.geolocation.getCurrentPosition(show_map); 4 } else { 5 alert("您的浏览器不支持HTML 5来获取地理位置信息。"); 6 } 7 } 8 function show_map(position) { 9 var latitude = position.coords.latitude; 10 var longitude = position.coords.longitude; 11 callSL(latitude, longitude); 12 } 13 14 function callSL(latitude, longitude) { 15 var slHost = document.getElementById("SilverlightControl"); 16 var page = slHost.Content.FootprintContainer; 17 slHost.Content.FootprintContainer.Process(longitude, latitude); 18 }
需要注意的是,Silverlight如何与javascript通信,代码中也有体现,如何大家感兴趣还是去查看相关资料,
7 早来的总结
这个总结的确来的比较早吧,因为还有一些用到的技术还没有来得及写,好不容易一个假期,我却需要来写毕设,马上又得去上班,因此的确没有更多的时间,再加上自己的确还有很多说不明白讲不清楚。不得不说一个人想将来过的好一点,年轻的时候就需要去拼搏。对于前面我讲的,相信大家也注意到了,我对代码的细节没有做更多的讲解,因为现在网上资源还是比较丰富的,只要大家知道有这么回事,知道关键词,便可以很容易的找到答案,因此我写的这篇文章的重点不是在说具体代码是如何实现的,而是提到一个个知识点,一条条思路。说到这儿,相信大虾开始笑了,不过我还是想说说,成为大虾是一个过程,而不是一个结果。
【摘要】
今天,WP7版布丁优惠券V1.0版在将近一个月的开发中终于上架了。大家可以在wp7商城免费下载,免费使用O(∩_∩)O~
网页下载链接:http://www.windowsphone.com/zh-CN/apps/294b3ba9-43f4-4c20-850e-28baf5c11a2c
当然,V1.0版还有很多小缺陷,现在也正在弥补,相信不久就会更新,到时候惠请大家更新哦。
【截图】



【看完图之后,几句寒暄】
希望看完截图,大家有兴趣去下载哈,的确可以给大家的生活省下不少钱哦,有异性朋友的那就更加要下载,不过目前国内拥有wp7手机的用户的确不多,
不过我相信以后会多起来的,不喜欢安卓的卡机,死机同学这个手机是个不错的选择。
【技术要点】
1.图片缓存,图片异步下载
在显示商家优惠券列表的时候,我是使用ListBox来包含所有的优惠券的信息,主要就是显示优惠券的小图片,标题,价格这些内容,当然首次加载的时候,
肯定要从服务器获取数据,对于文字信息好办,直接绑定就可以了,而对于图片,则需要做优化处理,我当时是参见如下,大家可以看看,写的很详细。
http://www.cnblogs.com/alexis/archive/2011/11/17/2253202.html
2.页面传值
对于页面传值的方式,网上有很多资料,当时我觉得用户比较多的是
(1.通过参数传值,类似http 中的get方式
(2.State保存应用程序临时值
PhoneApplicationService.Current.State["currentUserViewFav"] as CouponList;
(3.App中全局变量
3.MVVM
我主要使用的是MVVMlight.大家可以在这去学习下:http://www.galasoft.ch/mvvm/
4.为用户节省流量
这就需要应用对从网上获取的数据有缓存,使用独立存储,本地数据库都可以,当前,我简化了应用,使用的
独立存储,具体使用方式,我可以给出本系统中主要的函数,这些方法大家都可以直接用的。
public static void WriteFileToIso(string fileContent, string folderName, string fileName)
{
try
{
using (var myStore = IsolatedStorageFile.GetUserStoreForApplication())
{
string filePath = Path.Combine(folderName, fileName);
if (!myStore.DirectoryExists(folderName))
{
myStore.CreateDirectory(folderName);
}
using (IsolatedStorageFileStream myFileStream = new IsolatedStorageFileStream(filePath, FileMode.OpenOrCreate, myStore))
{
using (var isoFileWriter = new StreamWriter(myFileStream))
{
isoFileWriter.WriteLine(fileContent);
}
myFileStream.Close();
}
}
}
catch (Exception e)
{
//throw;
}
}
public static string ReadFileFromIso(string folderName, string fileName)
{
using (IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication())
{
try
{
// Specify the file path and options.
string filePath = Path.Combine(folderName, fileName);
string readcontent = null;
using (var isoFileStream = new IsolatedStorageFileStream(filePath, FileMode.Open, myStore))
{
// Read the data.
using (var isoFileReader = new StreamReader(isoFileStream))
{
readcontent = isoFileReader.ReadToEnd();
isoFileReader.Close();
if (readcontent != null)
return readcontent;
else
return null;
}
}
}
catch
{
// Handle the case when the user attempts to click the Read button first.
return null;
}
}
}
public static bool IsFileCached(string folderName, string fileName)
{
using (var myStore = IsolatedStorageFile.GetUserStoreForApplication())
{
string filePath = Path.Combine(folderName, fileName);
if (myStore.FileExists(filePath))
{
return true;
}
else
return false;
}
}
public static bool DeleteFileByFileName(string folderName, string fileName)
{
using (var myStore = IsolatedStorageFile.GetUserStoreForApplication())
{
string filePath = Path.Combine(folderName, fileName);
if (myStore.FileExists(filePath))
{
myStore.DeleteFile(filePath);
}
if (myStore.FileExists(filePath))
{
return false;
}
else
return true; //表示已经删除
}
}
5.了解发布到微软商城的协议
比如在使用GPS定位之前,需要向用户询问是否允许应用使用GPS. 了解这些其实也是为一次性通过微软审核做出准备,不然最后浪费的时间还是自己
的,每次的提交都需要近一个礼拜的等待。
我自己也上传了几个小应用,大致总结下,微软主要审核的内容
(1.语言,就是开发应用使用的语言要和你上传应用目标所在地的语言一致,不然不让通过,PS:当然可以使用本地化
(2.飞行模式测试,如果应用需要从网络上获取资源,要先判断用户当前网络状态
(3.使用手机服务提醒,并且需要在设置中拥有“开”“关”按钮。比如前文中的定位服务
(4.手机主题,再更改手机主题之后,应用中控件的可见性
6.保存页面的状态和应用程序的状态
这个可以参见MSDN,比我说的详细:
保存页面的状态:http://msdn.microsoft.com/zh-cn/library/ff967548(v=vs.92).aspx
保存应用程序的状态:http://msdn.microsoft.com/zh-cn/library/ff967547(v=vs.92).aspx
7.待续吧,还有工作没有完成,wp7布丁优惠券下一个版本的开发中...
【总结】
通过这个应用的开发,大概主要技术和要点,在上面说了,大家如果想需要WP7开发,可以先从这几方面入手,通过这几方便的学习,
相信大家就可以开发更好的应用。加油。自己也加油。对于wp7的开发,个人也觉得,微软做了一件好事,只要有ASP.Net或windowfrom或
C#或WPF或silverlight的开发经验来学习这个,真的不难。

