mercredi 11 avril 2012

沟通篇:男女有别,“别”在何处?


他和她,一个来自火星一个来自金星,彼此以不同的方式交流和表达爱意。深爱对方的他们却因沟通失败而转相怨恨,可见理解对方相异的交流方式何等重要,不要因为误解而失去人间最宝贵的东西——爱。
男人和女人在诸多方面都有着千差万别。他们以全然不同的视角看世界。要想理解二者的差异,关键在于了解男女沟通方式上的不同之处。
以下是男女在沟通中的6条重要区别,你应该对此有所了解以助提高同爱人的沟通能力,使交流更为顺畅和有效。
1. 为什么要说?
他认为沟通应该有明确目的。每次谈话的背后都存在一个需要解决的问题或需要阐明的观点。沟通是为了尽可能有效地找出困境的根源。
她利用沟通来明了自己的感受以及自己想诉说的内容。她视交谈为一种分享内心感受的行为和一次增进两人亲密感的机会。通过分享,她的消极情绪可以得到释放,同时两性关系也因此得以巩固。
2. 该说多少?
他将生产力和效率放在每日生活的首位,在交谈中也不例外。他对事情进行描述时已经从满脑的混沌中理清了思路,只分享他认为说明中必不可少的细节。他会奇怪“为什么女人有那么多话要说?”因此,在他听得差不多觉得足以提供解决办法时就会打断对方。
她利用交流来探查和整理思绪——便于发觉细节。她不知道哪些信息是必要的哪些是多余的,只有说出来后才知道。但女人打开话匣子不一定是在寻求解决办法,她只是想找个人倾听并理解自己的感受。
3.倾听的意义何在?
他习惯于有效地倾听。当女人发起谈话时他认为她是在征求意见或寻求帮助。他尽力去理解女人,将她说的每句话都过滤成为“眼下我该怎么办?”对他来说,要学会既耐心又不被动地倾听女人说话真不是件容易的事儿。
她认为谈话本身就是件非常重要的事。如果她感到自己被充分倾听或理解,就算问题得不到解决或改善对她而言也无所谓。爱人的倾听有助她缓解内心的焦虑和负面情绪带来的痛苦。与懂她爱她的人交心可以抚平她心灵的伤痕,是她借以摆脱外界麻烦和苦恼所必需的情感工具。
4. 当她心烦时……
他会希望帮助她解决所面临的问题,仿佛一名消防队员等不及想尽快将火扑灭。对他而言,最快的灭火方法就是提供解决办法。由于过于希望向爱人献策,他可能会非常在意对方的情绪并竭力为自己辩解。他或许只听到字面意思,而没有意识到他的爱人在心烦时只是将语言作为表达复杂情感的工具。
她在心烦时会借助语言这个工具来表达自己的复杂情感,从而处理好自己的负面情绪并使其得以发泄。她看重的是对方的支持和抚慰,从分享内心感受中得到极大的满足。当他伴以关切的提问或表达诚挚的关怀来表现出对她的在意时,会让她感受到自己被爱被关心着。他满足了她的第一需要——爱的需要。
5. 当他心烦时……
他心烦或感到有压力时经常会退缩到自己的“洞穴”中(变得安静和孤僻)。男人的“洞穴期”就好比休了一个短假:他通过忘却问题和关注其他事物(如看电视、看报纸或打电游)来缓解压力。
他可能会在压力期内避免同伴侣交流。如果她不断询问或指责,他则会退避得更远,因为他怕爱人不相信自己有能力处理好问题。然而,如果女人能多些支持和理解,男人则会走出洞穴,更愿意交谈、更关爱对方。
她可能会将爱人的沉默解读为她令他感到失望或者她正在失去他。她本能地想通过一大堆表示关心的问题来帮助他脱离困境,抑或会出于担心自己理想中的交流方式不受重视而存有戒心,她想要的是那种有益的开诚布公的交流。
最终,在理解他需要时间独处后她才对他起到更大的帮助,因为他由此看出她相信他自己可以解决问题。信任是她给予他最好的礼物。同时,她也该做些事来照料一下自己,以便不会在他“洞穴期”过后对他心存怨恨。
6. 何时会导致沟通失败
他觉得爱人总对自己指手画脚。对男人来说最重要的是表现出色。当他的能力受到质疑时不仅会感到受伤还会马上竖起一堵防御墙,此时沟通则开始陷入瘫痪。他在自己独当一面的环境中才会茁壮。与告知“你应该怎样”相比,他更愿意听从“你觉得……怎么样?”要想改变他,诀窍在于别告诉他该怎么做。
她的爱人会认为她所说的那些问题实际上都不存在也没那么紧急。他误认为自己说些类似“你是在小题大做”或“你想太多了”的话是在帮助她“检验问题的真实性”。但在她看来,他像是在贬低她的感受或劝她不要有这种感觉。
男人和女人都渴望能让自己的爱人满意,但由于难以理解和接受对方相异的沟通方式而未能如愿。男人和女人需要通过学习这些差异来帮助维持两性关系,才不至于有朝一日彼此怨恨或因沟通不利而犯愁。
当夫妻相处遇到障碍时,我建议他们一起阅读或听从两性关系自助书籍上的指导。如果仍然感到不知所措,那么就应该向专业心理咨询顾问寻求帮助,让自己重新进入正轨,增进彼此间的理解和沟通。

mardi 3 avril 2012

学习之响应式Web设计:Media Queries和Viewports

题外话


本来是想写篇关于Bootstrap的Scaffolding博文的,不过对于响应式Web设计不是很了解,所以就先有了这篇博文。
博客园原文地址:http://www.cnblogs.com/yhuang/archive/2012/03/29/responsive_web_design.html

目录


前言
什么是响应式Web设计(Responsive Web design)?
响应式Web设计的用途
Media queries & Viewport
参考文章



前言


现在使用移动设备人越来越多,移动版的Website随之也越来越重要;但是移动端设备的大小不一,屏幕分辨率各不相同,我们不可能为BlackBerry,iPhone, iPad等等每个都做单独的页面设计。所以我们需要的Website设计要能迎合多种device的要求并且兼容所有的屏幕分辨率,这种设计就叫响应式Web设计


