蛙蛙推荐:蛙蛙浏览器
蛙蛙推荐:蛙蛙浏览器
摘要:google推出了自己的网页浏览器,现在web浏览器的竞争更激烈了,各有各的用户群。其实有另一个领域没有多少竞争,那就是应用程序浏览器,今天给大家演示的蛙蛙浏览器,不仅可以浏览网页,还可以浏览.net应用程序,通信方面你可以使用webservice,remoting,本地存储你可以使用用户隔离存储区,本地数据库,要想播放多媒体也可以,总之比一般网页浏览器NB。
【概述】
用户刚安装完蛙蛙浏览器后什么都不能用,就像一个清空了本地缓存的IE。用户要想使用某些应用,可以通过一个web服务来获取应用描述,这个web服务是一个应用目录列表,相当于互联网的站点导航网站,你对哪个应用感兴趣就可以打开这个应用,相当于用网页浏览器打开网页。当然用户可以定制自己的应用列表,不必每次都去应用目录去加载所有的应用列表。
应用目录会提供应用程序集的地址、类型等其它一些信息,这些程序集是保存在远程服务器上的,地址类似于http://www.fetionmm.com/dllcache/application.dll这样的地址,.net的反射功能可以从远程加载程序集,并缓存到本地的C:\WINDOWS\assembly\download目录下,默认从网络上下载缓存的程序集权限是很低的,可以用代码安全工具设置来完全信任某一公钥或者某一网站上下载的程序集,这样你浏览的应用就可以访问本地网络,打印机等资源了,关于CAS的概念及配置,大家参考MSDN。然后网络上下载的程序集,如果远程的程序集版本更新,应该会自动加载缓存到本地,所以自动更新的问题也解决了。
远程的程序集其实主要是一些用户控件,而应用程序浏览器保留了一些面板和菜单占位符让这些应用来使用,子应用要和浏览器交互,所以子应用和浏览器应该各有一个接口,能互相调用,比如说子应用要在应用浏览器的菜单项里添加合并自己应用的特有菜单,要改变浏览器的标题,状态栏,要通过应用浏览器的统一异常处理程序来处理子应用内的异常等。这就相当于javascript能访问浏览器的BOM对象一样,我们的浏览器也有一些API供子应用来调用。还有就是浏览器也应该能访问子应用的一个公共接口,比如说在在应用框架切换应用的时候通知当前的 应用保存自己的应用状态等。
我们是接着上一篇帖子来做这个应用浏览器的,我们会在获取应用列表的时候填充“转到”菜单的下拉菜单项,并动态添加左下角的手风琴功能菜单条,我们会在子应用加载的时候动态合并菜单,并且在工具栏的新建按钮里添加子应用的新建类型菜单,并在手风琴工具条的上方添加子应用的左侧功能导航条,就像outlook在邮件应用的时候左边是个邮件文件夹的树,在联系人应用的时候左边是一些联系人视图切换的按钮条。我们也一样,不同的应用有不同的功能导航条。
另外MDI风格的桌面应用已经过时,现在都是全窗口应用,比如word,你新建一个word文档,新的文档在一个独立的主窗口里,有所有word的菜单,而不是在一个MDI的子窗口里,我们也顺应时代潮流,比如本例,有一个“我的RSS订阅”和“我的收藏”应用,你完全可以在主窗口里打开RSS订阅来看RSS新闻,然后新建窗口里来浏览“我的收藏”应用,来看自己收藏的网页。
【项目介绍】
WawaAppCommon:里面有应用浏览器和子应用的的接口定义以及一些XML序列化和图片序列化的帮助类。
WawaAppWebService:这是个web服务,用来给用户进行身份验证和返回应用目录列表,前者目前是假的,直接返回一个guid字符串作为登录令牌。
WawaAppClient:这是应用程序浏览器,包括一个登录窗体,主浏览器窗体和关于窗体。
WawaApplications:示例了两个子应用,一个是RSS订阅,一个是我的收藏。
【web服务】
public class AppFunctionality
{
public string Name;
public string MenuText;
public string ToolbarIconString;
public string TooltipText;
public string AssemlyUrl;
public string TypeName;
}
[WebMethod]
public AppFunctionality[] GetFunctionalityCatalog(string token)
{
List<AppFunctionality> list = new List<AppFunctionality>();
AppFunctionality app = new AppFunctionality();
app.Name = "我的RSS订阅";
app.MenuText = "我的RSS订阅";
app.ToolbarIconString = ImageSerializationHelper.Image2String(Resource.Folder24);
app.TooltipText = "可以订阅RSS新闻";
app.AssemlyUrl = @"E:\huhao\workfloder\src\WawaAppBrowser\WawaApplications\bin\Debug\WawaApplications.dll";
app.TypeName = "WawaSoft.WawaApp.Applications.RssReaderApp";
list.Add(app);
app = new AppFunctionality();
app.Name = "我的收藏";
app.MenuText = "我的收藏";
app.ToolbarIconString = ImageSerializationHelper.Image2String(Resource.Tasks24);
app.TooltipText = "可以收藏网页链接";
app.AssemlyUrl = @"E:\huhao\workfloder\src\WawaAppBrowser\WawaApplications\bin\Debug\WawaApplications.dll";
app.TypeName = "WawaSoft.WawaApp.Applications.UserControl1";
list.Add(app);
return list.ToArray();
}
客户端通过调用以上的web服务来获取应用列表,然后才加载到“转到”表菜的子菜单单和手风琴功能条。
【接口定义】
public interface IBrowser
{
string Caption { get; set;}
string Status { get; set;}
void HandleException(Exception ex);
DialogResult Alert(string message);
DialogResult Alert(string message, MessageBoxIcon icon);
DialogResult Alert(string message, MessageBoxButtons buttons, MessageBoxIcon icon);
void AddMenuItem(ISubApplication subApplication,string path, ToolStripMenuItem item);
void AddNewMenuItem(string Text,Image image, EventHandler handler);
void SetLeftControl(Control leftControl);
}
public interface ISubApplication
{
void SetBrowser(IBrowser browser);
}
【浏览器接口实现】
#region IBrowser 成员
public string Caption
{
get { return _caption; }
set
{
_caption = value;
Text = value != "" ? string.Format("{0}-{1}", _caption, BaseCaption) : BaseCaption;
}
}
public string Status
{
get
{
return lblStatus.Text;
}
set { lblStatus.Text = value != "" ? value : "就绪"; }
}
public void HandleException(Exception ex)
{
Alert(string.Format("{0}-{1}:{2}",
ex.GetType(), ex.Message, ex.StackTrace));
}
public DialogResult Alert(string message)
{
return Alert(message, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
public DialogResult Alert(string message, MessageBoxIcon icon)
{
return Alert(message, MessageBoxButtons.OK, icon);
}
public DialogResult Alert(string message, MessageBoxButtons buttons, MessageBoxIcon icon)
{
return MessageBox.Show(this, message, BaseCaption, buttons, icon);
}
public void AddMenuItem(ISubApplication subApplication, string path, ToolStripMenuItem item)
{
string lookForPath = path.Replace("&", "");
ToolStripMenuItem addTo = null;
foreach (ToolStripMenuItem topLevel in menuStrip1.Items)
{
if (string.Compare(topLevel.Text.Replace("&", ""), lookForPath) == 0)
{
addTo = topLevel;
break;
}
}
if (addTo == null)
{
addTo = new ToolStripMenuItem(path);
menuStrip1.Items.Insert(5, addTo); //添加在工具菜单后面
_subApplicationMenus.Add(addTo);
}
else
{
ToolStripItem toolStripItem = addTo.DropDownItems[addTo.DropDownItems.Count - 1];
bool needSep = !(_subApplicationMenus.Contains(toolStripItem));
if (needSep)
{
ToolStripSeparator sep = new ToolStripSeparator();
addTo.DropDownItems.Add(sep);
_subApplicationMenus.Add(sep);
}
}
addTo.DropDownItems.Add(item);
_subApplicationMenus.Add(item);
}
public void AddNewMenuItem(string text,Image image, EventHandler handler)
{
ToolStripMenuItem toolStripMenuItem =
new ToolStripMenuItem(text,image);
toolStripMenuItem.Click += handler;
menubtnNew.Visible = true;
menubtnNew.DropDownItems.Add(toolStripMenuItem);
toolStripMenuItem =
new ToolStripMenuItem(text,image);
toolStripMenuItem.Click += handler;
tsbNew.DropDownItems.Add(toolStripMenuItem);
}
public void SetLeftControl(Control leftControl)
{
leftControl.Dock = DockStyle.Fill;
menuStrip1.Focus();
splitContainer2.Panel1.Controls.Add(leftControl);
}
#endregion
【子应用接口实现示例】
#region ISubApplication 成员
public void SetBrowser(IBrowser browser)
{
_browser = browser;
ToolStripMenuItem item = new ToolStripMenuItem("新建订阅");
item.Click += delegate { AddRss(); };
Browser.AddMenuItem(this, "RSS订阅", item);
browser.AddNewMenuItem("订阅", Resource.Shortcut24, delegate { AddRss(); });
item = new ToolStripMenuItem("更新订阅");
item.Click += delegate { RefreshRss(); };
Browser.AddMenuItem(this, "操作", item);
LeftRssReaderPanel leftRssReaderPanel = new LeftRssReaderPanel(this);
Browser.SetLeftControl(leftRssReaderPanel);
_leftRssPanel = leftRssReaderPanel;
}
#endregion
其它主要的代码都在MainForm里面,比如动态加载控件,如何防止获取焦点的控件的父控件的控件集清空早晨整个窗体无法获取焦点的问题,如何在合适的地方加载合并菜单,如何添加菜单分割条,如果根据应用个数动态设置手风琴的toolstrip的高度等。
【截图如下】
【小节】
微软给咱们的只是工具,得用好才能赚到钱,winform,webform微软都给你了,就是给你多一些选择的机会,至于用户的选择,有时候要靠用户教育的,如果整个企业都是基于微软的管理,那么做一个应用程序浏览器,利用域脚本分发给每一个员工是很简单的事情,以后程序更新可以用到.net的Clickonece技术来达到自动部署,根本不费什么力,企业内的所有应用不可能都弄成web的,比如本地打印,尤其是套打,你用web来实现很麻烦的,定位你可以用css精确定位,可你能控制的了浏览器的打印页边距吗,那个得修改注册表才可以,你浏览器又没有相关的API去修改windows的注册表,winform做这个就很方便了,当然你web上弄一些报表控件也能实现,但那就不是web了。再说了windows支持无人值守安装,在无人值守安装里可以默认装一些初始化的员工软件的,所以我的结论是winform在企业内还是很有优势的,关键得大家去推广。
下载地址如下:
https://files.cnblogs.com/onlytiancai/WawaAppBrowser.7z
测试的时候注意修改functionality.xml文件的程序集的路径,另外app.config文件里的UseLocalAppCategory配置表示使用functionality.xml文件来加载应用列表,如果设置为false,必须得设置一个可用的获取应用目录的web服务地址,也在app.config里设置。
【参考】
windows企业应用高级编程