How do I get access to MessageBox using the low level WPF Automation API?
I have searched all over but there seems to be very little documentation for this. I would rather not use White as I need more control than it gives.
Thanks
Lets suppose you have that simple WPF application:
Xaml:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Button Name="Button1" Content="Click Me" Click="Button1_Click" />
</Grid>
</Window>
Code:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(this, "hello");
}
}
You can automate this application with a console app sample like this (run this once you have started the first project):
class Program
{
static void Main(string[] args)
{
// get the WPF app's process (must be named "WpfApplication1")
Process process = Process.GetProcessesByName("WpfApplication1")[0];
// get main window
AutomationElement mainWindow = AutomationElement.FromHandle(process.MainWindowHandle);
// get first button (WPF's "Button1")
AutomationElement button = mainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));
// click it
InvokePattern invoke = (InvokePattern)button.GetCurrentPattern(InvokePattern.Pattern);
invoke.Invoke();
// get the first dialog (in this case the message box that has been opened by the previous button invoke)
AutomationElement dlg = mainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "Dialog"));
AutomationElement dlgText = dlg.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text));
Console.WriteLine("Message Box text:" + dlgText.Current.Name);
// get the dialog's first button (in this case, 'OK')
AutomationElement dlgButton = dlg.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));
// click it
invoke = (InvokePattern)dlgButton.GetCurrentPattern(InvokePattern.Pattern);
invoke.Invoke();
}
Related
So I've been doing research on this for quite a few weeks now, and haven't really come up with an answer on why this doesn't work properly... I've even researched JumpLists to see if this was what I was looking for, but also to no avail. This problem relates to when you attempt to select 'Close All Windows' by right clicking an app's icon on the task bar...
For example, here is an EXTREMELY small and simple WPF application I wrote to demonstrate the problem I am having. Here is the app's icon in the task bar with its choices on the context menu for it...
contextmenutoolbar
I am selecting the choice 'Close all windows', for reference (the bottom one, with the X to the left of it).
This is a WPF application and here is the code for App.xaml:
<Application x:Class="CloseAllWindows.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Application.Resources>
</Application.Resources>
</Application>
Here is App.xaml.cs, which launches the MainWindow. It also sets the application's MainWindow property to the MainWindow that is instantiated. It also sets ShutdownMode to be only when the main window is closed... I don't want the application to still run if the main window is closed and some secondary windows are left open.
using System.Windows;
namespace CloseAllWindows
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
ShutdownMode = ShutdownMode.OnMainWindowClose;
var mainWindow = new MainWindow();
Application.Current.MainWindow = mainWindow;
mainWindow.Show();
}
}
}
Here is the code for MainWindow.xaml:
<Window x:Class="CloseAllWindows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CloseAllWindows"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Content="NewWindow" Click="ButtonBase_OnClick"></Button>
</StackPanel>
</Window>
And here is the code behind for it... which launches a secondary window when I click a button. It is setting the parent window (Owner property) to the main window, like all the examples I've seen say it should be set, and then call Show() on it.
using System;
using System.ComponentModel;
using System.Windows;
namespace CloseAllWindows
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var childWindow = new ChildWindow {Owner = this};
childWindow.Show();
}
}
}
Here is the code for the child window, ChildWindow.xaml:
<Window x:Class="CloseAllWindows.ChildWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CloseAllWindows"
mc:Ignorable="d"
Title="ChildWindow" Height="300" Width="300">
<Grid>
</Grid>
</Window>
And it's corresponding code behind, ChildWindow.xaml.cs:
using System;
using System.Windows;
namespace CloseAllWindows
{
/// <summary>
/// Interaction logic for ChildWindow.xaml
/// </summary>
public partial class ChildWindow : Window
{
public ChildWindow()
{
InitializeComponent();
}
}
}
As you can see, these classes do not do very much... it was the simplest example of code I could write that shows the problem I am having. So the issue is, if I select Close all windows from the task bar context menu, it never closes all the windows. Instead, it will close the one child window, and still leave the main window open. Interestingly, I hear the windows dialogue chime when I do this, almost like its getting interrupted by something, but I have no idea what.
It also appears to act very randomly... if I spawn 20 windows, it will sometimes close 6 of the windows, then all of them... sometimes it will close a few windows one by one, then close the rest... sometimes it will close all child windows and leave only the main window open. Needless to say, I am pretty baffled as to the behaviour since it doesn't seem to follow any noticeable pattern... any help greatly appreciated! And hopefully the example is good enough to explain what I am trying to get at....
Well you could add an event that will close every window the event is implanted in. try this example:
step 1: add a class to your project call it whatever you want, I called it CloseWindowListener, add this code to your class:
public static class CloseWindowListener
{
public static event EventHandler<EventArgs> ClosingWindows;
public static void CloseWindows()
{
var CWindows = ClosingWindows;
if (CWindows != null)
{
CWindows(null, EventArgs.Empty);
}
}
}
step 2: Add the event handler to the window you desire to close when it is called.
public partial class TestWindow1 : Window
{
public TestWindow1 ()
{
InitializeComponent();
CloseWindowListener.ClosingWindows += CloseWindowListener_ClosingWindows;
}
private void CloseWindowListener_ClosingWindows(object sender, EventArgs e)
{
this.Close();
}
}
step 3: simply call the event from your main window or where ever you want.
private void button_Click_1(object sender, RoutedEventArgs e)
{
CloseWindowListener.CloseWindows();
}
I am familiar with basic code to display an Emgu image in a WPF image box, when all the source code is in the MainWindow.xaml.cs code-behind.
However I am trying to place my Emgu-related code, including the "ProcessFrame" event / Queryframe snippet, into a separate class of static methods so that I can reuse them later. I am doing this because while I will want to be able to grab images from the same camera at a later stage, I also want the flexibility to display those images in a different image box. I am having trouble with this step.
If I could bind the Image box dynamically to a property in the static method (and also enable / disable that binding programmatically), I think that would solve my problem. However, there may be some other problem with the approach I am trying to take. Any code / xaml modifications greatly appreciated.
The following code works, but is unsatisfactory because it forces me to bundle ProcessFrame method into the MainWindow code:
XAML (working):
<Window x:Class="EmguWPF_Test.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>
<Image Height="215" HorizontalAlignment="Left" Margin="62,66,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="224" />
</Grid>
</Window>
MainWindow Code Snippet (working):
//using statements etc
public partial class MainWindow : Window
{
private Image<Bgr, Byte> image;
private Capture capture = null;
private void button1_Click(object sender, RoutedEventArgs e)
{
InitializeCameras();
timer = new DispatcherTimer();
timer.Tick+=new EventHandler(ProcessFrame);
timer.Interval = new TimeSpan(0, 0, 0, 0, 10);
timer.Start();
}
private void InitializeCameras()
{
if (capture == null)
{
try
{
capture = new Capture(0);
}
catch // etc
}
}
private void ProcessFrame(object sender, EventArgs arg)
{
image = capture.QueryFrame();
image1.Source = BitmapSourceConvert.ToBitmapSource(image);
}
}
public static class BitmapSourceConvert
{
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
public static BitmapSource ToBitmapSource(IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
return bs;
}
}
}
The following code is where I am up to but need help:
XAML (same as before)
<Window x:Class="EmguWPF_Test.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>
<Image Height="215" HorizontalAlignment="Left" Margin="62,66,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="224" />
</Grid>
</Window>
ViewModel Snippet (yes - perhaps too ambitious to be experimenting with design patterns):
public ViewModel()
{
CaptureMethods.InitializeCameras();
timer = new DispatcherTimer();
timer.Tick += new EventHandler(CaptureMethods.ProcessFrame);
timer.Interval = new TimeSpan(0, 0, 0, 0, 10);
timer.Start();
}
CaptureMethods class, not working as a separate class in the way I want it to. You will notice I am now defining the capture field in this class, not in the ViewModel class:
class CaptureMethods
{
private static Capture capture = null;
public static void InitializeCameras()
{
if (capture == null)
{
try
{
capture = new Capture(0);
}
catch // etc
}
}
public static void ProcessFrame(object sender, EventArgs arg)
{
image = capture.QueryFrame();
image1.Source = BitmapSourceConvert.ToBitmapSource(image); // this is my problem line
}
}
// BitmapSourceConvert class not repeated here to avoid duplication.
Thanks!
My suggestion is not to use the WPF Image Box, but the Emgu's ImageBox (Emgu.CV.UI.ImageBox). It is a more complete control and it is designed to use with the framework.
The only problem is that type of control only works with Windows Forms, but you can always create a WinForms User Control with a Emgu's Image Box and use it in WPF inside a WindowsFormsHost.
To expand a bit on celsoap7's answer here is what the resulting XAML might look like:
<Window x:Class="WPFEmguCV.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:emui="clr-namespace:Emgu.CV.UI;assembly=Emgu.CV.UI"
Title="MainWindow" Height="521" Width="1274">
<Grid>
<WindowsFormsHost>
<emui:ImageBox x:Name="CapturedImageBox" Width="409" Height="353" />
</WindowsFormsHost>
</Grid>
</Window>
I (and others) found that marshaling the images onto the UI thread takes too much CPU and so you are better off doing as celsoap7 suggests and put an EmguCV ImageBox inside a WPF WindowsFormsHost.
Sadly that may make the kind of MVVM binding you are asking about quite different from the structure you envisage.
Whenever I try to move focus programmatically the focus visual (the dotted rectangle) does not display.
What can be done to force this visual to display?
<Window x:Class="WpfApplication2.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" Loaded="OnLoaded">
<StackPanel>
<TextBlock x:Name="a" Focusable="True">A</TextBlock>
<TextBlock Focusable="True">B</TextBlock>
<Button Focusable="False" Click="OnClick">Move Focus</Button>
</StackPanel>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Keyboard.Focus(a);
}
private void OnClick(object sender, RoutedEventArgs e)
{
var request = new TraversalRequest(FocusNavigationDirection.Next);
var elementWithFocus = Keyboard.FocusedElement as UIElement;
if (elementWithFocus != null)
elementWithFocus.MoveFocus(request);
}
}
If you look (in reflector/ilspy) at the KeyboardNavigation's ShowFocusVisual you'll find that the framework will only show it if the last input was from the keyboard (or if an internal static property based on the KeyboardCues system parameter info is true). So I don't think there is a good way to do this short of using reflection to temporarily set that property or asynchronously focusing the element and forcing a keyboard action (maybe using the winforms SendKeys or keybd_event api) but I wouldn't recommend either.
How can this be done
Login window appears first and if every thing is fine just close login window and open second Main window.
in win forms we modify program.cs but in wpf there is no program.cs.
Any solutions.?
Actully i did most of the work in the window that is created By default and now want to make it secondary(mean it should appear and then close when wanted giving control to new window)
<Application x:Class="DevnMark_V1._0.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
var login = new MainWindow();
login.ShowDialog();
if (myAppSett.Default.validated == true)
{
var mainWindow = new DevNMarkMainWindow();
mainWindow.ShowDialog();
}
}
Login Window start XML
<Window x:Class="DevnMark_V1._0.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:local="clr-namespace:Progress"
Title="MainWindow" Height="292" Width="563" WindowStyle="None" BorderBrush="#FF0A6277" AllowsTransparency="True" WindowStartupLocation="CenterScreen" Topmost="True">
Exception occurs when i close Login window and occurs at point InitializeComponent();of second window when it is viewed when it is going to be initilized
I solved this problem in this way:
I removed from App.xaml the StartupUri="MainWinodw.xaml", leaving only Startup="Application_Startup".
In Application_Startup, I IMMEDIATELY referenced both login and main windows:
loginwindow Login = new loginwindow();
mainwindow Main = new mainwindow();
I verified my Login, then closed the login window and opened the main window with a simple .Show():
Login.ShowDialog();
if (!Login.DialogResult.HasValue || !Login.DialogResult.Value)
{
Application.Current.Shutdown();
}
main.Show();
No changes in ShutdownMode.
There may be no program.cs, but there is an App.xaml.cs in the default WPF program template and you can do the same thing there.
What you want to do is remove StartupUri="LoginWindow.xaml" from App.xaml and then modify App.xaml.cs's constructor to invoke your login window and your main window, like this:
public App() : base() {
bool authenticated = false;
LoginWindow login;
while (!authenticated)
{
login = new LoginWindow();
login.ShowDialog();
authenticated = ValidUser(login.username, login.password);
}
MainWindow main = new MainWindow(login.username);
main.ShowDialog();
}
The above example assumes you've added username and password as public properties to LoginWindow, and that you've modified MainWindow's constructor to take a parameter.
The proposed OnExplicitShutdown method works and you can avoid explicitly shutting the app down in the second window simply by opening it with ShowDialog followed by this.Shutdown(), all in App.xaml thus not interfering with the rest of the application.
I have a ComboBox with ItemsSource="{DynamicResource testResource}". The testResource is the Application resource that I set in C# code.
What I have noticed is that if I load Window befor Application created, the resource is not loaded by ComboBox:
Window window = (Window)LoadXaml("Window1.xaml");
Application app = new Application();
This code works
Application app = new Application();
Window window = (Window)LoadXaml("Window1.xaml");
Also, even if I created the window befor the application, I can load resource latter in button click handler.
Can some one explain, what happens? Why the order matters?
Window1.xaml:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox ItemsSource="{DynamicResource testResource}" SelectedIndex="0"></ComboBox>
<Button x:Name='testButton'>Test</Button>
</StackPanel>
</Window>
C#
class Program
{
[STAThread]
public static void Main()
{
Window window = (Window)LoadXaml("Window1.xaml");
Application app = new Application();
SetupResource();
(window.FindName("testButton") as Button).Click += new RoutedEventHandler(testButton_Click);
window.Show();
app.Run(window);
}
static void testButton_Click(object sender, RoutedEventArgs e)
{
SetupResource();
}
static void SetupResource()
{
List<string> list = new List<string>();
list.Add("Hola");
list.Add("Mundo");
Application.Current.Resources["testResource"] = list;
}
static object LoadXaml(string fileName)
{
return XamlReader.Load(File.Open(fileName, FileMode.Open));
}
}
Not sure, but I would guess because the Application's Resources are only loaded when the Application object is created. So if you want to access testResource, you need to do it after the call to new Application().