How to close MahApps ProgressDialog without losing focus on child control? - c#

In my app, I'm loading a DocumentViewer for previewing and printing. Because loading can take up to a few seconds, I would like to display a ProgressDialog. The logic is this: display ProgressDialog, create and display DocumentViewer, close ProgressDialog.
The problem is that when closing the dialog, the focus returns to the MainWindow, instead of remaining to the DocumentViewer window. I think it's because the dialog CloseAsync(). I've tried calling Focus(), Activate(), Show(), setting TopMost = true, setting the owner of the child window, but, although the window is displayed in front of the parent, the focus still returns to the parent. The only way to I restored the focus to the child was by doing a programmatic click inside the child window, in ProgressDialogController.Closed event, but the disadvantage is that there is an ugly transition (child initially appears in front, then parent, then child again).
Can CloseAsync() be avoided? Or how to close MahApps ProgressDialog without losing focus on child control?
UPDATE
I'm using MahApps 1.3.0.157. The code I'm using in Viewmodel:
public async void PrintLogExecute()
{
await DialogService.ShowProgressDialog(Properties.Resources.loading_message_box_title, Properties.Resources.loading_message_box_text);
ReportingClass.PrintDataGrid(LogEntries);
DialogService.CloseProgressDialog();
}
The DialogService class:
static class DialogService
{
public static async Task ShowProgressDialog(string dialogTitle, string message)
{
var metroWindow = (GetMainWindow() as MetroWindow);
controller = await metroWindow.ShowProgressAsync(dialogTitle, message);
controller.SetIndeterminate();
}
public static void CloseProgressDialog()
{
controller.CloseAsync();
}
private static Window GetMainWindow()
{
return Application.Current.Windows[0];
}
}
When executing PrintLogExecute(), the document previewer firstly appears in front of the main window and immediately after this, when executing DialogService.CloseProgressDialog() it goes to background. Like I said, the only way I restored the focus to the child was by doing a programmatic click inside the child window, in ProgressDialogController.Closed event.

maybe this helps you.
<mahApps:MetroWindow x:Class="WakeOnLanV2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:commands="clr-namespace:WakeOnLanV2.Commands"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mahApps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:viewModels="clr-namespace:WakeOnLanV2.ViewModels"
xmlns:views="clr-namespace:WakeOnLanV2.Views"
xmlns:wakeOnLan="clr-namespace:WakeOnLanV2"
x:Name="MainView" >
<Grid>
<Grid>
<!-- main grid -->
</Grid>
<mahApps:ProgressRing Width="150"
Height="150"
Canvas.ZIndex="110"
IsActive="{Binding Path=IsBusy,
UpdateSourceTrigger=PropertyChanged,
FallbackValue=False}"
IsLarge="True" />
</Grid>
</mahApps:MetroWindow>
The ring is under the MainGrid to keep it in the foreground.

I got the answer from GitHub:
https://github.com/MahApps/MahApps.Metro/issues/2532

Related

WPF ShowDialog - change parent window's cursor

