Why EvaluateJavaScriptAsync is not being executed? - c#

I have js function getState() that returns a css property(block or none) of an element but it isn't working, visual studio gives some Thread messages:
09-14 23:30:22.081 W/WebView ( 6707): java.lang.Throwable: A WebView method was called on thread 'Thread-12'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 2) {4aca651} called on null, FYI main Looper is Looper (main, tid 2) {4aca651})
09-14 23:30:22.081 W/WebView ( 6707): at android.webkit.WebView.checkThread(WebView.java:2539)
09-14 23:30:22.081 W/WebView ( 6707): at android.webkit.WebView.evaluateJavascript(WebView.java:1054)
Thread finished: #4
Can any one figure out where is the error? Also, when this be working, the returned type can be associated to a label text or it wouldn't be a simple string?
My code:
public static Label label1;
public static WebView webnav;
StackLayout parent = null;
public MainPage()
{
InitializeComponent();
webnav = new WebView
{
HeightRequest = 1000,
WidthRequest = 1000,
Source = "https://www.example.com/test.html",
};
webnav.Navigated += WebView_Navigated;
label1 = new Label
{
WidthRequest = 900,
HeightRequest = 60,
Text = "default text"
};
parent = new StackLayout();
parent.Children.Add(webnav);
parent.Children.Add(label1);
Content = parent;
}
public void WebView_Navigated(object sender, WebNavigatedEventArgs e)
{
Task.Run(JSRun);
}
public static string retorno;
public static async Task JSRun()
{
retorno = await webnav.EvaluateJavaScriptAsync("getState();");
}

All WebView methods must be called on the same thread...
FYI main Looper is Looper (main, tid 2)
You are using a Task.Run to execute a method that in turn calls EvaluateJavaScriptAsync and thus you are not on the Main/UI thread. That is what the error is trying to tell you.
So remove the Task.Run:
public void WebView_Navigated(object sender, WebNavigatedEventArgs e)
{
JSRun();
}
Or post your EvaluateJavaScriptAsync call to the UI message/looper queue:
Device.BeginInvokeOnMainThread(async() =>
{
retorno = await webnav.EvaluateJavaScriptAsync("getState();");
});

Related

Render to winforms picturebox over and over again

