[转载]Asp.net MVC +JQueryValidation + AjaxForm

mikel阅读(1041)

[转载]Asp.net MVC +JQueryValidation + AjaxForm – TT – 博客园.

效果图:

image

image

image

image

主要的代码:

HomeController.cs

using System.Linq;
using System.Threading;
using System.Web.Mvc;
using JqueryValidate.Models;

namespace JqueryValidate.Controllers
{
    [HandleError]
    public class HomeController : BaseController
    {
        public ActionResult Index()
        {
            var model = Database.List;
            return View("Index",model);
        }

        public ActionResult About()
        {
            return View();
        }

        public ActionResult Edit(int? id)
        {
            Thread.Sleep(1000);
            var model = Database.List.FirstOrDefault(z => z.Id == id) ??new MyModel {Id = 0};
            return PartialView("Edit", model);
        }

        [HttpPost]
        public ActionResult Edit(MyModel modelel)
        {
            if (!ModelState.IsValid)
                return JsonError(GetError(ModelState), true);
            if (modelel.Id != 0)
            {
                var find = Database.List.FirstOrDefault(z => z.Id == modelel.Id);
                if (find != null)
                {
                    find.Name = modelel.Name;
                    find.Address = modelel.Address;
                    find.Age = modelel.Age;
                    return Success("更新成功|",true);
                }
            }
            modelel.Id = Database.List.Count + 1;
            Database.List.Add(modelel);
            return Success("创建成功|",true);
        }
    }
}

MyModel.cs

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace JqueryValidate.Models
{
    public class MyModel
    {
        [HiddenInput(DisplayValue = false)]
        public int Id{ get; set;}

        [DisplayName("姓名")]
        [Required]
        public string Name{ get; set;}

        [DisplayName("地址")]
        [Required]
        [StringLength(5,ErrorMessage = "不能大于个字符")]
        public string Address{ get; set;}

        [DisplayName("年ê龄?")]
        [Required]
        [Range(1,100,ErrorMessage = "必须填写1-100以内的整数")]
        public int Age { get; set;}
    }
}

Database.cs

using System.Collections.Generic;

namespace JqueryValidate.Models
{
    public static class Database
    {
        static Database()
        {
            List = new List<MyModel>();
        }

        public static List<MyModel> List { get; set;}
    }
}

Index.aspx

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<JqueryValidate.Models.MyModel>>" %>
<%@ Import Namespace="JqueryValidate.Models" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%=Html.ActionLink("新?建¨", "Edit", new {}, new {@class = "d", width = 400, height = 300})%>
    <table>
        <thead>
        <tr>
            <th>Id</th>
            <th width="200">姓?名?</th>
            <th width="200">地?址·</th>
            <th>年ê龄?</th>
            <th>&nbsp;</th>
            </tr>
        </thead>
        <tbody>
        <%foreach (MyModel item in ViewData.Model)
            {%>
            <tr>
                <td><%=item.Id%></td>
                <td><%=item.Name%></td>
                <td><%=item.Address%></td>
                <td><%=item.Age%></td>
                <td><%=Html.ActionLink("编à辑-", "Edit", new {id = item.Id},new {@class = "d", width = 400, height = 300})%></td>
            </tr>
        <%}%>
        </tbody>
    </table>
</asp:Content>

Edit.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<JqueryValidate.Models.MyModel>" %>
<script src="/Scripts/jquery.form.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.pack.js" type="text/javascript"></script>
<%Html.EnableClientValidation();%>
<%using (Html.BeginForm("Edit", "Home", new {}, FormMethod.Post, new {})){%>
        <%=Html.EditorForModel()%>
        <input type="submit" value="确·认?" />
<%}%>
<script src="/Scripts/MicrosoftMvcJQueryValidation.js" type="text/javascript"></script>
<script type="text/javascript">
    //<![CDATA[
    $("input:text").css({
        border: "solid 1px #ccc",
        padding: "4px"
    });
    $("#form0 input:submit").button();
    function validate(formData, jqForm, options){
        return $("#form0").valid();
    }
    $(function(){
        var ajaxProjectImageListOptions = {
            beforeSubmit: validate ,            
            success: function(html){
                var result;
                try {
                    result = eval('(' + html + ')');
                } 
                catch (ex) {
                    result = html;
                }
                if (result.Error != undefined) {
                    alert(result.Text);
                }
                else {
                    $("#dialogPanel").dialog({
                        autoOpen: false,
                        modal: true,
                        buttons: {
                            关?闭?: function(){
                                $("#dialogPanel").empty();
                                $(this).dialog('close');
                            }
                        }
                    });
                    $("#dialogPanel").html(html);
                    $("#dialogPanel").dialog('open');
                }
            }
        };
        $("#form0").ajaxForm(ajaxProjectImageListOptions);
    });
    //]]>
</script>

源码下载:/Files/francis67/JqueryValidate.rar

[转载]Silverlight 4 中摄像头的运用—part1

mikel阅读(1034)

[转载]Silverlight 4 中摄像头的运用—part1 – 我和未来有约会 – 博客园.

输入的视频

摄像头经过一个Video对象就能让你看到视频,而这个对象是一个显示对象,所以显示对象能做得事情,它都能做,比如滤镜,变形,混合模式等 等。当然最强大的还是使用WriteableBitmap画出视频内容。这样,通过图像分析、 比较等等,对于图像处理来说就有着无限可能。

这里来看看如何引用摄像头并看到拍摄的视频。
===================================

01 public partial class MainPage : UserControl
02 {
03 CaptureSource _captureSource;
04 VideoCaptureDevice _video;
05 public MainPage()
06 {
07 InitializeComponent();
08 _captureSource = new CaptureSource();
09 _video = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
10 if (_video != null) {
11 _captureSource.VideoCaptureDevice = _video;
12 }
13 btnStart.Click += new RoutedEventHandler(btnStart_Click);
14 }
15
16
17
18 void btnStart_Click(object sender, RoutedEventArgs e)
19 {
20 if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
21 CaptureDeviceConfiguration.RequestDeviceAccess())
22 {
23 _captureSource.Start();
24 }
25 }
26 }

===================================

有了摄像头,就该输出视频了。把摄像头的数据作为视频刷子赋值到Rectangle对象上即可,再把Rectangle加入到场景中。
===================================

01 public partial class MainPage : UserControl
02 {
03 CaptureSource _captureSource;
04 VideoCaptureDevice _video;
05 VideoBrush _videoBrush;
06 Rectangle _rect;
07 public MainPage()
08 {
09 InitializeComponent();
10 _captureSource = new CaptureSource();
11 _video = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
12 if (_video != null) {
13 _captureSource.VideoCaptureDevice = _video;
14 _videoBrush = new VideoBrush();
15 _videoBrush.SetSource(_captureSource);
16 }
17 btnStart.Click += new RoutedEventHandler(btnStart_Click);
18 }
19
20
21
22 void btnStart_Click(object sender, RoutedEventArgs e)
23 {
24 if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
25 CaptureDeviceConfiguration.RequestDeviceAccess())
26 {
27 _rect = new Rectangle();
28 _rect.Width = 300;
29 _rect.Height = 225;
30
31
32
33 _rect.Fill = _videoBrush;
34
35
36
37 LayoutRoot.Children.Add(_rect);
38
39
40
41 _captureSource.Start();
42 }
43 }
44 }
===================================

视频尺寸和质量
场景上出现了摄像头拍摄的内容。但画面不够大,也不像其它看到的视频那样清晰。 这不是因为Silverlight不行,而是由于默认的设置不好。视频可以在创建时设置其大小,默认是640×480。我们可以查看Video对象。

这里可知他一共支持了21种格式。通过简单的设置便可以更改摄像头的清晰度。

_video.DesiredFormat = _video.SupportedFormats[3];

要 知道,越高的fps,机器在处理视频时越费劲。对于类似视频会议这种东西,视频的质量和尺寸并不是非常重要,所以不要为此花太大精力。

