I have imported a file "x.ani" into Resource file Resources.resx. Now trying to load that file using ResourceManager.GetObject("aero_busy.ani")
Cursor.Current = (Cursor)ResourcesX.GetObject("aero_busy.ani");
but it didn't work .. (certainly) :)
How can I change the current Cursor using resource object?
I do it by adding the cursor .cur file into the Resources part of the project (I am using Visual Studio). I'm not sure if it would have to be .cur, so long as the development program can load it.
Having done that in the variables declaration part of my code I create a MemoryStream from the cursor file:
private static System.IO.MemoryStream cursorMemoryStream = new System.IO.MemoryStream(myCurrentProject.Properties.Resources.myCursorFile);
...and then you can create the cursor from the MemoryStream:
private Cursor newCursor = new Cursor(cursorMemoryStream);
You can then assign the cursor as you like within the program, e.g.
pictureBox1.Cursor = newCursor;
and the new cursor is compiled as part of the program.
I haven't found any better way than dumping to a temp file and use the Win32 load cursor from file method. The hack goes something like this (I removed a big chunk of boilerplate code for clarity, in which a temp file is written with the data from the stream). Also, all exception handling etc. was removed.
[DllImport("User32.dll", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern IntPtr LoadCursorFromFile(String str);
public static Cursor LoadCursorFromResource(string resourceName)
{
Stream cursorStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
// Write a temp file here with the data in cursorStream
Cursor result = new Cursor(LoadCursorFromFile(tempFile));
File.Delete(tempFile);
return result.
}
You would use this as (remember namespaces when loading embedded resources).
Cursors.Current = LoadCursorFromResource("My.Namespace.Filename");
after a few turns to the issue, I find the elegant solution is:
internal static Cursor GetCursor(string cursorName)
{
var buffer = Properties.Resources.ResourceManager.GetObject(cursorName) as byte[];
using (var m = new MemoryStream(buffer))
{
return new Cursor(m);
}
}
I think the issue has to do with the fact that the cursor must have .cur extension in order to be used as Cursor.
// The following generates a cursor from an embedded resource.
// To add a custom cursor, create or use an existing 16x16 bitmap
// 1. Add a new cursor file to your project:
// File->Add New Item->Local Project Items->Cursor File
// 2. Select 16x16 image type:
// Image->Current Icon Image Types->16x16
The above was taken from MSDN.
Update : Found the answer why.
"Note Note
The Cursor class does not support animated cursors (.ani files) or cursors with colors other than black and white."
Found Here
Writing a file to disk then importing it as a cursor is impractical, there is simpler solution. Convert your .cur to .ico and import your cursor as an icon resource. Then you can simply do:
Cursor c = new Cursor(Properties.Resources.mycursor.Handle);
This will support 32 bit colors properly.
[DllImport("User32.dll")]
private static extern IntPtr LoadCursorFromFile(string str);
Cursor SetCursor(byte[] resourceName)
{
string tempPath = #"C:\Users\Public\Documents\temp.cur";
File.WriteAllBytes(tempPath, resourceName);
Cursor result = new Cursor(LoadCursorFromFile(tempPath));
File.Delete(tempPath);
return result;
}
Pulling it all together...
This is a combination of the strongly typed resources Visual Studio now provides, plus Win32 LoadCursorFromFile (via Anders original answer using manifest resource loads).
I also threw in a cache of instantiated cursors, because that's appropriate for my app. Nuke that if you don't need it.
namespace Draw
{
/// <summary>
/// Controls use of all the cursors in the app, supports loading from strongly typed resources, and caches all references for the lifetime of the app.
/// </summary>
public static class Cursors
{
// Cache of already loaded cursors
private static ConcurrentDictionary<byte[], Cursor> cache = new ConcurrentDictionary<byte[], Cursor>();
/// <summary>
/// Returns a cursor given the appropriate id from Resources.Designer.cs (auto-generated from Resources.resx). All cursors are
/// cached, so do not Dispose of the cursor returned from this function.
/// </summary>
/// <param name="cursorResource">The resource of the cursor to load. e.g. Properties.Resources.MyCursor (or byte array from .cur or .ani file)</param>
/// <returns>The cursor. Do not Dispose this returned cursor as it is cached for the app's lifetime.</returns>
public static Cursor GetCursor(byte[] cursorResource)
{
// Have we already loaded this cursor? Use that.
if (cache.TryGetValue(cursorResource, out Cursor cursor))
return cursor;
// Get a temporary file
var tmpFile = Utils.GetTempFile();
try
{
// Write the cursor resource to temp file
File.WriteAllBytes(tmpFile, cursorResource);
// Read back in from temp file as a cursor. Unlike Cursor(MemoryStream(byte[])) constructor,
// the Cursor(Int32 handle) version deals correctly with all cursor types.
cursor = new Cursor(Win32.LoadCursorFromFile(tmpFile));
// Update dictionary and return
cache.AddOrUpdate(cursorResource, cursor, (key, old) => cursor);
return cursor;
}
finally
{
// Remove the temp file
Utils.TryDeleteFile(tmpFile);
}
}
}
}
Example call:
Cursor = Draw.Cursors.GetCursor(Properties.Resources.Cursor_ArrowBoundary);
Related
I have an application that produces SVG diagrams, we would like to create an export to Visio option that would, ideally, behind the scenes (via server-side C#) take an SVG file and convert it directly to an MS Visio .vsdx file that the user can download and then ofcourse open and edit in Visio seamlessly (as opposed to having to download the SVG file, and then importing it themselves into Visio). In effect, trying to replicate the "Open SVG" functionality that the Visio GUI supplies in backend C#.
I have seen a lot of answers on going from Visio TO SVG, but this is the opposite.
I also know that .vsdx is simply a zip file, but with that extension. Inside are a bunch of visio specific files and folders - if anyone knows what is the bare minimum required of these files/folders and their contents and where the SVG fits in, perhaps that's one way to do it.
Any guidance would be much appreciated.
Dug this up, credit/source: https://programmer.group/5c650f3227420.html
SVG can be converted to Visio's vsd format. The method is very simple. The main method is to open the SVG file and save it as a vsd file. The invocation method is as follows:
/// <summary>
/// svg turn vsd
/// </summary>
/// <param name="svgFn">svn file name</param>
/// <param name="desVsdFn">Preserved vsd file name</param>
private static void Svg2Vsd(string svgFn, string desVsdFn)
{
var app = ComObj.Create("Visio.Application");
app["Visible"] = new ComObj(false);
var docs = app["Documents"];
short visOpenHidden = 64, visOpenRO = 2;
var doc = docs.Call("OpenEx", svgFn, visOpenHidden + visOpenRO);
doc.Call("SaveAs", desVsdFn);
doc.Call("Close");
var win = app["Window"];
app.Call("Quit");
}
Here I use a ComObj class I wrote myself. Its purpose is to make it easy to invoke Com components such as Office by reflection, and to make the code concise when invoking.
Why use reflection to invoke dynamically instead of directly referencing Com components? The main purpose is to reduce the dependence and coupling of program code to COM components, so as to facilitate the compilation and distribution of code deployment. Dynamic invocation can be compiled and run without adding component references. If the Com component is not installed on the server, you can also give an intuitive prompt instead of a program error.
The code for this class is as follows:
using System;
using System.Reflection;
namespace HZ.Common
{
/// <summary>
/// For convenience Com Object attributes, method calls
/// </summary>
public class ComObj
{
public static ComObj Create(string progId)
{
var type = Type.GetTypeFromProgID(progId);
if (type == null)
{
throw new Exception("Servers need to be installed" + progId + "To use this feature");
}
return new ComObj(Activator.CreateInstance(type));
}
private object _val;
/// <summary>
/// Actual value
/// </summary>
public object Val
{
get { return _val; }
}
public ComObj(object comObject)
{
_val = comObject;
}
public ComObj Call(string mehtod, params object[] args)
{
if (_val == null)
return null;
var ret = _val.GetType().InvokeMember(mehtod, BindingFlags.InvokeMethod, null, _val, args);
return new ComObj(ret);
}
public ComObj this[string property]
{
get
{
if (_val == null)
return null;
var ret = _val.GetType().InvokeMember(property, BindingFlags.GetProperty, null, _val, null);
return new ComObj(ret);
}
set
{
if (_val != null)
_val.GetType().InvokeMember(property, BindingFlags.SetProperty, null, _val, new object[] { value.Val });
}
}
}
}
i have an folder, in which i receive .csv files for every half hour with time stamps. Now, i need to take the latest file from the available files and import it into sql server.
For Example
in my source folder, i have
test_01112012_120122.csv
test_01112012_123022.csv
test_01112012_123555.csv
now i need to fetch the latest file and import that file into sql server with the help of SSIS.
Thanks
satish
The code from #garry Vass, or one like it, is going to be needed even if you're using SSIS as your import tool.
Within SSIS, you will need to update the connection string to your flat file connection manager to point to the new file. Ergo, you need to determine what is the most recent file.
Finding the most recent file
Whether you do it by file attributes (Garry's code) or slicing and dicing of file names is going to be dependent upon what your business rules are. Is it always the most recently modified file (attribute) or does it need to be based off the file name being interpreted as a sequence. This matters if the test_01112012_120122.csv had a mistake in it and the contents are updated. The modified date will change but the file name will not and those changes wouldn't get ported back into the database.
I would suggest you create 2 variables of type String and scoped to the package named RootFolder and CurrentFile. Optionally, you can create one called FileMask if you are restricting to a particular type like *.csv. RootFolder would be the base folder you expect to find files in C:\ssisdata\MyProject. CurrentFile will be assigned a value from a script of the fully qualified path to the most recently modified file. I find it helpful at this point to assign a design-time value to CurrentFile, usually to the oldest file in the collection.
Drag a Script Task onto the Control Flow and set as your ReadOnlyVariable User::RootFolder (optionally User::FileMask). Your ReadWriteVariable would be User::CurrentFile.
This script would go inside the public partial class ScriptMain: ... braces
/// <summary>
/// This verbose script identifies the most recently modified file of type fileMask
/// living in RootFolder and assigns that to a DTS level variable.
/// </summary>
public void Main()
{
string fileMask = "*.csv";
string mostRecentFile = string.Empty;
string rootFolder = string.Empty;
// Assign values from the DTS variables collection.
// This is case sensitive. User:: is not required
// but you must convert it from the Object type to a strong type
rootFolder = Dts.Variables["User::RootFolder"].Value.ToString();
// Repeat the above pattern to assign a value to fileMask if you wish
// to make it a more flexible approach
// Determine the most recent file, this could be null
System.IO.FileInfo candidate = ScriptMain.GetLatestFile(rootFolder, fileMask);
if (candidate != null)
{
mostRecentFile = candidate.FullName;
}
// Push the results back onto the variable
Dts.Variables["CurrentFile"].Value = mostRecentFile;
Dts.TaskResult = (int)ScriptResults.Success;
}
/// <summary>
/// Find the most recent file matching a pattern
/// </summary>
/// <param name="directoryName">Folder to begin searching in</param>
/// <param name="fileExtension">Extension to search, e.g. *.csv</param>
/// <returns></returns>
private static System.IO.FileInfo GetLatestFile(string directoryName, string fileExtension)
{
System.IO.DirectoryInfo directoryInfo = new System.IO.DirectoryInfo(directoryName);
System.IO.FileInfo mostRecent = null;
// Change the SearchOption to AllDirectories if you need to search subfolders
System.IO.FileInfo[] legacyArray = directoryInfo.GetFiles(fileExtension, System.IO.SearchOption.TopDirectoryOnly);
foreach (System.IO.FileInfo current in legacyArray)
{
if (mostRecent == null)
{
mostRecent = current;
}
if (current.LastWriteTimeUtc >= mostRecent.LastWriteTimeUtc)
{
mostRecent = current;
}
}
return mostRecent;
// To make the below code work, you'd need to edit the properties of the project
// change the TargetFramework to probably 3.5 or 4. Not sure
// Current error is the OrderByDescending doesn't exist for 2.0 framework
//return directoryInfo.GetFiles(fileExtension)
// .OrderByDescending(q => q.LastWriteTimeUtc)
// .FirstOrDefault();
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
Updating a Connection Manager
At this point, our script has assigned a value to the CurrentFile variable. The next step is to tell SSIS we need to use that file. In your Connection Manager for your CSV, you will need to set an Expression (F4 or right click and select Properties) for the ConnectionString. The value you want to assign is our CurrentFile variable and the way that's expressed is #[User::CurrentFile]
Finally, these screen shots are based on the upcoming release of SQL Server 2012 so the icons may appear different but the functionality remains the same.
Assuming that you wanted to use C#, to get the newest file in a given directory, you can use a method like this...
private static FileInfo GetLatestFile(string directoryName, string fileExtension)
{
DirectoryInfo directoryInfo = new DirectoryInfo(directoryName);
return directoryInfo.GetFiles(fileExtension)
.OrderByDescending(q => q.LastWriteTimeUtc)
.FirstOrDefault();
}
This method is called like...
FileInfo file = GetLatestFile( "C:\myDirectory", "*.csv");
And it returns a FileInfo instance (or null) of the file with the most recent write time. You can then use the FileInfo instance to get the name of the file and so on for your processing...
I'm searching for a way to make one resource manager that holds all the data form multiple resource files. Is this even possible? It would be useful for me if yes because I've got like 10+ resources files with translations. I want to make one wrapper class for this and make one resource manager so if I e.g. use rm.GetString("string"); I get this value from one of resource files. I probably think that this is not the best idea but... if you have any good ideas please share here!
I'm trying the following code:
var rm = new ResourceManager("ProjectNameSpace.ResourceName",
Assembly.GetExecutingAssembly());
By doing this I only load resources from file that I specified by: ProjectNameSpace.ResourceName, am I right?
Is there any nice workaround for this or different approach?
This is not exactly what you are asking about, but maybe it will help. This is some cut-down copy-and-paste code from a program I use that reads multiple resource files and creates a combined Dictionary of all of the icons in the resource files.
class Program
{
static void Main(string[] args)
{
IconManager.FindIconsInResources(Resources1.ResourceManager);
//IconManager.FindIconsInResources(Resources2.ResourceManager);
//IconManager.FindIconsInResources(Resources3.ResourceManager);
Image iconImage = IconManager.GetIcon("Incors_office_building_16x16");
}
}
public static class IconManager
{
private static readonly Dictionary<string, ResourceSet> _iconDictionary =
new Dictionary<string, ResourceSet>();
/// <summary>
/// Method to read the resources info for an assembly and find all of the icons and add them
/// to the icon collection.
/// </summary>
public static void FindIconsInResources(ResourceManager resourceManager)
{
// Get access to the resources (culture shouldn't matter, but has to be specified)
ResourceSet resourceSet =
resourceManager.GetResourceSet(CultureInfo.GetCultureInfo("en-us"), true, true);
if (resourceSet == null)
throw new Exception("Unable to create ResourceSet.");
// Top of loop to examine each resource object
foreach (DictionaryEntry dictionaryEntry in resourceSet)
{
// Check it's an icon (or some kind of graphic)
if (!(dictionaryEntry.Value is Bitmap))
continue;
// Get the resource name, which is basically the filename without ".png" and with '-'
// and blanks converted to '_'. Ignore .ico files.
string resourceKey = (string)dictionaryEntry.Key;
if (resourceKey.EndsWith("_ico", StringComparison.Ordinal))
continue;
// Add (or replace) the icon in the icon dictionary
_iconDictionary[resourceKey] = resourceSet;
}
}
/// <summary>
/// Method to get an icon image from one of several resource files.
/// </summary>
public static Image GetIcon(string iconName)
{
ResourceSet resourceSet;
_iconDictionary.TryGetValue(iconName, out resourceSet);
if (resourceSet == null)
return null;
return (Image)resourceSet.GetObject(iconName);
}
}
I'm trying to establish access to an embedded SQL resource file I've created in a Class Library. However, I'm not sure where to go from here.
I've accessed the resource using:
Assembly.GetExcecutingAssembly().GetManifestResourceStream("InsertTest.sql");
My understanding is that there is a way to access them in a strongly typed fashion, but I can't seem to get a handle on the project or the solution to browse through their respective properties or resources programatically.
What am I missing?
Although I did get some great suggestions (see Philip Daniels' answer - good stuff), none of them really addressed my specific concerns. However, I found that the easiest way to accomplish this was to do the following:
Right click your project and select 'Properties'
Select the 'Resources' tab. Create a new resources file if necessary.
In the upper left hand corner there is a drop down that defaults to 'Strings'. Click this box and choose 'Files'.
Drag and drop the resource file you'd like to embed in the project.
You can now access a strongly typed resource using the following syntax:
Project.Properties.Resources.ResourceName;
In my situation, this worked perfectly as I am storing inline SQL in these files and it returns the sql embedded in the file. Keep in mind, however, that by defaults these resources are linked and not embedded, but you can change their property to set them to embedded.
Hope this helps someone!
You're almost there. I have a couple of functions I use for this. You can do somehting very similar for images. I'm not sure it's worth creating properties like you want (you can do that through the Resources tab of the project properties if you insist).
/// <summary>
/// Gets an open stream on the specified embedded resource. It is the
/// caller's responsibility to call Dispose() on the stream.
/// The filename is of the format "folder.folder.filename.ext"
/// and is case sensitive.
/// </summary>
/// <param name="assembly">The assembly from which to retrieve the Stream.</param>
/// <param name="filename">Filename whose contents you want.</param>
/// <returns>Stream object.</returns>
public static Stream GetStream(Assembly assembly, string filename)
{
string name = String.Concat(assembly.GetName().Name, ".", filename);
Stream s = assembly.GetManifestResourceStream(name);
return s;
}
/// <summary>
/// Get the contents of an embedded file as a string.
/// The filename is of the format "folder.folder.filename.ext"
/// and is case sensitive.
/// </summary>
/// <param name="assembly">The assembly from which to retrieve the file.</param>
/// <param name="filename">Filename whose contents you want.</param>
/// <returns>String object.</returns>
public static string GetFileAsString(Assembly assembly, string filename)
{
using (Stream s = GetStream(assembly, filename))
using (StreamReader sr = new StreamReader(s))
{
string fileContents = sr.ReadToEnd();
return fileContents;
}
}
On a resource file you won't be able to have intellisense to build your sql script compare to have them as separate files in your project. You can create a helper class to access them in a strong type fashion:
public class Scripts
{
public static string Sql1
{
get
{
return GetResource("sql1.sql");
}
}
public static string Sql2
{
get
{
return GetResource("sql2.sql");
}
}
private static string GetResource(string name)
{
var assembly = Assembly.GetExecutingAssembly();
using(var stream = new StreamReader(assembly.GetManifestResourceStream("Myproject.Sql." + name)))
{
return stream.ReadToEnd();
}
}
}
For example, in Dapper, you can access your scripts like this:
using(var db = new SqlConnection("yourconnectionstring")){
db.Open();
var results = db.Query(Scripts.Sql1);
}
I have a program that uses clipboard but I want to restore the clipboard to its former state after I am done with it.
This is my code :
IDataObject temp = Clipboard.GetDataObject();
//Some stuff that change Cliboard here
Clipboard.SetText("Hello");
//Some stuff that change Cliboard here
Clipboard.SetDataObject(temp);
But it if I copy a text, and run this code, I get nothing on notepad.
NOTE : I can't use Clipboard.Contains because I want to preserve the Clipboard EXACLY how it was before, even if the user copied a file.
I cannot confirm whether this will work, but I see no reason why you shouldn't be able to back up the data using the longer approach of actually reading the data and restoring it afterwards.
Read here: http://msdn.microsoft.com/en-us/library/system.windows.forms.idataobject.aspx
You would do something like (pseudo-code)
//Backup
var lBackup = new Dictionary<string, object>();
var lDataObject = Clipboard.GetDataObject();
var lFormats = lDataObject.GetFormats(false);
foreach(var lFormat in lFormats)
{
lBackup.Add(lFormat, lDataObject.GetData(lFormat, false));
}
//Set test data
Clipboard.SetText("asd");
//Would be interesting to check the contents of lDataObject here
//Restore data
foreach(var lFormat in lFormats)
{
lDataObject.SetData(lBackup[lFormat]);
}
//This might be unnecessary
Clipboard.SetDataObject(lDataObject);
Is your application exiting after resetting the clipboard?
Assuming it is a Win Form app. (not sure how it works in wpf though)
You could use one of the other overloaded version of Clipboard.SetDataObject
public static void SetDataObject(object data, bool copy)
which preserves the data even after your app exits.
ex: in your case after removing the text content you could call Clipboard.SetDataObject(iDataObject, true);
EDIT:2
I Could source step through Clipboard.cs .NET Frameword 4 / VS 2010.
Download the .NET Framework 4 from here http://referencesource.microsoft.com/netframework.aspx.
Follow the below steps and if it asks for the source (Clipboard.cs) it would be in the Source sub-dir of the installation dir.
EDIT:1
Not sure why the same code doesn't work.
Cannot be a security/permission issue as the code doesn't throw an exception as you say.
There is another approach - source stepping into Framework code - Clipboard.cs
Based on the VS version and .NET framework it may vary ( I couldn't get the source stepping work for .NET 4 as the info is that the symbols with source support haven't yet been released). I'm trying my luck by downloading it manually from here (.NET Version 4)
If you are running VS 2008 and older version of .NET then the below steps should work for you.
More details are here. For .NET Framework 4 - here
This cannot be done. You cannot backup/restore the clipboard without causing unintended consequences.
Please see my post on a similar question. My answer is the one that starts with "It's folly to try to do this".
How do I backup and restore the system clipboard in C#?
Furthermore, I suspect that your motivation for wanting to backup/restore the clipboard is because you want to use it as a crutch to move data, without the user's knowledge or consent. Please read:
http://www.clipboardextender.com/developing-clipboard-aware-programs-for-windows/common-general-clipboard-mistakes
and
http://www.flounder.com/badprogram.htm#clipboard
Lastly, please read and understand this quote:
“Programs should not transfer data into our out of the clipboard without an explicit instruction from the user.” — Charles Petzold, Programming Windows 3.1, Microsoft Press, 1992
I tested the pseudocode from Lukas and found out doesn't work always, this works in all my tests:
// Backup clipboard
lBackup = new Dictionary<string, object>();
lDataObject = Clipboard.GetDataObject();
lFormats = lDataObject.GetFormats(false);
foreach (var lFormat in lFormats)
{
lBackup.Add(lFormat, lDataObject.GetData(lFormat, false));
}
//Set test data
Clipboard.SetText("asd");
//Restore clipboard
lDataObject = new DataObject();
foreach (var lFormat in lFormats)
{
lDataObject.SetData(lFormat, lBackup[lFormat]);
}
//This might be unnecessary
Clipboard.SetDataObject(lDataObject);
I have had success with this.
...to a certain degree.
Where I am currently falling down is trying to copy and restore Bitmaps of varying size.
I can successfully copy and restore a Bitmap of smallish size.
I then tried to do the same for (as the fore-warning Chris Thornton suggested) a gargantuan Excel worksheet with both thousands of cell data, as well as two sets of data on a graph, lying on the same worksheet.
I have found that the data copies and restores without problem. Where it falls down in this instance is allowing the 2-set graph with the worksheet copy.
If any of you have had a problem in copying and restoring Bitmaps, let me suggest what worked for me: when attempting to restore the Clipboard, iterate through the list of formats in reverse order and set each data object that way. (i.e. It seems that a Clipboard must be set in reverse order that it was copied in)
Regarding the case of the gargantuan Excel worksheet and accompanying graph, I also hit another stumbling block: I could not successfully copy the data object whose format was "Meta Data File". That could be the reason why Copy/Restore doesn't work in this case.
I got this far about two weeks ago, and tabled it for more pressing issues.
I wanted to put this out there to let anyone else trying to do the same that it seems like it can be done. (anything can be done in computer science. anything.)
I compiled this code and it seems to work for me. I am persisting via converting to and from json. (Note. It will not do steams so adapt if you need it to)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Clipboard
{
class Program
{
static void Main(string[] args)
{
Execute(() =>
{
var backup = Backup();
System.Windows.Forms.Clipboard.SetText("text"); //just to change clipboard
Restore(backup);
});
}
private static void Execute(Action action)
{
var thread = new Thread(() => action());
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
private static List<ClipboardItem> Backup()
{
var backup = new List<ClipboardItem>();
var data = System.Windows.Forms.Clipboard.GetDataObject();
System.Windows.Forms.Clipboard.SetDataObject(data, copy: true); //This seems to be needed to be able to serialize data later.
data = System.Windows.Forms.Clipboard.GetDataObject();
var formats = data.GetFormats(false).ToList();
formats.ForEach(f =>
{
if (data.GetData(f, false) != null && !(data.GetData(f, false) is Stream))
{
backup.Add(new ClipboardItem()
{
Format = f,
ObjectType = data.GetData(f, false).GetType(),
ObjectJson = JsonConvert.SerializeObject(data.GetData(f, false))
});
}
});
return backup;
}
private static void Restore(List<ClipboardItem> backup)
{
var data = new System.Windows.Forms.DataObject();
backup.ForEach(item =>
{
data.SetData(item.Format, JsonConvert.DeserializeObject(item.ObjectJson, item.ObjectType));
});
System.Windows.Forms.Clipboard.SetDataObject(data, copy: true);
}
}
public class ClipboardItem
{
public string Format { get; set; }
public Type ObjectType { get; set; }
public string ObjectJson { get; set; }
}
}