什么是响应式Web设计(Responsive Web design)?


响应式Web设计是一种Web设计和开发能够根据屏幕大小、平台和方向对用户的行为和环境做出响应的设计。它包括了灵活的网格和布局,图像和智能使用CSS的media queries特性。当用户从Laptop切换到iPad上时,该网站应能自动地切换CSS规则以适应Device分辨率,图像尺寸和脚本执行。换句话说,响应式Web设计是具备能够自动响应用户喜好的技术。这将一劳永逸的满足对每个新上市的移动终端都要进行不同设计和开发的需求。
需要注意的是:响应式Web设计不仅仅是可调屏幕分辨率和可自动调整大小的图像,而且更是一种采用全新思考的Website设计方法。


响应式Web设计的用途


随着越来越多的设备而来的是各式各样的屏幕分辨率、定义和方向。每天都有正在开发的新屏幕尺寸的设备。每个设备都可以拥有不同的尺寸、功能、甚至颜色。有些是横向,有些是纵向,甚至还有些完全是正方形的。如图所示:
Portrait Landscape


下图列出了一些最常见的: 
Sizes
 所以,如果你要使你的客户满意,你必须让你的网站能够自动调整并适合上述设备的屏幕。例如,你应该为不同的设备定制不同的布局结构:
Moving Content 定制布局

同样,有些时候,根据需要显示或隐藏一些内容: 
Digg Mobile显示或隐藏内容 
很明显,我们不能为每一种设备定制一个页面。所以,我们应该如何处理这种情况呢?

Media queries & Viewport

解决问题的关键就是Media queries和Viewports。下面的内容是关于如何最好的使用Media queries和Viewports的。但是我并不会深入的讲怎样增强响应的细节,如果感兴趣,可以查看这篇博文的最后一部分:“参考文章”。
  • @media queries
作为一个网页设计师或前端开发者,我们希望我们的网页总能很容易的自适应不同的设备和屏幕尺寸,不管我们用户使用的是21"台式显示器,13"笔记本电脑,10"的iPad或者更小的智能手机。响应式网页设计使用@media queries根据浏览器的宽度和CSS来改变页面的布局。我们可以这样使用的CSS:

/* Default wide-screen styles */
@media all and (max-width: 1024px) {
    /* styles for narrow desktop browsers and iPad landscape */
}
@media all and (max-width: 768px) {
    /* styles for narrower desktop browsers and iPad portrait */
}
@media all and (max-width: 480px) {
    /* styles for iPhone/Android landscape (and really narrow browser windows) */
}
@media all and (max-width: 320px) {
    /* styles for iPhone/Android portrait */
}
@media all and (max-width: 240px) {
    /* styles for smaller devices */
}

是的,我们可以设置更小的width,或者中间尺寸。我待会再讨论。CSS media queries是非常强大和复杂的,在这里不想过多的讨论,因为上面的代码已经足够应付响应式Web设计了。如果你想要知道更多关于Media Queries的细节,请阅读参考文献里的相关文章。
  • viewports
现在,当我们调整我们浏览器的大小时,上面的代码已经可以非常不错的完成工作了。但这并不能满足移动端的浏览器。原因是移动端浏览器(iPhone/Safari, Android/Chrome和Fennec)会默认页面是为宽屏幕设计的,所以将它缩小整个页面来适应小屏幕。这就表明了上面的代码完全不足于适应移动端的浏览器,因为设备无法识别正确的宽度。解决方法:在文档的头部使用苹果提供的viewport meta标签,并结合@media queries:

<meta name="viewport" content="...">

以上的代码中,content为空,因为我觉得有必要详细讲一下,而不是直接给出来,这样我们就能更清楚的记住,并且知道content里应该填些什么,并且为什么这样填。

    • width=device-width
我们看见很多网站都建议把content属性的值设置为width=device-width。这相当于告诉浏览器将页面宽度假设为设备宽度。不幸的是,只有当设备是纵向时假设才是正确的。当我们把设备旋转成横向时,device-width还是和纵向的一样(比如,320px),这意味着,即使我们把页面设计成适应了480px横向设备,它还是会返回320px的效果。

曾经尝试在media query里使用orientation来解决这个问题,但是orientation不会真正的告诉我们实际的设备宽度,因为它只告诉我们设备的宽度是大于还是小于设备的高度。正如有人指出,由于大部分网页往往垂直滚动,所以这是无关紧要的。

如果我们的页面在纵向和横向设备中样式相同,那么我们就可以用width=device-width就足够了,需要注意的是这个是唯一告诉android设备使用设备宽度的方法。

    • initial-scale=1.0,maximum-scale=1.0
initial-scale=1设置告诉浏览器初始化页面时不要对页面进行缩放。解决了没有使用viewport时出现的页面缩放问题。但还是有bug,当我们把移动端设备从纵向转成横向时,你就会发现这个问题了。这是因为initial-scale只在页面完全加载后有作用。在我们把移动设备从纵向转成横向的过程中,浏览器就会认为页面不变,但scales会设置为1.5,为了使320px的页面适应480px。但是,因为我们在@media queries中设置了480px这个宽度,那么页面CSS规则也会是适应480px的。结果就是,页面CSS规则是适应480px的,另外scale还是1.5。这结果并不可怕,但是不可取。
为解决这个bug,我们可以添加maximum-scale=1这个设置。它的作用是阻止页面在旋转时放大,但它同时带来了更严重的问题:也阻止了用户手动放大或缩小页面。同样user-scalable=no设置也会让用户不能缩放页面。所以一般情况下,不要使用以上俩个设置。

是不是无法解决这个bug了?首先这个bug最多只是在显示层面,带来的后果一点不严重,因为即使页面自动缩放了,它还是成比例的。

    • width=<actual width>
有些人建议在viewport里使用特定的width,并且也按这个width设计页面。如果你可以为每个种类的device编写页面的话,这个设置是可行的,但需要我们明白的是它不是响应式设计。打印时,使用固定宽度布局是必要的,但我们网页应该适应用户的各种样式的设备。总之,不要这样使用。

    • @media all and (device-width:480)