视频和位图
正如之前说的,摄像头最强大的应用是混合WriteableBitmap。所以,通过Render把视频绘 制在一个WriteableBitmap对象里,就能以像素级来控制整个东西了。当然,对于动画,就需要不停的绘制。

===================================

01 void btnStart_Click(object sender, RoutedEventArgs e)
02 {
03 if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
04 CaptureDeviceConfiguration.RequestDeviceAccess())
05 {
06 _rect = new Rectangle();
07 _rect.Width = 320;
08 _rect.Height = 240;
09
10
11
12 _rect.Fill = _videoBrush;
13 _rect.Visibility = Visibility.Collapsed;
14 LayoutRoot.Children.Add(_rect);
15
16
17
18 _wb = new WriteableBitmap(_rect, null);
19 _wb_image = new Image();
20 _wb_image.Width = 320;
21 _wb_image.Height = 240;
22 _wb_image.Source = _wb;
23
24
25
26 LayoutRoot.Children.Add(_wb_image);
27
28
29
30
31 _captureSource.Start();
32 _isEnableCamera = true;
33 }
34 }
35 void OnRender(object sender, EventArgs e)
36 {
37 if (_isEnableCamera)
38 {
39 _wb.Render(_rect, null);
40 _wb.Invalidate();
41 }
42 }
===================================

反转图像
要知道,用户在摄像头前所做的反应,总希望和镜子中一样,人往左,视频中人也往左,人 往右,其也往右。但事实并不是这样,所以需要我们来为之做反转图像。
===================================

01 void OnRender(object sender, EventArgs e)
02 {
03 if (_isEnableCamera)
04 {
05
06
07
08 MatrixTransform transform = new MatrixTransform();
09 transform.Matrix = new Matrix(-1, 0, 0, 1, 320, 0);
10
11
12
13 _wb.Render(_rect, transform);
14 _wb.Invalidate();
15 }
16 }

===================================

对象的绘制以传入的变形矩阵为依据。通过矩阵能做很多事情,虽然只有很简单的前四个参数,它们却控制着缩放,旋转和形变。这里让图形的x轴缩放率等 于 -1,意味着水平反转它。接着,0的意思是指图形没有旋转和形变,最后一个1是说y轴保持100%不变。但仅仅如此,还看不到任何东西。因为此时的矩阵是 从右向左扩展的,而最后两个参数是把矩阵移到图形的右上角,这样就保证落入了可视范围。

分析像素
现在你拥有了像素的控制权,该如何处置?记得我之前说过不要高质量、高分辨率的视频嘛?我 现在要更进一步的说,越低分辨率、越低质量越好。就算只有320×240大小的视频图像,每一帧要处理的像素也有76800个啊。所以不要搞太大的视频来 玩。事实上,你将会发现我要做的第一件事情,是设计如何除掉这么多像素带来的巨大信息。一般来说,我们只对视频的某个颜色区域,或者对比度之类的感兴趣。
分析颜色
首先我们尝试着跟踪一个具体颜色。假如用户拿一个颜色(红色)比较突出的东西在摄像头前面晃动,我们能跟踪其位置就算成功。

首先了解一下像素数组的排列方式。

所以像素的算法是

int pixel = bmSource.Pixels[y * imageWidth + x]
接下来根据pixel 反向算回颜色
1 Color color = Color.FromArgb(
2 (byte)((pixel >> 24) & 0xff),
3 (byte)((pixel >> 16) & 0xff),
4 (byte)((pixel >> 8) & 0xff),
5 (byte)((pixel) & 0xff));
有了以上的原理就可以分析WriteableBitmap的颜色了。

[转载]Silverlight中摄像头的运用—part2

mikel阅读(1294)

[转载]Silverlight中摄像头的运用—part2 – 我和未来有约会 – 博客园.

将跟踪颜色视作输入

好了,我们能够跟踪到这个颜色了,那这么做的意义是什么呢?实际上,我们可以根据它的位置来移动东西。接下来的例子中,创建的一个球会跟随这个颜色 一起移动。你可以用来作出很诡异的对象跟随画面移动的效果。

关键代码:

===================================
//part2
Ellipse ball = new Ellipse();
CompositeTransform ballctf;
ball.Width = 20;
ball.Height = 20;
ball.Fill = new SolidColorBrush(Colors.Orange);
ballctf = new CompositeTransform();
ball.RenderTransform = ballctf;


labRes.Content = “捕获颜色=” + found.ToString();
labPoint.Content = “坐标=” + _lastPoint.X.ToString() + “,” + _lastPoint.Y.ToString();

ballctf.TranslateX = _lastPoint.X;
ballctf.TranslateY = _lastPoint.Y;

Debug.WriteLine(found);
===================================

分析移动区域

在这一节,我们虽然还不去涉及如何跟踪物体的具体轨迹,但会知道如何判断是否有移动。  一个基本概念是:如果有移动,每帧的画面会明显不同。所以,如果发现两帧画面中位图的像素有不同的地方,就能知道发生了移动。

有两个潜在元素。第一,我们需要两张位图。第二,我们还需要一个比较函数。如果,你正在想着是否需要遍历所有像素来进行比较,那么我告诉你,这里有一个 很实用的技巧:使用混合模式。绘制时如果不指定混合模式,新的像素值就会完全覆盖以取代存在的像素值。这也是我们至今为止一直在做的事情。如果使用混合模 式,新的像素会影响已存在的像素,两张图片会以一种特别的方式混合在一起。而此刻,我们要用的混合模式叫做difference(差异),它对两张图片的 红、绿、蓝三个通道的每个像素进行一次比较,然后给出它们之间的相减所得的差值。如果两个像素完全一致,那么结果就是0,也就是黑色,否则就是别的其它什 么值(颜色)。这样,我们就把跟踪移动的问题简化了,只要寻找非黑色区域即可。
===================================
public partial class MotionTrackingUserControl
{
CaptureSource _captureSource;
VideoCaptureDevice _video;
VideoBrush _videoBrush;
Rectangle _rect;
Image _wb_image;
WriteableBitmap _wb = null;
bool _isEnableCamera = false;

WriteableBitmap _newFrameBitmap;
WriteableBitmap _oldFrameBitmap;

Image _newFrame;
Image _oldFrame;

public MotionTracking()
{
InitializeComponent();

_captureSource = new CaptureSource();
_video = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
if (_video != null)
{
_video.DesiredFormat = _video.SupportedFormats[1];
_captureSource.VideoCaptureDevice = _video;
_videoBrush = new VideoBrush();
_videoBrush.SetSource(_captureSource);
}
CompositionTarget.Rendering += new EventHandler(OnRender);

btnStart.Click += new RoutedEventHandler(btnStart_Click);

this.imageEffectsListBox.Items.Add(new NormalEffect());

this.imageEffectsListBox.Items.Add(new DarkenEffect());
this.imageEffectsListBox.Items.Add(new MultiplyEffect());
this.imageEffectsListBox.Items.Add(new ColorBurnEffect());
this.imageEffectsListBox.Items.Add(new LinearBurnEffect());

this.imageEffectsListBox.Items.Add(new LightenEffect());
this.imageEffectsListBox.Items.Add(new ScreenEffect());
this.imageEffectsListBox.Items.Add(new ColorDodgeEffect());
this.imageEffectsListBox.Items.Add(new LinearDodgeEffect());

this.imageEffectsListBox.Items.Add(new OverlayEffect());
this.imageEffectsListBox.Items.Add(new SoftLightEffect());
this.imageEffectsListBox.Items.Add(new HardLightEffect());
this.imageEffectsListBox.Items.Add(new VividLightEffect());
this.imageEffectsListBox.Items.Add(new LinearLightEffect());
this.imageEffectsListBox.Items.Add(new PinLightEffect());

this.imageEffectsListBox.Items.Add(new DifferenceEffect());
this.imageEffectsListBox.Items.Add(new ExclusionEffect());

this.imageEffectsListBox.Items.Add(new GlowEffect());
this.imageEffectsListBox.Items.Add(new ReflectEffect());

this.imageEffectsListBox.Items.Add(new HardMixEffect());
this.imageEffectsListBox.Items.Add(new NegationEffect());
this.imageEffectsListBox.Items.Add(new PhoenixEffect());

this.imageEffectsListBox.Items.Add(new AverageEffect());

this.imageEffectsListBox.SelectedIndex = 0;
}

void btnStart_Click(object sender, RoutedEventArgs e)
{
if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
CaptureDeviceConfiguration.RequestDeviceAccess())
{
_rect = new Rectangle();
_rect.Width = 320;
_rect.Height = 240;

_rect.Fill = _videoBrush;
_rect.Visibility = Visibility.Collapsed;
video_Canvas.Children.Add(_rect);

_newFrameBitmap = new WriteableBitmap(_rect, null);
_oldFrameBitmap = new WriteableBitmap(_rect, null);

_newFrame = new Image();
_newFrame.Width = 320;
_newFrame.Height = 240;
_newFrame.Source = _newFrameBitmap;
_newFrame.Visibility = Visibility.Collapsed;

_oldFrame = new Image();
_oldFrame.Width = 320;
_oldFrame.Height = 240;
_oldFrame.Source = _oldFrameBitmap;

video_Canvas.Children.Add(_oldFrame);
video_Canvas.Children.Add(_newFrame);

_captureSource.Start();
_isEnableCamera = true;

Thread thread = new Thread(new ThreadStart(ThreadProc));
thread.Start();

}
}

