It is possible, and not even that difficult, to embed Tableau Server views into a C# based Windows application. However, the putting all the pieces together to make it work smoothly is pretty difficult — the documentation on the Internet that describes the HOW is all over the place and unless you really know what Tableau is doing, it is not obvious what pieces of the .Net framework are actually useful.

Hopefully this post will put it all together enough for anyone interested to get started.
You will need:
- Tableau Server
- A separate web server, with some facility for dynamic web pages. Apache + PHP, or IIS + ASP.Net for example.
- Visual Studio
The Web Server
Why a separate web server? While you can always use the direct embed links to bring up a Tableau viz from Server, if you want it to integrate well:
- JavaScript API needs a wrapper web page to initialize from
- Trusted Authentication needs a web server to trust
- The correct headers in the web page will force the embedded IE browser to render the right way Get more skills from Tableau Online Course
JavaScript api
The JavaScript API has to be started from a web page, no matter how basic it is. JS API is useful for all the control it gives such as sizing, toolbar options and so forth. Plus, it is also the only facility for interaction between the Tableau Viz and the application.
Authentication
If your authentication is entirely Active Directory with ‘Enable automatic logon’ (Set in the ‘Configure Tableau Server’ program) , you are in luck! You don’t even have to worry about the trusted authentication portion.
Because the WebBrowser object in .Net is calling the local Internet Explorer on the computer, SSPI should work just fine.
For trusted auth, you don’t need anything incredibly complex from the dynamic web server. As long as it can receive the Ticket from the trusted auth request and substitute that in to the URL request, you’ll get smooth SSO in your application.
You can POST data to the dynamic page (seen below in the C# code), for example the username, and if your Tableau Server is set up with SSL, the whole process is completely secure.
Forcing standards compliant rendering
By default, the WebBrowser object renders all its content using IE7 compatibility mode. If you want to see Tableau Server completely break, render everything in IE7 mode. Luckily, there is an easy way to force the use of the most modern rendering the system supports (IE10 or 11 hopefully). Learn from Tableau Server Training
The first lines of HTML in all the pages served up by your web server should be
<!doctype html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
The doctype declaration and the meta tag together force IE to use it’s latest and greatest rendering engine, which is pretty good these days.
Updated: But this is not enough! For some reason, the WebBrowser part renders in IE10 Compatibility Mode, which while not being as terrible as IE7, still does things so incorrectly that it breaks the editing toolbar when using Web Edit.
You will only see the Undo button and nothing else if you are in Compatibility mode. To ensure that the browser always does the right thing, you’ll have to add some code to your application that changes a few registry entries while the program is running.
Stack Overflow provides an answer, which I’ve modified here with only the necessary pieces (and also throwing out IE7 altogether since Tableau Server 9 can’t use it). I put the two classes in the main Form1 public class in the example file, then use the SetWebBrowserFeatures() method on initialization of the form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetWebBrowserFeatures();
}
..........
/*
* This section handles forcing the WebBrowser into modern standards compliant mode
* Without it, you'll get dropped into IE10 compatibility mode and not all the buttons in web edit show up
* */
// set WebBrowser features, more info: http://stackoverflow.com/a/18333982/1768303
static void SetWebBrowserFeatures()
{
// don't change the registry if running in-proc inside Visual Studio
if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
return;
var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
var featureControlRegKey = @"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\";
Registry.SetValue(featureControlRegKey + "FEATURE_BROWSER_EMULATION",
appName, GetBrowserEmulationMode(), RegistryValueKind.DWord);
// enable the features which are "On" for the full Internet Explorer browser
Registry.SetValue(featureControlRegKey + "FEATURE_ENABLE_CLIPCHILDREN_OPTIMIZATION",
appName, 1, RegistryValueKind.DWord);
Registry.SetValue(featureControlRegKey + "FEATURE_AJAX_CONNECTIONEVENTS",
appName, 1, RegistryValueKind.DWord);
Registry.SetValue(featureControlRegKey + "FEATURE_GPU_RENDERING",
appName, 1, RegistryValueKind.DWord);
Registry.SetValue(featureControlRegKey + "FEATURE_WEBOC_DOCUMENT_ZOOM",
appName, 1, RegistryValueKind.DWord);
Registry.SetValue(featureControlRegKey + "FEATURE_NINPUT_LEGACYMODE",
appName, 0, RegistryValueKind.DWord);
}
static UInt32 GetBrowserEmulationMode()
{
int browserVersion = 0;
using (var ieKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Internet Explorer",
RegistryKeyPermissionCheck.ReadSubTree,
System.Security.AccessControl.RegistryRights.QueryValues))
{
var version = ieKey.GetValue("svcVersion");
if (null == version)
{
version = ieKey.GetValue("Version");
if (null == version)
throw new ApplicationException("Microsoft Internet Explorer is required!");
}
int.TryParse(version.ToString().Split('.')[0], out browserVersion);
}
if (browserVersion < 8)
{
throw new ApplicationException("Unsupported version of Microsoft Internet Explorer!");
}
UInt32 mode = 11000; // Internet Explorer 11. Webpages containing standards-based !DOCTYPE directives are displayed in IE11 Standards mode.
switch (browserVersion)
{
case 7:
mode = 7000; // Webpages containing standards-based !DOCTYPE directives are displayed in IE7 Standards mode.
break;
case 8:
mode = 8000; // Webpages containing standards-based !DOCTYPE directives are displayed in IE8 mode.
break;
case 9:
mode = 9000; // Internet Explorer 9. Webpages containing standards-based !DOCTYPE directives are displayed in IE9 mode.
break;
case 10:
mode = 10000; // Internet Explorer 10.
break;
}
return mode;
}
The Tableau Server
Configure it to trust the web server, per all the trusted auth setup. That’s all you need to do.
THE APPLICATION
Initializing the Web Browser to the page
The crux of everything is the .Net WebBrowser control. Bring one into your page layout ( in this example it is webBrowser1 ). get advanced skills from Tableau Advanced Training
As mentioned earlier, if your Tableau Server doesn’t have AD with automatic logon turned on, you’ll want to use HTTP POST to pass the username over to the dynamic page. You do this with the WebBrowser.Navigate method .
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Permissions;
namespace Embedded_Tableau_Example
{
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public partial class Form1 : Form
{
int tab_count = 1;
String username = ""; // Here you can get your username however you'd like
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string postData = String.Format("username={0}", username);
byte[] Post = Encoding.UTF8.GetBytes(postData);
string AdditionalHeaders = "Content-Type: application/x-www-form-urlencoded";
var page_location = "http://{TableauServerLocation}/c_sharp_embed.php"; // HTTPS for security in final environment
//webBrowser1.Navigate(page_location); // If using AD with automatic logon
webBrowser1.Navigate(page_location, "_self", Post, AdditionalHeaders);
webBrowser1.ObjectForScripting = this;
}
private void button1_Click(object sender, EventArgs e)
{
webBrowser1.Document.InvokeScript("pdfExport");
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
object[] args = { tab_count };
webBrowser1.Document.InvokeScript("switchTab", args);
tab_count++;
}
public void Test(String message)
{
MessageBox.Show(message, "client code");
}
private void button3_Click(object sender, EventArgs e)
{
webBrowser1.Document.InvokeScript("returnJSONString");
}
public void retrieveJSON(String json)
{
MessageBox.Show(json, "Stringy JSON");
}
}
}
ExtendedWebBrowser control to reroute popups
Everything in the above section is true, but the standard WebBrowser control has a major flaw — you can cancel the NewWindow event, but you can’t get any information about it. Web Edit, See Underlying Data, and all of the Exports (PDF, PNG, Crosstab) by default open a new IE window, which is not desirable.
The preferable action is capture the URLs of the new windows and reroute them back into our original WebBrowser control so that the actions all happen locally (particularly for the download methods).
For some reason, the Windows.Forms version of the WebBrowser control does not expose all of the events that are actually happening.
For example, NewWindow3 exists in the underlying control and provides the location for the new window being created via the bstrUrl string. How do we get to the NewWindow3 event though? Learn more skills from Learn Tableau Online
The solution provided by Mrojas at Art in Soft creates an ExtendedWebBrowser class that allows for capturing any of the events in the underlying controls. It’s also possible that this reference on Stack Overflow allows for the same thing with less code, but I was able to get the ExtendedWebBrowser code working and tested, so that’s my recommendation.
The entire project is available on GitHub. This is what I added to the Program.cs (I’ve added the necessary NewWindow3 event handler to the original from Art in Soft, with the DWebBrowserEvents2 from Pinvoke.net providing the COM interface code necessary).
To get in-depth knowledge, enroll for a live free demo on Tableau Online Training