看过前面的文章的人,看到这个标题后可能觉得很眼熟,是的,我前面也写过Flash与C#程序进行交互的例子,不过,前面的例子注重于开发网络应用程序,在没有服务器的机器上面,就没有什么用了。今天我的这个例子,是让Flash嵌入C#窗体应用程序中,然后双方进行通信。
最近一段时间,项目经理想让我用Flash做一个读取数据库的程序出来,又不能使用服务器,而我前面所讲的那些技术,除了直接读取本地文件的那个例子以外,基本都需要通过服务器来实现交互的。这个和项目经理的要求不符合,去掉,而读取本地文件,项目经理也不同意。悲剧……
于是我就在网上寻找Flash直接读取数据库的例子,果然,还真被我找到一个,不过这个是用Flash里面的Socket写的,这个例子,在本地直接打开时很正常,但是一旦嵌入网页或者其他程序,就会什么也不显示,弄的我很郁闷,最后我上网查了一些资料,才知道这个是由于Flash的安全沙箱造成的,因此,这个方法也不符合要求,虽然项目经理就喜欢这个方法,但是被我否决了。
虽然否决了,但是这个项目不能就这么放着啊,不甘心半途而废,所以这两天就在网上找了很多资料,最终,被我找出来一个可以直接通信的方法,就是ExternalInterface,这个Adobe公司提出的和外部的通信接口,在网上很多资料里面都是和JS进行网页通讯的,所以我一直没有注意这个东西,直接导致我转了很大的一个圈,浪费时间和精力。所以今天就写一下我的心得吧,希望能帮助到各位!
ExternalInterface 类允许 Flash 播放器以异步的方式与宿主程序进行通信,宿主程序一般指的是Web 浏览器(也可以是其他应用程序,比如C#窗体应用程序)。
ExternalInterface 支持以下浏览器:
Internet Explorer 5.0+ (Windows)
Netscape 8.0+ (Windows and Mac OS X)
Mozilla 1.7.5+ (Windows and Mac OS X)
Firefox 1.0+ (Windows and Mac OS X)
Safari 1.3+ (Mac OS X)
ActionScript调用JavaScript(C#,Java,PHP等)函数,可使用ExternalInterface.call()。该方法采用异步调用JS(C#,Java,PHP等)函数机制。用法:ExternalInterface.call(JS方法名:string[,参数1:*,参数2:*……])。如果JS函数有返回值,可用变量存储:如var str:String = ExternalInterface.call(“jsFunction”);
可以用ExternalInterface.available查看浏览器是否支持,若不支持,可使用navigateToURL()方法。
JavaScript(C#,Java,PHP等)调用ActionScript函数:使用ExternalInterface.addCallback()注册AS函数,然后在JS端进行调用。用法:ExternalInterface.addCallback(被调用函数对JS的别名:string,被调用函数:function)。
如:ExternalInterface.addCallback(“helloWordAS”,helloWord);
上面是对这个函数的详细解释,具体的还有一些属性可以在AS3帮助文档中找到。看完上面的,我们就可以开始Flash和C#窗体应用程序的通信之旅了。
今天我的这个例子其实是上面项目经理要求我做的程序的简化版。要求是从数据库中读取数据,然后将这些数据传递给Flash程序,由Flash程序进行解析后根据要求将一些数据和图像显示在舞台上。具体的数据和需要显示的图像根据需求确定。
首先,我们来建立一个Flash应用程序。
打开Flash CS4,新建一个Flash文件(Action Script 3.0),取名为showPic.fla,保存。然后,按Ctrl+N,新建一个Action Script文件,保存在刚刚Flash文件保存的同级目录,取名为ShowPic.as。接着,在ShowPic.as里面写如下代码:
package{//导入类
import flash.display.MovieClip;import flash.external.ExternalInterface; //这个是和C#程序通讯的关键
import flash.text.*;import flash.utils.setInterval;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.display.Bitmap;public class ShowPic extends MovieClip{//这里我用嵌入的方式直接将图片嵌入到Flash中,图方便
//source是路径,变量为类类型
[Embed(source = "computer.png")]
private var Picture:Class;
//显示图像的对象
private var pic:Bitmap;
public function ShowPic()
{init();}private function init():void{//将舞台左上对齐,不缩放
stage.align = StageAlign.TOP_LEFT;stage.scaleMode = StageScaleMode.NO_SCALE;//这个是给C#传值,可以没有
ExternalInterface.call("toNet");
//这个是关键:获取C#传递过来的值
ExternalInterface.addCallback("sendMsg",sendMsg);
}//处理C#传递过来的值 参数个数和下面的C#传递的值的个数必须一致,不然C#会报错
private function sendMsg(s1:String,s2:String,s3:String):void{//查看舞台上面是否有对象,如果有,就清除
while (this.numChildren>0){this.removeChildAt(0);
}//这里我没有做什么优化,随便坐的一个程序
//这个对象是数据库中的记录条数
var index:int = int(s1);//这个是C#窗体的宽和高
var strArr:Array = s2.split(",");
var w = strArr[0];var h = strArr[1];//每个图像需要显示的大小
var mean:Number = w / index;//这个数据里面存储的是传递过来的数据,这里传回来的是姓名
var nameArr:Array = s3.split(",");
//新建图片对象并设置相关属性
pic=new Picture();
pic.x = stage.stageWidth / 2 - pic.width / 2;pic.y = stage.stageHeight - pic.height;//将图片添加到舞台上面
addChild(pic);//动态文本,显示需要显示在舞台上面的数据,这里显示的IP
var textField:TextField=new TextField();
textField.x = pic.x + 20;textField.y = pic.y + 20;textField.textColor = 0xffffff;textField.autoSize = TextFieldAutoSize.CENTER;textField.text = "127.0.0.1";
//添加到舞台上
addChild(textField);graphics.clear();//画线,设置样式
graphics.lineStyle(1,0,1);//循环:每循环一次,个数减1
while (index>0)
{index--;//新建图片对象,设置属性并显示
var pic1:Bitmap=new Picture();
//这个是让图片均匀分布,好看一点
pic1.x = mean * index;pic1.y = 70;pic1.width = mean - 20;pic1.height = mean - 23;addChild(pic1);//动态文本,显示需要显示在舞台上面的数据,这里显示的传回来的姓名
var tf:TextField=new TextField();
tf.x = pic1.x - 20;tf.y = pic1.y + pic1.height/2-10;tf.textColor = 0xffffff;tf.autoSize = TextFieldAutoSize.CENTER;tf.text = nameArr[index];addChild(tf);//开始画线,设置起点和终点
graphics.moveTo(pic.x+pic.width/2,pic.y+6);graphics.lineTo((pic1.x+pic1.width/2),(pic1.y+pic1.height-10));}}}}
然后切换到showPic.fla,在属性窗口中,找到类属性,然后讲上面写好的As类输入进去,如下图:
设置好以后,按Ctrl+Enter测试,如果没有报错,则证明程序没有问题,虽然FLash上面空白一片,什么也没有,那是因为还没有接收到C#传递过来的参数。
上面的写完了,接下来要写的就是C#窗体应用程序了。
首先,打开VS2008,新建一个C#窗体应用程序,取名为FlashTest,然后确定保存。在编辑界面,我们打开工具箱,在上面点击右键,然后单击选择项…,打开组件窗口,选择COM组件,在里面选择Flash组件,如下图:
然后单击确定,你就可以在你的工具箱里面看到如下组件了
将Shockwave Flash Object 组件拖到窗体中,并进行设置,在属性窗口中,Movie是你的SWF文件的路径
然后点击事件按钮,选择FlashCall事件,这个事件是用来接收Flash传递过来的数据的,在这个例子中,由于不需要,所以就没有设置,大家可以按照需求去确定要不要使用,这个传递过来的是XML数据,需要进行解析的。
上面的属性设置好以后,再拖入一个Timer控件上去,这个控件的作用就是每隔一段时间获取一次数据库中的数据,然后传递给Flash程序,以更新Flash中的显示内容。
做好了这些,我们就直接右键,查看代码,在里面输入如下代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
namespace FlashTest
{public partial class FlashTest : Form{public FlashTest()
{InitializeComponent();}private void FlashTest_Load(object sender, EventArgs e){//传值给Flash程序
callFlash();//开启定时器,时间间隔我这里设置的是10秒
this.timer1.Start();
}/// <summary>
/// 获取数据库中的相关数据
/// </summary>
/// <returns></returns>
private string[] getContent(){string[] arr = new string[2];string name = "";int count = 0;
string conStr = "Data Source=.;Initial Catalog=NetForFlash;Persist Security Info=True;User ID=sa;Password=sa";SqlConnection con = new SqlConnection(conStr);
con.Open();string sql="SELECT NAME FROM USERS";SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader reader = cmd.ExecuteReader();while (reader.Read())
{count++;name = name + reader["NAME"].ToString()+",";}reader.Close();con.Close();arr[0] = count.ToString();name = name.Substring(0, name.Length - 1);arr[1] = name;return arr;
}private void timer1_Tick(object sender, EventArgs e){callFlash();}/// <summary>
/// 将参数传递给Flash应用程序
/// </summary>
private void callFlash(){//获取数据库中取出的数据
string[] arr = getContent();
int count = int.Parse(arr[0]);string name = arr[1];
//窗体的宽和高
int w = this.Width;int h = this.Height;string str = w + "," + h;//给Flash传值,name为Flash中回调函数名,type为XML,后面的都是参数,个数和Flash中的回调函数一直,不然会出错
axShockwaveFlash1.CallFunction("<invoke name=\"sendMsg\" returntype=\"xml\"><arguments><string>"
+ count + "</string><string>"
+ str + "</string><string>"
+ name + "</string></arguments></invoke>");
}}}
我的数据库设计的很简单,就只有几条数据和几列而已,只是做测试:
做完了这些事情,就可以运行程序了,运行结果如下:
因为有一个定时器存在,所以我们如果在数据库中增加或删除一条数据的话,可以实时更新窗体上面的显示:
写到这里,这个例子已经基本完成了,也基本能够实现我工作中的需要了。
为了完成项目经理交代的任务,我可以说是寻找了很多资料,遇到很多困难,但是大部分都是我在走弯路,但是我没有放弃,我坚信自己一定可以找到方法去完成这个的,而事实上我的确找到了。虽然走了很多弯路,但是在走弯路的过程中,我也对AS3.0更加熟悉,认识的更加深刻了,也许我不走这些弯路,很可能就学不到这些,所以,这弯路走得值得。呵呵,但是在这里我写出来,就是让大家有一个方案,少走一些弯路,毕竟效率还是很重要的。哈哈,相信自己可以,那么,你将一路前行,无所畏惧!