I have a little problem when opening a window by ShowDialog.
I have a main window which when the application is loading data from the data source, opens another small window as a dialog to show the loading progress.
When the small loading progress window opens up, the cursor of that window is set to a custom Busy cursor and I am trying to set the main window's cursor to be the same as in the small loading window, but no matter what I try, it doesn't work.
I know that when opening a window as a dialog with ShowDialog, the opening window's cursor is set to be the normal default cursor (when hovering over your dialog, you have your selected cursor, but when hovering over the main window while the dialog is open, the cursor is set to the default cursor [arrow]).
I am trying to make my app show the same custom Busy cursor when the loading dialog is open, while hovering over the dialog itself and over the window that has opened the dialog, while preventing the user from accessing the main window's content, preventing from minimizing the small loading window and preventing from letting the small loading window be under the main window.
Is there any way of doing so (as stated above) while using ShowDialog or something similar to ShowDialog?
Is there way of changing the default cursor of the ENTIRE application (for example a custom arrow cursor instead of the one windows has by default)?
Is there any way of opening a dialog, setting it's cursor to be a custom Busy cursor and by that setting that cursor to be present while hovering over any part of the application?
My codes:
private void Window_ContentRendered(object sender, EventArgs e)
{
Mouse.OverrideCursor = new Cursor(System.IO.Path.GetFullPath(System.IO.Path.Combine(strCurrentDir, #"..\..\")) + #"Cursors\busy.ani");
//this.Cursor = new Cursor(System.IO.Path.GetFullPath(System.IO.Path.Combine(strCurrentDir, #"..\..\")) + #"Cursors\busy.ani");
//Mouse.OverrideCursor = Cursors.Wait;
bwReload = new BackgroundWorker();
bwReload.WorkerReportsProgress = true;
bwReload.DoWork += (sender_bw, e_bw) =>
{
new Thread(() =>
{
Application.Current.Dispatcher.Invoke(() =>
{
ProgressBarWindow pbwLoadWindow = new ProgressBarWindow("Loading...", this);
pbwLoadWindow.ShowInTaskbar = false;
pbwLoadWindow.Owner = this;
pbwLoadWindow.Cursor = new Cursor(System.IO.Path.GetFullPath(System.IO.Path.Combine(strCurrentDir, #"..\..\")) + #"Cursors\busy.ani");
pbwLoadWindow.ShowDialog();
});
}).Start();
ReloadA();
ReloadB();
};
bwReload.RunWorkerCompleted += (sender_bw, e_bw) => btnViewDashboard_Click(this, null);
bwReload.RunWorkerAsync();
}
Thanks in advance.
SOLVED
It is simple.
All you need to do is go to the dialog window's xaml and fit the window like that, that it will cover the entire main window.
Code:
<Window x:Class="PlGui.ProgressBarWindow"
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:PlGui"
mc:Ignorable="d" Height="600" Width="1025"
ResizeMode="NoResize" WindowStartupLocation="CenterScreen"
WindowStyle="None" AllowsTransparency="True">
<Window.Background>
<SolidColorBrush Opacity="0.01" Color="White"/>
</Window.Background>
<Grid Height="100"
Width="350"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="#7f8c8d">
......
</Grid>
</Window>
The results:

UWP Grid.ContextFlyout not opening

In my UWP application, I've a list view in my main window and I've a secondary window. Each list view item in my list view is a Grid and I've Grid.ContextFlyout for the grid. The ContextFlyout contains a MenuFlyout with 4 MenuFlyoutItem.
I open my application, in the main window list view, I right click an item. I see the ContextFlyout opening and the EventHandler<object> Opened getting triggered.
Now, I open my app's secondary window and close the main window. Again I open my app's main window by opening my app from the start menu.
Now, If I right click the same item in the list view that I clicked before, I could see the EventHandler<object> Opened getting triggered, but the context flyout is not opening in the UI.
This issue occurs only in the above explained scenario(1. open the app, 2. right click the item, 3. open secondary window, 4. close main window, 5.open the main window of the app again from start menu, 6.right click the item)
Below is my Grid
<Grid
Name="RootGrid">
<Grid.ContextFlyout>
<MenuFlyout
x:Name="OptionsFlyout"
Opening="Flyout_Opening"
Opened="Flyout_Opened"
Closed="Flyout_Closed">
<MenuFlyoutItem Name="Item1"/>
<MenuFlyoutItem Name="Item2"/>
<MenuFlyoutItem Name="Item3"/>
<MenuFlyoutItem Name="Item4"/>
</MenuFlyout>
</Grid.ContextFlyout>
<TextBlock Text="MyGridItem"/>
</Grid>
In my App.xaml.cs I am using the below code in OnLaunched method to recover my main window
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
Window.Current.Dispatcher.RunOnUIThread(async () =>
{
tryShow = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(ApplicationView.GetApplicationViewIdForWindow(CoreApplication.GetCurrentView().CoreWindow), ViewSizePreference.Default, e.CurrentlyShownApplicationViewId, ViewSizePreference.Default);
});
}
Am I missing anything while opening/recovering my main window which is closed before? Or is there anything that could be done to fix this issue?
Below is the github link for the sample app that I created to reproduce this issue.
UWP Grid Context Menu
This issue occurs only in the above explained scenario(1. open the app, 2. right click the item, 3. open secondary window, 4. close main window, 5.open the main window of the app again from start menu, 6.right click the item)
I could reproduce this issue. I've reported to the relevant team. You also could submit it in our Feedback Hub.
Here's a workaround for you. You could use FlyoutBase.AttachedFlyout instead of 'ContextFlyout'. You just need to register the RightTapped event of the Grid and add a little code like the following:
<Grid Name="RootGrid" Height="50" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Green" RightTapped="RootGrid_RightTapped">
<FlyoutBase.AttachedFlyout>
<MenuFlyout
x:Name="OptionsFlyout"
Opening="MenuFlyout_Opening"
Opened="FolderOptionsFlyout_Opened"
Closed="FolderOptionsFlyout_Closed">
<MenuFlyoutItem Name="Item1" Text="Item1"/>
<MenuFlyoutItem Name="Item2" Text="Item2"/>
<MenuFlyoutItem Name="Item3" Text="Item3"/>
<MenuFlyoutItem Name="Item4" Text="Item4"/>
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<TextBlock Text="{x:Bind}" Height="150" Width="100"/>
</Grid>
private void RootGrid_RightTapped(System.Object sender, RightTappedRoutedEventArgs e)
{
var fe = sender as FrameworkElement;
var menu = Flyout.GetAttachedFlyout(fe);
menu.ShowAt(fe);
}
According to the docs closing the main window should just hide it.
From Show multiple views for an app
"If secondary views are open, the main view’s window can be hidden – for example, by clicking the close (x) button in the window title bar - but its thread remains active."
Starting with 1703 you can have the main window handle the close request event. Upon close, the code can switch to the secondary window while hiding the main window. Then tell the system that you have handled the close yourself by setting the Handled property to true.
In the appxmanifest add the confirmAppClose capability.
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="confirmAppClose"/>
</Capabilities>
Now handle the CloseRequested event. Here's what the code looks like:
private int MainViewId;
private int SecondViewId;
public MainPage()
{
this.InitializeComponent();
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += MainPage_CloseRequested;
}
private async void MainPage_CloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
// Switch to Secondary window, Hide main window
await ApplicationViewSwitcher.SwitchAsync(
SecondViewId,
MainViewId,
ApplicationViewSwitchingOptions.ConsolidateViews);
// The close was handled, don't do anything else
e.Handled = true;
}
private async void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
MainViewId = ApplicationView.GetForCurrentView().Id;
var newCoreApplicationView = CoreApplication.CreateNewView();
await newCoreApplicationView.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
SecondViewId = ApplicationView.GetForCurrentView().Id;
Window SecondWindow = Window.Current;
var frame = new Frame();
frame.Navigate(typeof(Assets.SecondWindow));
SecondWindow.Content = frame;
SecondWindow.Activate();
});
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(SecondViewId, ViewSizePreference.Default);
}

Using several DispatcherFrames to show Modal windows not working properly, issue can be duplicated with WPF MessageBox

I have a problem showing modal windows in my application. My application functions like a web browser in that it can host multiple windows (i.e. the same application runs in two or more windows). Due to this, I want Modal windows to only be modal to the window that called it (so if I have two windows open, each window can have its own modal window). This means I can't use ShowDialog() as it blocks the entire application.
I implemented the solution proposed in this stackoverflow answer by using DispatcherFrames and Dispatcher.PushFrame(). This seemed to work, but after some testing I've found the following:
I start the application, open up a new window, open up a Modal window in the first window and then a Modal window in the second. Then I close the first Modal window, set the DispatcherFrame.Continue to false, but the PushFrame() does not return control to the code that called the method to show the modal window until the Modal window belonging to the second window has also been closed. I suspect this happens because when the first Modal window closes, it returns control to the second DispatcherFrame belonging to the other Modal window instead of the Application DispatcherFrame. However, if I close the second Modal window first, it works the way it should and returns control.
As an additional test, I tested the same situation in a fresh application with WPF MessageBoxes, and it turns out the same problem exists here as well.
MainWindow.xaml:
<Window x:Class="TestMessageBoxDispatcherFrame.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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<Button Click="OpenMessageBoxClick" Content="Open message box" />
<Button Click="OpenNewWindowClick" Content="Open new window" />
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OpenMessageBoxClick(object sender, RoutedEventArgs e)
{
var messageBoxResult = MessageBox.Show("Hello World!");
Console.WriteLine("MessageBox closed");
}
private void OpenNewWindowClick(object sender, RoutedEventArgs e)
{
var window = new MainWindow { ShowInTaskbar = true };
window.Show();
}
}
If I open up a second window, and open up a message box in each of these two windows, MessageBox.Show() will only return once the second opened MessageBox has been closed (so the first one blocks until the second one has been closed).
Is this a known issue? Working as intended? Are there any workarounds?
I have been able to create something close to what I want by using async/await, but this requires all methods that wants to call a MessageBox/Modal window to be async. What is the best way to show Modal windows the way I want?

