WPF take screenshot of application including hidden content - wpf

Under the category "limitations of the technology":
I have received the requirement to have a screenshot button in my application that will take a screenshot and launch a printer dialog. Fair enough. My code achieves that. I simply take my window, and use a RenderTargetBitmap to render the window.
However, the requirement now states that it should include all content that is hidden behind scrollbars. Meaning, that in the screenshot the application should look "stretched" in order to eliminate scrollbars, and show all data. For instance in case there is a large list or datagrid, all the data should be visible.
Keeping in mind that WPF might be virtualizing and not rendering things that are not in view, is there any way I can achieve this requirement? Is there a possibility of rendering the visual tree to a seperate infinite space and taking a screenshot there? Something else?
In response to comments:
The screenshot button is on an outer shell that only holds the menu. Inside this shell any of 800+ views can be hosted. These views could contain datagrids, lists, large forms consisting of textboxes... anything. There is no way to tell what is 'inside' without walking the visual tree.
The functionality requested is similar to printing a webpage in your browser to PDF. It will also give you the entire DOM instead of just what you see in the limited view of the browser.

XAML:
<Grid>
<Button
x:Name="btnPrint"
Width="50"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Click="BtnPrint_Click"
Content="Print" />
<ScrollViewer Height="500" HorizontalAlignment="Center">
<Grid x:Name="toPrint">
<!--your code goes here-->
</Grid>
</ScrollViewer>
</Grid>
C#:
private void BtnPrint_Click(object sender, RoutedEventArgs e)
{
var pdialog = new PrintDialog();
if (pdialog.ShowDialog() == true)
{
System.Windows.Size pageSize = new System.Windows.Size { Height = pdialog.PrintableAreaHeight, Width = pdialog.PrintableAreaWidth };
toPrint.Measure(pageSize);
toPrint.UpdateLayout();
pdialog.PrintVisual(toPrint, "Print");
}
}

Related

How to set Wpf Prism Region ZIndex

If there are two prism regions defined in a shell, which one is on top is determined by how these are layed out in the shell one below the other.
<Grid Grid.Row="1" x:Name="_redGrid" >
<ContentControl prism:RegionManager.RegionName="RedRegion" />
</Grid>
<Grid Grid.Row="1" x:Name="_greenGrid">
<ContentControl prism:RegionManager.RegionName="GreenRegion" />
</Grid>
For a simple example, you can take a look at this. It has got a red view and a green view.
When you run, it will look like the following.
Is there a way to change this dynamically? Set the zindex of the prism region at run time?
I have extended the above example with a button, objective here is to click the button and change the top to bottom. You can see this example here.
I could do this in the backend file with brute force as follows.
private void btn1_Click(object sender, RoutedEventArgs e)
{
_mainGrid.Children.Remove(_redGrid);
_mainGrid.Children.Remove(_greenGrid);
if (_toggleRegion)
{
_mainGrid.Children.Add(_redGrid);
_mainGrid.Children.Add(_greenGrid);
}
else
{
_mainGrid.Children.Add(_greenGrid);
_mainGrid.Children.Add(_redGrid);
}
_toggleRegion = !_toggleRegion;
}
Click the button, and now the red is on top.
So whats the best way to do this? Is there a feature in Prism which can accomplish this?

Capture screen from a Silverlight Application to be included in a printed report

I want to include in a report (e.g., written in a word file) a screen shot from a Silverlight application. The problem is that the image resolution is very bad and the printed result is unsatisfactory when using the print-screen button.
Is there a way to create better screen captures?
Note: I have access to the source code and i can modify the application if needed. The app is written in Silverlight version 4.
Here's a sample code I use to capture a UIElement and save it as a .png image. The resolution is the size of the displayed element at the time you capture it.
I use PngEncoder from the ImageTools library. You need to add references to it in your project.
Here's a simple XAML page displaying a blue square with green borders, and a button to capture it:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot">
<StackPanel>
<Border x:Name="myElement"
Width="200"
Height="200"
BorderBrush="Green"
Background="AliceBlue"
BorderThickness="2" />
<Button Click="SaveScreenShot"
Content="Capture"/>
</StackPanel>
</Grid>
</UserControl>
And here's the SaveScreenShot method:
private void SaveScreenShot(object sender, RoutedEventArgs e)
{
//Capture the element
var screenShot = new WriteableBitmap((UIElement)myElement, null);
var encoder = new PngEncoder();
SaveFileDialog saveDialog = new SaveFileDialog();
saveDialog.Filter = "Picture Files (*.png)|*.png";
bool? result = saveDialog.ShowDialog();
if (result.Value)
{
using (Stream saveStream = saveDialog.OpenFile())
{
encoder.Encode(screenShot.ToImage(), saveStream);
}
}
}
When clicking the button, it will open a dialog for you to save the captured image as a file. You cannot use the clipboard as Silverlight doesn't allow to put images in it (See the "remarks" section of the MSDN page)
So if you add the button with the associated method in your page, and change the "myElement" in the callback to the name of the element you want to capture, you should be good.

