How to create a link inside a cell using EPPlus - c#

I am trying to figure out how to write a Hyperlink inside a cell using EPPlus instead of the cell containing the link text. I need it to be recognized as a link and be clickable.
Any help is appreciated.

This is the other way to do:
var cell = sheet.Cells["A1"];
cell.Hyperlink = new Uri("http://www.google.com");
cell.Value = "Click me!";
I have tested. It works fine.

The below code worked fine with me.
string FileRootPath = "http://www.google.com";
_Worksheet.Cells[intCellNumber, 1].Formula = "HYPERLINK(\"" + FileRootPath + "\",\"" + DisplayText + "\")";
I hope this would help you.
Happy coding!!

There's a few ways to go about it:
To use URI, then set a human readable name:
var cell = sheet.Cells["A1"];
cell.Hyperlink = new Uri("https://www.google.com");
cell.Value = "Click me!";
To use ExcelHyperLink and set a human readable name using object initializer :
var cell = sheet.Cells["A1"];
cell.Hyperlink = new ExcelHyperLink("https://www.google.com") { Display = "Click me!" };
To use =Hyperlink() formula:
var cell = sheet.Cells["A1"];
cell.Formula = string.Format("HYPERLINK({0},{1})", "https://www.google.com", "Click me!");
cell.Calculate();

Based on provided answers and documentation I was able to create an extension method that also deals with proper hyperlink formatting. It creates a named style, if needed, and use that style for all subsequent hyperlinks:
public static void WriteHyperlink(this ExcelRange cell, string text, string url, bool excelHyperlink = false, bool underline = true)
{
if (string.IsNullOrWhiteSpace(text))
return;
// trying to reuse hyperlink style if defined
var workBook = cell.Worksheet.Workbook;
string actualStyleName = underline ? HyperLinkStyleName : HyperLinkNoUnderlineStyleName;
var hyperlinkStyle = workBook.Styles.NamedStyles.FirstOrDefault(s => s.Name == actualStyleName);
if (hyperlinkStyle == null)
{
var namedStyle = workBook.Styles.CreateNamedStyle(actualStyleName);
namedStyle.Style.Font.UnderLine = underline;
namedStyle.Style.Font.Color.SetColor(Color.Blue);
}
if (excelHyperlink)
cell.Hyperlink = new ExcelHyperLink(url) { Display = text };
else
{
cell.Hyperlink = new Uri(url);
cell.Value = text;
cell.StyleName = actualStyleName;
}
}
Without the styling, the hyperlink will look just as regular text, if cell.Hyperlink = new Uri(url); is used without explicit styling (although the cursor will properly indicate that the text is actually a hyperlink text).

I don't know EPPlus, but in VBA (and I guess C# would use the same principle) you would use the following code:
Sub Test()
' place value into cell
ActiveSheet.[A1] = 13
' create link and set its range property
ActiveSheet.Hyperlinks.Add ActiveSheet.[A1], "http://www.google.at"
' use cell in a calculation
ActiveSheet.[A2].Formula = "=A1+2"
End Sub
Hyperlinks are objects having a range property, so while your cell value can be changed by overtyping, the link will remain. Edit the cell by a long mouse click
Hope this helps - good luck MikeD

If you are using EPPlus and want to create a link to another sheet within the same document, then this is the proper way to do this:
var link = "Another Excel Sheet"; //Maximum length is 31 symbols
using (var excel = new ExcelPackage())
{
var ws = excel.Workbook.Worksheets.Add("Test");
ws.Cells[row, col].Hyperlink =
new ExcelHyperLink((char)39 + link + (char)39 + "!A1",
"Name of another excel sheet could be more then 31 symbols");
}
This is the proper way to create a link to another sheet within Excel document. Having using formula with HYPERLINK function, if a file is downloaded to the client, latest Excel version will raise security warnings.

Related

LibreOffice Calc C# SDK: program to insert images into cells, stuck trying to create XGraphic

