When programmatically generating a "normal" Excel sheet, a Range's PageBreak property can be set to xlPageBreakManual and then you can specify where the page is to break with code like so:
workbook.Worksheets[0].VPageBreaks.Add(sheet.Range["G1"]);
...but is this possible when printing a PivotTable? I would think not, since the PivotTable is fed from a datasource (a page of "raw data" in my case, which I subsequently hide), but if it is, I would like to know how.
This is what I do now to prepare the PivotTablized sheet for printing:
private void FormatPivotTable()
{
. . .
ConfigureForPrinting(_xlPivotTableSheet.UsedRange.Rows.Count);
}
private void ConfigureForPrinting(int finalRow)
{
string lastColumn = GetExcelTextColumnName(_xlPivotTableSheet.UsedRange.Columns.Count);
string printArea = String.Format("A1:{0}{1}", lastColumn, finalRow);
_xlPivotTableSheet.PageSetup.PrintArea = printArea;
_xlPivotTableSheet.PageSetup.Orientation = XlPageOrientation.xlLandscape;
_xlPivotTableSheet.PageSetup.Zoom = false;
_xlPivotTableSheet.PageSetup.FitToPagesWide = 1;
_xlPivotTableSheet.PageSetup.FitToPagesTall = 50;
_xlPivotTableSheet.PageSetup.LeftMargin = _xlApp.Application.InchesToPoints(0.5);
_xlPivotTableSheet.PageSetup.RightMargin = _xlApp.Application.InchesToPoints(0.5);
_xlPivotTableSheet.PageSetup.TopMargin = _xlApp.Application.InchesToPoints(0.5);
_xlPivotTableSheet.PageSetup.BottomMargin = _xlApp.Application.InchesToPoints(0.5);
_xlPivotTableSheet.PageSetup.HeaderMargin = _xlApp.Application.InchesToPoints(0.5);
_xlPivotTableSheet.PageSetup.FooterMargin = _xlApp.Application.InchesToPoints(0.5);
string rangeToRepeat = string.Format("$A$6:${0}$7", lastColumn);
_xlPivotTableSheet.PageSetup.PrintTitleRows = rangeToRepeat;
}
What the user wants is that each logical block of data be kept together on a sheet, so that a block (5 rows) does not span more than one sheet. IOW, if there are less than 5 rows of space on the current page when a block begins, skip to the next page with a page break.
Is this possible with PivotTables?
I understand the that when you are referring to "logical blocks of data" it means data corresponding to a PivotFieldItem. If that is the case the try this:
Set these PivotTable properties to TRUE:
ActiveSheet.PivotTables("PivotTable1").PrintTitles = True
ActiveSheet.PivotTables("PivotTable1").RepeatItemsOnEachPrintedPage = True
Also set to TRUE the LayoutPageBreak property of the PivotField for which you want to set a page break every time there is a change of item:
ActiveSheet.PivotTables("PivotTable1").PivotFields("Class").LayoutPageBreak = True
Also need to set this property to TRUE in case there are items with only one record.
ActiveSheet.PivotTables("PivotTable1").PivotFields("Class").LayoutBlankLine = True
Replace "PivotTable1" and "Class" values as required
Although the commands above are in VBA they should give you a good idea of how to set these properties using C#
Related
I am not finding a way to set the ContentControl.Range.Text from where the C# is executing from (inside the content control). Perhaps I should be looking at it from a completely different perspective.
Currently I have a content control that produces a set of text with some text between [] square brackets and I want to select text and format the colour by setting the start and end of the range of characters between the []. I am stuck on trying to set the initial range to the contentcontrol I am currently using.
Most of what I have managed/found/patched together below.
object word;
Microsoft.Office.Interop.Word.Document _PWdDoc;
try
{
word = System.Runtime.InteropServices.Marshal.GetActiveObject("Word.Application");
//If there is a running Word instance, it gets saved into the word variable
}
catch (Exception ex)
{
//If there is no running instance, it creates a new one
Type type = Type.GetTypeFromProgID("Word.Application");
word = System.Activator.CreateInstance(type);
}
Microsoft.Office.Interop.Word.Application oWord = (Microsoft.Office.Interop.Word.Application) word;
_PWdDoc = oWord.ActiveDocument;
System.Collections.IEnumerator ContentX = _PWdDoc.ContentControls.GetEnumerator();
//Microsoft.Office.Interop.Word.ContentControl ContentX = Microsoft.Office.Interop.Word.ContentControls.Item[];
//Microsoft.Office.Interop.Word.Range rng = Microsoft.Office.Interop.Word.ContentControl.Range.Duplicate(ref ContentX);
//var rngX = Microsoft.Office.Interop.Word.ContentControl.Range(ContentX);
//Microsoft.Office.Interop.Word.ContentControl cc1 = ContentX;
Excuse the coding mess but it's all I can come up with with the minimal experience I have with this.
Now I have gotten the IEnumerator fo the Content Control(I think) I have no idea how to use it besides from what I have read, they say to iterate through the IEnumerables accessing each of them. That's not what I want to do. I want 1 content control. The current one that I am working in. I want to find it's range and assign it to a value. Then in that range's "text" I want to do some [fancy] highlighting.
Determining whether the current selection or a specific Range is in a content control and doing something with that content control is not a trivial matter. Most other Word objects will return something that they're "in"; content controls do not.
So the approach I use is to
create a Range that reaches from the current selection (or a specific Range) back to the beginning of the document
count the number of content controls in that range
then check whether the current selection is in the same range as the last content control of the extended range.
if it is, then I know the selection is within a content control and I can access the content control.
Here's some sample code. The snippet that calls the function I use to return the information:
Word.Range rng = null;
//Substitute a specific Range object if working with a Range, rather than a Selection
Word.ContentControl cc = IsSelectionInCC(wdApp.Selection.Range);
if ( cc != null)
{
rng = cc.Range;
rng.HighlightColorIndex = Word.WdColorIndex.wdYellow;
}
The function:
private Word.ContentControl IsSelectionInCC(Word.Range sel)
{
Word.Range rng = sel.Range;
Word.Document doc = (Word.Document) rng.Parent;
rng.Start = doc.Content.Start;
int nrCC = rng.ContentControls.Count;
Word.ContentControl cc = null;
bool InCC = false;
rng.Start = doc.Content.Start;
if (nrCC > 0)
{
if (sel.InRange(doc.ContentControls[nrCC].Range))
{
InCC = true; //Debug.Print ("Sel in cc")
cc = doc.ContentControls[nrCC];
}
else
{
sel.MoveEnd(Word.WdUnits.wdCharacter, 1);
if (sel.Text == null)
{
//Debug.Print ("Sel at end of cc")
InCC = true;
cc = doc.ContentControls[nrCC];
}
}
}
return cc;
}
Assuming you mean that the insertion point is inside a Content Control, and your Word Application object is called oWord, then you can get the range of that content control using e.g.
Microsoft.Office.Interop.Word.Range r = oWord.Selection.Range.ParentContentControl.Range
If you have nested controls You can verify that the insertion point is in a Content Control (Word 2013 and later, I think) by checking the value of inCC as follows:
Boolean inCC = (Boolean)oWord.Selection.Information[Microsoft.Office.Interop.Word.WdInformation.wdInContentControl]
However, when dealing with content controls, be aware that selecting a content control in the UI is different from selecting the "range of the content control". Programmatically, it's obvious how to select the Range - not so obvious how to select the control. If you select the Range, the ParentContentControl should be the control whose range you've selected. If you (or the user) selected the control, OTTOMH I am not so sure.
I hope this easy but I can't seem to find it. I'm working with a word document object in an Outlook VSTO project to modify values that are located between hidden key values.
IE [key_start] text [key_end]
When they make selections in the drop down from the addin I change the text in the body of the email for them.
To make this work
1) I un-hide all my keys
Word.Document doc = Inspector.WordEditor as Word.Document;
doc.Content.Font.Hidden = 0;
2) Then find my keys and generate a range between them
int start_pos = -1;
int end_pos = -1;
//SelectTextRange is a custom function to find range based on text
ValueRange = Custom.WordDocument.SelectTextRange(doc, key_value_start);
if (ValueRange != null) {
start_pos = ValueRange.End;
}
ValueRange = Custom.WordDocument.SelectTextRange(doc, key_value_end);
if (ValueRange != null) {
end_pos = ValueRange.Start;
}
3) Then update the text.
if (start_pos > -1 && end_pos > -1) {
ValueRange = doc.Range(start_pos, end_pos);
ValueRange.Text = " new text goes here ";
}
4) Then hide my keys again.
Everything works great but it looks a little tacky when the changes cascade through the doc as it updates. It looks as though the document updates on every command and doesn't wait until all my commands are finished. Is there a way to prevent the document from committing changes so I can make it do this all in one shot and not have the user see flickers of hidden text when this process occurs?
YowE3K nailed it. Thank you again.
Adding this to the beginning of my changes
doc.Application.ScreenUpdating = False
And then adding this to the end
doc.Application.ScreenUpdating = True
Did exactly what I needed. All my changes occured without the document updating and then once I set doc.Application.ScreenUpdating = True it draws the updated document with all my changes.
Is there a way to set specify where to break the page using EEPlus? I have the following code that sets the printer properties but haven't found a way to set a break point on a certain column.
// Set printer settings
ws.PrinterSettings.PaperSize = ePaperSize.Tabloid;
ws.PrinterSettings.Orientation = eOrientation.Landscape;
ws.PrinterSettings.FitToPage = true;
ws.PrinterSettings.FitToHeight = 1;
ws.PrinterSettings.FooterMargin = .05M;
ws.PrinterSettings.TopMargin = .05M;
ws.PrinterSettings.LeftMargin = .05M;
ws.PrinterSettings.RightMargin = .05M;
Edit (this helped solve my problem)
ws.Column(30).PageBreak = true;
ws.PrinterSettings.PaperSize = ePaperSize.A3;
ws.PrinterSettings.Orientation = eOrientation.Landscape;
ws.PrinterSettings.Scale = 75;
Just need to get reference to the Row and/or Column objects:
ws.Row(20).PageBreak = true;
ws.Column(2).PageBreak = true;
But keep in mind that FitToPage might suppress these.
I have a Windows Form that was mainly made using the graphical editor. It is connected to a database called, Database1. One of the tables in the database is called Table1, and contains the column CheckBox1. When I connected the form to the database, Database1DataSet.xsd and Database1DataSet.Designer.cs were automatically created.
CheckBox1 can either hold "Yes" or blank (this wasn't my decision). I would like to make a checkbox checked if the value in the CheckBox1 column is "Yes", and unchecked if the value is blank. If I drag a bound checkbox onto the form, it doesn't work because I assume that the values in the column need to be either 1 or 0. So I'm trying to work around this.
In my form, I have the following
// Form Constructor
public myForm()
{
// Initializes all the components in the form
InitializeComponent();
// Change the checkboxes checked state
this.myCheckBox.Checked = myCheckBox_Update();
}
// Method for determining if the checkbox should be checked
private bool myCheckBox_Update()
{
// This SHOULD bring in the current record of the database
DataRowView current = (DataRowView)this.Table1BindingSource.Current;
try
{
// This SHOULD determine if the value in the CheckBox1 field has a value
return current.Row["CheckBox1"].ToString().Length > 0;
}
catch (NullReferenceException ex)
{
MessageBox.Show("NullReferenceException was thrown!", "Error");
return false;
}
}
In the InitializeComponent() function, there is the following
// This line is generated when I drag a bound checkbox to the form
// this.myCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("CheckState", this.Table1BindingSource, "CheckBox1", true));
// For the miscellaneous information about the checkbox
this.myCheckBox.AutoSize = true;
this.myCheckBox.Location = new System.Drawing.Point(3, 3);
this.myCheckBox.Name = "myCheckBox";
this.myCheckBox.Size = new System.Drawing.Size(92, 23);
this.myCheckBox.TabIndex = 0;
this.myCheckBox.Text = "Check Box";
this.myCheckBox.UseVisualStyleBackColor = true;
However, it keeps throwing a NullReferenceException. I'm assuming that this is because this.Table1BindingSource.Current cannot be cast as a DataRowView.
I have looked at several other posts on SO that are somewhat related to this problem (e.g. This post, or this post), but I haven't found anything that has worked so far. The second link's answer doesn't work because I am iterating through the records with this.Table1BindingSource.MoveNext();, and I won't know the index.
Can someone steer me in the right direction? I'd really appreciate it
EDIT: The BindingSource is initialized with this
private System.Windows.Forms.BindingSource Table1BindingSource;
this.Table1BindingSource = new System.Windows.Forms.BindingSource(this.components);
I'll put this in an answer since it's not fit for a comment.
You may also try checking the value for null in your try statement.
try
{
// This SHOULD determine if the value in the CheckBox1 field has a value
object val = current.Row["CheckBox1"];
if (val == null)
{
return false;
}
else
{
return current.Row["CheckBox1"].ToString().Length > 0;
}
}
Or at least set a breakpoint at the if (val == null) statement and make sure val has a value that you would expect.
If by 'blank' you mean a String.Empty, then your code would work as you expect it to, but if blank means null, then you'll have to check for null, you can't call ToString() on a null value.
What I ended up doing is just changing the values in my database from "Yes" and blank to 1 and 0. Then in the InitializeComponent() method, I added the following line (or it might be generated for you if you drag it from the data source panel)
this.myCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("CheckState", this.Table1BindingSource, "CheckBox1", true));
Does anyone know how to set the excel page break to include a certain number of columns using C# with the OpenXML SDK? What I want to do is make x columns appear on one page. I had originally thought setting the print area would do it but it doesn't. I can't find any references to do this.
This is done manually in an excel spreadsheet's "Page Break View" where you drag the vertical dotted line to include more columns.
Thanks
The OpenXML SDK distinguish between manual horizontal page breaks and manual vertical page breaks.
A manual horizontal page break allows you to specify a break above a given row Id (index).
A vertical page break allows you to specify a break to the left of the specified column Id (index).
To programmatically insert a horizontal page break use the RowBreaks and Break class.
The RowBreaks class represents a collection of all horizontal page breaks in a worksheet.
The ColumnBreaks and Break class allow you to insert a vertical page break. The
ColumnBreaks class holds all vertical page breaks for a worksheet.
The following example demonstrate the insertion of a vertical page break.
The function InsertVerticalPageBreak() takes a columnIndex (where the page break should be inserted)
and the WorksheetPart. This function first checks if the worksheet already contains a
ColumnBreaks collection. If not, one will be created. Then the function creates an instance
of the Break class and sets the Id property to the column index. I've also set the Max property
to the maximum number of rows Excel is able to handle to get a continuing vertical page break. By setting the property ManualPageBreak to true we specifing a manual page break.
I've also added a InsertHorizontalPageBreak() function to the sample to show how to
add a horizontal page break.
private void InsertPageBreaks()
{
uint columnIndex = 17U;
uint rowIndex = 51U;
using (SpreadsheetDocument sd = SpreadsheetDocument.Open("c:\\temp\\spreadsheet.xlsx", true))
{
WorkbookPart workbookPart = sd.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.Last();
// Uncomment the following line to insert row page breaks.
// InsertHorizontalPageBreak(rowIndex, worksheetPart);
InsertColumnVerticalBreak(columnIndex, worksheetPart);
}
}
private void InsertHorizontalPageBreak(uint rowIndex, WorksheetPart worksheetPart)
{
Break rowBreak =
new Break() { Id = (UInt32Value)rowIndex, Max = (UInt32Value)16383U, ManualPageBreak = true };
RowBreaks rb = worksheetPart.Worksheet.GetFirstChild<RowBreaks>();
if (rb == null)
{
rb = new RowBreaks();
rb.ManualBreakCount = (UInt32Value)0;
rb.Count = (UInt32Value)0;
worksheetPart.Worksheet.Append(rb);
}
rb.Append(rowBreak);
rb.ManualBreakCount++;
rb.Count++;
}
private void InsertVerticalPageBreak(uint columnIndex, WorksheetPart worksheetPart)
{
ColumnBreaks cb = worksheetPart.Worksheet.GetFirstChild<ColumnBreaks>();
if (cb == null)
{
cb = new ColumnBreaks();
cb.ManualBreakCount = (UInt32Value)0;
cb.Count = (UInt32Value)0;
worksheetPart.Worksheet.Append(cb);
}
Break br =
new Break() { Id = (UInt32Value)columnIndex, Max = (UInt32Value)1048575U, ManualPageBreak = true };
cb.Append(br);
cb.ManualBreakCount++;
cb.Count++;
}