随笔 - 89, 评论 - 163, 引用 - 33

导航

关于

标签

每月存档

最新留言

广告

用LINQ快速改变XML元素的值

[原文作者]:Beth Massi

[原文链接]:Quickly Changing Values of XML Elements Using LINQ

    最近我在考虑一些关于如何使用LINQ查询XML文档(或片段)中一个特定节点并且改变其值的问题(这说明人们已经开始用这种方法了,这很是让我激动)。这是一个很方便的方法:我们可以改变查询返回节点的值,这样源XML中的值也会跟着改变。

   一个例子:

   Imports <xmlns="urn:mycompany:examples:plants">

   Module Module1

    Sub Main()

        Dim plants = <?xml version="1.0" encoding="ISO-8859-1"?>

                     <CATALOG xmlns="urn:mycompany:examples:plants">

                         <PLANT>

                             <COMMON>Bloodroot</COMMON>

                             <BOTANICAL>Sanguinaria canadensis</BOTANICAL>

                             <ZONE>4</ZONE>

                             <LIGHT>Mostly Shady</LIGHT>

                             <PRICE>$2.44</PRICE>

                             <AVAILABILITY>031599</AVAILABILITY>

                         </PLANT>

                         <PLANT>

                             <COMMON>Columbine</COMMON>

                             <BOTANICAL>Aquilegia canadensis</BOTANICAL>

                             <ZONE>3</ZONE>

                             <LIGHT>Mostly Shady</LIGHT>

                             <PRICE>$9.37</PRICE>

                             <AVAILABILITY>030699</AVAILABILITY>

                         </PLANT>

                         <PLANT>

                             <COMMON>Marsh Marigold</COMMON>

                             <BOTANICAL>Caltha palustris</BOTANICAL>

                             <ZONE>4</ZONE>

                             <LIGHT>Mostly Sunny</LIGHT>

                             <PRICE>$6.81</PRICE>

                             <AVAILABILITY>051799</AVAILABILITY>

                         </PLANT>

                     </CATALOG>

 

        Dim q = From plant In plants...<PLANT> _

                Where plant.<COMMON>.Value = "Columbine" _

                Select plant

 

        For Each item In q

            q.<PRICE>.Value = "$49.99"

            q.<LIGHT>.Value = "Full Sun"

        Next

 

        plants.Save("plants.xml")

    

    End Sub

 

 End Module

    注意两点:记得导入XML用到的所有命名空间,否则查询会返回空值;如果导入了构架(这个很容易就能办到,请参考这里)记得用XML智能提示。当然,我们也可以不把XML的内容全部写在代码里,而是从文件或者通过URI加载,这样可以得到一样的结果:

    Dim plants = XDocument.Load("plants.xml")

    Dim q = From plant In plants...<PLANT> _

        Where plant.<COMMON>.Value = "Columbine" _

        Select plant

 

    For Each item In q

    q.<PRICE>.Value = "$49.99"

    q.<LIGHT>.Value = "Full Sun"

    Next

 

    plants.Save("plants.xml")

   在这个例子中,我们用新的值覆盖源文档plants.xml。上文的两个例子产生同样的结果:

   <?xmlversion="1.0"encoding="iso-8859-1"?>

   <CATALOGxmlns="urn:mycompany:examples:plants">

    <PLANT>

    <COMMON>Bloodroot</COMMON>

    <BOTANICAL>Sanguinaria canadensis</BOTANICAL>

    <ZONE>4</ZONE>

    <LIGHT>Mostly Shady</LIGHT>

    <PRICE>$2.44</PRICE>

    <AVAILABILITY>031599</AVAILABILITY>

 </PLANT>

 <PLANT>

    <COMMON>Columbine</COMMON>

    <BOTANICAL>Aquilegia canadensis</BOTANICAL>

    <ZONE>3</ZONE>

    <LIGHT>Full Sun</LIGHT>

    <PRICE>$49.99</PRICE>

    <AVAILABILITY>030699</AVAILABILITY>

 </PLANT>

 <PLANT>

    <COMMON>Marsh Marigold</COMMON>

    <BOTANICAL>Caltha palustris</BOTANICAL>

    <ZONE>4</ZONE>

    <LIGHT>Mostly Sunny</LIGHT>

    <PRICE>$6.81</PRICE>

    <AVAILABILITY>051799</AVAILABILITY>

 </PLANT>

</CATALOG>

 

 

 

 

posted on 2008-09-27 16:41:40 by VBCTI  评论(0) 阅读(3061)

基于WPF LINQ的动态数据模型

[原文作者]:Beth Massi

[原文链接]:Dynamic Data Entry with WPF and LINQ

   上一节我讲到用XML编写动态WPF UI, 尽管这里的UI是动态生成的,但仍有一处不尽人意的地方,就是我们采用的是一个具体的对象customer(来源于LINQ to SQL classes)。 如果想要我们的应用程序既能够动态生成WPF UI 又能动态地编辑处理数据库里的任何表数据,就需要进一步参数化程序代码 ——而不仅限于customer。 这样只需要修改数据库表的定义而不用更新对象模块和重新编译代码。


   实现方法是试图在运行时加载,处理一个简单的无类型的(或者称作通用类型)DataTable,并运用XML literals特性 –为SqlDataAdapter生成SELECT和UPDATE语句。要注意一件事情,这个过程必须依赖数据库验证规则。因此这种方式只能用于处理非常简单的表(缩略语.维护表)。

 
   我需要创建一个强类型(指定类型)的DataTable包含表的定义,用来取代LINQ to SQL模板。这个datatable存储了字段的元数据(字段名,类型,等等)以便我们对表的操作。右键点击项目工程,添加新模板,选择DataSet template。我将它命名为TableSchemaDataSet,然后拖拽Server Explorer-〉Data Connection –〉Northwind Database下的存储过程GetTableSchema(这个已经在前面章节中生成好了)到DataSet 设计器上,自动为我们生成所需的DataTable,重命名为TableSchema,并保存。


 
   接下来精彩的部分。因为我们并不想知道当前表的定义,我们会加载一个通用类型的DataTable生成一个动态的WPF 窗口。DataTable和DataSet都建好了,还有两个步骤需要手动设置。


   首先定义一个公共属性(Public Property)存放表名,同时设置默认值为“Shippers”,然后创建一批私有变量引用所需的ADO.NET对象。

