关于一个Silverlight3的小项目总结

最近这段时间在为California的一家保险公司做外包项目,该公司的技术选型为MVC1.0+Silverlight3 +WCF+Multi DB(MS SqlServer2008+IBM DB2),从年初启动到现在半年过去了,项目基本上算是完了,正在最后测试准备上线之际。不过,我今天总结的并不是这个项目,而是在这个项目做到一半的时候,该公司想为他们的多个网站加个免费计算器的一个Silverlight 小工具。由我在Escrum上总用时差不多40个小时完成的,工具虽小,但五脏俱全,所以我还是做个小小的技术总结。

先说一下作用背景(按照意思的总结,并非翻译哈)吧:

1、可以很轻便的使用于不同的网站上,即无缝的挂接到现有的网站上去。

2、基于Silverlight3实现,因为Silverlight4好象只能在vs2010上做吧,貌似这样子。

3、需要尽快完成,一两周内就要提供上去。

4、尽可能的可以运行在不同浏览器上(这个听起来有点不对劲,多些一举的说法,不过,做到中间我还真遇到了复制至剪贴板不支持IE以外的情况。)

5、工具需要提供能够输入,复制剪贴板,打印,语音帮助,文字帮助功能。

6、一些业务规则和数据输出计算规则随后由BRD文档提供。

 

OK,就这些要求,看起来应该是蛮简单的。而事实上也确是如此,最终,我使用的是一个xaml文件,一个Model和一些辅助类来完成的这些。对,我选用的是MVVM模式来弄的。因为最初我还傻傻的以为他们最终会找个美工来实现界面漂亮点。从现在看来,对方公司好象对我做的页面也没有多大的意见,似乎也没请美工捉刀美化页面的意思,不过这些也不是重要的,重要的咱的技术总结:

 

1、要可以在不同的asp.net网站上运行,少不了要跟html页面打交道了,而html上的Dom控制的王者不言而喻的是Javascript了。Silverlight要和JavaScript交互使用,需要这样子来弄:

 

1-A,Silverlight调用Javascript方法:

直接在xaml.cs文件中使用 

  
    
HtmlPage.Window.Eval( " javascript:window.close(); " ); // 或者

HtmlPage.Window.Invoke(
" closePage " );

 上面closePage是在Javascript中定义的一个方法,不带参数的,如果带参数,也是可以的,实现做法一个样。另外上面的HtmlPage的完整类名叫System.Windows.Browser.HtmlPage。

 

1-B,Javascript调用Silverlight中的方法:

这个稍微要多点步骤了,首先是在xaml.cs的构造需要注册成scriptable对象 

  
    
HtmlPage.RegisterScriptableObject( " PrintPage " , this ); // 这里的PrintPage可以理解为ID,Javascript靠这个来识别到哪一个类中找方法,所以定义一个好的命名是会在写Javascript时有很大帮助的。

然后还在要被调用的方法或属性的前面申明[ScriptableMember],当然要是想把整个类都公开给Javascript,也可以在类的前面申明为[ScriptableType],建议不该公开的东西还是别公开吧。

最后,在Javascript就可以使用这个方式来访问了: 

基于前面的步骤用以下方式JS调SL方法
   
     
fucntion getPropertyFromSilverlight(){
var slCtrl = document.getElementById( " silverlightControl " ); // 获得Silverlight控件

var page = slCtrl.Content.PringPage; // 取得将要调用的方法的所在类。

alert(page.PrintText);
// 这里假设在xaml.cs类中定义了一个名为PrintText的public属性.
}

 

1-C,将一些Javascript方法或CSS写进C#中,这个花费过我一些时间,尤其是将CSS写入进去的时候,一定要写在div并且一定要在<style>前面加上一个<br/>才调成功,至于是否写成一行或者多行就没有关系了。下面这段代码是实现打印部分生成的文字的一段CSS代码 

