zoukankan      html  css  js  c++  java
  • 关于一个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则大致相当于两者的一个小综合体。他们的类关系图如下:

     


    所谓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。

  • 相关阅读:
    网络流量监控工具iftop
    CentOS6.X安装vsftpd服务
    CentOS 6.x版本升级Mysql
    CentOS 5.x版本升级Mysql
    CentOS 5.x版本升级PHP
    CentOS 6.X版本升级PHP
    Spring bean configuration inheritance
    cannot load such file -- openssl
    第八章、Linux 磁盘与文件系统管理
    Laravel Configuration
  • 原文地址:https://www.cnblogs.com/SLKnate/p/1794725.html
Copyright © 2011-2022 走看看