随笔-5  评论-58  文章-0  trackbacks-0
  2009年8月11日

问题描述:
  如果我们做了一个B/S系统,系统需要对数据库进行操作。假定将来要将该B/S转变为C/S系统的话,考虑到数据库安全等原因,客户端无法对数据库直接操作,所以必须采用分布式架构实现界面、业务与数据访问层的分离。比如,我们可以采用WebService的方式。
  这样一来,数据访问层以上的代码就必须修改,因为原本调用数据访问类的代码,现在必须改成对WebService的调用。为解决这个问题,我在项目中采用了以下
方案。
  先上架构图

 
  架构图中的“ClinicService“为业务类,它需要对数据访问类进行调用。
  IClinicDao为数据访问类的接口,ClinicService调用该接口。然后我们可以通过反射的机制对ClinicService中的IClinicDao进行依赖注入(Factory Pattern)。
  ClinicDao为该接口的实现,它最终实现对数据库的操作。如果是B/S程序,ClinicService调用的就是ClinicDao。
  而对于C/S系统,ClinicDao只能部署在服务器端,我们可以通过ClinicWebService.asmx将ClinicDao的接口包装成WebService接口。
  好了,我们再看客户端程序。客户端程序采用.net WebService代理类的方式,自动生成ClinicWebServiceSoapClient。然后让上层程序调用该代理类。
  这样问题就出现了,ClinicService依赖的是接口IClinicDao,而代理类并没有实现该接口。我们无法将ClinicWebServiceSoapClient绑定到ClinicService中。而我的解决方案是:采用Adapter模式,创建一个类ClinicDaoWrap,该类继承 ClinicWebServiceSoapClient,然后再实现接口IClinicDao。这样一来,ClinicDaoWrap即具有ClinicWebServiceSoapClient的方法,也能绑定到ClinicService中了。
  ClinicDaoWrap的代码很简单:
    public class ClinicDaoWrap : ClinicWebServiceSoapClient,IClinicDao
    {
        
public ClinicDaoWrap()
        {
        }
    }



  有了这个架构,我们只要简单地修改配置就能实现B/S到C/S的随意切换了。下面是配置文件:
1.B/S下的配置
  <object id="ClinicService" type="Clinic.Service.ClinicService,Clinic.Service">
    
<property name="_clinicDao">
      
<object type="Clinic.DAL.ClinicDao,Clinic.DAL">
      
</object>
      
<!-- object type="Clinic.ServiceReference.ReferencesWarp.ClinicDaoWrap,Clinic.ServiceReference">
      
</object -->
    
</property>
  
</object>



2.C/S下的配置
  <object id="ClinicService" type="Clinic.Service.ClinicService,Clinic.Service">
    
<property name="_clinicDao">
      
<!-- object type="Clinic.DAL.ClinicDao,Clinic.DAL">
      
</object -->
      
<object type="Clinic.ServiceReference.ReferencesWarp.ClinicDaoWrap,Clinic.ServiceReference">
      
</object>
    
</property>
  
</object>