C#嵌入CSS代码
   
     
public class PrintHelper
{
static string StyleId = Guid.NewGuid().ToString( " N " );
public static void PrintText( string text)
{
var body
= HtmlPage.Document.Body;
if (HtmlPage.Document.GetElementById(StyleId) == null )
{
var style
= HtmlPage.Document.CreateElement( " div " );
style.SetAttribute(
" id " , StyleId);
style.SetProperty(
" innerHtml " , @" <br />
<style type='text/css'>
#printHost
{
display: none;
}
@media print
{
#form1
{
display: none;
}
#printHost
{
display: block;
}
}
</style>
" );
body.AppendChild(style);
}

// var obj = HtmlPage.Document.CreateElement("object");
// obj.SetAttribute("id", "wb");
// obj.SetAttribute("name", "wb");
// obj.SetAttribute("height", "0");
// obj.SetAttribute("width", "0");
// obj.SetAttribute("classid", "CLSID:8856F961-340A-11D0-A96B-00C04FD705A2");

var innerHtml
= HtmlPage.Document.GetElementById( " printHost " );
if (innerHtml == null )
{
innerHtml
= HtmlPage.Document.CreateElement( " span " );
innerHtml.SetAttribute(
" id " , " printHost " );
innerHtml.SetAttribute(
" name " , " printHost " );
innerHtml.SetProperty(
" innerHTML " , text);
}

// body.AppendChild(obj);
body.AppendChild(innerHtml);

HtmlPage.Window.Eval(
" javascript:window.print(); " );
// HtmlPage.Window.Eval("javascript:var wb=document.getElementById('wb');wb.execwb(7, 1);"); // print view
// HtmlPage.Window.Eval("javascript:var wb=document.getElementById('wb');wb.execwb(6, 1);"); // print
// HtmlPage.Window.Eval("javascript:var wb=document.getElementById('wb');wb.execwb(8, 1);"); // print page setup
}
}

以上我们约定aspx页面的Form id是form1。

 

1-D,也是想用C#调用clipboradData来复制文本至剪贴板。当然这段代码是复制于网上,不过我最终没有真正的去跨非IE浏览器,因为对方说可以不做这个,直接给个不支持非IE的信息就可以了。先贴段代码在这里吧,以免哪一天打不开上面这段链接。 

ClipboardData
   
     
public class ClipboardHelper
{
const string HostNoClipboard = " The clipboard isn't available in the current host. " ;
const string ClipboardFailure = " The text couldn't be copied into the clipboard. " ;
const string BeforeFlashCopy = " The text will now attempt to be copied... " ;
const string NotSupportBrowser = " The clipboard isn't available in the current host. " ;
const string FlashMimeType = " application/x-shockwave-flash " ;

// HARD-CODED!
const string ClipboardFlashMovie = " ZeroClipboard.swf " ;

/// <summary>
/// Write to the clipboard (IE and/or Flash)
/// </summary>
public static void SetText( string text)
{
// document.window.clipboardData.setData(format, data);
var clipboardData = (ScriptObject)HtmlPage.Window.GetProperty( " clipboardData " );
if (clipboardData != null )
{
bool success = ( bool )clipboardData.Invoke( " setData " , " text " , text);
if ( ! success)
{
HtmlPage.Window.Alert(ClipboardFailure);
}
}
else
{
HtmlPage.Window.Alert(NotSupportBrowser);

// Append a Flash embed element with the data encoded
string safeText = HttpUtility.UrlEncode(text);
var elem
= HtmlPage.Document.CreateElement( " div " );
HtmlPage.Document.Body.AppendChild(elem);
elem.SetProperty(
" innerHTML " , " <embed src=\ "" +
ClipboardFlashMovie + " \ " " +
" FlashVars=\ " clipboard = " + safeText + " \ " width=\ " 0 \ " " +
" height=\ " 0 \ " type=\ "" + FlashMimeType + " \ " ></embed> " );
}
}
}

 

2、如果你不希望你的Xaml文件中的Style啊Resource之类的充斥其中的话。你可以新建一个文件夹专门用来存放这些内容的,然后在App.xaml中合并这些,作为StaticResource来使用。 

App.xaml中合并资源
   
     
< Application.Resources >

< ResourceDictionary >
< ResourceDictionary.MergedDictionaries >
< ResourceDictionary Source ="Themes/PopupWindow.xaml" />
< ResourceDictionary Source ="Themes/Converter.xaml" />
</ ResourceDictionary.MergedDictionaries >
</ ResourceDictionary >

</ Application.Resources >

很明显的,我是建了一个Themes的文件夹,里面加了两个ResourceDictionary,做了以上这些操作之后,如果在页面中,想使用Style也好,Converter也好,直接StaticResource Key就可以完成了。而不需要在页面中再申明了。这就是全局资源的好处,当然也不是定义全局资源就是好方法,页面资源就一无是处了,这个取舍还在于一个平衡度的问题。

 

3、讲到Converter了,我有一点深刻印象的是,要实现一个textBox的背景颜色的切换的问题,一般的直接用IValueConverter来实现的话,还不一定达到效果,我最后是在一位同事的指点下,利用一个集合来完成的。OK,先看我最终的IValueConverter吧: 

背景颜色切换Converter
   
     
public class StringToGrayColorConverter : DependencyObject,System.Windows.Data.IValueConverter
{

#region IValueConverter Members

public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// if (String.IsNullOrEmpty(value.ToString()))
// return Colors.White;
// if (value.ToString().ToLower() == "yes")
// return Colors.LightGray;
// else
// return Colors.White;

if ( null == value)
{
throw new System.ArgumentNullException( " value " );
}
BrushCollection brushes
= Brushes;
if (parameter != null )
brushes
= (BrushCollection)parameter;
return (value.ToString().ToLower() == " yes " ) ? brushes[ 0 ] : brushes[ 1 ];

}

public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}