void OnRender(object sender, EventArgs e)
{
if (_isEnableCamera)
{
MatrixTransform transform = new MatrixTransform();
transform.Matrix = new Matrix(-1, 0, 0, 1, 320, 0);

_newFrameBitmap.Render(_rect, transform);
_newFrameBitmap.Invalidate();
}
}

void ThreadProc()
{
while (true)
{
Thread.Sleep(20);

//Do the action in the UI thread
Dispatcher.BeginInvoke(ThreadUpdate);
}
}

void ThreadUpdate()
{
if (_isEnableCamera)
{
_oldFrameBitmap.Render(_newFrame, null);
_oldFrameBitmap.Invalidate();
}
}

private void imageEffectsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
BlendModeEffect effect = e.AddedItems[0] as BlendModeEffect;
if (effect != null)
{
if (_oldFrameBitmap != null)
{
ImageBrush _newImageBrush = new ImageBrush();
_newImageBrush.ImageSource = _newFrameBitmap;
ImageBrush _oldImageBrush = new ImageBrush();
_oldImageBrush.ImageSource = _oldFrameBitmap;

//effect.AInput = _oldImageBrush;
effect.BInput = _newImageBrush;
this._oldFrame.Effect = effect;
}
}
}
}

===================================

当运行刚启动,会出现一张纯黑色的矩形。但接着就会看到鬼一样移动的轮廓。这个轮廓就是两帧画面的不同之处。

接下来再使用threshold滤镜来对图片进行一下处理,对移动区域做更加精细的捕捉。

参考这里:http://kodierer.blogspot.com/2009/07/livin-on-edge- silverlight-parametric_4324.html

[转载]浅谈OSGi.NET开放服务平台和Discuz插件系统

mikel阅读(1009)

[转载]浅谈OSGi.NET开放服务平台和Discuz插件系统 – 道法自然 – 博客园.

我们团队从2008年5月份开始设计一个App Store,目前已经实现了产品的原型,现在产品已经进入了完善阶段。该产品最开始是基于我在2005年开发的一个Common Form Framework,此后结合了CAB & SCSF设计了Common UI Platform,从2008年5月份开始正式确定了产品的RoadMap,当然中间还是有不少变更了。它类似Google App Engine和Sina App Engine,只不过市场策略和模式是完全不同的。对于构建一个企业级的App Store,我个人认为.NET平台自身的特性在这领域确实不如Java,不过,好在也不是有什么是不可解决的。在产品设计过程中,我时刻关注业内的各种 App Engine、App Store和Plugin Framework,包括Equinox、SharpDevelop、Egeye、Mono.Addin、MAF、MEF、SCSF、 Google/Sina App Engine、Discuz等。每出现一种类似的产品,我都有一种心惊肉跳的感觉,担心别人抢在我之前设计了更好的同质产品,当然,也好在目前还没有看到 同质的产品。

今天我先介绍一下该Store的内核OSGi.NET,同时也谈一下我见过比较简陋的插件系统Discuz。

OSGi 是一个开放服务规范,“开放”意味着基于该平台可以使得很多人来共同使用和协作,而“服务”则是实现协作的一个手段。该规范可以总结为:A 插件化支持规范; B 面向服务支持规范; C 插件扩展规范; D 安全性与隔离性规范; E 系统服务规范。插件化规范完整详细定义了插件的结构、插件依赖、插件类加载、L10N和I18N、宿主插件和片段插件;面向服务支持规范定义了模块间服务 协作的支持,这个服务并不是传统意义的企业级Web Service,确切的讲,仅是“接口+实现”,并对实现的引用进行管理;插件扩展规范定义了一个插件如何对另一个插件进行扩展,这种扩展手段非常简单, 在这里提出了扩展点概念;安全性与隔离性则要确保被内核加载的插件不会对内核和其它插件产生一些副作用,比如我们决不能允许一个非法的插件来停止另一个插 件;系统服务规范则预定义了几个系统服务。OSGi规范是基于Java编写的,此前还没有一个针对.NET平台OSGi规范,原因在于.NET并不支持 ava那样优雅的类加载机制,不过还在我们目前都找到了绕过这些固有缺陷的方法并设计了针对.NET的规范。

我们团 队在设计OSGi.NET时候,把易用性放在首位,通过场景驱动来设计Usecase。从而,基于OSGi.NET开发一个插件和开发一个.NET项目方 式基本一样(当然,我们并不认为目前已经做到最好了,易用性肯定还有很大的改进空间)。以下是一个Hello World的插件,开发人员应该可以在5分钟内开发一个Hello World插件的。

1 在OSGi.NET的Plugins目录下,创建一个Class Library Project,并创建一个MyActivator类。

1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using UIShell.OSGi;
5
6 namespace UIShell.TestBundle
7 {
8 public class MyActivator : IBundleActivator
9 {

10 public void Start(IBundleContext context)
11 {
12 Console.WriteLine(Hello World! Plugin is started.);
13 }
14 public void Stop(IBundleContext context)
15 {
16 Console.WriteLine(Plugin is stopped.);
17 }
18 }
19}
20

2 定义一个Manifest.xml文件

1 <?xml version=”1.0″ encoding=”utf-8″ ?>
2 <Bundle Name=”TestBundle”
3 SymbolicName=”UIShell.TestBundle”
4 InitializedState=”Started”>
5 <Activator Type=”UIShell.TestBundle.MyActivator” />
6 <Runtime>
7 <Assembly Path=”bin/Debug/UIShell.TestBundle.dll” Share=”false” />
8 </Runtime>
9 </Bundle>
10

通过Console运行OSGi.NET后,这个插件便会被内核加载启 动,然后Print出Hello World。

基于OSGi.NET插件开发,一般只需做的事情有:(1)定义插件要实现 的功能;(2)判断插件是否需要引用其它插件的功能,如果有,则可以通过A)在Runtime声明一个Dependeny节点;B)在 MyActivator.Start方法中使用context.GetService方法获取依赖的服务;(3)通过定义Extension来扩展其它插 件功能,通过ExtensionPoint暴露扩展点。

对于Discuz的关注是始于我们在设计基于ASP.NET的 Store的。在设计这个Store的时候,我想比较一下基于OSGi.NET的ASP.NET插件平台和其它插件平台。Discuz插件系统是我见过设 计的最为简单也是最为简陋的插件系统了。我只看了一下反编译的结果,就没有深入研究它的欲望了。以下是通过反编译看到的插件定义。

它的插件系统是基于接口和实现类来设计了。这 种简陋的设计从严格意义上来讲不是一个插件平台,对开放性的支持也就更弱了,注定不可能有太多的人参与到这个产品插件的设计与扩展了。此外,每次内核的升 级都可能会导致原有的插件无法正常使用。不过,这也可能是因为Discuz在产品设计阶段并没有提出开放性目标。

