This code is replacing the text with an image but its placing the multiple copies of an image and placing them in the beginning of the document. I want the image to be placed at the same position where text was present. My find text is available in the table cell. Is it due to that?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using word = Microsoft.Office.Interop.Word;
using System.Runtime.InteropServices;
//using System.Drawing;
namespace WritingIntoDocx
{
[ComVisible(true)]
public interface IMyClass
{
void DocumentDigitalSign(string filep,string findt,string replacet);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class Program : IMyClass
{
public void DocumentDigitalSign(string filep, string findt, string imagepath)
{
string filepath = filep;
string Findtext = findt;
word.Application app = new word.Application();
word.Document doc = app.Documents.Open(filepath);
word.Range myStoryRange = doc.Range();
//First search the main document using the Selection
word.Find myFind = myStoryRange.Find;
myFind.Text = Findtext; myFind.Replacement.Application.Selection.InlineShapes.AddPicture(imagepath);
myFind.Forward = true;
myFind.Wrap = word.WdFindWrap.wdFindContinue;
myFind.Format = false;
myFind.MatchCase = false;
myFind.MatchWholeWord = false;
myFind.MatchWildcards = false;
myFind.MatchSoundsLike = false;
myFind.MatchAllWordForms = false;
myFind.Execute(Replace: word.WdReplace.wdReplaceAll);
//'Now search all other stories using Ranges
foreach (word.Range otherStoryRange in doc.StoryRanges)
{
if (otherStoryRange.StoryType != word.WdStoryType.wdMainTextStory)
{
word.Find myOtherFind = otherStoryRange.Find;
myOtherFind.Text = Findtext; myOtherFind.Replacement.Application.Selection.InlineShapes.AddPicture(imagepath);
myOtherFind.Wrap = word.WdFindWrap.wdFindContinue;
myOtherFind.Execute(Replace: word.WdReplace.wdReplaceAll);
}
// 'Now search all next stories of other stories (doc.storyRanges dont seem to cascades in sub story)
word.Range nextStoryRange = otherStoryRange.NextStoryRange;
while (nextStoryRange != null)
{
word.Find myNextStoryFind = nextStoryRange.Find;
myNextStoryFind.Text = Findtext;
myNextStoryFind.Replacement.Application.Selection.InlineShapes.AddPicture(imagepath);
myNextStoryFind.Wrap = word.WdFindWrap.wdFindContinue;
myNextStoryFind.Execute(Replace: word.WdReplace.wdReplaceAll);
nextStoryRange = nextStoryRange.NextStoryRange;
}
}
app.Documents.Save();
app.Documents.Close();
}
}
}
Replacement.Application is a reference to the application object. When you call AddPicture() on that, the picture is immediately inserted at the current position before the find operation is even executed.
I see two possibilities:
Load the picture, place it into the Windows clipboard and then execute the find operation specifying "^c" as replacement text. Word will replace "^c" with the current content of the clipboard. This is what the documentation says:
ReplaceWith
Type: System.Object
Optional Object.
The replacement text. To delete the text specified by the Find argument, use an empty string (""). You specify special characters and advanced search criteria just as you do for the Find argument. To specify a graphic object or other non-text item as the replacement, move the item to the Clipboard and specify "^c" for ReplaceWith.
Do not use wdReplaceAll, but wdReplaceNone, so that the find operation itself does not do any replacing. But you then have the chance to insert your content at the place found. Do that in a loop until no more occurrence is found.
Related
I am running some test and using OpenXML now I want to get the data into an Array and save to their respective content controls. I tried and I am getting this execption
Sequence contains no Elements at System.Linq.Enumerable.Single[Tsource](IEnumerable'1 source)
With this source code :
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PopulateContentsOpenXml
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string myfile = #"C:\Users\*****\Desktop\DTestDoc.docx";
string[] writeDocData = new string[] {FirstName.Text,FileNumber.Text,IDNumber.Text,LastName.Text };
WriteDataToContentControl(myfile, writeDocData);
}
private void WriteDataToContentControl(string filename,string[]data)
{
try
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(filename, true))
{
MainDocumentPart mainPart = doc.MainDocumentPart;
foreach (string text in data)
{
SdtElement text_block = mainPart.Document.Body.Descendants<SdtElement>().Where(r => r.SdtProperties.GetFirstChild<Tag>().Val == text).Single();
Text to = text_block.Descendants<Text>().Single();
to.Text = text;
mainPart.Document.Save();
MessageBox.Show("Ok i am fine now!");
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.ToString());
}
}
}
}
And i am getting the Exception on line 39 which is this line
SdtElement text_block = mainPart.Document.Body.Descendants<SdtElement>().Where(r => r.SdtProperties.GetFirstChild<Tag>().Val == text).Single();
Edits
Doing this
SdtElement text_block = mainPart.Document.Body.Descendants<SdtElement>().Where(r => r.SdtProperties.GetFirstChild<Tag>().Val == text).SingleOrDefault();
Text to = text_block.Descendants<Text>().Single();
I am having Object not set to instance of an object. now having checked, it says text_block is null
Why is this so?
There are a number of Linq extension methods that will throw an exception in case the sequence (e.g., IEnumerable<T>) is empty or does not fulfill the assertion you are making by calling that method.
Single<TSource>() will throw an exception if your sequence does not contain exactly one element. Thus, it throws an exception if the sequence is empty or contains more than one element. This is what happened to you. There is no SdtElement with that given tag value.
First<TSource>() and Last<TSource>() will also throw an exception if the sequence is empty. They expect at least one element in the sequence.
If you can't guarantee that your sequence is non-empty, meaning it will contain exactly or at least one SdtElement, you should use SingleOrDefault<TSource>() or potentially FirstOrDefault<TSource>().
SingleOrDefault<TSource>() will not throw if the sequence is empty, but it will throw if it contains more than one element. Thus, it expects at most one element.
FirstOrDefault<TSource>() will not throw in any case. It will return null if the sequence of SdtElement instances is empty. If it is not empty, it will return the first SdtElement, ignoring the others. Thus, it expects zero or more elements.
I'm trying to programatically fill in some ContentControls inside a MS Word document with C#.
So far I've been able to open the document and find all the controls, but they come back as generic ContentControl objects. Inspecting them with a debugger just reveals a generic System.__ComObject.
From the docs I can see that some of the controls should have a .Text property, but I cannot figure out how to access it.
I can determine the type of the control using the switch statement you see below, but it doesn't really help me -- I don't know what class to cast the object to (if that's even what I'm supposed to do).
There is a class called PlainTextContentControl but it exists in Microsoft.Office.Tools.Word, but the Application and Document and ContentControl live under Microsoft.Office.Interop.Word and these do not play nicely together.
So I'm lost. How do I access the Text property? Here's what I've got:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Word;
//using Microsoft.Office.Interop.Word;
using Microsoft.Office.Tools.Word;
using ContentControl = Microsoft.Office.Interop.Word.ContentControl;
using Document = Microsoft.Office.Interop.Word.Document;
namespace ConsoleApplication1
{
internal class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Opening Word Application...");
var app = new Application();
try
{
Console.WriteLine("Loading document...");
var doc = app.Documents.Open(
#"C:\blahblah\template3.docx");
Console.WriteLine("Finding controls...");
var controls = GetAllContentControls(doc);
foreach (var control in controls)
{
Console.WriteLine(control.Tag);
switch (control.Type)
{
case WdContentControlType.wdContentControlText:
var pt = control as PlainTextContentControl;
Console.WriteLine("hit"); // pt is null
break;
}
}
doc.Close();
}
finally
{
app.Quit();
}
}
public static List<ContentControl> GetAllContentControls(Document wordDocument)
{
var ccList = new List<ContentControl>();
foreach (Range range in wordDocument.StoryRanges)
{
var rangeStory = range;
do
{
try
{
foreach (ContentControl cc in rangeStory.ContentControls)
{
ccList.Add(cc);
}
}
catch (COMException)
{
}
rangeStory = rangeStory.NextStoryRange;
} while (rangeStory != null);
}
return ccList;
}
}
}
I should note that I'm using JetBrains Rider instead of Visual Studio. If this is impossible to do with Rider for some reason, I can probably obtain a copy of VS.
You can just use the code resemble the following:
switch (control.Type)
{
case WdContentControlType.wdContentControlText:
var text = control.Range.Text;
//var pt = control as PlainTextContentControl;// pt is null
Console.WriteLine(text);
break;
case WdContentControlType.wdContentControlRichText:
var richText = control.Range.Text;
//var pt1 = control as PlainTextContentControl;// pt1 is null
Console.WriteLine(richText);
break;
}
I have a Visio ER diagram and want to read the database properties (Columns, Primary, Foreign key, data type) information from an Entity. Also want to find the parent and child tables associated. How can I programmatically achieve it using C#?
I am using Interop Visio library and can read the pages and shape from ER diagram but don't know which functions or methods in Visio interop will let me get properties information from a Shape.
Below is the code I am using and I am not getting any property using it. My ER diagram have just two entities a Parent and a Child table.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Office.Interop.Visio;
namespace Visio_POC
{
public partial class Load_Visio : Form
{
static string strProperties = "";
public Load_Visio()
{
InitializeComponent();
string strFileName = "\\Visio_POC\\POC_Visio.vsd";
Microsoft.Office.Interop.Visio.Application vsApp = new Microsoft.Office.Interop.Visio.Application();
Microsoft.Office.Interop.Visio.Document docVisio = vsApp.Documents.Add(strFileName);
Page pgVisio = docVisio.Pages[1];
Shapes shpVisio = pgVisio.Shapes;
int intCnt = shpVisio.Count;
string[] strShapeText = new string[intCnt];
printProperties(pgVisio.Shapes);
txtProperties.Text = strProperties;
}
public static void printProperties(Shapes shapes)
{
// Look at each shape in the collection.
foreach (Shape shape in shapes)
{
// Use this index to look at each row in the properties
// section.
short iRow = (short) VisRowIndices.visRowFirst;
// While there are stil rows to look at.
while (shape.get_CellsSRCExists(
(short) VisSectionIndices.visSectionProp,
iRow,
(short) VisCellIndices.visCustPropsValue,
(short) 0) != 0)
{
// Get the label and value of the current property.
string label = shape.get_CellsSRC(
(short) VisSectionIndices.visSectionProp,
iRow,
(short) VisCellIndices.visCustPropsLabel
).get_ResultStr(VisUnitCodes.visNoCast);
string value = shape.get_CellsSRC(
(short) VisSectionIndices.visSectionProp,
iRow,
(short) VisCellIndices.visCustPropsValue
).get_ResultStr(VisUnitCodes.visNoCast);
// Print the results.
//Console.WriteLine(string.Format(
// "Shape={0} Label={1} Value={2}",
// shape.Name, label, value));
strProperties = strProperties + shape.Name + " - " + label + " - " + value;
// Move to the next row in the properties section.
iRow++;
}
// Now look at child shapes in the collection.
if (shape.Master == null && shape.Shapes.Count > 0)
printProperties(shape.Shapes);
}
}
}
}
The database reverse engineering is a closed solution. There was a book "Database Modeling" by Terry Halpin which covered this. Alternatively you could peruse my article at http://blog.bvisual.net/2014/03/26/creating-a-schema-from-visio-external-data-record-sets/ for an idea of how you could do it yourself.
i'm rather new and am trying to create a C# program that retrieves post from Facebook using FB API.
I have a word count feature which checks against a negative word dictionary.
This means that it would display the negative word along with its frequency occurrence.
The problem i'm facing now is that, i want to display the posts that contains this negative words. However, if the negative word exists 3 times in the post, the post would appear thrice. How do i solve this problem?
Below is my code:
(For designer)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace empTRUST
{
public partial class PostAnalysis : Form
{
DBStatusDL ad;
string target_fbid;
public PostAnalysis(string target_fbid)
{
InitializeComponent();
this.target_fbid = target_fbid;
ad = new DBStatusDL();
}
private void button_Displayposts_Click(object sender, EventArgs e)
{
int i = 1;
var dir = new DirectoryInfo(Application.StartupPath + "\\Dictionary"); //Load the dictionary from debug folder
var ed = new matchingWordsWithPosts();
var rows = ad.LoadStatus(target_fbid); //Call the load status function based on fb_id
foreach (FileInfo file in dir.GetFiles()) //For loop, to loop through files
{
var dict = File.ReadAllLines(dir.FullName + "\\" + file);
foreach (var row in rows)
{
List<DataRow> words = ed.countWordsInStatus(row, dict); // Retrieves word dictionary returned from function
foreach (var word in words)
{
var item = new ListViewItem(new[] { i.ToString() ,word["Status_message"].ToString(), word["Status_time"].ToString() });
listViewPosts.Items.Add(item);
i++;
}
}
}
}
private void button_Back_Click(object sender, EventArgs e)
{
this.Close();
var abc = new AnalysisPage(target_fbid);
abc.Show();
}
}
}
(For class)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Data;
namespace empTRUST
{
class matchingWordsWithPosts
{
public List<DataRow> countWordsInStatus(DataRow status, string[] dictArray)
{
List<DataRow> statusList = new List<DataRow>();
var words = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase); // local word dictionary is created here
foreach (var dictEntry in dictArray)
{
var wordPattern = new Regex(#"\w+");
string smallDictEntry = dictEntry.ToLower();
foreach (Match match in wordPattern.Matches(status["Status_message"].ToString()))
{
if (match.ToString() == smallDictEntry)
{
statusList.Add(status);
}
}
}
return statusList; // returns local word dictionary to receiving end
}
}
}
Because you didn't provide the countWordsInStatus() function, I can't know if that's the problem. However, it looks like the problem is that that function continues going through a post even if it has already matched one such word. To fix this, you could put continue; (or perhaps a break;, depending on the code you're using) after adding a post to the list you're returning. This would have the loop skip to the next post, and make sure it doesn't continue counting words in the post that has already had a match.
If you post that function, it should be much easier to understand the issue.
After a word is matched and you process the post exit the loop.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Text.RegularExpressions;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
TranslateText("hi", "German");
}
private void Form1_Load(object sender, EventArgs e)
{
}
public static string TranslateText(string input, string languagePair)
{
return TranslateText(input, languagePair, System.Text.Encoding.UTF7);
}
/// <summary>
/// Translate Text using Google Translate
/// </summary>
/// <param name="input">The string you want translated</param>
/// <param name="languagePair">2 letter Language Pair, delimited by "|".
/// e.g. "en|da" language pair means to translate from English to Danish</param>
/// <param name="encoding">The encoding.</param>
/// <returns>Translated to String</returns>
public static string TranslateText(string input, string languagePair, Encoding encoding)
{
string url = String.Format("http://www.google.com/translate_t?hl=en&ie=UTF8&text={0}&langpair={1}", input, languagePair);
string result = String.Empty;
using (WebClient webClient = new WebClient())
{
webClient.Encoding = encoding;
result = webClient.DownloadString(url);
}
Match m = Regex.Match(result, "(?<=<div id=result_box dir=\"ltr\">)(.*?)(?=</div>)");
if (m.Success)
result = m.Value;
MessageBox.Show(result);
return result;
}
}
}
I added in the constructor the line:
TranslateText("hi", "German");
And in the bottom i added:
MessageBox.Show(result);
I wanted for the test to translate the word "hi" to German
But the result im getting and in the messagebox is a very long text wich is containing all the google website.
I tried to go manualy to the web site in the string url address and its working im getting to the google translate website.
I dont understand why it dosent work.
I want later to put instead "hi" some text from a text file.
I tried ot use breakpoint and found that this part the Success is all the time return false dont know why:
if (m.Success)
result = m.Value;
I think you are not getting the translated text or value in your html result from your code and also from Google.
Reason:
If you execute this through the browser, it is not translating to the language you expect, example:
http://www.google.com/translate_t?hl=en&ie=UTF8&text=hi&langpair=de
I used langpair=de or langpair=German and doesn't work, it shows me always "hi" as my initial text and not "hallo" (text in german).
Well, just to answer your question to get the text, do the following:
Add this method to your class:
public static string getBetween(string strSource, string strStart, string strEnd)
{
int Start, End;
if (strSource.Contains(strStart) && strSource.Contains(strEnd))
{
Start = strSource.IndexOf(strStart, 0) + strStart.Length;
End = strSource.IndexOf(strEnd, Start);
return strSource.Substring(Start, End - Start);
}
else
{
return "";
}
}
Change the following in your "TranslateText" method:
//Match m = Regex.Match(result, "(?<=<div id=result_box dir=\"ltr\">)(.*?)(?=</div>)");
string text = getBetween(result, "<span id=result_box class=\"short_text\">", "</span>");
//if (m.Success)
// result = m.Value;
return text;
Now execute your code like this:
// this will return empty ("") if no text found.
// or any problem happens (like lose your internet connection)
string translatedText = TranslateText("hi", "German");
Console.Write(translatedText);
At this point, if you get the translated text from google, it will be retrieved in your app.
Recommendations:
Use a console application and no windows forms, it will be faster.
Warning:
"Google is not a free translating tool. What you do is terms violation".
Hope this helps :-)
Would be easier, and more robust to parse the html using something other than a regex. You can then search the parsed HTML tree for the result and extract it from there.
See What is the best way to parse html in C#?