Imports <xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">

Imports <xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

Imports System.Windows.Markup

Imports System.Data.SqlClient

Imports System.Data

 

Partial Public Class Window2

    'This is the metadata table we created in the DataSet Designer

    Private TableSchema As New TableSchemaDataSet.TableSchemaDataTable

    'ADO.NET objects used to load and save the table we're editing

    Private TableDataAdapter As New SqlDataAdapter

    Private TableConnection As New SqlConnection(My.Settings.NorthwindConnectionString)

    Private Table As DataTable

    'This is the key field used in searching for a row in this example

    Private PKField As TableSchemaDataSet.TableSchemaRow

 

    'This property can be set before the Form.Show() to edit any table

    Private m_tableName As String = "Shippers"

    Public Property TableName() As String

        Get

            Return m_tableName

        End Get

        Set(ByVal value As String)

            m_tableName = value

        End Set

    End Property

 


    现在UI定义好了,然后设置主键字段(TableSchemaDataRow 对象)以便我们点击Find按钮时运用到Update和Select操作。

Private Sub Window1_Loaded() Handles MyBase.Loaded

    Try

        'Get the schema of the database table we want to edit

        Dim taSchema As New TableSchemaDataSetTableAdapters.TableSchemaTableAdapter

        taSchema.Fill(Me.TableSchema, Me.TableName)

 

        'Create the DataTable that will hold the record we're editing

        Me.Table = New DataTable(Me.TableName)

        Me.Title = Me.TableName

        Me.LoadUI()

        Me.SetPrimaryKey()
        Me.SetUpdateCommand()        

    Catch ex As Exception
        MsgBox(ex.ToString)
        Me.Close()
    End Try
End Sub
 
Private Sub LoadUI()
 
    Dim UI = <Grid xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 Name="Grid1">
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="100*"/>
                     <ColumnDefinition Width="200*"/>
                 </Grid.ColumnDefinitions>
                 <StackPanel Name="StackLabels" Margin="3">
                     <%= From column In Me.TableSchema _
                         Where column.IsPrimaryKey = 0 AndAlso column.DataType <> "timestamp" _
                         Select <Label
                                    Height="28"
                                    Name=<%= column.ColumnName & "Label" %>
                                    HorizontalContentAlignment="Right">
                                    <%= column.ColumnName %>:</Label> %>
                 </StackPanel>
                 <StackPanel Grid.Column="1" Name="StackFields" Margin="3">
                     <%= From column In Me.TableSchema _
                         Where column.IsPrimaryKey = 0 AndAlso column.DataType <> "timestamp" _
                         Select GetUIElement(column) %>
                 </StackPanel>
             </Grid>

     Me.DynamicContent.Content = XamlReader.Load(UI.CreateReader())
 
End Sub
 
Private Function GetUIElement(ByVal columnInfo As TableSchemaDataSet.TableSchemaRow) As XElement
    Select Case columnInfo.DataType.ToLower
        Case "datetime", "int", "smallint", "money"
            Return <TextBox
                       Height="28"
                       Name=<%= "txt" & columnInfo.ColumnName %>
                       Text=<%= "{Binding Path=" & columnInfo.ColumnName & "}" %>/>
        Case "bit"
            Return <CheckBox
                       HorizontalContentAlignment="Left"
                       Name=<%= "chk" & columnInfo.ColumnName %>
                       IsChecked=<%= "{Binding Path=" & columnInfo.ColumnName & "}" %>>
                       <%= columnInfo.ColumnName %>
                   </CheckBox>
        Case Else
            Return <TextBox
                       Height="28"
                       Name=<%= "txt" & columnInfo.ColumnName %>
                       MaxLength=<%= columnInfo.MaxLength %>
                       Text=<%= "{Binding Path=" & columnInfo.ColumnName & "}" %>/>
    End Select
End Function

 

Private Sub SetPrimaryKey()    这样我们就可以通过设置TableName,动态生成UI,通过Find找到想要的数据,并可以修改保存数据。而且可以不用重新编译修改数据库表结构。
 

    'Grab the Primary Key column of the table we want to edit so we can use it in the search

    Me.PKField = (From column In Me.TableSchema Where column.IsPrimaryKey = 1).FirstOrDefault()

End Sub

Private Sub btnFind_Click() Handles btnFind.Click

    If Me.txtSearch.Text <> "" Then

        Try

            'Create the SELECT command

            Dim cmdText = <s>

                          SELECT * FROM <%= Me.TableName %>

                          WHERE <%= Me.PKField.ColumnName %> =

                                <%= If(Me.PKField.DataType.Contains("char"), _

                                    "'" & Me.txtSearch.Text & "'", _

                                    Me.txtSearch.Text) %>

                          </s>.Value

 

            Dim cmd As New SqlCommand(cmdText, Me.TableConnection)

            Me.Table.Clear()

            Me.TableDataAdapter.SelectCommand = cmd

            Me.TableDataAdapter.Fill(Me.Table)

 

            Me.DataContext = Me.Table

            Dim view = CollectionViewSource.GetDefaultView(Me.Table)

            view.MoveCurrentToFirst()

 

        Catch ex As Exception

            MsgBox(ex.ToString)

            Me.DataContext = Nothing

        End Try

    Else

        Me.DataContext = Nothing

    End If