不知道园子的朋友有没有遇到相同的问题,希望我的方法能帮助大家解决一些实际问题,欢迎大家指正与批评。
posted @ 2009-08-11 12:50 snowwolflibo 阅读(347) 评论(3) 编辑
  2008年12月5日
  .Net Windows Form程序开发较ASP.Net而言,是较麻烦的一件事,原因如下:
  ASP.Net应用是面向ISAPI与W3C编程,核心的功能,IIS与浏览器都给你做了。你的工作差不多就是读写数据库、生成对象、吐Html或javascript而已。总之,这个“舞台”很小。
  而Windows Form程序则不然,它是架设在操作系统上的,“舞台”很大,牵涉的知识点很多。比如:窗体自绘、多线程管理、进程间通信、跨线程资源访问、平台调用、托管与非托管的互操作、内存占用的优化、跨平台、软件升级。下面就将我在实际项目中的收获拿出来与大家分享。
  
  记得清理干净你的对象
  在.Net中,托管资源可以交与GC释放,但非托管资源呢,比如说Bitmap,记得在用完后手动Dispose。
  而static的托管资源,GC也不会释放,因为它的生命周期与进程一致。在编程时,就要注意了,如果某个窗体被某static对象引用着,即使你关掉窗体,也不会释放资源的。所以说,如果你要把一个窗体做成单例模式,记得在窗体关闭时,将单例对象的static对象置null。否则,这个窗体就一直在内存中呆着,直接进程退出。
  此外,对象中的事件也可能会让对象一直被别的对象引用,这样GC也不会释放它。所以在窗体关闭或对象Dispose时,记得将把事件给“-=”了。
  
  当对象被轮“X”时
  如果对象被两个或以上的线程访问,就要考虑并发的情况。常用的方式是lock。说到lock就不得不提“最佳实践”,建议为每个需要被lock的对象建立一个private static 的object对象,然后lock该对象,据说是可以避免死锁。但究其原理,高手们从来都是神秘一笑,反正大家按“最佳实践”来就行。
  
  谁分配,谁释放!--关于互操作时的内存分配
  我的项目是用.Net写的,但其中也调用了一个C++的组件,接口签名如下:
  IntPtr GetCertString()。即.Net向C++获取一个指针地址。
  程序在xp,2003上运行正常,但Vista下却出以下错误:
System.AccessViolationException: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏
  MD,这个问题困扰了我们长时间,最后才发现,是C++的接口方法中分配了一块内存(用于存放一个字符串),然后没有释放(当然不能释放,因为要返回给.Net),结果在返回给.Net时报上面的错。
  仔细分析,我们知道:C++是不会释放这个字符串的,.Net也不会,因为它本来就是C++分配的。所以这就存在“内存泄露”的危险。而Vista对内存安全的要求很严格,在运行时直接给你报异常了。
  所以后来我把接口改成:void GetCertString(StringBuilder s),然后在调用前分配StringBuilder,将该StringBuilder传给C++,C++“搞”完后,返回给.Net,然后由GC来释放StringBuilder。这就做到了“谁分配,谁释放”。
 
  退出程序的麻烦事
  如何彻底退出你的进程?
  Application.Exit?
  Environment.Exit?
  Process.GetCurrentProcess().Kill?
  后两种太暴力。推荐用第一种,但如果程序中有启动其他线程时要注意了,记得将设置Thread.IsBackground=true,否则,这些线程是不会随主线程退出的。
  还有一个比较麻烦的问题,如果在Application.Exit的过程中,又有一个新线程启动,Application.Exit可能就无法终止它了。这个问题也折腾了我很长时间--明明已经退出了,但任务管理器里还有进程名。怎么办哩?
  我的办法就是在Application.Exit后加上System.Environment.Exit(0)。文明关闭搞不死你,就再加个暴力的。就叫“先礼后兵”。
 
  Process.Start的困惑
  我的程序有这么一个逻辑,程序A.exe启动程序B.exe,程序B再启动程序C.exe。结果发现,进程C死活就启动不了。但如果直接双击B,进程C是可以启动的。
  原因是这样的:B与C在同一文件夹,启动时用的是相对路径,也就是直接Process.Start("C.exe")。而A是在另一个文件夹,结果在B的进程中,Process.Start("C.exe")就到A的文件夹找C.exe了。
  后来修改了A的代码,在启动B时指定domain,也就是B运行的上下文,就OK了。
  当然如果改B,将Process.Start("C.exe")改成Process.Start(AppDomain.CurrentDomain.BaseDirectory  +"C.exe")也可以的。
 
 
  最好别用WebService
  由于我们的.Net程序要运行在没有.Net Framework的平台上,所以我们借助了Salamander技术。先说说Salamander是什么。
  它就是将.Net所需的组件给你提炼出来,与你的程序放在一起,然后做一个.Net虚机的引导程序VM.exe。运行VM.exe时,先加载.Net运行时与相关组件,然后再启动你自己的程序。
  其实说白了,它与装了.Net Framework的原理差不多,只不过有Framework的机器是操作系统负责启动.Net Runtime。而Salamander时在运行VM.exe时启动Runtime。
  而.Net WebService是依赖反射技术的(我是把WebService组件反编译后知道的),反射技术需要动态编译(我觉得是的,高手请指正),而编译就得用到csc.exe。
  后面的原理我就不知道了,反正结果就是WebService在没装.Net Framework的XP机器上不好使。
  后来我就回到“原始社会”了,自己用HttpWebRequest做了一个与WebService类似的类。
  搞定了。
 
 
  如何在新线程里创建窗体
  如何在一个新线程里创建一个窗体?看下面的代码:
   public Form1()
        {
            Thread thread = new Thread(new ThreadStart(AAA));
            thread.Start();
            InitializeComponent();
        }
        void AAA()
        {
            Form f = new Form();
            f.Show();
        }
  结果就是,窗体闪一下就没了。改成下面的就没问题:
   public Form1()
        {
            Thread thread = new Thread(new ThreadStart(AAA));
            thread.Start();
            InitializeComponent();
        }
        void AAA()
        {
            Form f = new Form();
            Application.Run(f);
        }
  不难发现,Application.Run里面其实就是一个死循环,以便确保该线程永远运行着。
