I'm developing a C#/Xaml UWP text editing app that uses the RichEditBox control for editing text files from. However, I've noticed when I load larger files (~1mb and above, perhaps even less) that the control struggles in a couple of key areas: 1) it takes a while to load the contents of the file and 2) once it finally has, scrolling is very jerky and input into the file is neither smooth nor responsive.
The closest answer I've come across is this (what it describes is exactly the problem I'm having) but it doesn't appear to be applicable to a UWP app.
EDIT:
When I open a file, the noteworthy code to share is:
_currentFile.Content = await readFile(file);
_currentFile._richeditbox.Document.SetText(TextSetOptions.None, _currentFile.Content);
This is the readFile() function, which was helped via https://social.msdn.microsoft.com/Forums/sqlserver/en-US/0f3cd056-a2e3-411b-8e8a-d2109255359a/uwpc-reading-ansi-text-file?forum=wpdevelop:
private async Task<string> readFile(StorageFile file)
{
string charSet = null;
try
{
await Task.Run(() =>
{
try
{
using (FileStream fs = System.IO.File.OpenRead(file.Path))
{
Ude.CharsetDetector cdet = new Ude.CharsetDetector();
cdet.Feed(fs);
cdet.DataEnd();
charSet = cdet.Charset;
}
}
catch (Exception ex)
{
}
});
}
catch (Exception e)
{
}
Classes.File._lastEncoding = charSet;
IBuffer buffer = await FileIO.ReadBufferAsync(file);
DataReader reader = DataReader.FromBuffer(buffer);
byte[] fileContent = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(fileContent);
string content = "";
if (charSet == "windows-1252")
{
content = Encoding.GetEncoding("iso-8859-1").GetString(fileContent, 0, fileContent.Length);
}
else if (charSet == "UTF-16LE")
{
content = Encoding.Unicode.GetString(fileContent, 0, fileContent.Length);
}
else if (charSet == "UTF-16BE")
{
content = Encoding.BigEndianUnicode.GetString(fileContent, 0, fileContent.Length);
}
else
{
content = Encoding.UTF8.GetString(fileContent, 0, fileContent.Length);
}
return content;
}
When debugging, there's absolutely no delay when the readFile() function is called; it gets executed very quickly.
I've tried loading a 1.14mb text file. It takes some time to load, and when it does although the scrollbar height indicates all of it has loaded, it actually doesn't display any text from line 2098 onwards (there are 3,771 lines in total); it stops at this line consistently even on subsequent reloads.
See picture:
As you can also see the last line that is visible gets mashed up with the line above it.
For reference, the file I'm having the problems with can be downloaded from here (but to be clear it's a problem with all text files of a similar size, and possibly much less even): http://mtrostyle.net/appytext/testfile.txt.
Yes, I get the same result in my side when I load you provide txt file. I has been reporting this issue to related team. Currently, one workaround to show the file is that render the text in the WebView control. And the set import and export for the WebView. For more you could refer to WebView.
<body>
<textarea id="input" style="width:100%; height:400px;"></textarea>
<script type="text/javascript">
function SetText(text) {
document.getElementById('input').textContent = text;
}
function SaveText() {
var note = document.getElementById('input').textContent;
window.external.notify(note);
}
</script>
</body>
MainPage.xaml
<WebView x:Name="MyWebView" Source="ms-appx-web:///HomePage.html" Height="400" />
MainPage.xaml.cs
public MainPage()
{
this.InitializeComponent();
MyWebView.ScriptNotify += MyWebView_ScriptNotify;
}
private void MyWebView_ScriptNotify(object sender, NotifyEventArgs e)
{
var saveText = e.Value.ToString();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
Windows.Storage.Pickers.FileOpenPicker open =
new Windows.Storage.Pickers.FileOpenPicker();
open.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
open.FileTypeFilter.Add(".rtf");
open.FileTypeFilter.Add(".txt");
Windows.Storage.StorageFile file = await open.PickSingleFileAsync();
if (file != null)
{
var Content = await readFile(file);
string[] args = { Content };
await MyWebView.InvokeScriptAsync("SetText", args);
}
}
private async Task<string> readFile(StorageFile file)
{
string charSet = null;
try
{
await Task.Run(async () =>
{
try
{
using (var fs = await file.OpenAsync(FileAccessMode.Read))
{
var tem = fs.AsStream();
Ude.CharsetDetector cdet = new Ude.CharsetDetector();
cdet.Feed(tem);
cdet.DataEnd();
charSet = cdet.Charset;
}
}
catch (Exception ex)
{
}
});
}
catch (Exception e)
{
}
IBuffer buffer = await FileIO.ReadBufferAsync(file);
DataReader reader = DataReader.FromBuffer(buffer);
byte[] fileContent = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(fileContent);
string content = "";
if (charSet == "windows-1252")
{
content = Encoding.GetEncoding("iso-8859-1").GetString(fileContent, 0, fileContent.Length);
}
else if (charSet == "UTF-16LE")
{
content = Encoding.Unicode.GetString(fileContent, 0, fileContent.Length);
}
else if (charSet == "UTF-16BE")
{
content = Encoding.BigEndianUnicode.GetString(fileContent, 0, fileContent.Length);
}
else
{
content = Encoding.UTF8.GetString(fileContent, 0, fileContent.Length);
}
return content;
}
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
await MyWebView.InvokeScriptAsync("SaveText", null);
}
Related
This question already has answers here:
Read/Write text file progressbar in C#
(1 answer)
How to show progress of reading from a file and writing to a database
(1 answer)
Closed 1 year ago.
I am trying to read a large txt file (>50MB) asynchronously and while it is being read, report the progress on the UI progressbar and have the option to cancel the process. So far I have read and processed the file async as I wanted but I could not solve the progressbar part.
public static async Task<string> ReadTxtAsync(string filePath)
{
try
{
using (var reader = File.OpenText(filePath))
{
var content = await reader.ReadToEndAsync();
return content;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return null;
}
}
public static async Task<Dictionary<string, int>> OpenTxtAsync()
{
Dictionary<string, int> uniqueWords = new Dictionary<string, int>();
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Text Documents (*.txt)|*.txt";
string content = null;
try
{
if (openFileDialog.ShowDialog() == true)
{
string filePath = openFileDialog.FileName.ToString();
if (openFileDialog.CheckFileExists && new[] { ".txt" }.Contains(Path.GetExtension(filePath).ToLower()) && filePath != null)
{
Task<string> readText = ReadTxtAsync(filePath);
content = await readText;
uniqueWords = WordExtractor.CountWords(ref content);
}
else MessageBox.Show("Please use .txt format extension!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return uniqueWords;
}
private async void LoadFileButtonClick(object sender, RoutedEventArgs e)
{
Task<Dictionary<string, int>> dictionaryContent = TextFileLoader.OpenTxtAsync();
uniqueWords = await dictionaryContent;
UpdateListView();
}
How can I check where ReadToEndAsync() is currently? How can I get it to continously update the progressbar and how can I cancel it?
EDIT:
Thanks to #emoacht I managed to get the progressbar to update correctly and display its percentage. The only thing that remains is to cancel the task, which I tried according to a Tim Corey video, but it did not work on my code.
public static async Task<string> ReadTextAsync(string filePath, IProgress<(double current, double total)> progress, CancellationToken cancellationToken)
{
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var reader = new StreamReader(stream);
var readTask = reader.ReadToEndAsync();
cancellationToken.ThrowIfCancellationRequested();
var progressTask = Task.Run(async () =>
{
while (stream.Position < stream.Length)
{
await Task.Delay(TimeSpan.FromMilliseconds(100));
progress.Report((stream.Position, stream.Length));
}
});
await Task.WhenAll(readTask, progressTask);
return readTask.Result;
}
try
{
Task<string> readText = TextFileLoader.ReadTextAsync(filePath, progress, cts.Token);
content = await readText;
LabelProgress.Content = "Done Reading! Now creating wordlist...";
}
catch (OperationCanceledException)
{
LabelProgress.Content = "File laden wurde abgebrochen";
}
I have a buttonClick Event for cancel cts.Cancel(); but the only part where it works is the Dictionary creation. If I place the cancellationToken.ThrowIfCancellationRequested(); into the progressbar update part, it stops only the update, the stream reading still continues. If I place is right below var readTask = reader.ReadToEndAsync(); it does nothing.
You can get the current position while reading by checking Stream.Position property at regular interval. The following method will check the current position once per 100 milliseconds and report it by current value of progress parameter. To use this method, instantiate Progess<(double current, double total)> and subscribe to its ProgressChanged event.
public static async Task<string> ReadTextAsync(string filePath, IProgress<(double current, double total)> progress)
{
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var reader = new StreamReader(stream);
var readTask = reader.ReadToEndAsync();
var progressTask = Task.Run(async () =>
{
while (stream.Position < stream.Length)
{
await Task.Delay(TimeSpan.FromMilliseconds(100));
progress.Report((stream.Position, stream.Length));
}
});
await Task.WhenAll(readTask, progressTask);
return readTask.Result;
}
I am trying to load a picture from my PC as a raw image in order to use it with the Microsoft cognitive services emotion (UWP).
below is a piece of my code:
//Chose Image from PC
private async void chosefile_Click(object sender, RoutedEventArgs e)
{
//Open Dialog
FileOpenPicker open = new FileOpenPicker();
open.ViewMode = PickerViewMode.Thumbnail;
open.SuggestedStartLocation = PickerLocationId.Desktop;
open.FileTypeFilter.Add(".jpg");
open.FileTypeFilter.Add(".jpeg");
open.FileTypeFilter.Add(".gif");
open.FileTypeFilter.Add(".png");
file = await open.PickSingleFileAsync();
if (file != null)
{//imagestream is declared as IRandomAccessStream.
imagestream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var image = new BitmapImage();
image.SetSource(imagestream);
imageView.Source = image;
}
else
{
//
}
}
The part above works fine, it selects a photo from the pc (dialog box) and displays it in Image box.
private async void analyse_Click(object sender, RoutedEventArgs e)
{
try
{
emotionResult = await emotionServiceClient.RecognizeAsync(imagestream.AsStream());
}
catch
{
output.Text = "something is wrong in stream";
}
try {
if(emotionResult!= null)
{
Scores score = emotionResult[0].Scores;
output.Text = "Your emotions are: \n" +
"Happiness: " + score.Happiness + "\n" +
"Sadness: " + score.Sadness;
}
}
catch
{
output.Text = "Something went wrong";
}
}
I think the error is due to imagestream.AsStream()
imagestream is declared as IRandomAccessStream.
Can someone please tell me how to fix that part and if the error is in fact due to not loading the image correctly?
EDIT:
Also is there a better way to do this, instead of using stream to pass the emotionServiceClient a saved file instead of a stream?
Your problem is that you've advanced the stream position by virtue of creating the BitmapImage, so your read position is at the end by the time you call emotionServiceClient.RecognizeAsync. So you'll need to 'rewind':
var stream = imagestream.AsStreamForRead();
stream.Position = 0;
emotionResult = await emotionServiceClient.RecognizeAsync(stream);
Why not use their example, instead of trying to hold the file in memory, why don't you hold a path, and then use the path to read the stream at the time.
https://www.microsoft.com/cognitive-services/en-us/Emotion-api/documentation/GetStarted
In there example;
using (Stream imageFileStream = File.OpenRead(imageFilePath))
{
//
// Detect the emotions in the URL
//
emotionResult = await emotionServiceClient.RecognizeAsync(imageFileStream);
return emotionResult;
}
So you would be capturing imageFilePath as the result of the open file dialog.
I am working on a project which makes drawing.
I don't use axml because I do my drawing in a class called filledpolygon and calling the function in MainActivity. I just want to take screenshot in my project. Is there any basic function, which I can call in onCreate method? So, when the program runs, it will automatically take the screenshot. I found answers except Xamarin platform.
Since Android 28 DrawingCacheEnabled is deprecated and without it we are forcing our view to to redraw on our custom canvas wich can cause artifacts with custom controls and renderers and the screenshot version might be different from what we see on screen.
The legacy code that is still working on simple cases is:
public byte[] CaptureScreenshot()
{
var view=
Xamarin.Essentials.Platform.CurrentActivity.Window.DecorView.RootView;
if (view.Height < 1 || view.Width < 1)
return null;
byte[] buffer = null;
view.DrawingCacheEnabled = true;
using (var screenshot = Bitmap.CreateBitmap(
view.Width,
view.Height,
Bitmap.Config.Argb8888))
{
var canvas = new Canvas(screenshot);
view.Draw(canvas);
using (var stream = new MemoryStream())
{
screenshot.Compress(Bitmap.CompressFormat.Png, 90, stream);
buffer = stream.ToArray();
}
}
view.DrawingCacheEnabled = false;
return buffer;
}
Use legacy method above as follows
if ((int)Android.OS.Build.VERSION.SdkInt < 28)
{
//legacy
}
The DrawingCacheEnabled obsolete warning redirects us to use PixelCopy. This method is acting with a callback so to use it synchronously have made some helpers:
Usage:
public byte[] CaptureScreenshot()
{
using var helper = new ScreenshotHelper(
Xamarin.Essentials.Platform.CurrentActivity.Window.DecorView.RootView,
Xamarin.Essentials.Platform.CurrentActivity);
byte[] buffer = null;
bool wait = true;
Task.Run(async () =>
{
helper.Capture((Bitmap bitmap) =>
{
try
{
if (!helper.Error)
{
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 90, stream);
buffer = stream.ToArray();
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
wait = false;
}
});
}).ConfigureAwait(false);
while (wait)
{
Task.Delay(10).Wait();
}
return buffer;
}
The helper:
public class ScreenshotHelper : Java.Lang.Object, PixelCopy.IOnPixelCopyFinishedListener
{
public void OnPixelCopyFinished(int copyResult)
{
var stop = true;
if (copyResult == (int) PixelCopyResult.Success)
{
Error = false;
//todo CallbackGotScreenshot();
_callback(_bitmap);
}
else
{
Error = true;
}
_callback(_bitmap);
}
public bool Error { get; protected set; }
public ScreenshotHelper(Android.Views.View view, Activity activity)
{
_view = view;
_activity = activity;
_bitmap = Bitmap.CreateBitmap(
_view.Width,
_view.Height,
Bitmap.Config.Argb8888);
}
// Starts a background thread and its {#link Handler}.
private void StartBackgroundThread()
{
_BackgroundThread = new HandlerThread("ScreeshotMakerBackground");
_BackgroundThread.Start();
_BackgroundHandler = new Handler(_BackgroundThread.Looper);
}
// Stops the background thread and its {#link Handler}.
private void StopBackgroundThread()
{
try
{
_BackgroundThread.QuitSafely();
_BackgroundThread.Join();
_BackgroundThread = null;
_BackgroundHandler = null;
}
catch (Exception e)
{
//e.PrintStackTrace();
}
}
public void Capture(Action<Bitmap> callback)
{
//var locationOfViewInWindow = new int[2];
//_view.GetLocationInWindow(locationOfViewInWindow);
_callback = callback;
try
{
StartBackgroundThread();
//todo could create-use background handler
PixelCopy.Request(_activity.Window, _bitmap, this,
_BackgroundHandler);
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
Task.Run(StopBackgroundThread);
}
}
private Android.Views.View _view;
private Activity _activity;
private Bitmap _bitmap;
private HandlerThread _BackgroundThread;
private Handler _BackgroundHandler;
private Action<Bitmap> _callback;
public new void Dispose()
{
_bitmap?.Dispose();
_bitmap= null;
_activity = null;
_view = null;
_callback = null;
base.Dispose();
}
}
In your View you could run the following code which will take a screenshot. I have not tried running it in OnCreate() before so you may need to test that out to make sure the view has been fully rendered.
*Edit: According to this post you may have trouble running this code in OnCreate() so you will need to find a better place. I was unable to figure out what post the user was referring to in the link he posted.
*Edit #2: Just found out that Compress() does not take the quality parameter (which is listed as 0 below) into account since PNG is lossless, but if you change the format to JPEG for example, then you may want to turn up the quality parameter since your image will look like garbage.
public byte[] SaveImage() {
DrawingCacheEnabled = true; //Enable cache for the next method below
Bitmap bitmap = GetDrawingCache(true); //Gets the image from the cache
byte[] bitmapData;
using(MemoryStream stream = new MemoryStream()) {
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return bitmapData;
}
I want to create a C# application using windows forms that let me upload files to a webserver, i have seen a lot of tutorial and everyone of them prove to be useless to solve my problem.
and with my project i have the next code in the button for upload
WebClient client = new WebClient();
client.UploadFile("http://localhost:8080/", location);
from here i had have several errors, an try multiple ideas, some of my more common errors are 404 not found or innaccessible path, also sometimes it doenst display me an error and works but the file doesn't save in the indicated path.
some of the links i use to solve the problem are the next ones:
http://www.c-sharpcorner.com/UploadFile/scottlysle/UploadwithCSharpWS05032007121259PM/UploadwithCSharpWS.aspx
http://www.c-sharpcorner.com/Blogs/8180/
How to upload a file in window forms?
upload a file to FTP server using C# from our local hard disk.
private void UploadFileToFTP()
{
FtpWebRequest ftpReq = (FtpWebRequest)WebRequest.Create("ftp://www.server.com/sample.txt");
ftpReq.UseBinary = true;
ftpReq.Method = WebRequestMethods.Ftp.UploadFile;
ftpReq.Credentials = new NetworkCredential("user", "pass");
byte[] b = File.ReadAllBytes(#"E:\sample.txt");
ftpReq.ContentLength = b.Length;
using (Stream s = ftpReq.GetRequestStream())
{
s.Write(b, 0, b.Length);
}
FtpWebResponse ftpResp = (FtpWebResponse)ftpReq.GetResponse();
if (ftpResp != null)
{
if(ftpResp.StatusDescription.StartsWith("226"))
{
Console.WriteLine("File Uploaded.");
}
}
}
In windows:
private void uploadButton_Click(object sender, EventArgs e)
{
var openFileDialog = new OpenFileDialog();
var dialogResult = openFileDialog.ShowDialog();
if (dialogResult != DialogResult.OK) return;
Upload(openFileDialog.FileName);
}
private void Upload(string fileName)
{
var client = new WebClient();
var uri = new Uri("http://www.yoursite.com/UploadMethod/");
try
{
client.Headers.Add("fileName", System.IO.Path.GetFileName(fileName));
var data = System.IO.File.ReadAllBytes(fileName);
client.UploadDataAsync(uri, data);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
In server:
[HttpPost]
public async Task<object> UploadMethod()
{
var file = await Request.Content.ReadAsByteArrayAsync();
var fileName = Request.Headers.GetValues("fileName").FirstOrDefault();
var filePath = "/upload/files/";
try
{
File.WriteAllBytes(HttpContext.Current.Server.MapPath(filePath) + fileName, file);
}
catch (Exception ex)
{
// ignored
}
return null;
}
winform
string fullUploadFilePath = #"C:\Users\cc\Desktop\files\test.txt";
string uploadWebUrl = "http://localhost:8080/upload.aspx";
client.UploadFile(uploadWebUrl , fullUploadFilePath );
asp.net create upload.aspx as below
<%# Import Namespace="System"%>
<%# Import Namespace="System.IO"%>
<%# Import Namespace="System.Net"%>
<%# Import NameSpace="System.Web"%>
<Script language="C#" runat=server>
void Page_Load(object sender, EventArgs e) {
foreach(string f in Request.Files.AllKeys) {
HttpPostedFile file = Request.Files[f];
file.SaveAs(Server.MapPath("~/Uploads/" + file.FileName));
}
}
</Script>
<html>
<body>
<p> Upload complete. </p>
</body>
</html>
you should set in win app
WebClient myWebClient = new WebClient();
string fileName = "File Address";
Console.WriteLine("Uploading {0} to {1} ...",fileName,uriString);
// Upload the file to the URI.
// The 'UploadFile(uriString,fileName)' method implicitly uses HTTP POST method.
byte[] responseArray = myWebClient.UploadFile(uriString,fileName);
then set read and write permission for SubDir
create a simple API Controller file in the Controllers folder and name it UploadController.
Let’s modify that file by adding a new action that will be responsible for the upload logic:
[HttpPost, DisableRequestSizeLimit]
public IActionResult UploadFile()
{
try
{
var file = Request.Form.Files[0];
string folderName = "Upload";
string webRootPath = _host.WebRootPath;
string newPath = Path.Combine(webRootPath, folderName);
string ext = Path.GetExtension(file.FileName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
if (file.Length > 0)
{
string fileName = "";
string name = Path.GetFileNameWithoutExtension(file.FileName);
string fullPath = Path.Combine(newPath, name + ext);
int counter = 2;
while (System.IO.File.Exists(fullPath))
{
fileName = name + "(" + counter + ")" + ext;
fullPath = Path.Combine(newPath, fileName);
counter++;
}
using (var stream = new FileStream(fullPath, FileMode.Create))
{
file.CopyTo(stream);
}
return Ok();
}
return Ok();
}
catch (System.Exception ex)
{
return BadRequest();
}
}
We are using a POST action for the upload-related logic and disabling the request size limit as well.
and use code below in Winform
private void Upload(string fileName)
{
var client = new WebClient();
var uri = new Uri("https://localhost/api/upload");
try
{
client.Headers.Add("fileName", System.IO.Path.GetFileName(fileName));
client.UploadFileAsync(uri, directoryfile);
client.UploadFileCompleted += Client_UploadFileCompleted;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Client_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
{
MessageBox.Show("done");
}
Goodluck
well i try a lot of things but i don't really understand everything of save in localstorage. I know how its work since the camera, but i don't know how to make it with inkManager. If you're any ideas ?
This is my code to save where the user wants, but i would like to "auto-save" in localstorage :
private async void save_Button(object sender, RoutedEventArgs e)
{
if (_inkManager.GetStrokes().Count > 0)
{
try
{
Windows.Storage.Pickers.FileSavePicker save = new Windows.Storage.Pickers.FileSavePicker();
save.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.Desktop;
save.DefaultFileExtension = ".jpg";
save.FileTypeChoices.Add("JPG", new string[] { ".jpg" });
StorageFile filesave = await save.PickSaveFileAsync();
IOutputStream ab = await filesave.OpenAsync(FileAccessMode.ReadWrite);
if (ab != null)
await _inkManager.SaveAsync(ab);
// await save.CopyAsync(ApplicationData.Current.LocalFolder, "merge1.jpg");
if (save != null)
{
Clipboard.Source = new Windows.UI.Xaml.Media.Imaging.BitmapImage(new Uri("ms-appdata:///local/merge1.jpg"));
}
}
catch (Exception)
{
}
}
}
The solution is pretty easy finally :
StorageFile myMerge = await ApplicationData.Current.LocalFolder.CreateFileAsync("myimg.png");
IOutputStream ac = await myMerge.OpenAsync(FileAccessMode.ReadWrite);
if (ac != null)
await _inkManager.SaveAsync(ac);