I need to print document from WebBrowser control as landscape (without showing PrintPreview and changing default printer page orientation).
I've tried:
TemplatePrinter
Here's my template template.html:
<HTML XMLNS:IE>
<HEAD>
<?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default">
<TITLE>Landscape</TITLE>
</HEAD>
<BODY>
<IE:TEMPLATEPRINTER id="Printer"/>
<SCRIPT Language="JavaScript">
Printer.orientation="landscape";
</SCRIPT>
</BODY>
</HTML>
And I try to use it:
mshtml.IHTMLDocument2 doc = wbReport.Document.DomDocument as mshtml.IHTMLDocument2;
doc.execCommand("print", false, "template.tpl");
Create reg key "orientation" with value "2" in HKCU\Software\Microsoft\Internet Explorer\PageSetup
Add style like:
<style type="text/css" media="print">
#page
{
size: landscape;
margin: 2cm;
}
</style>
But I still print my html as portrait.
Maybe there is better solution to print html as landscape or I have some mistakes when using TemplatePrinter?
After messing around with the registry, attempting to use print templates in IE and various other methods (none of which worked) i've produced a somewhat hacky workaround.
I've extended the WebBrowser control and created a new print preview method inside which I have methods to pass the ALT+L key combination to enable landscape view:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace YourProjectNamespace
{
class ExWebBrowser : WebBrowser
{
void setLandscapeTimer_Tick(object sender, EventArgs e)
{
SendKeys.SendWait("%L");
this.ProcessDialogKey(Keys.Alt & Keys.L);
SendKeys.Flush();
}
public void _ShowPrintPreviewDialog()
{
this.ShowPrintPreviewDialog();
this.Focus();
Timer setLandscapeTimer = new Timer();
setLandscapeTimer.Interval = 500;
setLandscapeTimer.Tick += new EventHandler(setLandscapeTimer_Tick);
setLandscapeTimer.Start();
}
}
}
All you need to do is simply instantiate the object, load a document as with the standard WebBrowser and call _ShowPrintPreviewDialog.
Note you may want to stop the timer once the landscape view has been set.
HTH somebody with the same issue.
Related
I'm trying to dynamically create an image within a WebView2, using a source passed in from the WebView2's containing application.
The source image exists on an arbitrary location on the filesystem. To allow the WebView2 to access the image, I'm using SetVirtualHostNameToFolderMapping, which maps a hostname within the webview to some folder.
I want this to happen only after some interaction from inside the webview, using postMessage and the WebMessageReceived event, as described here.
If I call SetVirtualHostNameToFolderMapping outside of the WebMessageReceived handler, even after I've navigated to the HTML using NavigateToString, the webview loads the image.
But if I call SetVirtualHostNameToFolderMapping within the WebMessageReceived handler:
webview.CoreWebView2.WebMessageReceived += (s, e) => {
webview.CoreWebView2.SetVirtualHostNameToFolderMapping(
"assets",
#"C:\path\to\folder",
CoreWebView2HostResourceAccessKind.Allow
);
webview.CoreWebView2.PostWebMessageAsString(#"breakpoint.bmp");
};
the webview can't find the image; it fails with:
Failed to load resource: net::ERR_NAME_NOT_RESOLVED
How can I debug this? Are there any other alternatives I could use to do something similar?
XAML:
<Window x:Class="_testWebviewDialogs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wv="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf">
<wv:WebView2 x:Name="webview" />
</Window>
Code-behind:
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Web.WebView2.Core;
namespace _testWebviewDialogs;
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
Loaded += async (s, e) => await initializeAsync();
}
private async Task initializeAsync() {
await webview.EnsureCoreWebView2Async();
webview.CoreWebView2.WebMessageReceived += (s, e) => {
webview.CoreWebView2.SetVirtualHostNameToFolderMapping(
"assets",
#"C:\path\to\folder",
CoreWebView2HostResourceAccessKind.Allow
);
webview.CoreWebView2.PostWebMessageAsString(#"breakpoint.bmp");
};
var html = await File.ReadAllTextAsync("container.html");
webview.NavigateToString(html);
}
}
container.html:
<!DOCTYPE html>
<html>
<body>
<button id="button1">Click me</button>
<img id ="img1" src="" />
<script>
document.getElementById("button1").addEventListener('click', ev => {
chrome.webview.postMessage('source');
});
window.chrome.webview.addEventListener('message', event => {
document.getElementById('img1').src = `https://assets/${event.data}`;
});
</script>
</body>
</html>
I filed an issue for this. Per the response, this behavior is documented:
As the resource loaders for the current page might have already been created and running, changes to the mapping might not be applied to the current page and a reload of the page is needed to apply the new mapping. (source)
But it's possible to create (and recreate) a symbolic link to different target paths, and map the virtual host name to that symbolic link. The virtual host mapping uses the current symlink target.
var symlinkPath =
Path.Combine(
Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location!
)!,
"assets"
);
Directory.CreateDirectory(symlinkPath);
webview.CoreWebView2.SetVirtualHostNameToFolderMapping(
"assets",
symlinkPath,
CoreWebView2HostResourceAccessKind.Allow
);
webview.CoreWebView2.WebMessageReceived += (s, e) => {
Directory.Delete(symlinkPath);
Directory.CreateSymbolicLink(symlinkPath, #"C:\path\to\target\directory");
webview.CoreWebView2.PostWebMessageAsString(#"breakpoint.bmp");
};
In this post I wanted to figure out how to create dynamically created textboxes in C# Visual Studio.
Adding additional textboxes to aspx based on xml
However, when I try to call the ID of these dynamically created textboxes later in my code to figure out what text the user entered into them, I am getting an error that says these IDs do not exist in the current context. Does anyone know how I would be able to call these?
credit to Adding additional textboxes to aspx based on xml
Here is my entire code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Linq;
namespace WebApplication4
{
public partial class WebForm15 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsCallback)
{
//credit to https://stackoverflow.com/questions/44076955/adding-additional-textboxes-to-aspx-based-on-xml#comment75336978_44078684
const string xml = #"<Number>
<Num>1</Num>
<Num>2</Num>
<Num>3</Num>
</Number>";
XDocument doc = XDocument.Parse(xml);
int i = 0;
foreach (XElement num in doc.Root.Elements())
{
TextBox box = new TextBox
{
ID = "dynamicTextBox" + i,
Text = num.Value,
ReadOnly = false
};
divToAddTo.Controls.Add(box);
divToAddTo.Controls.Add(new LiteralControl("<br/>"));
i++;
}
}
}
protected void BtnGetValues_Click(object sender, EventArgs e)
{
IList<string> valueReturnArray = new List<string>();
foreach (Control d in divToAddTo.Controls)
{
if (d is TextBox)
{
valueReturnArray.Add(((TextBox)d).Text);
}
}
//valueReturnArray will now contain the values of all the textboxes
}
}
}
Here is aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm15.aspx.cs" Inherits="WebApplication4.WebForm15" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
</head>
<body>
<form id="form1" runat="server">
<div id="divToAddTo" runat="server" />
<asp:Button runat="server" ID="BtnGetValues" Text="GetValues" OnClick="BtnGetValues_Click" />
</form>
</body>
</html>
Figured it out!!! Here is what I found after scouring the internet for hours
Solution:
When using dynamic controls, you must remember that they will exist only until the next postback.ASP.NET will not re-create a dynamically added control. If you need to re-create a control multiple times, you should perform the control creation in the PageLoad event handler ( As currently you are just creating only for first time the TextBox using Condition: !IsPostabck ). This has the additional benefit of allowing you to use view state with your dynamic control. Even though view state is normally restored before the Page.Load event, if you create a control in the handler for the PageLoad event, ASP.NET will apply any view state information that it has after the PageLoad event handler ends.
So, Remove the Condition: !IsPostback, So that each time the page Loads, The TextBox control is also created. You will also see the State of Text box saved after PageLoad handler completes. [ Obviously you have not disabled ViewState!!! ]
Example:
protected void Page_Load(object sender, EventArgs e)
{
TextBox txtBox = new TextBox();
// Assign some text and an ID so you can retrieve it later.
txtBox.ID = "newButton";
PlaceHolder1.Controls.Add(txtBox);
}
Now after running it, type anything in text box and see what happens when you click any button that causes postback. The Text Box still has maintained its State!!!
Let's say I have the following page:
<html>
<body>
<content1>
</content1>
<content2>
</content2>
<content3>
</content3>
</body>
</html>
I want in my Metro app to display only the part of the page contained between <content2></content2>. For a full page I would use a <WebView> and the Navigate() method. But I don't seem to find a way to adapt that to what I need.
to actually write any solution code I would need to know what are Content1 and content3. assuming them being DIV with an ID, I can say
void WebView5_LoadCompleted(object sender, NavigationEventArgs e)
{
string script = #"var d=document.getElementById('content1');d.style.visibility='hidden'";
string[] args = { script };
string foo = WebView5.InvokeScript("eval", args);
}`
The following code creates file.aspx and file.aspx.cs:
protected void Button1_Click(object sender, EventArgs e)
{
string fielName = Server.MapPath("~/file.aspx");
TextWriter tw = new StreamWriter(fielName);
tw.WriteLine(#"<%# Page Language=""C#"" AutoEventWireup=""true"" CodeFile=""file.aspx.cs"" Inherits=""file"" %>
<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">
<html xmlns=""http://www.w3.org/1999/xhtml"">
<head runat=""server"">
<title></title>
</head>
<body>
<form id=""form1"" runat=""server"">
<div>
</div>
</form>
</body>
</html>
");
tw.Close();
tw = new StreamWriter(fielName + ".cs");
tw.WriteLine(#"using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
public partial class file : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(""new File "");
}
}
");
tw.Close();
}
I want to make the page name written in my Textbox.
I have tried putting textbox in html source of above code, but I'm getting an error.
CodeFile="""+TextBox1.Text+""" Inherits="""+TextBox1.Text+"""
You would be much farther ahead to work the way ASP.NET "thinks" about the page. I once worked on a very large, dynamic questionnaire. All of the controls were generated dynamically along with validations and everything else. At it's core, the way we did it was:
place a panel on the page
add controls to the panel
the code very roughly would look something like this:
var btn = new Button();
btn.ID = "theId";
btn.Text = "hi";
pnlDynamic.Controls.Add(btn);
Because you're dealing with dynamic controls, you might also want to make sure that you understand the page life-cycle...: http://msdn.microsoft.com/en-us/library/ms178472(v=vs.100).aspx
Make sure your web project is declared as a web site rather than a web application.
A web site is willing to dynamically compile each page on demand, unlike a web application, so something like this is in principle doable. If you really want to do it.
I am writing a bit of code to add a link tag to the head tag in the code behind... i.e.
HtmlGenericControl css = new HtmlGenericControl("link");
css.Attributes["rel"] = "Stylesheet";
css.Attributes["type"] = "text/css";
css.Attributes["href"] = String.Format("/Assets/CSS/{0}", cssFile);
to try and achieve something like...
<link rel="Stylesheet" type="text/css" href="/CSS/Blah.css" />
I am using the HtmlGenericControl to achieve this... the issue I am having is that the control ultimatly gets rendered as...
<link rel="Stylesheet" type="text/css" href="/CSS/Blah.css"></link>
I cant seem to find what I am missing to not render the additional </link>, I assumed it should be a property on the object.
Am I missing something or is this just not possible with this control?
I think you'd have to derive from HtmlGenericControl, and override the Render method.
You'll then be able to write out the "/>" yourself (or you can use HtmlTextWriter's SelfClosingTagEnd constant).
Edit: Here's an example (in VB)
While trying to write a workaround for umbraco.library:RegisterStyleSheetFile(string key, string url) I ended up with the same question as the OP and found the following.
According to the specs, the link tag is a void element. It cannot have any content, but can be self closing. The W3C validator did not validate <link></link> as correct html5.
Apparently
HtmlGenericControl css = new HtmlGenericControl("link");
is rendered by default as <link></link>. Using the specific control for the link tag solved my problem:
HtmlLink css = new HtmlLink();
It produces the mark-up <link/> which was validated as correct xhtml and html5.
In addition to link, System.Web.UI.HtmlControls contains classes for other void element controls, such as img, input and meta.
Alternatively you can use Page.ParseControl(string), which gives you a control with the same contents as the string you pass.
I'm actually doing this exact same thing in my current project. Of course it requires a reference to the current page, (the handler), but that shouldn't pose any problems.
The only caveat in this method, as I see it, is that you don't get any "OO"-approach for creating your control (eg. control.Attributes.Add("href", theValue") etc.)
I just created a solution for this, based on Ragaraths comments in another forum:
http://forums.asp.net/p/1537143/3737667.aspx
Override the HtmlGenericControl with this
protected override void Render(HtmlTextWriter writer)
{
if (this.Controls.Count > 0)
base.Render(writer); // render in normal way
else
{
writer.Write(HtmlTextWriter.TagLeftChar + this.TagName); // render opening tag
Attributes.Render(writer); // Add the attributes.
writer.Write(HtmlTextWriter.SelfClosingTagEnd); // render closing tag
}
writer.Write(Environment.NewLine); // make it one per line
}
The slightly hacky way.
Put the control inside a PlaceHolder element.
In the code behind hijack the render method of the PlaceHolder.
Render the PlaceHolders content exactly as you wish.
This is page / control specific and does not require any overrides. So it has minimal impact on the rest of your system.
<asp:PlaceHolder ID="myPlaceHolder" runat="server">
<hr id="someElement" runat="server" />
</asp:PlaceHolder>
protected void Page_Init(object sender, EventArgs e)
{
myPlaceHolder.SetRenderMethodDelegate(ClosingRenderMethod);
}
protected void ClosingRenderMethod(HtmlTextWriter output, Control container)
{
var voidTags = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "br", "hr", "link", "img" };
foreach (Control child in container.Controls)
{
var generic = child as HtmlGenericControl;
if (generic != null && voidTags.Contains(generic.TagName))
{
output.WriteBeginTag(generic.TagName);
output.WriteAttribute("id", generic.ClientID);
generic.Attributes.Render(output);
output.Write(HtmlTextWriter.SelfClosingTagEnd);
}
else
{
child.RenderControl(output);
}
}
}