Background: I'm trying to write a program to insert an image into a cell of a spreadsheet. LibreOffice recently changed how this is done, and all the samples I could find use the old method which no longer works.
Technically I know that you can't "insert" an image into a cell and that such an image is an overlay on a DrawPage that sits on top of the spreadsheet to "decorate" it.
One of the first steps in doing this (the new way) is to create an XGraphic object which contains the image. The process is to create an XGraphicProvider and call it with MediaProperties that specify the image file URL to be loaded. I have a program that is supposed to do this but the resulting XGraphic is null. The LO SDK gives pretty much no information when you do something wrong; it just doesn't work.
Here is the code I have, with all the headers removed:
// addpic
// add picture to spreadsheet - debug version
class OpenOfficeApp {
[STAThread]
static void Main(string[] args) {
bool lreadonly;
string pqfile;
string pqURL;
string pqpic;
pqfile = "file:///D:/Documents/NSexeye/ODS%20File%20Access/"+
"addpix/addpic.ods";
pqpic = "addpic2";
pqURL = pqpic+".jpg";
lreadonly = false;
Console.WriteLine("Using: "+pqfile);
// get the desktop
XComponentContext XCC = uno.util.Bootstrap.bootstrap();
XMultiComponentFactory XMCF =
(XMultiComponentFactory)XCC.getServiceManager();
XMultiServiceFactory XMSF = (XMultiServiceFactory)XCC.getServiceManager();
XComponentLoader XCL =
(XComponentLoader)XMSF.createInstance("com.sun.star.frame.Desktop");
// open the spreadsheet
PropertyValue[] pPV = new PropertyValue[2];
pPV[0] = new PropertyValue();
pPV[0].Name = "Hidden";
pPV[0].Value = new uno.Any(true);
pPV[1] = new PropertyValue();
pPV[1].Name = "ReadOnly";
if (lreadonly) pPV[1].Value = new uno.Any(true);
else pPV[1].Value = new uno.Any(false);
XComponent XCo = XCL.loadComponentFromURL(pqfile,"_blank",0,pPV);
// create graphic object containing image
object oGP = XMCF.createInstanceWithContext(
"com.sun.star.graphic.GraphicProvider",XCC);
if (oGP == null) {
Console.WriteLine("oGP is null. Aborting.");
return;
}
XGraphicProvider XGP = (XGraphicProvider)oGP;
if (XGP == null) {
Console.WriteLine("XGP is null. Aborting.");
return;
}
pPV = new PropertyValue[1];
pPV[0] = new PropertyValue();
pPV[0].Name = "URL";
pPV[0].Value = new uno.Any(pqURL);
Console.WriteLine("Creating XGraphic containing "+pqURL);
XGraphic XG = XGP.queryGraphic(pPV);
// *** XG is null here
if (XG == null) {
Console.WriteLine("XG is null. Aborting.");
return;
}
// ... lots of stuff to be added here
// save and close the spreadsheet
XModifiable XM = (XModifiable)XCo;
XM.setModified(true);
XStorable XSt = (XStorable)XCo;
XSt.store();
XCloseable XCl = (XCloseable)XCo;
XCl.close(true);
// terminate LibreOffice
// *** I want this to not terminate it if something else is open
XDesktop XD = (XDesktop)XCL;
if (XD != null) XD.terminate();
}
}
I get a null for the XGraphic, in the place indicated in the comments. I don't know if the call to create it is failing, or if one of the earlier steps of the process are incorrect.
My goal here, in addition to getting my program working, is to create a sample program showing how to add an image to a Calc spreadsheet cell, and to manipulate such images. There are a fair number of people asking questions about this and none of the examples I've found will work. I think a good working sample will be of value.
I've spent a lot of time searching for information and code samples for this, with nothing that helps. I've tried to find ways to verify the validity of the XGraphicProvider interface with no luck. I've run out of things to try.
I'm hoping someone who knows about the LibreOffice SDK can take a look and maybe see what I'm doing wrong.
Update: I figured out what I was doing wrong: I was passing a bare filename in the "URL" property to XGraphicProvider. It has to be the same format (starting with "file:///") as the spreadsheet's file name specification.
Now I'm stuck with another property problem. The XGraphic has to be specified as a parameter to the GraphicObjectShape's Graphic property, but the setPropertyValue() function requires that it be a uno.Any type. I can't figure out how to specify an interface name like XGraphic as a uno.Any.
Here is the piece of code that won't compile, complaining that it can't convert an XGraphic to a uno.Any, in the first setPropertyValue call:
// set image XGraphic
XPropertySet XPS = (XPropertySet)XS;
XPS.setPropertyValue("Graphic",XG);
XPS.setPropertyValue("Name",new uno.Any(pqpic));
XG is an XGraphic type. Using "new uno.Any(XG)" doesn't work either, giving a similar compiler error.
After trying unsuccessfully for a few hours to get the latest LO SDK up and running, let me offer some untested ideas.
First of all, here is some working Basic code, no doubt similar to what you're translating from. The important line is oShape.Graphic = oProvider.queryGraphic(Props()).
oDoc = ThisComponent
oSheet = oDoc.CurrentController.ActiveSheet
pqURL = "file:///C:/Users/JimK/Desktop/addpic.jpg"
oProvider = createUnoService("com.sun.star.graphic.GraphicProvider")
oShape = oDoc.createInstance("com.sun.star.drawing.GraphicObjectShape")
Dim Props(0) as new com.sun.star.beans.PropertyValue
Props(0).Name= "URL"
Props(0).Value = pqURL
oShape.Graphic = oProvider.queryGraphic(Props())
oCell = oSheet.getCellByPosition(5,5)
oShape.Name = oCell.AbsoluteName + "##" + Props(0).Value
oShape.Anchor = oCell
oSheet.DrawPage.add(oShape)
'Resize
w = oShape.Graphic.Size.Width
h = oShape.Graphic.Size.Height
wcl = oCell.Size.Width
hcl = oCell.Size.Height
If w<>0 and h<>0 then
oCell.String=""
Dim Size as new com.sun.star.awt.Size
Size.Width = wcl
Size.Height = h*wcl/w
If Size.Height > hcl then
Size.Width = hcl*w/h
Size.Height = hcl
Endif
oShape.setSize(Size)
oShape.setPosition(oCell.Position)
erase oShape
Else
oShape.dispose()
Endif
Now, how to translate this to C#? It looks like you may need to explicitly specify the type. In the SDK example, there are calls like this.
xFieldProp.setPropertyValue(
"Orientation",
new uno.Any(
typeof (unoidl.com.sun.star.sheet.DataPilotFieldOrientation),
unoidl.com.sun.star.sheet.DataPilotFieldOrientation.DATA ) );
So in your case, something like this:
XPS.setPropertyValue(
"Graphic"
new uno.Any(
typeof(unoidl.com.sun.star.graphic.XGraphic),
XG));
Alternatively, follow the suggestion here: set GraphicURL, which should load the image and set Graphic for you.

