设为首页收藏本站language 语言切换
查看: 1342|回复: 0
收起左侧

使用VB.NET的五个技巧

[复制链接]
发表于 2010-2-25 10:29:32 | 显示全部楼层 |阅读模式
<p >  .NET框架组件太大了,比任何以前所写的封装功能的库都要大。这样有好处,因为它大幅削减了建立应用程序所需编写的代码,但是也使我们不可能完全了解该框架组件。但是我们很容易从中找到一些技巧。<p ><p >  <b>窗体嵌套</b><p >  经验丰富的Visual Basic开发者知道多文档界面(MDI)应用程序能够包含子窗体,那些子窗体由MDI父窗体管理。但是如果没有MDI的能力你怎样实现包含嵌套窗体?例如一个MDI子窗体也可能需要包含另一个窗体。<p >有时能够使用用户控件(UserControl)实现这种功能,但是如果你真的需要把一个窗体嵌套进另一个窗体,有多种方法可以实现。窗体衍生自Control类,这意味着它能被放入另一个窗体的控件集合中,使用如下的逻辑:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim f As New frmEmbed2()Me.Controls.Add(f)f.Show()</ccid_code></pre></td></tr></table></ccid_nobr><p >  但是很不幸,这段代码将会导致一个运行时(runtime)异常(见图1)。<p >   <img  src="http://www.hh010.com/upload_files/article/244/9_flpwhd144194.gif"><p >  图1.试图把一个窗体添加到另一个窗体的控件集合时出现的运行时错误<p >  为了避免这种异常,该窗体的TopLevel属性必须设置为False(见下面的代码)。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim f As New frmEmbed2()f.TopLevel = FalseMe.Controls.Add(f)f.Show()</ccid_code></pre></td></tr></table></ccid_nobr><p >  图2显示的是使用上面的逻辑实现的一个窗体嵌入另一个窗体。嵌入的窗体有一个标题条(它的颜色是未激活的系统颜色),因此该嵌入窗体能在容器窗体内四处拖动。在图2中,该窗体从它的开始位置(左上角)拖到了右下角。<p >   <img  src="http://www.hh010.com/upload_files/article/244/9_nrkpbz144195.gif"><p >  图2.在容器窗体中有一个嵌入的窗体。嵌入的窗体能在容器窗体中拖动。<p >  通常在显示嵌入的窗体前先设置它的位置。这只需要简单的设置嵌入窗体的Left和Top属性。嵌入窗体的位置与容器窗体是相对的。<p >  与MDI子窗体不同,嵌入窗体能覆盖容器窗体上的控件。图3显示了它们的不同。<p >  <img  src="http://www.hh010.com/upload_files/article/244/9_nqlcva144196.gif"><p >  图3.嵌入窗体(左)可以覆盖容器窗体上的控件。MDI子窗体(右)不能覆盖MDI父窗体上的控件。<p >  在右边的MDI例子中,没有办法使按钮隐藏在子窗体的后面。但是在左边该按钮被嵌入窗体覆盖了。<p >  当窗体第一次被嵌入时,它将显示在容器窗体上的已存在的控件的后面。当它被点击时,它走向前台并停留在那儿。这会打扰用户,但是能通过插入下面的代码防止这种情况发生:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>f.BringToFront()</ccid_code></pre></td></tr></table></ccid_nobr><p >  嵌入的窗体可以包含其它的嵌入窗体,没有实际的限制。图4显示了一个本身包含嵌入的窗体的嵌入窗体。<p >   <img  src="http://www.hh010.com/upload_files/article/244/9_szupvf144197.gif"><p >  图4.一个包含嵌入窗体的嵌入窗体<p >  处理数据行(DataRow)<p >Windows窗体中的数据绑定列表框和组合框很节省时间。典型的代码如下(假定已经建立了SqlDataAdapter或者其它部件获取数据):<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim ds As New DataSet()SqlDataAdapter1.Fill(ds, &quot;Customers&quot;)ListBox1.DataSource = ds.Tables(&quot;Customers&quot;)ListBox1.DisplayMember = &quot;CompanyName&quot;ListBox1.ValueMember = &quot;CustomerID&quot;</ccid_code></pre></td></tr></table></ccid_nobr><p >  在这种情况下,代码使用Northwind数据库的顾客记录工作。DisplayMember属性设置为你希望用户在列表框中看到的记录字段,它是customers表的CompanyName。通常ValueMember属性设置为数据表中的一个键字段,对于customer来说是CustomerID。一旦用户选择了列表框中的一行,很容易使用列表框的SelectedValue属性获得键字段:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>MsgBox(ListBox1.SelectedValue)</ccid_code></pre></td></tr></table></ccid_nobr><p >  但是有可能需要一个与被选择项相关的整个数据行对象的引用。例如,如果被选择的行需要被删除,就不知道键了。你需要一个数据行的引用以使用Delete方法。<p >  典型的Visual Basic开发者通常这样想:"我已经得到了该行的键了,我将编写一些逻辑来查找使用该键的行"。这样可以实现,但是有更好的实现方法。可以使用一行代码获取与列表框中选项关联的数据行:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim dr As DataRow = CType(ListBox1.SelectedItem, DataRowView).Row</ccid_code></pre></td></tr></table></ccid_nobr><p >  通常该逻辑不会凭直觉出现,即使对经验丰富的开发者。为了解释这是怎样实现的,我把上面的一行拆成几行,下面的代码与上面代码的功能相同:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim drv As DataRowViewdrv = CType(ListBox1.SelectedItem, DataRowView)Dim dr As DataRowdr = drv.Row</ccid_code></pre></td></tr></table></ccid_nobr><p >  DataRowView类是数据行的包装,它被多个Windows窗体控件使用。它使得显示与控件中的数据行相关的数据更加容易。当列表框被数据绑定到数据表时(假定列表框中的有些行当前被选定了),列表框的SelectedItem属性保存了一个DataRowView对象。<p >  这意味着我们能把列表框的SelectedItem属性转换到DataRowView对象,这就是上面代码中的第二行实现的。接着DataRowView暴露一个Row属性,它指向被包装的数据行。上面的代码声明了一个数据行并设置了Row属性。<p >  转换对象的类型以访问它的接口的技术在Visual Basic 6.0中不是经常使用,但是在Visual Basic .NET中这是经常的。有了上面的例子后,大多数有经验的开发者迅速跟上了这种技术。<p >  数据行的引用(dr)可用于用任何方式维护行。访问数据行中的任何特定字段是可行的。行中的数据可以被改变,能使数据行的Delete方法把该行标识为删除,或者从数据表的行集合中删除该行。下面的代码标识删除了一行:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>dr.Delete()</ccid_code></pre></td></tr></table></ccid_nobr><p >  使用主键(由ListBox.SelectedValue返回)查找下层数据行的方法需要很多代码,要花很长时间,执行起来更慢。对于刚开始使用Visual Basic .NET的程序员来说花几个小时编码是很正常的。理解上面的技术节约了很多时间,更简单、容易维护代码。<p ><p >  <b>给控件绑定颜色</b><p >  数据绑定能应用于控件的任何属性。我看到过很多人提到能够绑定文本框的背景颜色到数据项,举个例子,超期的帐号的背景色显示红色。<p >  但是如果你试图使用数据集或者数据表实现该功能,将会遇到问题。数据行只能保持受到限制的数据类型,并且不支持Color类型。如果你不能把颜色存储在数据中怎么能绑定颜色呢?<p >  有些途径可以解决这个问题,但是最简单的是用绑定到自定义数据对象代替绑定到数据表。自定义业务对象的属性可能是Color型的,这样的属性能绑定到控件的BackColor属性。<p >  为了演示,我定义了下面的自定义事务对象:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>ublic Class AccountDim m_nAccountID As IntegerDim m_sCustomerName As StringDim m_dblBalance As DoublePublic Sub New(ByVal nAccountID As Integer, ByVal sCustomerName As String, _   ByVal dblBalance As Double)    Me.AccountID = nAccountID    Me.CustomerName = sCustomerName    Me.Balance = dblBalanceEnd SubPublic Property AccountID() As Integer    Get        Return m_nAccountID    End Get    Set(ByVal Value As Integer)        m_nAccountID = Value    End SetEnd PropertyPublic Property CustomerName() As String    Get        Return m_sCustomerName    End Get    Set(ByVal Value As String)        m_sCustomerName = Value    End SetEnd PropertyPublic Property Balance() As Double    Get        Return m_dblBalance    End Get    Set(ByVal Value As Double)        m_dblBalance = Value    End SetEnd PropertyPublic ReadOnly Property BackColor() As Color    Get        If m_dblBalance &lt; 0 Then            Return Color.Salmon        Else            Return SystemColors.Window        End If    End GetEnd PropertyEnd Class</ccid_code></pre></td></tr></table></ccid_nobr><p >  注意只读的BackColor属性从Balance属性中得到值,并且为负平衡(negative balance)暴露了一个不同的颜色。该类的其它元素很直接。<p >  现在我们建立一个界面来操作这些对象的集合(见图5)。<p >   <img  src="http://www.hh010.com/upload_files/article/244/9_9goraa144198.gif"><p >  图5.演示背景颜色绑定的窗体(设计时)<p >  上面的三个文本框都用于保持当前Account对象的数据。它们分别叫txtAccountID、txtCustomerName和txtBalance。显示Load的按钮叫btnLoad,用于载入帐号集合。另两个按钮在记录间导航,分别叫btnBack 和 btnForward。<p >  帐号对象集合可以保持在ArrayList(数组列表)中,因此下面一行代码应该在窗体代码的最前面:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim colAccounts As ArrayList</ccid_code></pre></td></tr></table></ccid_nobr><p >  下面是Load方法的Click事件代码。它建立了一些Account对象并把它们放入一个集合中,接着把该集合绑定到文本框。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>colAccounts = New ArrayList()colAccounts.Add(New Account(1, &quot;ABC Company&quot;, 10))colAccounts.Add(New Account(2, &quot;XYZ, Inc.&quot;, -10))colAccounts.Add(New Account(3, &quot;MNP Limited&quot;, 0))txtAccountID.DataBindings.Add(New _              Binding(&quot;Text&quot;, colAccounts, &quot;AccountID&quot;))txtCustomerName.DataBindings.Add(New _              Binding(&quot;Text&quot;, colAccounts, &quot;CustomerName&quot;))txtBalance.DataBindings.Add(New _             Binding(&quot;Text&quot;, colAccounts, &quot;Balance&quot;))txtBalance.DataBindings.Add(New _              Binding(&quot;BackColor&quot;, colAccounts, &quot;BackColor&quot;))</ccid_code></pre></td></tr></table></ccid_nobr><p >  注意最后两行。txtBalance的Text属性绑定到一个帐号的Balance属性,并且该控件的BackColor属性绑定到帐号对象的BackColor属性。它演示了.NET框架组件绑定一个以上属性到不同数据项。<p >  现在点击btnBack的Click事件,填入一下代码:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>If Me.BindingContext(colAccounts).Position &gt; 0 Then    Me.BindingContext(colAccounts).Position -= 1End If在btnForward的Click事件中写入以下代码:If Me.BindingContext(colAccounts).Position &lt; colAccounts.Count - 1 Then    Me.BindingContext(colAccounts).Position += 1End If</ccid_code></pre></td></tr></table></ccid_nobr><p >  启动项目并点击Load按钮。ABC公司的记录出现在文本框中。点击向前按钮,就是XYZ公司记录,同时,txtBalance的背景色变为橙红色(见图6)。<p >  <img  src="http://www.hh010.com/upload_files/article/244/9_ylbrwj144199.gif"><p >  图6.数据绑定窗体显示了一个负平衡记录,引起Balance字段的背景色不同<p >  过了该帐号记录后,该文本框的背景颜色将变回正常色。<p >  Account类不是特别复杂。但是这个例子最少让你看到了怎样绑定不同属性(例如控件颜色)。<p ><p >  <b>修改数据窗体向导</b><p >  使用数据窗体向导(Data Form Wizard)你能迅速获得文件操作程序窗体。为了使用它,选择Project菜单的Add New Item,接着选择Data Form(数据窗体)。该向导将一步一步帮助你指定希望的数据,并为那些数据建立一个文件操作程序。图7显示了一个从Northwind数据库的Products表中产生的数据窗体。<p >   <img  src="http://www.hh010.com/upload_files/article/244/9_o58tzd144200.gif"><p >  图7. Northwind Products表的文件维护窗体,它由数据窗体向导产生<p >  但是这种自动生成程序有一个重要的限制。如果被访问的数据有任何字段不能为空(因为数据库大纲不允许空值),那么向导生成的程序不能添加记录。当点击Add按钮时,将出现错误信息,提示记录中的第一个字段不允许为空(如果你没有最新的服务包,你也许看不到该错误信息,但是程序拒绝添加记录)。<p >  该问题是由于数据窗体向导使用BindingContext对象给绑定的数据表添加了一行。下面是btnAdd_Click事件程序失败的代码:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Me.BindingContext(objProducts, &quotroducts&quot;).AddNew()</ccid_code></pre></td></tr></table></ccid_nobr><p >  解决方法是为新行略过BindingContext对象。下面是添加新行的典型代码,该代码应该代替上面的一行代码:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim dr As DataRowdr = objProducts.Tables(&quotroducts&quot;).NewRowdr.Item(&quotroductName&quot;) = &quot;&quot;dr.Item(&quot;Discontinued&quot;) = False' Set any other fields that cannot null to default values.objProducts.Tables(&quotroducts&quot;).Rows.Add(dr)</ccid_code></pre></td></tr></table></ccid_nobr><p >  在用数据表的NewRow方法获得一个空行时,该代码给不能为空的字段填充值。接着数据表接受新行,通过数据表行集合的Add方法添加新行。<p >  有了这个补丁后,该数据程序能够运行。可以对它进行增强或改变,例如改变SupplierID 和CategoryID字段以从包含供应商和类别的下拉列表中选择。<p ><p >  <b>在.NET框架组件中显示时间</b><p >  开发过程过程中我们通常对特定代码片运行所花的时间很感兴趣。当然有一些标准程序和代码工具可以查看到它,但是有经验的Visual Basic 6.0开发者有更快的办法。仅仅捕捉开始时间(使用Now关键字)和终止时间(再次使用Now关键字),两种相减,就能知道结果了。<p >  如果使用Visual Basic .NET编写,首先尝试的代码可能是这样的:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim StartTime As DateTime = Now' {code to check for timing goes here}Dim EndTime As DateTime = NowConsole.WriteLine((StartTime - EndTime).ToString)</ccid_code></pre></td></tr></table></ccid_nobr><p >  但是这段代码的最后一行有语法错误。错误消息是"日期类型没有定义'-'操作符"。这意味着我们不能执行减法。日期数据类型不支持减法操作,那么我们怎么得到两次时间的差别呢?<p >  答案就是使用TimeSpan类。它是用于保持时间段的。上面的代码看起来与.NET框架组件中的相似:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>Dim StartTime As DateTime = Now' {code to check for timing goes here}Dim EndTime As DateTime = NowDim RunLength As System.TimeSpanRunLength = EndTime.Subtract(StartTime)Console.WriteLine(RunLength.ToString)</ccid_code></pre></td></tr></table></ccid_nobr><p >  计算使用的是类Date的Subtract方法。最后一行将输出时间的跨度,格式化成小时、分钟和秒(包括秒的小数位)。典型的输出是这样的:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="f6f6f6" class="code"><pre><ccid_code>00:00:10.4850768</ccid_code></pre></td></tr></table></ccid_nobr><p >  该时间跨度是10秒半。尽管显示了7位小数,但是只能相信两位,但是已经足够了。<p ><p >  <b>结论</b><p >  .NET是一种有趣的技术。.NET框架组件有超过8000个类!在如此庞大的内容中却很容易找到有用的功能。我希望上面的几个技巧在你的应用程序中能够用到。<p ><center>(责任编辑 <ccid_nobr>西门吹雪</ccid_nobr>)</center><p align="center"></p></p>
您需要登录后才可以回帖 登录 | 论坛注册

本版积分规则

QQ|Archiver|手机版|小黑屋|sitemap|鸿鹄论坛 ( 京ICP备14027439号 )  

GMT+8, 2025-4-30 15:43 , Processed in 0.270085 second(s), 24 queries , Redis On.  

  Powered by Discuz!

  © 2001-2025 HH010.COM

快速回复 返回顶部 返回列表