I have some bugs that need fixing, one of them involves an out of memory error.
Does anyone know how to do this properly? Thanks, I don't want it to be too messy, or too complicated. I just want to treat a new image as a buffer to render another image to (because of positional changes), and do it via a background thread. Not the UI thread (Too slow likely).
I get out of memory errors, and such. Also not able to access members of Form1 from within the thread function (images and the like throw access errors such as "Object already in use")
Here is my code:
System.Threading.Thread t;
public Image b;
public Bitmap c;
public Bitmap d;
public Bitmap e;
public Bitmap bg;
public Bitmap spr;
int spritex = 0;
int spritey = 0;
int spritedir = 1;
public Form1()
{
InitializeComponent();
Text = "Escape The Hypno Mansion!!".ToString();
t = new System.Threading.Thread(DoThisAllTheTime);
t.Start();
textBox1.Text = "Press Begin button to start!";
pictureBox1.Image = Image.FromFile(#"Images\introgirl.jpg");
b = new Bitmap(#"Images\introgirl.jpg");
c = new Bitmap(#"Images\sprite.png");
var graphics = Graphics.FromImage(b);
Pen blackpen = new Pen(Color.Black, 3);
graphics.DrawLine(blackpen, 0, 0, 100, 100);
graphics.DrawImage(c, new Point(500, 500));
pictureBox1.Image = b;
//pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}
public void DoThisAllTheTime()
{
while (true)
{
Point p = new Point(spritex, spritey);
bg = new Bitmap(#"Images\test.bmp");
spr = new Bitmap(#"Images\sprite.png");
using (var graphics = Graphics.FromImage(bg))
{
graphics.DrawImage(spr, p);
}
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = bg;
pictureBox1.Invalidate();
if (spritedir == 1) { spritex += 5; }
if (spritedir == 2) { spritex -= 5; }
if (spritex < 0) { spritex = 0; spritedir = 1; }
if (spritex > 700) { spritex = 700; spritedir = 2; }
}
}
The reason you can't change the image in your picturebox is because the thread that created the image is not the thread that created the picturebox.
In a debugger you can check this by asking the picturebox for InvokeRequired (function Control.IsInvokeRequired) just before changing the function.
So let's rewrite your function and show that modern classes Like Task are much easier to use the your thread.
I'll start your task when the form is loading, and try to stop it when the form is closing.
private Task myTask = null;
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private void OnFormLoading(object sender, EventArgs e)
{
// Start the task
this.myTask = Task.Run( () => DoMyWork(this.cancellationTokenSource.Token));
}
private void OnFormClosing(object sender, FormClosingEventArgs e)
{
// if the Task is still running, ask it to stop itself:
if (myTask != null && !myTask.IsCompleted)
{ // ask the task to stop and wait until it is completed:
this.cancellationTokenSource.Cancel();
// all Tokens extractes from this source will get state CancellationRequested
// wait maximum 5 seconds until the task is completed:
this.UseWaitCursor = true;
this.myTask.Wait(TimeSpan.FromSeconds(5));
this.UseWaitCursor = false;
// cancel closing if the task is still not completed
e.Cancel = !this.myTask.Completed;
}
}
Now the function DoMyWork:
private void DoMyWork(CancellationToken cancellationToken)
{
// Do the same as in your DoThisAllTheTime
// except that you regularly check cancellationToken.IsCancelRequested:
while(!cancellationToken.IsCancelRequested)
{
// calculate the image to display
var imageToDisplay = ...
this.DisplayImage(imageToDisplay);
}
}
void DisplayImage(Image imageToDisplay)
{
if (this.pictureBox1.InvokeRequired)
{
this.Invoke(new MethodInvoker( () => this.DisplayImage(imageToDisplay)));
}
else
{
this.PictureBox1.Image = imageToDisplay;
}
}
See:
How to cancel a Task and its children
Use InvokeRequired with lambda expression
Dispose every disposable instances before the loop ends. Your memory leak is related with disposable items not being cleaned from memory, so you'll eventually run out of memory in your infinite loop.
At the very least, you'll want to dispose both bitmaps at the end of the loop:
bg = new Bitmap(#"Images\test.bmp");
spr = new Bitmap(#"Images\sprite.png");

wpf Multiple dispatch timer for timer application : Run multiple timer simulteniously

Read multiple stackoverflow, codeproject solution, could not integrate to my problem.
Have a datagrid in a usercontrol which is loaded in a window. Each DataRow in the DataGrid represents a timer setting.
Like:
timer name : Test 1 , Timer : 1h 3m
timer name : Test 2 , Timer : 2h 2m
timer name : Test 3 , Timer : 3h 1m
Selecting a row, clicking on the button Start, Starts the timer of that row. And with dispatcher tick event, it updates the grid I have done till this. Now I have to start another(or two or ...) timer which will do the same at the same time. I am stuck on this. Let me share what I have tried!
btnStartClickEvent in mainwindow.xaml.cs
if (btnStart.Content.ToString() == "Start")
{
if (_AUC == ActiveUserControl.Grid)
{
runningRow = (TaskGridData)_TG.dgEmployee.SelectedItem;
if (runningRow != null)
{
currentlyRunningID.Add(runningRow.ID);
btnStart.Content = "Stop";
//worker.RunWorkerAsync(runningRow);
StartTimer(runningRow);
}
}
}
else if (btnStart.Content.ToString() == "Stop")
{
btnStart.Content = "Start";
StopTimer();
}
private DateTime TimerStart { get; set; }
private void StartTimer(TaskGridData tgd)
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1, 0);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
TimerStart = DateTime.Now;
dispatcherTimer.Start();
//worker.RunWorkerAsync();
//string etime = DateTime.Now.Second.ToString();
}
private void StopTimer()
{
dispatcherTimer.Stop();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
var currentValue = DateTime.Now - TimerStart;
runningRow.Duration = DurationValueToString(currentValue);
temp = (List<TaskGridData>)_TG.dgEmployee.ItemsSource;
foreach (TaskGridData item in temp)
{
if (item.ID == runningRow.ID)
{
item.Duration = DurationValueToString(DurationStringToVlaue(item.Duration) - DurationStringToVlaue(runningRow.Duration));
break;
}
}
//_TG.dgEmployee.ItemsSource = null;
//_TG.dgEmployee.ItemsSource = temp;
Thread NewThreadforStartProcessAfterTraining = new Thread(() => UpdateGrid());
NewThreadforStartProcessAfterTraining.IsBackground = true;
NewThreadforStartProcessAfterTraining.SetApartmentState(ApartmentState.STA);
NewThreadforStartProcessAfterTraining.Start();
}
private void UpdateGrid()
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
_TG.dgEmployee.ItemsSource = null;
_TG.dgEmployee.ItemsSource = temp;
}));
}
I know this code is for single timer. If I click a 2nd row and try to start timer, then it gets error in tick event, running row is found null.
I am wondering how can I keep this code and make it work for multiple timer. May be multithreading. A guide to do that, will be very helpful.
Thread NewThreadforStartProcessAfterTraining = new Thread(() => UpdateGrid());
NewThreadforStartProcessAfterTraining.IsBackground = true;
NewThreadforStartProcessAfterTraining.SetApartmentState(ApartmentState.STA);
NewThreadforStartProcessAfterTraining.Start();
All the above part where you start a new STA thread is unneeded and wrong in this context, since you can't update the visual tree in this way.
You can find a correct example of using a STA thread in one of my previous answers: https://stackoverflow.com/a/42473167/6996876
Try to understand the concept of thread affinity in WPF.
You simply need an UpdateGrid() where you have to delegate UI work to the dispatcher.
Furthermore, passing an argument to the Tick event is already explained here: https://stackoverflow.com/a/16380663/6996876
In your case you may want to change the current unique runningRow so that it's passed to the event instead.

Firing events in loop are not updating UI in sequence

I was trying to update status on UI for a Long Running Operating. I've created a demo form based application, task it have multiple rows, each row is having days and default values in each column of datagrid is 0, once computation file computes one iteration for one day it will update UI and set 1 for that day.
I am using threading, delegates and events to implement this and it is working as expected if I put Thread.Sleep(100) between two event calls. If I put "Thread.Sleep(100)" inside last nested for loop then it updates UI as expected but as soon as I remove it and run loop without sleep, then it skips some of the columns on UI and directly update last few/random columns, as you can see in attached image link(Image of output of my code without thread sleep) only last column is getting updated.
If I am not mistaken all the events are getting fired in sequence then they should update UI in sequence too but it's not happening and I don't know why. I don't want to do this Sleep thing because I have around 14 calls in actual application for UI status update and it will run under a loop so if It put sleep(100) then it will cost me a lot, is there any way to do it without SLEEP?
Image of output of my code without thread sleep
public class Class1 : IGenerate
{
public event MessageEventHandler OnMessageSending;
public void LongOperationMethod(BindingList<Status> _statusData)
{
if (OnMessageSending != null)
{
MessageEventArgs me = new MessageEventArgs();
/// Loop for 2-3 Weeks
for (; ; ){
/// Loop for 7 day
for (; ; )
{
/// Calculation on everyday
for (int j = 0; j != 1000; ++j)
{
// to do
}
me.weekNo = k;
me.DayNo = i;
OnMessageSending(me);
}
}
me.Message = "Process completed successfully...";
OnMessageSending(me);
}
else
{
throw new ArgumentException("Event hasn`t been rised, so we cannot continue working.");
}
}
}
**UI file:**
<pre><code>
public partial class Form1 : Form
{
BindingList<Status> _statusData = new BindingList<Status>();
delegate void StringParameterDelegate(string value);
Class1 cls = new Class1();
public Form1()
{
InitializeComponent();
labelProgress.Text = "";
}
private void button1_Click_1(object sender, EventArgs e)
{
for (int i = 1; i <= 2; ++i)
{
_statusData.Add(new Status { Week = "Week" + i, Day1 = 0, Day2 = 0, Day3 = 0, Day4 = 0, Day5 = 0, Day6 = 0, Day7 = 0 });
}
dataGridView1.DataSource = _statusData;
}
private void button2_Click(object sender, EventArgs e)
{
Thread t1 = new Thread(() => StartingThread(_statusData));
t1.Start();
}
void StartingThread(BindingList<Status> _statusData)
{
IGenerate generate = new Class1();
generate.OnMessageSending += new MessageEventHandler(generate_OnMessageSending);
generate.LongOperationMethod(_statusData);
}
private void generate_OnMessageSending(MessageEventArgs e)
{
int weekNo = e.weekNo;
int dayNo = e.DayNo;
this.dataGridView1.BeginInvoke(new MethodInvoker(() => dataGridView1.Rows[e.weekNo].Cells[e.DayNo + 1].Value = 1));
this.labelProgress.BeginInvoke(new MethodInvoker(() => this.labelProgress.Text = e.Message));
}
}
</code></pre>
It looks like you are sending the same instance of MessageEventArgs every time, and just updating that one instance on the background thread. This means that your event handler on the UI thread will retrieve the exact same instance of MessageEventArgs that is being updated in the loop! By the time your UI handler gets the MessageEventArgs, its .weekNo and .DayNo properties could well have been modified by the next iteration of the loop, since they are running on separate threads.
To fix this, create a new instance of MessageEventArgs every time you call OnMessageSending().
Relevant snippet:
MessageEventArgs me = new MessageEventArgs();
me.weekNo = k;
me.DayNo = i;
OnMessageSending(me);

Android.Media.AudioTrack.GetMinBufferSize Method - how to use its syntax in Xamarin.Android (c#)?

I am currently making a music app for Android in Xamarin studio using csharp. I am making a simple synthesizer app and at the moment I am stuck with GetMinBufferSize method with csharp.
Xamarin.Android documentation provides the following syntax for the GetMinBufferSize method (here's the link: xamarin API audiotrack documentation:
[Android.Runtime.Register("getMinBufferSize", "(III)I", "")]
public static int GetMinBufferSize (int sampleRateInHz,
[Android.Runtime.GeneratedEnum] ChannelOut channelConfig,
[Android.Runtime.GeneratedEnum] Encoding audioFormat)
which corresponds to the following in Java (link: android reference):
public static int getMinBufferSize (int sampleRateInHz, int channelConfig, int audioFormat)
The thing I do not understand is what are theses things and how I should use them:
[Android.Runtime.Register("getMinBufferSize", "(III)I", "")]
[Android.Runtime.GeneratedEnum]
[Android.Runtime.GeneratedEnum]
The code in Java was easier:
int buffsize = AudioTrack.getMinBufferSize(_sampleRate,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
where _sampleRate is int _sampleRate = 44100; and represents frequency rate.
So, if you at least told me what those three lines in brackets from xamarin documentation are - I would be so grateful.
Thank you in advance and have a great day.
My code so far:
namespace simple_synth
{
[Activity (Label = "_secondAct")] //activity that opens the second screen
public class _secondAct : Activity
{
Thread _thread; //audio processing thread
int _sRate = 44100; //sampling rate
bool isRunning = true; //switch on/off
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout._secondLay);
Button btn2 = FindViewById<Button> (Resource.Id.myButton_synth);
_audio _audioSound = new _audio ();
btn2.Click += (sender, e) => {
btn2.Text = "GOOD";
Thread _audioThread = new Thread(_audioSound._makeSound);
_audioThread.Start();
Console.WriteLine("audio thread: started");
while (!_audioThread.IsAlive);
Thread.Sleep(1000);
_audioSound._stopRequest();
_audioThread.Join();
Console.WriteLine("audio thread: terminated now!");
_audioSound._startRequest();
};
}
}//_secondAct
public class _audio{
private volatile bool _stopItNow;
public void _makeSound(){ // This method will be called when the thread is started.
while (!_stopItNow) {
Console.WriteLine ("audio thread: is playing the sound...");
AudioTrack _audioTrack = new AudioTrack (Stream.Music, 22050,
ChannelConfiguration.Mono,
Android.Media.Encoding.Pcm16bit,
_audBuffer.Length, AudioTrackMode.Stream);
_audioTrack.Play ();
_audioTrack.Write (_audBuffer, 0, _audBuffer.Length);
}
Console.WriteLine ("audio thread: terminated.");
} //doWork
public void _stopRequest()
{
_stopItNow = true;
}
public void _startRequest()
{
_stopItNow = false;
}
}
}
Very simple in C# as well:
var minBufSize = AudioTrack.GetMinBufferSize (
44100,
ChannelOut.Mono,
Encoding.Pcm16bit);

Task blocks UI from refreshing in WPF

I am building a WPF application that converts a powerpoint to WPF elements when you select one from a list.
I am using MVVM light to bind a ViewModel to my view and to add communication between ViewModels.
I have two views: OpenLocalView and PresentationView. When I select a powerpoint in the OpenLocalView, a message will be sent by MVVM light to the ViewModel of PresentationView and the MainViewModel with the path to that powerpoint. The MainViewModel switches the view to the PresentationView, and the PresentationViewModel executes this code to convert the powerpoint, and when that is finished, set the current slide so it is shown in the PresentationView:
public void StartPresentation(string location)
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Loading = true;
Task.Factory.StartNew(() =>
{
var converterFactory = new ConverterFactory();
var converter = converterFactory.CreatePowerPointConverter();
_slides = converter.Convert(location).Slides;
},
CancellationToken.None,
TaskCreationOptions.LongRunning,
scheduler).ContinueWith(x =>
{
Loading = false;
CurrentSlide = _slides.First();
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
scheduler);
}
When the Loading property is set, the view gets updated with a "loading" message, to make the UI more responsive:
public Boolean Loading
{
get { return _loading; }
set
{
_loading = value;
RaisePropertyChanged("Loading");
}
}
The problem is, this executes properly the first time when I load a powerpoint: The view switches to the PresentationView, the "loading" message is displayed, and after the converting is finished, the message disappears and the slide is shown. But when I go back to the OpenLocalView, and choose another powerpoint, the OpenLocalView hangs and it switches to the PresentationView after the converter is finished, not showing the "loading" message at all.
For reference, I will add some more relevant code.
This is executed when a powerpoint is selected in the OpenLocalViewModel:
private void PerformOpenPresentation(string location)
{
Messenger.Default.Send<OpenPowerPointMessage>(new OpenPowerPointMessage {Location = location});
}
The MainViewModel is subscribed to the messenger and switches the view:
Messenger.Default.Register<OpenPowerPointMessage>(this,
delegate
{
if (_presentation == null) _presentation = new PresentationView();
CurrentView = _presentation;
});
The PresentationViewModel is subscribed to the messenger as well and executes the method shown above:
Messenger.Default.Register<OpenPowerPointMessage>(this, message => StartPresentation(message.Location));
So, what am I doing wrong? Again, it executes fine one time, then after that not anymore, although the same code is executed.
Maybe the UI isn't updated yet when you already start converting. Try waiting a few milliseconds between setting the Loading to true and the start of the converter thread :)
Look here:
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Loading = true;
Task.Factory.StartNew(() =>
{
var converterFactory = new ConverterFactory();
var converter = converterFactory.CreatePowerPointConverter();
_slides = converter.Convert(location).Slides;
},
CancellationToken.None,
TaskCreationOptions.LongRunning,
here ----> scheduler).ContinueWith(x =>
{
Loading = false;
CurrentSlide = _slides.First();
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
scheduler);
You're starting the 'long running' task on the synchronization context, that is - on the UI thread.
Get rid of the scheduler in the long running task, leave it on continuation. :)
I suggest moving the "Loading = true" in to task start block.. but make sure to use dispatcher while setting "Loading" value. i may not give the actual reason for the problem but its worth a try...
something like this may help..
Task.Factory.StartNew(() =>
{
System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate()
{
Loading = true;
});
var converterFactory = new ConverterFactory();
var converter = converterFactory.CreatePowerPointConverter();
_slides = converter.Convert(location).Slides;
}
Ok, I did it with windows forms, but I think is the same
I create a Label in the Task thread and call the Invoke of the form
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void AddControl(Control control)
{
if (InvokeRequired)
{
this.Invoke(new Action<Control>(AddControl), new object[] { control });
return;
}
this.Controls.Add(control);
}
private void Form1_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
var label = new Label
{
Location = new Point(0, 0),
Text = "hola",
ForeColor = Color.Black
};
this.Invoke(new Action<Control>(AddControl), new object[] { label });
});
}
}
EDIT
Ok how about using Dispather.Invoke I'm not sure if this is going to block the UI....
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void AddControl()
{
var l = new Label
{
Content = "Label",
Height = 28,
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(209, 118, 0, 0),
Name = "label1",
VerticalAlignment = System.Windows.VerticalAlignment.Top
};
Grid.Children.Add(l);
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
Dispatcher.Invoke(new Action(AddControl), null);
});
}
}

Categories