I have what should be a very simple desktop application I'm working on but I'm having issues doing a few basic tasks. I'm using Visual Studio 2013.
I have created a project from a blank WPF template. I created a new Page, named Page1.xaml, to go along with the default MainWindow.xaml Window.
In my MainWindow.xaml window I have Grid and inside the grid is an Image.
<Grid MouseDown="Grid_MouseDown_1" Cursor="Hand" >
<Image Name="ImageIntro" Source="images/Stories-intro.jpg" Stretch="None" />
</Grid>
The Grid has a MouseDown event so that I can detect when a user clicks anywhere inside the Grid.
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
}
Pretty basic and that all works and compiles as intended.
The issue I have is that I'm unable to load the Page1.xaml inside my window on MainWindow.xaml. I don't want to open a separate window, I just want the content on Page1.xaml to be displayed inside the visible window of MainWindow.xaml.
I tried using the following but I get an error when I click the on my link: An unhandled exception of type 'System.NullReferenceException' occurred
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
Uri uri = new Uri("Page1.xaml", UriKind.Relative);
NavigationService ns = NavigationService.GetNavigationService(this);
ns.Navigate(uri);
}
This is not a browser application, it's simply a desktop application. The first screen (MainWindow.xaml) should just click thru to display the second screen (Page1.xaml).
I want the Page1.xaml content to take up the entire Window of MainWindow.xaml (sorry, but I can't stress that enough, I don't want a frame or any content from MainWindow.xaml showing when the user is on Page1.xaml).
I'm pretty new to Desktop apps but I have extensive knowledge with .Net C# for web applications. I'm not against changing the flow of what I have if there's a better way to accomplish this. For example, perhaps I shouldn't be using a Window to Page navigation and should instead use a Window to Window or something else.
I would imagine this would be a relatively simple task, but I haven't found anything that works yet so hopefully someone on here can explain it.
create a frame in Main Window
then in your event hander
Page1 mypage=new Page1();//object of the page 1
frame.Navigate(mypage);//pass it to frame navigate method
Read this MSDN link, the requirements for that to work say that this (in your code) must be a Frame. I don't see any XAML code here, but I'm guessing this is a Window. You need a frame to host the navigation, so your MainWindow should probably just the frame, and the contents of your current window should be "Page0".
Thanks Everyone who helped, here is the solution I used (I wanted to make sure a code example was here for anyone who has this same issue in the future).
I added a Frame to my MainWindow.xaml page:
<Grid MouseDown="Grid_MouseDown_1" Cursor="Hand" >
<Image Name="ImageIntro" Source="images/Stories-intro.jpg" Stretch="None" />
<Frame Name="Frame1" Content="" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
Then I added the following to my event:
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
Page1 mypage = new Page1();
Frame1.Navigate(mypage);
}
This allowed me to click on my link and the new page, Page1.xaml appeared. There was also a navigation bar that appeared at the top and the content didn't completely take up the MainWindow.xaml window but I think I can fiddle around with the settings and get it to where I want it.
Much thanks to Filippo B, Nauman Ahmad, and CodingGorilla for the assist.
Related
I have a usercontrol with grid as content panel, which loads its content from datatemplate, specified in xaml where usercontrol is used.
I am using this usercontrol widely and everything is fine with standard xaml controls in template.
now I am trying to show some charts in this control with OxyPlot
<DataTemplate x:Key="SomeChart">
<Grid x:Name="oxyGrid" >
<oxy:PlotView x:Name="oxyChart" Model="{Binding model}" />
</Grid>
</DataTemplate>
I initialize data model for the chart in usercontrol_loaded
but the chart will not be shown until I manually call InvalidateMeasure for it.
if I call invalidatemeasure from usercontrol_loaded , it will not help.
if I call invalidatemeasure from any point when chart is on the screen - it helps and chart will shows up.
currently, I found that I can call invalidatemeasure in usercontrol_layoutupdated handler but don't like as it fires too often
and requires code outside of usercontrol.
if I try to handle event layoutupdated inside usercontrol and call invalidatemeasure (for oxychart) from there, I am getting "Layout cycle detected. Layout could not complete."
any ideas about what's wrong and how to fix :)
will be greatly appreciated
thanks in advance
ilya
never hurry to post questions :)
just necessary to call UpdateLayout() in usercontrol after loading template
private void spContentPanel_Loaded(object sender, RoutedEventArgs e)
{
LoadTemplate();
UpdateLayout();
}
where spContentPanel is just Grid for template loading
<Grid Grid.Row="1" Name="spContentPanel" Loaded="spContentPanel_Loaded"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</Grid>
We are using the prism and WPF to build application. Recently we started using UI Automation (UIA) to test our app. But some strange behavior occurred when we run UIA test. Here's simplified shell:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0" Grid.Column="0"
Name="loadingProgressText"
VerticalAlignment="Center" HorizontalAlignment="Center"
Text="Loading, please wait..."/>
<Border
Grid.Row="0"
x:Name="MainViewArea">
<Grid>
...
</Grid>
</Border>
<!-- Popup -->
<ContentControl
x:Name="PopupContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="PopupRegion"
Focusable="False">
</ContentControl>
<!-- ErrorPopup -->
<ContentControl
x:Name="ErrorContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="ErrorRegion"
Focusable="False">
</ContentControl>
</Grid>
In our app, we use layers (Popup and ErrorPopup) to hide MainViewArea, to deny access to the controls. To show Popup, we use next method:
//In constructor of current ViewModel we store _popupRegion instance to the local variable:
_popupRegion = _regionManager.Regions["PopupRegion"];
//---
private readonly Stack<UserControl> _popups = new Stack<UserControl>();
public void ShowPopup(UserControl popup)
{
_popups.Push(popup);
_popupRegion.Add(PopupView);
_popupRegion.Activate(PopupView);
}
public UserControl PopupView
{
get
{
if (_popups.Any())
return _popups.Peek();
return null;
}
}
Similar to this, we show ErrorPopup over all elements of our application:
// In constructor we store _errorRegion:
_errorRegion = _regionManager.Regions["ErrorRegion"]
// ---
private UserControl _error_popup;
public void ShowError(UserControl popup)
{
if (_error_popup == null)
{
_error_popup = popup;
_errorRegion.Add(_error_popup);
_errorRegion.Activate(_error_popup);
}
}
Mistics...
When we run it as users do it (double click on app icon), we can see both custom controls (using AutomationElement.FindFirst method, or through Visual UI Automation Verify). But when we start it using UI Automation test - ErrorPopup disapears from the tree of the controls. We trying to start the application like this:
System.Diagnostics.Process.Start(pathToExeFile);
I think that we missed something. But what?
Edit #1
As #chrismead said, we tried to run our app with UseShellExecute flag set to true, but this does not help. But if we start app from cmd line, and manually click the button, Popup and ErrorPopup are visible in automation controls tree.
Thread appThread = new Thread(delegate()
{
_userAppProcess = new Process();
_userAppProcess.StartInfo.FileName = pathToExeFile;
_userAppProcess.StartInfo.WorkingDirectory = System.IO.Directory.GetCurrentDirectory();
_userAppProcess.StartInfo.UseShellExecute = true;
_userAppProcess.Start();
});
appThread.SetApartmentState(ApartmentState.STA);
appThread.Start();
One of our suggestion is when we use method FindAll or FindFirst to search the button to click, window somehow cached its UI Automation state, and does not update it.
Edit #2
We have find, that extension method of prism library IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) have some strange behavior. If we stopped use it, this solve our problem particulary. Now we able to see ErrorView and any kind of view in PopupContentControl, and application updates UIA elements tree structure. But this is not an answer - "Just stop use this feature"!
In MainViewArea we have a ContentControl, which updates it content depending on user actions, and we are able to see only the first loaded UserControl to that ContentControl.Content property. This is performed like this:
IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.MainContentRegion, this.Uri);
And if we change the view, no updates will performed in UI Automation tree - the first loaded view will be in it instead. But visually we observe another View, and WPFInspector shows it properly (its show not a UI Automation tree), but Inspect.exe - not.
Also our suggestion that window use some kind of caching is wrong - caching in UI Automation client we have to turn on explicitly, but we don't do it.
I'm sorry that I've missed some detail, that was the key to the answer. I think that it was not important thing. Anyway.
We used NavBar from DevExpress controls library for WPF. What turns out, is when NavBar is present, dynamically created views are not appears on the UI Automation tree. When remove it from the window, there was an ability to see all dynamically loaded views. What does the NavBar - still mistic for me.
Here bright example to see what happened, if NavBar is present or absent on the Window (DevExpress is required).
MainWindow.xaml:
<Window xmlns:dxn="http://schemas.devexpress.com/winfx/2008/xaml/navbar"
x:Class="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 Name="ContentGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<!--Comment NavBar to see dynamic control in UI Automation tree-->
<dxn:NavBarControl Name="asdasd">
<dxn:NavBarControl.Groups>
<dxn:NavBarGroup Header="asdasdasdasd" />
</dxn:NavBarControl.Groups>
</dxn:NavBarControl>
<TextBox Grid.Column="1" Name="Statictb" Text="static is visible in ui automation tree" />
<Button Grid.Row="1" Content="Create controls" Height="25" Click="Button_Click"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
TextBox tb = new TextBox();
Grid.SetRow(tb, 1);
Grid.SetColumn(tb, 1);
tb.Text = "dynamic is not visible, if NavBar here...";
ContentGrid.Children.Add(tb);
}
}
Edit
According to the DevExpress answer on their support site:
After a peer is created, listening of automation events may cause performance issues. We have decided to clear invocation lists of automation events to resolve it. In your specific situation, you need to disabling clearing. To do it, please set the static DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled property to False in the Window constructor.
This solve the problem.
My guess is that the ContentControl's automation peer should update its children with AutomationPeer.ResetChildrenCache() after the view has been changed.
AutomationPeer.InvalidatePeer() should have the same effect (in addition to other side effects) and it is supposed to be called automatically in response to the LayoutUpdated event. You might want to check that the LayoutUpdated event is raised when the view changes.
stukselbax, try to find a sequence of keystrokes (TABs, and an ENTER most likely) to click the button that enables you to see the items. it is pretty easy to send keystrokes and i can add more in here about that if that works for you. you can always establish a tab order in your application that makes the most sense for users.
------ Update on 6/20/12 --------
Have you tried double clicking a shortcut to your app on the desktop using PInvoke to see if you can see the controls when it is opened that way? Here is a link to an example here on stackoverflow:
Directing mouse events [DllImport("user32.dll")] click, double click
Another idea: some of the controls on the app I am currently automating don't show up in the tree until a mouse click occurs on them. To accomplish this without using any hardcoded coordinates, I find something in the tree which is just (above/below/etc) the place where I need to click to get the control to appear. I then get the mouse coordinates for that item and put the mouse at a small offset from there and click. Then I can find my controls in the tree. If the app is resized, moved around, etc. this will still work since the small offset is still valid.
I am trying to migrate an existing Winforms project into WPF. However: there are some user controls I need to leave as WinForm controls.
I have added a WinForms UserControl into a WPF Window. It consists of a RichTextBox and some buttons and labels. This is subclassed into various further user controls.
When I embed the UserControl into a WPF window it renders - but none of the buttons appear to do anything. When underlying processes update e.g. the RichTextBox it does not display the content. Yet when I inspect the textbox in debug I can see the content (though I have to click on 'base' to see this.)
[ One difference I have spotted - though it may not be relevant - is that when this control is on a WPF and non-working Visual Studio shows the object as 'sealed' but when in the original Winforms project when it is fully working it does not show as sealed. ]
I have added code to change the text in the labels - and they also firmly refuse to update: yet again I can see the text if I examine the label in debug mode.
This stack overflow question may address the same issue:
WindowsFormsHost Winform pdfviewer control problem
but the answer didn't make a lot of sense to me:
It mentioned replacing
new Window { Content = CreateContent(), Title = title }.Show();
But this is not a piece of code I recognise: I am using a xaml file with code behind and it's called up using
System.Windows.Application app = new System.Windows.Application();
app.Run(new FormWPFApp());
(where FormWPFApp is my name for the WPF window)
Here is the xaml header:-
<Window x:Class="ZedApp.FormWPFApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Printers="clr-namespace:ZedApp.UserControls.Printers"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="Conversion version" Height="661" Width="1559" Loaded="Window_Loaded">
Here is the xaml I use for the two UserControls (they both inherit from the same base class) :-
<WindowsFormsHost Height="430" HorizontalAlignment="Left" Margin="192,32,0,0" Name="windowsFormsHostTicketPrinter" VerticalAlignment="Top" Width="324" Grid.Row="1" Grid.Column="1">
<Printers:TicketPrinter x:Name="ticketPrinter">
</Printers:TicketPrinter>
</WindowsFormsHost>
<WindowsFormsHost Height="430" HorizontalAlignment="Left" Margin="522,32,0,0" Name="windowsFormsHostJournalPrinter" VerticalAlignment="Top" Width="324" Grid.Row="1" Grid.Column="1">
<Printers:JournalPrinter x:Name="journalPrinter">
</Printers:JournalPrinter>
</WindowsFormsHost>
[Another thing I have noticed is a method that clears the Rich Text Box on one of the windows starts kicking out errors of the following type if run under WindowsFormsHost in WPF -
"Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
private void ClearRichTextBox(RichTextBox rtbToClear)
{
if (rtbToClear.IsHandleCreated)
{
if (rtbToClear.InvokeRequired)
{
this.Invoke(new Action<RichTextBox>(ClearRichTextBox), new object[] {rtbToClear});
return;
}
rtbToClear.Clear();
}
}
]
What is the likely cause of this behaviour and what do I need to do to get the elements within the User Control working?
Proper input interop with WinForms requires some cooperation between the host and the WPF input system. The topic Message Loops Between Win32 and WPF in the SDK explains this well. In your setup, the easiest way to make this happen is to use code like this:
Window w = new Window1();
System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop(w);
w.Show();
ElementHost.EnableModelessKeyboardInterop() essentially registers an input hook with the WinForms Application object (which normally runs the message loop) and calls ComponentDispatcher.RaiseThreadMessage().
So I have a Panorama control and the PanoramaItems are programmatically added to the control using the following template.
<UserControl>
<Grid x:Name="LayoutRoot">
<controls:PanoramaItem Name="sitePanoramaItem" Header="{Binding Name}">
<Controls:DockPanel VerticalAlignment="Stretch">
<StackPanel Orientation="Horizontal" Margin="0,10,0,0" Controls:DockPanel.Dock="Top">
<Image Source="../Images/action.png" Width="64" />
<TextBlock Text="{Binding Stats, Mode=TwoWay}" FontSize="45" Margin="15,0,0,0" />
</StackPanel>
<Grid x:Name="graphCanvas" HorizontalAlignment="Stretch" Margin="10,10,10,10"> </Grid>
</Controls:DockPanel>
</controls:PanoramaItem>
</Grid>
</UserControl>
When I click on graphCanvas what I'd like to do is sorta pop the graphCanvas out and display that fullscreen then when I click again restore it to where it was. I've been all over this site and Google and can't find anything similar to what I'm looking for.
I would still like to maintain the Panorama control functionality so that the graphCanvas is still the only one visible but you can cycle through them. Currently I have it sorta working in that I remove the Grid from the DockPanel and put it directly in the LayoutRoot while making the sitePanoramaItem collapsed. However, it's not fullscreen as the Panorama name is still visible (I guess I could hide that as well...) When I put the graphCanvas back int he DockPanel the size of the canvas is all screwed up.
I was hoping there was a simpler way.
Is it even possible?
It is possible to create the UI you describe but it's not going to be simple. You're on the right track with removing it in code and adding it the LayoutRoot and making the Panorama hidden. However you would have to code the scrolling behavior yourself and that is going to be quite tricky - especially making it feel the way to panorama does.
One trick you could try is actually layer a PivotControl on top of your Panorama and have it be collapsed by default. Also edit it's template to remove all default content eg: remove the header control, set margins to 0, etc). Then when you want to go full screen you can remove all the graphCanvases from the Panorama items and and add them to new PivotItems in the PivotControl. Then hide the Panorama and show the Pivot. This will give you scrolling capability for free and the illusion of full screen.
Having said all that I'm not sure I would recommend this. The more common approach would be to simply be to navigate to another page when the user selects an item and handle the full screen aspects there (possibly using the Pivot control again for scrolling). And when you want to leave "fullscreen" mode simply navigate back to the first page. Handling Tombstoning of the fullscreen state will be much easier with this approach for one thing.
You can try making the graphCanvas a Page and putting it in a different XAML. Then add a frame (name it InnerFrame for example) in the same place where you have the graphCanvas right now and navigate to that page with InnerFrame. When the frame is clicked, you navigate with the RootFrame of the app to your graphCanvas page. When you decide to close it, just navigate back with the RootFrame.
Hope it's clear enough :)
Edit:
Navigation in WP7 works very similar as the standard navigation in Silverlight 4, but it's a bit more restrictive. Just throw a PhoneApplicationFrame in your XAML like this:
<phone:PhoneApplicationFrame x:Name="Frame" />
This is basically the same as a Silverlight frame. All the pages you create inherit from PhoneApplicationPage by default, so they can be showed in a frame without any changes.
Your whole application actually runs on a PhoneApplicationFrame. If you take a look at your App class you will see this:
public PhoneApplicationFrame RootFrame { get; private set; }
Here's the MSDN documentation for the navigation system on WP7
I'm trying to capture keypress events anywhere in my WPF application, regardless of which UI element has the focus. Currently I'm having no luck. Can anyone suggest some strategies that I might not have tried? Or, ideally, provide an answer like "oh that's easy, you just do this".
It's a distributed application, which has a chat system. The effect that I'm looking for is that the user can start typing a chat message at any time, without switching to a standard chat box. I'll display their message in the application myself, using FormattedText objects. This is important because it means there are no text input elements in the application anywhere.
My XAML structure looks roughly like:
<MainWindow>
<Canvas 1>
<Canvas 2>
<Image 1 />
</Canvas 2>
<Image 2 />
</Canvas 1>
</MainWindow>
I programmatically add elements into Canvas 2, and manipulate Image 2, which is why it has that structure.
I've tried adding KeyDown, KeyUp and the Preview events to MainWindow and Canvas 1, but none of them seem to fire (I check with breakpoints). I've also, after reading another related question here, tried manually setting the focus on the main window in the Loaded() method.
I realise there are many related questions on this site, but they haven't helped me because:
there aren't any answers (will my question be answered?)
they assume a text entry widget and are interested in bubbling up events
they want a keybinding for a small number of keys - I would like to capture any key
they are interested in detecting if a control/shift/alt key is down after they've already captured the event
Thank you for taking the time to read my long winded post, and thank you for suggestions.
Update (After Rachel's comment) When I put in a TextBox and set the focus to the TextBox, a key event method at the MainWindow level will fire. So that works as advertised.
However, I would really like to not have an explicit text entry widget in the application at all. I would like the user to be able to just start typing to compose a message.
A little bit of tinkering got me this:
XAML:
<Window x:Class="KeyInput.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="100" Width="225">
<Canvas>
<Grid>
<Label Name="test" Content="Empty" />
</Grid>
</Canvas>
</Window>
CS:
using System.Windows;
using System.Windows.Input;
namespace KeyInput
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.KeyDown += new KeyEventHandler(OnButtonKeyDown);
}
private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
test.Content = test.Content + e.Key.ToString();
}
}
}
This prints out stuff like "Shift" So you'd obviously have to use switches... but it has a Text Box that collects key presses.
I managed to work it out, inspired by answers from Rachel and WernerCD. As they both demonstrated, having the event capture at the MainWindow level does work. The problem was that I neglected to mention that I had a dialog before the MainWindow loaded, which seems to interfere with normal keyboard focus on the MainWindow. Putting explicit Keyboard.focus() in the Loaded() method is too soon. I fixed the problem by putting Keyboard.focus() in the MainWindow_ContentRendered() method.
All is now well (until the next issue anyway). Thank you for the help.
I usually add a PreviewKeyDown event to the MainWindow.
Perhaps your problem is you don't have any control that accepts keyboard focus on your application. Do you get the same results if you add a TextBox to the MainWindow and have focus set there?