#endregion


public BrushCollection Brushes
{
get { return (BrushCollection)GetValue(BrushesProperty); }
set { SetValue(BrushesProperty, value); }
}

public static readonly DependencyProperty BrushesProperty =
DependencyProperty.Register(
" Brushes " , typeof (BrushCollection), typeof (StringToGrayColorConverter), new PropertyMetadata( null ));

}

public class BrushCollection : List < Brush >
{

}

然后,我们在Converter.xaml中如是定义,才可以使用: 

颜色变换的Converter
   
     
< Color x:Key ="DisableBackgroundColor" > LightGray </ Color >
< Color x:Key ="EnableBackgroundColor" > White </ Color >
< Converters:StringToGrayColorConverter x:Key ="stringToGrayColorConverter" >
< Converters:StringToGrayColorConverter.Brushes >
< Converters:BrushCollection >
< SolidColorBrush Color =" {StaticResource EnableBackgroundColor} " />
< SolidColorBrush Color =" {StaticResource DisableBackgroundColor} " />
</ Converters:BrushCollection >
</ Converters:StringToGrayColorConverter.Brushes >
</ Converters:StringToGrayColorConverter >

 

4、Behavior和Action

在VS2008中,如果有装Blend话,会有建Behavior或Action的模板,但不知为什么,在VS2010里面反倒没有了。不过,用Expression Studio4的话就还是有这些选项的。对于Triggers、Actions和Behavior,我还是在网上做了一些功课的。摘抄地址在这里

TriggersActions Behaviors使得在Silverlight应用程序中进行交互操作变得更为容易,XAML即可完成诸多功能,可以减去复写后台代码的烦恼,需要借助Blend 3 SDK的System.Windows.Interactivity.dll和Microsoft.Expression.Interactions.dll程序集。    

 TriggersActions是因果关系模型,一个触发器可以调用一个或多个操作,而Behaviors则大致相当于两者的一个小综合体。他们的类关系图如下:

 

