Open XML word content controls - ms-word

Here is my code trying to get the content controls with the tag "company"
using (WordprocessingDocument template = WordprocessingDocument.Open("d:/dev/ProposalTemplate1.dotx", true))
{
MainDocumentPart mainPart = template.MainDocumentPart;
SdtBlock block = mainPart.Document.Body.Descendants<SdtBlock>().Where(r => r.SdtProperties.GetFirstChild<Tag>().Val == "TEST").Single();
Text t = block.Descendants<Text>().Single();
t.Text = "COMPANY_NAME";
}
I got the error "Object reference not set to an instance of an object" because of the query line but I don't know why...
This works well when I create a simple template with just one content controls but not when using a bigger word template
Any idea ?
EDIT
I try doing it without .Single() but still not working
MainDocumentPart mainPart = template.MainDocumentPart;
var blocks = mainPart.Document.Body.Descendants<SdtBlock>().Where(r => r.SdtProperties.GetFirstChild<Tag>().Val == "Company");
foreach (SdtBlock block in blocks)
{
Text t = block.Descendants<Text>().Single();
t.Text = "COMPANY1";
}
EDIT 2
I change the Text.Single()
The problem is still there "Object reference not set to an instance of an object" on the SdtBlock block = ... line
MainDocumentPart mainPart = template.MainDocumentPart;
var blocks = mainPart.Document.Body.Descendants<SdtBlock>().Where(r => r.SdtProperties.GetFirstChild<Tag>().Val == "Company");
foreach (SdtBlock block in blocks)
{
var t = block.Descendants<Text>();
foreach (Text text in t)
{
text.Text = "COMPANY1";
}
}

Not all SdtBlock elements have child Tag elements. You are assuming one exists and attempting to access the Val property but are getting a null reference exception in doing so.
You can fix it by checking for null within the Where predicate:
var blocks = mainPart.Document.Body.Descendants<SdtBlock>().Where(r =>
{
var tag = r.SdtProperties.GetFirstChild<Tag>();
return tag != null && tag.Val == "Company";
});
As per the comments there is more information about the issues you originally had with using Single in my answer here.

Try this:
foreach( SdtBlock sdt in sdtList )
{
if( sdt.SdtProperties != null )
{
Tag tag = sdt.SdtProperties.GetFirstChild<Tag>();
if( tag!= null )
{
if( tag.Val.Value == "Company" )
{
if( sdt.InnerText != string.Empty )
{
//Do something
}
}
}
}
}

Accepted solution returns no results on my query either. So i come up with this solution;
var doc = document.MainDocumentPart.Document;
List<Tag> sdtSubTable = doc.Body.Descendants<Tag>().Where(r =>
{
return r != null && r.Val.Value.Contains("Company");
}).ToList();

Related

Accent insensitive in filter backend to datatable in ASP.NET MVC 5