posted @ 2008-12-05 17:13 snowwolflibo 阅读(1806) 评论(21) 编辑
  2008年6月4日

  1、.Net技术将大行其道
  虽然就程序员比例而言,.Net与Java还有一定的差距,但在微软操作系统的支持与WPF等新技术推动下,越来越多程序员与企业开始选择.Net技术。个人认为,.Net相对Java最大的优势在于语言的统一性。如是我是老板,招一批.Net程序员,后台程序会做了,前端的富客户端(Silverlight)也会做了,而且.Net入门可比Java快多了,毕竟是微软的技术,易上手啊,谁让我们从小就用微软的东西。
  而在桌面软件方面,.Net则有更大的优势。因为Vista与Vienna将是微软下一代主流操作系统,它们可以天然就带了.Net runtime的。如果这两个操作系统得以普及,.Net技术也将在桌面软件领域大行其道,就这一点,Java是无法望其项背的。
  那相对C++呢,我想过不了几年,硬件水平将有很大提升,摩尔定律就是这样说的,C++所谓的高性能也将不再是核心优势,想必能运行Vista或Vienna的机器,.Net程序也不在话下吧。而.Net开发的快捷与内存安全将成为与C++等非托管语言竞争的杀手锏。
  我们熟知的飞信、Fiddler等大众桌面软件就已经是.Net开发的了。据悉,电信、网通也将陆续推出基于.Net技术的大型客户端软件。.Net的春天指日可待。
  随便说一句,在WPF与Silverlight等技术成熟的时候,.Net与Java也就相当于今天的C++了。
 

  2、OpenID-网络用户集中认证
  相信每个网民都有不下一个的网络ID,新浪的,网易的,yahoo的,Hotmail的,如果要享受某个网站的服务,我就得先注册成它的会员,并且不同网站的用户之间的通信也存在障碍。
  如果能有一个通用的帐号,能在各个网站畅通无阻,这将大大提高用户的上网体验,也没必要记那么多的帐号与密码了。
  这事最闹心了,本来我在各大网站的用户名都是snowwolf的,可在某些网站,也不知谁先给抢注了,我就只得改用snowwolflibo,结果是上网时,我也不知道哪些用户是snowwolf,哪些用户是snowwolflibo。
  现在好了,有了OpenID(据说是微软,Google,Yahoo等力挺的一个概念,具体是什么,我也说不清,反正很拽),各网站可以把用户认证管理托付给另一专注于OpenID服务的系统,实现整个互联网的单点登录。
  除了网站,IM工具的互通互联也势在必行,看看现在的白领一族,每天打开电脑第一件事就是启动各大IM工具,QQ/MSN/Fetion,据说现在又有了百度Hi。我们的桌面早已凌乱不堪了。
  当然IM与网站的集中认证的推行瓶颈不在技术,而是商业层面的东西。所谓的互联可能会先在市场份额小的产商中进行,如百度Hi就宣称要支持互联。但互联也可能出现在大产商对小产商的并购中。这听起来有点像战国时代的“合众”与“连横”。

 
  3、互联网的高度分化
  据说Facebook、Myspace等Web2.0网站已经支持插件扩展了,往大了说,它们已具有互联网平台的雏形了。这点与操作系统极其类似。Windows就是一个开放的平台,基于该平台的API,第三方软件产商可以开发各种应用。而如今(或者说不久将来),互联网也将具备该特性。
  之前互联网倡导的开放、共享、自由仅限于内容领域--我做一个网站,用户可以在上面share各自的文章、相片、视频。而未来的互联网,将极好地实现应用与服务的可扩展化。
  设想一下,未来的互联网产商大致分为四类:
  平台产商:类似Windows、Linux等,它们仅提供网络应用的“插座”,实现数据、界面、通讯、安全等基础逻辑。
  应用产商:类似Office、QQ、SQL Server等应用软件,它们基于平台API编程,实现丰富的网络应用。
  内容产商:利用RSS等技术向网络应用提供内容。
  权限提供商:类似于前面说的OpenID。为整个互联网提供统一的权限认证与用户资料管理。
  而前三者就类似于移动服务的运营商、SP、CP。三者专注于各自领域,联合起来为用户提供内容与服务,并合理分成。
  到那时,新浪之类的网站将不复存在,取而代之的是一个开放的基础平台(网络操作系统,姑且这么说),几个新闻浏览器、视频播放器、博客服务中心,与来源于各大内容提供商的丰富资讯、视频、音频等。
 
  4、社区门户--小区生活的网络延伸
  打开新浪,弹出一个深圳楼盘打折的信息,价格十分诱人,但我看一眼就关掉了。为什么,不关我事啊,咱是要在北京定居的。
  这就是传统门户的弱点,它的内容与广告不具针对性,盲目投放,关注率与消费行为转化率极低。既增加了用户的反感度,也损失了广告主的利益,同时浪漫带宽。
  而我们要做的,是将用户按区域细分。区域可以是按省、市、县,甚至街道,小区。这类新的网络模式我们暂称其为“社区门户”。如果可以精确到小区,社区门户将为不同小区用户提供更对口的服务与内容。
  比如说,小区附近哪的购物最便利,哪有健身房,谁谁谁发起了一场篮球赛,这些信息比传统的信息大杂绘对用户更具实际意义。
  个人觉得,最具建设“社区门户”能力的是电信等运营商。因为他们在宽带接入时就知道了用户的确切位置。
 
  5、定向门户--仅为您提供服务
  如果说“社区门户”是根据区域对用户进行了细分,那“定向门户”则根据身份、职业甚至喜欢为用户量身定制。
  用户向网站发起请求后,网站将依照用户IP、Cookie等参量分析该用户特性,比如说某某某,男,IT从业人士,酷爱数码产品,NBA粉丝,喜欢爱田由与小仓优子(举个例子,看官勿对号入座),持有大量中石油股票。接下来,系统将优先推送与上述特殊相近信息与广告。
  而谁来捕获和分析用户特性?这就是下面要说的内容。
 
  6、提供用户喜好--搜索引擎下一个赢利点
  请先避开个人隐私等法律问题。仅就技术和网络需求而言,提供用户喜好将成为搜索引擎与其合作产商的一大金矿。上面提到的“定向门户”可以为用户提供个性化的服务与内容,而对用户特性偏好最为了解的莫过了Google 百度等搜索引擎。
  而定向门户则可以通过搜索引擎的接口获取当前用户特性,从而推送定向业务。
  就技术而言。搜索引擎基于IP或Cookie或Mac地址等个性化参数存储用户搜索行为。特性分析系统将依据用户搜索行为提炼出用户特征。然后通过WebService等远程技术向定向门户系统公开接口。商业上,两者可进行利益分成。
 
  7、跨终端--大互联网时代来临
  咱也凑个热闹,说说云计算。
  个人觉得,云计算将数据与计算交付给了网络,就存在离线操作的问题,要解决该问题,就必须实现强大的数据与应用的同步功能。就这点而言,云计算要走的路还很长,太虚。
  但另一方面,云计算可以将PC、手机、手表、汽车(此处可大胆想象,个人觉得一切皆可互联)等设备全部纳入网络,这实现了数据与应用在不同设备上的共享。在此咱称之为“跨终端”(我杜撰的),而我们日常生活中的各种器材将与传统的PC、手机一起,进入一个崭新的“大互联网”时代(再次杜撰)。
  设想这样一个场景。我通过PC在网络上找到了一首歌曲,点击“加入收藏”,回到车里,我只要通过汽车的终端设置点击“我的收藏夹”就可以找到该歌曲了。
  还有,记得比尔.盖茨也说过,将来要让房子变得与电脑一样。也许就是这个意思。
 
  完了。欢迎拍我。
