UWP Grid.ContextFlyout not opening - c#

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);
}

Related

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

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

How to build my own close button?

I have a wpf application with a MVVM. I am trying here to build my own close button. Based on this answer Creating a custom Close Button in WPF I added a button event handler in the View(xaml.cs) code. However, it is not recognizing the Close(); call (doesn't exist in the context - Can't resolve symbol).
Also I tried the other answer and added Command and CommandParameter into my button's xaml. But the function behind is not getting hits. In How to bind Close command to a button using the RelayCommand also my wpf is not recognizing RelayCommand. Then How can I use the RelayCommand in wpf said that I have to write it myself(really?). I remember there was a simple way similar to just set an event handler for the button and call Close();. But, how can I do that or why it is not working for me?
View code:
private void closeButton_Click(object sender, RoutedEventArgs e)
{
// I want to call close the whole app on button click
//Close(); is not recognized
}
private void performMainCloseButtonCommand(object Parameter)
{
// This doesn't get hits on button click
Window objWindow = Parameter as Window;
objWindow.Close();
}
Button XAML:
<Button x:Name="closeButton" RenderTransformOrigin="0.5,0.5" Padding="0" Margin="701,0,0,0" BorderThickness="0" Click="closeButton_Click" Command="{Binding MainCloseButtonCommand}" CommandParameter="{Binding ElementName = mainWindow}" Height="45" Width="45" >
<StackPanel Height="45" Width="45">
<Image x:Name="closeButtonImage" Margin="0" Source="/ProjectName;component/Resources/x.fw.png" Height="33"/>
<TextBlock Text="Close" Width="36" Padding="6,0,0,0" HorizontalAlignment="Center" Height="13" FontSize="10"/>
</StackPanel>
</Button>
Close isn't recognized in your event handler because there is probably no method called Close in your current class. If you want to call main window's close method you can use:
private void closeButton_Click(object sender, RoutedEventArgs e)
{
Application.Current.MainWindow.Close();
}
Above is not a good way to do this and does not align with MVVM pattern. Which relates to your second question. Without seeing remaining part of your code, its hard to say why command binding isn't working. My guess you haven't wired up the commands properly for it to fire. You will need to ensure that you have created your RelayCommand instance and your command properties are correctly set.

Flyout's "Light Dismissal" Causing 2 Taps to be Required to Open Next Flyout

I have two buttons that show Flyouts when clicked. I am displaying them the same way as demonstrated in the XAML UI Basics sample:
private void ButtonTapped(object sender, TappedRoutedEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
if (element != null)
{
FlyoutBase.ShowAttachedFlyout(element);
}
}
My problem is that if Button 1's flyout is open, the next tap on the screen closes the flyout. This is fine, but if the next tap happens to be on Button 2, I want the button's tap event to be fired and open its flyout. Instead, the button doesn't register a tap at all and closes Button 1's flyout.
This results in needing to tap two times - one to dismiss the Button 1's flyout, and a second to show Button 2's flyout.
In other words:
Current Flow:
Tap Button 1
Button 1's Flyout is opened
Tap Button 2
Button 1's flyout is closed, (Button 2, nor Page registers the tap)
Tap Button 2
Now Button 2's flyout is opened
What I'm Looking For:
Tap Button 1
Button 1's Flyout is opened
Tap Button 2
Button 1's flyout is closed, Button 2's flyout opens.
How can I do this? I've tried intercepting the Tapped event for the page, but when the flyout is open, it seems to intercept the Tapped event so it can be used for the Flyout's light dismissal
Would overriding the style of the Flyout, or the FlyoutPresenterStyle help me here? Or perhaps opening the Flyout in a more MVVM-ish way which would allow me for finer control on how the Flyout is opened/closed?
I'm not sure how to get around this!
Here is something I found on Microsoft documents:
When dismissing with a tap, this gesture is typically absorbed and not passed on to the UI underneath. For example, if there’s a button visible behind an open flyout, the user’s first tap dismisses the flyout but does not activate this button. Pressing the button requires a second tap.
You can change this behaviour by designating the button as an input pass-through element for the flyout. The flyout will close as a result of the light dismiss actions described above and will also pass the tap event to its designated OverlayInputPassThroughElement. Consider adopting this behaviour to speed up user interactions on functionally similar items. If your app has a favourites collection and each item in the collection includes an attached flyout, it's reasonable to expect that users may want to interact with multiple flyouts in rapid succession.
[!NOTE] Be careful not to designate an overlay input pass-through element which results in a destructive action. Users have become habituated to discreet light dismiss actions which do not activate primary UI. Close, Delete or similarly destructive buttons should not activate on light dismiss to avoid the unexpected and disruptive behaviour.
In the following example, all three buttons inside FavoritesBar will be activated on the first tap.
<Page>
<Page.Resources>
<Flyout x:Name="TravelFlyout" x:Key="TravelFlyout"
OverlayInputPassThroughElement="{x:Bind FavoritesBar}">
<StackPanel>
<HyperlinkButton Content="Washington Trails Association"/>
<HyperlinkButton Content="Washington Cascades - Go Northwest! A Travel Guide"/>
</StackPanel>
</Flyout>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="FavoritesBar" Orientation="Horizontal">
<HyperlinkButton x:Name="PageLinkBtn">Bing</HyperlinkButton>
<Button x:Name="Folder1" Content="Travel" Flyout="{StaticResource TravelFlyout}"/>
<Button x:Name="Folder2" Content="Entertainment" Click="Folder2_Click"/>
</StackPanel>
<ScrollViewer Grid.Row="1">
<WebView x:Name="WebContent"/>
</ScrollViewer>
</Grid>
private void Folder2_Click(object sender, RoutedEventArgs e){
Flyout flyout = new Flyout();
flyout.OverlayInputPassThroughElement = FavoritesBar;
flyout.ShowAt(sender as FrameworkElement);}

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?

How to open window directly after main application window

My app's main UI is in MainWindow.xaml.
Firstly - what is causing this to be the window the application opens on startup. It does not appear to be defined as a "starup object" and there does not appear to be any code that specifically launches this window.
I can make my login window appear when the app starts by in the loaded event of MainWindow.xaml defining a new "login.xaml" and telling it to show as a dialog. However, if I do this then MainWindow does not appear until Login has been closed.
What I want to achieve is when my app starts, the MainWindow appears and then on top of that the Login window is displayed modally.
How can this be done?
The startup of the MainWindow is defined in App.xaml by default when creating a project in VS:
<Application ...
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Creating the dialogue in the Loaded event should work, just don't do it in the constructor where it is not yet loaded.
Loaded="Window_Loaded"
private void Window_Loaded(object sender, RoutedEventArgs e)
{
new LoginDialogue().ShowDialog();
}
One way could be to add a Loaded event handler to your main window and in that display the login window:
this.Loaded += LoadedEventHander;
void LoadedEventHander(object sender, RoutedEventArgs e)
{
// Show Login.xaml here.
}
In app.xaml the following line defines the startup window, you can change it if you want
StartupUri="MainWindowView.xaml"
If you are following MVVM you can bind a command to the windows loaded event using System.Windows.Interactivity (otherwise simply create an event handler as the others have suggested)
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding MyICommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

Categories

Resources