这是个media query而不是viewport标签里的选项, 我在很多地方看到过这样的代码,但我并不认为这是好的做法。为什么?根据CSS3对media queries的描述,device-width在media queries里表示的是输出设备表面渲染的宽度。对于continuous media来说,device-width就是屏幕的宽度;对于paged media来说,device-width就是页面尺寸的宽度。以continuous media为例,device-width就是设备屏幕的宽度。除非浏览器最大化,它始终大于viewport的width。

测试表明,大多数桌面浏览器把device-width和width当作同义词。而移动端浏览器对此会有点混淆。至于viewport标签里,device-width只在纵向时等于设备的width。例如,一个320*480的设备,device-width总是320px,不论方向。然而对于CSS media queries,device-width是基于其目前方向上屏幕的width。

如果你一定要这样使用,请和orientation一起使用。但绝对不要使用max-device-width和min-device-width,因为用max-width和min-width替换会比较好。同样需要注意的是,新型号设备的宽度可能会改变。 

    • 中间尺寸 
上面我提到过,我们可以为任意数量的width来设计页面。最重要的是在不同width的浏览器中测试他们,通过调整window浏览器的大小来测试是最简单的方法。随着设计页面的width越来越小,我们可以去掉(Display:none;)一些不重要的内容,比如footer,sidebars,menu等,为主要内容留足够大的空间。我们的网站也许需要一个可以在所有width屏幕上运行良好的布局,也许只需要满足两到三个布局。这是非常容易设计和测试的,所以没有理由不做。

    • 推荐设置
最后,是我推荐的做法:
  1. 使用viewport标签
  2. 使用media queries来选择最适合页面尺寸的CSS
  3. 在viewport标签里,使用width=device-width,initial-scale=1或者单独使用width=device-width
  4. 不要使用maximum-scale=1和user-scalable=no
  5. 不要使用width=<specific width>
  6. 不要使用@media all and (*-device-width: xxx)

参考文章 
  1. Ethan Marcotte. 2010. Responsive Web Design. In A List Apart #306. ISSN: 1534-0295.
  2. Jeremy Keith. 2010. Responsive Enhancement. In adactio.
  3. Kayla Knight. 2011. Responsive Web Design: What It Is and How To Use It. In Smashing Magazine.
  4. Webkit based desktop browsers re-render the page correctly as you resize the browser, however they have a minimum width of 385px (on MacOSX) and I was unable to shrink the browser below this. Firefox 4 re-renders the page correctly until the width gets too narrow to fit the navigation toolbar. At that point the viewport width stays fixed even if you shrink the browser. The page is re-rendered if you type something (anything) into the URL bar. Opera 10/11 re-render correctly at all sizes.
  5. Peter Paul Koch. 2010. A tale of two viewports — part two. In Quirksmode.
  6. Using the Viewport on Safari. In Safari Web Content Guide.
  7. The viewport meta tag. In Safari HTML Reference.
  8. MDC. 2010. Using the viewport meta tag to control layout on mobile browsers. In Mozilla Developer Network.
  9. Peter Paul Koch. 2010. Combining meta viewport and media queries. In Quirksmode.
  10. Willison & Downe. Lanyrd.
  11. Lie et al. 2010. Media QueriesW3C Candidate Recommendation 27 July 2010.
  12. If you design your page for the narrow view and expect it to scale when rotated, then use width=device-width and nothing else. If, instead, you design your page for either width, then use width=device-width,initial-scale=1. This is the only way to get the android browser to render a page with the intended width. Mobile Safari will render the page exactly as if initial-scale=1 were specified alone. You will still end up with the zoom on rotate bug.
  13. David Calhoun. 2010. The viewport metatag (Mobile web part I).

lundi 2 avril 2012

分享之$(document).ready() jQuery

本文将讲述$(document).ready()和其重要性。
作者: Karl Swedberg
学习jQuery的第一件事是:如果你想要一个事件运行在你的页面上,你必须在$(document).ready()里调用这个事件。所有包括在$(document).ready()里面的元素或事件都将会在DOM完成加载之后立即加载,并且在页面内容加载之前。
If you want an event to work on your page, you should call it inside the $(document).ready() function. Everything inside it will load as soon as theDOM is loaded and before the page contents are loaded.
 $(document).ready(function() {
   // put all your jQuery goodness in here.
 });


有很多方法可以确保事件在页面上正常工作,$(document).ready()比其它方法要更有优势。首先,你不必在HTML上放置任何“ 行为性的”标记。另外,你可以将JavaScript/jQuery写入一个独立的js文件,这样既容易维护,又保证了js与页面内容的隔离。如果你在浏览网页时更加细心,你就会常常看见这种情况:当你将鼠标悬停在一个连接时,有时状态栏中会在显示“javascript:void()”这样的消息。这就是你把一个事件直接放在<a href>标签里所造成的。

在一些使用traditional JavaScript的页面,你会在<body>标签中看见“onload”属性。这会导致一个问题:它限定了在body上只能有一个函数事件。是的,因为它又往内容中添加的“行为性的”标签。如果你想解决这个问题,请参考Jeremy Keith的书:DOM Scripting,里面讲述了如何在一个单独的js文件里创建一个 “addLoadEvent” 函数,它允许多个函数可以在body里被加载。但是这种方法需要为本来很简单的问题编写相当数量的代码,另外,这种方法是在加载window时触发这些事件的,这不得不让我再一次想起 $(document).ready()的好处。

使用 $(document).ready(),你能让你的事件在window加载之前加载或触发。所有你写在这个方法里面的都准备在最早的时刻加载或触发。也就是说,一旦DOM在浏览器中注册后,$(document).ready()里的代码就开始执行。这样用户在第一眼看见页面元素时,特效就可以运行了。

mercredi 28 mars 2012

学习之模块架构 DotNetNuke 6

前面的博客介绍了:

博客园原文地址 :  学习之模块架构 DotNetNuke 6