posted @ 2008-06-04 13:06 snowwolflibo 阅读(142) 评论(3) 编辑
  2008年4月25日

  近期在赶一个客户的项目,采用Silverlight技术制作一个视频播放页面。目前版本基于SL 2 Beta 1开发,已初步完Demo版,现将开发所得经验分享如下:
  效果图:

(播放视频)



(鼠标放在缩略图上,显示相关说明)



(鼠标放在视频名称上,显示相关说明)



(点击缩略图视频“翻跟头”进入播放区)



(点击缩略图视频“翻跟头”进入播放区)


页面浏览:http://cms.huadublog.cn/VodSL/VodSLTestPage.html


SL自定义控件经验分享

1、VideoPlayer视频播放控件
  该控件主要功能有Play/Pause/Stop等,这些功能我认为比较简单,在此不提。主要说说如果通过Slider控件实现播放进度条的方法。
  首先创建MediaElement与Slider。
  然后分析下进度条的实现原理:
    A、如何实现Slider与播放进度的同步?答:为MediaElement设置TimelineMarker,将MarkerReached时修改Slider进度值。注意,一定要在MediaElement.MediaOpened事件中添加TimelineMarker。因为添加TimelineMarker要考虑影片的实际长度,比如说,如果想在Slider上设置100个点,那每一点代表的时间就是影片总长度的1/100。看看我的代码:

            this.slider.Minimum = 0;
            
