Accessing Silverlight 3.0 App from JavaScript - c#

I'm trying to access properties on the RootVisual object:
[ScriptableType]
public class ApplicationInfo
{
public string Name { get; set; }
public string Version { get; set; }
}
[ScriptableType]
public partial class MainPage : UserControl
{
[ScriptableMember]
public ApplicationInfo ApplicationInfo { get; set; }
public MainPage()
{
InitializeComponent();
this.ApplicationInfo = new ApplicationInfo();
this.ApplicationInfo.Name = "My Application";
this.ApplicationInfo.Version = "0.1";
HtmlPage.RegisterScriptableObject("myapp", this);
}
}
In my ASPX hosting page I've the following JavaScript snippet:
<script type="text/javascript">
function onPluginLoaded(plugin) {
alert('in plugin');
alert(plugin.Name); //This gives me the x:Name of the RootVisual object
var appInfo = plugin.ApplicationInfo;
alert(appInfo);
alert(plugin.myapp);
document.title = appInfo.Name + " " + appInfo.Version;
}
</script>
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%" OnPluginLoaded="onPluginLoaded" >
<param name="source" value="ClientBin/SLVersion.xap"/>
<param name="onload" value="onPluginLoaded" />
This doesn't work. I'd like to know why! Thanks in advance.

There are two things wrong.
Silverlight Documentation
The Silverlight documentation about this area of functionality is really quite confused. Here is thing, the object provided as the sender parameter in the onLoad method isn't what the documentation says that it is, it's not the silverlight plugin.
At least its not the plugin as seen by the HTML DOM / Javascript. It seems to be some form of the Javascript API version of a Framework element. In order to get the plugin object that is useful to us we need to call the getHost method on it.
function onPluginLoaded(sender) {
var plugin = sender.getHost();
}
That gets us one step closer.
Accessing Registered Scriptable Objects
Scriptable objects that have been registered on HTMLPage are accessed as properties of the Plugin's Content property. Hence to access the ApplicationInfo object you would need:-
function onPluginLoaded(sender) {
var plugin = sender.getHost();
var appInfo = plugin.Content.myapp.ApplicationInfo;
alert(appInfo.Name + " " + appInfo.Version);
}
That'll get you going.
ScriptableType
Remove [ScriptableType] from MainPage, in this case you only want to mark specific members as available to script hence you use the [ScriptableMember]. By using [ScriptableType] you expose all public members automatically as scriptable. You are correctly doing that on your ApplicationInfo.

Related

Blazor onended is not firing