关于一个Silverlight3的小项目总结
所谓Trigger,就是监听某些条件的变化,比如事件触发,属性值改变等,进而触发一些动作的发生。这些Triggers可能是EventTriggerCollisionTrigger 等,当然更多的或许是创建自己的Trigger。自定义Trigger只需要从TriggerBase<DependencyObject>继承,并覆盖OnAttachedOnDetaching方法即可。


所谓Action,就是去执行某些操作。可以根据需要创建自己的Action,常见的需要创建Action的情况有:改变属性、调用方法、打开窗口、导航到某个页面、设置焦点等。自定义Action可从 TriggerAction<DependencyObject>TargetedTriggerAction<DependencyObject>继承,区别在于操作对象是关联对象还是特定的目标对象,实现时覆盖Invoke方法即可。

       

 TriggersActions理论是可以相互独立,任意组合的。当你在定义时发现有些逻辑上需要相互确定或者假定发生时,Behaviors需要登台了。Behaviors乍看起来像是Actions,但它是逻辑独立功能自备的独立单元,它无需触发器,定义Behavior时就已经确定。

       

创建自定义Behavior需要从Behavior<DependencyObject>继承,//自定义行为默认继承Behavior<DependencyObject>使用DependencyObject类型的行为是不能访问对象的鼠标事件的,如果要访问鼠标操作的事件,可以使用具体的UI组件类型或者直接使用UI元素基类UIElement。//

并覆盖OnAttachedOnDetaching方法,复杂行为时需要用到ICommand. 当然在Blend 3中已经预定义了不少Behaviors,如MouseDragElementBehavior等。可以利用,同时在Expression Gallery 也可以共享他人或自己的Behavior。(PSEffects Themes也可以在这里共享。)

我在这个项目中,只使用了一个Action和一个Behavior,都作用于textBox上面,分别用来获得焦点时就全选文本和控制只能输入数字的作用。

首先来看Action:  

SelectAll in textBox
   
     
public class TextboxAction : TargetedTriggerAction < TextBox >
{
public TextboxAction()
{
// Insert code required on object creation below this point.
}

protected override void Invoke( object o)
{
// Insert code that defines what the Action will do when triggered/invoked.
TextBox tbx = Target;
tbx.SelectAll();
}
}

然后看一下只允许输入数字的一个Behavior。 

只允许输入数字的Behavior
   
     
public class DigitalOnlyBehavior : Behavior < UIElement >
{
public DigitalOnlyBehavior()
{
// Insert code required on object creation below this point.

//
// The line of code below sets up the relationship between the command and the function
// to call. Uncomment the below line and add a reference to Microsoft.Expression.Interactions
// if you choose to use the commented out version of MyFunction and MyCommand instead of
// creating your own implementation.
//
// The documentation will provide you with an example of a simple command implementation
// you can use instead of using ActionCommand and referencing the Interactions assembly.
//
// this.MyCommand = new ActionCommand(this.MyFunction);
}

protected override void OnAttached()
{
base .OnAttached();
this .AssociatedObject.KeyDown += new KeyEventHandler(AssociatedObject_KeyDown);
// Insert code that you would want run when the Behavior is attached to an object.
}

void AssociatedObject_KeyDown( object sender, KeyEventArgs e)
{
char c = ( char )e.PlatformKeyCode;

switch (e.PlatformKeyCode)
{
case 190 :
case 110 :
c
= ' . ' ;
break ;

case 96 :
case 97 :
case 98 :
case 99 :
case 100 :
case 101 :
case 102 :
case 103 :
case 104 :
case 105 :
c
= IntToChar(e.PlatformKeyCode - 96 );
break ;
}

if ( ! Regex.IsMatch(c.ToString(), Filter))
{
e.Handled
= true ;
}
}

protected override void OnDetaching()
{
base .OnDetaching();
this .AssociatedObject.KeyDown -= AssociatedObject_KeyDown;
// Insert code that you would want run when the Behavior is removed from an object.
}

public static readonly DependencyProperty FilterProperty =
DependencyProperty.Register(
" Filter " , typeof ( string ), typeof (DigitalOnlyBehavior), new PropertyMetadata( @" .* " ));

public string Filter
{
get { return ( string )GetValue(FilterProperty); }
set { SetValue(FilterProperty, value); }
}

char IntToChar( int intN)
{
return ( char )(intN + 48 );
}
/*
public ICommand MyCommand
{
get;
private set;
}

private void MyFunction()
{
// Insert code that defines what the behavior will do when invoked.
}
*/
}