this.slider.Maximum = 100;
            
double seconds = this.mediaElement1.NaturalDuration.TimeSpan.TotalSeconds;
            
for (int i = (int)this.slider.Minimum; i < (int)this.slider.Maximum; i++)
            
{
                TimelineMarker marker 
= new TimelineMarker();
                
double time = seconds / this.slider.Maximum * i;
                marker.Time 
= new TimeSpan(00, (int)time);
                marker.Text 
= "a";
                marker.Type 
= "Test";
                
this.mediaElement1.Markers.Add(marker);
            }

  在此需要注意的是,一定要设置marker.Type,否则您试试,一运行程序就崩溃了。呵呵。
  好了,接下来在MediaElement.MarkerReached事件中添加以下代码:

            double time= e.Marker.Time.TotalSeconds;
            
double seconds= this.mediaElement1.NaturalDuration.TimeSpan.TotalSeconds;
            
if (seconds <= 0)
            
{
                
return;
            }

            
double marker = (time * this.slider.Maximum / seconds);
            txtSlider.Text 
= e.Marker.Time.Hours + ":" + e.Marker.Time.Minutes + ":" + e.Marker.Time.Seconds;
            
this.slider.Value = marker;

  在此,我们根据当前Marker的时间点与总时长算出播放时间占总时间的比例,然后计算出Slider当前应该的进度值。就OK了,如果要显示当前时长的话,只要txtSlider.Text = e.Marker.Time.Hours + ":" + e.Marker.Time.Minutes + ":" + e.Marker.Time.Seconds;即可。

  然后我们再实现进度条的拖拽功能。
  在Slider.ValueChanged事件中添加以下代码:

            double seconds = this.mediaElement1.NaturalDuration.TimeSpan.TotalSeconds;
            