End Sub

 

Private Sub SetUpdateCommand()

    'Set the UpdateCommand so that we can save edited records in the table

    Dim cmdText = <s>

                  UPDATE <%= Me.TableName %>

                  SET <%= From column In Me.TableSchema _

                          Where column.IsPrimaryKey = 0 AndAlso column.DataType <> "timestamp" _

                          Select <c>

                                     <%= column.ColumnName %> = @<%= column.ColumnName %>

                                     <%= If(Me.TableSchema.Rows.IndexOf(column) < _

                                            Me.TableSchema.Rows.Count - 1, ", ", "") %>

                                 </c>.Value %>

                  WHERE <%= Me.PKField.ColumnName %> = @<%= Me.PKField.ColumnName %>

                        <%= From column In Me.TableSchema _

                            Where column.IsPrimaryKey = 0 AndAlso column.DataType = "timestamp" _

                            Select <c>

                                     AND <%= column.ColumnName %> = @<%= column.ColumnName %>

                                   </c>.Value %>

                  </s>.Value

 

    Dim cmd As New SqlCommand(cmdText, Me.TableConnection)

    Dim p As SqlParameter

 

    For Each column In Me.TableSchema

        If column.IsPrimaryKey = 0 AndAlso column.DataType = "timestamp" Then

            'Note: It's recommended to use a TimeStamp column in your tables for concurrency checking

            p = New SqlParameter("@" & column.ColumnName, SqlDbType.Timestamp)

            p.SourceVersion = DataRowVersion.Original

            p.SourceColumn = column.ColumnName

            cmd.Parameters.Add(p)

        Else

            p = New SqlParameter("@" & column.ColumnName, _

                                 CType([Enum].Parse(GetType(SqlDbType), column.DataType, True), SqlDbType))

            p.SourceColumn = column.ColumnName

            p.SourceVersion = DataRowVersion.Current

            cmd.Parameters.Add(p)

        End If

    Next

 

    Me.TableDataAdapter.UpdateCommand = cmd

End Sub

 

    然后我们可以在Loaded事件处理函数中编写逻辑代码,加载元数据,创建和加载XAML显示UI,再然后就是处理TableDataAdaper上的UpdateCommand。

posted on 2008-09-22 17:05:39 by VBCTI  评论(0) 阅读(3488)

使用Open XML Diff工具比较两个开发XML包

[原文作者]:Beth Massi

[原文链接]:Comparing Two Open XML Packages with Open XML Diff

    昨天我从微软的一员工那里收到一封email,内容是关于体验一下他正在设计的一个工具Open XML Diff。 他的名字叫Pranav Wagh。 他也发布了这个工具的一个版本,可以从他的博客上看到。


   当你准备写代码去生成一个开放XML文档,而且当你不确定要写的XML语句是什么的时候,你可以使用Open XML Diff。也就是说你知道你想要的文档在Word中是如何呈现的,但是不是很明白怎样去设置某个元素或者属性。你可以保存一个文档的备份,修改并且保存它,然后用此工具比较前后两个文件,就能使你看出你所需要的XML语法。
我在我的Vista机器上下载了源码,但是遇到了很多信任问题,工程需要找一个叫xmldiffpatch.dll的文件,而说明文档说从这里来安装,但是这样安装没有将程序集放在C:\Program Files\目录下, 在vista系统中这样引用程序集是不行的。 我发现Pranav在OpenXMLDiff\bin\Realease文件夹中已经提供了xmldiffpatch.dll。所以我为那三个工程创建了一个解决方案,并且设置两个工程间的引用,一个是OpenXMLComparer Winforms client到OpenXMLDiff library,另一个是OpenXMLDiff library到ViewRenderer。 现在你需要添加的唯一的二进制文件引 用就是XMLdiffPatch。最后设置工程OpenXMLComparer作为启动项,编译整个解决方案。


   为了测试这个工具,我以上次发布的Word文档“MyDocument.docx”作为对象,这个文档只包含文本“This is my document”。对它我保存了一个副本文件叫做“MyDocument1.docx”,并且做了一点小的改动:我降所有的句子字体加粗。现在我用工具比较这两个文档。报告发现包中三个xml文件改变了。其中最重要的是MainDocument部分,文件是document.xml。下面是报告中的部分快照,从中可以看出文件的区别:

 

   相当完美!这个确实帮助我理解通过office修改开放XML包文件,哪些文件的改变起了作用。需要了解更多请阅读Office 设计文档。


   务必在Open XML Diff站点上给出你的反馈。

 

posted on 2008-09-19 17:42:15 by VBCTI  评论(0) 阅读(3360)

质量控制的里程碑 & 狗食

[原文作者]:Matt Gertz