这篇博客主要是讲模块的架构和加载,以及模块与DotNetNuke门户网站(Portals)系统的关系。充分了解模块可以帮助DNN开发者根据需要更清晰的构建模块。
在DNN中,模块是一个可插入的用户接口组件,用来处理请求并生成动态的内容。它只能出现在ASP.NET页面上,而页面可以包括任意数量的模块"实例"。
整个模块架构包括四个部分 : 门户网站(Portal),页面(Tab),模块(Module)和模块容器(Container)。

  • 门户网站(Portal)
Portal可以定义为一个基于Web的应用程序,从不同的源聚集内容,并寄宿信息系统的表示层(模块)。下图描述了Portal的基本架构,DNN处理页面请求时需要执行许多步骤。下述步骤在页面初始化的过程中执行,用来在运行时动态地加载模块。动态创建的模块然后就能够处理它们自己的生存周期,包括诸如初始化、加载、呈现等各种事件。

图一 : Portal的基本架构

第一步: 页面配置检索(Page Configuration Retrieval)

第1步就是为被请求的页面检索模块。检索步骤由许多重要的信息块构成,例如页面上出现的各个模块、模块在页面上显示的区域(即内容窗格Pane),以及与每个模块相关联的安全角色。
//Default.aspx.cs//OnInit//load skin control and register UI js
UI.Skins.Skin ctlSkin = UI.Skins.Skin.GetSkin(this);
//add skin to page
SkinPlaceHolder.Controls.Add(ctlSkin);

//DotNetNuke.UI.Skins.Skin.cs : OnInit()
bool success;
//Load the Module Control(s)
success = Globals.IsAdminControl() ? ProcessSlaveModule() : ProcessMasterModules();

