Blazor JSInterop static vs instance C# methods - c#

Could somebody please let me know how static methods work in regard to calling C# methods from JavaScript using JS interop?
If I have a static method in the code section of a component that references objects in that component, and there are multiple instances of that component on a page, will all instances be affected by the method call?
Or even worse, will this act as a Singleton on my server causing updates to every single client instance of that component?
I'm currently developing an audio recording system that cycles through phrases marked in text, and whenever JavaScript detects 5 frames of silence in the media stream, I want to call a C# method that will highlight the next section of text by changing its CSS class.

If you want to change some styles in your component, you definitely not want to use a static method. Because a static method could not interact with the instance of the component - as you cannot use the this keyword in a static method.
What I think you want to do is first, in C# side, create a classic method in your component C# code. For example create:
public void ChangeStyle()
{
this.ClassToDisplay = DoSomeManipulation();
}
Then, JS will need the instance of the the component. So you can do something like that: (I use the OnAfterRenderAsync because it is shown in Microsoft documentation, but the goal is to do it once)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
DotNetObjectReference<MyComponent>? dotNetHelper = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("JSFile.setDotNetHelper", dotNetHelper);
}
}
Then in JS side, you will need the setDotNetHelper method, with the following content:
class JSFile
{
static dotNetHelper;
static setDotNetHelper(value)
{
JSFile.dotNetHelper = value;
}
}
Now you are ready, you can call whenever you want the ChangeStyle method of your component. To do so, use this instruction in JS:
await dotNetHelper.invokeMethodAsync('ChangeStyle');
Et voilĂ . I am available for clarifications or questions, have a good one!
// Dylan

Related

Use injected service from JS code to static Blazor method

I have an app that uses a jQuery lib to show 'confirmation' dialogs.
When a user clicks 'OK' or 'Cancel' on the dialog, then first a callback is made to my Javascript code. From there I do a call to a Blazor method, based on the decision the user made.
This all looks something like this:
My js code:
$('.alert-yesno).on('click', function() {
// For simplicity I immediately call the Blazor method
DotNet.invokeMethodAsync('[Assembly name]', 'DoSomething')
.then(data => {
// do something...
});
});
Blazor page:
#inject MyService MyService
<button class="alert-yesno">Show dialog</button>
#code
{
[JSInvokable]
public static async Task DoSomething()
{
// How to use the non static MyService here...?
}
}
This all works fine. The Javascript part calls my Blazor method. But how can I use MyService from there? This is injected into the page. Which is good, because it makes use of dependency injection.
I don't want to create a new instance everytime from within the static method, because that way I loose all the auto injected dependencies.
How can I solve this issue?
See https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interop?view=aspnetcore-3.0#instance-method-call for a more complete example, but you can pass a reference to the the .net instance to the javascript code by calling
DotNetObjectReference.Create(myInstance);
Then the javascript code can call the instance method on that object. Since the component (and thus the service which is injected into it) has a potentially different lifespan than the jquery click handler, this might cause some other issues, but the link above should be a good starting point.

Access windows forms controls from external DLLs in C#

