的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
源码指引:github源码指引_初级代码游戏的博客-CSDN博客
前一篇VSTO(C#)Excel开发4:打印设置-CSDN博客
Excel打印可以直接把表格打印到一页,但是用的是缩放的方法,而且不会改变纵横比例,如果比例不合适,会留下大片空白。如果我们希望不要缩放而是通过改变列宽和行高来平均铺满纸张呢?
前面研究了打印设置,发现并没有很简单的方法通过直接计算来把表格铺满一页,怎么办呢?幸好PageSetup.Pages.Count总是正确指示一共有多少页,因此可以用一些技巧来实现调整行宽和列高来使表格铺满一页。
这是纯粹的编程技巧,或者说算法,不包含任何新的关于对象模型的知识点。
目录
一、算法思想
虽然我不知道厘米、磅、字符宽度、像素之间什么关系,但是我们可以通过尝试调整列宽和行高,找到刚好是一页纸的最佳值。
一个最简单的思路就是把所有行和列都设为最小值,然后一点一点增加,直到打印页面变为2,最后回到之前的值即可。
每一步增加的值存在一个合理的最小单位,低于最小单位的差异是没有意义的,因为打印机的精度不高,肉眼的精度更低。
步子太小需要尝试很多次,很容易想到先尝试较大的步子,再逐渐缩小步子,算法上类似于用二分查找替代线性查找。
二、源码
源码用了一个新按钮,按钮我们很熟了,就不重复说了。按钮名字叫“button_FitToOnePage”,单击事件的代码如下:
private void button_FitToOnePage_Click(object sender, RibbonControlEventArgs e)
{
string str = "开始操作。。。。。。\n";
try
{
Worksheet worksheet = Globals.ThisAddIn.Application.ActiveSheet;
Range usedRange = worksheet.UsedRange;
int firstColumn = usedRange.Column;
int columnCount = usedRange.Columns.Count;
int firstRow = usedRange.Row;
int rowCount = usedRange.Rows.Count;
str += " Pages:" + worksheet.PageSetup.Pages.Count + "\n";
double originalTotalWidth = 0;
double originalTotalHeigh = 0;
double[] originalWidth = new double[columnCount];
double[] originalHeight = new double[rowCount];
for (int i = 0; i < columnCount; ++i)
{
Range colum = worksheet.Columns[firstColumn + i];
originalWidth[i] = colum.ColumnWidth;
originalTotalWidth += colum.ColumnWidth;
}
for (int i = 0; i < rowCount; ++i)
{
Range row = worksheet.Rows[firstRow + i];
originalHeight[i] = row.RowHeight;
originalTotalHeigh += row.RowHeight;
}
str += " originalTotalWidth:" + originalTotalWidth + "\n";
str += " originalTotalHeigh:" + originalTotalHeigh + "\n";
double step = 100;//步进,如果超出一页就减小步进值,直到步进小于某个值
double fix = 0;
while (worksheet.PageSetup.Pages.Count == 1 && step >= 0.1)//注意列宽单位是标准字符
{
fix += step;
for (int i = 0; i < columnCount; ++i)
{
Range colum = worksheet.Columns[firstColumn + i];
colum.ColumnWidth = originalWidth[i] + fix * 0.1;
}
if (worksheet.PageSetup.Pages.Count > 1)
{
fix -= step;
step /= 2;
for (int i = 0; i < columnCount; ++i)
{
Range colum = worksheet.Columns[firstColumn + i];
colum.ColumnWidth = originalWidth[i] + fix * 0.1;
}
}
}
str += " Pages:" + worksheet.PageSetup.Pages.Count + "\n";
step = 100;
fix = 0;
while (worksheet.PageSetup.Pages.Count == 1 && step >= 1)//注意行高单位是像素
{
fix += step;
for (int i = 0; i < rowCount; ++i)
{
Range row = worksheet.Rows[firstRow + i];
row.RowHeight = originalHeight[i] + fix;
}
if (worksheet.PageSetup.Pages.Count > 1)
{
fix -= step;
step /= 2;
for (int i = 0; i < rowCount; ++i)
{
Range row = worksheet.Rows[firstRow + i];
row.RowHeight = originalHeight[i] + fix;
}
}
}
str += " Pages:" + worksheet.PageSetup.Pages.Count + "\n";
str += "操作成功完成\n";
worksheet.PrintPreview();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
MessageBox.Show(str);
}
三、效果
先执行button1得到一些数据:
按钮AutoFit就是AutoFit,之前的源码提出来的。
button_FitToOnePage的label是FitToOnePage,点一下:
自动打开了打印预览,确实看到平铺成一页了。
注意这个代码只能把填不满的放大,如果已经是超过一页是不会去缩小的。
四、算法详解
1.1 获取初始值
代码中除了PageSetup.Pages.Count是每次都重新取,要处理的范围是一开始获取到后存成简单变量,不会再动态获取的,这是一个编程的好习惯,很多值可能在运行中被并行修改,可能会导致程序异常。比如有数据的列数,如果代码正在运算同时手工改变了数据,那么存原始值的数组没有变,代码使用新的列数会导致出错。
1.2 步进值step
步进值从一个比较大的数开始,然后循环增加列宽或行高,如果超出一页就退回上一次的值,并把step减半,step设置了最小值,低于最小值就结束。
1.3 增量的计算
列宽单位是字符,最小step设置为0.1,行高单位是像素,最小设置为1好了。
编程的很多技巧是纯算法的,整个功能在完全不理解各种参数的实际意义的情况下就完成了,而且适应任何变化(意即没有任何不必要的假设)。
(这里是文档结束)