I made a method on the back-end side to handle the filter of my datatable.
On the other hand, this one does not manage the accents of the French language, so if I have "école" and I write "ecole" it cannot find it.
I found this method on another question on stackoverflow
public static String RemoveDiacritics(this String s)
{
String normalizedString = s.Normalize(NormalizationForm.FormD);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < normalizedString.Length; i++)
{
Char c = normalizedString[i];
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}
and it works, but only for part of my problem. It works on the letter or the word that is written in the search, but I am not able to apply it in my linq query, so with the .RemoveDiacritics() method my "école" becomes "ecole", but I don't am not able to apply it in the column of my table and it always looks for "école".
Here the code for the search:
if (search != null)
{
int n;
search = search.Trim();
var isNumeric = int.TryParse(search, out n);
if (isNumeric)
{
IdFilter = n;
query = query.Where(x => x.UsagerId == IdFilter || x.Niveau == IdFilter);
}
else if (search != "")
{
// this line work
textFilter = search.ToLower().RemoveDiacritics();
// This is the full line, but absolutely takes the accents out to get the right information out
// query = query.Where(x => x.Nom.ToLower().Contains(textFilter) || x.Prenom.ToLower().Contains(textFilter) || x.Username.ToLower().Contains(textFilter) || x.Email.ToLower().Contains(textFilter) || x.EtabNom.ToLower().Contains(textFilter) || x.ActifStatut.ToLower().Contains(textFilter));
// This is the line that will replace the line above, which I try and it doesn't work ( this part: x.Prenom.ToLower().RemoveDiacritics())
query = query.Where(x => x.Prenom.ToLower().RemoveDiacritics().Contains(textFilter));
}
}
This is the basic query:
IQueryable<ListeUsagers> query = (from u in db.USAGERs
join e in db.ETABLISSEMENTs on u.USAGER_INST equals e.ETAB_CODE
where u.USAGER_INST == instId && u.USAGER_NIVEAU > 3 && u.USAGER_NIVEAU < 5 //&& u.USAGER_ACTIF == 1
select new ListeUsagers()
{
UsagerId = u.USAGER_id,
Nom = u.USAGER_NOM,
Prenom = u.USAGER_PRENOM,
EtabCode = e.ETAB_CODE,
EtabNom = e.ETAB_NOM_COURT,
EtabType = e.ETAB_TYPE,
Niveau = u.USAGER_NIVEAU,
Username = u.USAGER_USERNAME,
UserPassword = u.USAGER_MP,
DateCreation = u.USAGER_DATE_INSC,
Sexe = u.USAGER_SEXE,
Lang = u.USAGER_LANGUE,
Telephone = u.USAGER_TELEPHONE,
Email = u.USAGER_EMAIL,
FonctionTravail = u.USAGER_FONCTION,
LieuTravail = u.USAGER_LIEUTRAVAIL,
Note = u.USAGER_NOTE,
Actif = u.USAGER_ACTIF,
ActifStatut = u.USAGER_ACTIF == 0 ? "Inactif" : "Actif"
});
This is the error:
LINQ to Entities does not recognize the method 'System.String RemoveDiacritics(System.String)' method, and this method cannot be translated into a store expression.
There's built-in functionality to do this in entityframework: https://learn.microsoft.com/en-us/ef/core/miscellaneous/collations-and-case-sensitivity if you're using EF 5+
You'll want an accent insensitive collation ("AI", not "AS" in the examples on that page.)

Custom properties are not updated for the word via openXML

I am trying to update custom properties of word document thru Open XML programming but it seems the updated properties are not getting saved properly for the word document. So when I opening document after successful execution of the update custom property code, I am getting the message box which is "This document contains field that may refer to other files; Do you want to update the fields in the Document?" If I am pressing 'NO' button then all the update properties would not be saved to the document. If we are going for yes option then it will update properties but I need to save the properties explicitly. Please suggest to save properties to the document without getting confirmation message or corrupting the document. :)
the code snippet is given as below,
public void SetCustomValue(
WordprocessingDocument document, string propname, string aValue)
{
CustomFilePropertiesPart oDocCustomProps = document.CustomFilePropertiesPart;
Properties props = oDocCustomProps.Properties;
if (props != null)
{
//logger.Debug("props is not null");
foreach (var prop in props.Elements<CustomDocumentProperty>())
{
if (prop != null && prop.Name == propname)
{
//logger.Debug("Setting Property: " + prop.Name + " to value: " + aValue);
prop.Remove();
var newProp = new CustomDocumentProperty();
newProp.FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
newProp.Name = prop.Name;
VTLPWSTR vTLPWSTR1 = new VTLPWSTR();
vTLPWSTR1.Text = aValue;
newProp.Append(vTLPWSTR1);
props.AppendChild(newProp);
props.Save();
}
}
int pid = 2;
foreach (CustomDocumentProperty item in props)
{
item.PropertyId = pid++;
}
props.Save();
}
}
I am using .Net framework 3.5 with Open XML SDK 2.0 and Office 2013.
Try this one
var CustomeProperties = xmlDOc.CustomFilePropertiesPart.Properties;
foreach (CustomDocumentProperty customeProperty in CustomeProperties)
{
if (customeProperty.Name == "DocumentName")
{
customeProperty.VTLPWSTR = new VTLPWSTR("My Custom Name");
}
else if (customeProperty.Name == "DocumentID")
{
customeProperty.VTLPWSTR = new VTLPWSTR("FNP.SMS.IQC");
}
else if (customeProperty.Name == "DocumentLastUpdate")
{
customeProperty.VTLPWSTR = new VTLPWSTR(DateTime.Now.ToShortDateString());
}
}
//Open Word Setting File
DocumentSettingsPart settingsPart = xmlDOc.MainDocumentPart.GetPartsOfType<DocumentSettingsPart>().First();
//Update Fields
UpdateFieldsOnOpen updateFields = new UpdateFieldsOnOpen();
updateFields.Val = new OnOffValue(true);
settingsPart.Settings.PrependChild<UpdateFieldsOnOpen>(updateFields);
settingsPart.Settings.Save();
you have to update your document fields on open.