Invalid Filename in PrintManager.PrintToFileName

I am having an issue with the Revit API PrintManager properties, in particular with the PrintToFileName property and the error: Invalid fileName.
What is the proper way to declare what you wish for a printed view to be named when printing to pdf?
My Source (Puts a schedule onto an empty sheet and prints it):
ViewSheet sheet = ViewSheet.Create(Doc, ElementId.InvalidElementId);
Element view = new FilteredElementCollector(Doc)
.OfClass(typeof(ViewSchedule))
.Where(o => o.Name == element) //element is a string from a list of items in a WPF listbox.
.First();
ScheduleSheetInstance.Create(Doc, sheet.Id, view.Id, XYZ.Zero);
ViewSet set = new ViewSet();
set.Insert(sheet);
IList<ElementId> print = new List<ElementId>();
print.Add(sheet.Id);
Uidoc.Selection.SetElementIds(print);
printManager.PrintRange = PrintRange.Select;
ViewSheetSetting viewSheetSetting = printManager.ViewSheetSetting;
viewSheetSetting.CurrentViewSheetSet.Views = set;
printManager.SelectNewPrintDriver("Adobe PDF");
printManager.PrintToFile = true;
printManager.CombinedFile = true;
printManager.PrintToFileName = "TestFileName"; //ERROR HERE
printManager.Apply();
printManager.SubmitPrint(sheet);
Maybe you need to input the full path + name of the file to work.
Update: Found the problem in my formatting thanks to Bogdans post as well as This comment on the Autodesk Revit Api Forum.
Turns out, the PrintToFileName calls for both a path and a filename string. Working snippet is as follows:
string path = #"[same file path here ]";
string currentViewName = view.Name;
printManager.PrintToFileName = path + currentViewName + ".pdf";
printManager.Apply();
Thanks for the response, Bogdan.

