Retrieving Building Blocks from user generated Word document - c#

I've searched far and wide, and I haven't found a proper solution yet.
First off, this is a WinForms application using .NET 4.0 and DevExpress.
I've been attempting to retrieve Building Blocks (Watermarks, Cover Pages etc) from Word documents (.docx), or at least user generated template files (.dotx), that a user uploads to my application, which will then be saved to a database.
I must be able to retrieve all the Building Blocks used in that file.
I have tried a lot of different ways of retrieving them, but I can only retrieve them from the Built-In Building Blocks.dotx file, located in:
C:\Users\<username>\AppData\Roaming\Microsoft\Document Building Blocks\1033\14\Built-In Building Blocks.dotx
I haven't figured out how to extract them from a user generated file.
Here is work in progress code I've been using (many iterations so I can easily debug it):
private void SaveBuildingBlock(string savedFile)
{
try
{
Microsoft.Office.Interop.Word.ApplicationClass wordApplication = null;
wordApplication = new Microsoft.Office.Interop.Word.ApplicationClass();
Microsoft.Office.Interop.Word.Document wordDocument = wordApplication.Documents.OpenNoRepairDialog(savedFile);
object missing = System.Type.Missing;
Database.ToolbarItemsWordInsertFileData.FileData = FileHandler.FileByteLocked(savedFile);
Database.ToolbarItemsWordInsertFileData.Id = 0;
Database.ToolbarItemsWordInsertFileData.ToolbarItemId = ToolbarId;
Database.ToolbarItemsWordInsertFileData.Save();
ListBuildingBlocks(wordApplication, wordPage);
}
catch (Exception err)
{
Logger.Log(err);
}
}
Next method:
private void ListBuildingBlocks(Microsoft.Office.Interop.Word.Application wordApplication, WordPages wordPage)
{
Microsoft.Office.Interop.Word.WdBuildingBlockTypes type = wordPage == WordPages.CoverPage ? type = Microsoft.Office.Interop.Word.WdBuildingBlockTypes.wdTypeCoverPage : type = Microsoft.Office.Interop.Word.WdBuildingBlockTypes.wdTypeWatermarks;
for (int i = 1; i <= wordApplication.Templates.Count; i++)
{
try
{
if (wordApplication.Templates[i] != null)
{
Microsoft.Office.Interop.Word.Categories categories = wordApplication.Templates[i].BuildingBlockTypes.Item(type).Categories;
for (int c = 1; c <= categories.Count; c++)
{
try
{
//Category cat = categories.Application[0];
Microsoft.Office.Interop.Word.BuildingBlocks buildingBlocks = wordApplication.Templates[i].BuildingBlockTypes.Item(type).Categories.Item(categories.Item(c).Name).BuildingBlocks;
for (int b = 1; b <= buildingBlocks.Count; b++)
{
try
{
Microsoft.Office.Interop.Word.BuildingBlock buildingBlock = buildingBlocks.Item(b);
if (buildingBlock != null)
{
//saving to database occurs here
}
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
}
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
}
}
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
}
}
The WordPages parameter [wordPage] is used in the application to specify if it's a Watermark or Cover Page (it's a property in this user control).

You need to add it to the current list of add-ins, then you can open it as a template for working with building blocks. Apart from changing the attached template and loading it that way, I don't see any other way of doing this:
Dim app = Globals.ThisAddIn.Application
'load the file containing the building blocks
app.AddIns.Add("\\Path to File.dotx")
'get the object for the loaded template
Dim template = app.Templates(app.AddIns.Count)
'go through each building block in the file
For I = 1 to template.BuildingBlockEntries.Count
Dim bb = template.BuildingBlockEntries.Item(i)
Next
If you need to go down the xml route, the building blocks are stored inside the package in word\glossary\document.xml which is probably easier if you are doing this server side then you wont need to work with the word object model at all, just use the Open Xml SDK, it's pretty useful for things like this!

Related

Automatically generate nodes in a Canoe configuration simulation

I am trying to automate and create a Canoe simulation.
My usecase :
I have a configuration (LibraryTest.cfg) with a CAN Network and a node ACAN in the network. I want to create another node BCAN automatically into the existing configuration along with ACAN. I am trying this using C# .NET Canoe Library for this.
CANoe.Application mApp;
CANoe.Measurement mMsr;
CANoe.Networks mNet;
mApp = new CANoe.Application();
string ConfigFile=
"C:\\Users\\deepasreeraj\\Desktop\\GAC\\TestUnit1\\LibraryTest.cfg";
try
{
mApp.Open(ConfigFile, true, true);
mMsr = (CANoe.Measurement)mApp.Measurement;
mNet = mApp.Networks;
CANoe.Simulation mSim = mApp.Simulation;
if (mNet != null)
{
if(mNet != null)
{
int count = mNet.Count;
for (int i = 0; i < count; i++)
{
mNet.Add("BCAN");
string Nodename = mNet[i].NetworkInterfaces;
}
}
}
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.Message);
}
}
In this, while the code reaches mNet.Add("BCAN"); it gives an exception "The method or operation is not implemented." Can someone please help me with this?
If you want to add a node, the Networks property is wrong.
You have to use mApp.Configuration.SimulationSetup.Buses.Nodes. There you can call Add to add a new node.
Just check the page Technical References -> COM Interface -> Object Hierarchy in the CANoe help for the complete API Reference.

