HtmlControl.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. // "Therefore those skilled at the unorthodox
  2. // are infinite as heaven and earth,
  3. // inexhaustible as the great rivers.
  4. // When they come to an end,
  5. // they begin again,
  6. // like the days and months;
  7. // they die and are reborn,
  8. // like the four seasons."
  9. //
  10. // - Sun Tsu,
  11. // "The Art of War"
  12. using System;
  13. using Avalonia.Controls.Primitives;
  14. using Avalonia.HtmlRenderer;
  15. using Avalonia.Input;
  16. using Avalonia.Interactivity;
  17. using Avalonia.Media;
  18. using Avalonia.Threading;
  19. using Avalonia.VisualTree;
  20. using TheArtOfDev.HtmlRenderer.Core;
  21. using TheArtOfDev.HtmlRenderer.Core.Entities;
  22. using TheArtOfDev.HtmlRenderer.Avalonia;
  23. using TheArtOfDev.HtmlRenderer.Avalonia.Adapters;
  24. namespace Avalonia.Controls.Html
  25. {
  26. /// <summary>
  27. /// Provides HTML rendering using the text property.<br/>
  28. /// Avalonia control that will render html content in it's client rectangle.<br/>
  29. /// The control will handle mouse and keyboard events on it to support html text selection, copy-paste and mouse clicks.<br/>
  30. /// <para>
  31. /// The major differential to use HtmlPanel or HtmlLabel is size and scrollbars.<br/>
  32. /// If the size of the control depends on the html content the HtmlLabel should be used.<br/>
  33. /// If the size is set by some kind of layout then HtmlPanel is more suitable, also shows scrollbars if the html contents is larger than the control client rectangle.<br/>
  34. /// </para>
  35. /// <para>
  36. /// <h4>LinkClicked event:</h4>
  37. /// Raised when the user clicks on a link in the html.<br/>
  38. /// Allows canceling the execution of the link.
  39. /// </para>
  40. /// <para>
  41. /// <h4>StylesheetLoad event:</h4>
  42. /// Raised when a stylesheet is about to be loaded by file path or URI by link element.<br/>
  43. /// This event allows to provide the stylesheet manually or provide new source (file or uri) to load from.<br/>
  44. /// If no alternative data is provided the original source will be used.<br/>
  45. /// </para>
  46. /// <para>
  47. /// <h4>ImageLoad event:</h4>
  48. /// Raised when an image is about to be loaded by file path or URI.<br/>
  49. /// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI.
  50. /// </para>
  51. /// <para>
  52. /// <h4>RenderError event:</h4>
  53. /// Raised when an error occurred during html rendering.<br/>
  54. /// </para>
  55. /// </summary>
  56. public class HtmlControl : Control
  57. {
  58. /// <summary>
  59. /// Underline html container instance.
  60. /// </summary>
  61. protected readonly HtmlContainer _htmlContainer;
  62. /// <summary>
  63. /// the base stylesheet data used in the control
  64. /// </summary>
  65. protected CssData _baseCssData;
  66. /// <summary>
  67. /// The last position of the scrollbars to know if it has changed to update mouse
  68. /// </summary>
  69. protected Point _lastScrollOffset;
  70. public static readonly AvaloniaProperty AvoidImagesLateLoadingProperty =
  71. PropertyHelper.Register<HtmlControl, bool>(nameof(AvoidImagesLateLoading), false, OnAvaloniaProperty_valueChanged);
  72. public static readonly AvaloniaProperty IsSelectionEnabledProperty =
  73. PropertyHelper.Register<HtmlControl, bool>(nameof(IsSelectionEnabled), true, OnAvaloniaProperty_valueChanged);
  74. public static readonly AvaloniaProperty IsContextMenuEnabledProperty =
  75. PropertyHelper.Register<HtmlControl, bool>(nameof(IsContextMenuEnabled), true, OnAvaloniaProperty_valueChanged);
  76. public static readonly AvaloniaProperty BaseStylesheetProperty =
  77. PropertyHelper.Register<HtmlControl, string>(nameof(BaseStylesheet), null, OnAvaloniaProperty_valueChanged);
  78. public static readonly AvaloniaProperty TextProperty =
  79. PropertyHelper.Register<HtmlControl, string>(nameof(Text), null, OnAvaloniaProperty_valueChanged);
  80. public static readonly StyledProperty<IBrush> BackgroundProperty =
  81. Border.BackgroundProperty.AddOwner<HtmlControl>();
  82. public static readonly AvaloniaProperty BorderThicknessProperty =
  83. AvaloniaProperty.Register<HtmlControl, Thickness>(nameof(BorderThickness), new Thickness(0));
  84. public static readonly AvaloniaProperty BorderBrushProperty =
  85. AvaloniaProperty.Register<HtmlControl, IBrush>(nameof(BorderBrush));
  86. public static readonly AvaloniaProperty PaddingProperty =
  87. AvaloniaProperty.Register<HtmlControl, Thickness>(nameof(Padding), new Thickness(0));
  88. public static readonly RoutedEvent LoadCompleteEvent =
  89. RoutedEvent.Register<RoutedEventArgs>("LoadComplete", RoutingStrategies.Bubble, typeof(HtmlControl));
  90. public static readonly RoutedEvent LinkClickedEvent =
  91. RoutedEvent.Register<HtmlRendererRoutedEventArgs<HtmlLinkClickedEventArgs>>("LinkClicked", RoutingStrategies.Bubble, typeof(HtmlControl));
  92. public static readonly RoutedEvent RenderErrorEvent
  93. = RoutedEvent.Register<HtmlRendererRoutedEventArgs<HtmlRenderErrorEventArgs>>("RenderError", RoutingStrategies.Bubble, typeof(HtmlControl));
  94. public static readonly RoutedEvent RefreshEvent
  95. = RoutedEvent.Register<HtmlRendererRoutedEventArgs<HtmlRefreshEventArgs>>("Refresh", RoutingStrategies.Bubble, typeof(HtmlControl));
  96. public static readonly RoutedEvent StylesheetLoadEvent
  97. = RoutedEvent.Register<HtmlRendererRoutedEventArgs<HtmlStylesheetLoadEventArgs>>("StylesheetLoad", RoutingStrategies.Bubble, typeof(HtmlControl));
  98. public static readonly RoutedEvent ImageLoadEvent
  99. = RoutedEvent.Register<HtmlRendererRoutedEventArgs<HtmlImageLoadEventArgs>>("ImageLoad", RoutingStrategies.Bubble,
  100. typeof (HtmlControl));
  101. static HtmlControl()
  102. {
  103. FocusableProperty.OverrideDefaultValue(typeof(HtmlControl), true);
  104. }
  105. /// <summary>
  106. /// Creates a new HtmlPanel and sets a basic css for it's styling.
  107. /// </summary>
  108. protected HtmlControl()
  109. {
  110. _htmlContainer = new HtmlContainer();
  111. _htmlContainer.LoadComplete += (_, e) => OnLoadComplete(e);
  112. _htmlContainer.LinkClicked += (_, e) => OnLinkClicked(e);
  113. _htmlContainer.RenderError += (_, e) => OnRenderError(e);
  114. _htmlContainer.Refresh += (_, e) => OnRefresh(e);
  115. _htmlContainer.StylesheetLoad += (_, e) => OnStylesheetLoad(e);
  116. _htmlContainer.ImageLoad += (_, e) => OnImageLoad(e);
  117. }
  118. //Hack for adapter
  119. internal bool LeftMouseButton { get; private set; }
  120. /// <summary>
  121. /// Raised when the set html document has been fully loaded.<br/>
  122. /// Allows manipulation of the html dom, scroll position, etc.
  123. /// </summary>
  124. public event EventHandler<HtmlRendererRoutedEventArgs<EventArgs>> LoadComplete
  125. {
  126. add { AddHandler(LoadCompleteEvent, value); }
  127. remove { RemoveHandler(LoadCompleteEvent, value); }
  128. }
  129. /// <summary>
  130. /// Raised when the user clicks on a link in the html.<br/>
  131. /// Allows canceling the execution of the link.
  132. /// </summary>
  133. public event EventHandler<HtmlRendererRoutedEventArgs<HtmlLinkClickedEventArgs>> LinkClicked
  134. {
  135. add { AddHandler(LinkClickedEvent, value); }
  136. remove { RemoveHandler(LinkClickedEvent, value); }
  137. }
  138. /// <summary>
  139. /// Raised when an error occurred during html rendering.<br/>
  140. /// </summary>
  141. public event EventHandler<HtmlRendererRoutedEventArgs<HtmlRenderErrorEventArgs>> RenderError
  142. {
  143. add { AddHandler(RenderErrorEvent, value); }
  144. remove { RemoveHandler(RenderErrorEvent, value); }
  145. }
  146. /// <summary>
  147. /// Raised when a stylesheet is about to be loaded by file path or URI by link element.<br/>
  148. /// This event allows to provide the stylesheet manually or provide new source (file or uri) to load from.<br/>
  149. /// If no alternative data is provided the original source will be used.<br/>
  150. /// </summary>
  151. public event EventHandler<HtmlRendererRoutedEventArgs<HtmlStylesheetLoadEventArgs>> StylesheetLoad
  152. {
  153. add { AddHandler(StylesheetLoadEvent, value); }
  154. remove { RemoveHandler(StylesheetLoadEvent, value); }
  155. }
  156. /// <summary>
  157. /// Raised when an image is about to be loaded by file path or URI.<br/>
  158. /// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI.
  159. /// </summary>
  160. public event EventHandler<HtmlRendererRoutedEventArgs<HtmlImageLoadEventArgs>> ImageLoad
  161. {
  162. add { AddHandler(ImageLoadEvent, value); }
  163. remove { RemoveHandler(ImageLoadEvent, value); }
  164. }
  165. /// <summary>
  166. /// Gets or sets a value indicating if image loading only when visible should be avoided (default - false).<br/>
  167. /// True - images are loaded as soon as the html is parsed.<br/>
  168. /// False - images that are not visible because of scroll location are not loaded until they are scrolled to.
  169. /// </summary>
  170. /// <remarks>
  171. /// Images late loading improve performance if the page contains image outside the visible scroll area, especially if there is large
  172. /// amount of images, as all image loading is delayed (downloading and loading into memory).<br/>
  173. /// Late image loading may effect the layout and actual size as image without set size will not have actual size until they are loaded
  174. /// resulting in layout change during user scroll.<br/>
  175. /// Early image loading may also effect the layout if image without known size above the current scroll location are loaded as they
  176. /// will push the html elements down.
  177. /// </remarks>
  178. [Category("Behavior")]
  179. [Description("If image loading only when visible should be avoided")]
  180. public bool AvoidImagesLateLoading
  181. {
  182. get { return (bool)GetValue(AvoidImagesLateLoadingProperty); }
  183. set { SetValue(AvoidImagesLateLoadingProperty, value); }
  184. }
  185. /// <summary>
  186. /// Is content selection is enabled for the rendered html (default - true).<br/>
  187. /// If set to 'false' the rendered html will be static only with ability to click on links.
  188. /// </summary>
  189. [Category("Behavior")]
  190. [Description("Is content selection is enabled for the rendered html.")]
  191. public bool IsSelectionEnabled
  192. {
  193. get { return (bool)GetValue(IsSelectionEnabledProperty); }
  194. set { SetValue(IsSelectionEnabledProperty, value); }
  195. }
  196. /// <summary>
  197. /// Is the build-in context menu enabled and will be shown on mouse right click (default - true)
  198. /// </summary>
  199. [Category("Behavior")]
  200. [Description("Is the build-in context menu enabled and will be shown on mouse right click.")]
  201. public bool IsContextMenuEnabled
  202. {
  203. get { return (bool)GetValue(IsContextMenuEnabledProperty); }
  204. set { SetValue(IsContextMenuEnabledProperty, value); }
  205. }
  206. /// <summary>
  207. /// Set base stylesheet to be used by html rendered in the panel.
  208. /// </summary>
  209. [Category("Appearance")]
  210. [Description("Set base stylesheet to be used by html rendered in the control.")]
  211. public string BaseStylesheet
  212. {
  213. get { return (string)GetValue(BaseStylesheetProperty); }
  214. set { SetValue(BaseStylesheetProperty, value); }
  215. }
  216. /// <summary>
  217. /// Gets or sets the text of this panel
  218. /// </summary>
  219. [Description("Sets the html of this control.")]
  220. public string Text
  221. {
  222. get { return (string)GetValue(TextProperty); }
  223. set { SetValue(TextProperty, value); }
  224. }
  225. public Thickness BorderThickness
  226. {
  227. get { return (Thickness) GetValue(BorderThicknessProperty); }
  228. set { SetValue(BorderThicknessProperty, value); }
  229. }
  230. public IBrush BorderBrush
  231. {
  232. get { return (IBrush)GetValue(BorderBrushProperty); }
  233. set { SetValue(BorderThicknessProperty, value); }
  234. }
  235. public Thickness Padding
  236. {
  237. get { return (Thickness)GetValue(PaddingProperty); }
  238. set { SetValue(PaddingProperty, value); }
  239. }
  240. public IBrush Background
  241. {
  242. get { return (IBrush) GetValue(BackgroundProperty); }
  243. set { SetValue(BackgroundProperty, value);}
  244. }
  245. /// <summary>
  246. /// Get the currently selected text segment in the html.
  247. /// </summary>
  248. [Browsable(false)]
  249. public virtual string SelectedText
  250. {
  251. get { return _htmlContainer.SelectedText; }
  252. }
  253. /// <summary>
  254. /// Copy the currently selected html segment with style.
  255. /// </summary>
  256. [Browsable(false)]
  257. public virtual string SelectedHtml
  258. {
  259. get { return _htmlContainer.SelectedHtml; }
  260. }
  261. /// <summary>
  262. /// Get html from the current DOM tree with inline style.
  263. /// </summary>
  264. /// <returns>generated html</returns>
  265. public virtual string GetHtml()
  266. {
  267. return _htmlContainer != null ? _htmlContainer.GetHtml() : null;
  268. }
  269. /// <summary>
  270. /// Get the rectangle of html element as calculated by html layout.<br/>
  271. /// Element if found by id (id attribute on the html element).<br/>
  272. /// Note: to get the screen rectangle you need to adjust by the hosting control.<br/>
  273. /// </summary>
  274. /// <param name="elementId">the id of the element to get its rectangle</param>
  275. /// <returns>the rectangle of the element or null if not found</returns>
  276. public virtual Rect? GetElementRectangle(string elementId)
  277. {
  278. return _htmlContainer != null ? _htmlContainer.GetElementRectangle(elementId) : null;
  279. }
  280. /// <summary>
  281. /// Clear the current selection.
  282. /// </summary>
  283. public void ClearSelection()
  284. {
  285. if (_htmlContainer != null)
  286. _htmlContainer.ClearSelection();
  287. }
  288. //HACK: We don't have support for RenderSize for now
  289. private Size RenderSize => new Size(Bounds.Width, Bounds.Height);
  290. public override void Render(DrawingContext context)
  291. {
  292. context.FillRectangle(Background, new Rect(RenderSize));
  293. if (BorderThickness != new Thickness(0) && BorderBrush != null)
  294. {
  295. var brush = new SolidColorBrush(Colors.Black);
  296. if (BorderThickness.Top > 0)
  297. context.FillRectangle(brush, new Rect(0, 0, RenderSize.Width, BorderThickness.Top));
  298. if (BorderThickness.Bottom > 0)
  299. context.FillRectangle(brush, new Rect(0, RenderSize.Height - BorderThickness.Bottom, RenderSize.Width, BorderThickness.Bottom));
  300. if (BorderThickness.Left > 0)
  301. context.FillRectangle(brush, new Rect(0, 0, BorderThickness.Left, RenderSize.Height));
  302. if (BorderThickness.Right > 0)
  303. context.FillRectangle(brush, new Rect(RenderSize.Width - BorderThickness.Right, 0, BorderThickness.Right, RenderSize.Height));
  304. }
  305. var htmlWidth = HtmlWidth(RenderSize);
  306. var htmlHeight = HtmlHeight(RenderSize);
  307. if (_htmlContainer != null && htmlWidth > 0 && htmlHeight > 0)
  308. {
  309. /*
  310. //TODO: Revert antialiasing fixes
  311. var windows = Window.GetWindow(this);
  312. if (windows != null)
  313. {
  314. // adjust render location to round point so we won't get anti-alias smugness
  315. var wPoint = TranslatePoint(new Point(0, 0), windows);
  316. wPoint.Offset(-(int)wPoint.X, -(int)wPoint.Y);
  317. var xTrans = wPoint.X < .5 ? -wPoint.X : 1 - wPoint.X;
  318. var yTrans = wPoint.Y < .5 ? -wPoint.Y : 1 - wPoint.Y;
  319. context.PushTransform(new TranslateTransform(xTrans, yTrans));
  320. }*/
  321. using (context.PushClip(new Rect(Padding.Left + BorderThickness.Left, Padding.Top + BorderThickness.Top,
  322. htmlWidth, (int) htmlHeight)))
  323. {
  324. _htmlContainer.Location = new Point(Padding.Left + BorderThickness.Left,
  325. Padding.Top + BorderThickness.Top);
  326. _htmlContainer.PerformPaint(context,
  327. new Rect(Padding.Left + BorderThickness.Left, Padding.Top + BorderThickness.Top, htmlWidth,
  328. htmlHeight));
  329. }
  330. if (!_lastScrollOffset.Equals(_htmlContainer.ScrollOffset))
  331. {
  332. _lastScrollOffset = _htmlContainer.ScrollOffset;
  333. InvokeMouseMove();
  334. }
  335. }
  336. }
  337. /// <summary>
  338. /// Handle mouse move to handle hover cursor and text selection.
  339. /// </summary>
  340. protected override void OnPointerMoved(PointerEventArgs e)
  341. {
  342. base.OnPointerMoved(e);
  343. if (_htmlContainer != null)
  344. _htmlContainer.HandleMouseMove(this, e.GetPosition(this));
  345. }
  346. /// <summary>
  347. /// Handle mouse leave to handle cursor change.
  348. /// </summary>
  349. protected override void OnPointerLeave(PointerEventArgs e)
  350. {
  351. base.OnPointerLeave(e);
  352. if (_htmlContainer != null)
  353. _htmlContainer.HandleMouseLeave(this);
  354. }
  355. /// <summary>
  356. /// Handle mouse down to handle selection.
  357. /// </summary>
  358. protected override void OnPointerPressed(PointerPressedEventArgs e)
  359. {
  360. base.OnPointerPressed(e);
  361. LeftMouseButton = true;
  362. _htmlContainer?.HandleLeftMouseDown(this, e);
  363. }
  364. /// <summary>
  365. /// Handle mouse up to handle selection and link click.
  366. /// </summary>
  367. protected override void OnPointerReleased(PointerReleasedEventArgs e)
  368. {
  369. base.OnPointerReleased(e);
  370. LeftMouseButton = false;
  371. if (_htmlContainer != null)
  372. _htmlContainer.HandleLeftMouseUp(this, e);
  373. }
  374. //TODO: Implement double click
  375. /*
  376. /// <summary>
  377. /// Handle mouse double click to select word under the mouse.
  378. /// </summary>
  379. protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
  380. {
  381. base.OnMouseDoubleClick(e);
  382. if (_htmlContainer != null)
  383. _htmlContainer.HandleMouseDoubleClick(this, e);
  384. }
  385. */
  386. /// <summary>
  387. /// Handle key down event for selection, copy and scrollbars handling.
  388. /// </summary>
  389. protected override void OnKeyDown(KeyEventArgs e)
  390. {
  391. base.OnKeyDown(e);
  392. if (_htmlContainer != null)
  393. _htmlContainer.HandleKeyDown(this, e);
  394. }
  395. void RaiseRouted<T>(RoutedEvent ev, T arg)
  396. {
  397. var e =new HtmlRendererRoutedEventArgs<T>
  398. {
  399. Event = arg,
  400. Source = this,
  401. RoutedEvent = ev,
  402. Route = ev.RoutingStrategies
  403. };
  404. RaiseEvent(e);
  405. }
  406. /// <summary>
  407. /// Propagate the LoadComplete event from root container.
  408. /// </summary>
  409. protected virtual void OnLoadComplete(EventArgs e) => RaiseRouted(LoadCompleteEvent, e);
  410. /// <summary>
  411. /// Propagate the LinkClicked event from root container.
  412. /// </summary>
  413. protected virtual void OnLinkClicked(HtmlLinkClickedEventArgs e) => RaiseRouted(LinkClickedEvent, e);
  414. /// <summary>
  415. /// Propagate the Render Error event from root container.
  416. /// </summary>
  417. protected virtual void OnRenderError(HtmlRenderErrorEventArgs e) => RaiseRouted(RenderErrorEvent, e);
  418. /// <summary>
  419. /// Propagate the stylesheet load event from root container.
  420. /// </summary>
  421. protected virtual void OnStylesheetLoad(HtmlStylesheetLoadEventArgs e) => RaiseRouted(StylesheetLoadEvent, e);
  422. /// <summary>
  423. /// Propagate the image load event from root container.
  424. /// </summary>
  425. protected virtual void OnImageLoad(HtmlImageLoadEventArgs e) => RaiseRouted(ImageLoadEvent, e);
  426. /// <summary>
  427. /// Handle html renderer invalidate and re-layout as requested.
  428. /// </summary>
  429. protected virtual void OnRefresh(HtmlRefreshEventArgs e)
  430. {
  431. if (e.Layout)
  432. InvalidateMeasure();
  433. InvalidateVisual();
  434. }
  435. /// <summary>
  436. /// Get the width the HTML has to render in (not including vertical scroll iff it is visible)
  437. /// </summary>
  438. protected virtual double HtmlWidth(Size size)
  439. {
  440. return size.Width - Padding.Left - Padding.Right - BorderThickness.Left - BorderThickness.Right;
  441. }
  442. /// <summary>
  443. /// Get the width the HTML has to render in (not including vertical scroll iff it is visible)
  444. /// </summary>
  445. protected virtual double HtmlHeight(Size size)
  446. {
  447. return size.Height - Padding.Top - Padding.Bottom - BorderThickness.Top - BorderThickness.Bottom;
  448. }
  449. /// <summary>
  450. /// call mouse move to handle paint after scroll or html change affecting mouse cursor.
  451. /// </summary>
  452. protected virtual void InvokeMouseMove()
  453. {
  454. _htmlContainer.HandleMouseMove(this, (this.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(this) ?? default(Point));
  455. }
  456. /// <summary>
  457. /// Handle when dependency property value changes to update the underline HtmlContainer with the new value.
  458. /// </summary>
  459. private static void OnAvaloniaProperty_valueChanged(AvaloniaObject AvaloniaObject,
  460. AvaloniaPropertyChangedEventArgs e)
  461. {
  462. var control = AvaloniaObject as HtmlControl;
  463. if (control != null)
  464. {
  465. var htmlContainer = control._htmlContainer;
  466. if (e.Property == AvoidImagesLateLoadingProperty)
  467. {
  468. htmlContainer.AvoidImagesLateLoading = (bool) e.NewValue;
  469. }
  470. else if (e.Property == IsSelectionEnabledProperty)
  471. {
  472. htmlContainer.IsSelectionEnabled = (bool) e.NewValue;
  473. }
  474. else if (e.Property == IsContextMenuEnabledProperty)
  475. {
  476. htmlContainer.IsContextMenuEnabled = (bool) e.NewValue;
  477. }
  478. else if (e.Property == BaseStylesheetProperty)
  479. {
  480. var baseCssData = CssData.Parse(AvaloniaAdapter.Instance, (string) e.NewValue);
  481. control._baseCssData = baseCssData;
  482. htmlContainer.SetHtml(control.Text, baseCssData);
  483. }
  484. else if (e.Property == TextProperty)
  485. {
  486. htmlContainer.ScrollOffset = new Point(0, 0);
  487. htmlContainer.SetHtml((string) e.NewValue, control._baseCssData);
  488. control.InvalidateMeasure();
  489. control.InvalidateVisual();
  490. if (control.VisualRoot != null)
  491. {
  492. control.InvokeMouseMove();
  493. }
  494. }
  495. }
  496. }
  497. //TODO: Implement CheckAccess calls
  498. /*
  499. private void OnLoadComplete(object sender, EventArgs e)
  500. {
  501. if (CheckAccess())
  502. OnLoadComplete(e);
  503. else
  504. Dispatcher.UIThread.Invoke(new Action<HtmlLinkClickedEventArgs>(OnLinkClicked), e);
  505. }
  506. private void OnLinkClicked(object sender, HtmlLinkClickedEventArgs e)
  507. {
  508. if (CheckAccess())
  509. OnLinkClicked(e);
  510. else
  511. Dispatcher.UIThread.Invoke(new Action<HtmlLinkClickedEventArgs>(OnLinkClicked), e);
  512. }
  513. private void OnRenderError(object sender, HtmlRenderErrorEventArgs e)
  514. {
  515. if (CheckAccess())
  516. OnRenderError(e);
  517. else
  518. Dispatcher.UIThread.Invoke(new Action<HtmlRenderErrorEventArgs>(OnRenderError), e);
  519. }
  520. private void OnStylesheetLoad(object sender, HtmlStylesheetLoadEventArgs e)
  521. {
  522. if (CheckAccess())
  523. OnStylesheetLoad(e);
  524. else
  525. Dispatcher.UIThread.Invoke(new Action<HtmlStylesheetLoadEventArgs>(OnStylesheetLoad), e);
  526. }
  527. private void OnImageLoad(object sender, HtmlImageLoadEventArgs e)
  528. {
  529. if (CheckAccess())
  530. OnImageLoad(e);
  531. else
  532. Dispatcher.UIThread.Invoke(new Action<HtmlImageLoadEventArgs>(OnImageLoad), e);
  533. }
  534. private void OnRefresh(object sender, HtmlRefreshEventArgs e)
  535. {
  536. if (CheckAccess())
  537. OnRefresh(e);
  538. else
  539. Dispatcher.UIThread.Invoke(new Action<HtmlRefreshEventArgs>(OnRefresh), e);
  540. }
  541. */
  542. }
  543. }