Thanks for any help in advance :)
Context
I am using SpreadsheetGear within my Windows Application and there are certain cases where a user will want to copy data from an open Excel application and paste the two dimensional grid into the SpreadsheetGear object in my application.
Motivation
I'm attempting to acquire information from the data in the clipboard in preparation for the paste to happen. The numbers of rows and columns of the data coming in needs to be determined before the paste happens so that the SpreadsheetGear control and other controls on the page are "ready" for the data.
Problem 1
How do I acquire such data from the Clipboard? I'm using
System.Windows.Forms.Clipboard.GetData(...)
but I'm not sure whether I should indicate the DataFormat to be CommaSeparatedValue (CSV) or Text. Will one way or the other work best? Is there another DataFormat that I am overlooking that could help me out?
Problem 2
I used this statement in the Immediate Window of Visual Studio 2012:
System.Diagnostics.Debug.WriteLine(Clipboard.GetText())
Interestingly, this returned a portion of the data I selected and copied in Excel. Is there a limit to the amount of data that the clipboard can handle from Excel? Or is there a way for my Windows App to help allocate more space on the clipboard, knowing the user is about the select data from Excel and copy that data to the clipboard?
Please let me know if I can provide more clarification. I'm a little lost and not sure about the scope of this issue. Thanks!
Here is what ended up working for me:
try
{
var data = Clipboard.GetDataObject();
var stream = (System.IO.Stream)data.GetData(DataFormats.CommaSeparatedValue);
var enc = new System.Text.UTF8Encoding();
var reader = new System.IO.StreamReader(stream, enc);
string data_csv = reader.ReadToEnd();
string[] data_csv_array = data_csv.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
...
}
catch (Exception e)
{
ErrorHandling.ShowError("There is no data on the Clipboard to paste.");=
}
The "..." indicates that I do something pretty particular with the data once I have it in array form. What I wanted to do was display the portion of my solution that would help people in general with a similar need.
Related
I am working on automating a process within my business, part of which is sending an email through SalesForce. We don't have access to the SF API and the email has to be sent through salesforce in order to keep the communication searchable for the coworkers.
I need to use a template which can be selected in SalesForce, however this function does not work in IE (which our RPA solution uses) so I need to build this email from scratch.
I see two options for this:
Use the HTML to recreate the format with the right variables. This entails inserting/injecting/manipulating HTML.
Copy the format into memory/the clipboard, edit it programatically and paste it into the SF interface
This question will be about option 2. I have posted an additional question with regards to the first option separately. It can be found here.
Now on to the question: I found that I can copy the text in the template from the SalesForce webpage to the clipboard (using CTRL+C) and then paste it into word, doing so will preserve the formatting and layout of the text as well as included images. I can then copy and paste this back from word to the SF webpage as well preserving the images, format and layout. This means that the formatting and image data is retained in the clipboard.
Now I need to edit the template text before I paste it into the webpage in order to use this as a solution for automation. I have been experimenting with this for the past week but I can't find a way to edit the text while preserving the formatting and layout.
I use C# and know how to GetText and SetText as wel as images (separately) from the clipboard. However the text is then a plain string without any layout and formatting. I want to replace certain keywords in the template with the required variables.
At this point I have the following code to investigate the contents of the clipboard:
// Initialise a DataObject
DataObject t = null;
try
{
// Get Data from clipboard
t = (DataObject)Clipboard.GetDataObject(); // GetData(DataFormats.Html);
// Get formats in the DataObject
string[] tFormats = t.GetFormats();
// For each format that was found create a separate object (I did this manually, I
// inspected the formats earlier by eye.
object objectDescriptor = t.GetData("Object Descriptor", true);
object rtf = t.GetData("Rich Text Format", true);
object HTMLFormat = t.GetData("HTML Format", true);
object sysString = t.GetData("System.String", true);
object unicodeText = t.GetData("UnicodeText", true);
object text = t.GetData("Text", true);
object enhancedMetafile = t.GetData("EnhancedMetafile", true);
object metaFilePict = t.GetData("MetaFilePict", true);
object embedSource = t.GetData("Embed Source", true);
object linkSource = t.GetData("Link Source", true);
object linkSourceDescriptor = t.GetData("Link Source Descriptor", true);
object objectLink = t.GetData("ObjectLink", true);
// Try replacing the text
HTMLFormat = (object)HTMLFormat.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("HTML Format", HTMLFormat);
sysString = (object)sysString.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("System.String", sysString);
unicodeText = (object)unicodeText.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("UnicodeText", unicodeText);
text = (object)text.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("Text", text);
Clipboard.SetDataObject(t);
}
catch (exception ex)
{
// Do exception handling
}
Inspecting all these objects I see that, even though they are apparently included in the DataObject, they are all null, except for HTML Format, System.String, UnicodeText and Text. Within these options the HTML Format data is the only one that appears to have some formatting data in there (I don't know where the included image is stored though). I am able to replace the keyword in that HTML Format succesfully, however if I set it back into the DataObject and then extract the HTML format again nothing has changed. The same goes for the other text items.
I also tried changing the text in the same way for all the text items. This however has the same results. If I then try to paste the clipboard contents into Word it freezes for a little while and then nothing happens.
How can I edit this DataObject? And will that actually translate into a properly formatted and layout-ed text including the image and hyperlinks, or is this approach a dead end?
Cheers!
I am working on Enterprise Architect through C# add-ins. I need to display the image manager through automation where user can add directly add images on an "add image" button click in form.
I use the API Repository.InvokeConstructPicker() but it only opens the select package/class/component window. Is there an EA API available to open the Image Manager.
No, there is none. There is the undocumented Respository.CustomCommand which can open several properties windows. But the image manager is not part of that (or it has not been discovered what parameters to supply).
Please see Edit2 below about adding new values to the table.
Edit: Based on another question I had to dig into this a bit deeper.
I found out that, although EA imports a number of different image formats, it internally uses PNG to store the image. Obviously their BMP-importer does not like all BMP formats (not so deep in that, but I seem to remember there's some 8/16 bit stuff; typical Windoze weirdness). Anyhow, I used this Python code snippet to retrieve some test image data, previously imported into EA:
import sys
import win32com.client
import base64
import xml.etree.ElementTree
eaRep = None
try:
eaApp = win32com.client.GetActiveObject(Class="EA.App")
eaRep = eaApp.repository
except:
print "failure to open EA"
sys.exit(2)
def dump():
sqlRes = eaRep.SQLQuery("SELECT * FROM t_image")
root = xml.etree.ElementTree.fromstring(sqlRes)
for dataset in root:
for data in dataset:
for row in data:
name = row[1].text
print name
data = row[3].text
png = base64.standard_b64decode(data)
file = open("c:/" + name + ".png", "wb")
file.write(png)
file.close()
dump()
This correctly extracted the images from the database.
Edit2: I was assuming that EA stores the png as base64, but that's not true. EA only delivers base64 on return of SQLQuery. But they internally just store the raw png in Image. So, unfortunately, you can not use Repository.Execute since it can not transport binary data - or at least I have not figured out how to do that. As a work around you can look into Repository.ConnectionString and open a native connection to the database. Once you have plugged the new picture(s) in the table you can use them via thier ImageID.
Contents of t_image:
ImageID : You just need to create an unique ID
Name : an arbitrary string
Type : fixed string Bitmap
Image : blob of a png
Here's a Python snippet that connects natively to an EAP file:
import pyodbc
db_file = r'''C:\Documents and Settings\Administrator\Desktop\empty.eap'''
odbc_conn_str = 'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=' + db_file
conn = pyodbc.connect(odbc_conn_str)
cursor = conn.cursor()
cursor.execute("select * from t_image")
row = cursor.fetchone()
if row:
print(row)
Rather than printing the row with the image data (which shows that its contents is a png-blob) you can use it to actually issue an INSERT or UPDATE to modify t_image.
Windows Forms - VSTO - Outlook
Background - I am creating a digital archive add-in for Office where the user can search the database for the client (whom the document belongs to) and it will save the file to the appropriate folder(s) based on the nature of the file. So far this is working for Word as planned but I am now using Outlook which has more to consider (attachments, message body, etc.).
I have got it working so far that the attachments are saved into a temporary folder (which is emptied each time the windows form closes) ready to be sorted and I can obtain information about sender/subject/email body. The list of attachments is set out into a CheckedListBox
Current Problem - When a user is looking to archive an attachment (a lot of documents/scanned documents will come up), images will be confusing as they may be necessary or entirely unimportant so I wish to preview the images.
I am trying to get it so on the event of
private void chkAttachments_SelectedValueChanged(object sender, EventArgs e)
The image shows in picAttachPreview (PictureBox) as a preview of that file. This will be taking the image from tempfolder (#"c:\temp\DigitalArchive").
I understand this is wrong but I am trying to set the source for the image shown on screen on that SelectedValueChanged event.
My [Incorrect] Code -
if(chkAttachments.Text.Contains(".jpg"))
{
var selectedImage = chkAttachments.SelectedValue.ToString();
picAttachPreview.Image = tempfolder + #"\" + selectedImage; //(A)
}
The (A) line is the issue and although I understand why, I don't know how to resolve it. The filepath is constructed with tempfolder and selectedImage (e.g. ScannedDoc.jpg) but the file path type is String but picAttachPreview is System.Drawing.Image so I assume I am looking at the wrong property of picAttachPreview to set the source of the image.
Any help or guidance will be immensely appreciated. Thank you.
(Also if you know of any good way I can set the same nature of preview for documents/PDF then I will be immensely grateful)
Edit Although the link solves part of my problem, there is an issue with chkAttachments.SelectedValue.ToString() which I answered below. (If anyone can advise me on the site etiquette for this situation. Do I delete the question or leave it with the answer I found so that people can find the solution to the same problem in future? Thank you)
After some playing around, I found another problem (chkAttachment.Text works whereas .SelectedValue.ToString() does not.
Also the issue with the string to image format is resolved by prefixing the path with Image.FromFile(
So the correct way of changing the image upon selection is:
if(chkAttachments.Text.Contains(".jpg"))
{
var selectedImage = chkAttachments.Text;
picAttachPreview.Image = Image.FromFile(tempfolder + #"\" + selectedImage);
}
I'm developing a system that takes data input from textboxes, and on a button click, saves these values to the respective listbox ready to be written to a text file once the process is complete.
The next stage has been using this data to create graphs, which has gone successfully but I'm now looking for a way to add these onto the end of my text file so it's all included in one place.
I originally tried it like this (included in the total 'saveToFile' function):
consoleFile.WriteLine(chartBP.Text); //chart title
chartBP.SaveImage((fileName), System.Drawing.Imaging.ImageFormat.Jpeg);
consoleFile.WriteLine("\n\n");
This appeared to work ok but threw a run-time error stating that the file could not be accessed because it was being used by another process.
I don't think I'm far off where I need to be, but I don't have enough experience with charts to know what to try next.
Does anyone have any idea how to make this work, or another method that wouldn't produce the error?
Any help is greatly appreciated!
If your problem is just to get rid of the exception, then you could do this:
consoleFile.WriteLine(chartBP.Text);
consoleFile.Close();
FileStream imgFile = File.Open(filename, FileMode.Append);
chartBP.SaveImage(imgFile, ChartImageFormat.Jpeg);
imgFile.Close();
consoleFile = new StreamWriter(filename, true);
consoleFile.WriteLine("\n\n");
consoleFile.Close();
But remember, you're not going to see any images in your text file, just a messy stream of characters corresponding to the binary image, displayed as text.
I will do my best to explain in detail what I'm trying to achieve.
I'm using C# with IntPtr window handles to perform a CTRL-C copy operation on an external application from my own C# application. I had to do this because there was no way of accessing the text directly using GET_TEXT. I'm then using the text content of that copy within my application. The problem here is that I have now overwritten the clipboard.
What I would like to be able to do is:
Backup the original contents of the clipboard which could have been set by any application other than my own.
Then perform the copy and store the value into my application.
Then restore the original contents of the clipboard so that the user still has access to his/her original clipboard data.
This is the code I have tried so far:
private void GetClipboardText()
{
text = "";
IDataObject backupClipboad = Clipboard.GetDataObject();
KeyboardInput input = new KeyboardInput(this);
input.Copy(dialogHandle); // Performs a CTRL-C (copy) operation
IDataObject clipboard = Clipboard.GetDataObject();
if (clipboard.GetDataPresent(DataFormats.Text))
{
// Retrieves the text from the clipboard
text = clipboard.GetData(DataFormats.Text) as string;
}
if (backupClipboad != null)
{
Clipboard.SetDataObject(backupClipboad, true); // throws exception
}
}
I am using the System.Windows.Clipboard and not the System.Windows.Forms.Clipboard. The reason for this was that when I performed the CTRL-C, the Clipboard class from System.Windows.Forms did not return any data, but the system clipboard did.
I looked into some of the low level user32 calls like OpenClipboard, EmptyClipboard, and CloseClipboard hoping that they would help my do this but so far I keep getting COM exceptions when trying to restore.
I thought perhaps this had to do with the OpenClipboard parameter which is expecting an IntPtr window handle of the application which wants to take control of the clipboard. Since I mentioned that my application does not have a GUI this is a challenge. I wasn't sure what to pass here. Maybe someone can shed some light on that?
Am I using the Clipboard class incorrectly? Is there a clear way to obtain the IntPtr window handle of an application with no GUI? Does anyone know of a better way to backup and restore the system clipboard?
It's folly to try to do this. You cannot faithfully restore the clipboard to its prior state. There could be dozens of unrendered data formats present using "delayed rendering", and if you attempt to render them all, you'll cause the source app to run out of resources. It's like walking into a resturaunt and saying "give me one of everything".
Suppose that the user has selected 500 rows x 100 columns in Excel, and has copied that to the clipboard. Excel "advertises" that it can produce this data in about 25 different formats, including Bitmap. Once you paste it as a Bitmap, you force Excel to render it as a bitmap. That's 50000 cells, and would be a bitmap approx 10,000 x 15,000 pixels. And you expect the user to wait around while Excel coughs that up, along with 24 other formats? Not feasible.
Furthermore, you're going to be triggering WM_DrawClipboard events, which will impact other clipboard viewers.
Give up.
You could save the content of the clipboard in a dictionary, and restore it afterwards :
public IDictionary<string, object> GetClipboardData()
{
var dict = new Dictionary<string, object>();
var dataObject = Clipboard.GetDataObject();
foreach(var format in dataObject.GetFormats())
{
dict.Add(format, dataObject.GetData(format));
}
return dict;
}
public void SetClipboardData(IDictionary<string, object> dict)
{
var dataObject = Clipboard.GetDataObject();
foreach(var kvp in dict)
{
dataObject.SetData(kvp.Key, kvp.Value);
}
}
...
var backup = GetClipboardData();
// Do something with the clipboard...
...
SetClipboardData(backup);