unable to read text file using stream reader and place txt into custom class

so i am trying to read text from a txt file and then add the text in to a custom class list,
the code is
public static List<BookInfo> LoadCSVFile(string fileName, out string qError)
{
qError = "";
fileName = "books.txt";
List<BookInfo> Book_Info = new List<BookInfo>();
StreamReader read = null;
try
{
read = new StreamReader(fileName);
while (!read.EndOfStream)
{
string line = read.ReadLine();
string[] values = line.Split(',');
if (values.Length == 3)
{
string Title = values[0].Trim();
string Author = values[1].Trim();
string ISBN = values[2].Trim();
try
{
Book_Info.Add(new BookInfo(Title, Author, ISBN));
}
catch (Exception ex)
{
qError = ex.Message;
return null;
}
}
else
{
qError = $"line {line} was unable to be read";
return null;
}
}
}
catch
{
qError = $"failed to open file: {fileName}";
return null;
}
finally
{
if (read != null)
{
read.Close();
}
}
if (qError == "")
{
return Book_Info;
}
return null;
}
once i have read the text it will be displayed in a form which i believe to be coded correctly
i have placed an error message in to show when the file has been read and each time i try something new the same error appears.
have i gone wrong somewhere when reading the txt file?
Edit:
Text file was created using visual studio and is in the same solution, text file is in bin/debug
I totally agree with thegeneral's answer, but to answer your initial question I suspect your books.txt file was not located in your Bin/Debug folder. I did test your code ;-P
Some notes
if you are going to use something that Implements IDisposable, it's always good practice to use the using statement
If this is only a small file, why bother with StreamReader when you can just use File.ReadAllLines
Linq is your friend, projection is a wonderful thing.
If you really want to parse a CSV file, I'd seriously consider a dedicated CSV parser library (like CsvHelper). It will save you many headaches
This is not really the bastion of perfect coding, however I tried to work with what you had and the spirit of what you were trying to do.
Some code:
public static List<BookInfo> LoadCSVFile(string fileName, out string qError)
{
try
{
// read all lines in to another type
// just makes it easier for errors which you seem to want
var lines = File.ReadAllLines(fileName)
.Select(x => new { Values = x.Split(','), Text = x })
.ToList();
// get a list of errors,
var errors = lines.Where(x => x.Values.Length != 3)
.Select((s, i) => $"Bad book! Line {i} : {s.Text}");
// return some errors
qError = string.Join(Environment.NewLine, errors);
// project lines to your books
return lines.Where(x => x.Values.Length == 3)
.Select(x => new BookInfo(x.Values[0], x.Values[0], x.Values[0]))
.ToList();
}
catch (Exception e)
{
qError = e.Message;
}
return null;
}
Disclaimer
I wouldn't usually catch errors like this, it's rather nasty
The whole anonymous type and returning errors is a bit smelly, if the file is corrupt, I'd just throw a big nasty exception and be done with it. Make the user do the right thing
This is going to fail the second you have a title with a comma in it
This is completely untested!

