Bazaprogram.ru

Новости из мира ПК
5 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Webbrowser c javascript

How to inject Javascript in WebBrowser control?

scriptEl.InnerHtml and scriptEl.InnerText both give errors:

Is there an easy way to inject a script into the dom?

15 Answers 15

For some reason Richard’s solution didn’t work on my end (insertAdjacentText failed with an exception). This however seems to work:

This answer explains how to get the IHTMLScriptElement interface into your project.

(tested in .NET 4 / Windows Forms App)

Edit: Fixed case issue in function set.

Here is the easiest way that I found after working on this:

What global JavaScript function eval(str) does is parses and executes whatever is written in str. Check w3schools ref here.

Also, in .NET 4 this is even easier if you use the dynamic keyword:

If all you really want is to run javascript, this would be easiest (VB .Net):

I guess that this wouldn’t «inject» it but it’ll run your function, if that’s what you’re after. (Just in case you’ve over-complicated the problem.) And if you can figure out how to inject in javascript, put that into the body of the function «foo» and let the javascript do the injection for you.

The managed wrapper for the HTML document doesn’t completely implement the functionality you need, so you need to dip into the MSHTML API to accomplish what you want:

1) Add a reference to MSHTML, which will probalby be called «Microsoft HTML Object Library» under COM references.

2) Add ‘using mshtml;’ to your namespaces.

3) Get a reference to your script element’s IHTMLElement:

4) Call the insertAdjacentText method, with the first parameter value of «afterBegin». All the possible values are listed here:

5) Now you’ll be able to see the code in the scriptEl.InnerText property.

As a follow-up to the accepted answer, this is a minimal definition of the IHTMLScriptElement interface which does not require to include additional type libraries:

So a full code inside a WebBrowser control derived class would look like:

I believe the most simple method to inject Javascript in a WebBrowser Control HTML Document from c# is to invoke the «execScript» method with the code to be injected as argument.

In this example the javascript code is injected and executed at global scope:

If you want to delay execution, inject functions and call them after:

This is valid for Windows Forms and WPF WebBrowser controls.

This solution is not cross browser because «execScript» is defined only in IE and Chrome. But the question is about Microsoft WebBrowser controls and IE is the only one supported.

For a valid cross browser method to inject javascript code, create a Function object with the new Keyword. This example creates an anonymous function with injected code and executes it (javascript implements closures and the function has access to global space without local variable pollution).

Of course, you can delay execution:

this is a solution using mshtml

Then you can execute and get the result with:

I hope to be helpful

Here is a VB.Net example if you are trying to retrieve the value of a variable from within a page loaded in a WebBrowser control.

Step 1) Add a COM reference in your project to Microsoft HTML Object Library

Step 2) Next, add this VB.Net code to your Form1 to import the mshtml library:
Imports mshtml

Step 3) Add this VB.Net code above your «Public Class Form1» line:

Step 4) Add a WebBrowser control to your project

Step 5) Add this VB.Net code to your Form1_Load function:
WebBrowser1.ObjectForScripting = Me

Step 6) Add this VB.Net sub which will inject a function «CallbackGetVar» into the web page’s Javascript:

Step 7) Add the following VB.Net sub which the Javascript will then look for when invoked:

Step 8) Finally, to invoke the Javascript callback, add this VB.Net code when a button is pressed, or wherever you like:

Step 9) If it surprises you that this works, you may want to read up on the Javascript «eval» function, used in Step 6, which is what makes this possible. It will take a string and determine whether a variable exists with that name and, if so, returns the value of that variable.

Элемент управления WebBrowser

WPF стирает границы между традиционными настольными и веб-приложениями. За счет использования страниц можно создавать WPF-приложения с возможностями навигации в веб-стиле. ХВАР позволяют выполнять WPF-приложения внутри окна браузера. С применением элемента управления Frame можно проделывать обратное, размещая HTML-страницу в окне WPF.

Однако при использовании элемента Frame для отображения HTML-содержимого все возможности по управлению содержимым утрачиваются. Не остается никакого способа для его инспектирования или для следования ему, когда пользователь переходит на новую страницу по щелчку на ссылке. И, конечно же, отсутствует возможность вызова методов JavaScript внутри HTML-страницы либо их обращения к коду WPF. Вот здесь как раз и приходит на помощь элемент управления WebBrowser.

