I have a TreeView that launches a new window when each of its TreeViewItems Selected event is raised.
<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>
<TreeView Name="treeView1">
<TreeViewItem Header="Root">
<TreeViewItem Header="Parent" Selected="ParentTreeViewItem_Selected">
<TreeViewItem Header="Child" Selected="TreeViewItem_Selected" ></TreeViewItem>
</TreeViewItem>
</TreeViewItem>
</TreeView>
</Grid>
</Window>
Code Behind
namespace WpfApplication1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
Window w = new Window();
w.Show();
e.Handled = true;
}
private void ParentTreeViewItem_Selected(object sender, RoutedEventArgs e)
{
}
}
}
When I click on the child node the new window is launched as expected. However imediatly afterwords its parents Selected eventis fired stealing focus from the new window, and marking the parent node as the current selection!
My expectation was that the newly launched window would have focus, and the node that was clicked would turn gray indicating to the users his/hers selection. Does anyone have any idea of why this happens and how I can prevent it?
Thanks,
Brette
Thought I would post the answer. I finally found a way around this. Setting w.Owner = this; has no effect. Turns out launching a new window on the Selected event of a TreeViewItem causes some focus issues. I have not found out what the root cause is by executing this on the Dispatcher seems to correct it. See Below
private void ChildTreeViewItem_Selected(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => new Window().Show()));
}
Hope this saves someone else some time.
Brette
Add:
w.Owner = this
Example:
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
Window w = new Window();
w.Owner = this;
w.Show();
e.Handled = true;
}
Related
I have two xaml. One is MainWindow and other is NewWindow.
I want show NewWindow 5 seconds, when program is run.
And after 5 seconds, I want show MainWindow.
How to change xaml in WPF?
Here is MainWindow.
<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">
<Grid>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
Here is NewWindow.
<Window x:Class="WpfApplication2.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<Grid>
</Grid>
public partial class NewWindow : Window
{
public NewWindow()
{
InitializeComponent();
}
}
1) First, we need to stop MainWindow from opening as soon as we run the Application. To do this, first remove the StartupUri="MainWindow.xaml" setting from the App.xaml file and replace it by setting the Startup property instead:
<Application x:Class="AppName.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup">
2) Then, add the handler for the Application.Startup event and launch your child (or splash screen) Window:
private SplashScreen splashScreen;
...
public void App_Startup(object sender, StartupEventArgs e)
{
// Open your child Window here
splashScreen = new SplashScreen();
splashScreen.Show();
}
3) At this point, there are several different ways to go, dependent on whether you need to wait for the SplashScreen Window to do anything or not. In this particular question, the requirement is to simply open the MainWindow after 5 seconds, so we'll need a DispatcherTimer:
public void App_Startup(object sender, StartupEventArgs e)
{
// Open your child Window here
splashScreen = new SplashScreen();
splashScreen.Show();
// Initialise timer
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 5, 0);
timer.Tick += Timer_Tick;
}
...
private void Timer_Tick(object sender, EventArgs e)
{
splashScreen.Close();
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
That's it.
There are plenty ways to do this. As some people suggested i suggest too to DO NOT DO THIS if you're trying to create a splash screen, there are better ways to do that. But.. here what you asked for:
using System.ComponentModel; //Remember to add this
public partial class MainWindow : Window
{
private BackgroundWorker waitingWorker = new BackgroundWorker();
private NewWindow myNewWindow = new NewWindow();
public MainWindow()
{
InitializeComponent();
waitingWorker.DoWork += waitingWorker_DoWork;
waitingWorker.RunWorkerCompleted += waitingWorker_RunWorkerCompleted;
waitingWorker.RunWorkerAsync();
}
private void waitingWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
myNewWindow.Show();
this.Close();
}
private void waitingWorker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(5000);
}
}
it's a simple background worker that waits for 5 seconds, then opens the NewWindow and close MainWindow. Yes, you can do it without background worker too, but Thread.Sleep(5000); will totally freeze your GUI and make your little app unresponsive, so you need another thread to wait while the main thread can keep your GUI alive. I suggest you to study at least how a background worker works.
HERE the official MSDN documentation, but google is your friend and you can find tons of tutorial and explanation about it
Here is another way to do it:
Set Startup to "App_Startup" as shown in one of the other posts.
<Application x:Class="AppName.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup">
And in App_OnStartup:
private async void App_Startup(object sender, StartupEventArgs e)
{
var splash = new SplashWindow();
splash.Show();
await Task.Delay(5000);
var mainWindow = new MainWindow();
mainWindow.Show();
splash.Close();
}
The mainWindow should also be loaded before closing the splashScreen This way your splashscreen shows as long as it is loading.You can add additional time in the splashScreen.Close() Function.
private SplashScreen splashScreen;
private void App_Startup(object sender, StartupEventArgs e)
{
splashScreen = new SplashScreen("SplashScreen1.png"); // Or new WPF window
splashScreen.Show(false);
MainWindow mainWindow = new MainWindow();
splashScreen.Close(new TimeSpan(0, 0, 3));
mainWindow.Show();
}
Does anybody know how to implement a double-click event handler that opens a new window in a way the new window becomes the front most window? (Just the behavior that is normally expected).
In WPF there is a strange behavior of windows when opening a second window in the double-click event handler. The second window opens but the first window, where the double-click-event was fired, becomes activated again immediately.
Opening a window in a click event handler, works as expected. The second window opens and remains the front window.
For demonstration purposes I created the following application. Two window classes with just a button control. To distinguish between click and double-click on the button control, the click-event works only if the left shift key is pressed.
After double-click
http://blog.mutter.ch/wp-content/uploads/2014/05/wpf_window1.png
After click (this is also the expected behavior for double-click)
http://blog.mutter.ch/wp-content/uploads/2014/05/wpf_window2.png
Main Window
<Window x:Class="WpfWindowSwitching.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="600">
<Grid>
<Button Margin="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
MouseDoubleClick="doubleClick"
Click="click">
<TextBlock FontWeight="Bold"
FontSize="22">
I am the first Window, double click this button...
</TextBlock>
</Button>
</Grid>
</Window>
The code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void doubleClick(object sender, MouseButtonEventArgs e)
{
openNewWindow();
}
private static void openNewWindow()
{
var window = new SecondWindow();
window.Show();
}
private void click(object sender, RoutedEventArgs e)
{
if (!Keyboard.IsKeyDown(Key.LeftShift)) return;
openNewWindow();
}
}
Second Window
<Window x:Class="WpfWindowSwitching.SecondWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SecondWindow" Height="200" Width="600">
<Grid>
<Button Margin="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Click="click">
<TextBlock FontWeight="Bold"
FontSize="22">
I am the second Window
</TextBlock>
</Button>
</Grid>
</Window>
The code behind:
public partial class SecondWindow : Window
{
public SecondWindow()
{
InitializeComponent();
}
private void click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
After MouseDoubleClick event, MouseUp event is raised which gets handled on MainWindow. Hence secondary window gets activated momentarily and with subsequent event bubbling, main window gets activated.
In case you don't want that, you can explicitly stop event bubbling by setting e.Handled to True after mouse double click event. This way secondary window will remain activated.
private void doubleClick(object sender, MouseButtonEventArgs e)
{
openNewWindow();
e.Handled = true;
}
I have ListBoxes defined in a DataTemplateColumn:
<Window x:Class="DoubleclickTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataGrid Name="dg" IsReadOnly="True" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ListBox Loaded="ListBox_Loaded" MouseDoubleClick="ListBox_MouseDoubleClick" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
and I want to open a new window on double-clicking the listbox:
public MainWindow() {
InitializeComponent();
dg.ItemsSource = new[] { 1, 2, 3, 4, 5 };
}
private void ListBox_Loaded(object sender, RoutedEventArgs e) {
((ListBox)sender).ItemsSource=Enumerable.Range(1,5);
}
private void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
e.Handled = true;
var win = new MainWindow();
win.Show();
//Neither of these help:
//win.Activate();
//win.Focus();
}
The new window opens underneath the current window.
How can I have the new window open over the current window (without using ShowDialog)?
Update
Using an ItemsControl instead of a ListBox doesn't help.
That was a weird behavior but the following method will help:
<Window ... Loaded="Window_Loaded">
Use a timer to activate the window:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Timer t = new Timer(10);
t.Elapsed += t_Elapsed;
t.Start();
}
void t_Elapsed(object sender, ElapsedEventArgs e)
{
(sender as Timer).Stop();
Dispatcher.Invoke(() =>
{
this.Activate();
this.Focus();
});
}
The used timer is system timer:
using System.Timers;
Using .NET 4.5:
private async void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
e.Handled = true;
await Task.Delay(20);
var win = new MainWindow();
win.Show();
}
This is still a bit of a hack to work around the issue, but if there are no better solutions I will use this.
I have a screen with several UserControls, but only one of them remains active. The other UserControls aren't shown, but the user can switch the active flag of any of those who are not active. One of the UserControl contains an ItemsControl.
I need to know all the controls in the view, including those generated by an ItemsControl, after loading the first UserControl that is active in the screen, when view is finally initialized.
For ItemsControl, wpf didn't instance any item until it was painted on the screen that contains the UserControl (so I've tried, until the Load event is launched), so that I can't found the controls contained by the view because it didn't exist.
Is there any way to change this behavior?
I try to change the value of property VirtualizingStackPanel.IsVirtualizing to false, to avoid the previous behaviour, with no success. To illustrate this, I write this view example:
<Window x:Class="ContenidoEnTabs.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">
<StackPanel x:Name="spContainer" Orientation="Vertical" VirtualizingStackPanel.IsVirtualizing="False">
<Button Content="Push" Click="Button_Click" />
</StackPanel>
</Window>
This view creates a second control not visible until the user press the button:
public partial class MainWindow : Window
{
private NotPaintedOnInitUserControl controlExtra;
public MainWindow()
{
InitializeComponent();
controlExtra = new NotPaintedOnInitUserControl();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
spContainer.Children.Add(controlExtra);
}
}
The control not visible initially is as follow:
<UserControl x:Class="ContenidoEnTabs.NotPaintedOnInitUserControl"
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">
<ItemsControl ItemsSource="{Binding MyCollection}" x:Name="itemsControlTarget"
VirtualizingStackPanel.IsVirtualizing="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox x:Name="aTextBox" Width="80" Initialized="ATextBox_Initialized" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
and in CodeBehind I detect when the Items were created
public partial class NotPaintedOnInitUserControl : UserControl
{
public NotPaintedOnInitUserControl()
{
InitializeComponent();
DataContext = new SimpleListDataContext();
}
private void ATextBox_Initialized(object sender, EventArgs e)
{
}
}
And the DataContext used:
public class SimpleListDataContext
{
private List<string> _myCollection;
public List<string> MyCollection
{
get { return _myCollection ?? (_myCollection = new List<string> { "one", "two" }); }
set { _myCollection = value; }
}
}
Any ideas?
Thanks in advance.
If you want WPF to generate the tree for a control that isn't part of the view, you can "hydrate" and layout the control by forcing the layout to run. Something like this should work:
public partial class MainWindow : Window
{
private NotPaintedOnInitUserControl controlExtra;
public MainWindow()
{
InitializeComponent();
controlExtra = new NotPaintedOnInitUserControl();
// Force the control to render, even though it's not on the screen yet.
var size = new Size(this.Width, this.Height);
var rect = new Rect(new Point(0,0), size);
controlExtra.Measure(size);
controlExtra.Arrange(rect);
controlExtra.InvalidateVisual();
controlExtra.UpdateLayout();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
spContainer.Children.Add(controlExtra);
}
}
Not sure if this is what you're asking. If not, please clarify paragraph 2.
Have a look at LogicalTreeHelper.GetChildren(myUiElement)
This looks at the logical tree rather than the visual tree so it examines the structure without needing to have loaded the control to get the visual structure
In the below control to find is the name of the contorl i.e. myDatagrid
You could also adapt this to just get all the children of a particular control i.e.
FindChildInVisualTree(this, "mydatagrid"); // assumming this a UIElement (i.e. your in the code behind)
find the control using the below then using LogicalTreeHelper get all it's children.
public static UIElement FindChildInVisualTree(UIElement view, string controlToFind)
{
UIElement control = null;
try
{
if (view != null)
{
if ((view as FrameworkElement).Name.ToUpper() == controlToFind.ToUpper())
{
control = view;
}
else
{
DependencyObject depObj = view as DependencyObject;
if (depObj != null)
{
foreach (var item in LogicalTreeHelper.GetChildren(depObj))
{
control = FindChildInVisualTree(item as UIElement, controlToFind);
if (control != null)
{
break;
}
}
}
}
}
}
catch (Exception ex)
{
throw new ApplicationException("Error finding child control: " + controlToFind, ex);
}
return control;
}
I need to perform some operations on the keydown event on a wpf hyperlink.
I have a simple richtextbox in which i have a hyperlink. I want Keydown event to be fired only when the focus is on the hyperlink, that is the cursor is on the hyperlink text.
Doing this doesn't work and i couldn't find any explanation of why this doesn't work.
<Hyperlink KeyDown="Hyperlink_KeyDown">
test
</Hyperlink>
I would really appreciate it if you could help me.
Thanks.
Have a good day,
Astig.
it doesn't work because hyperlink isn't recognized like focused, you may catch this event in parent control for example grid but before it will be caught you must click on it.
So you may catch window's keydown event like this:
XAML:
<Window x:Class="WpfApplication3.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"
Name="MW" KeyDown="MW_KeyDown">
<Grid>
<TextBlock>
<Hyperlink Name="HL1" NavigateUri="http://www.google.com/" RequestNavigate="HL1_RequestNavigate">
Focus it and key down
</Hyperlink>
</TextBlock>
</Grid>
and code:
private void MW_KeyDown(object sender, KeyEventArgs e)
{
if (HL1.IsMouseOver == true)
HL1_RequestNavigate(HL1,new RequestNavigateEventArgs(HL1.NavigateUri, HL1.Name));
}
private void HL1_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
Edit
Also you can set focus to hyperlink like that:
XAML:
<Hyperlink Name="HL1" NavigateUri="http://www.google.com/" RequestNavigate="HL1_RequestNavigate" KeyDown="HL1_KeyDown" MouseEnter="HL1_MouseEnter">
code:
private void HL1_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
private void HL1_KeyDown(object sender, KeyEventArgs e)
{
HL1_RequestNavigate(HL1, new RequestNavigateEventArgs(HL1.NavigateUri, HL1.Name));
}
private void HL1_MouseEnter(object sender, MouseEventArgs e)
{
HL1.Focus();
}