调用时分别使用: 

自定义Action和Behavior的调用
   
     
< i:Interaction.Triggers >
< i:EventTrigger >
< actions:TextboxAction />
</ i:EventTrigger >
</ i:Interaction.Triggers >
< i:Interaction.Behaviors >
< behaviors:DigitalOnlyBehavior Filter ="{StaticResource DigitalOnlyFilter}" />
</ i:Interaction.Behaviors >

<!-- DigitalOnlyFilter定义在我们前面介绍的Converter.xaml中
<system:String x:Key="DigitalOnlyFilter" >[0-9.\t]</system:String>
-->

 

5、验证,本小项目中,验证全部是在Property的Set中验证的。然后在xmal文件中使用Binding时加上以下两个属性,NotifyOnValidationError=true, ValidatesOnExceptions=true 这两个属性由于较长,一般容易写错,所以我特意挑出来放在这里作为总结。

 

 6、ViewModel和View之间的沟通,在View构造时,将DataContext赋值为ViewModel即可。当然也可以写在Page Resource中,这是很多网上的一些教程使用的方法。

 

7、有个向下靠千取整的问题,目前我使用的是Math.Round的方法来实现的。我也知道,这个算法肯定是登不了大雅之堂的。如果哪位看到了这里,可否留言指教一下这个算法应该如何写最高效最简洁最优雅。 

向下靠1000取整
   
     
var temp = 总金额 / 总人数 / 1000 ;
if (( int )Math.Round(temp) == ( int )temp)
平均金额
= Math.Round(temp, 0 ) * 1000 ;
else
平均金额
= Math.Round(temp - 0.5m , 0 ) * 1000 ;

 

结论:其实,Silverlight使用起来也是蛮方便,也不是很难,特别是用MVVM这种模式来做的话,其实这个模式一大好处之一是跟MVC一样,可以让程序员更放心的交付可靠代码,分离程序员和美工的职责,让项目同步进行。做Silverlight的ViewModel没有多大的不同以往,注意Converter,Behavior,Action,Trigger这些,然后界面可能就要强调一下VSM,Animation等内容了。 

 

后记:天下事,事与愿违之事有之,昨天接到邮件,该公司最终决定不用Silverlight来做这个小工具,搞着玩的嘛?我辛苦花了40多个小时的时间完成,其间也来来回回有过QA记录,难道到了最后,才发现用Silverlight不好?要改用传统的Asp.net才好?郁闷!不过,事还是要做的,按照他们的要求做成Asp.net的,OK,好,改就改吧。与Silverlight几点不同。

 

1、textBox要显示Currency的格式。这点不同于Silverlight。Silverlight只需写个Converter即可实现,而在Asp.net中,要实现的话,只能靠Javascript的onfocus和onblur事件来改变了。

显示或不显示$的textBox
   
     
function formatCurrencyTextBox(ctrl) {
ctrl.value
= formatCurrencyValue(ctrl.value);
}

function formatCurrencyValue(amount) {
var delimiter = " , " ; // replace comma if desired
var a = amount.split( ' . ' , 2 )
var d = a[ 1 ];
var i = parseInt(a[ 0 ]);
if (isNaN(i)) { return '' ; }
var minus = '' ;
if (i < 0 ) { minus = ' - ' ; }
i
= Math.abs(i);
var n = new String(i);
var a = [];
while (n.length > 3 ) {
var nn = n.substr(n.length - 3 );
a.unshift(nn);
n
= n.substr( 0 , n.length - 3 );
}
if (n.length > 0 ) { a.unshift(n); }
n
= a.join(delimiter);
if (d == null || d.length < 1 ) { amount = n; }
else { amount = n + ' . ' + d; }
amount
= minus + amount;
return " $ " + amount;
}

