/******************************************************************************
*
* File: WindowMove.cs
*
* Description:
* Moves a WCP window from a random location on the desktop to the
* top-left corner of the desktop. This is accomplished by obtaining an
* automation element based on the text value of the window,
* and then obtaining a TransformPattern object from this element.
* If the element is moveable, rects for both the element and the
* desktop are created.
* The top and left coordinates needed to move the parent window are set
* and, if not out of range, the window is moved to the new coordinates.
*
* Programming Elements:
* This sample demonstrates the following UI Automation programming elements
* from the System.Windows.Automation namespace:
* Automation class
* ScopeFlags
* AutomationElement class
* RootElement property
* AutomationIDProperty property
* TryGetCurrentPattern() method
* Current struct
* BoundingRectangle property
* Condition property
* WindowPattern class
* WaitForInputIdle method
* WindowVisualState property
* SetWindowVisualState method
* TransformPattern class
* CanMove property
* Move() method
* AutomationProperty class
* ToString() method
*
*
* This file is part of the Microsoft .NET Framework SDK Code Samples.
*
* Copyright (C) Microsoft Corporation. All rights reserved.
*
* This source code is intended only as a supplement to Microsoft
* Development Tools and/or on-line documentation. See these other
* materials for detailed information regarding Microsoft code samples.
*
* THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*****************************************************************************/
using System;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Threading;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using System.Windows.Threading;
namespace SDKSample
{
///
/// WindowMove application.
///
public class WindowMove : Application
{
private DockPanel informationPanel;
private AutomationElement targetWindow;
private WindowPattern windowPattern;
private TransformPattern transformPattern;
private Point targetLocation;
private TextBox xCoordinate;
private TextBox yCoordinate;
private Button moveTarget;
private StringBuilder feedbackText = new StringBuilder();
private String targetApplication = "Notepad.exe";
// Delegates for updating the client UI based on target application events.
private delegate void FeedbackDelegate(string message);
private delegate void ClientControlsDelegate(Object src);
//
///
/// The Startup handler.
///
/// The event arguments
protected override void OnStartup(StartupEventArgs e)
{
// Start the WindowMove client.
CreateWindow();
try
{
// Obtain an AutomationElement from the target window handle.
targetWindow = StartTargetApp(targetApplication);
// Does the automation element exist?
if (targetWindow == null)
{
Feedback("No target.");
return;
}
Feedback("Found target.");
// find current location of our window
targetLocation = targetWindow.Current.BoundingRectangle.Location;
// Obtain required control patterns from our automation element
windowPattern = GetControlPattern(targetWindow,
WindowPattern.Pattern) as WindowPattern;
if (windowPattern == null) return;
// Make sure our window is usable.
// WaitForInputIdle will return before the specified time
// if the window is ready.
if (false == windowPattern.WaitForInputIdle(10000))
{
Feedback("Object not responding in a timely manner.");
return;
}
Feedback("Window ready for user interaction");
// Register for required events
RegisterForEvents(
targetWindow, WindowPattern.Pattern, TreeScope.Element);
// Obtain required control patterns from our automation element
transformPattern =
GetControlPattern(targetWindow, TransformPattern.Pattern)
as TransformPattern;
if (transformPattern == null) return;
// Is the TransformPattern object moveable?
if (transformPattern.Current.CanMove)
{
// Enable our WindowMove fields
xCoordinate.IsEnabled = true;
yCoordinate.IsEnabled = true;
moveTarget.IsEnabled = true;
// Move element
transformPattern.Move(0, 0);
}
else
{
Feedback("Window is not moveable.");
}
}
catch (ElementNotAvailableException)
{
Feedback("Client window no longer available.");
return;
}
catch (InvalidOperationException)
{
Feedback("Client window cannot be moved.");
return;
}
catch (Exception exc)
{
Feedback(exc.ToString());
}
}
//
///
/// Start the WindowMove client.
///
private void CreateWindow()
{
Window window = new Window();
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Horizontal;
sp.HorizontalAlignment = HorizontalAlignment.Center;
TextBlock txtX = new TextBlock();
txtX.Text = "X-coordinate: ";
txtX.VerticalAlignment = VerticalAlignment.Center;
xCoordinate = new TextBox();
xCoordinate.Text = "0";
xCoordinate.IsEnabled = false;
xCoordinate.MaxLines = 1;
xCoordinate.MaxLength = 4;
xCoordinate.Margin = new Thickness(10, 0, 10, 0);
TextBlock txtY = new TextBlock();
txtY.Text = "Y-coordinate: ";
txtY.VerticalAlignment = VerticalAlignment.Center;
yCoordinate = new TextBox();
yCoordinate.Text = "0";
yCoordinate.IsEnabled = false;
yCoordinate.MaxLines = 1;
yCoordinate.MaxLength = 4;
yCoordinate.Margin = new Thickness(10, 0, 10, 0);
moveTarget = new Button();
moveTarget.IsEnabled = false;
moveTarget.Width = 100;
moveTarget.Content = "Move Window";
moveTarget.Click += new RoutedEventHandler(btnMove_Click);
sp.Children.Add(txtX);
sp.Children.Add(xCoordinate);
sp.Children.Add(txtY);
sp.Children.Add(yCoordinate);
sp.Children.Add(moveTarget);
informationPanel = new DockPanel();
informationPanel.LastChildFill = false;
DockPanel.SetDock(sp, Dock.Top);
informationPanel.Children.Add(sp);
window.Content = informationPanel;
window.Show();
}
///--------------------------------------------------------------------
///
/// Provides feedback in the client.
///
/// The string to display.
///
/// Since the events may happen on a different thread than the
/// client we need to use a Dispatcher delegate to handle them.
///
///--------------------------------------------------------------------
private void Feedback(string message)
{
// Check if we need to call BeginInvoke.
if (this.Dispatcher.CheckAccess() == false)
{
// Pass the same function to BeginInvoke,
// but the call would come on the correct
// thread and InvokeRequired will be false.
this.Dispatcher.BeginInvoke(
DispatcherPriority.Send,
new FeedbackDelegate(Feedback),
message);
return;
}
TextBlock textElement = new TextBlock();
textElement.Text = message;
DockPanel.SetDock(textElement, Dock.Top);
informationPanel.Children.Add(textElement);
}
//
///
/// Handles the 'Move' button invoked event.
/// By default, the Move method does not allow an object
/// to be moved completely off-screen.
///
/// The object that raised the event.
/// Event arguments.
private void btnMove_Click(object src, RoutedEventArgs e)
{
try
{
// If coordinate left blank, substitute 0
if (xCoordinate.Text == "") xCoordinate.Text = "0";
if (yCoordinate.Text == "") yCoordinate.Text = "0";
// Reset background colours
xCoordinate.Background = System.Windows.Media.Brushes.White;
yCoordinate.Background = System.Windows.Media.Brushes.White;
if (windowPattern.Current.WindowVisualState ==
WindowVisualState.Minimized)
windowPattern.SetWindowVisualState(WindowVisualState.Normal);
double X = double.Parse(xCoordinate.Text);
double Y = double.Parse(yCoordinate.Text);
// Should validate the requested screen location
if ((X < 0) ||
(X >= (SystemParameters.WorkArea.Width -
targetWindow.Current.BoundingRectangle.Width)))
{
Feedback("X-coordinate would place the window all or partially off-screen.");
xCoordinate.Background = System.Windows.Media.Brushes.Yellow;
}
if ((Y < 0) ||
(Y >= (SystemParameters.WorkArea.Height -
targetWindow.Current.BoundingRectangle.Height)))
{
Feedback("Y-coordinate would place the window all or partially off-screen.");
yCoordinate.Background = System.Windows.Media.Brushes.Yellow;
}
// transformPattern was obtained from the target window.
transformPattern.Move(X, Y);
}
catch (ElementNotAvailableException)
{
Feedback("Client window no longer available.");
return;
}
catch (InvalidOperationException)
{
Feedback("Client window cannot be moved.");
return;
}
}
//
///
/// Update client controls based on target location changes.
///
/// The object that raised the event.
private void UpdateClientControls(Object src)
{
// If window is minimized, no need to report new screen coordinates
if (windowPattern.Current.WindowVisualState == WindowVisualState.Minimized)
return;
Point ptCurrent =
((AutomationElement)src).Current.BoundingRectangle.Location;
if (targetLocation != ptCurrent)
{
Feedback("Window moved from " + targetLocation.ToString() +
" to " + ptCurrent.ToString());
targetLocation = ptCurrent;
}
if (targetLocation.X != double.Parse(xCoordinate.Text))
{
Feedback("Alert: Final X-coordinate not equal to requested X-coordinate.");
xCoordinate.Text = targetLocation.X.ToString();
}
if (targetLocation.Y != double.Parse(yCoordinate.Text))
{
Feedback("Alert: Final Y-coordinate not equal to requested Y-coordinate.");
yCoordinate.Text = targetLocation.Y.ToString();
}
}
///
/// Window move event handler.
///
/// The object that raised the event.
/// Event arguments.
private void OnWindowMove(Object src, AutomationPropertyChangedEventArgs e)
{
// Pass the same function to BeginInvoke.
this.Dispatcher.BeginInvoke(
DispatcherPriority.Send,
new ClientControlsDelegate(UpdateClientControls), src);
}
///
/// Starts the target application.
///
/// The object that raised the event.
/// Event arguments.
/// The target automation element.
private AutomationElement StartTargetApp(string app)
{
try
{
// Start application.
Process p = Process.Start(app);
if (p.WaitForInputIdle(20000) == true)
Feedback("Window ready for user interaction");
else return null;
// Give application a second to startup.
Thread.Sleep(2000);
// Return the automation element
return AutomationElement.FromHandle(p.MainWindowHandle);
}
catch (ArgumentException e)
{
Feedback(e.ToString());
return null;
}
catch (Win32Exception e)
{
Feedback(e.ToString());
return null;
}
}
///
/// Gets a specified control pattern.
///
///
/// The automation element we want to obtain the control pattern from.
///
/// The control pattern of interest.
/// A ControlPattern object.
private Object GetControlPattern(
AutomationElement ae, AutomationPattern ap)
{
Object oPattern = null;
if (false == ae.TryGetCurrentPattern(ap, out oPattern))
{
Feedback("Object does not support the " +
ap.ProgrammaticName.ToString() + " Pattern");
return null;
}
Feedback("Object supports the " +
ap.ProgrammaticName.ToString() + " Pattern.");
return oPattern;
}
///
/// Register for events of interest.
///
/// The automation element of interest.
/// The control pattern of interest.
/// The tree scope of interest.
private void RegisterForEvents(AutomationElement ae,
AutomationPattern ap, TreeScope ts)
{
if (ap.Id == WindowPattern.Pattern.Id)
{
// The WindowPattern Exposes an element's ability
// to change its on-screen position or size.
// The following code shows an example of listening for the
// BoundingRectangle property changed event on the window.
Feedback("Start listening for WindowMove events for the control.");
// Define an AutomationPropertyChangedEventHandler delegate to
// listen for window moved events.
AutomationPropertyChangedEventHandler moveHandler =
new AutomationPropertyChangedEventHandler(OnWindowMove);
Automation.AddAutomationPropertyChangedEventHandler(
ae, ts, moveHandler,
AutomationElement.BoundingRectangleProperty);
}
}
///
/// Window shut down event handler.
///
/// The exit event arguments.
protected override void OnExit(ExitEventArgs e)
{
Automation.RemoveAllEventHandlers();
base.OnExit(e);
}
///
/// Launch the sample application.
///
internal sealed class TestMain
{
[STAThread()]
static void Main()
{
// Create an instance of the sample class
WindowMove app = new WindowMove();
app.Run();
}
}
}
}