装配中的脑袋

用程序装配大脑,再用大脑装配程序
随笔 - 118, 评论 - 1214, 引用 - 11

导航

关于

如果想发较大的信件,请用Ninputer @ gmail.com

不要在我的Blog评论中张贴广告,除非同意向我付款。

标签

每月存档

最新留言

广告

Excel开发(VSTO2005):简化工作表中选定区域的操作。

这是我第一次发Office开发相关的帖子。说到Office开发,我只能算新手。这次是碰巧开发了一个Excel智能文档项目,其中用到了这个小小的技巧,就发出来让大家看看。
在Excel开发中,工作表上最基本也是最常用的元素就是Range,Range可以表达一个获任意多个单元格或者矩形区域的组合,其复杂程度相当高。如果我们的智能文档程序要与用户打交道的话,势必要编程控制文档中的单元格,或与用户选择的单元格交互。而Range的对象模型并不符合.NET开发人员的习惯,要想获取用户选中区域的形状或者操作特定形状的区域都十分繁琐。而MSDN和VSTO的推销人员们只关心诸如怎么把单元格和数据源或者XML绑定之类,这种“小事”只能靠我们自己动手了。我的任务就是编写一个Range的封装类,将Range中所有单元格和矩形区域转化为易于访问的对象模型。首先我们看看Range的组成,一个普通的Range可以是一个或多个矩形区域的集合,每个矩形区域都由一组连续的列和连续的行组成。其中行使用阿拉伯数字索引,而列采用字母索引。如图所示:

注意,多个矩形区域可以不连续,还可以交叠。每个Range都有一个描述其位置的字符串,称为Range的地址字符串。地址字符串不但包含所有位置信息,还可以被Excel用来直接快速定位,所以我们就以地址字符串为桥梁,编写我们的包装类。
ColumnWrapper类:主要用于吧表示列的字符串“A”,“B”,“AA”等转化为1开始的整数序列,或者相反。我这里用到的算法可以支持无限大的整数与列名字符串互转,但其实Excel只支持到256列。

ColumnWrapper

有了ColumnWrapper,下面就是CellWrapper,表示单个单元格。
CellWrapper

接下来我们要表示矩形区域RectRangeWrapper。我们用来表示矩形区域。当我们需要计算矩形区域的大小时,只要使用着两个角单元格的位置信息即可算出。
RectRangeWrapper

最后是多个矩形区域组成的完整Range,我们用MultiRectRangeWrapper类来描述。它其实就是一个矩形区域的集合。
MultiRectRangeWrapper

好了,现在四个包装类已经全部写完了。用法就简单多了。用Range.Address属性初始化MultiRectRangeWrapper类,就可以得到一个该Range中所有矩形区域的集合,进而轻松访问该举行区域中每个单元格。比如下面这个例子,展示了一些简单的区域操作:
'取得用户选定的区域
Dim myrange As New MultiRectRangeWrapper(Application.Selection.Address)
For Each rectRange As RectRangeWrapper In myrange.Areas
    
If Not rectRange.IsWideRange Then
        
'非扩展选择的区域,比如整行或整列
        '修改左上角的数字格式
        Me.Range(rectRange.LeftTopCell.Address).NumberFormat = "0.00"

        
'将最后一个选中区域复制到剪贴版
        Me.Range(rectRange.Address).Cut()
    
End If
Next

我写这个只是为了我自己写程序方便,并没有考虑太多因素,所以可能写得比较粗糙,功能有限。有兴趣的可以在我这程序的基础上继续改进。

posted on 2006-03-23 11:49:00 by Ninputer  评论(9) 阅读(8505)

泛型技巧系列:避免基类及接口约束

本系列未经许可,禁止转载(包括网络媒体刊载)