[原文链接]:Milestone Quality & Dogfooding

    在辛苦又有成果的工作后,我们完成了质量控制的里程碑。这个里程碑的目的是让我们准备好开发下一版本的Visual Studio,Team System.NET。虽然这些开发不是专门针对Visual Basic的,我还是选择利用这个“天字第一号讲坛”让大家知道这些努力。Visual Basic连同Visual Studio.NET产品都是会受到影响的。

   像在我前面的帖子中提到的,质量控制里程碑的工作不会包括新的特性和类似这样的事情,它的主要工作是准备工具和程序,好让我们可以开始下一个周期的工作。在开始编码之前这个是很重要的。因为在工程进度的中间做改变,是非常具有破坏性的。质量控制的里程碑的工作对于我们来说也是非常有挑战性的,而我很高兴能看到我们在设计上做的很多改变都没有造成什么问题。

   在之前的帖子里面讲到过,我们决定把大部份质量控制里程碑的工作集中在“dogfooding”上面—用我们自己的代码去完成开发工作,当然质量控制里程碑的工作也会包括其他一些准备工作。在这篇文章里面我主要讲"dogfooding”。因为三个原因我们会"dogfood"我们的代码。

1. 为用户增加产品的稳定性:在产品发布给用户之前,我们以每天使用的方式来找缺陷。
2. 实践我们所倡导的:如果我们觉得某个特性用户使用起来比较好,那么我们也应该会使用   它。
3. 这是做事情的正确方法: 我们坚信我们为产品开发提供的是最好的基础,这些产品包括从业余爱好写的代码到企业的解决方案。开发Visual Studio最好的工具就是Visual Studio

   现在我们对dogfooding不再陌生了,但是我们从来没有那么早开始做dogfooding。我们会在代码大部分完成的时候,在Beta1Beta2阶段做dogfooding。在早期的版本中,(例如重写了所有东西的Visual Studio .NET 2002),这样做就是比较有效的,因为那个时候代码没有集合在一起。另一方面,在后期去做dogfooding没有给我们带来相同的覆盖率,因为在Beta阶段,我们会集中精力去解决缺陷而不是开发新的特性,开发有质量的特性是VSTS,和.NET 的重点。现在我们有稳定的代码平台,这样我们就可以调整早些去做dogfooding,早些找到开发缺陷,在还容易做改动的时候就对此特性做改进。我非常兴奋可以做到这点,我一直都羡慕Windows和Office团队的能力可以在早期就做dogfooding

 

Team Foundation Server (TFS)

   在早期的产品开发周期中,我们用专门的系统去跟踪缺陷,另外一个系统存储代码,还有缺陷报告和报表去跟踪特性的进度。这三者彼此没有进行任何交流。一个状态报告也要比它应该有的复杂得多。一个我们要求的基于代码大小和管理生命周期的系统一直都是我们的目标,现在我们可以转向我们自己的产品了,Team Foundation Server(TFS),一个可以处理这三个方面的产品(还有很多其他方面)。

   尽管对这个改变有很多支持的声音,但诚实的说,在我们转向TFS的时候,还是很紧张不安的。Visual Studio, Team System.NET组成了一个相当庞大的部门。如果只看开发者部门不考虑微软其余的部门,都已经可以成为世界上最大的企业级的软件销售商之一,进入前五名是很容易的。我们长期来都以精确的方式做事情,因为很多的程序也要随之一起改变,所以从效率的角度,我们也很难放弃。

   幸运的是,现在已经不必担心了。可以肯定的是,在VS2008开发时,我们25%的产品已经率先使用了TFS,在体验中,也做了相应的改进。正因为他们努力的dogfooding,其余的部门在几个月之后可以很容易的转向TFS—我们仅在几天内就把所有的源代码移进新的项目并且编译成功,那可是很多的代码。在做的同时,我们也进行了一些改进,使开发者们可以更好的和存贮的代码交互—这个改进最终也会使我们的用户受益。

   此外,我们也用TFS跟踪缺陷和特性,将Team Explorer作为Visual Studio的家族产品补充进去。这是另外一个先驱者们在前面的VS版本做dogfooding的例子。所以这最后的转变也是同样的顺利。最后的结果就是现在我们用报告服务可以连接代码,特性计划和缺陷跟踪。这样,在管理下个产品生命周期上面的节约是显著的。我非常兴奋现在正在使用TFS


Side-by-side (SxS)

   当然,如果我们计划用产品本身去开发下个产品,我们必须能够像客户一样安装产品,还不能干扰可能还存在于我们机器上的前面版本的产品。这个目标被叫做"side by side”或者简称为"SxS”。这里面包括了很多版本控制,改变注册表信息,解决一些必须的硬编码程序集引用等等,这会跟随着很多很多测试工作。在产品生命周期中,很多组件都需要改变,这些组件来自不同的团队。在过去,这些事情会到beta里程碑的时候才完成,因为很多人在beta之前都在非常努力的写特性,他们所有的时间都花在这个上面了。但是,推迟SxS的工作并不是我们希望这样做的—这意味着,用户安装beta版本的产品时,有个问题会一直存在,那就是在卸载beta后,会要求修复到之前的产品。此外,我们在早期的产品生命周期中添加特性,同时也造成了更多的SxS副作用。


   要避免这些缺陷,我们决定在MQ里面来解决这个问题。坦白说,要这么快的做好这件事是很有挑战性的—花掉了我们所有人很多个夜晚和周末的时间,为了加快工作我们陷入很多需要重组的程序中。但是,在我们开始为下一版本开发特性之前,花额外的时间来更早的完成SxS的工作,这不仅使我们可以在不破坏VS2008安装(有可能我们已经安装了VS2008)的情况下dogfood我们的产品,而且用户的beta安装和卸载体验也会有更高的质量,这会使用户有更好 的beta体验,我们也会得到更好的回馈。

 

 

posted on 2008-09-19 15:06:29 by VBCTI  评论(0) 阅读(3360)

在WPF中显示 验证数据有效性信息

[原文作者]:Beth Massi

