In the previous post I wrote about the problem I had showing a WPF window on a second screen. And this is a major requirement of the application. After much searching I came to the conclusion that it could not be done easily. The only reasonable solution seemed to be building a winforms app and hosting a WPF control for the second screen. I tried that and it worked fine. The way to do it is pretty simple. I created a new project called WPFControls, it would get a better name in the real version – this is just to try it out – and created a WPF control which contained a Rich Textbox. It won’t stay that way; it will ultimately be a FlowDocumentReader or a FlowDocumentPageReader, but at this stage I don’t know which one. And in the previous code in WPF I had built a RichTextBox for editing songs and so, to test this line, I will just use the same code to load a song, hence the RTB.
The Rich TextBox loads the song from the database by reading in the flowdocument stored in the lyrics field for the song. All this is described in the previous articles.
On the windows form, which can be shown on any screen, we place a control of type System.Windows.Forms.Integration.ElementHost. In my case this was given the name EditSongHost.
When the EditSongForm is loaded it loads the WPF control and sets the Song property to the currently selected song.
private void EditSongForm_Load(object sender, EventArgs e)
{
WpfControls.SongEditCtrl ctrl = new SongEditCtrl();
ctrl.Song= song;
EditSongHost.Child = ctrl;
}
This worked just fine, but I now had a winforms application with some WPF controls, and this was a second best solution, as far as I was concerned. I really wanted a WPF application. I could have approximated that by building all the user controls in WPF and hosting all of them in windows forms, but that seemed to me to be madness. WPF is a relatively new technology, and maybe that was the only way possible at the moment. If so then, so be it.
But today I came across another solution on the Microsoft Forums, in a reply given by Larry Olsen, WPF Program Manager. This is his solution
1.
Started a new WPF Application project in Visual Studio
2.
Added the needed references to System.Windows.Forms (needed for Screen) and System.Drawing (needed for Rectangle)
3.
Removed the StartupUri in the App.xaml file and overrode the OnStartup() method in code-behind. I did this because StartupUri points to just one Window, Window1.xaml, in the WPF Application template and OnStartup will let me control what happens when the application starts.
4.
Added Window2.xaml to the project so that I would have a second Window to display on a second monitor.
5.
Added code similar to the article you linked to the override of OnStartup which was:Code Block
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);Window1 w1 = new Window1();
Window2 w2 = new Window2();Screen s1 = Screen.AllScreens[0];
Screen s2 = Screen.AllScreens[1];Rectangle r1 = s1.WorkingArea;
Rectangle r2 = s2.WorkingArea;w1.Top = r1.Top;
w1.Left = r1.Left;w2.Top = r2.Top;
w2.Left = r2.Left;w1.Show();
w2.Show();w2.Owner = w1;
}
I adapted that code so that the second window was displayed after clicking a button. It worked perfectly! I just need to make a few changes. First I needed to set the window style to none so that I had a full screen window with no title bar or maximise, minimise and close buttons. This is why it is important to set the window’s Owner property. If you don’t set that and close the main window the second screen is still active and the program is still running. In any event, the second screen needs to be entirely controlled by the primary screen.
When I ran the program the second screen duly opened, but it wasn’t maximised. So I set its WindowState to Maximized and then tried it. It was maximised but it was showing on the primary monitor, not the second monitor. Why was this happening? I have no idea. But after trying a few things I decided to call the Show method and then set the WindowState property to Maximized in code, after the Show() method. I don’t know why this makes a difference, but it does. Well, actually I can see why my solution works, but I don’t understand why setting the property before the Show() method makes a difference. I don’t know what is going on in the bowels of windows, and just to be sure I moved the WindowState property setting line of code to just before the Show() method, so that it was being set in code, not by the designer. I didn’t expect it to help, and it didn’t. No matter, it all seems to work fine.
Here is the code I used, hooked up to a temporary button
private void btnShow_Click(object sender, RoutedEventArgs e)
{
DisplayWindow displayWindow = new DisplayWindow();
Screen s2 = Screen.AllScreens[1];
Rectangle rectangle = s2.WorkingArea;
displayWindow.Top = rectangle.Top;
displayWindow.Left = rectangle.Left;
displayWindow.Show();
displayWindow.WindowState = System.Windows.WindowState.Maximized;
displayWindow.Owner = this;
}
The only niggling doubt I have is that winforms and WPF use different units for screen and control measurement. Winforms uses pixels, and WPF doesn’t. Rather they have Device Independent Pixels which are equal to 1/96 of an inch. There are benefits to the way that WPF does its screen rendering, but even though it looks fine on my two monitor set up, I will have to test it on a projector which is likely to have a drifferent resolution to my monitor. I don’t think that WPF will have a problem with that, but the Screen properties, specifically the WorkingArea is a winforms property. I don’t expect a problem, but it will need to be checked.