I am using a Blazor Server application that have a screencasting feature. My goal is to get the data from clipboard and I am talking about not only a text which is easy but overall some img, files, texts etc.
I have a service named ClipboardService and it using Microsoft.JSInterop, I know for a fact that you can do something like this to get a text
public ValueTask<string> ReadTextAsync() {
return _jsRuntime.InvokeAsync<string>("navigator.clipboard.readText");
}
and you can use something like navigator.clipboard.read but it returns I suppose an empty array for my case. Tried to copy an image and sent it to viewer from blazor app and it just doesn't work how I expect it to work.
Clipboard service:
using Microsoft.JSInterop;
public sealed class ClipboardService
{
private readonly IJSRuntime _jsRuntime;
public ClipboardService(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public ValueTask<string> ReadTextAsync()
{
return _jsRuntime.InvokeAsync<string>("navigator.clipboard.readText");
}
public ValueTask WriteTextAsync(string text)
{
return _jsRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text);
}
}
Does anyone faced something familiar?
For managing textual content via Clipboard API you should use readText / writeText, but for images it should be read / write.
For for textual content you can use the service like in Gérald Barré example:
#page "/"
#inject ClipboardService ClipboardService
<h1>Demo Clipboard!</h1>
<button #onclick="ReadFromClipboard">Read from clipboard</button>
<button #onclick="CopyToClipboard">Copy to clipboard</button>
#text
#code {
string text;
async Task ReadFromClipboard()
{
// Reading from the clipboard may be denied, so you must handle the exception
try
{
text = await ClipboardService.ReadTextAsync();
}
catch
{
Console.WriteLine("Cannot read from clipboard");
}
}
async Task CopyToClipboard()
{
// Writing to the clipboard may be denied, so you must handle the exception
try
{
await ClipboardService.WriteTextAsync("https://www.meziantou.net");
}
catch
{
Console.WriteLine("Cannot write text to clipboard");
}
}
}
You can handle read/write operations for images similar way - e.g. via DOM canvas.toBlob. As a starting point, see post by Christian Liebel how to interact with Clipboard API to copy and paste images to/from clipboard.
Additional Links:
For mixed content I would recommend to have a closer look at how to handle ClipboardItem in this post: Unblocking clipboard access by Jason Miller and Thomas Steiner
Read nice overview of APIs - Clipboard API and deprecated Document.execCommand - for interacting with the clipboard in Copy to Clipboard in Blazor post by Chris Sainty.
Related
I'm facing an issue with uploading a file via the Drag'n Drop API.
Here the following blazor component:
<InputTextArea
#ondrop="HandleDrop"
#ondragenter="HandleDragEnter"
#ondragleave="HandleDragLeave"/>
</InputTextArea>
#code {
private async Task HandleDrop(DragEventArgs args)
{
var files = args.DataTransfer.Files;
// Do something to upload the file and get the content
}
I want to upload the file and display it in the textarea. Now since .NET6 the DragEventArgs will list all files (if any) associated with the Drag'n Drop event.
Natively there seems to be no way to get the content of those files.
Therefore I tried to achieve this with JavaScript interop:
private async Task HandleDrop(DragEventArgs args)
{
var content = await jsRuntime.InvokeAsync<string>("getContentFromFile", args);
}
With the following JavaScript function (which is referenced in the _Layout.cshtml):
async function getContentFromFile(args) {
// Use some API here? File Upload API? Didn't work as args is only a data object and not the "real" DragEventArgs from JavaScript
// I also tried FileReader
const fileName = args.Files[0]; // Let's assume we always have one file
let content = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.onload = (e) => resolve(fileReader.result);
fileReader.readAsText(fileName);
});
console.log(content);
return content;
}
This approach let to other issues and exceptions, like that the FileReader threw:
parameter is not of type 'Blob'
Is this with this approach with the current version of blazor (.net6) possible at all? Any tips are welcome.
For a project I am currently working on I'm required to use the clipboard to a certain extent.
What I need:
Save text and some additional application specific data to the clipboard. The text is supposed to be usable with CTRL + V within other applications while the application data should usually be omitted as it is mostly used for referencing stuff (like quotes and so on)
What I tried:
Copying custom object to clipboard and overwriting the ToString-Method, which was a little naive to think it would work
[Serializable]
public class TestData {
public string txt;
public string additionalStuffs;
public override string ToString() {
return txt;
}
}
Clipboard.SetData( "TestData", new TestData() { txt = "This is a text", additionalStuffs = "Stuffs" } );
I would now need the txt to be pastable into other applications as a string while the other data is ignored unless posted in my application. For the sake of being readable and easy to use for the user.
Can any of you explain how I need to approach this problem? Is there even a way to do that?
Okay, a little more trial and error using the documentation and I actually found a solution.
For everyone having the same problem: the trick is using a DataObject like the following:
[Serializable]
public class TestData {
public string Whatever;
}
IDataObject dataObject = new DataObject();
dataObject.SetData( "System.String", "Test" );
dataObject.SetData( "Text", "Test" );
dataObject.SetData( "UnicodeText", "Test" );
dataObject.SetData( "OEMText", "Test" );
dataObject.SetData( "TestData", new TestData() { Whatever = "NONONONONO", } );
Clipboard.SetDataObject( dataObject );
Using this construct you can set a Text using multiple "DataTypes" so whatever the application you want to paste to requires you have a value supplied. This way only the text shows up when pasting but hidden inside is also the additional data.
Sorry for putting this question up without researching to the end. Have a great day!
I'm making an educational game (Windows 10 UWP, C# + XAML) and I need to store user information (in particular, their current score) and retrieve it when they start the app again. I've found a way to do this (see code below) but I have no idea if this is a normal solution to this problem. I'm currently creating a txt file and storing and retrieving data in/from it. Are there more common, or simpler ways to do this?
Here's what I'm currently doing:
Create the file:
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.CreateFileAsync("nameOfTextFile.txt", CreationCollisionOption.OpenIfExists); //other options are ReplaceExisting
Open the file:
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.GetFileAsync("nameOfTextFile.txt");
Write text to the file:
await FileIO.WriteTextAsync(sampleFile, "Put the added text here");
Read text from the file:
string someVariableName = await FileIO.ReadTextAsync(sampleFile);
-Thanks in advance for any help!!
While the file-based approach is valid, there are easier ways, at least for simple data: You can use roaming (or local) settings. Roaming settings are roamed between devices, as long as their size don't exceed 64K, and would carry the score from the user's desktop to the user's phone, for example. Local settings stay on the machine.
Settings are easy to use:
IPropertySet propertySet = ApplicationData.Current.RoamingSettings.Values;
// Get previous score (or 0 if none)
int score = (int)(propertySet["Score"] ?? 0);
// ...play game...
// Set updated score:
propertySet["Score"] = score;
The way I go about doing projects and settings like this is creating a propery setting in Visual Studio, then Setting and Getting the setting / Value.
You can access this by going to the application properties.
This allows access to read,write, and save information / onload restore information.
Some Informational Links:
https://msdn.microsoft.com/en-us/library/bb397755(v=vs.110).aspx
and (Suggested)
https://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
OK, so here goes an example of using a class to store your settings in.
There are many, many more ways you could do this. Too many to list.
Create a settings class:
public class YourSettingsClass
{
public string UserFirstName { get; set; }
public string UserLastName { get; set; }
public string UserScore { get; set; }
}
Create an AppSettings helper
public AppSettings
{
private static YourSettingsClass _settings = new YourSettingsClass();
public static string UserFirstName
{
get { return _settings.UserFirstName; }
set { _settings.UserFirstName = value; }
}
public static string UserLastName
{
get { return _settings.UserLastName; }
set { _settings.UserLastName = value; }
}
public static string UserScore
{
get { return _settings.UserScore; }
set { _settings.UserScore = value; }
}
public static void SaveSettings()
{
// Now, use your "settingsfile.xml" (or whatever you're saving as)
// to write your settings to from your _settings static field object.
// I'll let you have a play as to how you want to do this...
}
public static void LoadSettings()
{
YourSettingsClass tempSettingsClass = new YourSettingsClass();
// Now, use your "settingsfile.xml" (or whatever you've saved it as)
// to load in your settings and assign to your tempSettingsClass variable.
// I'll let you have a play as to how you want to do this...
// Assign the settings from your loaded object.
_settings = tempSettingsClass;
}
}
Now, from any other class, you can call AppSettings.LoadSettings(). You could do this on App Startup, or on-demand.
When you've loaded the settings in, just reference AppSettings.UserFirstName or whatever property you want to either get the value or set the value.
When you're ready to, you can then save the settings back to the XML file on disk, through AppSettings.SaveSettings().
I've purposely omitted the code for loading and saving from the storage, and for se/deserializing class objects as I haven't got any UWP components on this PC and I've done this all from memory so I don't want to put anything in to throw you off.
Plus it's a little more learning (even trial/error) for you to do.
Lastly
In the getters for your AppSettings static properties you could also do a null or string.IsNullOrWhiteSpace check for the _settings' property in question, and call the LoadSettings() method if so.
This would save you having to manually call it in-code elsewhere.
Useful links
XmlSerializer and how to use the Serialize method
All about what you can do with the FileIO.WriteTextAsync
Not an article, but a similar question: UWP C# Read & Write XML File
I really hope this helps, somewhat.
Good luck!
I am trying the multi language features in an application. I have created the resource files GlobalTexts.en-EN.resx GlobalTexts.fr-FR.resx and a class that sets the culture and returns the texts like (I will not go in all the details, just show the structure):
public class Multilanguage
{
...
_res_man_global = new ResourceManager("GlobalResources.Resources.GlobalTexts", System.Reflection.Assembly.GetExecutingAssembly());
...
public virtual string GetText(string _key)
{
return = _res_man_global.GetString(_key, _culture);
}
}
...
Multilanguage _translations = new Multilanguage();
...
someText = _translations.GetText(_someKey);
...
This works just fine.
Now, I would like to use this application in another solution that basically extends it (more windows etc.) which also has resource files ExtendedTexts.en-En.resx ExtendedTexts.fr-FR.resx and a new class like:
public class ExtendedMultilanguage : Multilanguage
{
...
_res_man_local = new ResourceManager("ExtendedResources.Resources.ExtendedTexts", System.Reflection.Assembly.GetExecutingAssembly());
...
public override string GetText(string _key)
{
string _result;
try
{
_result = _res_man_local.GetString(_key, _culture);
}
catch (Exception ex)
{
_result = base.GetText(_key);
}
}
...
ExtendedMultilanguage _translations = new Multilanguage();
...
someText = _translations.GetText(_someKey);
...
the idea being that if the key is not found in ExtendedTexts the method will call the base class which is looking into GlobalTexts. I did this in order to use the call GetText(wantedKey) everywhere in the code without having to care about the location of the resource (I do not want to include the translations from the extensions in the GlobalTexts files); it is juts the used class that is different from project to project.
The problem I am facing is that the try/catch is very slow when exceptions raise- I wait seconds for one window to populate. I tested with direct call and works much faster, but then I need to care all the time where the resource is located...
The question is: is there an alternative way of doing this (having resources spread in various files and have only one method that gives the desired resource without throwing an error)?
In the end I took a workaround solution and loaded all the content of the resource files in dictionaries. This way I can use ContainsKey and see if the key exists or not.
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; }
}
}