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().
Related
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();
}
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.
I have a simple UserControl called UserControl1 that contains a TextBlock:
<UserControl x:Class="WpfApplication2.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding}"/>
</Grid>
</UserControl>
I initialized a new instance of it and gave it a DataContext in code. when the window is closing I have to draw this control to an image file.
The UserControl does not render the bounded text in the file that been created.
and this is my code using the usercontrol:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Closing += MainWindow_Closing;
}
void MainWindow_Closing(object sender, CancelEventArgs e)
{
UserControl1 uc = new UserControl1();
uc.DataContext = "hello";
uc.Height = 100;
uc.Width = 100;
uc.Background = Brushes.LightBlue;
DrawToImage(uc);
}
private void DrawToImage(FrameworkElement element)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(element.DesiredSize));
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)element.Width, (int)element.Height,
120.0, 120.0, PixelFormats.Pbgra32);
bitmap.Render(element);
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (Stream s = File.OpenWrite(#"C:\555.png"))
{
encoder.Save(s);
}
}
}
I Hope It's clear enough, any help will be very appreciated.
You just forgot to force a Layout update on your control after manually Measuring/Arrangeing it (which will not be enough to force binding resolving).
A simple call to UpdateLayout makes it work :
private void DrawToImage(FrameworkElement element)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(element.DesiredSize));
element.UpdateLayout();
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)element.Width, (int)element.Height,
120.0, 120.0, PixelFormats.Pbgra32);
bitmap.Render(element);
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (Stream s = File.OpenWrite(#"C:\555.png"))
{
encoder.Save(s);
}
}
Edit : More on when bindings are resolved : link
Try to call the function SaveImage() on userControl1.Loaded event
If I do this it works, not sure this is what you want though:
<Window x:Class="DrawImage.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DrawImage="clr-namespace:DrawImage"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DrawImage:UserControl1 x:Name="uc" Visibility="Hidden"/>
</Grid>
</Window>
void MainWindow_Closing(object sender, CancelEventArgs e)
{
uc.DataContext = "hello";
uc.Height = 100;
uc.Width = 100;
uc.Background = Brushes.LightBlue;
uc.Visibility = Visibility.Visible;
DrawToImage(uc);
}
EDIT
I am now able to reproduce the issue. If I set the DataContext in the Window contstructor then it works. If I set it in the Winndow_Closed event I get the exact same result that you get.
I guess there might no workaround since the WPF needs some time to actually render the text on the UI thread. If you render the PNG before the WPF has rendered the text on the UI thread it will not appear on the PNG.
A workaround does not seem to exist since the window will be destroyed when the Closed event handlers has been running. There is no way to block the UI thread on the one hand to prvevent to window from beeing detroyed when you on the other hand want the UI thread to render the control.
I'd suggest to render the image as soon as the control has been rendered and save the image file when the window is closed.
I posted an Article in my Blog (putting in the account the png Transparancy (causes the black background)):
Saving FrameworkElement as Image
FrameworkElement element = myControl.Content;
// you can set the size as you need.
Size theTargetSize = new Size(1500,2000)
element.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(theTargetSize ));
// to affect the changes in the UI, you must call this method at the end to apply the new changes
element.UpdateLayout();
You can find the full cod in the Blog Post.
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've been trying for hours to get to the point where I can start a WPF application and have full control. I want to be able to create a ViewModel, create a View (Window), set the data context of the View to be the ViewModel, then show the View.
I've tried lots of methods, the most promising being to change the App.xaml to be a page and then adding my own Main method. Unfortunately this doesn't work properly because VS2010 then does not show the styles from the App.xaml in the designer, though they do work when running the app.
Is there a way to do what I want? If not, how do people normally start MVVM apps in WPF, creating a ViewModel outside of the View itself?
I would use the Startup event. You can add this to the App.xaml and remove the StartupUri line. When you add it, Visual Studio can create the event for you within the App.xaml.cs file. You can initialise your ViewModel and View within.
Here is one simple way...
<Application
x:Class="Demo.Ux.WpfApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
Here is the basic App.xaml.cs
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
try
{
var mainView = new MainView();
mainView.Show();
mainView.DataContext = new MainViewModel();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
}
Application.MainWindow can be used as well. The first displayed Window will be assigned to MainWindow auto-magically. Of course, you can skip creating your mainView and write directly to MainWindow which would thin out the syntax as well.
MainWindow = new MainView();
MainWindow.Show();
MainWindow.DataContext = new MainViewModel();
One final note, I'm doing the Show before the data bind. You need to do this to avoid a situation where the MainViewModel throw an exception during creation. If the MainView hasn't been shown, the app will close without letting you see the error.
in our application, we have choosen the way which you already proposed: writing a new Main method. You also have to make some changes in the project application settings then (no startup object). The app xaml has to look something like this:
<Application x:Class="EVOCURA.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup"
Exit="Application_Exit">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--Custom Controls-->
<ResourceDictionary Source="<your resources here>"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The code behind will look something like this:
public sealed partial class App : Application
{
static App()
{ }
public App()
{ }
private void Application_Startup(object sender, StartupEventArgs e)
{
// create the main window and assign your datacontext
MainAppWindow main = new MainAppWindow();
main.DataContext = <your datacontext here>
main.Show();
}
[STAThreadAttribute]
public static int Main(string[] args)
{
App app = new App();
app.InitializeComponent();
app.Run();
return 0;
}
}
Have a look at the Startup Event and notice, that no default StartupUri is specified im App.xaml
You could also pass the DataContext in a new constructor of your MainWindow, or create the DataContext directly in xaml.
The simplest way to assign an instance of the ViewModel to the DataContext of the view is in the code behind of the Window.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new myViewModel();
}
}
For the first part of your question, you can have the control of your application in the StartUp event
<Application x:Class="myApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
Code Behind :
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
// Place your code here
}
}