custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Please post public support tickets here. Note: for private support tickets, please send an email to support@cshtml5.com instead.
Sesztak
Posts: 172
Joined: Fri Jun 24, 2016 2:19 am

custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby Sesztak » Wed Oct 12, 2016 12:12 pm

Dear JS-Support,

We have a fundamental question:

We would like to implement some custom control (inherited from Control or UserControl).

This custom controls or userControl controls use 3rd party javascript libraries, as e.g. jQuery or jQuery UI.

To handle native html CSHTML5 advise to use following pattern:
1.) -in control constructor set the HtmlRepresentation by CSharpXamlForHtml5.DomManagement.SetHtmlRepresentation(),
+add Loaded even.
-in Control_Loaded event: check it is in visual tree and add actions, like: (Action<object>)this.OnChanged...

2.) So, normally the custom control instantiated (constructor hitted !) before your other advise occured:
Page_Loaded => LoadCssFile () & LoadJavaScriptFile().

According 1.) & 2.) : there is an antagonism / contradiction :
If you do that in that order (first point 1. and 2. point after): wo will get exception: external javascript and/or css not yet loaded.
How to use it ?

The opposite way (point 2. first, and point 1 after) is not possible or not known by us.

So, our question: how to do both : implement custom control based on 3rd party *.js / *.css ?
What is the preferred order, policy ?

Thanks in advance,
Br,
Péter

JS-Support @Userware
Site Admin
Posts: 1142
Joined: Tue Apr 08, 2014 3:42 pm

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby JS-Support @Userware » Fri Oct 14, 2016 3:26 am

Hi Péter,

- Even though "SetHtmlRepresentation" is done in the constructor, the string you pass to it is used only later when the element is added to the Visual Tree.

- Additionally, the string you pass to "SetHtmlRepresentation" usually does not contain anything that requires particular scripts: in fact, even though they may have IDs that may be used later by some scripts, the stuff you add usually are things such as DIVs, <input> tags, and other standard HTML dom elements. If you need to add anything else special, that requires a script to be loaded before, please let me know.

Thanks.
Regards,
JS-Support

Sesztak
Posts: 172
Joined: Fri Jun 24, 2016 2:19 am

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby Sesztak » Fri Oct 14, 2016 3:50 am

Dear JS-Support,
thanks for your kind reply:
"script to be loaded before, please let me know.": this is what we need exactly ! :)
Would you be so kind to help us, how to do that ?

Thanks in advance,
Br,
Péter

JS-Support @Userware
Site Admin
Posts: 1142
Joined: Tue Apr 08, 2014 3:42 pm

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby JS-Support @Userware » Fri Oct 14, 2016 3:56 am

Hi Péter,

By "please let me know", I mean please let me know what you need to pass to "SetHtmlRepresentation" that requires a script to be loaded before.

Thanks.
Regards,
JS-Support

kmatt
Posts: 30
Joined: Wed Feb 01, 2017 11:16 am

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby kmatt » Thu Sep 07, 2017 6:18 am

I have an example I need this type of functionality for. I am trying to subclass the Kendo UI controls, I'm starting with button using MVVM. The actual button gets instantiated in xaml like so:
xmlns:kendoUI="clr-namespace:GUI.Controls.Kendo"
<kendoUI:KendoUIButton x:Name="OK" Caption="OK"/>