How to set Publish Date in Word Document using OpenXML

I have a situation where a user can upload a word document which contains placeholders for certain properties (i.e. in MS Word the user has gone to Insert > Quick Parts > Document Properties and chosen a property). The specific properties I want to support are Title, Author, Company and Publish Date.
Title and Author are set as Package Properties, and Company is set as an Extended File Property. These are set with the below code, which works correctly:
private static void SetDocumentProperties(ReportTemplateModel model, WordprocessingDocument wordDocument)
{
//these properties always exist
wordDocument.PackageProperties.Title = model.Title;
wordDocument.PackageProperties.Creator = model.Author;
wordDocument.PackageProperties.Created = DateTime.Now;
wordDocument.PackageProperties.Modified = DateTime.Now;
//these properties can be null
if (wordDocument.ExtendedFilePropertiesPart == null)
{
wordDocument.AddNewPart<ExtendedFilePropertiesPart>();
}
if (wordDocument.ExtendedFilePropertiesPart.Properties == null)
{
wordDocument.ExtendedFilePropertiesPart.Properties = new Properties(new Company(model.SiteName));
}
else
{
wordDocument.ExtendedFilePropertiesPart.Properties.Company = new Company(model.SiteName);
}
}
My problem is that I can't work out how the set the Publish Date property. I have tried adding it as a Custom File Property using the below code (which is adapted from https://www.snip2code.com/Snippet/292005/WDSetCustomProperty), but this does not work. I've read a few things about setting custom properties, but I'm confused how they're meant to work. I'm also unsure if the Publish Date should actually be set as a custom property, or some other type of property.
var customProps = wordDocument.CustomFilePropertiesPart;
if (customProps == null)
{
customProps = wordDocument.AddCustomFilePropertiesPart();
customProps.Properties = new DocumentFormat.OpenXml.CustomProperties.Properties();
}
var properties1 = new DocumentFormat.OpenXml.CustomProperties.Properties();
//I have tried both of these lines, neither worked.
//properties1.AddNamespaceDeclaration("op", "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties");
properties1.AddNamespaceDeclaration("vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes");
var customDocumentProperty1 = new DocumentFormat.OpenXml.CustomProperties.CustomDocumentProperty()
{
FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
PropertyId = 2,
Name = "Publish Date"
};
customDocumentProperty1.Append(new DocumentFormat.OpenXml.VariantTypes.VTLPWSTR { Text = DateTime.Today.ToShortDateString() });
properties1.Append(customDocumentProperty1);
part.Properties = properties1;
What type of property should the Publish Date be set as, and what is the right syntax for setting this?
Update: I have found that Publish Date is a CoverPageProperty which can be created using the below snippet, but I still haven't found how to set it correctly within the document.
var coverPageProps = new DocumentFormat.OpenXml.Office.CoverPageProps.CoverPageProperties
{
PublishDate = new PublishDate(DateTime.Today.ToShortDateString())
};
Adding the below code to my SetDocumentProperties method seems to work. I must admit I don't fully understand the below code, so any explanation would still be welcome. Additionally, if anyone has a solution which doesn't include writing XML as a string inside C# I would much prefer to avoid that.
const string publishDatePartId = "publishDatePart";
var publishDateXmlPart = wordDocument.MainDocumentPart.AddNewPart<CustomXmlPart>("application/xml", publishDatePartId);
var writer = new XmlTextWriter(publishDateXmlPart.GetStream(FileMode.Create), System.Text.Encoding.UTF8);
writer.WriteRaw($"<CoverPageProperties xmlns=\"http://schemas.microsoft.com/office/2006/coverPageProps\">" +
$"<PublishDate>{DateTime.Today.ToShortDateString()}</PublishDate>" +
$"</CoverPageProperties>");
writer.Flush();
writer.Close();
var customXmlPropertiesPart = publishDateXmlPart.AddNewPart<CustomXmlPropertiesPart>(publishDatePartId);
customXmlPropertiesPart.DataStoreItem = new DocumentFormat.OpenXml.CustomXmlDataProperties.DataStoreItem()
{
//I don't know what this ID is, but it seems to somehow relate to the Publish Date
ItemId = "{55AF091B-3C7A-41E3-B477-F2FDAA23CFDA}"
};