[原文链接]:Displaying Data Validation Messages in WPF

  

    
     你可能从我之前的那几篇文章中看出,我最近在研究WPF中的不同的数据案例。昨天我在摆弄WPF.net 3.5中的数据有效性验证,而WPF.net 3.5一起工作的非常好。在这篇文章中,我将从头到尾过一遍如何通过使用IDataErrorInfo 接口来触发你自己的数据对象中有效性验证,然后再介绍一些你能用来显示给用户的有效性验证错误信息的验证错误信息模板。
     在数据对象中应用有效性验证
 
    如果你使用自定义业务逻辑对象或者LINQ to SQL类,你首先需要实现IDataErrorInfo接口来收集你的对象上的有效性验证信息.如果你在WPF或者Windows Forms中使用DataSets, DataRowView已经实现了这个接口,所以你只需加有效性验证到你的DataTable Patial类就足够了.打开DataSet 设计器,右击DataTable, 选择”View Code” 就可以了. 比如,如果你有个customer数据表我们能像下面这样来验证LastName 字段的有效性:
    Partial Class CustomerDataSet
    Partial Class CustomerDataTable
 
        Private Sub CheckLastName(ByVal row As CustomerRow)
            If row.IsNull("LastName") OrElse row.LastName = "" Then
                row.SetColumnError(Me.LastNameColumn, "Please enter a last name")
            Else
row.SetColumnError(Me.LastNameColumn, "")
            End If
        End Sub
 
        Private Sub CustomerDataTable_ColumnChanged(ByVal sender As Object, _
                                                    ByVal e As System.Data.DataColumnChangeEventArgs) _
                                                    Handles Me.ColumnChanged
            If e.Column Is Me.LastNameColumn Then
                Me.CheckLastName(CType(e.Row, CustomerRow))
            End If
        End Sub
 
 
        Private Sub CustomerDataTable_TableNewRow(ByVal sender As Object, _
                                                  ByVal e As System.Data.DataTableNewRowEventArgs) _
                                                  Handles Me.TableNewRow
            Dim row As CustomerRow = CType(e.Row, CustomerRow)
            'This will fire the ColumnChanged event which will give
            'immediate feedback to user when a row is added.
            '(Stick other default values here too.)
            row.LastName = ""
        End Sub
    End Class
 
End Class
 
    如果你是创建自己的自定义业务逻辑对象或者使用LINQ to SQL 类,那么你需要自己实现IDataErrorInfo接口。我示范过如何在LINQ to SQL类实现这个的,当时是在一个业务逻辑基类中实现的。这里有个精简版的对一个customer Linq to SQL 类的实现示例,同样是验证LastName字段的有效性:
Partial Class Customer
    Implements System.ComponentModel.IDataErrorInfo
 
    'This dictionary contains a list of our validation errors for each field
    Private validationErrors As New Dictionary(Of String, String)
 
    Protected Sub AddError(ByVal columnName As String, ByVal msg As String)
        If Not validationErrors.ContainsKey(columnName) Then
            validationErrors.Add(columnName, msg)
        End If
    End Sub
 
    Protected Sub RemoveError(ByVal columnName As String)
        If validationErrors.ContainsKey(columnName) Then
            validationErrors.Remove(columnName)
        End If
    End Sub
 
    Public Overridable ReadOnly Property HasErrors() As Boolean
        Get
            Return (validationErrors.Count > 0)
        End Get
    End Property
 
    Public ReadOnly Property [Error]() As String _
        Implements System.ComponentModel.IDataErrorInfo.Error
        Get
            If validationErrors.Count > 0 Then
                Return String.Format("{0} data is invalid.", TypeName(Me))
            Else
                Return Nothing
            End If
        End Get
    End Property
 
    Default Public ReadOnly Property Item(ByVal columnName As String) As String _
        Implements System.ComponentModel.IDataErrorInfo.Item
        Get
            If validationErrors.ContainsKey(columnName) Then
                Return validationErrors(columnName).ToString
            Else
                Return Nothing
            End If
        End Get
    End Property
 
    Private Sub OnValidate(ByVal action As System.Data.Linq.ChangeAction)
        Me.CheckLastName(Me.LastName)
 
        If Me.HasErrors Then
            Throw New Exception(Me.Error)
        End If
    End Sub
 
    Private Sub OnLastNameChanging(ByVal value As String)
        Me.CheckLastName(value)
    End Sub
 
    Private Sub CheckLastName(ByVal value As String)
        If value = "" Then
            Me.AddError("LastName", "Please enter a last name")
        Else
            Me.RemoveError("LastName")
        End If
    End Sub
   
End Class
 
WPF中的数据绑定
 
   既然我们的数据对象能自己验证有效性,我们就能把它们绑定到一个Form上了。一旦你掌握了记住语法的窍门,在XAML中建立一个简单的包含一些TextBoxWPF窗口并绑定它们,是非常简单的。关键是确保你指定BindingValidateOnDataErrors属性并设置为True. 看看下面的XAML中的TextBox元素:
<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Customers" Height="253" Width="300" Name="Window1">
    <Grid Margin="6">
        <Grid.RowDefinitions>
            <RowDefinition Height="222*" />
            <RowDefinition Height="40*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="112*" />
            <ColumnDefinition Width="166*" />
        </Grid.ColumnDefinitions>
        <StackPanel Name="StackPanel1">
            <Label Name="Label1"
                   Width="Auto"
                   HorizontalContentAlignment="Right"
                   Margin="3">
                Last Name:</Label>
            <Label Name="Label2"
                   Width="Auto"
                   HorizontalContentAlignment="Right"
                   Margin="3">
                First Name:</Label>
            <Label Name="Label3"
                   Width="Auto"
                   HorizontalContentAlignment="Right"
                   Margin="3">
                City:</Label>
            <Label Name="Label4"
                   Width="Auto"
                   HorizontalContentAlignment="Right"
                   Margin="3">
                State:</Label>
            <Label Name="Label5"
                   Width="Auto"
                   HorizontalContentAlignment="Right"
                   Margin="3">
                ZIP:</Label>
        </StackPanel>
        <StackPanel Grid.Column="1" Name="StackPanel2">
            <TextBox
                Text="{Binding Path=LastName, ValidatesOnDataErrors=True}"
                Name="TextBox1"
                Height="28"
                Width="Auto"
                HorizontalContentAlignment="Left"
                Margin="3" />
            <TextBox
                Text="{Binding Path=FirstName, ValidatesOnDataErrors=True}"
                Name="TextBox2"
                Height="28"
                Width="Auto"
                HorizontalContentAlignment="Left"
                Margin="3" />
            <TextBox
                Text="{Binding Path=City, ValidatesOnDataErrors=True}"
                Name="TextBox3"
                Height="28"
                Width="Auto"
                HorizontalContentAlignment="Left"
                Margin="3"/>
            <TextBox
                Text="{Binding Path=State, ValidatesOnDataErrors=True}"
                Name="TextBox4"
                Height="28"
                Width="Auto"
                HorizontalContentAlignment="Left"
                Margin="3"/>
            <TextBox
                Text="{Binding Path=ZIP, ValidatesOnDataErrors=True}"
                Name="TextBox5"
                Height="28"
                Width="Auto"
                HorizontalContentAlignment="Left"
                Margin="3" />
        </StackPanel>
        <Button Name="btnAdd"
                Grid.Column="1" Grid.Row="1"
                Margin="0,0,79,6"
                Height="24" Width="75"
                VerticalAlignment="Bottom"
                HorizontalAlignment="Right" >Add</Button>
        <Button Name="btnSave"
                Grid.Column="1" Grid.Row="1"
                HorizontalAlignment="Right"
                Margin="0,0,0,6"
                Width="75" Height="24"
                VerticalAlignment="Bottom">Save</Button>
    </Grid>
</Window>
 
 
    现在我们能载入我们的数据,在Window_Loaded事件处理函数中赋给Window.DataContext. 如果你使用DataSet,那么像平常一样建立TableAdapter Query并填充数据。然后将Customer数据表赋给窗体的DataContext:
Me.CustomerTableAdapter.Fill(Me.MyCustomerData.Customer)
Me.CustomerTableAdater.Fill(Me.MyCustomerData.Customer)
 
 
 
如果你使用LINQ to SQL类,那么只要调用SQL DataContext来加载你的customer列表:
Dim db As New MyDatabaseDataContext
Me.DataContext = From Customer In db.Customers _
                 Where Customer.LastName = "Massi"
 
WPF的默认有效性验证错误模
 
   如果我们就这样执行的话,当我们的有效性验证失败的时候WPF会给我们一个默认的可见的线索. 控件边框将绘制为红色显示这里存在着问题,然而并没有错误信息显示. 对啊,这还真有帮助,你就等着你的技术支持热线的电话铃声响起吧,亲爱的。
 
 

设置自定义有效性验证Style
   我们显然希望能让用户知道这里的问题怎么解决.让我们来做一些简单的例子在ToolTip中来显示错误信息. 现在我们能在Window.Resources部分中创建一个Style,然后应用于这个Form中的TextBox. 这个 Style创建一个触发器,Validation.HasError变成True的时候 ,用来设置ToolTip属性为有效性验证的错误信息 .
<Window.Resources>
    <Style TargetType="TextBox">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                        Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
 
 
   现在当我们再次运行这个程序,当鼠标置于TextBox上的时候你能看到显示在ToolTip里的有效性验证信息. 这比之前的好了些 ! 但是这个办法只适用于TextBox. 那像 checkBox,ComboBox等这样的控件又怎么办呢 ? 我们需要把这些都声明在一个适应于整个应用程序的地方 .没问题,我们可以把这种Style贴在Application.Resources. 我们也能指定 TargetType=”Control”,这样我们就能定义另外的适合基于这种Style的其他控件的Style.打开Application.xaml,将它们加入Resources部分:
 
<Application.Resources>
    <Style TargetType="Control" x:Key="myErrorTemplate">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip"
                    Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                    Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style TargetType="TextBox" BasedOn="{StaticResource myErrorTemplate}" />
    <Style TargetType="CheckBox" BasedOn="{StaticResource myErrorTemplate}" />
    <Style TargetType="ComboBox" BasedOn="{StaticResource myErrorTemplate}" />
</Application.Resources>
    我们只需指定模板的x:Key,然后就能在继承的Style上设置BasedOn属性了 . 现在整个应用程序的控件都能应用这种Style了。
    替换整个的错误模板
    到目前为止,我们所做的是指定一个Style触发器了. 默认的WPF错误模板仍然在使用中因为我们还是看到控件的红边框. 这里我们能在Applicaion.Resource定义一个新的错误模板来完全的改变这个错误模板.让我们来通过一个简单的示例来示范创立一个在控件上显示一般错误信息的错误模板.Style 中的Trigger上面的部分(我们会把ToolTip信息放置于那儿),我们设置Validation.ErrorTemplate属性的值为我们自己的控件模板。
 
