在Visual Studio 2010 里,Dotfuscator变得更好(依然免费)

[原文作者]:Beth Massi     

[原文链接]:http://blogs.msdn.com/bethmassi/archive/2010/02/23/dotfuscator-gets-better-and-still-free-in-visual-studio-2010.aspx

        这周我学习了Visual Studio 2010里面的一个由 PreEmptive Solutions 提供的免费工具Dotfuscator软件服务,如果您正在使用Visual Studio2008的版本,那么你已经熟悉了其代码模糊技术(code obfuscation technology)。你将在2010里更深入了。新增加了一个全新基于代码注入的功能类,例如防止篡改和通知, 正如性能和使用的监督一样 。 如果你曾经想通过你的程序收集用户实际操作程序的信息来改进软件,它可以为你提供解决方案。

        我一直和一些人制作操作演示视频,展示如何让程序迅速启动和更快运行 。这些视频这品将会在 Developer Center Visual Basic开发中心发布,敬请期待。

        目前为止我所学到的,用Visual Studio 2010可以非常简单的建立你的程序并且编译这些功能。之后程序和一个叫做Runtime Intelligence Services Portal免费云服务通讯,它可以收集正在运行的程序的数据。但是,如果你不想使用PreEmptive的免费云服务,希望用自己的终端来收集信息,该怎么做?你十分幸运,因为今天PreEmptive发布了一个用于创建并且托管的终端开源的软件stater kit,在CodePlex上得到它:http://riendpointkit.codeplex.com/

        真的吗,如果它全部是免费的,这些家伙怎么赚钱?虽然Visual Studio2010的免费版中有一些伟大的功能,但是, 当然,专业版本有更多强大的功能,请看看这里两者的功能比较

        希望你会喜欢!

WPF在SP1中更好的数据编辑功能

[原文作者]:Beth Massi    
[原文链接]:Better Data Editing Features in WPF with SP1       
 
 
   当我开始钻研数据绑定时,在WPF中我最想念的一项功能,是在BindingListCollectionView和我所喜欢的winforms BindingSource之间的一致性功能。BindingListCollectionView就像在winforms下的资源捆绑一样,提供了限制收集数据(或数据表)的导航、流动、过滤和排序。
   然而在合集中并不支持处理添加和删除项目。你可以从我的WPF窗体数据录像中注意到,当我从数据中添加或者删除一行时,我只能直接进入数据表进行操作。当我用数据表工作时这不会有问题,因为他们可以做他们自己的处理编辑(连同修改记录)。不过,为了有良好的客户业务集合,实现典型的绑定接口,这通常是一个必要的功能
SP1在WPF’s BindingListCollectionView中增加了新的属性和方法
   发布的Visual Studio/.NET FX SP1已经增强了BindingListCollectionView,包括新的属性和方法:CanAddNew 属性, CanCancelEdit 属性, CanRemove属性, CurrentAddItem属性, CurrentEditItem属性, IsAddingNew属性, IsEditingItem属性, ItemProperties属性, NewItemPlaceholderPosition属性, AddNew方法, CancelEdit 方法, CancelNew 方法, CommitEdit 方法, CommitlNew 方法, EditItem 方法, Remove 方法, RemoveAt 方法.
   有一件事情我想在这里指出,不同的是,在Winforms中,无论是修改或增加数据源(也就是数据表或集合),我们都习惯于访问BindingSource上的EndEdit来实现处理变换。在WPF中,有一个CommitEditCommitNew的单独访问,如果在增加的时候调用了AddNew,你必须要确认你不是在调用CommitEdit,否则你就只能调用CommitNew。我不知道他们为什么把这两者区分开来。在使用DataSets时,我总是几乎立刻提交处理(如在填写默认值后),并且可以通过Accept/RejectChanges方法来使用DataSet的修改记录。
 
用新的AddNew和Remove Methods来增加或删除数据
   当我们想在数据表中增加一行新的数据,我们现在可以直接调用BindingListCollectionView中的AddNew。例如:
Private OrderData As New OrdersDataSet
Private OrdersViewSource As BindingListCollectionView
Sub New()
    ‘ This call is required by the Windows Form Designer.
    InitializeComponent()
 
    ‘ Add any initialization after the InitializeComponent() call.
    Me.LoadData()
    Me.DataContext = Me.OrderData.Orders
    Me.OrdersViewSource = CollectionViewSource.GetDefaultView(Me.DataContext)