I'm starting to work with Blazor. My intention is playing one random Video after another. Therefore i wanted to use the Blazor Event Listener. But the onended Event is not firing (everything is working fine with onclick).
video Element:
<figure id="video-player">
<video autoplay="autoplay" #onended="NewVideo">
#videoUrl
</video>
</figure>
Codeblock:
private MarkupString videoUrl;
protected override void OnInitialized()
{
NewVideo();
}
private void NewVideo()
{
videoUrl = new MarkupString($"<source src=\"videos/{tv.GetRandomVideoFileName()}\" type=\"video/mp4\">");
}
OnInitialized is working as intended, and if I change the onended to onclick everything is also working fine.
To Mention: I know, only changing the source wouldnt start the next Video. That would be my next Task on the List :). First I only want to Change the source in the DOM.
The short story is that this isn't supported, and did not make the v5 release. See the issue here:
https://github.com/dotnet/aspnetcore/issues/24323
The longer story is that you'll have to fire up some JS interop. Reference is here:
https://learn.microsoft.com/en-us/aspnet/core/blazor/call-dotnet-from-javascript?view=aspnetcore-5.0#component-instance-method-call
On the JS side, you'll need something like this:
var player = document.getElementById('player');
player.onended = (e) => {
DotNet.invokeMethodAsync('{assembly_name_here}', 'SongEnded');
};
The assembly name is probably the name of the project, like MyBlazorApp. Then in your component, you'll need a static action to get the thing wired up:
protected override void OnInitialized()
{
action = UpdateMessage;
}
private static Action action;
private void UpdateMessage()
{
// DO STUFF HERE
}
[JSInvokable]
public static void SongEnded()
{
action.Invoke();
}
It's a little clunky, sure, but essentially you're mapping a JS event to a static method on the component. You're probably thinking, "Well, what if I have multiple instances of the component?" In that case, you'll have to be a little creative about passing context to the static method and finding the right instance. Again, check the reference above, it has plenty of examples.
Came up with a solution to avoid static method. In code part we need to hold reference of DotNetObjectReference and also some index of it (that will play part in JS), this is example class:
public abstract class VideoPlayerBase : ComponentBase, IDisposable
{
[Parameter] public string Source { get; set; }
[Parameter] public EventCallback VideoEndedCallback { get; set; }
[Inject] protected IJSRuntime JS { get; set; }
/// <summary>
/// DotNetObjectReference of current component instance.
/// This is used for catching `onended` event on video tag (which apparently is not supported by Blazor)
/// </summary>
private DotNetObjectReference<VideoPlayerBase>? _dotNetObjectReference;
/// <summary>
/// Index of DotNetObjectReference inside BlazorApp.dotNetObjectReferences array.
/// This is used to be able to relevant DotNetObjectReference from PriskApp.dotNetObjectReferences array.
/// </summary>
protected int DotNetObjectReferenceIndex { get; set; } = -1;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_dotNetObjectReference = DotNetObjectReference.Create(this);
DotNetObjectReferenceIndex = await JS.InvokeAsync<int>("BlazorApp.addDotNetObjectReference", _dotNetObjectReference);
}
[JSInvokable("VideoEnded")]
public async Task VideoEndedAsync()
{
await VideoEndedCallback.InvokeAsync(null);
}
public void Dispose()
{
_dotNetObjectReference?.Dispose();
}
}
Then we need js part:
var BlazorApp = BlazorApp || {
dotNetObjectReferences: [],
addDotNetObjectReference: function (dotNetObjectReference) {
PriskApp.dotNetObjectReferences.push(dotNetObjectReference);
return PriskApp.dotNetObjectReferences.length - 1;
},
getDotNetObjectReference: function (index) {
return PriskApp.dotNetObjectReferences[index];
},
};
And lastly the component view:
#inherits VideoPlayerBase
<div>
<video style="width:100%;" controls onended="(BlazorApp.getDotNetObjectReference(#DotNetObjectReferenceIndex).invokeMethodAsync('VideoEnded'))()">
<source src="#Source" type="video/mp4" controls>
Your browser does not support HTML video.
</video>
</div>
So basically this work by creating DotNetObjectReference in OnInitializedAsync method and then calling js function BlazorApp.addDotNetObjectReference to add this reference to some js array and to get index of it, which is used in view part. Then once Video ends onended event is triggered and inside it it gets the relevant DotNetObjectReference and calls its method.

How to bind and call method of c# obejct in React component class

I am having a Cefsharp container in which I am loading a react app hosted on my local node server. I have regeisterd a c# object in my .net project as shown below:-
browser.JavascriptObjectRepository.Register("bound", new Client(), isAsync: true, options: BindingOptions.DefaultBinder);
now in one of my react component class I want to refer a method of that C# object. What I am doing is I am trying to access that using
componentWillMount() {
this.invoke();
}
async invoke() {
const message = await window.bound.message();
this.setState({selectedItem: message});
}
And here is my C# object:-
public class Client
{
public string Id { get; set; }
public string Name { get; set; }
public string Message()
{
return "ClientABCD";
}
}
But this is not working. I am getting "bound is not defined error". I have gone through dozens of articles but most of them are referring to CefSharp library in their HTML file which is part of their .Net project. But in my case I don't have any HTML file nor I have any CefSharp library at react side. Does 'bound' gets bind with window object in javascript? Please suggest.

Error of context in .aspx when calling c# methods

