Несоответствие типа HTMLCanvasElement

Я использую VBA для создания сценария экземпляра InternetExplorer с Canvas. Я настроил его, чтобы использовать классы VBA, такие как Circle и Line, для рисования на холсте. Все это работает нормально, и у меня работает анимированный график, направленный силой.

Однако теперь я пытаюсь зафиксировать пользовательские события, такие как щелчки мыши, как события в VBA, и столкнулся со странной проблемой. Я создаю переменную типа HTMLCanvasElement, но когда пытаюсь присвоить ей объект HTMLCanvasElement, получаю ошибку "Несоответствие типов".

ПРИМЕЧАНИЕ. Необходимо добавить ссылки на «Microsoft Internet Controls» для объекта класса InternetExplorer и «Библиотека объектов Microsoft HTML» для объектов класса MSHTML.

Option Compare Database
Option Explicit

Dim ie As InternetExplorer
Dim document As HTMLDocument
Dim WithEvents window As HTMLWindow2
Dim ctx As ICanvasRenderingContext2D
Dim WithEvents canvas As MSHTML.HTMLCanvasElement

Private Sub Class_Initialize()
    Set ie = New InternetExplorer
    ie.StatusBar = False
    ie.AddressBar = False
    ie.MenuBar = False
    ie.Toolbar = False

    ie.Navigate "about:blank"
    While Not ie.ReadyState = READYSTATE_COMPLETE
        DoEvents
    Wend
    ie.Visible = True
    Set document = ie.document
    document.body.innerHTML = "<div><canvas id='myCanvas' width='10' height='10' style=""border:1px solid #000000;"">" & _
            "Your browser does not support the HTML5 canvas tag" & _
            "</canvas></div>"

    'Set canvas = document.getElementById("myCanvas")  'This line triggers a Type Mismatch!!!

    Set ctx = document.getElementById("myCanvas").getContext("2d")

    Set window = document.parentWindow

    resizeCanvas
End Sub

Private Sub window_onload()
    window_onresize
End Sub

Private Sub window_onresize()
    resizeCanvas
End Sub

Public Sub resizeCanvas()
    ctx.canvas.Width = window.innerWidth - 23
    ctx.canvas.Height = window.innerHeight - 23
    redraw
End Sub

Public Function isClosed() As Boolean
    isClosed = window.closed
End Function

Private Sub redraw()
    'redraw shapes using ctx
End Sub

Public Sub Clear()
    ctx.clearRect 0, 0, ctx.canvas.Width, ctx.canvas.Height
End Sub

Я закомментировал строку с несоответствием типов, чтобы вы могли видеть, что все работает с этим исключением.

Я тестировал несколько разных вещей:

  • В окне Watch указан тип document.getElementById("myCanvas") как HTMLCanvasElement, как и ожидалось.
  • ?TypeName(document.getElementById("myCanvas")) в окне интерпретации возвращает "HTMLCanvasElement"
  • ?typeof document.getElementById("myCanvas") is HTMLCanvasElement в окне интерпретации возвращает False. Это определенно странно и заставляет меня задуматься, возможно, HTMLCanvasElement неправильно определен в библиотеке типов mshtml.tlb.

person Blackhawk    schedule 22.09.2016    source источник
comment
Я подумываю о том, чтобы окружить холст Div, а затем использовать HTMLDivElement как способ перехвата событий. Кто-нибудь знает, как обеспечить, чтобы события холста всплывали в окружающий Div?   -  person Blackhawk    schedule 23.09.2016
comment
Глядя на холст в document.all, он относится к типу Variant\Object\HTMLCanvasElement.   -  person ThunderFrame    schedule 23.09.2016
comment
@ThunderFrame именно поэтому я ожидал, что это сработает, но, увы :(   -  person Blackhawk    schedule 23.09.2016


Ответы (2)


Я взял на себя смелость взломать tlb, чтобы посмотреть на себя. Вот как это определяется:

    [id(0x00000440)]
    HRESULT getElementById(
                    [in] BSTR v, 
                    [out, retval] IHTMLElement** pel);

Обратите внимание, что он возвращает указатель интерфейса, а не строго типизированный объект. Это может испортить ваши работы, потому что HTMLCanvasElement нельзя создать:

[
  uuid(305106E5-98B5-11CF-BB82-00AA00BDCE0B),
  noncreatable
]
coclass HTMLCanvasElement {

И ничего в tlb не возвращает экземпляр компонентного класса. Это создает проблему для объявления его WithEvents, потому что IHTMLCanvasElement не является источником событий - они поступают из HTMLElementEvents.

Обратите внимание, что в VBA также нет способа преобразовать между IHTMLElement и HTMLCanvasElement. Похоже, что макет tlb, вероятно, ограничит вас либо разработкой Function Declare, чтобы попытаться обмануть маршаллер, чтобы он вернул вам строго типизированную ссылку на объект, либо работой через какую-то оболочку, которая сделает это.

person Comintern    schedule 22.09.2016

Вы пытались объявить его как IHTMLCanvasElement?

https://msdn.microsoft.com/en-us/library/ff975817(v=vs.85).aspx

person Ambie    schedule 22.09.2016
comment
IHTMLCanvasElement не является источником событий. - person Comintern; 23.09.2016
comment
@Comintern, хороший момент, я полностью пропустил аспект событий. Ничего себе, это становится трудной проблемой для решения! - person Ambie; 23.09.2016