WebBrowser control keyboard and focus behavior

Apparently, there are some serious keyboard and focus issues with WPF WebBrowser control. I've put together a trivial WPF app, just a WebBrowser and two buttons. The app loads a very basic editable HTML markup (<body contentEditable='true'>some text</body>) and demonstrates the following:
Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.
When user switches away from the app (e.g., with Alt-Tab), then goes back, the caret is gone and she is unable to type at all. A physical mouse click into the WebBrowser's window client area is required to get back the caret and keystrokes.
Inconsistently, a dotted focus rectangle shows up around WebBrowser (when tabbing, but not when clicking). I could not find a way to get rid of it (FocusVisualStyle="{x:Null}" does not help).
Internally, WebBrowser never receives the focus. That's true for both logical focus (FocusManager) and input focus (Keyboard). The Keyboard.GotKeyboardFocusEvent and FocusManager.GotFocusEvent events never get fired for WebBrowser (although they both do for buttons in the same focus scope). Even when the caret is inside WebBrowser, FocusManager.GetFocusedElement(mainWindow) points to a previously focused element (a button) and Keyboard.FocusedElement is null. At the same time, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin() returns true.
I'd say, such behaviour is almost too dysfunctional to be true, but that's how it works. I could probably come up with some hacks to fix it and bring it in row with native WPF controls like TextBox. Still I hope, maybe I'm missing something obscure yet simple here. Has anyone dealt with a similar problem? Any suggestions on how to fix this would be greatly appreciated.
At this point, I'm inclined to develop an in-house WPF wrapper for WebBrowser ActiveX Control, based upon HwndHost. We are also considering other alternatives to WebBrowser, such as Chromium Embedded Framework (CEF).
The VS2012 project can be downloaded from here in case someone wants to play with it.
This is XAML:
<Window x:Class="WpfWebBrowserTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="640" Height="480" Background="LightGray">
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="300"/>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
</Window>
This is C# code, it has a bunch of diagnostic traces to show how focus/keyboard events are routed and where the focus is:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Navigation;
namespace WpfWebBrowserTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// watch these events for diagnostics
EventManager.RegisterClassHandler(typeof(UIElement), Keyboard.PreviewKeyDownEvent, new KeyEventHandler(MainWindow_PreviewKeyDown));
EventManager.RegisterClassHandler(typeof(UIElement), Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(MainWindow_GotKeyboardFocus));
EventManager.RegisterClassHandler(typeof(UIElement), FocusManager.GotFocusEvent, new RoutedEventHandler(MainWindow_GotFocus));
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
// load the browser
this.webBrowser.NavigateToString("<body contentEditable='true' onload='focus()'>Line 1<br>Line 3<br>Line 3<br></body>");
this.btnLoad.IsChecked = true;
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
// close the form
if (MessageBox.Show("Close it?", this.Title, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
this.Close();
}
// Diagnostic events
void MainWindow_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Debug.Print("{0}, source: {1}, {2}", FormatMethodName(), FormatType(e.Source), FormatFocused());
}
void MainWindow_GotFocus(object sender, RoutedEventArgs e)
{
Debug.Print("{0}, source: {1}, {2}", FormatMethodName(), FormatType(e.Source), FormatFocused());
}
void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
Debug.Print("{0}, key: {1}, source: {2}, {3}", FormatMethodName(), e.Key.ToString(), FormatType(e.Source), FormatFocused());
}
// Debug output formatting helpers
string FormatFocused()
{
// show current focus and keyboard focus
return String.Format("Focus: {0}, Keyboard focus: {1}, webBrowser.HasFocusWithin: {2}",
FormatType(FocusManager.GetFocusedElement(this)),
FormatType(Keyboard.FocusedElement),
((System.Windows.Interop.IKeyboardInputSink)this.webBrowser).HasFocusWithin());
}
string FormatType(object p)
{
string result = p != null ? String.Concat('*', p.GetType().Name, '*') : "null";
if (p == this.webBrowser )
result += "!!";
return result;
}
static string FormatMethodName()
{
return new StackTrace(true).GetFrame(1).GetMethod().Name;
}
}
}
[UPDATE] The situation doesn't get better if I host WinForms WebBrowser (in place of, or side-by-side with WPF WebBrowser):
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<WindowsFormsHost Name="wfHost" Focusable="True" Height="150" Margin="10,10,10,10">
<wf:WebBrowser x:Name="wfWebBrowser" />
</WindowsFormsHost>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
The only improvement is that I do see focus events on WindowsFormsHost.
[UPDATE] An extreme case: two WebBrowser controls with two carets showing at the same time:
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<WebBrowser Name="webBrowser2" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
this.webBrowser.NavigateToString("<body onload='text.focus()'><textarea id='text' style='width: 100%; height: 100%'>text</textarea></body>");
this.webBrowser2.NavigateToString("<body onload='text.focus()'><textarea id='text' style='width: 100%; height: 100%'>text2</textarea></body>");
This also illustrates that the focus handling issue is not specific to contentEditable=true content.
For anyone else stumbling upon this post and needing to set keyboard focus to the browser control (not a particular element within the control, necessarily), this bit of code worked for me.
First, add a project reference (under Extensions in VS) for Microsoft.mshtml.
Next, whenever you'd like to focus the browser control (say for example, when the Window loads), simply "focus" the HTML document:
// Constructor
public MyWindow()
{
Loaded += (_, __) =>
{
((HTMLDocument) Browser.Document).focus();
};
}
This will place keyboard focus inside the web browser control, and inside the "invisible" ActiveX window, allowing keys like PgUp / PgDown to work on the HTML page.
If you want to, you might be able to use DOM selection to find a particular element on the page, and try to focus() that particular element. I have not tried this myself.
The reason it behaves this way is related to the fact that it's an ActiveX control which itself is a fully windows class (it handles mouse and keyboard interaction). In fact much of the time you see the component used you'll find it is the main component taking up a full window because of this. It doesn't have to be done that way but it presents issues.
Here's a forum discussing the exact same issue and it's causes can be clarified by reading the last commentators article links:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/1b50fec6-6596-4c0a-9191-32cd059f18f7/focus-issues-with-systemwindowscontrolswebbrowser
To outline the issues you're having
Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.
that's because the browser control itself is a window which can be tabbed to. It doesn't "forward" the tab to it's child elements immediately.
One way to change this would be to handle the WM message for the component itself but keep in mind that doing so gets tricky when you want the "child" document inside of it to be able to handle messages.
See: Prevent WebBrowser control from stealing focus? specifically the "answer". Although their answer doesn't account that you can control whether the component interacts through dialogs with the user by setting the Silent property (may or may not exist in the WPF control... not sure)
When user switches away from the app (e.g., with Alt-Tab), then goes back, the caret is gone and she is unable to type at all. A physical mouse click into the WebBrowser's window client area is required to get back the caret and keystrokes.
This is because the control itself has received the focus. Another consideration is to add code to handle the GotFocus event and to then "change" where the focus goes. Tricky part is figuring out if this was "from" the document -> browser control or your app -> browser control. I can think of a few hacky ways to do this (variable reference based on losing focus event checked on gotfocus for example) but nothing that screams elegant.
Inconsistently, a dotted focus rectangle shows up around WebBrowser (when tabbing, but not when clicking). I could not find a way to get rid of it (FocusVisualStyle="{x:Null}" does not help).
I wonder if changing Focusable would help or hinder. Never tried it but I'm going to venture a guess that if it did work it would stop it from being keyboard navigable at all.
Internally, WebBrowser never receives the focus. That's true for both logical focus (FocusManager) and input focus (Keyboard). The Keyboard.GotKeyboardFocusEvent and FocusManager.GotFocusEvent events never get fired for WebBrowser (although they both do for buttons in the same focus scope). Even when the caret is inside WebBrowser, FocusManager.GetFocusedElement(mainWindow) points to a previously focused element (a button) and Keyboard.FocusedElement is null. At the same time, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin() returns true.
People have hit issues with where 2 browser controls both show the focus(well... the caret) or even had a hidden control take the focus.
All in all it's pretty awesome what you can do with the component but it's just the right mix of letting you control/change the behavior along with predefined sets of behavior to be maddening.
My suggestion would be to try to subclass the messages so you can direct the focus control directly through code and bypass it's window from trying to do so.