IQueryable in foreach - open DataReader error

When i run this code
public PartialViewResult GetHardware()
{
IQueryable<Hardware> hardware = db.Hardwares;
HardwareState hwState = new HardwareState();
IQueryable<IGrouping<string, Hardware>> groupByCategory = hardware.GroupBy(g => g.Category);
foreach (IGrouping<string, Hardware> group in groupByCategory)
{
hwState.GroupName = group.Key;
hwState.GroupUnitsCount = group.Count();
hwState.StorageReservedCount
= group.Where(m =>
m.Place.IsStorage == true &&
m.PlaceID != (int)Constants.HardwareState.Created &&
m.HardwareState == (int)Constants.HardwareState.Reserved).Count();
}
return PartialView(hwState);
}
I get an error about that the navigation property m.Place = null
when i transfer some of the text with the code of the foreach block
public PartialViewResult GetHardware()
{
IQueryable<Hardware> hardware = db.Hardwares;
HardwareState hwState = new HardwareState();
IQueryable<IGrouping<string, Hardware>> groupByCategory = hardware.GroupBy(g => g.Category);
hwState.StorageReservedCount
= hardware.Where(m =>
m.Place.IsStorage == true &&
m.PlaceID != (int)Constants.HardwareState.Created &&
m.HardwareState == (int)Constants.HardwareState.Reserved).Count();
foreach (IGrouping<string, Hardware> group in groupByCategory)
{
hwState.GroupName = group.Key;
hwState.GroupUnitsCount = group.Count();
}
return PartialView(hwState);
}
,the navigation property is not set to null and the error does not appear
Extension methods such as .AsQueryable or Include(x => x.Place) do not help me
How can i solve this problem?
UPDATE: If i change the type to IEnumerable instead IQueryable it begins to work!
but i would like to work with the IQueryable type
UPDATE2: I'm sorry, i did not put it correctly when i wrote that the error is an empty navigation property. Error that appears in fact
"There is already an open DataReader associated with this Command which must be closed first."
As described in this answer it is because: (quote)
"Another scenario when this always happens is when you iterate through
result of the query (IQueryable) and you will trigger lazy loading for
loaded entity inside the iteration."
But it does not say how to solve the problem without using ToList () or MARS

how to debug EF 5 null reference exception?