double time = seconds / this.slider.Maximum * this.slider.Value;
            mediaElement1.Position 
= new TimeSpan(00,(int) time);

  一切搞定!
  不过细心的你可能会发现一个问题,那就是MediaElement.MarkerReaded可以改变Slider.Value,而Slider.Value的改变又会引发Slider.ValueChanged,该事件中又有MediaElement.Position=v,这又会引发MediaElement.MarkerReaded,呵呵,死循环吧,没关系,自己加一个信号变量就行了。这我不再详述了。
  

posted @ 2008-04-25 14:58 snowwolflibo 阅读(2490) 评论(6) 编辑
  2008年4月22日

   自Silverlight(以下称SL)1.0 Alpha发布一来,一直对该技术备加关注,加上项目组正好有用到该技术,所以对SL也有了一定程度的了解。SL是一种强大的RIA技术,但同时由于其产品的不成熟,存在很多困扰开发者的问题。不论是对中文的支持,还是对媒体播放的支持,还是对WebService的支持,SL都没能提供很好的解决方案。(当然SL2 Beta 1对中文支持已经很好了)。其实本人一直看好SL,但所谓爱之越深,恨之越切,在此就仅对SL 2Beta 1的不足之处作个说明。当然,如果有不对之处,还请博友们指正。

  Silverlight 2 Beta 1版本缺陷列表

  1、WebService不支持同步调用
        在SL工程中添加Service Reference后,自动生成代理类ServiceSoapClient,实例化ServiceSoapClient后,我们只能看到异步调用的方法。比如,我的WebService提供了一个GetVideoTypes方法,在ServiceSoapClient中却只有GetVideoTypesAsync与GetVideoTypesCompleted,前者是异步方法,后者是相应的事件。上网找了资料,发现在有的外国论坛上,有人也提出了这个问题。

  2、WebRequest不支持同步调用
   与WebService一样,WebRequest也不支持同步,具体不再详述。在此说一下,WebRequest在System.Net.dll中,大家在开发时要自动引用一下。

  3、System.Runtime.Serialization.Json.DataContractJsonSerializer方法不友好
   在SL2 Beta 1中用JSON,我只想说,这是一个比较奢侈的要求,目前MS封闭的System.Runtime.Serialization.Json.DataContractJsonSerializer类接口函数很不友好,只支持ReadObject与WriteObject,没有我期待的Serialize与Deserialize。呵呵,你看看ReadObject接受的参数是什么,Stream!!哈哈,你还要构造一个Stream,而如果在分布式系统中,这个Stream就要从WebRequest与WebResponse得到了,嘿嘿,这两类还不支持同步调用。麻烦啊。不过我记得早期版本是有Serialize与Deserialize的,不知道是给去掉了,还是我没找到。
  BTW,System.Runtime.Serialization.Json.DataContractJsonSerializer在System.ServiceModel.Web.dll中,很不好找啊。

  4、对相对路径支持很不好
  我有一个视频播放的页面,要动态加载一些图片,一开始这可把我难住了。因为SL是客户端执行的,而这些图片是在服务器上。我在Xaml中直接写相对路径如"images/1.png",不好使。为什么呢,我觉得也是可以理解嘛,毕竟SL在客户端执行,客户端哪有这个文件,但你SL的Runtime是不是应该动态的去服务器把这图片download下来啊。我记得老版本是可以的啊。没办法,我一开始只能写完整的uri,如http://localhost/vodsl/images/1.png。
  不过还有一个奇怪的现象,在同一个SL页面中,有的图片显示不出来(如果用相对路径的话),但有的图片又可以显示。My God,想来想去,答案是:SL还是做了动态下载,只不过这个下载可能在SL渲染后完成,就是说有的图片下载不赶趟了。

  
   5、重绘SilderBar控件会让你抓狂
  SL 2 Beta 1引入了TextBox Button ScrollBar等控件,值得夸奖,毕竟在1.1时,自己封装TextBox 还麻烦了(绝对地狱似的编程)。
  而这些控件在美观上是远远不能满足人们日益增长的审美要求的。于是乎,我想重绘它们。感谢SL为我们提供了重绘控件的机制。具体方法是为控件(如:Button ScrollBar)指定Style,这个Style其实就是一个Resource,你可以直接在Xaml中写。但要写在相应控件的前面,否则在运行时,SL会认为控件的Style是一个无效对象。
  但定义这一个Style可不简单耶!!看看SDK中的例子,那是相当长的一段代码。而且你很难知道控件的各个元素叫什么名(我是通过SDK/Google才知道的)。很不人性啊。其实我就是想改一个小滑块的样式,(ScrollBar中的小滑块),就要写一大堆Xaml。大家可以试试,我是花了很长时间才明白这大堆Xaml的具体含义。
  好了,终于可以自定义ScrollBar的样式了,但发现ScrollBar总有一个背景图去不掉,就是下面这个图片:

  无论我怎么修改Style,这个底图总存在。没办法,我最近只好放弃重绘ScrollBar。

  6、说说Grid与DataGrid
  Grid是SL的一个相对定位容器,在Grid中的控件是不能绝对定位的,如果你也与我一样试图在Blend中拖动Grid中的控件,一定感触颇深。我觉得这是很合理的,Grid就像Html中的Table,可用作页面整体的布局,事实上SL的很多控件(比如ScrollBar)内部也是用Grid布局的。但Grid没有提供Rows.Add与Columns.Add等接口,在想动态放置子控件时比较不方便。但Grid引入了RowDefinition与ColumnDefinition的概念,这个我个人比较喜欢。它实现了Grid布局定义与子控件放置的分离,你先定义Grid有几行几列,然后在后面加子控件,再为这些子控件指定Grid.Row与Grid.Column,这样就避免了HTML Table中的层层嵌套。这样的缺陷就是不代码不直观。
  BTW,Grid的单元格似乎不能自动随子控件撑大,我只看到了Maxheight与MinHeight属性,但似乎不能实现这个功能。
  DataGrid居然没有Rows.Add接口,但有Columns.Add接口。

  7、有些属性要通过SetValue与GetValue方法
   .Net 3.0与SL引入了DependencyProperty机制,很好很强大,我们可以在一个类中定义属性,然后别的对象也能用这个属性,没有明白?比如说在Canvas中定义了Left,我们可以在任何控件对象中SetValue(Canvas.Left,v)。凡是可以嵌在Canvas中的控件都可以通过这种方式来设置Left;而Grid也有LeftProperty,同样,凡是可以嵌在Grid中的控件都可以SetValue(Grid.Left,v)。仔细想想,不难发现,在任何一个控件对象中SetValue(Grid.Left,v),最终都会调用Grid类中的一段逻辑。你要是自己定义一个容器控件,比如MyPanel,你在这个类中加一个LeftProperty,然后实现一个设置子控件Left的逻辑,那么所有子控件都可以SetValue(MyPanel.Left,v)了。(关于DependencyProperty以后再说)
  用DependencyProperty定义控件属性的缺点就是不方便,如果是GetValue还要将返回值强制转换。即使是Left,Right这样的常用属性,也要通过SetValue与GetValue方式,很是麻烦。相信下一版本的SL会封闭友好的属性接口。当然这仅仅是为了一个方便而已。


      如果大家发现有地方说的不对,还请指正。

posted @ 2008-04-22 16:22 snowwolflibo 阅读(1824) 评论(25) 编辑
昵称:snowwolflibo
园龄:3年9个月
粉丝:1
关注:0
<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

搜索

 
 

常用链接

我的标签

随笔档案

最新评论