function unFormatCurrencyTextBox(ctrl) {
ctrl.value
= unFormatCurrencyValue(ctrl);
ctrl.select();
}

function unFormatCurrencyValue(ctrl) {
var value = ctrl.value.replace( new RegExp( ' , ' , ' g ' ), '' );
value
= value.replace( new RegExp( ' \\$ ' , ' g ' ), '' );
return value;
}

 

2、复制剪贴板和打印部分区域的。可以沿用原Silverlight的思路,因为asp.net的打印/复制的目标文字是写在Html标签中和Asp.net标签(动态内容)中的,而不像Silverlight时,直接在ViewModel中公开一个string属性,传过来给PrintHelper打印或CopyHelper复制即可。因此,需要在上面提到的这些标签的内容的前后加个识别标识,为不引起不必要的显示或岐义,加上<!--startprint-->共17个字符开始和<!--endprint-->字符结束。单单加个这个还没有什么作用的,需要用一段Javascript代码来获取这区间段内的文字

获取Html区间文本
   
     
function getCopyPrintText() {
var strBody = window.document.body.innerHTML;
var strBegin = " <!--startprint--> " ;
var strEnd = " <!--endprint--> " ;
var strPrint = strBody.substr(strBody.indexOf(strBegin) + 17 );
strPrint
= strPrint.substring( 0 , strPrint.indexOf(strEnd));
return strPrint;
}

复制时,IE下直接这样子

  
    
clipboardData.setData( " Text " ,getCopyPrintText());

然而,不知是什么原因,该公司对Firefox似乎很有情感,这也说明可能美国那边的Firefox的市场占有率还是蛮高的。要求这个asp.net的要支持Firefox,于是,复制的这段代码也被封装成了一个支持IE和Firefox的函数了

支持IE和Firefox复制
   
     
function copyToClipboard(s)
{
if ( window.clipboardData && clipboardData.setData )
{
clipboardData.setData(
" Text " , s);
}
else
{
// You have to sign the code to enable this or allow the action in about:config by changing
// ("signed.applets.codebase_principal_support", true);
try {
netscape.security.PrivilegeManager.enablePrivilege(
' UniversalXPConnect ' );
}
catch (e) {
alert(
" Access Denial!\nPlease enter 'about:config' in the address bar,\n and make sure the 'signed.applets.codebase_principal_support' is true " );
}

var clip = Components.classes[ ' @mozilla.org/widget/clipboard;1 ' ].createInstance(Components.interfaces.nsIClipboard);
if ( ! clip) return ;

// create a transferable
var trans = Components.classes[ ' @mozilla.org/widget/transferable;1 ' ].createInstance(Components.interfaces.nsITransferable);
if ( ! trans) return ;

// specify the data we wish to handle. Plaintext in this case.
trans.addDataFlavor( ' text/unicode ' );

// To get the data from the transferable we need two new objects
var str = new Object();
var len = new Object();

var str = Components.classes[ " @mozilla.org/supports-string;1 " ].createInstance(Components.interfaces.nsISupportsString);

var copytext = s;

str.data
= copytext;

trans.setTransferData(
" text/unicode " ,str,copytext.length * 2 );

var clipid = Components.interfaces.nsIClipboard;

if ( ! clip) return false ;

clip.setData(trans,
null ,clipid.kGlobalClipboard);
}
}

然后复制也变成

  
    
copyToClipboard(RemoveHtmlTag(getCopyPrintText(), false ));

上面的RemoveHtmlTag是一个去掉Html标签的脚本,在修改这段代码的时候,让我深感学好正则表达式的好处。当然,如果是在IE下,不用下面这个函数去掉标签,复制进clipboard之后,也是没有标签的,但firefox却有,为保一致,还是加上这个。