Creative  Commons License

本文基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必 须保留本文的署名道法自然(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言

[转载]什么是序列化和反序列

mikel阅读(1064)

[转载]什么是序列化和反序列 – YangLei’s – 博客园.

net的运行时环境用来支持用户定义类型的流化的机制。它是将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及 类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。

序列化的目的:
1、以某种存储形式使自定义对象持久化;
2、将对象从一个地方传递到另一个地方。

实质上序列化机制是将类的值转化为一个一般的(即连续的)字节流,然后就可以将该流写到磁盘文件或任何其他流化目标上。而要想实际的写出这个流,就要使用 那些实现了IFormatter接口的类里的Serialize和Deserialize方法。
在.net框架里提供了这样两个类:
一、BinaryFormatter

BinaryFormatter使用二进制格式化程序进行序列化。您只需创建一个要使用的流和格式化程序的实例,然后调用格式化程序的   Serialize   方法。流和要序列化的对象实例作为参数提供给此调用。类中的所有成员变量(甚至标记为   private   的变量)都将被序列化。

首先我们创建一个类:
[Serializable]
public   class   MyObject   {
public   int   n1   =   0;
public   int   n2   =   0;
public   String   str   =   null;
}
Serializable属性用来明确表示该类可以被序列化。同样的,我们可以用NonSerializable属性用来明确表示类不能被序列化。
接着我们创建一个该类的实例,然后序列化,并存到文件里持久:
MyObject   obj   =   new   MyObject();
obj.n1   =   1;
obj.n2   =   24;
obj.str   =   “一些字符串”;
IFormatter   formatter   =   new   BinaryFormatter();
Stream   stream   =   new   FileStream(“MyFile.bin”,   FileMode.Create,
FileAccess.Write,   FileShare.None);
formatter.Serialize(stream,   obj);
stream.Close();

而将对象还原到它以前的状态也非常容易。首先,创建格式化程序和流以进行读取,然后让格式化程序对对象进行反序列化。
IFormatter   formatter   =   new   BinaryFormatter();
Stream   stream   =   new   FileStream(“MyFile.bin”,   FileMode.Open,
FileAccess.Read,   FileShare.Read);
MyObject   obj   =   (MyObject)   formatter.Deserialize(fromStream);
stream.Close();

//   下面是证明
Console.WriteLine(“n1:   {0}”,   obj.n1);
Console.WriteLine(“n2:   {0}”,   obj.n2);
Console.WriteLine(“str:   {0}”,   obj.str);

二、SoapFormatter

前面我们用BinaryFormatter以二进制格式来序列化。很容易的我们就能把前面的例子改为用SoapFormatter的,这样将以xml格式 化,因此能有更好的可移植性。所要做的更改只是将以上代码中的格式化程序换成   SoapFormatter,而   Serialize   和   Deserialize   调用不变。对于上面使用的示例,该格式化程序将生成以下结果。

<SOAP-ENV:Envelope
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”
xmlns:SOAP-   ENC=http://schemas.xmlsoap.org/soap/encoding/
xmlns:SOAP-   ENV=http://schemas.xmlsoap.org/soap/envelope/
SOAP-ENV:encodingStyle=
“http://schemas.microsoft.com/soap/encoding/clr/1.0
http://schemas.xmlsoap.org/soap/encoding/”
xmlns:a1=”http://schemas.microsoft.com/clr/assem/ToFile”>

<SOAP-ENV:Body>
<a1:MyObject  >
<n1>1</n1>
<n2>24</n2>
<str  >一些字符串</str>
</a1:MyObject>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

在这里需要注意的是,无法继承   Serializable   属性。如果从   MyObject   派生出一个新的类,则这个新的类也必须使用该属性进行标记,否则将无法序列化。例如,如果试图序列化以下类实例,将会显示一个   SerializationException,说明   MyStuff   类型未标记为可序列化。

public   class   MyStuff   :   MyObject
{
public   int   n3;
}

然而关于格式化器,还有个问题,假设我们只需要xml,但不需要soap特有的额外信息,那么该怎么做?有两个方案:1、编写一个实现 IFormatter接口的类,采用的方式类似于SoapFormatter,但是可以没有你不需要的信息;2、使用框架提供的类 XmlSerializer。
XmlSerializer类和前两个主流的序列化类的几个不同点是:
1、不需要Serializable属性,Serializable和NonSerializable属性将会被忽略,但是使用XmlIgnore属性, 和NonSerializable属性类似。
2、该类不能安全地访问私有变成员,所以学要将私有成员改为公共成员,或者提供合适的公共特性。
3、要求被序列化的类要有一个默认的构造器。

我们改一下前面的MyObject类为:

public   class   MyObject   {
public   int   n1;
public   String   str;
public   MyObject(){}
public   MyObject(n1,str)
{
this.n1=n1;
this.str=str;
}
public   override   string   ToString()
{
return   String.Format(“{0}:{1}”,this.str,this.n1);
}
}
现 在我们用XmlSerializer类来对修改后的MyObject进行序列化。因为XmlSerializer类的构造器里有个Type参数,所以 XmlSerializer对象被明确的   连到该Type参数所表示的类了。XmlSerializer类也有Serialize和Deserialize方法:
MyObject   obj   =   new   MyObject(12,”some   string…”);
XmlSerializer   formatter   =   new   XmlSerializer(typeof(MyObject));
Stream   stream   =   new   FileStream(“MyFile.xml”,   FileMode.Create,
FileAccess.Write,   FileShare.None);
formatter.Serialize(stream,   obj);
//下面是反序列化
stream.Seek(0,SeekOrigin.Begin)
MyObject   obj_out=(MyObject)formatter.Deserialize(stream)
stream.Close();
Console.WriteLine(obj_out);

这个简单的列子可以加以扩展,以便利用更多的XmlSerializer功能,包括使用属性控制xml标记、使用xml模式和进行soap编码。

自定义序列化

如果你希望让用户对类实现序列化,但是对数据流的组织方式不完全满意,那么可以通过在对象上实现   ISerializable   接口来自定义序列化过程。这一功能在反序列化后成员变量的值失效时尤其有用,但是需要为变量提供值以重建对象的完整状态。除了必须将类申明为   Serializable   的同时,还要要实现   ISerializable接口,需要实现   GetObjectData   方法以及一个特殊的构造函数,在反序列化对象时要用到此构造函数。在实现   GetObjectData   方法时,最常调用的SerializationInfo的方法是AddValue,这个方法具有针对所有标准类型(int、char等等)的重载版本;而   StreamingContext   参数描述给定的序列化流的源和目标,这样我们就可以知道我们是将对象序列化到持久性存储还是在将他们跨进程或机器序列化。而在反序列化时,我们调用 SerializationInfo提供的一组Getxxx方法,他们针对所有标准类型数据执行各种AddValue重载版本的逆操作。下代码示例说明了 如何在前一部分中提到的   MyObject   类上实现   ISerializable。

[Serializable]
public   class   MyObject   :   ISerializable
{
public   int   n1;
public   int   n2;
public   String   str;

public   MyObject()
{
}

protected   MyObject(SerializationInfo   info,   StreamingContext   context)
{
n1   =   info.GetInt32(“i”);
n2   =   info.GetInt32(“j”);
str   =   info.GetString(“k”);
}

public   virtual   void   GetObjectData(SerializationInfo   info,
StreamingContext   context)
{
info.AddValue(“i”,   n1);
info.AddValue(“j”,   n2);
info.AddValue(“k”,   str);
}
}
在序列化过程中调用   GetObjectData   时,需要填充方法调用中提供的   SerializationInfo   对象。只需按名称/值对的形式添加将要序列化的变量。其名称可以是任何文本。只要已序列化的数据足以在反序列化过程中还原对象,便可以自由选择添加至   SerializationInfo   的成员变量。如果基对象实现了   ISerializable,则派生类应调用其基对象的   GetObjectData   方法。

需要强调的是,将   ISerializable   添加至某个类时,需要同时实现   GetObjectData   以及特殊的具有特定原型的构造函数--重要的是,该构造函数的参数列表必须与GetObjectData相同,这个构造函数将会在反序列化的过程中使用: 格式化器从流中反序列化数据,然后通过这个构造函数对对象进行实列化。如果缺少   GetObjectData,编译器将发出警告。但是,由于无法强制实现构造函数,所以,缺少构造函数时不会发出警告。如果在没有构造函数的情况下尝试反 序列化某个类,将会出现异常。在消除潜在安全性和版本控制问题等方面,当前设计优于   SetObjectData   方法。例如,如果将   SetObjectData   方法定义为某个接口的一部分,则此方法必须是公共方法,这使得用户不得不编写代码来防止多次调用   SetObjectData   方法。可以想象,如果某个对象正在执行某些操作,而某个恶意应用程序却调用此对象的   SetObjectData   方法,将会引起一些潜在的麻烦。

在反序列化过程中,使用出于此目的而提供的构造函数将   SerializationInfo   传递给类。对象反序列化时,对构造函数的任何可见性约束都将被忽略,因此,可以将类标记为   public、protected、internal   或   private。一个不错的办法是,在类未封装的情况下,将构造函数标记为   protect。如果类已封装,则应标记为   private。要还原对象的状态,只需使用序列化时采用的名称,从   SerializationInfo   中检索变量的值。如果基类实现了   ISerializable,则应调用基类的构造函数,以使基础对象可以还原其变量。

[转载]web地图信息格网索引-----基于google map api和oracle查询的生活地图

mikel阅读(1242)

[转载]web地图信息格网索引—–基于google map api和oracle查询的生活地图 – 梦开始的地方 – 博客园.

当前项目中的优惠促销信息需要和地图位置相关起来,所以就用到了google map。根据需求,要做一个在地图上显示郑州地区所有优惠促销信息的页面。首先对这个需求进行了分析,郑 州的优惠促销信息是非常多的,如何在地图上将所有的信息显示出来?如果全部显示出来,将会出现信息的压盖和信息将地图压盖起来,用户看不到地图,也看不清 楚信息。所以就需要一定的处理。

网上通行的做法就是对地图上显示的信息进行分页展示,这样在页面上显示的信息就不会很多,用可以很 清晰的看到地图和地图上所展示的信息。如google map

其它的都是同样的方式,只是显示的样式有所不同。

Google map还提供了另一种方式,

将所有的信息都呈现在地图上,google map所呈现的所有信息点是绘制在图片上的,也就是说,google map的开发人员利用api有开发和制作了一个图层,和影像、纹理是同一级别的图片索引集合,数据传送到本地浏览器,这样数据不 用做显示,提高了浏览器的速度,也完成了信息的查询。如果利用google map提供的marker来做,显然数据量是非常巨大的,浏览器无法 承受如此多的marker对象的绘制。所以google map才用了静态图片来实现。

我们也想在地图上显示所有的数据,但是我们没有google那么强大的实力,说以就仿照了google 高考2009地图:

将信息聚合显示在地图之上,也就是说在某一位置,告诉用户这个地方有多少优惠促销信息,你可以放大或 点击进入进行查看。

目标有了之后就开始研究如何实现。我的测试数据是100万条优惠促销信息。

初始实现方法:根据查询,将所有符合条件的数据读取出来,通过ajax发送到前台,然后在前台进行相交压盖判断。

试验结果是一次读取的数据量太大,消耗太多的时间,另一个就是数据读取完成之后发送到前台占用大量的带宽。再有就是前台浏览器运算压盖消耗太多 的时间。

经过试验,不可行,需要进行改进,不过流程上没有太大的问题。所以感觉的思路如下:

1、 减少数据库中读取出来的数据进行。

2、 在前台建立格网索引,加快计算压盖速度。

经过分析,如果减少数 据库读取出来的数据,那就没有在地图上显示或提示出网站所有的优惠促销信息,在前台建立格网索引,依然需要将大量的数据传送到前台,那为什么不在后台建立 格网索引呢。分析过后,将格网索引放在后台进行处理。经过改进,是比第一次有所提高,但还不够。

经过一系列的试验分析思考之后, 得出了解决方案。

将格网索引放置在数据库中,一个查询就可聚合分组查询出所以优惠促销。减少数据的读取和网络间的传 送,简化前台开发(oracle 有支持地理实体的组件,Oracle Spatial,但是对于这个需求并不实用)

具体方法如下:

1、 google map区域的建立格网

因为google map的缩放分级,所以,建立格网时需要根据不同的级别进行创建。当前所做的指真对郑州地图,所以格网重12级到19级。如 图:

12级为16*16的 格网

13级为32*32的 格网

….

格网的递增为四叉树。因为从12级开始就需要显示丰富的信息,说以第一级不是一个2*2的网 格,而是一个15*15的网格。

格网个2维的数组,将其转换为1维数组,即12级格网号顺序从1开始到256结束。 第一排从116,第二排从17-32以 次类推,最终完成格网的模型创建。

下边开始对数据库进行设计。

创建数据表。

字段

类型描述

信息id

关联的信息id

Lev12

整型格网号

Lev13

整型格网号

Lev14

整型格网号

Lev15

整型格网号

Lev16

整型格网号

Lev17

整型格网号

Lev18

整型格网号

Lev19

整型格网号

通过计算,将二维的点状信息转换为相应级别下的格网好

insert into shopindex (shopid) values (sid);

更新索引

update shopindex s

set (lev12, lev13, lev14, lev15, lev16, lev17, lev18, lev19) = (select GetGirdNo(px,

py,

1212) lev12,

GetGirdNo(px,

py,

1312) lev13,

GetGirdNo(px,

py,

1412) lev14,

GetGirdNo(px,

py,

1512) lev15,

GetGirdNo(px,

py,

1612) lev16,

GetGirdNo(px,

py,

1712) lev17,

GetGirdNo(px,

py,

1812) lev18,

GetGirdNo(px,

py,

1912) lev19

from ooshop f

where s.shopid = f.id)

where s.shopid = sid;

根据xylev获取格网号 函数

create or replace function GETGIRDNO(x in integer,

y in integer,

lev in integer)

return integer is

girdnum INT := 15;

width INT := 110866;

height INT := 77900;

gnum INT;

stpwidth int;

stpheight int;

xmin int := 40869800;

ymin int := 12467548;

col int;

row int;

res int;

BEGIN

gnum := girdnum * POWER(2, lev);

stpwidth := width / gnum;

stpheight := height / gnum;

col := (x – xmin) / stpwidth;

row := (y – ymin) / stpheight;

res := col + row * gnum;

if res < 0 or res >girdnum*girdnum * POWER(4, lev) then

res := 0;

end if;

return res;

END GetGirdNo;

信息如图

有了这个索引表之后,就可以用oracle的分组查询,一个查询将给定范围的数据进行分组查询出来,分组的依据就个格网,这样查询出的数据就是 在这个格网下有多少条优惠促销信息。

获取12级的分组数据

select lev12,count(shopid) from shopindex t group by lev12


真正的查询要比这复杂点,要根据给定的地图范围进行查询,这样就需要一个函数,即根据地图范围获取这个范围下的所有格网。函数如下:

create or replace function GETGIRDNOS(stcol in integer,

strow in integer,

encol in integer,

enrow in integer,

lev   in integer)

RETURN colrows IS

gnum    int;

i       int;

j       int;

girdnum INT := 15;

colrow  colrows := colrows();

BEGIN

i := strow;


gnum := girdnum * POWER(2, lev);

while (i <= enrow) loop

j := stcol;

while (j <= encol) loop

colrow.EXTEND;

colrow(colrow.COUNT) := j + i * gnum;

j := j + 1;

end loop;

i := i + 1;

end loop;

RETURN colrow;

END GetGirdNos;

数 据的读取

var result = new Collection<ooBFeature>();

while (reader.Read())

{

ooBFeature gm = new ooBFeature(1);

gm.I = reader.GetInt32(0);

gm.N = reader.GetValue(1).ToString();

int row = (int)(gm.I / grid.GNum);

int col = (int)(gm.I % grid.GNum);

gm.X = col * grid.StpWidth + grid.StpWidth / 2 + ooGridModel.x;

gm.Y = row * grid.StpHeight + grid.StpHeight / 2 + ooGridModel.y;

result.Add(gm);

}

return result;

根据读取出的格网计算出地 图上的坐标。以上就是用oracle建立地理信息索引的方法,如有问题, 可进行交流。

效果页面http://www.map85.com/map

[转载]群发邮件2

mikel阅读(1023)

[转载]群发邮件2 – pcajax – 博客园.

群发邮件,对于网站和一些推广应用,可谓不可或缺的“技能”

对于.NET而言,从2.0开始,发邮件已经是一件非常easy 的事了。下面我给出一个用C#群发邮件的实例,做了比较详细的注解,希望对有需要的朋友有所help。看了这篇BLOG,// 引入命名空间

using System.Net;
using System.Net.Mail;

SmtpClient smtp = new SmtpClient(); //实例化一个SmtpClient
smtp.DeliveryMethod = SmtpDeliveryMethod.Network; //将smtp的出站方式设为 Network
smtp.EnableSsl = false;//smtp服务器是否启用SSL加密
smtp.Host = "smtp.163.com"; //指定 smtp 服务器地址
smtp.Port = 25;             //指定 smtp 服务器的端口,默认是25,如果采用默认端口,可省去
//如果你的SMTP服务器不需要身份认证,则使用下面的方式,不过,目前基本没有不需要认证的了
smtp.UseDefaultCredentials = true;
//如果需要认证,则用下面的方式
smtp.Credentials = new NetworkCredential("邮箱帐号@163.com", "邮箱密码");
MailMessage mm = new MailMessage(); //实例化一个邮件类
mm.Priority = MailPriority.High; //邮件的优先级,分为 Low, Normal, High,通常用 Normal即可
mm.From = new MailAddress("邮箱帐号@163.com", "真有意思", Encoding.GetEncoding(936));
//收件方看到的邮件来源;
//第一个参数是发信人邮件地址
//第二参数是发信人显示的名称
//第三个参数是 第二个参数所使用的编码,如果指定不正确,则对方收到后显示乱码
//936是简体中文的codepage值

注:上面的邮件来源, 一定要和你登录邮箱的帐号一致,否则会认证失败

mm.ReplyTo = new MailAddress("test_box@gmail.com", "我的接收邮箱", Encoding.GetEncoding(936));
//ReplyTo 表示对方回复邮件时默认的接收地址,即:你用一个邮箱发信,但却用另一个来收信
//上面后两个参数的意义, 同 From 的意义
mm.CC.Add("a@163.com,b@163.com,c@163.com");
//邮件的抄送者,支持群发,多个邮件地址之间用 半角逗号 分开
//当然也可以用全地址,如下:
mm.CC.Add(new MailAddress("a@163.com", "抄送者A", Encoding.GetEncoding(936)));
mm.CC.Add(new MailAddress("b@163.com", "抄送者B", Encoding.GetEncoding(936)));
mm.CC.Add(new MailAddress("c@163.com", "抄送者C", Encoding.GetEncoding(936)));
mm.Bcc.Add("d@163.com,e@163.com");
//邮件的密送者,支持群发,多个邮件地址之间用 半角逗号 分开
//当然也可以用全地址,如下:
mm.CC.Add(new MailAddress("d@163.com", "密送者D", Encoding.GetEncoding(936)));
mm.CC.Add(new MailAddress("e@163.com", "密送者E", Encoding.GetEncoding(936)));
mm.Sender = new MailAddress("xxx@xxx.com", "邮件发送者", Encoding.GetEncoding(936));
//可以任意设置,此信息包含在邮件头中,但并不会验证有效性,也不会显示给收件人
//说实话,我不知道有啥实际作用,大家可不理会,也可不写此项
mm.To.Add("g@163.com,h@163.com");
//邮件的接收者,支持群发,多个地址之间用 半角逗号 分开
//当然也可以用全地址添加
mm.To.Add(new MailAddress("g@163.com", "接收者g", Encoding.GetEncoding(936)));
mm.To.Add(new MailAddress("h@163.com", "接收者h", Encoding.GetEncoding(936)));
mm.Subject = "这是邮件标题"; //邮件标题
mm.SubjectEncoding = Encoding.GetEncoding(936);
// 这里非常重要,如果你的邮件标题包含中文,这里一定要指定,否则对方收到的极有可能是乱码。
// 936是简体中文的pagecode,如果是英文标题,这句可以忽略不用
mm.IsBodyHtml = true; //邮件正文是否是HTML格式
mm.BodyEncoding = Encoding.GetEncoding(936);
//邮件正文的编码, 设置不正确, 接收者会收到乱码
mm.Body = "<font color="red">邮件测试,呵呵</font>";
//邮件正文
mm.Attachments.Add( new Attachment( @"d:a.doc", System.Net.Mime.MediaTypeNames.Application.Rtf ) );
//添加附件,第二个参数,表示附件的文件类型,可以不用指定
//可以添加多个附件
mm.Attachments.Add( new Attachment( @"d:b.doc") );
smtp.Send( mm ); //发送邮件,如果不返回异常, 则大功告成了。

OH, 卖糕的,终于写完了

[转载]减少ASP.NET运行时的工作

mikel阅读(954)

[转载]减少ASP.NET运行时的工作 – 无风 – 博客园.

在底层,.NET和ASP.NET基础结构为了让开发的工作更轻松,做了大量的基础工作。虽然这个体系结构作为一个黑盒很不错,但有时了解一切如故无缝地 运行的实现细节也是很有益的,可以有效地使用运行时获得性能上的优势。

1、视图状态的优化

可以从不同的粒度控制网站的视图状态:在web.config设置EnableViewState为false禁用所有页面的 ViewState,Page的EnableViewState属性继承web.config的设置,如果要在单独的页面可以设置 EnableViewState。另外可以单独设置控件的EnableViewState。

web.config: <pages enableViewState=“false”/>
page:<%@ Page Language=”C#” CodeBehind=”Default.aspx.cs” Inherits=”Default” EnableViewState=”false”%>
控件:<asp:Label ID=“lbInfo” runat=“server” EnableViewState=“false” />

2、尽量减少服务器控件的数量

页面上服务器端的控件应该保持最低的数量上,服务器端的控件不仅生成视图状态,它们也需要额外的运行时处理来绑定到成员变量,呈现,然后卸载。能不用服务 器控件的尽量不用。

3、会话状态的考虑

在创建一个Web应用程序是一个重大的问题就是页面请求直接维护状态,因为http请求时无状态的。例如,知道客户在购物车中放置了什么产品。有很多机制 可以配合使用以取得状态的持久性:cookies,视图状态、QueryString,Session。

ASP.NET提供三种不同的会话状态提供者(In-Proc、状态服务器、数据库),极大地提高了会话功能。进程间会话状态提供者是这三者中最快的,状 态服务器虽然不如进程间的快,但可以在多个服务器器直接共享会话状态。数据库存储时最健壮的但最慢。

无论使用哪种会话状态提供者,都不要滥用会话功能,存储大量的数据或对象,“大量”的定义将取决于需要支持的用户的数量和应用程序的硬件。如果在有些页面 不使用Session可以禁用页面的Session,设置EnableSessionState为False,也可以稍稍减少页面出来的开销。

4、把异常减到最少

异常是通知程序执行期间发生的错误的一个非常有用的机制。和返回代码不同,程序不能忽略异常——它们将会改变程序的执行流。在性能和内存都很关键的应用程 序中经常避开以免额外的开销。在.NET运行时的托管环境中,用try和catch设置异常处理代码只对性能有可以忽略不计的影响,但如果有抛出异常性能 的损失就比较大。

5、使用早期绑定

前期绑定是在编译的时候就确定了要绑定的数据,前期绑定如果失败,会在编译时报编译错误,可以避免在运行时编译错误。而后期绑定是在运行的时候才填充数 据,后期绑定失败只有在运行时的时候才发生。

[转载]ASP.NET数据和事件回发机制

mikel阅读(1058)

[转载]数据和事件回发机制 – 清风飘过 – 博客园.

其实这块知识在ASP.NET 页面揭秘之页面生命周期都有提到过,但是并没有明确提出这个概念,在ASP.NET中通过视图机制实现两次页面请求之间的关联,视图区域信息存储 在页面上的一个隐藏字段,里面存储每次需要视图机制保存的一些信息,每次提交时,都会以“客户端ßà服务器端”形式来回一次,当处理完成后,最后会以处理 后的新结果作为新的视图信息存储到页面的隐藏字段,并与页面一起返回到客户端。

数据回发机制就是完成处理视图信息的功能。数据回发具体过程为,服务器端控件实现了IPostBackDataHandler接口,则当客户端提交 后,就会有机会利用IPostBackDataHandler接口的LoadPostData方法,在该方法内部处理子控件的新旧值逻辑,这时视图信息数 据以一个集合对象形式作为LoadPostData参数,并可以决定是否触发控件值变化后的事件。

IPostBackDataHandler接口包含LoadPostData(string postDataKey,NameValueCollection postCollection)和RaisePostDataChangeEvent()两个方法。LoadPostData用来检测提交给服务器数据, 根据控件状态数据和回发数据是否发生更改而判断是否调用RaisePostDataChangeEvent方法,如果为true,.NET Fromewrok会自动调用RaisePostDataChangeEvent方法,就是将新值和旧值比较,如果不一样就需要数据回发了。

事件回发

要控件捕获回发事件,则它必须实现IPostBackEventHandler接口。此接口可以使服务器控件上触发的事件响应来自客户端的回发。该 接口包含一个RaisePostBackEvent(string eventArgument)方法。

这里引用事 件揭秘里出现过的一个例子

//声明一个委托 [SerializableAttribute] [ComVisibleAttribute(true)] public delegate void EventHandler (Object sender,EventArgs e) [DefaultEvent("Click")] [toolboxData("<{0}:PostBackEventControl runat=server></{0}:PostBackEventControl>")] public class PostBackEventControl:Control,IPostBackEventHandler { //事件名称 编译器其实这里实行3个构造 public event EventHanlder Click; //定义一个负责引发事件的方法来通知已订阅事件的对象事件已经发生 protected virtual void OnClick(EventArgs e) { if(Click!=null) { //是通知订阅对象(其实这里就是调用了服务器一个方法) Click(this.e); } } //触发事件 public void RaisePostBackEvent(string eventArgument) { //因为这里没有额外的信息传递所以赋空值 OnClick(EventArgs.Empty); } Protected overrrid void Render(HtmlTextWriter output) { output.Write("<INPUT TYPE=submit name="+this.UniqueID+"Value='单击我'/>"); } }

在来看看该控件,按钮类型为:submit,其本身就可以提交事件到服务器,但是该控件还是不能捕捉到该按钮事件。它必须具备两个条件1.继承 IPostBackEventHandler接口并实现接口中的方法。2。name值设置为 UniqueID。

“[DefaultEvent(“Click”)]”的功能是定义Click事件为默认事件。定义了默认事件,在设计器中双击控件就会从.aspx 页切到.cs页,并可以自动注册默认事件

将事件与处理事件的方法相关联

This.button1.Clcik+=new EventHandler(Button1_Click);

这里可以参考委 托揭秘

将对于不引起回发的HTML元素启动回发可以有以下几种方法

1.使用GetPostBackEventReference和GetPostBackClientHyperlink

将上面代码中TYPE=submit修改为button那么此按钮就不在具有回发到服务器的功能。那么此时如果使用 GetPostBackEventReference就可以回发到服务器了。

这里介绍其中一个重载方法

public String GetPostBackEventReference(Control control,String argument)
前一个参数为一个控件,后一个可以是附加信息

修正以后给出代码

Output.Write(“<INPUT name=”+this.UniqueID+”Value=’单击我’ onclick=”+Page.ClientScript.GetPostBackEventReference(this,””));

这里ClientScript暂不做介绍。

从上面最终输出地HTML代码可以看到多了这么一个_doPostBack方法. eventTarget就是GetPostBackEventReference的第一参数,这里指当前控件,eventArgument就是 GetPostBackEventReference的第二参数,为空。注意到最后提交窗体 “theForm.submit();”就实现了我们想要的回发。

function __doPostBack(eventTarget, eventArgument) {

if (!theForm.onsubmit || (theForm.onsubmit() != false)) {

theForm.__EVENTTARGET.value = eventTarget;

theForm.__EVENTARGUMENT.value = eventArgument;

theForm.submit();

}

2.GetPostBackClientHyperlink

Output.Write(“<a herf=”+ Page.ClientScript. GetPostBackClientHyperlink (this,””)+”>点击我</a>”);

也上产生与上面相同的效应。

如果我们要把服务器某些控件(必须实现IPostBackDataHandler接口)处理回发,那么可以通过 Page.RegisterRequiresPostBack实现。例

TextBox txt=new TextBox();

Protect void Page_PreRender(object sender,EventArgs e)

{

This.Page. RegisterRequiresPostBack(txt);

}

[转载]基于stratus +flex+MySQL的简易在线随机视频聊天室的开发

mikel阅读(995)

[转载]基于stratus +flex+MySQL的简易在线随机视频聊天室的开发 – 疯狂的八神庵 – 博客园.

前段时间听说flashplayer已经开始支持p2p了,对这块非常感兴趣于是开始玩flex,一玩下来不可自拔。用Stratus搭建p2p环 境如此简单,双方只需要能连上Stratus服务器就能直接进行语音视频的聊天,不需要任何客户端。Adobe还真是NB,呵呵出了这么XX的东东,前途 无可限量啊。

闲话少说,这几天小搞了下,用MySQL和flex弄了个简易随机视频聊天室,和有共同爱好的各位一起分享下。高手可以直接飞过哈,欢迎提出意见, 毕竟刚玩没多久,问题很多。

下面正式开始,程序主体主要分为3大块:

一、p2p语音视频功能模块

这个模块网上有很多教程了,Adobe官方的那个Sample就很 好。我就是以此为基础进行开发的。可能有些朋友还不是很了解,为了每个人都能搞清楚,下面针对代码详细地进行下介绍。

由于是p2p模式,每个用户既是呼叫者又是被呼叫者。更具体点,举个例子有两个人A和B打电话。A呼叫B,此时A是呼叫者,B是被呼叫者。反之B是 呼叫者,A是被呼叫者。因此在每个p2p模块中必须要有呼叫者和被呼叫者两个部分。

被呼叫部分:

001 //被呼叫 者发布监听流,以便呼叫者连接
002 listenStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
003 listenStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
004 listenStream.publish(username);
005 //呼叫者一旦订阅上面的监听 流,就会触发onPeerConnect事件
006 var c:Object =new Object();
007 c.onPeerConnect = function(caller:NetStream):Boolean
008 {
009 if(callState == CallReady)
010 {
011
012 //接受呼叫者发布的视频语音流
013 callState = CallRinging;
014 idManager.change(callState);
015 busyState = busyOn;
016 incomingStream = new NetStream(netConnection,caller.farID);
017 incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
018 video =new Video();
019 video.attachNetStream(incomingStream);
020 remoteVideoDisplay.addChild(video);
021 incomingStream.play("caller");
022
023 var st:SoundTransform = new SoundTransform(speakerVolumeSlider.value);
024 incomingStream.soundTransform = st;
025 //处理呼叫的方法
026 var i:Object = new Object();
027 i.onIncomingCall = function(caller:String):void
028 {
029 if(callState != CallRinging)
030 {
031 txtInfo.text += "onIncomingCall: Wrong call state: " + callState + "\n";
032 return;
033 }
034 send_bn.enabled=true;
035 txtInfo.text +=  caller + "已经成功与您连接上\n";
036 partnername = caller;
037
038 //outgoingStream.send("onCallAccepted", username);
039 callState = CallEstablished;
040 //idManager.change(callState);
041 }
042 i.onIm = function(caller:String,text:String):void
043 {
044 txtMessage.text += caller+": "+text+"\n";
045 }
046 i.onDisconnected = function(caller:String):void
047 {
048 txtInfo.text += caller+"和你断开连接\n";
049 send_bn.enabled=false;
050 stop();
051 }
052 incomingStream.client = i;
053 //对呼叫者发布自己的语音视频流
054 outgoingStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
055 outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,callee_outgoingStreamHandler);
056 outgoingStream.attachCamera(camera);
057 outgoingStream.attachAudio(mic);
058 outgoingStream.publish("callee");
059
060 return true;
061 }
062 txtInfo.text += "onPeerConnect: all rejected due to state: " + callState + "\n";
063
064 return false;
065 }
066 listenStream.client = c;
067
068
069
070 callState = CallReady;
071
072 呼叫部分:
073
074 callState = CallCalling;
075 idManager.change(callState);
076 busyState = busyOn;
077 //播放被呼叫者的监听流
078 controlStream = new NetStream(netConnection,farPeerID);
079 controlStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
080 controlStream.play(partnername);
081 //对被呼叫者发布自己的视频语音流
082 outgoingStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
083 outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,caller_outgoingStreamHandler);
084 outgoingStream.attachCamera(camera);
085 outgoingStream.attachAudio(mic);
086 outgoingStream.publish("caller");
087 txtInfo.text += "正在与"+partnername+"建立连接\n";
088 //播放被呼叫者的视频语音流
089 incomingStream = new NetStream(netConnection,farPeerID);
090 incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
091 video =new Video();
092 video.attachNetStream(incomingStream);
093 remoteVideoDisplay.addChild(video);
094 incomingStream.play("callee");
095
096 var st:SoundTransform = new SoundTransform(speakerVolumeSlider.value);
097 incomingStream.soundTransform = st;
098 //处理呼叫的方法
099 var i:Object = new Object();
100 i.onIm = function(callee:String,text:String):void
101 {
102 txtMessage.text += callee+": "+text+"\n";
103 }
104 i.onCallAccepted = function(callee:String):void
105 {
106 if (callState != CallCalling)
107 {
108 txtInfo.text += "连接失 败";
109 return;
110 }
111 send_bn.enabled=true;
112 txtInfo.text += callee+"已经成功与您连接上\n";
113 callState = CallEstablished;
114 //idManager.change(callState);
115 }
116 i.onDisconnected = function(callee:String):void
117 {
118 txtInfo.text += callee+"和你断开连接\n";
119 send_bn.enabled=false;
120 stop();
121 }
122 incomingStream.client = i;