Monotouch.Dialog not sizing cells appropriately with StyledMultilineElement?

I'm using Xamarin and Monotouch.Dialog to show some text comments in a UIPopoverControl. If the text being shown in a StyledMultilineElement is of any great length then the Caption and the text is being shown further down (see image below). This problem gets worse if your text string is really big, say about 20,000 characters, because you get a huge scrolling cell with no text in it at all. Simplified version of the code I'm using in my UITapGestureRecognizer below.
Am I doing something wrong?
Is there a workaround?
I did have a look at the source of the StyledMultilineElement here and am not sure if the issue is with UITableView.StringSize method.
Any known issues with UITableView.StringSize?
Thanks,
Gavin
Sample code:
RootElement root = new RootElement("Title") { UnevenRows = true };
var section = new Section("Section Heading");
// Get a value string that is approx 1,000 chars long
var value = GetLongString();
var element = new StyledMultilineElement("Caption", value, UITableViewCellStyle.Subtitle)
{
LineBreakMode = UILineBreakMode.WordWrap,
Font = UIFont.FromName("HelveticaNeue", 16f),
SubtitleFont = UIFont.FromName("HelveticaNeue", 16f)
};
section.Add(element);
root.Add(new List<Section>() { section });
var viewController = new DialogViewController(root);
var navbarController = new UINavigationController(viewController);
_contactPopup = new UIPopoverController(navbarController);
_contactPopup.PresentFromRect(cell.Bounds, cell, UIPopoverArrowDirection.Any, true);
Edit:
Found the issues page on git hub and added issue there too Git Hub Issue #225

C# Downloadable Excel Files from Class Library