The button code derives currently from FrameworkElement:
namespace GUI.Controls.Kendo
{
public partial class KendoUIButton : FrameworkElement
{
public KendoUIButton()
{
InitializeComponent();
CSharpXamlForHtml5.DomManagement.SetHtmlRepresentation(this, $@"<button id=""{Name}"" Name=""{Name}"" data-bind=""events:{{click: listener, dblclick: listener, mouseover: listener, mouseout: listener}}"" class=""k-button k-primary""/>");
Loaded += KendoUIButton_Loaded;
}
Name however does not have a value yet in the constructor.
private void KendoUIButton_Loaded(object sender, RoutedEventArgs e)
{
var control = (KendoUIButton)sender;
CSharpXamlForHtml5.DomManagement.SetHtmlRepresentation(control, $@"<button id=""{Name}"" Name=""{Name}"" data-bind=""events:{{click: listener, dblclick: listener, mouseover: listener, mouseout: listener}}"" class=""k-button k-primary""/>");
Interop.ExecuteJavaScript($@"
$(document).ready(function() {{
var viewModel = kendo.observable({{
text: ""{Caption}"",
listener: function(e) {{$0(e);}}
}});
kendo.bind($(""#{Name}""), viewModel);
}});",
(Action<object>)OnButtonCallback);
}
public void OnButtonCallback(object e){}
}
}
If we leave it like this, we get a blank button that does not respond to the click, but no errors. We tried replacing $(""#{Name}"") with document.getElementsByName, it returns a zero length array. If we do not have the second SetHtmlRepresentation call, the button has no id or name since the Name property is not valid yet in the constructor. How can we accomplish this?
Attachments
Capture.JPG
Capture.JPG (72.63 KiB) Viewed 24350 times

JS-Support @Userware
Site Admin
Posts: 1142
Joined: Tue Apr 08, 2014 3:42 pm

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby JS-Support @Userware » Thu Sep 07, 2017 6:54 am

Hi,

If you need to set the Html at a later time (later than the constructor), you can follow this alternative approach:

1. Inherit from "HtmlPresenter" instead of "Control" or "UserControl"
2. Call:

Code: Select all

this.Html = @"<Enter your Html here>";


Alternatively, if you want to inherit from UserControl, you can do so and declare a <HtmlPresenter/> in the XAML of the UserControl.

The documentation for the HtmlPresenter control can be found at: http://cshtml5.com/links/how-to-use-the-htmlpresenter.aspx

Thanks.
Regards,
JS-Support

kmatt
Posts: 30
Joined: Wed Feb 01, 2017 11:16 am

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby kmatt » Wed Sep 13, 2017 7:23 am

FYI, when I inherit from HtmlPresenter:

namespace GUI.KendoExtension
{
public partial class KendoUIButton : HtmlPresenter
{
...
}
}

I get:

Exception thrown: 'System.InvalidCastException' in CSharpXamlForHtml5.dll

Additional information: Unable to cast object of type 'GUI.KendoExtension.KendoUIButton' to type 'Windows.UI.Xaml.Controls.ContentControl'.

TaterJuice
Posts: 147
Joined: Thu Mar 16, 2017 5:40 am
Contact:

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby TaterJuice » Wed Sep 13, 2017 7:44 am

kmatt wrote:FYI, when I inherit from HtmlPresenter:
Exception thrown: 'System.InvalidCastException' in CSharpXamlForHtml5.dll

Additional information: Unable to cast object of type 'GUI.KendoExtension.KendoUIButton' to type 'Windows.UI.Xaml.Controls.ContentControl'.


What does your XAML look like? That error says you're trying to cast your KendoUIButton class to a ContentControl, and that must be happening somewhere else in your code - your KendoUIButton is not a ContentControl, its a UserControl which derives from an HTMLPresenter. You can't cast it to a ContentControl, but you can use it as a DataTemplate for a ContentControl or ContentPresenter, or for items in an ItemsPresenter (like ListBox).

kmatt
Posts: 30
Joined: Wed Feb 01, 2017 11:16 am

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby kmatt » Wed Sep 13, 2017 8:10 am

It happened here:

public sealed partial class App : Application
{
/// <summary>
/// Initializes a new instance of the <see cref="App" /> class.
/// </summary>
public App()
{
InitializeComponent();
try
{
var mainPage = new MainPage();
Window.Current.Content = mainPage; <---


Where
<Page x:Class="GUIProject.MainPage"
and inside of it was the KendoUIButton control:
<kendoUI:KendoUIButton x:Name="LoginButton" Caption="Login"/>
I got the same kind of casting error when I tried to inherit from UserControl as well. ButtonBase however works, since it derives from ContentControl.

TaterJuice
Posts: 147
Joined: Thu Mar 16, 2017 5:40 am
Contact:

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby TaterJuice » Wed Sep 13, 2017 11:37 am

So, if I understand you correctly, you're trying to create a clickable button which can have dynamic HTML content, including custom\external JS libraries and custom\external CSS?

kmatt
Posts: 30
Joined: Wed Feb 01, 2017 11:16 am

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby kmatt » Wed Sep 13, 2017 12:25 pm

I'm just trying to wrap the Kendo UI javascript/html5 Button element. I tried the Button first thinking it would be simplest and I could use what I learned and extend it to the other Kendo UI components. I want to wrap a number of them including the grid, I was thinking I would release it as an extension after getting it to work. As you can see, I did not cast that control to a ContentControl or anything derived from it, but I believe that app.xaml.cs is pretty much your demo code. I've tried every combination of UserControl, HtmlPresenter, ContentControl and ButtonBase I can find, they all have some problem. Using ButtonBase or ContentControl with an HtmlPresenter inside I can see the Kendo UI button, but I also see the ContentControl behind it on the GUI and I can't get rid of it. Using UserControl with HtmlPresenter inside or just plain HtmlPresenter as the control type I inherit from gives the error I showed above.

TaterJuice
Posts: 147
Joined: Thu Mar 16, 2017 5:40 am
Contact:

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby TaterJuice » Wed Sep 13, 2017 1:46 pm

Can you share a sample project like what you just described? I think I might misunderstand your issue, but I'll give it a try anyway. A Sample project would be helpful in helping you troubleshoot.

-------

So, I've never used KendoUI, and I'm not sure I understand your issue completely, but I do know how to work with the CSHTML5 Interop to build a plugin for a JS library...

For my (SUPER CONTRIVED) test, I've created a custom control with the following specifications, class KendoUIButton.
1. A Button who's content is made of HTML, CSS and Javascript
2. Control the HTML and CSS content of the button with calls to Javascript functions (for your needs, you'll replace my JS functions with KendoUI functions)

Our goal is to dynamically update the Button's HTML content and CSS properties via JS Interop (we will give it a random string of text between 5 and 10 characters in length, and a randomized css color name from a predefined list of 5 colors)

Keep in mind - Changing a Control's Content and Foreground color can be done MUCH simpler using the built-in dependency properties, bindings, visual states, styles, events, and much more. This is just an example of interacting with JS Interop in order to build a plugin for a JS library.

This control is defined as follows:

C#

Code: Select all

using CSHTML5;
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace HTMLButtonTest
{
    public partial class KendoUIButton : UserControl
    {
        private Random rand = new Random();
        public KendoUIButton()
        {
            this.InitializeComponent();
            this.ButtonControl.Visibility = Visibility.Collapsed; // hide the button until we populate its content
            this.Loaded += KendoUIButton_Loaded;
        }

        private void KendoUIButton_Loaded(object sender, RoutedEventArgs e)
        {
            ChangeButtonWithJSInterop("Click Me!", "black");
        }
        private void ChangeButtonWithJSInterop(string newHtmlContent, string cssColorname)
        {
            if(ButtonControl.Visibility != Visibility.Visible)
                ButtonControl.Visibility = Visibility.Visible;
            //Set the html content via javascript
            Interop.ExecuteJavaScript(@"$1.innerHTML = $0;", newHtmlContent, Interop.GetDiv(HTMLControl));
            //Set the css color via javascript
            Interop.ExecuteJavaScript(@"$1.style.color = $0;", cssColorname, Interop.GetDiv(HTMLControl));
        }

        private void ButtonControl_Click(object sender, RoutedEventArgs e)
        {
            //On Click, set the button's HTML content to a random string of text between 5 and 10 characters long, and a random CSS color name
            ChangeButtonWithJSInterop(new StringGenerator().MakeString(rand.Next(5,10)), GetRandomCSSColorName());
        }
        private string GetRandomCSSColorName()
        {
            switch(rand.Next(1,5))
            {
                case 1: return "blue";
                case 2: return "red";
                case 3: return "green";
                case 4: return "purple";
                default: return "black";
            }
        }
    }
}


XAML

Code: Select all

<UserControl
    x:Class="HTMLButtonTest.KendoUIButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:HTMLButtonTest">
    <Button
        x:Name="ButtonControl"
        Click="ButtonControl_Click"
        Margin="10"
        HorizontalAlignment="Center"
        VerticalAlignment="Center">
        <native:HtmlPresenter
            x:Name="HTMLControl"
            xmlns:native="using:CSHTML5.Native.Html.Controls"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            mc:Ignorable="native" />
    </Button>
</UserControl>


Full sample project, including supporting Code for my "StringGenerator" class, can be found in the .zip archive of my sample project, which I've uploaded here: https://www.dropbox.com/s/fcsaj5uwh6mhcfh/HTMLButtonTest.zip?dl=0

Conclusion:
Assuming I understood you correctly - this is a working example of interacting with CSS and Javascript to build a JS extension. The next steps would be to use Interop.LoadJavascriptFile("hostedurl") to load your external KendoUI library, and then start replacing your Javascript functions - ie, replace ""$1.innerHTML = $0;" with "$1.someKendoUIJSFunction($0);"

kmatt
Posts: 30
Joined: Wed Feb 01, 2017 11:16 am

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby kmatt » Thu Sep 14, 2017 4:45 am

Awesome, thank you so much!!!

kmatt
Posts: 30
Joined: Wed Feb 01, 2017 11:16 am

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby kmatt » Thu Sep 14, 2017 6:05 am

I tried this with my control. The problem is that you aren't actually wrapping a javascript visual control, you are setting properties on the HtmlPresenter control or the Xaml Button control, I'm not totally sure. If your InnerHtml had included <button/> code, you would see what I see - a grey button (from the xaml Button) in the back (underneath) the third party button control (the one presented with the innerhtml). I could already get this result as I stated before.
I tried your code WITHOUT the xaml Button control, using only the HtmlPresenter inside the UserControl, I still see the same type of thing - a clickable grey UserControl with the third party button on top of it. Setting the background and foreground of the UserControl to Transparent doesn't help, either. The only workable solution I can see would be to use the HtmlPresenter as the inherited class, but that gives the error in your code in App.xaml.cs.

TaterJuice
Posts: 147
Joined: Thu Mar 16, 2017 5:40 am
Contact:

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby TaterJuice » Thu Sep 14, 2017 7:13 am

Ah, I understand. Can you share a sample project where you have the KendoUI Button displaying, and give me an example of something you want to DO with the button (ie, what do you to do when the button is clicked?). I'm pretty sure I can help get you going.

kmatt
Posts: 30
Joined: Wed Feb 01, 2017 11:16 am

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby kmatt » Thu Sep 14, 2017 12:43 pm

Here you go.
Attachments
HTMLButtonTest (2).zip
(10.39 MiB) Downloaded 436 times

TaterJuice
Posts: 147
Joined: Thu Mar 16, 2017 5:40 am
Contact:

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby TaterJuice » Mon Sep 18, 2017 8:42 am

kmatt wrote:Here you go.

Working on this, but encountered a bug that I've reported here:
http://forums.cshtml5.com/viewtopic.php?f=5&p=9563#p9563

TaterJuice
Posts: 147
Joined: Thu Mar 16, 2017 5:40 am
Contact:

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby TaterJuice » Mon Sep 25, 2017 9:47 pm

So your issue with the extra <Button /> seems to be something kendoUI is doing, but has an easy fix. I added the following style to the page and the extra button disappears:

Code: Select all

//HideExtraButton.css
.k-button + button {
    display:none;
}


Image

Code: Select all

 await Interop.LoadCssFile("ms-appx:///HTMLButtonTest/Styles/HideExtraButton.css");


That said, I can't get any of your kendoUI Listeners to attach using JS Interop. The functions all work in the simulator, but they produce an exception in the JS output:

Uncaught Error: The function 'System.Void HTMLButtonTest.KendoUIButton::<OnDependanciesLoaded>b__3_0()' could not be translated.


I've tried a variety of solutions, but as soon as I execute kendo JS or pass an Action to the button listeners, I receive the above error.

Code: Select all

        private void OnDependanciesLoaded()
        {
            this.Dispatcher.BeginInvoke(() =>
            {
                var buttontext = String.Format(@"<button type='button' id='{0}' name='{0}' data-bind='events:{{click: listener, dblclick: listener}}' class='k-button k-primary'>{1}<button/>", Name, Caption);
                HTMLControl.Html = buttontext;
                Interop.ExecuteJavaScript(@"
                    window.jQuery('#" + Name + @"').click(function() {
                        alert('Clicked!');
                    });", (Action)OnButtonCallback);

                //Uncaught Error: The function 'System.Void HTMLButtonTest.KendoUIButton::<OnDependanciesLoaded>b__3_0()' could not be translated.
                //Interop.ExecuteJavaScript(String.Format(@"
                //    window.jQuery(document).ready(function() {{
                //        var viewModel = kendo.observable({{
                //            text: '{0}',
                //            listener: function() {{
                //                alert('Clicked!');
                //            }}
                //        }});
                //        kendo.bind(document.getElementById('{1}'), viewModel);
                //    }});", Caption, Name));

                //Uncaught Error: The function 'System.Void HTMLButtonTest.KendoUIButton::<OnDependanciesLoaded>b__3_0()' could not be translated.
                //Interop.ExecuteJavaScript(String.Format(@"
                //    window.jQuery(document).ready(function() {{
                //        var viewModel = kendo.observable({{
                //            text: '{0}',
                //            listener: function(e) {{
                //              $0(e)
                //            }}
                //        }});
                //        kendo.bind(document.getElementById('{1}'), viewModel);
                //    }});", Caption, Name), (Action<object>)OnButtonCallback);

                //Uncaught Error: The function 'System.Void HTMLButtonTest.KendoUIButton::<OnDependanciesLoaded>b__3_0()' could not be translated.
                //Interop.ExecuteJavaScript(String.Format(@"
                //    window.jQuery(document).ready(function() {
                //        var viewModel = kendo.observable({
                //            text: '{0}',
                //            listener: $0
                //        });
                //        kendo.bind(document.getElementById('{1}'), viewModel);
                //    });", Caption, Name), (Action<object>)OnButtonCallback);

                //Uncaught Error: The function 'System.Void HTMLButtonTest.KendoUIButton::<OnDependanciesLoaded>b__3_0()' could not be translated.
                //Interop.ExecuteJavaScript(@"
                //    window.jQuery('#" + Name + @"').click(function() {
                //        $0()
                //    });", (Action<object>)OnButtonCallback);

            });
        }


I've attached the updated project. Yay, we hid the button! :-/

https://www.dropbox.com/s/zoamzda88zcabq1/htmlbuttontest3.zip?dl=0

JS-Support @Userware
Site Admin
Posts: 1142
Joined: Tue Apr 08, 2014 3:42 pm

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby JS-Support @Userware » Tue Sep 26, 2017 12:46 am

TaterJuice wrote:I receive the above error


Please make sure that the first argument of the method ExecuteJavaScript is a string "literal", which means that its value is known ad design-time rather than runtime. For example, you cannot concatenate two strings with "string1" + "string2", and you also cannot use the "String.Format" method. Instead, if you want to pass content that changes at runtime, you need to pass it via the other arguments of the method, and use $0, $1, etc.

To better understand this issue and how to fix it, please read the paragraph that says "IMPORTANT" on the following page:
http://cshtml5.com/links/how-to-call-javascript.aspx

Thanks.
Regards,
JS-Support

TaterJuice
Posts: 147
Joined: Thu Mar 16, 2017 5:40 am
Contact:

Re: custom control or UserControl vs 3rd party javascript library as e.g. jQuery

Postby TaterJuice » Tue Sep 26, 2017 7:12 am

JS-Support wrote:
TaterJuice wrote:I receive the above error


Please make sure that the first argument of the method ExecuteJavaScript is a string "literal", which means that its value is known ad design-time rather than runtime. For example, you cannot concatenate two strings with "string1" + "string2", and you also cannot use the "String.Format" method. Instead, if you want to pass content that changes at runtime, you need to pass it via the other arguments of the method, and use $0, $1, etc.

To better understand this issue and how to fix it, please read the paragraph that says "IMPORTANT" on the following page:
http://cshtml5.com/links/how-to-call-javascript.aspx

Thanks.
Regards,
JS-Support


Thanks for the info, I've adjusted the calls to Interop but I still can't get it to work in the JS output. I was using String.Format() to try to work around the following issues. Here's what I've tried:

Code: Select all

Interop.ExecuteJavaScript(@"
   window.jQuery(document).ready(function() {
      var viewModel = kendo.observable({
         text: $0,
         listener: function() {
            alert('Clicked!');
         }
      });
      kendo.bind(document.getElementById($1), viewModel);
   });", Caption, Name);

Simulator: Works
JS Output:
Uncaught TypeError: this.get_Caption is not a function
at HTMLDocument.<anonymous> (HTMLButtonTest.js?20179260809:154)
at j (jquery-3.1.1.min.js:2)
at k (jquery-3.1.1.min.js:2)



Code: Select all

Interop.ExecuteJavaScript(@"
   window.jQuery(document).ready(function() {
      var viewModel = kendo.observable({
         text: '$0',
         listener: function() {
            alert('Clicked!');
         }
      });
      kendo.bind(document.getElementById($1), viewModel);
   });", Caption, Name);

Simulator: Works
JS Output:
Uncaught SyntaxError: Unexpected identifier

Uncaught TypeError: Cannot read property 'App' of undefined
at runMain (index.html:32)
at browserFinishedLoadingCallback (JSIL.Browser.js?20179260812:1138)


Return to “Technical Support”

Who is online

Users browsing this forum: No registered users and 3 guests