How to load user control with scroll bars in WPF?

I am trying to load a UserControl in the Window by using content control's content property in code behind. Every thing is fine, but my friends were not able to see all the control on the page due to resolution problem. How can I fix this to have scroll bar. I have tried putting ScrollViewer also, but it's not working. So, my solution works on the bigger Window which developed, but its not working on the Smaller resolution windows.
Sample code structure of loading the UserControl:
Window.Xaml
<ScrollViewer>
<ContentControl Name="ContentX" Margin="15,10,15,0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label FontWeight="Black" FontSize="18" FontFamily="Calibri">Some Content</Label>
</ContentControl>
</ScrollViewer>
Window.Xaml.cs
ContentX.Content = new UserControl();
UserControl.Xaml
//Contains the Code for User Control
I got it solved my Content Control is in another Grid Column where i fixed the width and height to be *, now i removed it it's working fine when i place it inside a Scroll Viewer

WP7 Silverlight -- tapping image in a stackpanel scrollview to show that same picture in another grid

I am probably the absolute worst "coder" trying to create a Windows Phone 7 app, but I am in dire need of help, and some of you may even consider it to be laughably easy (which it probably is).
My problem: How on earth do I code the tapping of a picture from one grid to display as a bigger image on another grid?
And I'll elaborate:
I have an app page (in landscape mode only), with two grids splitting the screen.
The first grid (smallgrid) contains a Scrollviewer (small) with a Stackpanel (smallimages) of images reduced to 1/10 of their size within it, essentially showing thumbnails of images.
The second grid (contentgrid) is designed where once you tap on a thumbnail image from smallgrid that image will be shown in contentgrid
By default, balloon0 is displayed in the contentgrid and will change when a person taps on one of the smaller images.
I'll try to provide some mock code for this:
<grid x:name="smallgrid">
<scrollviewer x:name="small">
<stackpanel x:name="smallimages">
<image="balloon0.jpg"><image>
<image="balloon1.jpg"><image>
<image="balloon2.jpg"><image>
<image="balloon3.jpg"><image>
</stackpanel>
</scrollviewer>
</grid>
<grid x:name="contentgrid">
<image source="balloon0.jpg"><image>
</grid>
The code behind is where I need help. I am thinking I either use a button that once clicked, that image then replaces the image in contentgrid but I have no idea how to do that.
Or I can use a gesturelistener that when an image is tapped, it will replace the image in contentgrid... but I also don't know how to do that.
Any insight is helpful. Thank you for any help, as I am not a C# coder, let alone know the language or WP7 silverlight too well.
Be sure to add the Silverlight Toolkit assembly reference to your phoneapplicationpage element (and as a reference to the project):
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
You can use the GestureListener from the Silverlight Toolkit in your XAML like this (also be sure to add the name property to your large image):
<grid x:name="smallgrid">
<scrollviewer x:name="small">
<stackpanel x:name="smallimages">
<image="balloon0.jpg">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="smallImage_Tap" />
</toolkit:GestureService.GestureListener>
<image>
<image="balloon1.jpg">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="smallImage_Tap" />
</toolkit:GestureService.GestureListener>
<image>
<image="balloon2.jpg">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="smallImage_Tap" />
</toolkit:GestureService.GestureListener>
<image>
<image="balloon3.jpg">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="smallImage_Tap" />
</toolkit:GestureService.GestureListener>
<image>
</stackpanel>
</scrollviewer>
</grid>
<grid x:name="contentgrid">
<image x:Name="BigImage" source="balloon0.jpg"><image>
</grid>
Then in your code-behind you can handle the event like this:
private void smallImage_Tap(object sender, GestureEventArgs e)
{
BigImage.Source = (sender as Image).Source;
}
If you look into the toolkit source code, then it appears that GestureListener.Tap event is generated whenever XNA TouchPanel detects Tap gesture. Intuitively I would expect that this happens whenever MouseLeftButtonUp event is generated. Ok, not always, but in the described type of interaction it is basically "always".
Hence I feel both these levels (XNA and Toolkit classes) as unnecessary overhead - at least for something as simple as the tap event. (Other negative consequences: App size increases as you have to include the toolkit, slower launching as more assemblies have to be loaded.)
Having said that, I would start by simply listening to MouseLeftButtonUp event as for example
<Image Source="123.jpg" MouseLeftButtonUp="smallImage_Tap" ImageOpened="..." ImageFailed="..."/>
I included also ImageOpened/Failed events. You can optionally use them to fine tune your app. They could solve problems such as too frequent tap events or image load failures.

Resources