How to add an image to a cell - c#

We have Word Interop based process in place to generate invoices. This process, which runs for years, loads a pre-built docx template file and populates with the data. Both header and footer of that template have a table each. Currently the code below populates the text in a given cell of the footer
protected override void OnEnterRow()
{
Try(() => _parent.OLE_WordCell.Value = _parent.OLE_WordTable.Value.Cell(u.ToInt(I_RowNumber), u.ToInt(I_CellNumber)));
Try(() => _parent.OLE_WordCell.Value.Range.Text = u.TrimEnd(I_CellText));
Try(() => _parent.OLE_WordCell.Value.Range.Font.Bold = u.ToInt(I_CellBold));
}
I need to add an image to the footer of the invoice, based on a certain, simple logic. I have seen multiple examples on this and other sites how to do it. Though in my case it does add the image, but not in the footer and I will much appreciate any help.
Before doing any significant change, I wanted to simply show the image instead of the text, so I have disabled the lines above and applied the following code:
Try(() => _parent.OLE_WordCell.Value = _parent.OLE_WordTable.Value.Cell(u.ToInt(I_RowNumber), u.ToInt(I_CellNumber)));
Try(() => _parent.OLE_WordCell.Value.Range.InlineShapes.AddPicture(I_FilePath).ConvertToShape());
It added the image in a middle of the invoice (NOT the footer). Since I have tried various codes and below is the latest, but to no avail.
This code placed one big image all over the invoice
var _cell = _parent.OLE_WordTable.Value.Cell(u.ToInt(I_RowNumber), u.ToInt(I_CellNumber));
var _range = _cell.Range;
var _shape = _range.InlineShapes.AddPicture(I_FilePath.Value.ToString().Trim()).ConvertToShape();
so I have added height and width:
var _cell = _parent.OLE_WordTable.Value.Cell(u.ToInt(I_RowNumber), u.ToInt(I_CellNumber));
var _range = _cell.Range;
var _shape = _range.InlineShapes.AddPicture(I_FilePath.Value.ToString().Trim()).ConvertToShape();
_shape.HeightRelative = 8f;
_shape.WidthRelative = 10f;
It succeeded to reduce the size, but the image remained in a middle of the invoice. Then I have added Select() as I have seen someone advised. But that placed the image in the left top corner of the page.
var _cell = _parent.OLE_WordTable.Value.Cell(u.ToInt(I_RowNumber), u.ToInt(I_CellNumber));
_cell.Select();
var _range = _cell.Range;
var _shape = _range.InlineShapes.AddPicture(I_FilePath.Value.ToString().Trim()).ConvertToShape();
_shape.HeightRelative = 8f;
_shape.WidthRelative = 10f;
Anything else I shall try, please?

Related

Can't make Legend visible in chart

Hello I'm trying to show a legend in runtime but for some kind of reason it won't show up or the complete size of my chart changes but still I don't see the legend.
Here is the part of the code that draws the Chart
foreach (DataGridViewRow row in dataGridView1.Rows)
{
TempDate = Convert.ToDateTime(row.Cells[0].Value);
if (row.Cells[1].Value.ToString().Equals(SelectedSpeed))
{
//Get vibration values
yHDE = Convert.ToDouble(row.Cells[2].Value, CultureInfo.InvariantCulture);
//Plot Horizontal DE chart
if (yHDE != 0)
{
if (MachineName == newMachineName)
{
//Draw the points in the selected series
this.chartHorizontalDE.Series[MachineName].Points.AddXY(TempDate, yHDE);
}
else if (MachineName != newMachineName)
{
//When a new machine has been selected create a new series name for the chart
this.chartHorizontalDE.Series.Add(newMachineName);
this.chartHorizontalDE.Series[newMachineName].ChartType = SeriesChartType.Line;
}
}
}
Legend legend = new Legend();
legend.Docking = Docking.Right;
chartHorizontalDE.Legends.Add(legend);
}
I believe I have all the info in my code and the series for the particular chart should be available. The above last three lines finally gave a result but just changing the size of the chart and I was not able to see the legend. The example on MSDN gave me error that the particular series allready existed btw. I have deleted a big part of the code so maybe I do certain things like it is here is because there is more code but not relevant for the question (I think).
Where do I make a mistake?