//DotNetNuke.UI.Skins.Skin.cs : ProcessMasterModules()
bool success = true;
if (TabPermissionController.CanViewPage())
{
  //check portal expiry date
  if (!CheckExpired())
  {
    if ((PortalSettings.ActiveTab.StartDate < DateAndTime.Now && PortalSettings.ActiveTab.EndDate > DateAndTime.Now) || TabPermissionController.CanAdminPage() || Globals.IsLayoutMode())
    {
      //dynamically populate the panes with modules
      if (PortalSettings.ActiveTab.Modules.Count > 0)
      {
        foreach (ModuleInfo objModule in PortalSettings.ActiveTab.Modules)
        {
          success = ProcessModule(objModule);
        }
      }

第二步: 安全审计(Security Audit)

第2步就是判断在上一步中检索出来的安全信息。通过检查当前用户的角色(是注册用户还是匿名用户)以及与每个模块相关联的查看角色,就可以为当前页面形成一列经过“授权”的模块。
private Boolean ProcessModule(ModuleInfo module)
{
  bool success = true;
  if (ModulePermissionController.CanViewModule(module) && module.IsDeleted == false &&
((module.StartDate < DateTime.Now && module.EndDate > DateTime.Now) || Globals.IsLayoutMode() || Globals.IsEditMode()))
  {
  //...
  }
  //...
}

第三步:内容注入(Content Injection)

第3步(也是最后一步)就是将“授权”模块动态地插入到页面上相应的内容窗格中。在所有的模块加载之后,每个模块就能够执行各自的事件并呈现内容。
Pane pane = GetPane(module);
if (pane != null)
{
  success = InjectModule(pane, module);
}

  • 页面(Tab)
图二描述了基本的Portal的Tab组件。页面本身表示一个完整的标记文档,由大量的内容窗格(Pane)组成,并且在每个内容窗格中还包含大量的模块。

图二:页面组件

每个模块都包含一个标题、若干装饰(Container)以及由模块生成的内容。装饰可以包括若干按钮、链接以及一个改变模块状态或者执行与模块相关功能的悬浮菜单。

  • 模块(Module)
正如前面所述,Portal是基于Web的应用程序,处理各种请求并生成动态内容。每个模块都生成自己的标记块(称为段),再和皮肤的标记一起展示完整的文档。
因为每个模块都生成自己的标记,所以可以将模块视为较大应用程序中的微型应用程序。通常,用户通过单击链接或者提交表单(所提交的表单由门户网站系统及传递给相应模块的命令处理)与每个模块生成的内容交互。

  • 模块容器(Container)
模块周围的装饰称为模块容器。通过容器,用户能够与模块交互,执行诸如最小化、最大化以及其他高级特性(如果用户拥有该页面的编辑特权)的动作。
图三示范了一个HTML模块在用户以编辑特权登录时的模块容器。这个模块容器包含许多项,例如拥有一列管理选项的悬浮菜单、模块的标题。

图三:模块容器

DotNetNuke est le plus gros projet open-source en technologie .Net.

lundi 26 mars 2012

分享之数据库实体关系图 DotNetNuke 5.6.1

下图所示是DotNetNuke 5.6.1社区版的ERD(Entity-Relationship Diagram). 找了好久只找到这个版本的,估计不是官方的,是一个叫R2i的公司在维护的。但我觉得这个已经足够了,因为5.6.x是DNN 6之前的最后一个版本,所以我觉得DNN 6和它之间内核库不会有Break changes,当然少许的改动或优化总是有的。
为什么要分享这个图呢? 因为我觉得ERD对我们理解全部DNN的架构非常重要,并且能够帮助我们减少开发第三方模块的时间。
在下图中,表框的不同颜色表示了他们类别的不同 :
  • 蓝色 : 认证系统,比如用户认证,页面模块查看权限...
  • 红色 : 内容管理,比如皮肤,页面...
  • 绿色 : 辅助系统,比如异常记录,任务...
  • 黑色 : 配置系统,比如模块定义,语言包...
  • 灰色 : 第三方核心插件,比如Text/HTML模块,搜索模块...




这个图是有点小了,估计大家看不清楚。所以提供PDF下载外加MetaData下载 : ERD PDF || METADATA XLS
希望想学习或想了解DNN的园友看完之后,对DNN的Architecture有更全面的了解。5 jours avant 01 Avril !

vendredi 23 mars 2012

如何创建新模块 DotNetNuke 6 & Entity Framework Code First

Soleil 索引 :
第一部分:开发新模块 - DNN6

Étoile 如果你还不知道DotNetNuke是什么的话,请访问 www.dotnetnuke.com
Étoile 如果你还不知道如何在本地安装DNN 6, 请猛戳 http://codeciel.blogspot.fr/2012/03/how-to-install-dotnetnuke-6-on-local.html
Arc-en-ciel 如果你想要知道如何开发一个你自己的DNN6模块,那么这篇文章就是你需要的。


  • 步骤1:通过DNN6 Host创建desktop module和module definition
从数据库的角度出发,这一步会把新模块的相关信息存入数据库,让DNN可以识别,这样你就可以把这个模块加入任意页面。如果你仔细看了DNN数据库的结构后,你会发现有很多和模块(Module)相关的表 - dbo.DesktopModules, dbo.Modules, dbo.ModuleDefinitions, dbo.TabModules, dbo.PortalDesktopModules. 这儿不会细讲这些表的用途,如果可以的话,有机会我会专门为此写一篇博客。
所以,现在你只要按照下面的步骤就可以创建一个属于你的DNN模块 :
  - 用host账户登入你的DNN6网站
  - 点击Host页面,再进入Extensions页面
  - 单击Manage按钮, 选择Create New Module,你将会看到(如Figure 1所示):
提示: 如果你找不到Manage按钮,请确认你的页面模式(通常在右上角可以看见)是Edit而不是View或Layout。
1
Figure 1 : 创建模块
  - 填好这些文本框后。(最后把"Add Test Page ?"打上勾,以简化步骤)
  - 单击“Create Module”, 然后你就可以看见新的模块已经出现在了“TestEFCodeFirstPage”页面上(如Figure 2 所示)了
2
Figure 2 : 新页面里包含了新模块

  • 步骤2:把新模块与VS项目联系起来
  - 现在打开VS2010,创建新项目,如Figure 3 所示。
提示:值得注意的事,项目的名字必须与module的名字相同,即“EFCodeFirst”,开始的时候你可以把这个项目保存到任何位置,因为一旦完成创建之后,我们会把这个文件夹移动到<网站根目录>/DesktopModules里,合并这俩个文件夹。
3
Figure 3 : 创建新vs2010项目
  - 再次用VS2010打开“EFCodeFirst.csproj”. 因为我们还需要配置这个项目,来让它可以顺利的在DNN6下编译和运行。
  - 点击项目属性,然后进入到Compiler页面,修改输出路径为<网站根目录>\bin\
  - 如果你Site的运行环境是.net framework version 2.0.50727.5448的话,请在IIS里修改Site的application pool为ASP.NET V4.0。因为EF Code First是需要.net 4.0的。
  - 添加Dotnetnuke.dll引用,它可以在<网站根目录>\bin\下找到。需要注意的是在属性里将copy local设置为false。
提示:上面这种添加Dotnetnuke.dll引用的方法不是企业开发常用的,或者说是应该避免的才比较正确。因为Dotnetnuke不断在更新,为了保证团队开发时,Dotnetnuke.dll引用都是同一个版本,我们通常会固定一个稳定的版本来开发。具体操作是:存放指定版本Dotnetnuke.dll的文件夹是共享的,所有的dnn引用都是指向这个文件夹的。
  - 重命名web.config文件为web.config.old,因为在网站的根目录已经有了一个web.config。
  - 删除EFView.ascx文件
  - 添加一个新文件,文件类型是Web User Control,还是命名为“EFView.ascx ”,以便DNN能识别。
  - 打开文件EFView.ascx, 我们随便写点东西进去,比如 : “This is my first dnn6 module”
  - 打开文件EFView.ascx.cs,将它的基类换成PortalModuleBase,保存并编译这个项目
  - 刷新页面http://dnn614/TestEFCodeFirstPage.aspx,你将会看到如Figure 4所示 :
4
Figure 4 : 测试页面和行模块

好了,看到这里你就应该知道如何创建一个DNN6模块了。在这个模块里,你可以根据需求,随意开发。下面的第二部分就是,在一个DNN模块里使用Entity Framework Code First进行实战开发。




第二部分 : DNN模块里使用EF Code First

Pouce levé 这部分是基于这篇文章: Introduction to Entity Framework Code First


  • 步骤 3* : Entity Framework 简介
在我直接进入正题之前,我想先介绍一下EF。EF是一个.NET平台下的ORM框架,个人觉得做的很不错,之前还接触过另外俩款在.NET平台下的ORM - LLBLGen Pro和NHibernate。LLBLGen Pro自从收费之后,功能也越来越强大,用起来也挺顺手的,之所以没有继续用的原因很简单:我现在这个客户公司,不想花钱买它的licence。NHibernate是从Java版的Hibernate移植过来的,就好比Lucene.NET与Lucene的关系一样,不同的是Lucene.NET早早就进Apache的孵化器了,而NHibernate的生命力还很强大。我没有用NHibernate的原因也很简单,被它的配置文件惊吓住了。比较这三款的优缺点不是这里的重点,每个人的习惯和思考方式不一样,所以喜好自然不同,选一款自己用的顺手又顺心的ORM就可以了。下面开始介绍EF的三种使用方法 :
EF在4.0的时候只提供俩种与实体交互的方法,即数据库优先模型优先。前一种情况下,你在开发你的应用前开始设计数据库。然后你可以用VS自带的实体数据模型设计工具来设计一个虚拟模型,操作很简单:直接从数据库把表拖拽到设计器中(如Figure 5所示)。后一种情况下,我们先用实体数据模型设计工具来手工设计模型(如Figure 6所示),然后指定数据库的连接。但不管是前面哪种情况,你都要使用实体数据模型设计器来生成你的数据模型对象。

2-1
Figure 5 : 数据库优先

2-2
Figure 6 : 模型优先

EF在4.1中引入一种新的模式来生成模型对象 : 代码优先(Code First)。在代码优先这种方式里,你不会遇到任何设计器。先全部用.NET Framework来搭建对象类,然后用一些特定属性和相关技术来生成EF数据模型。(如Figure 7一样)

2-3
Figure 7 : 代码优先

上面大致讲了一下这三种方法,如果你感觉有点混淆,静下心来,仔细看Figure 8,我相信你肯定会清楚不少。在我们这个模块中,我们使用的是最后一种。


2-4
Figure 8 : EF与实体交互的方式

第二部分的目的不仅仅是完成我们的模块,而是一步一步的,让大家弄清楚使用EF Code First的全部过程。接下来,在开发模块,你将处理一个用来表示博客帖子的数据模型,实际上,数据是驻留在SQL Server里一个名叫BlogPosts表中的。通过使用模型对象,你能够从表中读写数据。


  • 步骤 3 :  创建模型
假设我们的数据模型是用来储存博客帖子和博客贴分类的。根据三层架构的思想,我们应该另外创建一个project来专门开发数据模型 :在与“EFCodeFirst”项目同一个solution里创建Library类型的新项目,命名为“EFCodeFirstLib”。这个类库项目里将会包括俩个模型对象,即BlogPost和Category。他们的模型代码如下展示 :
提示: EFCodeFirstLib项目的文件夹应该放在<网站根文件夹>/DesktopModules/, 并且其编译路径应该是<网站根文件夹>/bin/.
public class BlogPost
{
  public Guid Id { get; set; }
  public string Title { get; set; }
  public string Content { get; set; }
  public DateTime PublishDate { get; set; }
  public virtual Category Category { get; set; }
}
  
public class Category
{
  public Guid Id { get; set; }
  public string Name { get; set; }
  public virtual ICollection<BlogPost> BlogPosts { get; set; }
}

在BlogPost类中,定义了五个属性: Id, Title, Content, PublishDate 和 Category。注意里面的Category是外键。Category类有三个属性:Id, Name 和 BlogPosts。其中的BlogPosts 是一个属性集合,因为可以有很多个博客帖子属于同一个category。需要记住的是,默认情况下,表示主键的属性类型应该是int或Guid。当然,你可以用一些attributes定制主键。我们将在后面讨论。


  • 步骤4 : 创建Data Context
现在模型对象都已经创建好了,让我们开始创建一个Data Context,通过它可以让你查询和处理实体数据,另外,它的基类是DbContext类。下面就是我们在模块中要使用的Data Context:
public partial class BlogContext : DbContext
{
  public BlogContext():base("BlogDb")
  {
  }

  public DbSet<BlogPost> BlogPosts { get; set; }
  public DbSet<Category> Categories { get; set; }
}
BlogContext类继承了DbContext类。在它的构造函数中,调用了基类的构造器并且传递了所需的数据库名称。当你首次运行应用时,将会为你用这个名字创建数据库。如果不传递这个名称,默认创建数据库的名字将会是DbContext类的fully qualified name。在我们这个例子中就是“EFCodeFirstLib.BlogContext”。
BlogContext类定义了俩个公共属性,即BlogPosts和Categories。这些属性的类型是DbSet。DbSet类代表了一个实体集(从数据库角度,就是一个数据库的表),可以执行CRUD(创建,读取,更新,删除)操作


  • 步骤5 : 测试数据库的生成(可选)
我们现在可以测试我们已经写的代码了,当然你也可以选择跳过这一步。
首先在EFCodeFirst项目的“EFView.ascx”中添加一个按钮(button),并且生成相应的按钮点击事件方法。加入我们刚刚创建的EFCodeFirstLib项目的引用。



Figure 9 : 测试页面


然后在按钮点击事件方法中写入以下代码:
using (var db = new BlogContext())
{
  Guid id = Guid.NewGuid();
  var cat = new Category { Id = id, Name = "ASP.NET" };
  var post = new BlogPost { Title="Title1", Content="Hello World!", PublishDate=new DateTime(2011,1,1), Category=cat};
  db.Categories.Add(cat);
  db.BlogPosts.Add(post);
  int i = db.SaveChanges();
}
上面的代码创建了一个BlogContext实例,接着创建了Category和BlogPost类的各自实例。把新创建的实体实例加入到DbSets里面,最后调用SaveChanges()方法。 SaveChanges()方法将持久化加入的实体数据,也就是把实体数据写入数据库相应的表中,并且返回此次操作执行的记录数。
现在我们编译这俩个项目,在浏览器中打开http://dnn614/TestEFCodeFirstPage.aspx. 点击页面中的按钮。 因为我们是第一次运行并点击按钮,EF将会在SQL Express里创建新的数据库和表。库名是BlogDb,因为我们在DataContext的构造里已经指出了;俩个表名分别是BlogPosts和Categories。


Sans titre1
Figure 11: SQLExpress里创建的数据库和表

值得注意的是BlogPosts和Categories表里的Id列被设定为主键, 同样BlogPosts表中,有一名叫Category_Id的外键被定义。

  • 步骤6 : 每次运行(点击按钮)时重新创建数据库
在测试时,你也许想要在每次运行应用时都创建或重新创建数据库,以便初始化运行环境。同时也想在创建数据库的时候加入一些测试数据。当你修改了你的实体代码时,因为数据库的表也会相应的改变, 数据库也应该重新创建。默认情况下,EF只会在你首次运行时创建数据库,之后应用会一直操作这同一个数据库。这当然不是我们想要的,因为我们的实体代码会随着需求的变更而修改或增加,为了改变这种默认行为,你需要在代码里创建一个database initializer,并且继承DropCreateDatabaseAlways类。如名字暗示的那样,DropCreateDatabaseAlways类总是会重新创建数据库。下面的代码就是我们自己的BlogContextInitializer :
提示:你也可以继承DropCreateDatabaseIfModelChanges类,它只会在Model改变的时候,重新创建数据库。
public class BlogContextInitializer : DropCreateDatabaseAlways<BlogContext>
{
  protected override void Seed(BlogContext context)
  {
    Category cat1 = new Category { Id=Guid.NewGuid(), Name=".NET Framework" };
    Category cat2 = new Category { Id = Guid.NewGuid(), Name = "SQL Server" };
    Category cat3 = new Category { Id = Guid.NewGuid(), Name = "jQuery" };
    context.Categories.Add(cat1);
    context.Categories.Add(cat2);
    context.Categories.Add(cat3);
    context.SaveChanges();
  }
}
BlogContextInitializer 继承了DropCreateDatabaseAlways 基类,并且重写了Seed()方法,方法里写入了一些往数据库里添加样本数据的代码:添加了三个categories。一旦完成了initializer,你需要添加以下代码到按钮点击事件方法里去:
protected void Button1_Click(object sender, EventArgs e)
{
  Database.SetInitializer<BlogContext>(new BlogContextInitializer());
   ...
   ...
}
现在你就会发现每次点击按钮时,数据库都会重新创建,并且填充样本数据到Categories表里。

  • 步骤7 : 在Code First里使用已有的数据库
上面的代码里,你允许EF自己创建数据。但在许多情况下,你会想要用已经存在的数据库而不是创建一个新的。又因为我们在DNN里使用EF Code First,所以在任何情况下都不可能全部重新创建DNN使用的数据库(DNN614)。我们不得不使用其它的数据库(比如DNN614_BlogDb)。如何让Code First知道我们想要使用那个数据库呢?只需要在web.config的Connection String里指明就可以了:
<connectionStrings>
  <add name="SiteSqlServer" connectionString="Data Source=(local)\LEGAL;Initial Catalog=DNN614;User ID=login614;Password=xxx" providerName="System.Data.SqlClient" />
  <add name="BlogDb" connectionString="Data Source=(local)\LEGAL;Initial Catalog=DNN614_BlogDb;User ID=lgfr;Password=xxx" providerName="System.Data.SqlClient"/>
</connectionStrings>

Sans titre
Figure 12 : 指明已有的数据库

需要注意的地方是,我们在<connectionStrings>里添加的数据库连接字符串的名字,必须和我们在DataContext构造器里指明的一样(“BlogDb”)。
如果你在DataContext没有指明,那么那个数据库连接字符串的名字就必须和DataContext类名一样(“BlogContext”)。
为了让大家更清楚Code First选择数据库连接字符串的流程,我们用这个例子再解释一遍 :
  •   情况一:在DataContext中(BlogContext)指明了名字“BlogDb”
      EF首先会在configuration文件里查找是否有名字为“BlogDb”的数据库连接字符串。
      如果有,EF就会使用这个。如果没有,EF就会在本地的SQL Express中创建一个名为“BlogDb”的新数据库。
  •   情况二:没有在DataContext中(BlogContext)指明
      EF首先也会在configuration文件里查找名为“BlogContext ”的数据库连接字符串。
      如果找到,EF就是使用;否则EF就会在SQL Express里创建一个名为“EFCodeFirstLib.BlogContext”的新数据库。其中EFCodeFirstLib是BlogContext的命名空间。

  • 步骤8 : 使用Data Annotations来标明数据表和键的信息
上面的例子中,当创建实体对象对应的数据库和表时,EF使用的是默认的约定。我们也可以用一些data annotations来定制EF的行为。例如下面代码所示,我们添加了一些data annotations属性来标明表名([Table]),主键([Key]) 和外键([ForeignKey])信息。
[Table("BlogPosts")]
public class BlogPost
{
  [Key]
  public Guid Id { get; set; }
  public string Title { get; set; }
  public string Content { get; set; }
  public DateTime PublishDate { get; set; }
  [ForeignKey("Category")]
  public Guid CategoryId { get; set; }
  public virtual Category Category { get; set; }
}

[Table("Categories")]
public class Category
{
  [Key]
  public Guid Id { get; set; }
  public string Name { get; set; }
  public virtual ICollection<BlogPost> BlogPosts { get; set; }
}

  • 总结 :
Entity Framework Code First允许我们直接在代码使用EF特性,比如未经任何实体数据模型设计。这种方式下,全部使用C#类来创建实体对象,需要定义一个继承了DbContext类的DataContext类,同时可以使用data annotation属性显式地标明相关信息。默认时,当应用第一次运行时,EF会在SQL Express里创建新的数据库;我们可以通过创建自己的数据库初始化策略(Database Initializer)来改变这种默认行为。也可以使用已经存在的数据库,只需在configuration文件里标明了数据库连接字符串。 
Code First相对与其他两种方式来说,有很大的灵活性。但我觉得在DNN中不太适合使用,因为我不是特别能接受一个DNN网站同时有俩个数据库,维护起立不方便。所以,在现实项目中,我经常使用的方式是Database First。
最后友情提供一下本例中的DNN模块安装包和源代码:安装包  源代码Profitez-le et je vous souhaite un très bon weekend !!

jeudi 22 mars 2012

分享几个Web开发必备小抄

摘选自: "20 Cheat Sheets for Web Development you must have".


点击可查看大图!


1) PHP
本人说不上讨厌PHP,却绝对的不喜欢,可能是上学的时候留下的阴影吧。最近正在用Wordpress建站,不得已得用PHP。



2) HTML Character Entities



3) CSS