.NET泛型的一大特点是在编译阶段对类型参数不做任何假设。也就是说,面对类型参数T和他的变量,你没有什么能做的——不能调用除Object成员之外的任何方法,不能进行大多数运算符的运算等等。它提供了一个叫约束的机制,能在编译期对类型实参的取值进行一些检查。许多人都将约束视为在类型参数上提供操作支持的唯一方法,并大量使用——你有没有约束过IComparable呢?但是,这种做法是不对的,因为约束仅仅能检测声明类型是否实现了某接口或继承自某类,但通过接口和基类实现的多态机制是一个运行时检查的机制,约束没有从任何方面帮助接口和基类工作。实际上,绝大多数情况下,你用一个约束了IComparable的类型参数T来编程序,和直接用IComparable作为你的操作类型没有什么不同。

约束的真正目的是减少类型实参的取值范围,也就是降低抽象性,是一个泛型具体化的手法。只有当你清楚,你的目的就是“约束”本身,而不是想给类型参数增加些操作的时候,才应该使用约束。那么只想给类型参数增加操作应该怎么办呢?我推荐的方法是:将实现该操作的功能封装到一个外部的辅助类中。比如我们常常想约束IComparable,因为我们想比较类型参数对象的大小。有很多人都用这种方法:

[VB]
Sub
Sort(Of T As IComparable(Of T))(arr As T())

[C#]
void Sort<T>(T[] arr) where T : IComparable<T>

显而易见,Sort需要数组元素能够比较大小,那么为什么约束不好呢?注意我们约束的是T实现IComparable(Of T),如果T实现的是IComparable呢,就不能比较大小了吗?约束无法表达“实现了IComparable(Of T)或IComparable”这种情况。更进一步,仅有这样的对象才能比较大小吗?有许多类型并没有实现这两个接口中的任何一个,但是仍有比较大小的可能,我们一旦约束就等于将他们拒之门外。因此约束是不适合解决此类问题的。记住:我们唯一需要的就是比较,因此正确的方法是这样:

[VB]
Sub
Sort(Of T)(arr As T(), comparer As IComparer(Of T))

[C#]
void Sort<T>(T[] arr, IComparer<T> comparer)

 注意我们对T现在没有任何限制,但是要求提供一个IComparer(Of T)的实例。这个IComparer(Of T)的实现和T可以没有关系,因此不仅T可以不知晓它的存在,还可以提供不只一种的比较方法。但是这样做,无形要求用户必须提供一个额外的辅助对象,对于一些显而易见可比较大小的对象(如Int32),这种要求显得有些多余。那我们的解决方法就是提供一个默认的比较器:

[VB]
Sub
Sort(Of T)(arr As T())
    Sort(arr, Comparable(Of T).Default)
End Sub

[C#]
void Sort<T>(T[] arr)
{
    Sort(arr, Comparable<T>.Default);
}

代码中的Comparer(Of T)是System.Collection.Generic下的一个类型,专门用于提供类型默认的比较器。注意到什么了吗?我们现在没有约束了,但是对用户来说,和有约束时的语法一样简单而清晰。比较那些实现了IComparable和IComparable(Of T)的类型时,Comparer(Of T)都提供了支持,而在比较没有实现这类接口的自定义类型时,可自行实现IComparer(Of T)提供比较机制。完美解决。

这种方法还能进行一些约束都实现不了的做法:支持运算符。我们知道在类型参数上实现哪怕最简单的加法都是不允许的,而且没有任何接口可以帮你做到这一点。这时如果能够使用外部辅助类的做法,就能够突破这一恼人的限制,比如VBF就用了下面一个机制来计算类型参数的加法。

[VB]
Function
 Add(Of T)(a As T, b As T, calc As ICalculator(Of T))As T
    Return calc.Add(a, b)
End Function

Function Add(Of T)(a As T, b As T)As T
    Return Calculator(Of T).Default.Add(a, b)
End Function

[C#]
T Add<T>(T a, T b, ICalculator<T> calc)
{
    return calc.Add(a, b);
}

T Add<T>(T a, T b)
{
    return Calculator<T>.Default.Add(a, b);

}

现在剩下的问题就是,诸如Comparer(Of T)和Calculator(Of T)这类默认的比较器和计算器是如何实现的?如何保证所实现操作的高效率?这就是我们下一次的任务——类型字典和Type Traits。

posted on 2006-03-18 14:21:00 by Ninputer  评论(26) 阅读(8274)

Powered by: Joycode.MVC引擎 0.5.2.0