Inserting a new page after each generated page

I'm generating a .pdf file using the iText 7 library. And I would like to basically duplicate every page, preferably using a PdfDocumentEvent.END_PAGE EventHandler. Because I would like to have each duplicate page, alternating after each other with a different header/footer.
The following image will illustrate the desired result:
I realise that this desired result can probably be achieved by generating a single instance first and then using another reader and writer to duplicate each page and add the header/footer with some kind of stamper.
But because of different requirements I would like to solve this while generating. But it might just not be possible the way I want to solve this.
I've tried a couple of things already but I get some strange behaviours and I can't seem to figure this out by myself. I'll add some of this code, which hopefully also helps explaining the issue further.
This is the example code to generate the document:
protected void ManipulatePdf(string dest)
{
var writer = new PdfWriter(dest);
var pdfDoc = new PdfDocument(writer);
var doc = new Document(pdfDoc, PageSize.A4);
//Custom EventHandler that adds a page footer
pdfDoc.AddEventHandler(PdfDocumentEvent.END_PAGE, new CustomEndPageEventHandler(pdfDoc));
//The actual generating of the document
for (int i = 0; i < 3; i++)
{
doc.Add(new Paragraph("Test " + (i + 1)));
if (i != 2)
{
doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
}
/* I would like to add that in reality it is not known exactly when page-breaks will occur */
doc.Close();
}
And below is the code I have so far for adding the extra pages. Please note that this code is not correct and gives some strange behaviour.
There is no code for alternating the header/footer yet, this code was simply supposed to insert duplicates between each page.
I'll also add another image below with the generated result, but this is not what is expected.
protected class CustomEndPageEventHandler : IEventHandler
{
protected PdfFormXObject placeholder;
protected float side = 20;
protected float x = 300;
protected float y = 25;
protected float space = 4.5f;
protected float descent = 3;
public CustomEndPageEventHandler(PdfDocument pdf) {
placeholder =
new PdfFormXObject(new Rectangle(0, 0, side, side));
}
public void HandleEvent(Event currentEvent)
{
if(!(currentEvent is PdfDocumentEvent docEvent)) return;
var pdf = docEvent.GetDocument();
var page = docEvent.GetPage();
var pageNumber = pdf.GetPageNumber(page);
var pageSize = page.GetPageSize();
var pdfCanvas = new PdfCanvas(
page.GetLastContentStream(), page.GetResources(), pdf);
var canvas = new Canvas(pdfCanvas, pdf, pageSize);
Paragraph p = new Paragraph()
.Add($"{pageNumber}");
canvas.ShowTextAligned(p, x, y, TextAlignment.RIGHT);
pdfCanvas.AddXObject(placeholder, x + space, y - descent);
pdfCanvas.Release();
//Here starts the custom code to insert a duplicate
if ((pageNumber % 2) != 0 && pageNumber < 6)
{
pdf.AddPage(pageNumber + 1, page);
pdf.AddNewPage(pageNumber+2);
}
}
public void WriteTotal(PdfDocument pdf) {
var canvas = new Canvas(placeholder, pdf);
canvas.ShowTextAligned($"/{pdf.GetNumberOfPages()}",0, descent, TextAlignment.LEFT);
}
}
The above code results into something like this. I've tried a couple of different variations of this, when you for example when you remove the line pdf.AddNewPage(pageNumber+2); then the page with Test 2 and Test 3 are overlapping each other.
I'm actually stuck on this and would really like to know if this can be done in the PdfDocumentEvent.END_PAGE EventHandler, so I'm looking forward to seeing what you can come up with.

Programmatically added LinkLabel not visible

I am encountering a strange phenomenon: I have a WinForms application with four GroupBoxes, all four initially empty. I use this to track new followers/unfollowers on Twitter, planning on expanding its use once this functions properly.
It does work properly for new followers. For these I have a GroupBox called grpFollow, to which I add LinkLabels with the ScreenNames of my new followers like this:
var folTop = new Point(grpFollow.Left + 5, grpFollow.Top + 5);
lblFollowers.Text = Properties.Settings.Default.FollowersNow.Count.ToString();
lblFriends.Text = Properties.Settings.Default.FriendsNow.Count.ToString();
var ctr = 1;
foreach (var fol in newFollowers)
{
var kvp = LookupUser(fol);
if (string.IsNullOrEmpty(kvp.Key)) continue;
var linklabel = new LinkLabel()
{
Text = kvp.Value,
Width = 200,
Height = 15,
Location = folTop,
Visible = true,
Name = $"follbl{ctr}"
};
ctr++;
linklabel.Links.Add(0, linklabel.Width-1, $"https://twitter.com/{kvp.Key}");
linklabel.Click += Linklabel_Click;
grpFollow.Controls.Add(linklabel);
folTop.Y += 25;
}
LookupUser is just a function that passes the user id to the Twitter API and returns the name & screen_name of that user. Works fine, no problem. LinkLabels added nicely, no problem there either.
The trouble is with the other group boxes, e.g. the one for new friends:
folTop = new Point(grpFriends.Left + 15, grpFriends.Top + 15);
ctr = 1;
foreach (var fol in newFriends)
{
var kvp = LookupUser(fol);
if (string.IsNullOrEmpty(kvp.Key)) continue;
var llabel = new LinkLabel()
{
Text = kvp.Value,
Width = 200,
Height = 15,
Location = folTop,
Visible = true,
Name = $"frdlbl{ctr}"
};
ctr++;
llabel.Links.Add(0, llabel.Width - 1, $"https://twitter.com/{kvp.Key}");
llabel.Click += Linklabel_Click;
grpFriends.Controls.Add(llabel);
folTop.Y += 25;
}
As you can see, the logic is identical (because I want to extract this part to a separate method to avoid repetition). The location is set relative to the grpFriends group box, everything else is the same. Yet, the LinkLabel does not show, i.e. the second group box remains (visually) empty!
I have set a breakpoint to check what might go wrong. I single stepped through: the correct screen name is being retrieved, the location is correct, the control is added - but nothing ever shows up.
P.S: This code is in the RunWorkerCompleted method of a background worker, no further code is executed after this point.
Any idea why the Label isn't displayed?
Edit: I'll be damned!
I just changed the location of the grpFriends LinkLabel to 10,10: it appears, juuust clipping the lower border of my friends' group.
Now here is where this gets weird for me:
As you can see, the group has a Y value of 351. Point (10,10) should not even be in the box. So, it seems that the location is the culprit and the original code created the label outside the form.
Replacing grpFriends.Top as Y value with grpFriends.Top - grpFriends.Height got me closer. The LinkLabel is farther down from the top than I'd like but that's not so bad.
Very strange.
Okay, thanks to #raBinn I figured it out. The mistake here was me assuming, the LinkLabels needed a location relative to the Form. However, when adding them to the Controls collection of a GroupBox, they automatically assume a location relative to the GroupBox, not the Form.
So, my code is now basically the same for all four group boxes and looks like this:
PopulateGroup(newFollowers, grpFollow);
PopulateGroup(unFollow, grpLost);
PopulateGroup(newFriends, grpFriends);
PopulateGroup(unFriend, grpDitched);
And:
private void PopulateGroup(List<string> collPeople, GroupBox groupBox)
{
var folTop = new Point(12, 25);
foreach (var fol in collPeople)
{
var kvp = LookupUser(fol);
if (string.IsNullOrEmpty(kvp.Key)) continue;
var linklabel = new LinkLabel()
{
Text = kvp.Value,
Width = 200,
Height = 15,
Location = folTop
};
ctr++;
linklabel.Links.Add(0, linklabel.Width - 1, $"https://twitter.com/{kvp.Key}");
linklabel.Click += Linklabel_Click;
groupBox.Controls.Add(linklabel);
folTop.Y += 25;
}
}
So it doesn't really matter whether the group box is on the left or right, top or bottom of the form...

Center printing PrintDocument

I'm using PrintDocument to print out multiple pages that each have one control on. So far I'm able to print them all out on individual sheets as desired, however I'm unable to center the controls in the middle of the pages and it always shows in the top corner of the page. I'll post my code below.
var dialog = new PrintDialog();
var queue = GetPrinterQueue(pPrinterId);
if (queue == null)
return;
dialog.PrintQueue = queue;
dialog.PrintTicket.PageOrientation = paperOrientation
? PageOrientation.Portrait
: PageOrientation.Landscape;
var document = new FixedDocument();
var fixedPage = new FixedPage();
fixedPage.Children.Add(front);
//fixedPage.Measure(size);
//fixedPage.Arrange(new Rect());
//fixedPage.UpdateLayout();
var pageContent = new PageContent();
((IAddChild)pageContent).AddChild(fixedPage);
document.Pages.Add(pageContent);
dialog.PrintDocument(document.DocumentPaginator, "Badge");
That is the bare minimum to make it print out a control per sheet, to make it simpler I have taken out the parts that print to different pages, for the sake of this it only needs to print one.
I've tried changing what is passed into .Arrange() and it makes no difference, what am I missing?
Thanks

auto arrange visio shapes through C#

I am using Microsoft Visio as a COM object in my C# application. I want to auto arrange shapes on a Visio page. What should I code for this task? The shapes are database entities.
userView.Shapes.SomeMethod();
userView is name of COM object but what should SomeMethod be?
I know this is an 'older' question, but
I'm working on something quite similar, and have managed to 'Auto-Layout' a flow chart with the following code:
public enum GraphStyles { TopDown, LeftRight };
public void ArrangeGraph(GraphStyles Style)
{
if (Style == GraphStyles.TopDown)
{
// set 'PlaceStyle'
var placeStyleCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOPlaceStyle).ResultIU = 1;
// set 'RouteStyle'
var routeStyleCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLORouteStyle).ResultIU = 5;
// set 'PageShapeSplit'
var pageShapeSplitCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOSplit).ResultIU = 1;
}
else if (Style == GraphStyles.LeftRight)
{
// set 'PlaceStyle'
var placeStyleCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOPlaceStyle).ResultIU = 2;
// set 'RouteStyle'
var routeStyleCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLORouteStyle).ResultIU = 6;
// set 'PageShapeSplit'
var pageShapeSplitCell = VisApp.ActivePage.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOSplit).ResultIU = 1;
}
else { throw new NotImplementedException("GraphStyle " + Style.ToString() + " is not supported"); }
VisApp.ActivePage.Layout();
}
Hopefully this saves someone some time.
It took me awhile to figure it out.
I'm using visio 2010 and visual studio 2010
This might help
Pertinent quote
To lay out a subset of the shapes of a page, master, or group,
establish a Selection object in which the shapes to be laid out are
selected, and then call the Layout method. If the Layout method is
performed on a Selection object and the object has no shapes selected,
all shapes in the page, master, or group of the selection are laid
out.
EDIT: noonand 2012-09-21 Added information about the LayoutIncremental method
Just had another look at the object model and it appears the method you want is the LayoutIncremental method
Excerpt from the relevant help topic says:
Page.LayoutIncremental(AlignOrSpace, AlignHorizontal, AlignVertical, SpaceHorizontal, SpaceVertical, UnitsNameOrCode)
I needed to do something similar a while ago..
I used Microsofts Glee library for the layout. There are very good samples included with the download which show you how to add nodes and relations and make them "auto arrange". However do note that Glee is not free for commercial use.
http://research.microsoft.com/en-us/downloads/f1303e46-965f-401a-87c3-34e1331d32c5/default.aspx
And then I used this example for converting the calculated positions from Glee to a Visio drawing.
http://www.syntaxwarriors.com/2012/generating-visio-uml-diagrams-from-c/
Basically what I do is add all my nodes and relations too Glee and then get a list of nodes and their positions and add them to Visio using the second link.
Here is a graph example of what Glee can do:

Categories

Resources