4) Javascript



5) JQuery 1.3



6) Regular Expressions
不多说了,必须的必备。



7) Apache Mod Rewrite
Url重写,最近在WordPress里会用到它,既可以让url更易读,又可以提高SEO。



8) WordPress Mind Map
一个讲解了WordPress流程的思维导图,非常清晰。
      


9) SEO
做一个优秀的SEO必然要花一番功夫的,有的放矢能事半功倍。
     


小小总结:
以上九个小抄就是我摘录下来的,基本是现在都用的着的。不过有些东东可能正在过时,正在更新好像更准确一些。要是有人或自己能不断更新这些图就好了。Quelle Bonne Idée!



mercredi 21 mars 2012

Juice UI: 整合了jQuery UI的开源ASP.NET Web控件

前言 

这篇文章的原文来自Jon Galloway,想看原文的请猛戳链接。第一次接触这个Juice UI是看到同事分享的邮件,当时只是粗略看看,不过感觉把jQuery UI整合到ASP.NET Web控件里去确实是个不错的主意。今天偶然在网上看到了这篇文章,就动手把它翻译过来,和大家分享。以下是译文:

今天早上的MVP峰会上,Scott Hunter刚刚从appendTo网站新发布了一个开源项目,名字叫Juice UI。Juice UI是一个Web窗体控件的集合,并整合了jQuery UI。你可以通过添加JuiceUI NuGet package到你自己的应用中,立即就可以使用它。同时源码是提供下载的(go nuts with the source), 他的版权协议是基于MIT和GPL的。