I am getting a null refrence exception when im filtering EF but I am absolultely clueless.
public IEnumerable<TonalityBatchModel> GetTonalityBatch(int briefID)
{
try
{
var brief = NeptuneUnitOfWork.Briefs.FindWhere(b => b.ID == briefID).FirstOrDefault();
if (brief != null && brief.TonalityCriteria != null)
{
return brief.TonalityCriteria.TonalityBatches
.Select(b => new TonalityBatchModel()
{
BriefID = b.BriefID,
Status = b.TonalityCriteria.IsActive == true ?"Active":"Ended",
BatchID = b.ID,
CompetitorID = b.BriefCompetitorID,
Competitor = brief.BriefCompetitors.Where(i=>i.ID == b.BriefCompetitorID).Select(c=>c.Organisation.Name).First(),
Size = b.BatchSize,
StartDate = b.StartDate,
EndDate = b.EndDate,
IsPublished = b.Lookup_TonalityBatchStatus.ID == (int)TonalityBatchStatus.Published?"Yes":"No",
IsCompleted = b.Lookup_TonalityBatchStatus.ID == (int)TonalityBatchStatus.Completed ? "Yes" : "No",
IsAssigned = b.Lookup_TonalityBatchStatus.ID == (int)TonalityBatchStatus.Allocated ? "Yes" : "No",
ImportantCount = b.TonalityItems.Count(i=> i.IsImportant),
ArticlesCount = b.TonalityItems.Count,
FavourableCount = b.TonalityItems.Count(i => i.Lookup_TonalityScoreTypes.ID ==(int)TonalitySourceType.Favourable),
UnfavourableCount = b.TonalityItems.Count(i => i.Lookup_TonalityScoreTypes.ID ==(int)TonalitySourceType.Unfavourable),
NeutralCount = b.TonalityItems.Count(i => i.Lookup_TonalityScoreTypes.ID ==(int)TonalitySourceType.Neutral)
}).ToList();
}
return new List<TonalityBatchModel>();
}
catch (Exception ex)
{
Logger.Error(ex);
throw;
}
}
You'll need to reduce your query to a simpler query, and then start building it back up again until the NullReferenceException occurs. Looking at your code, here are some likely places (I'm making some assumptions since I don't know everything about your model):
Competitor = brief.BriefCompetitors.Where(i=>i.ID == b.BriefCompetitorID).Select(c=>c.Organisation.Name).First()
BriefCompetitors could be null. c.Organisation could be null.
IsPublished = b.Lookup_TonalityBatchStatus.ID == (int)TonalityBatchStatus.Published?"Yes":"No",
(and other similar lines) b.Lookup_TonalityBatchStatus might be null.
ImportantCount = b.TonalityItems.Count(i=> i.IsImportant),
(and other similar lines) b.TonalityItems might be null.
I believe this is because your count is returning null records. I could be wrong but the SQL that's being produced here is something like:
INNER JOIN TonalityItems i on i.Lookup_TonalityScoreTypes == x
Where x is the value of (int)TonalitySourceType.Favourable. Because this join has no matching results there is nothing to do a count on. You could try adding ?? 0 to the end of the query:
FavourableCount = b.TonalityItems.Count(i => i.Lookup_TonalityScoreTypes.ID ==(int)TonalitySourceType.Favourable) ?? 0,

Add Hyperlink to Cell in Excel 2007 Using Open XML SDK 2.0