How to search for empty cells in a specific column in a CSV file for all rows?

Goal: Search through a specific single column in a CSV for empty rows only in that column and replace with string "No Box".
Attempts: So far I have tried to use CsvHelper and CsvTools(CsvReader) via Nuget C#. I am not very experienced with C# so not sure how to accomplish my task. Searching did not turn up any examples or references that helped me understand what I need to implement. There are a lot of similar questions, but none of them searched a specific column. I am hoping someone can provide me with advice on how to get my for loop to work and get the number of rows for my checking.
Image sample of my CSV file.
Sample of CSV data column Site
private static void SiteBlanks()
{
try
{
MutableDataTable dt = DataAccess.DataTable.New.ReadCsv(#"C:\temp.csv");
for (int i = 0; i <= dt.Rows.Count; i++) // Cannot be applied to data types, so this errors.
{
if (!string.IsNullOrEmpty(dt.GetRow(i)["Site"])) // Check if cells in column 1 are empty
{
dt.Columns[1].Values[i] = "No Box"; // Update empty values with No Box
}
}
dt.SaveCSV(#"C:\temp.csv"); // Save file after changes.
}
catch (Exception ex)
{
//Set Error message
Error("ERROR: SiteBlanks()", ex);
}
}
Note: This is my first question ever asked so be gentle and tell me what I may have did wrong posting wise.
Based on your current code you can try the following
private static void SiteBlanks() {
try {
string filePath = #"C:\temp.csv";
MutableDataTable dt = DataTable.New.ReadCsv(filePath);
string columnName = "Site";
var numberOfRows = dt.NumRows ;
for (int i = 0; i < numberOfRows; i++) {
var row = dt.GetRow(i);
if (string.IsNullOrEmpty(row[columnName])) {
row[columnName] = "No Box";
}
}
dt.SaveCSV(filePath);
} catch (Exception ex) {
//Set Error message
Error("ERROR: SiteBlanks()", ex);
}
}

C# COMException reading property of MSWord Shape object Microsoft.Office.Interop.Word

I am trying to loop through all shapes in a document and check their "Alternate Text" which has had the source filename for that image recorded as it's alternate text. I need to read specific source images and convert them to a different image format.
I am able to get to the point of reading the AlternateText of the shape but it throws an exception:
'((Microsoft.Office.Interop.Word.Shape)(s)).AlternativeText' threw an exception of type 'System.Runtime.InteropServices.COMException'
When I set a breakpoint and view the "s" object, the majority of properties are throwing this exception, however some are not, for example I can read the LinkFormat property and a few others without issue, but the majority of properties throw an error.
Here is the code I am using:
Word.Application WordApp = new Word.Application();
d = WordApp.Documents.Open(#strFilename, ReadOnly: true, Visible: false);
int iReplacements = 0;
int iReplacementNoLink = 0;
foreach (Word.Shape s in d.Shapes)
{
Application.DoEvents();
try
{
if (s.LinkFormat.SourceName.ToString().Contains(".eps") || s.LinkFormat.SourceName.ToString().Contains(".png"))
{
iReplacements++;
}
if (s.AlternativeText != "")
{
iReplacementNoLink++;
}
}
catch (Exception fff)
{
Console.Write(fff);
}
}
The if statement checking the s.AlternateText always ends up in the catch.
I am using Visual Studio 2013 and I have Office 2007, I am not sure if that is relevant or not.
Can anyone tell me what I need to do to be able to read the Alternate Text of the shapes? If I am going about it in the wrong way or need to include a library or if I need to upgrade VS or Office? It seems like it should be really straight forward.
Thank you for any assistance you can provide.
I am unsure why this worked, but I was able to resolve this issue by using the "Select" method of the shape. Once the shape is selected the majority of the properties that previously were throwing errors are populated. There are still approximately 20 properties that thow the error, but I am now able to access things like "AlternativeText","Name","Callout" which were previously throwing the error.
Word.Application WordApp = new Word.Application();
d = WordApp.Documents.Open(#strFilename, ReadOnly: true, Visible: false);
int iReplacements = 0;
int iReplacementNoLink = 0;
foreach (Word.Shape s in d.Shapes)
{
Application.DoEvents();
try
{
//if (s.Type == Microsoft.Office.Core.MsoShapeType.msoLinkedPicture)
if (s.LinkFormat.SourceName.ToString().Contains(".eps") || s.LinkFormat.SourceName.ToString().Contains(".png"))
{
iReplacements++;
}
s.Select();
if (s.AlternativeText != "" && s.AlternativeText != null)
{
iReplacementNoLink++;
}
}
catch (Exception fff)
{
Console.Write(fff);
}
}

strange behavior of XamlReader.Load()?

I've got a very strange issue while parsing an external XAML file. The pre-history is that I want to load an external XAML file with content to process. But I want to load as many different files as I want. That happens by unloading the old and loading the new one.
My issue is:
When I load a xaml the first time, everything is good, all as it should be.
But when I load the same xaml the second time, every entry of the object im Loading is there twice. If I run this again, every object is there three times and so on...
To debug the project yourself, download it here. The function starts at line 137 in the file "Control Panel.xaml.cs". I realy don't know what this is. Is it my fault or simply a bug? If yes, is there a workaround?
/// <summary>
/// Load a xaml file and parse it
/// </summary>
public void LoadPresentation()
{
this.Title = "Control Panel - " + System.IO.Path.GetFileName(global.file);
System.IO.FileStream XAML_file = new System.IO.FileStream(global.file, System.IO.FileMode.Open);
try
{
System.IO.StreamReader reader = new System.IO.StreamReader(XAML_file);
string dump = reader.ReadToEnd(); //This is only for debugging purposes because of the strange issue...
XAML_file.Seek(0, System.IO.SeekOrigin.Begin);
presentation = (ResourceDictionary)XamlReader.Load(XAML_file);
//Keys the resourceDictionary must have to be valid
if (presentation["INDEX"] == null || presentation["MAIN_GRID"] == null || presentation["CONTAINER"] == null || presentation["LAYOUTLIST"] == null)
{
throw new Exception();
}
//When this list is loaded, every item in it is there twice or three times or four... Why????
TopicList Index = null;
Index = (TopicList)presentation["INDEX"];
for (int i = 0; i < topics.Count; )
{
topics.RemoveAt(i);
}
foreach (TopicListItem item in Index.Topics)
{
topics.Insert(item.TopicIndex, (Topic)presentation[item.ResourceKey]);
}
lv_topics.SelectedIndex = 0;
selectedIndex = 0;
}
catch
{
System.Windows.Forms.MessageBox.Show("Failed to load XAML file \"" + global.file + "\"", "Parsing Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
presentation = null;
}
finally
{
XAML_file.Close();
}
}
Edit:
I have tried to serialize the object that was read from the XamlReader and in the output was nowhere any childelement... But if I pull the object out of the dictionary, the children are all there (duplicated and triplicated, but there).
I have already tried to clear the list over
topics.Clear();
and
topics=new ObservableCollection<TopicListItem>();
lv_topics.ItemsSource=topics;
Try Index.Topics.Clear() after loading the Topics into your topics object. That appears to get rid of the duplication.
//When this list is loaded, every item in it is there twice or three times or four... Why????
TopicList Index = null;
Index = (TopicList)presentation["INDEX"];
topics.Clear();
foreach (TopicListItem item in Index.Topics)
{
topics.Insert(item.TopicIndex, (Topic)presentation[item.ResourceKey]);
}
Index.Topics.Clear(); //Adding this will prevent the duplication
lv_topics.SelectedIndex = 0;
selectedIndex = 0;
In the code post topics is not declared in LoadPresentation() so naturally it will have any prior values.
I know you said you tried topics=new ObservableCollection(); but please try again. And put that IN LoadPresentation()
public void LoadPresentation()
{
ObservableCollection<TopicListItem> topics = new ObservableCollection<TopicListItem>()
I would pass filename
public void LoadPresentation(string fileName)
I get you may need to use topics outside LoadPresentation but this is debugging. If you need topics outside the return it.
public ObservableCollection<TopicListItem> LoadPresentation(string fileName)
If that does not fix it I would put a try catch block on the XAML_file.Close(); to see if something weird is not going on.

Categories

Resources