Juice UI,能做什么?

jQuery UI 是一个构建在jQuery之上的UI库。它为平常的场景提供了非常多且强大的部件,例如日期选择期,对话框和选项卡等等;并且一些顶尖的js开发者(some of the sharpest Javascript developers in the field)参与开发提供了坚实的基础。虽然你总是可以利用一些使用了jQuery和jQuery UI的库,但是全新的Juice UI控件可以让这些工作更简单。
例如:
   1:  <asp:TextBox runat="server" ID="_Restrict" />
   2:  <Juice:Datepicker
   3:       runat="server"
   4:       TargetControlID="_Restrict"
   5:       MinDate="-20"
   6:       MaxDate="+1M +10D" /> 

执行这段代码,就可以得到:
2012-02-28 09h16_03


组件和行为

Juice UI 发布的时候就已经包括了14个部件或行为。如果你想查看全部控件列表的话,请点击http://juiceui.com/controls, 并且提供互动例子让我们更深入了解控件。
2012-02-28 09h17_29
下面也列出了所有控件,点击可直接转到相应文档:

实战

  • 添加JuiceUI NuGet package
打开VS2010,创建项目前请确保VS2010已安装了NuGet插件。创建一个ASP.NET 4 Web Forms项目。右击项目中的References文件夹,选择Manage NuGet Packages..., 然后找到 "juiceui",点击安装。