I'm looking for some advice. I'm building on an additional feature to a C# project that someone else wrote. The solution of the project consists of an MVC web application, with a few class libraries.
What I'm editing is the sales reporting function. In the original build, a summary of the sales reports were generated on the web application. When the user generates the sales report, a Reporting class is called in one of the C# class libraries. I'm trying to make the sales reports downloadable in an excel file when the user selects a radio button.
Here is a snippet of code from the Reporting class:
public AdminSalesReport GetCompleteAdminSalesReport(AdminSalesReportRequest reportRequest)
{
AdminSalesReport report = new AdminSalesReport();
string dateRange = null;
List<ProductSale> productSales = GetFilteredListOfAdminProductSales(reportRequest, out dateRange);
report.DateRange = dateRange;
if (titleSales.Count > 0)
{
report.HasData = true;
report.Total = GetTotalAdminSales(productSales);
if (reportRequest.Type == AdminSalesReportRequest.AdminSalesReportType.Complete)
{
report.ProductSales = GetAdminProductSales(productSales);
report.CustomerSales = GetAdminCustomerSales(productSales);
report.ManufacturerSales = GetAdminManufacturerSales(productSales);
if (reportRequest.Download)
{
FileResult ExcelDownload = GetExcelDownload(productSales);
}
}
}
return report;
}
So as you can see, if reportRequest.Download == true, the class should start up the process of creating the excel file. All the GetAdminSales functions do it use linq queries to sort out the sales if they are being displayed on the webpage.
So I have added this along with the GetAdminSales functions:
private FileResult GetExcelDownload(List<TitleSale> titleSales)
{
CustomisedSalesReport CustSalesRep = new CustomisedSalesReport();
Stream SalesReport = CustSalesRep.GenerateCustomisedSalesStream(productSales);
return new FileStreamResult(SalesReport, "application/ms-excel")
{
FileDownloadName = "SalesReport" + DateTime.Now.ToString("MMMM d, yyy") + ".xls"
};
}
and to format the excel sheet, I'm using the NPOI library, and my formatter class is laid out like so:
public class CustomisedSalesReport
{
public Stream GenerateCustomisedSalesStream(List<ProductSale> productSales)
{
return GenerateCustomisedSalesFile(productSales);
}
private Stream GenerateCustomisedSalesFile(List<ProductSale> productSales)
{
MemoryStream ms = new MemoryStream();
HSSFWorkbook templateWorkbook = new HSSFWorkbook();
HSSFSheet sheet = templateWorkbook.CreateSheet("Sales Report");
HSSFRow dataRow = sheet.CreateRow(0);
HSSFCell cell = dataRow.CreateCell(0);
cell = dataRow.CreateCell(0);
cell.SetCellValue(DateTime.Now.ToString("MMMM yyyy") + " Sales Report");
dataRow = sheet.CreateRow(2);
string[] colHeaders = new string[] {
"Product Code",
"Product Name",
"Qty Sold",
"Earnings",
};
int colPosition = 0;
foreach (string colHeader in colHeaders)
{
cell = dataRow.CreateCell(colPosition++);
cell.SetCellValue(colHeader);
}
int row = 4;
var adminTotalSales = GetAdminProductSales(productSales);
foreach (SummaryAdminProductSale t in adminTotalSales)
{
dataRow = sheet.CreateRow(row++);
colPosition = 0;
cell = dataRow.CreateCell(colPosition++);
cell.SetCellValue(t.ProductCode);
cell = dataRow.CreateCell(colPosition++);
cell.SetCellValue(t.ProductName);
cell = dataRow.CreateCell(colPosition++);
cell.SetCellValue(t.QtySold);
cell = dataRow.CreateCell(colPosition++);
cell.SetCellValue(t.Total.ToString("0.00"));
}
}
templateWorkbook.Write(ms);
ms.Position = 0;
return ms;
}
Again like before, the GetAdminSales (GetAdminProductSales, etc) are contained in the bottom of the class, and are just linq queries to gather the data.
So when I run this, I don't get any obvious errors. The summary sales report appears on screen as normal but no excel document downloads. What I have done, which may be putting this off is in my class library I have referened the System.Web.Mvc dll in order to download the file (I have not done it any other way before - and after reading up on the net I got the impression I could use it in a class library).
When I debug through the code to get a closer picture of what's going on, everything seems to be working ok, all the right data is being captured but I found that from the very start - the MemoryStream ms = new Memory Stream declaration line in my formatter class shows up this (very hidden mind you) :
ReadTimeout '((System.IO.Stream)(ms)).ReadTimeout'
threw an exception of type
'System.InvalidOperationException' int
{System.InvalidOperationException}
+{"Timeouts are not supported on this stream."} System.SystemException
{System.InvalidOperationException}
I get the same for 'WriteTimeout'...
Apologies for the long windedness of the explaination. I'd appreciate it if anyone could point me in the right direction, either to solve my current issue, or an alternative way of making this work.
Without getting bogged down in the details, the obvious error is that in GenerateCustomisedSalesFile you create a MemoryStream ms, do nothing with it, then return it.

Categories

Resources