This is my first Topic here and I didn't find any similar Topics so I try to describe my problem as good as I can:
I was ordered by my Company to create a modular C# program to assist our Software Developers with Background tasks. The Programm is composed of a Windows Forms application with a User Interface that calls external DLLs that do the actual work. All These DLLs are written by me aswell and follow certain rules to make them compatible to the Main App. That way I can easily add new funcions to the Programm just by putting the DLL into a predefined Folder. So to say Plug-and-Run
The main program contains a ListBox that shows all available PlugIns and if one get's selected and the "start" button is clicked, the Main program calls the corresponding DLL and Invokes the method "program" that starts the DLLs actual function. Furthermore the Main contains a method "Output" that is supposed to write the result of every PlugIn into a Tab of my TabControl. That way the results of every PlugIn running in separate threads can be viewed independently. The Access to the tab already has a delegate to make it threadsafe. The Information is gathered by invoke from the PlugIn's own "returnOutput" method that simply Returns a List of strings containing the results to the Main.
My Problem now is: How can i implement a Kind of a callback into my PlugIn DLLs so they can order the Main Program to gather the results at any time?
My first idea was to simply add the result as return values to the "program" method itself but that would make the Information only available at the end of the program and some of the Tasks require a "live update" during runtime.
My second idea was to use the delegate for the Control as Parameter and pass it to the PlugIn so the PlugIn DLL could Access the Control on it's own. This idea failed because the DLL doesn't "know" the Main program and can't Access it's Methods or the delegates instance so I am Always missing a reference.
Is there a way to solve my problem? If necessary I can provide Code snippets but the program has already around 800 lines of Code and each PlugIn adds a few hundred more..
Thanks in advance for every answer and sorry for my non-native english :D
Best Regards
Gerrit "Raketenmaulwurf" M.
Edit: I am using SharpDevelop 5.1
Code Snippet for the DLL call:
PlugIn = PlugIns.SelectedItem.ToString();
Assembly PlugInDLL = Assembly.LoadFile(#PlugInOrdner+"\\"+PlugIn+".dll");
Object Objekt = PlugInDLL.CreateInstance("DLL.PlugIn");
MethodInfo Info1 = Objekt.GetType().GetMethod("Programm");
Info1.Invoke(Objekt, new Object[]{Projekt, TIAInstanz});
it basically Looks for a DLL file that has the same Name as the highlighted item in the ListBox
There are many different ways to do this. Some of the suggestions in the comments are really good and implementing them would make a robust and extendable solution.
If you are looking for a quick and easy way to get messages from your plugins, though, then you can pass your callback directly to the plugin as an Action:
public class PluginRunner
{
public class PluginMessageEventArgs
{
public string Text { get; set; }
}
public event EventHandler<PluginMessageEventArgs> PluginMessage;
public void Run( string pluginPath )
{
Assembly PlugInDLL = Assembly.LoadFile(pluginPath);
Object Objekt = PlugInDLL.CreateInstance("DLL.PlugIn");
MethodInfo Info1 = Objekt.GetType().GetMethod("Programm");
Info1.Invoke(Objekt, new Object[] { Projekt, TIAInstanz, new Action<string>(Log) });
}
private void Log(string s)
{
PluginMessage?.Invoke(this, new PluginMessageEventArgs { Text = s });
}
}
so you can use it like:
var path =
Path.Combine(
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
"Plugins",
"MyAwesomePlugin.dll");
var pr = new PluginRunner();
// be aware that your event delegate might be invoked on a plugin's thread, not the application's UI thread!
pr.PluginMessage += (s,e) => Console.WriteLine("LOG: " + e.Text);
pr.Run(path);
then your plugin's Programm method writes its logs:
public void Programm( ProjektClass p0, TIAClass p1, Action<string> log )
{
Task.Run(() =>
{
// do something
log.Invoke("here am I!");
// do something else
log.Invoke("here am I again!");
// do something more
});
}
I must admit, that this is not the ideal way to deal with messaging. There are far better (and, unfortunately, more complicated to implement) solutions out there. This one is fairly simple though. Just don't forget that you receive your message on the same thread that have sent it and avoid deadlocks.

C# - How to add code INTO background threads before starting them?

I want to write a helper function start that starts pool threads for me but also adds some code before the actual background processing starts in the very same pool thread. So the background thread must do some extra work. I would call start very often and the extra code might change. So I wanted create a kinda factory for pool threads.
Would that be even possible ? If yes, how would I "inject" code into threads ?
I tried this:
class Program
{
private static void test()
{
Console.WriteLine("hello world");
}
private static void start1(Action param1)
{
ThreadPool.QueueUserWorkItem(o =>
{
// extra work is here
param1.Invoke(); // starts another subthread ?
});
}
private static void start2(WaitCallback param1)
{
ThreadPool.QueueUserWorkItem(param1);
}
static void Main(string[] args)
{
start1(new Action(test));
start2(o => { test(); });
Console.ReadKey();
}
}
ThreadPool.QueueUserWorkItem(o =>
{
ExtraWork();
param1.Invoke();
});
is equivalent to
ThreadPool.QueueUserWorkItem(o =>
{
ExtraWork();
param1();
});
Invoking a delegate does not start a new thread. (Why do you think it might?) So this code works just fine as it is. It will invoke the two functions sequentially.
You cannot add random. net code to run by string value. Unless you play with compiler and take the code, compile it into a DLL then call it with reflection i don't see how it can be done. If that's what you need i am scared to ask how the hell you need to do that.
If you do not require to have random code but specific functions well it's your lucky day. Functions can be pass as parameters. you can also build a list of function to get called in order and do it. That you will need to do more search on that, i haven't done this in years. Or maybe fellow stacker can comment and add link to nice article.
If the code need to be randomly generated and is not forced to be .net, example listing file in directory, opening program. Well you can look into scripting language. Worst case you can build a .vbs (Visual Basic Script) dynamically and run it. Then wait for it to create a text file for the results and read it to know what happened.

Anythings in WinForms Webbrowser like Android Javascript Interface?

Sorry for stupid question,
I just want to pass a JOSN string to the webpage in the webbrowser from my VB/C# code,
and can the javascript in webpage call the VB/C# method through some interface ?
Just like android javascript interface, many thanks
If I understand you correctly,
webBrowser2.ObjectForScripting = new ScriptClass();
webBrowser2.DocumentText = "<html><script>window.external.Test('hello')</script></html>";
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class ScriptClass
{
public void Test(string msg)
{
MessageBox.Show(msg);
}
}
So you're using a WebBrowser control and need to call a JavaScript method within the page it shows? Yeah, you can do this. It's not on the WebBrowser itself, but the HtmlDocument object, exposed by the WebBrowser's Document property, has an InvokeScript() method group, which will allow you to programatically invoke any ECMAScript-compliant method within the DOM of the page, optionally passing a series of parameters as an Object array.
That, in turn, can be used to trigger a call from JavaScript to a JSON web service (which you expose in your main CLR program, and told the page about by passing it the info via InvokeScript()). This is basic JSON/AJAX client-server scripting, all wrapped up into a single program talking to itself. It's not the most efficient way to get things done, but if you already have these layers that get the job done, and just want to release a self-contained app, it works.

Update a control's value from a static class in ASP.NET

Say that I have an ASP.NET page with a Label control and the following static class which executes a scheduled job:
public static class Job
{
// The Execute method is called by a scheduler and must therefore
// have this exact signature (i.e. it cannot take any paramters).
public static string Execute()
{
// Do work
}
}
When the job is done, the execute method should update the value of the Label control on the page.
I've done some research and the only way seems to be to use HttpContext.Current.CurrentHandler. However, this is undesirable for me since it can potentially return null.
Since the Execute method cannot take any parameters (see comment), passing the Page instance as an argument is not an option.
Is there any other way to update the control from the static class?
NOTE: the Execute method must be static because I'm creating an EPiServer scheduled job, which requires a static Execute method (that doesn't take any parameters).
If the job is not executed synchronously (or even if it is), I think that you may want to consider the order of control.
What I suggest in a case like this is a structure similar to the following:
1) The web page issues the request for the job
2) Somewhere, a unique reference to the job is created and stored (such as GUID or an identity column in a database table)
3) The job is executed asynchronously by code-behind and then the unique identifier is returned to the web page.
4) The web page, on startup, initiates a javascript method (using window.timeout, for example) that on a regular basis, issues an ajax query to the web server to check on the status of the job.
5) When the job is complete, it updates the global reference with the appropriate information.
6) When the javascript sees that the job is complete, it updates the label.
This process allows the user to carry on with other work if necessary and not have to worry about timeouts due to long postback times, etc.
For your specific scenario, you could add a GUID property to the Job class (which would be passed back to the client).
When Execute is complete, you could add this GUID to a static collection (i.e. Dictionary<Guid, string>) which the ajax request would check (the string value could store status or completion information).
When the ajax request fires, it would check this static collection and, when it finds its job, remove it and return the value to the caller.
You can create a static property that is updated by your Execute method and bind the Text property of the Label to the static property in the aspx's OnInit method, Label.Text = Job.StaticProperty, if you need a somewhat dynamic response you could use the Ajax Timer Control to call a method on the aspx page to return the same static value from the aspx Page.
public static class Job
{
public static string UpdatedValue { get; private set; } // Or whatever the property is you wish to expose.
// The Execute method is called by a scheduler and must therefore
// have this exact signature (i.e. it cannot take any paramters).
public static string Execute()
{
// Do work
Job.UpdatedValue = "Execute Completed";
}
}
protected override OnInit(EventArgs e)
{
base.OnInit(e);
this.TextLabel.Text = Job.UpdatedValue;
}
// Using MSDN basic sample
protected void Timer1_Tick(object sender, EventArgs e)
{
this.TextLabel.Text = Job.UpdatedValue;
}
Try with a global variable (static) or raise an event from Execute() method.

Categories

Resources