I'm working on a presenttion project for which I needed to set the display on which a set of WPF applications are running. There's a total of 6 WPF applications that will be running on the same machine at once. Each application is going to be running on a different display. I thought I would share the solution that I used to control which display that each instance of this application went to.
The first thing I needed to do was to retrieve the information on how how many displays that the computer has and the coordinates of each display. The computer creates a single (logical) display and maps each display device to cover a range of coordinates on this logical display. So I needed to retrieve a list of the displays and the coordinates to which it is mapped. The class available for doing this is in in the System.Windows.Forms library. Since I wasn't making a Windows Forms application I didn't want to add a using directive that would include the entire library; if I did there would be some class names that exists both in this name space and a WPF name space that I was using that could cause some resolution issues. So I only included the single class from the namespace.
using Screen=System.Windows.Forms.Screen
The Screen
class contains a member named AllScreens
that contains a collection of Screen
objects that give the information on each screen. If you wanted to make a simple WPF program that displayed all of the screens and their positions it only takes a few lines of code. The following is the code for the code-behind and the XAML for such a program.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = Screen.AllScreens;
}
}
<Window x:Class="ScreenTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DeviceName}" />
<TextBlock Grid.Column="1" Text="{Binding WorkingArea.X}" />
<TextBlock Grid.Column="2" Text="{Binding WorkingArea.Y}" />
<TextBlock Grid.Column="3" Text="{Binding WorkingArea.Width}" />
<TextBlock Grid.Column="4" Text="{Binding WorkingArea.Height}" />
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Screenshot of the interface running
For the programs that I'll be running I've set device name for the program's intended display in a configuration file. The typical display name for the first display is \\.\DISPLAY1
. I've made a method that will take the indended display name, try to find it, and set the position and size of the window accordingly.
void SetTargetDisplay()
{
var targetDeviceName = Settings.Default.DisplayDevice;
if(!String.IsNullOrEmpty(targetDeviceName))
{
var screen = (from s in Screen.AllScreens
where s.DeviceName.ToLower().Equals(targetDeviceName.ToLower())
select s).FirstOrDefault();
if (screen != null)
{
Left = screen.WorkingArea.Left;
Top = screen.WorkingArea.Top;
Width = screen.WorkingArea.Width;
Height = screen.WorkingArea.Height;
}
}
}
If the display doesn't exists (which could happen because of a typographical error or the program having been configured for another machine) then the method will just ignore the request.