Open referenced control in a new window

Issue
I want to load a referenced control from the main window into a new window. The referenced control is already child of the main window, causing the following exception when attempting to render the new window:
System.ArgumentException was unhandled:
Must disconnect specified child from current parent Visual before attaching to new parent Visual.
I do not want to disconnect it from the main window and I also cannot create a new instance of the control since I do not know how it's instantiated or what members are applied.
Background
I'm developing an application that allows developers to extend the application with additional views of configuration options. The container of these views may turn out to be too small for large view extensions (imagine a scheduling control for agendas as example), so I wish to provide the user with the ability to open the extended view in a new window.
Code
So far I've created a behavior to attach to Hyperlinks that opens a new window with the referenced control upon the Click event. The following code is the most basic implementation to demonstrate my intention:
public class ExpandViewBehavior : Behavior<Hyperlink>
{
public static DependencyProperty ViewProperty = DependencyProperty.Register("View", typeof(object), typeof(ExpandViewBehavior));
public object View
{
get { return GetValue(ViewProperty); }
set { SetValue(ViewProperty, value); }
}
protected override void OnAttached()
{
this.AssociatedObject.Click += AssociatedObject_Click;
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
if (View != null)
{
var window = new Window()
{
Content = View
};
window.Show();
}
}
}
Attached to a Hyperlink in the main window, referencing a simple TextBox to load in the new window. Where i is the System.Windows.Interactivity namespace and local my project namespace.
xmlns:local="clr-namespace:WpfApplication"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
...
<StackPanel>
<TextBlock>
<Hyperlink>
<i:Interaction.Behaviors>
<local:ExpandViewBehavior
View="{Binding Source={x:Reference SomeControl}}" />
</i:Interaction.Behaviors>
<TextBlock
Text="(Open in new window)" />
</Hyperlink>
</TextBlock>
<TextBox
x:Name="SomeControl" />
</StackPanel>
My question is, is there is a way to load the referenced control without disconnecting it from the main window?
As you can't display the same control in two places at once, you will either need to disconnect the control from the main window (as noted in the error text), or create a copy of the control to place into the child window.
You can clone the control by exporting its XAML, and then create a new control from this XAML.
See this answer How can you clone a WPF object? for more details.

How to remove a control from window programmatically?

I have a window with a button in it, and I need to remove it OR not depending on the argument passed to the window:
public MainWindow(bool removeControl)
{
InitializeComponent();
if (removeControl)
{
//code to remove the button
}
}
In the XAML file I declare a normal button:
<Button Width="120" Height="25" Content="Click" Name="ClickButton"></Button>
I know this can be done by doing the reverse thing which means add the button depending of the Boolean parameter, but I need to do so.
You can do:
mybutton.Visibility = Visibility.Collapsed;
...or if you really want it to be removed from the "logical tree"...then it all depends what "container"/parent that Button is in, in how you remove it.
Disconnecting an element from any/unspecified parent container in WPF
Remove Control from Window in WPF
http://joe-bq-wang.iteye.com/blog/1613370

Categories

Resources