I can't seem to find any documentation or code samples on how to add a hyperlink to a cell in Excel 2007 using the Open XML SDK 2.0. I am using the following code, but is there a step I am missing?
WorksheetPart workSheetPart = ExcelUtilities.GetWorkSheetPart(mWorkBookPart, "Program");
workSheetPart.AddHyperlinkRelationship(new Uri("http://www.google.com", UriKind.Absolute), true);
workSheetPart.Worksheet.Save();
mWorkBookPart.Workbook.Save();
Then when I try and open the Excel document it says the file is corrupted because the Relationship Id for the hyperlink cannot be found. How do you setup or create that Relationship Id?
Another possibility, (which I used), is to use the HYPERLINK formula for Excel. I needed to create individual hyperlinks in each cell, yet the cells had to display different text, (I had to display tracking numbers in the cells yet have a hyperlink for each tracking number to the carrier's site and had to handle multiple carriers).
Once I instantiated an individual cell, the formula was applied in this manner to each cell (there are undoubtedly numerous way):
// ...
Cell cell1 = new Cell(){ CellReference = "A1", StyleIndex = (UInt32Value)1U, DataType = CellValues.InlineString };
CellValue cellValue1 = new CellValue();
CellFormula cellFormula1 = new CellFormula() { Space = SpaceProcessingModeValues.Preserve };
cellFormula1.Text = #"HYPERLINK(""http://www.theclash.com"", ""Radio Clash"")";
cellValue1.Text = "Radio Clash";
cell1.Append(cellFormula1);
cell1.Append(cellValue1);
// append cell, etc.
In this way, I was able to create individual hyperlinks and text for each cell. By the way, the links will appear with the default font color unless you reference a style with blue font.
Hope this helps.
I was able to add a hyperlink to a cell using System.IO.Packaging code:
private void HyperlinkCreate(PackagePart part, XmlNamespaceManager nsm, XmlNode _cellElement, string CellAddress)
{
Uri _hyperlink = new Uri("http://www.yahoo.com");
XmlNode linkParent = _cellElement.OwnerDocument.SelectSingleNode("//d:hyperlinks", nsm);
if (linkParent == null)
{
// create the hyperlinks node
linkParent = _cellElement.OwnerDocument.CreateElement("hyperlinks", #"http://schemas.openxmlformats.org/spreadsheetml/2006/main");
XmlNode prevNode = _cellElement.OwnerDocument.SelectSingleNode("//d:conditionalFormatting", nsm);
if (prevNode == null)
{
prevNode = _cellElement.OwnerDocument.SelectSingleNode("//d:mergeCells", nsm);
if (prevNode == null)
{
prevNode = _cellElement.OwnerDocument.SelectSingleNode("//d:sheetData", nsm);
}
}
_cellElement.OwnerDocument.DocumentElement.InsertAfter(linkParent, prevNode);
}
string searchString = string.Format("./d:hyperlink[#ref = '{0}']", CellAddress);
XmlElement linkNode = (XmlElement)linkParent.SelectSingleNode(searchString, nsm);
XmlAttribute attr;
if (linkNode == null)
{
linkNode = _cellElement.OwnerDocument.CreateElement("hyperlink", #"http://schemas.openxmlformats.org/spreadsheetml/2006/main");
// now add cell address attribute
linkNode.SetAttribute("ref", CellAddress);
linkParent.AppendChild(linkNode);
}
attr = (XmlAttribute)linkNode.Attributes.GetNamedItem("id", #"http://schemas.openxmlformats.org/officeDocument/2006/relationships");
if (attr == null)
{
attr = _cellElement.OwnerDocument.CreateAttribute("r", "id", #"http://schemas.openxmlformats.org/officeDocument/2006/relationships");
linkNode.Attributes.Append(attr);
}
PackageRelationship relationship = null;
string relID = attr.Value;
if (relID == "")
relationship = part.CreateRelationship(_hyperlink, TargetMode.External, #"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink");
else
{
relationship = part.GetRelationship(relID);
if (relationship.TargetUri != _hyperlink)
relationship = part.CreateRelationship(_hyperlink, TargetMode.External, #"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink");
}
attr.Value = relationship.Id;
}
I then translated this code using the Open XML SDK 2.0 and it doesn't work. It seems the AddHyperlinkRelationship method doesn't actually add the relationship to the .rels file. I'm not sure why, but it sure seems like a bug to me.
private void HyperlinkCreate(PackagePart part, XmlNamespaceManager nsm, XmlNode _cellElement, string CellAddress)
{
WorksheetPart workSheetPart = ExcelUtilities.GetWorkSheetPart(mWorkBookPart, "Program");
Uri hyperlinkUri = new Uri("http://www.yahoo.com", UriKind.Absolute);
Hyperlinks hyperlinks = workSheetPart.Worksheet.Descendants<Hyperlinks>().FirstOrDefault();
// Check to see if the <x:hyperlinks> element exists; if not figure out
// where to insert it depending on which elements are present in the Worksheet
if (hyperlinks == null)
{
// Create the hyperlinks node
hyperlinks = new Hyperlinks();
OpenXmlCompositeElement prevElement = workSheetPart.Worksheet.Descendants<ConditionalFormatting>().FirstOrDefault();
if (prevElement == null)
{
prevElement = workSheetPart.Worksheet.Descendants<MergeCells>().FirstOrDefault();
if (prevElement == null)
{
// No FirstOrDefault needed since a Worksheet requires SheetData or the excel doc will be corrupt
prevElement = workSheetPart.Worksheet.Descendants<SheetData>().First();
}
}
workSheetPart.Worksheet.InsertAfter(hyperlinks, prevElement);
}
Hyperlink hyperlink = hyperlinks.Descendants<Hyperlink>().Where(r => r.Reference.Equals(CellAddress)).FirstOrDefault();
if (hyperlink == null)
{
hyperlink = new Hyperlink() { Reference = CellAddress, Id = string.Empty };
}
HyperlinkRelationship hyperlinkRelationship = null;
string relId = hyperlink.Id;
if (relId.Equals(string.Empty))
{
hyperlinkRelationship = workSheetPart.AddHyperlinkRelationship(hyperlinkUri, true);
}
else
{
hyperlinkRelationship = workSheetPart.GetReferenceRelationship(relId) as HyperlinkRelationship;
if (!hyperlinkRelationship.Uri.Equals(hyperlinkUri))
{
hyperlinkRelationship = workSheetPart.AddHyperlinkRelationship(hyperlinkUri, true);
}
}
hyperlink.Id = hyperlinkRelationship.Id;
hyperlinks.AppendChild<Hyperlink>(hyperlink);
workSheetPart.Worksheet.Save();
}
You should be adding it to an object that accepts hyperlinks, such as a cell, instead of the worksheet. Something like this should work for you:
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
namespace GeneratedCode
{
public class GeneratedClass
{
// Creates an Worksheet instance and adds its children.
public Worksheet GenerateWorksheet()
{
Worksheet worksheet1 = new Worksheet(){ MCAttributes = new MarkupCompatibilityAttributes(){ Ignorable = "x14ac" } };
worksheet1.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
worksheet1.AddNamespaceDeclaration("mc", "http://schemas.openxmlformats.org/markup-compatibility/2006");
worksheet1.AddNamespaceDeclaration("x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
SheetDimension sheetDimension1 = new SheetDimension(){ Reference = "A1" };
SheetViews sheetViews1 = new SheetViews();
SheetView sheetView1 = new SheetView(){ TabSelected = true, WorkbookViewId = (UInt32Value)0U };
sheetViews1.Append(sheetView1);
SheetFormatProperties sheetFormatProperties1 = new SheetFormatProperties(){ DefaultRowHeight = 14.4D, DyDescent = 0.3D };
SheetData sheetData1 = new SheetData();
Row row1 = new Row(){ RowIndex = (UInt32Value)1U, Spans = new ListValue<StringValue>() { InnerText = "1:1" }, DyDescent = 0.3D };
Cell cell1 = new Cell(){ CellReference = "A1", StyleIndex = (UInt32Value)1U, DataType = CellValues.SharedString };
CellValue cellValue1 = new CellValue();
cellValue1.Text = "0";
cell1.Append(cellValue1);
row1.Append(cell1);
sheetData1.Append(row1);
Hyperlinks hyperlinks1 = new Hyperlinks();
Hyperlink hyperlink1 = new Hyperlink(){ Reference = "A1", Id = "rId1" };
hyperlinks1.Append(hyperlink1);
PageMargins pageMargins1 = new PageMargins(){ Left = 0.7D, Right = 0.7D, Top = 0.75D, Bottom = 0.75D, Header = 0.3D, Footer = 0.3D };
worksheet1.Append(sheetDimension1);
worksheet1.Append(sheetViews1);
worksheet1.Append(sheetFormatProperties1);
worksheet1.Append(sheetData1);
worksheet1.Append(hyperlinks1);
worksheet1.Append(pageMargins1);
return worksheet1;
}
}
}
Easiest way is to use the HyperLink formular, but the links aren't blue by default, this is why the styleIndex is set.
private Cell BuildHyperlinkCell(string url) =>
new Cell
{
DataType = new EnumValue<CellValues>(CellValues.String),
CellFormula = new CellFormula($"HyperLink(\"{url}\")"),
StyleIndex = 4u
};
Adding the styling to the workbook:
http://www.dispatchertimer.com/tutorial/how-to-create-an-excel-file-in-net-using-openxml-part-3-add-stylesheet-to-the-spreadsheet/