<Style TargetType="Control" x:Key="myErrorTemplate">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <TextBlock Foreground="Red" Text="DOH! Thank you for trying."/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip"
                Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>
      现在当我们再次运行,我们仍然可以看到我们的ToolTip当我们将鼠标放在控件上的时候.但是我们将在我们自己的控件模板中的TextBlock覆盖了原来的控件,注意:现在已经没有红色的边框了:

 

 

    好的, 我承认这是一个蹩脚的示例,.问题(除了这讽刺的消息)TextBlock确实覆盖了那控件,你必须将鼠标置于边缘来显示ToolTip. 另外一个问题当然是如果我们开始再次输入字段值,直到我们输入完毕之后那个信息才会消失,这是很不方便的.
   你可以插入一个DockPanel到控件模板里面,然后把TextBlock停靠在右边以在控件后面显示文本 (这里让我们只显示一个星号),如果说你仍然想有红色边框在控件周围,我们可以在我们的XAML中为错误模板的Setter.Value设置一个叫AdornedElementPlaceholder的特别元素:
<Setter Property="Validation.ErrorTemplate">
    <Setter.Value>
        <ControlTemplate>
            <DockPanel LastChildFill="True">
                <TextBlock DockPanel.Dock="Right"
                        Foreground="Red"
                        FontSize="11pt"
                        FontWeight="Bold">*
                </TextBlock>
                <Border BorderBrush="Red" BorderThickness="1">
                    <AdornedElementPlaceholder Name="myControl"/>
                </Border>
            </DockPanel>
        </ControlTemplate>
    </Setter.Value>
</Setter>
 

 

   

     在WPF中用.net framework 3.5来验证你的数据对象的有效性跟之前用IdataErrorInfo接口的Winform是一样的.然而,WPF式样和控件模板可以非常方便的显示可视线索给用户.如果你能发挥想象,你就可以用WPF来做.    

     享受这一切吧!
 
 

 

 

   

posted on 2008-09-17 17:07:12 by VBCTI  评论(0) 阅读(2202)

分部方法

[原文作者]:VB Team

[原文链接]:Partial Method

   大概两星期前,我有幸出席了微软举办的产品组晚宴和稍后举办的全球 MVP 最高级会议。除了晚宴,我还有机会见到了一些MVP, 谈了在Ocras中,他们对Visual Studio和对所有新特性的印象。晚宴时,我和一个来自日本的VB MVPS组谈话,还有另外五六个VB的组员。他们其中几个人都问了我关于分部方法的问题。不幸的是,我不会说日语,所以回答起来有些困难。用英语写出来要比我用带有很多习惯用语的口语解释容易懂得多,所以我决定把分部方法作为我博客的下一篇帖子。

   下面我列出了他们的问题和我的回答。

 
      什么是分部方法?

   概括的说,分部方法主要被代码生成器在处理轻量级事件的时候使用。分部方法必须是私有的空方法,并使用partial关键字(而分部方法和的声明和定义一般会分别写在一个类的两个partial定义中)。分部方法会在包含它的类中被重新执行。如果分部方法被重新执行,编译器会将所有对分部方法的调用重定向到正在执行的方法。如果一个分部方法没有在包含它的类中执行,编译器会在后台移除对这个方法的所有调用。这些与事件有不同的地方,主要有:

    1.事件可以有很多个句柄,分部方法只能有一个实现方法。
    2.事件的句柄可以在有访问事件实例的权限的任何类型中定义,分部方法的执行方   法必须定义在同一个类中就像分部方法必须在同一个类中一样。
    3.如果事件没有句柄而又被调用了,将会抛出一个事件。这时通常会将空的事件和安全的异常做比较。另一方面,如果分部方法没有执行体,编译器将会对它的所有调用做一个简单的优化。
    4.事件的链接是动态的,因为程序在运行时,句柄可以从事件上被添加或者移除。这个可以通过隐式的使用变量WithEvents和子句Handles,也可以显式的使用AddHandler和RemoveHandler语句来完成。分部方法的链接都是在编译的时候决定的。实现方法要和声明它的方法关联起来,只需要实现方法的名字和签名跟分部方法一样就可以了。这样一来,所有关于这个分部方法的调用将会被编译器重写并将调用指向它的执行方法。
    5.事件可以被声明为任何的访问权限(public,private,或者friend)。分部方法只能是私有的(private)。实现它的方法可以有任何访问权限。
        分部方法的一个例子:
 'Designer File
Partial Class DesignerGeneratedClass
    Partial Private Sub OnFoo() 
    End Sub

    Partial
Private Sub OnBar()
    End Sub

    Sub
DoSomething()
        OnFoo()
        OnBar()
    End Sub
End
Class

'Code Behind File
partial Class DesignerGeneratedClass
   Sub Public OnFoo Overridable ()
        Console.WriteLine("Foo Occured")
    End Sub
End
Class

   这里我们定义了一个类DesignerGeneratedClass,类里面有两个分部方法。这段代码模仿了典型的代码生成场景,一个分部方法包含设计者生成代码,另一个分部方法包含用户生成代码。举个例子,Windows Form类和ASP.Net 网络程序里的pages利用这个方法。在设计者产生的文件里面,我们定义了一个应用方法:DoSomething,它调用两个分部方法OnFoo和OnBar。用户文件只定义了OnFoo,没有定义OnBar。当VB编译器执行到这个类的时候,它注意到这点,给DoSomething生成了只会调用OnFoo()的方法体。(OnBar的方法体则被简单忽略)
     分部方法有什么用处?

   对于大多数程序来说,由于事件的灵活性,所以比起分部方法来是一个更好的选择。但是,在有些场景当中,分部方法会变得很好用。例如:

1.自动代码生成器
   分部方法可以让代码生成器通过创建很多“钩子”来写非常灵活的代码,开发者可以提供他们自定义的功能并集成到之前代码生成器的代码中。如果“钩子”不会被使用的话,它会被优化掉,所以即使创建了很多“钩子”也不会 造成性能方面的损失。如果生成的代码需要在高性能的场景下使用的话,分部方法会相当有用。例如:DLINQ设计者出于分部方法的这个优点使用了分部方法。开发者希望不需要DLNQ用户忍受性能问题的同时,又可以调用自定义代码自己的数据对象当这些数据对象的属性被设置的时候。
 
2.使用条件编译常量增加了代码的可读性
   看看下面的使用了条件编译常量的VB代码:
   下面的代码明显不具有可读性。所以用分部方法能使代码更有可读性:

Class UglyConditonalCompilationCode

#If DEBUG Then
    Private
Sub LogMessage(ByVal s As String)
        Console.WriteLine(s)
    End Sub
#End If

    Sub
DoStuff()
        DoFirstThing()
#If DEBUG Then
        LogMessage("Did first thing")
#End If
        DoSecondThing()
#If DEBUG Then
        LogMessage("Did second thing")
#End If
        DoThirdThing()
#If DEBUG Then
        LogMessage("Did third thing")
#End If
    End
Sub
End
Class

Class PrettyConditonalCompilationCode
    Partial Private Sub LogMessage(ByVal s As String)
    End
Sub

#If DEBUG
Then
    Private
Sub LogMessage(ByVal s As String)
        Console.WriteLine(s)
    End
Sub
#End
If

    Sub
DoStuff()
        DoFirstThing()
        LogMessage("Did first thing")
        DoSecondThing()
        LogMessage("Did second thing")
        DoThirdThing()
        LogMessage("Did third thing")
    End
Sub
End
Class
   (本例正是利用了部分方法LogMessage最终调用与否是由其是否被定义决定的这一特性)
    分部方法与AOP(面向方面编程)是一样的吗?
   不一样。在分部方法提供一个联系“钩子”和自定义代码的渠道上来说它确实和。AOP有相似之处然而,不像AOP系统,分部方法要求扩展点由用户显式定义。AOP系统允许开发者将隐式定义的“钩子”的功能性用另外的渠道“迂回”的添加到代码里。他们通常的做法是,在使用“钩子”的地方指定一个修饰搜索字符串,表示这个地方需要实现“钩子”。编译器负责找到所有合适的扩展点,以实现“钩子”和注射所有需要的方法调用。VB中的分部方法没有这样做。

posted on 2008-09-08 12:30:29 by VBCTI  评论(0) 阅读(3339)

使用Visual Studio插件浏览Office2007 文档

[原文链接]:Handy Visual Studio Add-In to View Office 2007 Files

[原文作者]:Beth Massi

      上个月我在Redmond的时候向一个同事提起过我真的非常欣赏Office 2007正在应用的Open XML的格式Opem XML 是继如doc、.dot、.xls 和 .ppt等二进制 文件后一种全新的office文件格式)和我是如何应用这种格式及”LINQ toXML” 的.他也向我介绍了VSTO Power Tools,这个工具包含了称作Open XML Package Editor 的Visual Studio 插件。 这个工具可以让用户通过简洁小巧的树状菜单在Word,PowerPoint和Excel中浏览Office2007 文档,这可以使得用户操纵Open XML 文件和XML中的其它各个部分。在安装了VSTO Power Tools 后, 只要从Visual Studio项目中直接双击Office 2007 文件打开这个工具就可以使用了。

<来自VSTO Power Tool文档>
      打开XML Package Editor
      这是一个Visual Studio2008插件,它允许分析和编辑Open Packaging Conventions的文件, 包括Word, Excel and PowerPoint 文档。它的特性如下:
  • 在Visual Studio里直接打开Office 2007 Open XML Package 文件或者 XPS Package 文件。 (Office XML 格式是针对 Office Word 2007、Office Excel 2007 和 Office PowerPoint 2007 的完全可编辑文件格式,XPS 是为 Microsoft Windows Vista 操作系统推出的标页码的固定文档格式。)
  • 直观和易于浏览的Package 文件树状结构。
  • 可以在Visual Studio的rich XML 编辑器里直接打开文件所包含的XML部分。
  • 易于使用的用户接口去添加和移除文件的相关部分部件和关连。
  • 对文件导入导出所包含的各个部分的内容。
  • 探测在Visual Studio中打开的文件是否在外部被更改了。并提示用户重载文件在不需要关闭任何打开的XML 部件编辑器。
  • 通过Visual Studio 的新建对话框提供的一套模板来创建新的Office Packages

      图一:显示了Visual Studio中打开的Open XML Package文件树形结构:

      图二:如果您双击任何文件中的XML组成部分,被点击的部分将在标准的Visual Studio XML编辑其中打开。
 
<文章结束>
      我注意到了一件事情,如果XML编辑器打开着,并且XML内容全部集中在一行上,您可以选择所有内容然后剪切并黏贴回编辑器,这时XML内容会自动进行分行并回到正常显示格式。(Ctrl + A,X,V).
还有其它9个工具包括在VSTO Power Tools 中,我强烈推荐使用如果你正在使用Visual Studio进行Office开发的话。 并且您可以查看VSTO Team BlogVSTO Developer Portal 去了解更多Visual Studio 的Office开发信息。
 
Enjoy!

  

posted on 2008-09-08 11:24:22 by VBCTI  评论(0) 阅读(4013)

Powered by: Joycode.MVC引擎 0.5.2.0