Элемент управления Frame является прекрасным вариантом, когда необходим контейнер, способный гладко переключаться между содержимым WPF и HTML. Элемент управления WebBrowser более удобен, когда нужно изучать объектную модель страницы, ограничивать или следить за страничной навигацией или создавать путь, через который код JavaScript и код WPF могут взаимодействовать.

И WebBrowser, и Frame (с HTML-содержимым) отображают стандартное окно Internet Explorer. Это окно обладает всеми возможностями и средствами браузера Internet Explorer, включая поддержку JavaScript, Dynamic HTML, элементов управления ActiveX и подключаемых модулей. Однако дополнительных деталей вроде панели инструментов, адресной строки или строки информации о состоянии это окно не содержит (хотя все эти ингредиенты легко добавить к форме с использованием соответствующих элементов управления).

Читать еще:  Internal exception java nio channels closedchannelexception

Элемент управления WebBrowser не был написан с нуля в виде управляемого кода. Подобно Frame (когда он отображает HTML-содержимое), он является оболочкой для СОМ-компонента shdocvw.dll, который представляет собой часть Internet Explorer и включен в Windows. Как следствие, WebBrowser и Frame имеют несколько графических ограничений, которые отсутствуют у остальных элементов управления WPF. Например, не допускается размещать другие элементы поверх HTML-содержимого, отображаемого в этих элементах управления, равно как применять трансформации для скашивания или поворота.

В качестве средства, способность WPF отображать HTML-содержимое (с помощью Frame или WebBrowser) даже близко не настолько же полезно, как страничная модель или ХВАР. Тем не менее, в особых ситуациях при наличии готового HTML-содержимого, которое не хотелось бы заменять, это средство может пригодиться. Например, элемент WebBrowser может использоваться для отображения внутри приложения HTML-документации или для предоставления возможности переходить к функциональности, предлагаемой на веб-сайте сторонних разработчиков.

Навигация к странице