二、用户数据库及webservice接口

这个项目我使用MySQL进行用户数据库设计,用户端产生相应行为后(如登录,下线,聊天,空闲等)会在数据库中更新自己的状态,其他用户根据查询得到的 结果进行操作。数据库结构见下图:

用户 名           peerID        更新时间              在线状态    聊天状态

该项目只要进行简单的数据库操作就可以了,所以我使用了HTTPService组件和一个简单的php网页进行与数据库的交互。

HTTPService组件位于mx.rpc.http包下,当调用 HTTPService 对象的 send() 方法时,将发出对指定 URL 的 HTTP 请求,并且返回 HTTP 响应。可以选择向指定 URL 传递参数。项目中通过HTTPService组件传递用户端的参数到php网页,由php根据相应的参数操作数据库,返回相应的结果交由用户端执行。

HTTPService组件部分:

1 mHttpService = new HTTPService();
2 mHttpService.url = mWebServiceUrl;
01 //添加对结果时间的侦听
02 mHttpService.addEventListener("result", httpResult);
03 mHttpService.addEventListener("fault", httpFault);
04 //实例化request请求对象并发送
05 var request:Object = new Object();
06 var now:Date = new Date();
07 request.time = now.getTime();
08 request.username = user;
09 request.identity = id;
10 request.online = x;
11 request.busy = x;
12 mHttpService.cancel();
13 mHttpService.send(request);