2012-02-28 08h10_11

  • JuiceUI 的名字空间
通过NuGet package添加JuiceUI时,NuGet会自动的把Juice UI的名字空间写入到你的web.config文件里, 比如:
   1:  <configuration> 
   2:      <system.web> 
   3:          <compilation debug="true" targetFramework="4.0" /> 
   4:          <pages> 
   5:              <controls> 
   6:                  <add assembly="JuiceUI" namespace="Juice" tagPrefix="juice" /> 
   7:              </controls> 
   8:          </pages> 
   9:      </system.web> 
  10:  </configuration> 

如果确实需要Juice UI名字空间的话,通常我会移除上面这段config,在页面中使用<@Import Namespace="JuiceUI" />指令来添加名字空间。


使用Juice UI控件
首先,你需要一个<asp:ScriptManager> - 把它添加到一个页面上或者你site的Master页面上。
   1:  <asp:ScriptManager id="_Script" runat="server" /> 

添加完成后,你就可以使用控件了。这些都是扩展控件,所以你需要使用TargetControlID属性来指出哪个Web窗体控件将扩展Juice UI的行为。这儿有一个精简了的例子,将DatePicker行为与一个TextBox挂钩:
   1:  <asp:TextBox runat="server" ID="DateSample" /> 
   2:  <Juice:Datepicker runat="server" TargetControlID="DateSample" /> 

另外我把一个可拖拽(Draggable )的行为指向一个Panel:
   1:  <asp:Panel runat="server" ID="DragBox" Style="border:1px solid; width:100px;"> 
   2:      Hi. You can drag me around. 
   3:  </asp:Panel> 
   4:  <Juice:Draggable runat="server" TargetControlID="DragBox" /> 

需要注意的是,这只是非常简单的例子并且没有CSS。在Juice UI源码里有很多更复杂的Juice UI样例。

运行这个页面,我们将会看见我们所期待的:一个拥有日期选择器的textbox和一个可以拖拽的panel。
2012-02-28 10h34_32
下面是这个页面的源代码:
   1:  <%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" 
   2:      CodeBehind="Default.aspx.cs" Inherits="Juice_Sample._Default" %> 
   3:  <asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent"> 
   4:  </asp:Content> 
   5:  <asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent"> 
   6:   
   7:      <asp:ScriptManager id="_Script" runat="server" /> 
   8:      
   9:      <asp:TextBox runat="server" ID="DateSample" /> 
  10:      <Juice:Datepicker runat="server" TargetControlID="DateSample" /> 
  11:      
  12:      <asp:Panel runat="server" ID="DragBox" Style="border:1px solid; width:100px;"> 
  13:          Hi. You can drag me around. 
  14:      </asp:Panel> 
  15:      <Juice:Draggable runat="server" TargetControlID="DragBox" /> 
  16:   
  17:  </asp:Content> 

如果你有兴趣的话,在浏览器中查看网页的源代码,你会发现web控件和JuiceUI行为已经整合了,使用的是HTML5的data-属性:
   1:  <input name="ctl00$MainContent$DateSample" type="text" 
   2:          id="MainContent_DateSample" 
   3:          data-ui-widget="datepicker" /> 
   4:  <div id="MainContent_DragBox" 
   5:          data-ui-widget="draggable" 
   6:          style="border:1px solid; width:100px;"> 
   7:      Hi. You can drag me around. 
   8:  </div> 


似乎很熟悉...

确实,Juice UI用起来非常像Ajax Control Toolkit,但它的核心是jQuery UI。从另一方面说,它是为所有jQuery UI里的部件(widgets)和效果(effects)创建了Web窗体扩展和脚本控制。


更多...

想要寻找更多关于Juice UI的资料?最好的地方是Juice UI site,里面有提供互动的例子和文档。
需要源代码的,可以从GitHub repository下载,里面包含了一个样例项目。
最后,特别推荐的是StackOverflow (using the juiceui tag)ASP.NET jQuery forum,经常逛逛可以获得不少帮助。


后记
个人觉得,Juice UI要被广泛接受还需要走很长的一段路,毕竟Ajax control Toolkit在不断更新,其长期发展方向也是jQuery,所以竞争力很强。不管怎样,Juice UI为我们提出了一个新的思路去整合现有的前端JS框架。Bonne idée, c'est parti!