После помещения элемента управления WebBrowser в окно ему должен быть указан какой-нибудь документ. Самый простой подход предполагает установку его свойства Source в соответствующий URL-адрес. В общем случае это свойство может принимать в качестве значения любой удаленный URL-адрес (наподобие http://mysite.com/mypage.html) и полностью уточненный путь к файлу (такой как file:///c:mydocument.text). URL-адрес может указывать на файл любого типа, который Internet Explorer способен открывать, хотя практически всегда WebBrowser используется для отображения HTML-страниц:

Элемент управления WebBrowser можно также направить на каталог. Например, если присвоить его свойству Source путь вроде file:///c:, то в этом случае окно WebBrowser приобретет вид хорошо знакомого окна для просмотра файлов в стиле проводника Windows, позволяя открывать, копировать, вставлять и удалять файлы. Однако никаких событий или свойств, которые бы дали возможность ограничить эти функции (либо хотя бы отслеживать их), не предусмотрено, поэтому соблюдайте осторожность!

Кроме свойства Source доступны также и методы навигации, которые перечислены ниже:

Navigate()

Выполняет переход по указанному URL-адресу. Перегруженная версия позволяет загружать документ в определенный фрейм, выполнять обратную отправку данных и передавать дополнительные HTTP-заголовки

NavigateToString()

Загружает содержимое из переданной ему строки с полным HTML-содержимым веб-страницы. Это открывает интересные возможности, такие как извлечение HTML-текста из ресурса внутри приложения и его отображение

NavigateToStream()

Загружает содержимое из потока, который содержит HTML-документ. Это позволяет открывать файл и передавать его прямо в элемент управления WebBrowser для визуализации без удержания всего HTML-содержимого в памяти

GoBack() и GoForward()

Переходят к предыдущему или следующему документу в хронологии навигации. Во избежание ошибок перед использованием этих методов необходимо проверять свойства CanGoBack и CanGoForward, т.к. попытка перейти к документу, которого не существует (например, перейти назад, находясь на первом документе в хронологии), вызывает генерацию исключения

Refresh()

Перезагружает текущий документ

Вся навигация WebBrowser является асинхронной. Это значит, что код приложения продолжает выполнение во время загрузки страницы. Элемент управления WebBrowser поддерживает небольшой набор событий, которые описаны ниже:

Событие Navigating срабатывает при установке нового URL-адреса или при выполнении пользователем щелчка на ссылке. URL-адрес можно инспектировать и отменять навигацию, устанавливая е.Cancel в true.

Событие Navigated срабатывает после Navigating непосредственно перед тем, как в WebBrowser начинается загрузка страницы.

Событие LoadCompleted срабатывает после завершения процесса загрузки страницы и представляет собой удобную возможность для обработки полученной страницы.

Построение дерева DOM

При использовании элемента управления WebBrowser допускается писать код C#, который позволяет проходить по дереву HTML-элементов на странице. Можно даже изменять, удалять или вставлять элементы с помощью программной модели, подобной HTML DOM, которая применяется в таких языках сценариев для веб-браузеров, как JavaScript.

Прежде чем можно будет использовать модель DOM с WebBrowser, необходимо добавить ссылку на библиотеку Microsoft HTML Object Library (mshtml.tlb). Поскольку она является библиотекой СОМ-объектов, в Visual Studio понадобится сгенерировать для нее соответствующую управляемую оболочку. Для этого нужно выбрать в меню Project (Проект) пункт Add Reference (Добавить ссылку), перейти на вкладку СОМ, выбрать опцию Microsoft HTML Object Library (Библиотека объектов HTML от Microsoft) и щелкнуть на кнопке ОК.

Стартовой точкой для исследования содержимого на веб-странице является свойство WebBrowser.Document. Оно дает объект HTMLDocument, который представляет одиночную веб-страницу в виде иерархической коллекции объектов IHTMLElement.

Для каждого имеющегося на веб-странице дескриптора предусмотрен отдельный объект IHTMLElement, включая параграфы ( ), гиперссылки ( ) и прочие знакомые ингредиенты HTML-разметки.

Свойство WebBrowser.Document доступно только для чтения. Это означает, что связанный с ним объект HtmlDocument модифицировать можно, но создавать для него новый объект HtmlDocument «на лету» нельзя. Вместо этого понадобится либо установить свойство Source, либо вызывать метод Navigate() для загрузки новой страницы. После срабатывания события WebBrowser.LoadCompleted можно получать доступ к свойству Document.

Построение объекта HTMLDocument занимает небольшое, но все же заметное время (в зависимости от размеров и сложности веб-страницы). Элемент управления WebBrowser на самом деле не создает объект HTMLDocument для страницы до тех пор, пока не будет предпринята первая попытка доступа к свойству Document. Каждый объект IHTMLElement имеет описанные ниже ключевые свойства:

Свойство id содержит значение атрибута id, если он был указан. Элементы идентифицируются с помощью уникальных атрибутов id, когда необходимости иметь возможность манипулировать ими в средстве автоматизации или серверном коде.

Свойство children содержит коллекцию объектов IHTMLElement, по одному объекту для каждого имеющегося дескриптора.

Свойство innerHTML хранит полное содержимое дескриптора, включая любые вложенные в него дескрипторы вместе с их содержимым.

Свойство innerText хранит полное содержимое самого дескриптора и содержимое любых вложенных в него дескрипторов. Однако все HTML-дескрипторы отбрасываются.

Свойства outerHTML и outerText исполняют ту же роль, что и innerHTML и innerText, но включают текущий дескриптор (а не только его содержимое).

Чтобы лучше понять свойства innerText, innertHTML и outerHTML, предположим, что есть следующий дескриптор:

Значение свойства innerText для этого дескриптора будет выглядеть так:

Значение свойства innerHTML — так:

И, наконец, значение свойства outerHTML включает в себя полностью весь дескриптор:

Читать еще:  Java arraylist indexof

Вдобавок с помощью метода IHTMLElement.getAttribute() можно извлечь значение атрибута для элемента по имени.

Для навигации по модели документа HTML-страницы нужно просто перемещаться по коллекции дочерних элементов каждого объекта IHTMLElement. В следующем коде эта задача выполняется в ответ на щелчок на кнопке. При этом создается дерево, отражающее структуру элементов и содержимого на странице:

Элемент управления WebBrowser

WPF стирает границы между традиционными настольными и веб-приложениями. За счет использования страниц можно создавать WPF-приложения с возможностями навигации в веб-стиле. ХВАР позволяют выполнять WPF-приложения внутри окна браузера. С применением элемента управления Frame можно проделывать обратное, размещая HTML-страницу в окне WPF.

Однако при использовании элемента Frame для отображения HTML-содержимого все возможности по управлению содержимым утрачиваются. Не остается никакого способа для его инспектирования или для следования ему, когда пользователь переходит на новую страницу по щелчку на ссылке. И, конечно же, отсутствует возможность вызова методов JavaScript внутри HTML-страницы либо их обращения к коду WPF. Вот здесь как раз и приходит на помощь элемент управления WebBrowser.

Элемент управления Frame является прекрасным вариантом, когда необходим контейнер, способный гладко переключаться между содержимым WPF и HTML. Элемент управления WebBrowser более удобен, когда нужно изучать объектную модель страницы, ограничивать или следить за страничной навигацией или создавать путь, через который код JavaScript и код WPF могут взаимодействовать.

И WebBrowser, и Frame (с HTML-содержимым) отображают стандартное окно Internet Explorer. Это окно обладает всеми возможностями и средствами браузера Internet Explorer, включая поддержку JavaScript, Dynamic HTML, элементов управления ActiveX и подключаемых модулей. Однако дополнительных деталей вроде панели инструментов, адресной строки или строки информации о состоянии это окно не содержит (хотя все эти ингредиенты легко добавить к форме с использованием соответствующих элементов управления).

Элемент управления WebBrowser не был написан с нуля в виде управляемого кода. Подобно Frame (когда он отображает HTML-содержимое), он является оболочкой для СОМ-компонента shdocvw.dll, который представляет собой часть Internet Explorer и включен в Windows. Как следствие, WebBrowser и Frame имеют несколько графических ограничений, которые отсутствуют у остальных элементов управления WPF. Например, не допускается размещать другие элементы поверх HTML-содержимого, отображаемого в этих элементах управления, равно как применять трансформации для скашивания или поворота.

В качестве средства, способность WPF отображать HTML-содержимое (с помощью Frame или WebBrowser) даже близко не настолько же полезно, как страничная модель или ХВАР. Тем не менее, в особых ситуациях при наличии готового HTML-содержимого, которое не хотелось бы заменять, это средство может пригодиться. Например, элемент WebBrowser может использоваться для отображения внутри приложения HTML-документации или для предоставления возможности переходить к функциональности, предлагаемой на веб-сайте сторонних разработчиков.

Навигация к странице

После помещения элемента управления WebBrowser в окно ему должен быть указан какой-нибудь документ. Самый простой подход предполагает установку его свойства Source в соответствующий URL-адрес. В общем случае это свойство может принимать в качестве значения любой удаленный URL-адрес (наподобие http://mysite.com/mypage.html) и полностью уточненный путь к файлу (такой как file:///c:mydocument.text). URL-адрес может указывать на файл любого типа, который Internet Explorer способен открывать, хотя практически всегда WebBrowser используется для отображения HTML-страниц:

Элемент управления WebBrowser можно также направить на каталог. Например, если присвоить его свойству Source путь вроде file:///c:, то в этом случае окно WebBrowser приобретет вид хорошо знакомого окна для просмотра файлов в стиле проводника Windows, позволяя открывать, копировать, вставлять и удалять файлы. Однако никаких событий или свойств, которые бы дали возможность ограничить эти функции (либо хотя бы отслеживать их), не предусмотрено, поэтому соблюдайте осторожность!

Кроме свойства Source доступны также и методы навигации, которые перечислены ниже:

Navigate()

Выполняет переход по указанному URL-адресу. Перегруженная версия позволяет загружать документ в определенный фрейм, выполнять обратную отправку данных и передавать дополнительные HTTP-заголовки

NavigateToString()

Загружает содержимое из переданной ему строки с полным HTML-содержимым веб-страницы. Это открывает интересные возможности, такие как извлечение HTML-текста из ресурса внутри приложения и его отображение

NavigateToStream()

Загружает содержимое из потока, который содержит HTML-документ. Это позволяет открывать файл и передавать его прямо в элемент управления WebBrowser для визуализации без удержания всего HTML-содержимого в памяти

GoBack() и GoForward()

Переходят к предыдущему или следующему документу в хронологии навигации. Во избежание ошибок перед использованием этих методов необходимо проверять свойства CanGoBack и CanGoForward, т.к. попытка перейти к документу, которого не существует (например, перейти назад, находясь на первом документе в хронологии), вызывает генерацию исключения

Refresh()

Перезагружает текущий документ

Вся навигация WebBrowser является асинхронной. Это значит, что код приложения продолжает выполнение во время загрузки страницы. Элемент управления WebBrowser поддерживает небольшой набор событий, которые описаны ниже:

Событие Navigating срабатывает при установке нового URL-адреса или при выполнении пользователем щелчка на ссылке. URL-адрес можно инспектировать и отменять навигацию, устанавливая е.Cancel в true.

Событие Navigated срабатывает после Navigating непосредственно перед тем, как в WebBrowser начинается загрузка страницы.

Событие LoadCompleted срабатывает после завершения процесса загрузки страницы и представляет собой удобную возможность для обработки полученной страницы.

Построение дерева DOM

При использовании элемента управления WebBrowser допускается писать код C#, который позволяет проходить по дереву HTML-элементов на странице. Можно даже изменять, удалять или вставлять элементы с помощью программной модели, подобной HTML DOM, которая применяется в таких языках сценариев для веб-браузеров, как JavaScript.

Прежде чем можно будет использовать модель DOM с WebBrowser, необходимо добавить ссылку на библиотеку Microsoft HTML Object Library (mshtml.tlb). Поскольку она является библиотекой СОМ-объектов, в Visual Studio понадобится сгенерировать для нее соответствующую управляемую оболочку. Для этого нужно выбрать в меню Project (Проект) пункт Add Reference (Добавить ссылку), перейти на вкладку СОМ, выбрать опцию Microsoft HTML Object Library (Библиотека объектов HTML от Microsoft) и щелкнуть на кнопке ОК.

Читать еще:  Java util comparator

Стартовой точкой для исследования содержимого на веб-странице является свойство WebBrowser.Document. Оно дает объект HTMLDocument, который представляет одиночную веб-страницу в виде иерархической коллекции объектов IHTMLElement.

Для каждого имеющегося на веб-странице дескриптора предусмотрен отдельный объект IHTMLElement, включая параграфы ( ), гиперссылки ( ) и прочие знакомые ингредиенты HTML-разметки.

Свойство WebBrowser.Document доступно только для чтения. Это означает, что связанный с ним объект HtmlDocument модифицировать можно, но создавать для него новый объект HtmlDocument «на лету» нельзя. Вместо этого понадобится либо установить свойство Source, либо вызывать метод Navigate() для загрузки новой страницы. После срабатывания события WebBrowser.LoadCompleted можно получать доступ к свойству Document.

Построение объекта HTMLDocument занимает небольшое, но все же заметное время (в зависимости от размеров и сложности веб-страницы). Элемент управления WebBrowser на самом деле не создает объект HTMLDocument для страницы до тех пор, пока не будет предпринята первая попытка доступа к свойству Document. Каждый объект IHTMLElement имеет описанные ниже ключевые свойства:

Свойство id содержит значение атрибута id, если он был указан. Элементы идентифицируются с помощью уникальных атрибутов id, когда необходимости иметь возможность манипулировать ими в средстве автоматизации или серверном коде.

Свойство children содержит коллекцию объектов IHTMLElement, по одному объекту для каждого имеющегося дескриптора.

Свойство innerHTML хранит полное содержимое дескриптора, включая любые вложенные в него дескрипторы вместе с их содержимым.

Свойство innerText хранит полное содержимое самого дескриптора и содержимое любых вложенных в него дескрипторов. Однако все HTML-дескрипторы отбрасываются.

Свойства outerHTML и outerText исполняют ту же роль, что и innerHTML и innerText, но включают текущий дескриптор (а не только его содержимое).

Чтобы лучше понять свойства innerText, innertHTML и outerHTML, предположим, что есть следующий дескриптор:

Значение свойства innerText для этого дескриптора будет выглядеть так:

Значение свойства innerHTML — так:

И, наконец, значение свойства outerHTML включает в себя полностью весь дескриптор:

Вдобавок с помощью метода IHTMLElement.getAttribute() можно извлечь значение атрибута для элемента по имени.

Для навигации по модели документа HTML-страницы нужно просто перемещаться по коллекции дочерних элементов каждого объекта IHTMLElement. В следующем коде эта задача выполняется в ответ на щелчок на кнопке. При этом создается дерево, отражающее структуру элементов и содержимого на странице:

День восемнадцатый. Элемент WebBrowser

Вступление

Продолжаем знакомиться с разными элементами, которые используются в Windows Phone 7. Сегодня узнаем побольше о важном элементе WebBrowser.

Знакомство с WebBrowser

Само название элемента WebBrowser говорит о том, что вы можете с его помощью зайти на определенную страницу. Можете считать данный элемент урезанным браузером, у которого нет адресной строки, избранного, вкладок и т.д. При этом у него работают стандартные жесты для зума или прокрутки.

Добавим WebBrowser на страницу простым перетаскиванием из панели инструментов. В XAML появится код:

При этом будет подключено пространство имён xmlns:phone=»clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone».

Для того чтобы в браузере отобразить страницу, достаточно вызвать один метод:

Главная особенность элемента WebBrowser заключается в том, что вы можете загрузить часть информации локально, и часть с интернета. Таким образом вы можете внедрить HTML-страницы в приложение и загружать их локально на телефоне без необходимости подключения к интернету.

Загрузка локальных HTML-страниц в WebBrowser

Подготовим необходимые HTML-файлы для проекта. Для нашего примера достаточно двух файлов, но вы можете использовать и больше.

Добавим в приложение две кнопки для загрузки каждого файла. Так как мы будем работать с файлом, нам необходимо добавить к проекту пространство имен System.IO:

Также мы добавим пространство имен Microsoft.Xna.Framework:

Возможно вас удивит, что мы хотим использовать XNA в Silverlight-проекте. Чуть позже вы поймете, почему нам понадобился XNA. А сейчас просто поверьте на слово. В обработчике события для кнопки пишем две строчки кода. В первой строчке загружаем содержимое HTML в StreamReader, а во второй — загружаем содержимое HTML-файла в WebBrowser, используя метод NavigateToString():

Кроме загрузки уже существующей страницы, мы можем генерировать HTML прямо в коде приложения:

Функция ConvertExtendedAscii используется для устранения возможных проблем с кодировкой.

Включаем выполнение сценариев JavaScript

По умолчанию выполнение сценариев JavaScript в WebBrowser отключено. Если вы хотите, чтобы сценарии в ваших веб-документах выполнялись, вам необходимо установить соответствующее разрешение. Делается это очень просто при помощи свойства IsScriptEnabled:

Взаимодействие между приложением и сценарием

При взаимодействии со страницей, загруженной в WebBrowser мы можем вызывать из C# функции JavaScript, а из JavaScript уведомлять Silverlight приложение. Вызвать JavaScript-функцию из C# можно при помощи метода InvokeScript, передавая имя метода сценария. В данном примере мы вызываем функцию «functionName» с параметром «parametr1» и получаем возвращаемое значение:

Для оповещения приложения из сценария JavaScript надо подписаться на событие ScriptNotify и в обработчике события получить параметр, переданный из сценария. NotifyEventArgs.Value будет содержать нужное значение:

Далее вызываем событие:

Используем индикатор прогресса

Так как загрузка содержимоего из сети может занимать некоторое время, нужно показать пользователю, что программа не зависла, а продолжает работать. Например, можно вывести на экран индикатор прогресса (ProgressBar). Вам нужно установить значение IsIndeterminate в true и показывать и скрывать индикатор в нужное время. Ниже представлен кода на XAML и C#:

Соответственно, когда страница загружается, ProgressBar будет виден, а после загрузки страницы он будет скрыт.

Эффекты в WebBrowser

Важно отметить, что элемент управления WebBrowser в Windows Phone 7, в отличие от своего аналога в десктопном Silverlight не нуждается в HtmlBrush, так как большинство графических преобразований можно сделать над самим браузером. Например, повернём WebBrowser на 45 градусов по оси Y:

Вот как это будет выглядеть:

При этом элемент WebBrowser полностью интерактивен и продолжает работать как обычно. Во внебраузерном Silverlight 4 приложении мы такое бы сделать не смогли.

Заключение

Сегодня вы познакомились с элементом WebBrowser для навигации по html-страницам. Для лучшего понимания материала, рекомендую скачать исходник и изучить его.

Ссылка на основную публикацию
Adblock
detector