In my front end, I'm trying to call methods dynamically, but I keep getting errors of contexts.
My code looks like this and the error is given by my "i":
.aspx :
<script src="https://PCYULD0029:8012/Maps/leaflet.js"></script>
<script >
... some code
AllObject = "<%=(ParseMapObjects())%>";
L.marker([0, 0], 0).addTo(map).bindPopup("test");
for (var i = 0; i < 2; i++)
{
ObjLongitude = AllObject[i].Longitude;
ObjLatitue = AllObject[i].Latitude;
CreateMarkers(ObjLongitude, ObjLatitude)
L.marker([0, 0], 0).addTo(map).bindPopup("test");
L.marker([0, 50], 0).addTo(map).bindPopup(ObjLatitue);
}
function CreateMarkers(ObjLong, ObjLat) {
L.marker([ObjLong, ObjLat], 0).addTo(map).bindPopup("Test");
}
... some code
</script>
.aspx.cs :
public List<MapObjectEntity> ParseMapObjects()
{
List<MapObjectEntity> MapObjects = new List<MapObjectEntity>();
.... Some code
return MapObjects;
}
public class MapObjectEntity
{
// Properties
public Guid MapObjectGuid { get; set; }
public string Data { get; set; }
public int Latitude { get; set; }
public Guid Link { get; set; }
public int Longitude { get; set; }
public int RelativeHeight { get; set; }
public int RelativeWidth { get; set; }
public int Rotation { get; set; }
public bool ObjectShowFov { get; set; }
public Guid MapObjectType { get; set; }
}
So, in ParseMapObjects, I return a List of MapObjectEntity. I then try to catch that in my .aspx (frontend), so that I can use it as an object (ex: Object.property).
With the line AllObject = "<%=(ParseMapObjects())%>"; I succeed at pulling the object, but I can't seem to find a way to use AllObject to get my properties (ex: AllObject[0].Latitude).
My objective here is to loop through my AllObject and extract all the Latitude and Longitude values. But I can
thank you
Unless ParseMapObjects() returns a JSON string (something like caner's answer)
this won't work.
And bear in mind, once the page has loaded, this value will be fixed, since it's the result of the C# method that is output to the page, not a reference to the method. If you run the Javascript multiple times, the value assigned to AllObject on that line will stay fixed, because it's basically a static object in the JS context (check the View Source of your browser to see what is output).
You can't call a C# method directly from JavaScript, and vice versa. C# executes on the server and constructs the HTML, CSS and JS to output to the browser. JavaScript runs in the client's browser after the page (i.e. the content generated by the C#) has been downloaded from the server to the browser and initialised. They are in totally separate environments (usually on different computers) and intrinsically have no knowledge of each other. This separation is a vital concept to grasp if you want to develop web applications.
If you want communication between the client (browser) and the server outside of the normal page load / refresh / postback mechanism, then you need to use AJAX functionality, or even, if you want real-time communication, something like WebSockets and/or SignalR.
You need serialization...
public string ParseMapObjects()
{
List<MapObjectEntity> MapObjects = new List<MapObjectEntity>();
return new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(MapObjects);
}
and access in your script like
var AllObject = <%=(ParseMapObjects())%>;

Create C# Wrapper for Javascript Library

I'm looking for an automated way to create a simple C# wrapper for an existing JavaScript library. I want to create a string of JavaScript using a C# class on the server, then send the JavaScript to the client.
Example:
I’m creating charts using the KendoUI JavaScript library. I’m creating the JavaScript to render the charts server side because a single page can render infinitely many charts based on how the user chooses to configure the chart.
The JavaScript string that is being generated server side looks like this (greatly simplified for example):
function createChart() {
$("#chart").kendoChart({
title: {
text: "Chart Title",
},
series: [{
field: "Cost"
}],
categoryAxis: {
field: "StartDate",
}
});
}
Right now my C# code is simply building a string to create the JavaScript string (greatly simplified for example):
private string CreateChartJS(string ChartTitle, string SeriesField, string CategoryField)
{
return "function createChart() { $(\"#chart\").kendoChart({ title: { text: \""+ChartTitle+"\", }, series: [{ field: \""+SeriesField+"\" }], categoryAxis: { field: \""+CategoryField+"\", } }); } ";
}
What I'm looking for is some type of converter/generator tool that can automatically create a simple C# wrapper class from the JavaScript library source code that I'm using. I am well aware of the fact that Telerik already makes a version of their charts for ASP.NET that includes custom made server side wrappers, this is not what I'm looking for as I'm just using the KendoUI as an example.
In the Chrome web developer tools console I can examine the created KendoUI Chart and view all of its properties:
The tool I'm looking for should be able to create a simple C# class out of the properties that are exposed in the screenshot above (in this case, the group of properties I'm looking to configure are the child items of the 'Options' object).
Example of what the generated C# class might look like (greatly simplified for example):
public class KendoChartWrapper
{
public class options
{
public bool autoBind { get; set; }
public class axisDefaults { }
public class categoryAxies { }
public class chartArea { }
public class dataSource { }
public class legend { }
public string name { get; set; }
public class navigator { }
public class plotArea { }
public string prefix { get; set; }
public string renderAs { get; set; }
}
}
This would allow me to create my chart JavaScript by creating an instance of the class in C# and serializing the result. What I'm looking for should be able to create these simple C# classes given the source code of any javascript library (examples: jQuery, jQuery UI, etc...).
I'm guessing that there is already an existing tool that can do this, I just don't know what it is called. I'm hoping someone can point me in the right direction.
Is it just the options you want as a JavaScript object? If it is just the options you want then on the server you can serialize the object to a JSON using:
string json = Newtonsoft.Json.JsonConvert.Serialize(YourObject)
Then when you receive this at the Javascript end you can turn this into a JavaScript object and use it for your chart options using:
var options = JSON.parse(json);
$("#chart").kendoChart(options);
Available on modern browsers and you can get a fallback for older browsers
I found exactly what I was looking for: http://jsonclassgenerator.codeplex.com/

Why can't I bind a property in .aspx while it is feasible in .xaml

The Error I am getting is:
The name 'strTitle'(name of propert) does not exist in the current context
To make it very clear I will show the code:
Silverlight:
var myData =from xAudioinfo in xResponse.Descendants(ns + "DIDL-Lite").Elements(ns + "item")
select new RMSMedia
{
strTitle =((string)xFolderInfo.Element(dc + "title")).Trim(),
objectID = (int)xFolderInfo.Attribute("id")
};
Now I am able to bind this in my XAML like below:
<TextBlock Text ="{Binding strTitle}" />
But this is not working in ASP.NET.
ASP.NET
var myData =from xAudioinfo in xResponse.Descendants(ns + "DIDL-Lite").Elements(ns + "item")
select new RMSMedia
{
strTitle =((string)xFolderInfo.Element(dc + "title")).Trim(),
objectID = (int)xFolderInfo.Attribute("id")
};
Trying to bind with HTML Control:
<asp:Label ID="lbWindCondition" runat="server" Text=<%#strTitle %>></asp:Label>
Edit:#Prisoner ZERO
My code structure is as below:
public class currentWeatherCondition
{
public string condition { get; set; }
public string temp { get; set; }
public string imageURL { get; set; }
public string humidity { get; set; }
public string windCondition { get; set; }
}
public partial class _Default : System.Web.UI.Page
{
protected void btnGetDetails_Click(object sender, EventArgs e)
{
try
{
var weatherXML = XDocument.Load(weatherURL);
var weatherResult = from weatherDetail in weatherXML.Descendants("current_conditions")
select new currentWeatherCondition
{
condition = ((string)weatherDetail.Element("condition").Attribute("data")).Trim(),
temp = ((string)weatherDetail.Element("temp_c").Attribute("data")).Trim(),
imageURL = ((string)weatherDetail.Element("icon").Attribute("data")).Trim(),
humidity = ((string)weatherDetail.Element("humidity").Attribute("data")).Trim(),
windCondition = ((string)weatherDetail.Element("wind_condition").Attribute("data")).Trim(),
};
}
catch(..)
{
......
}
}
So do we mean I will directly create the prpoerty in _Default class or create object of currentWeatherCondition in default class and then bind/render it to the control.
I would have to see the rest of your code, but I would guess the value "strTitle" exists solely within the scope of the RMSMedia block. You must (then) expose that value as a public property in one of four ways:
From a public property of the page.
From a public property of the RMSMedia object.
Write a databinding function for the container.
Set the value directly
Just in case you don't know what that means...
(1) From a public property of the page.
public String Title { get; set; }
Then set strTitle to Title and <%=Title %>.
(2) From a public property of the RMSMedia object.
Then set myData.strTitle thusly <%=myData.strTitle %>.
(3) Write a databinding function for the container.
myContainer.DataBinding += new EventHandler(myContainer_DataBinding);
protected void myContainer_DataBinding(object sender, EventArgs e)
{
myContainer.DataSource = myData.strTitle;
}
To call this last one you would use the following: myContainer.DataBind();
(4) Set the value directly within your function
lbWindCondition.Text = myData.strTitle;
UPDATE:
There are a bunch of options, so really, you just choose one. Even though it has nothing to do with the control in question, I added the 3rd one above just as an "extra" to show other ways to bind in ASP.NET. The 4th option is the most obvious answer though because you set the Text value directly (meaning, without indirection).
So do we mean I will directly create the property in _Default class?
Yes...if you like that option.
<asp:Label ID="lbWindCondition" runat="server" Text=<%=Title %>></asp:Label>
Or do we create object of currentWeatherCondition in default class and then it to the control?
Yes...if you like that option.
<asp:Label ID="lbWindCondition" runat="server" Text=<%#currentWeatherCondition.YourProperty %>></asp:Label>
You can also use the DataBinder object, but I usually reserve that for collections of objects (plus folks tell me to avoid it because it is slow...my guess is that it is slow because it must use reflection alongside late-binding to expose the property.). However, you certainly can use it.
Lastly, none of the answers above really discuss which option is the RIGHT choice. In all honesty, anytime you use the indirection operators (<% %>) you utilize late-binding (which is bad). Now folks will tell you that ASP.NET is (now) optimized for late-binding so it's okay...but obviously it's still late binding...so if you "can" do so, set the value directly or use the data-binding option.
I hope this helps!
The Label ASP.NET Control does not have DataSource Property so that it is not possible to directly databind it against anonymous object. What you need to do is
1) Create a page container property which would hold the myData collection
2) Bind the Label against that property. =>
Text = '<%# DataBinder.Eval(ContainerObject, "strTitle"); %>'
I hope that this helps :)

Categories

Resources