End Sub
Private Sub AddNewOrder()
    ‘— Old Code —
    ‘Add a new row to the collection
    ‘Dim order = Me.OrderData.Orders.NewOrdersRow
    ‘Me.OrderData.Orders.AddOrdersRow(order)
    ‘Up to us to update the position
    ‘Me.OrdersViewSource.MoveCurrentToLast()
 
    ‘— New Code —
    ‘Add a new row to the collection
    Me.OrdersViewSource.AddNew()
    ‘Push changes into the DataTable
    Me.OrdersViewSource.CommitNew()
End Sub
 
 
   首先我通过DataSetpartial类来处理所有的验证和设置的默认值,因此如果我们工作于自己的商业对象集合时,AddNewOrder()方法仍将保持不变。这就是为什么我习惯于用Winforms,因为你可以轻松地交换数据源,又不会弄乱数据绑定代码。不过需要指出的是,如果CommitNewDataRow无法被调用,你再写入DataSet时就会报错。所以一定要确保调用在数据表partial类中的TableNewRow事件,并且写入有效的默认值。
删除当前行也简单:
Private Sub RemoveOrder()
    If Me.OrdersViewSource.CurrentPosition > -1 Then
        ‘— Old Code —
        ‘Dim order As OrdersDataSet.OrdersRow
        ‘order = CType(CType(Me.OrdersViewSource.CurrentItem, DataRowView).Row, OrdersDataSet.OrdersRow)
        ‘order.Delete()
 
        ‘— New Code —
        Me.OrdersViewSource.Remove(Me.OrdersViewSource.CurrentItem)
    End If
End Sub 
 
WPF中使用Master-Detail窗体
 
   这些都是很好的改进,但是由于BindingSource是一个可视化的组件,并且能够处理CurrencyManager 目前Winforms中的BindingSource仍然比IMHO方便。当你在WPF中用master-detail绑定是一件痛苦的事情。你必须获得一个BindingListCollectionView的详细引用,每次detail view有变动,都需要再次引用(这将是ListBox或者ListView上的一个ItemSource)。如果你在XAML中能正确地建立绑定,那么当父模块变换时detail view就会自动变换。然而,如果你想在子模块中用AddNew,你就需要实时在每次都获得一个代码的引用,因为视图是动态的。Winforms 绑定源处理这种情况更好些。
   尽管我们需要每次都获得引用,这也是相当简单的:
Private Sub AddNewDetail()
    If Me.OrdersViewSource.CurrentPosition > -1 Then
        ‘— Old Code —
        ‘Dim order As OrdersDataSet.OrdersRow
        ‘order = CType(CType(Me.OrdersViewSource.CurrentItem, DataRowView).Row, _
        ‘              OrdersDataSet.OrdersRow)
        ‘Dim detail = Me.OrderData.OrderDetail.NewOrderDetailRow
        ‘detail.OrderID = order.OrderID
        ‘Me.OrderData.OrderDetail.AddOrderDetailRow(detail)
 
        ‘— New Code —
        Dim detailView As BindingListCollectionView = _
                          CollectionViewSource.GetDefaultView(Me.lstDetails.ItemsSource)
        detailView.AddNew()
        ‘Note that the related OrderID is set for us automatically just like Winforms
        detailView.CommitNew()
    End If
End Sub
 
Private Sub RemoveDetail()
    If Me.OrdersViewSource.CurrentPosition > -1 Then
 
        Dim detailView As BindingListCollectionView = _
                          CollectionViewSource.GetDefaultView(Me.lstDetails.ItemsSource)
 
        If detailView.CurrentPosition > -1 Then
            ‘— Old Code —
            ‘Dim detail As OrdersDataSet.OrderDetailRow
            ‘detail = CType(CType(Me.OrderDetailsViewSource.CurrentItem, DataRowView).Row, _
            ‘               OrdersDataSet.OrderDetailRow)
            ‘detail.Delete()
 
            ‘— New Code —
            detailView.Remove(detailView.CurrentItem)
        End If
    End If
End Sub
 
   通过使用新的AddNewRemove方法似乎也能解决一些问题,我曾经遇到过使用LINQ to SQL产生子模块集合(EntitySets),所以最好开始运用这些新方法的优势。在以后的博客帖子中,我会用我们所做的LINQ to SQL N-Tier application,在末尾补上WPF Front
   我可能不会更新我们已经做的工作的视频,但以后我会在适当的时候使用这些新的方法和属性,所以请下载SP1
Enjoy!

用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>

 

 

 

 

基于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。

使用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站点上给出你的反馈。