php网页部分:

01 <?php
02 $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
03 $output .= "<result>";
04 if ( isset($_GET["username"]) || isset($_GET["friends"]) || isset($_GET["fetch"]))
05 {
06 define( "DATABASE_SERVER", "localhost" );
07 define( "DATABASE_USERNAME", "root" );
08 define( "DATABASE_PASSWORD", "root");
09 define( "DATABASE_NAME", "root" );
10 $mysql = mysql_connect( DATABASE_SERVER,
11 DATABASE_USERNAME,
12 DATABASE_PASSWORD )
13 or die( mysql_error() );
14 mysql_select_db( DATABASE_NAME );
15 }
16 //注册更新部分
17 if ( isset($_GET[ "username" ]) )
18 {
19
20 $user   = mysql_real_escape_string( $_GET[ "username" ] );
21 $identity   = mysql_real_escape_string( $_GET[ "identity" ] );
22 $online = mysql_real_escape_string( $_GET[ "online" ] );
23 $busy = mysql_real_escape_string( $_GET[ "busy" ] );
24
25 $query = "SELECT * FROM registrations WHERE m_username='$user' ;";
26 $result = mysql_query( $query );
27
28 $isNew = ($result && mysql_num_rows($result) == 0);
29 if( $isNew ) {
30 $query = "INSERT INTO registrations SET ";
31 $query .= "m_username='$user', m_identity='$identity', m_updatetime=now(), m_online='$online', m_busy='$busy';";
32 } else {
33 $query = "UPDATE registrations SET ";
34 $query .= " m_identity='$identity', m_updatetime=now(), m_online='$online', m_busy='$busy' where m_username='$user';";
35 }
36 $result = mysql_query( $query );
37 if( $isNew )
38 $output .= "<update>true</update>";
39 else
40 $output .= "<update>false</update>";
41 }
01 //查找用户部分
02 if ( isset($_GET[ "friends" ]) )
03 {
04 $friends    = mysql_real_escape_string( $_GET[ "friends" ] );
05 $output .= "<friend><user>$friends</user>";
06 $query = "select * from registrations where m_username = '$friends' and TIMEDIFF(now(),m_updatetime) >0 ;";
07
08 $result = mysql_query( $query );
09 if( $result ) {
10 while( $item = mysql_fetch_assoc( $result ) ) {
11 $output .= "<identity>".$item["m_identity"]."</identity>";
12 }
13 }
14 $output .= "</friend>";
15 }
01 //获取返回结果
02 if ( isset( $_GET["fetch"]) )
03 {
04 $time = mysql_real_escape_string( $_GET[ "time" ] );
05 $query = "select m_number from registrations order by m_username asc ;" ;
06
07 $result =mysql_query($query);
08 if( $result )
09 {
10 $num = mysql_num_rows($result);
11 $output .= "<fetch><num>".$num."</num>";
12 }
13
14 $query = "select m_username from registrations where m_online ='1' and m_busy = '0' order by m_username asc ;";
15 $result = mysql_query($query);
16
17 if( $result )
18 {
19 $onlinenum = mysql_num_rows($result);
20 $output .="<onlinenum>".$onlinenum."</onlinenum><users>";
21 $i = 1;
22 while ($row = mysql_fetch_row($result))
23 {
24 $output .= "<user".$i.">".$row[0]."</user".$i.">";
25 $i++;
26 }
27 $output .= "</users></fetch>";";
28 }
29 }
30
31
32
33
34 $output .= "</result>";
35
36 header( "Content-Type: text/xml" );
37 echo $output;
38 ?>

三、主程序部分

主程序界面实时显示在线用户状态,可以双击列表空闲用户进行聊天 也可以点击随机按钮随机选取在线空闲用户进行聊天。

退出时记得点击离开正常退出哈。

Demo:http://p2pchat.online.cm/

这个免费空间貌似有广告,不点它就是了

有问题欢迎交流-_-