正则式去掉html标签
   
     
function RemoveHtmlTag(str, noEnter) {
var html = str;
html
= html.replace( / ^[ ]* / img, " " ); // space
html = html.replace( / <!--[\s\S]*?--> / img, "" ); // comment
html = html.replace( / <[\ / ] * table[ ^> ] *> / img, "\n"); // table
html = html.replace( / <[\ / ] * tbody[ ^> ] *> / img, ""); // tbody
html = html.replace( / <[\ / ] * tr[ ^> ] *> / img, "\n"); // tr
html = html.replace( / <[\ / ] * td[ ^> ] *> / img, "\n"); // td
html = html.replace( / <[\ / ] * p[ ^> ] *> / img, "\n"); // p
html = html.replace( / <[\ / ] * a[ ^> ] *> / img, ""); // a
html = html.replace( / <[\ / ] * col[ ^> ] *> / img, "\n"); // col
html = html.replace( / <[\ / ] * br[ ^> ] *> / img, "\n"); // br
html = html.replace( / <[\ / ] * [ ^> ] *> / img, ""); / /
html
= html.replace( / <[\ / ] * span[ ^> ] *> / img, ""); // span
html = html.replace( / <[\ / ] * center[ ^> ] *> / img, ""); // center
html = html.replace( / <[\ / ] * ul[ ^> ] *> / img, ""); // ul
html = html.replace( / <[\ / ] * i[ ^> ] *> / img, ""); // i
html = html.replace( / <[\ / ] * li[ ^> ] *> / img, ""); // li
html = html.replace( / <[\ / ] * b[ ^> ] *> / img, ""); // b
html = html.replace( / <[\ / ] * hr[ ^> ] *> / img, ""); // hr
html = html.replace( / <[\ / ] * h\d + [ ^> ] *> / img, ""); // h1,2,3,4,5,6
html = html.replace( / <STYLE[\s\S]*?<\ / STYLE > / img, ""); // style
html = html.replace( / <script[\s\S]*?<\ / script > / img, ""); // reference script
// html = html.replace(/<[\?!A-Za-z\][^><]*>/img, "");alert("str:"+html)
html = html.replace( / \r / img, "" ); // break
html = html.replace( / \n / img, " \r\n " ); // enter
//
html = html.replace(/[ |\s]*\r\n[ |\s]*\r\n/img, "\r\n");
if (noEnter) {
html
= html.replace( / \r\n / img, "" );
html
= html.replace( / \n / img, "" );
html
= html.replace( / \r / img, "" );
}
return (html);
}

至于思路同于Silverlight打印的那段代码,在原html里加个<div id="printHost"></div>,用CSS控制打印区域,当然Javascript需要给printHost区域赋值,然后用window.print()即可。 

  
    
function printText() {
var printHost = document.getElementById( " printHost " );
printHost.innerHTML
= getCopyPrintText();
window.print();
}

 

3、业务规则,跟Silverlight一样。

 

4、至于是否使用Ajax,就不总结记录了。

 

5、使用Ajax,如果要在cs后台中显示alert,需要这样子包装:

  
    
ScriptManager.RegisterStartupScript(up1,up1.GetType(), " zeroAmount " , " <script type='text/javascript'> alert('up1 is UpdatePanel.');</script> " , false );

没有用Ajax的话,就这样子

  
    
Page.Response.Write( " <script type='text/javascript'> alert('This is a test.');</script> " );

就好了。

 

总结:Silverlight跟Asp.net还是有一些区别的,至少在实现一些细节的时候,Silverlight要方便些,比方说与Javascript之间的交互啊(因为都是客户端嘛),自定义显示啊等。

 

最后,希望这次做完之后,没有多少大的改变了,至少我想不会做成WPF了的,或者再回到Winform里去吧。

 

又续:决定升级silverlight4,需要安装vs2010,却出现:Error code 1601...,解决方法是msiexec /unreg和msiexec /regserver.据说2003类的用net stop msiserver和net start msiserver。

 

 

 

你可能感兴趣的:(silverlight)