WPF popup wont close even with StaysOpen="false" - c#

I am trying to achieve the following:
User brings up a context menu in a datagrid.
User selects a context menu item which then opens a popup and displays some information.
when the user clicks anywhere else in the application, not in the popup, the popup closes.
Everything works fine until I come to closing the popup.
From searching elsewhere I am aware that I need Staysopen to be set to false ( which it is)
I also read the best way is to bind the IsOpen value to a property in the view model and set its binding to 2 way ( also done )
As a side note I have found that if I add a textbox and click inside the box, when I then click outside the popup it closes as desired.
Another thing I unsuccessfully tried as a workaround was to programmatically set the keyboard focus on the text box to get the "autoclose" functionality I desired.
Here is code:
xaml -
<Popup Name="PredictionsPopup" Height="200" Width="200" AllowsTransparency="false" StaysOpen="False" IsOpen="{Binding DisplaySummaryPopup, Mode=TwoWay}">
<StackPanel Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
<TextBlock Text="here is some stuff" />
<TextBox Name="hiddenBox" Text="moo"/>
</StackPanel>
</Popup>
Codebehind that sets the property on the viewmodel when the menu item is selected.
private void CurrentPredicitions_OnClick(object sender, RadRoutedEventArgs e)
{
PredictionsPopup.Placement = PlacementMode.MousePoint;
ViewModel.DisplaySummaryPopup = true;
}
Viewmodel property
public bool? DisplaySummaryPopup
{
get
{
return this.displaySummaryPopup;
}
set
{
this.displaySummaryPopup = value;
RaisePropertyChanged(() => this.DisplaySummaryPopup);
}
}
Please let me know if you need anymore details.

Here you have a working example:
MainWindow XAML:
<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>
<Popup Name="PredictionsPopup" Height="200" Width="200" AllowsTransparency="false" StaysOpen="False" IsOpen="{Binding DisplaySummaryPopup, Mode=TwoWay}">
<StackPanel Background="Red">
<TextBlock Text="here is some stuff" />
<TextBox Name="hiddenBox" Text="moo"/>
</StackPanel>
</Popup>
<DataGrid AutoGenerateColumns="False" Name="dataGrid1" IsReadOnly="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="Site" Width="150" />
<DataGridTextColumn Header="Subject" Width="310" />
</DataGrid.Columns>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Click Me" Click="ButtonBase_OnClick">
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
</Window>
MainWindow cs :
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
PredictionsPopup.Placement = PlacementMode.MousePoint;
PredictionsPopup.IsOpen = true;
}
ViewModel:
public class TestViewModel : INotifyPropertyChanged
{
private bool _displaySumarry;
public bool DisplaySummaryPopup
{
get { return _displaySumarry; }
set
{
_displaySumarry = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I think that your implementation for INotifyPropertyChanged is the one which causes the problem. I tried myself the code and is working now.

After trying to track this problem down I worked out that the issue is something to do with the context menu. I know this because as per the answer above instead of launching my popup via a context menu I launched it from a test button and all worked as expected.
I still don't know the exact reason for this issue but I think its something to do with the fact that the context menu is itself a subclass of popup and the focus isn't being set correctly on the new popup, so it never detects popup loss and closes.
To get round my problem I have added a close button to the popup, and then whenever the active tab in the control that hosts the popup changes it fires an event that the popup picks up and closes.

Had the same problem. The reason was, that the toggle button's ClickMode property was set to "Press". Setting it back to "Release" solved it :).

For me, the solution was to add this line in the constructor of the popup's code-behind:
LostFocus += delegate { this.IsOpen = false; };
Many hours were spent, when such a quickie line was all it took :)

I faced same problem a few times. Every time it was occuring, when a popup was changing its "isOpen" property to true from an event, which was raised from listview or datagrid element, like selectedItemChanged event, or items mouseUp event. I don't know reason, however resolved it by opening the popup from another task with code as below:
Task.Run(()=> Dispatcher.Invoke( () => myPopup.IsOpen = true));
Dispatcher is used to avoid an exception from changing any GUI object property from another than the main thread.

Related

MouseEnter event does not fire on a Popup control after a mouse click on the Control

I was testing a Popup control in WPF with the following code
<Window x:Class="Popup1.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"
xmlns:local="clr-namespace:Popup1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="250">
<Grid>
<TextBlock TextWrapping="Wrap">You can use a popup to provide a link for a specific
<Run TextDecorations="Underline" MouseEnter="ContentElement_OnMouseEnter">
term
</Run>
</TextBlock>
<Popup Name="popLink" StaysOpen="False" Placement="Mouse" MaxWidth="200"
PopupAnimation="Slide" AllowsTransparency="True">
<Border>
<TextBlock Margin="10" TextWrapping="Wrap">
For more information, see
<Hyperlink NavigateUri="http://en.wikipedia.org/wiki/Term" Click="Hyperlink_OnClick">Wikipedia</Hyperlink>
</TextBlock>
</Border>
</Popup>
</Grid>
</Window>
and the handlers
private void ContentElement_OnMouseEnter(object sender, MouseEventArgs e) {
popLink.IsOpen = true;
}
private void Hyperlink_OnClick(object sender, RoutedEventArgs e) {
Process.Start(((Hyperlink) sender).NavigateUri.ToString());
}
The result is a trivial window that contains a textblock with a link to a popup control that visually appears when the mouse hovers over the link to the popup.
The normal behavioris the popup to stay visible until a mouse click. This works fine as long as the mouse click is not on the link to the popup
The strange behaviorthat i can't explain happens when i click the mouse over the link to the popup.Then, the popup closes ( as expected ) but it never appears again when the mouse hovers over the link (as it should).
Can you explain this behavior?
As commented, the reason is probably a race condition between closing popup and re-opening because the mouse is over the textblock. You can prevent this situation by delaying the popup open action until current work is completed:
private void ContentElement_OnMouseEnter(object sender, MouseEventArgs e)
{
Dispatcher.BeginInvoke(new Action(() => popLink.IsOpen = true));
}
Regarding your title text: the MouseEnter event is actually fired (debug it!), just the action within is not working as expected because the popup is in an inconsistent state.
After some tweaking the best behaviour is achieved if we add an extra event (comparing to the initial code) handler for the Popup Close event that sets the IsOpen property to false when the popup closes
private void PopLink_OnClosed(object sender, EventArgs e) {
if (popLink.IsOpen) {
popLink.IsOpen = false;
}
}
and the ammenment in XAML
<Popup Name="popLink" StaysOpen="False" Placement="Mouse" MaxWidth="200"
PopupAnimation="Slide" AllowsTransparency="True"
Closed="PopLink_OnClosed">
<Border Background="Bisque">
<TextBlock Margin="10" TextWrapping="Wrap">
For more information, see
<Hyperlink NavigateUri="http://en.wikipedia.org/wiki/Term" Click="Hyperlink_OnClick">Wikipedia</Hyperlink>
</TextBlock>
</Border>
</Popup>

Wpf - popup issue inside Usercontrol when click on button

I have a Window page, after click a button from window page --> then a UserControl page showing. After Inside UserControl there is a <Popup Name="MyPopup"> popup. The Popup always stays on top problem. How can I solve this issue ?
I have tried,
<Window x:Class="WpfApplication1.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" Background="Green">
<Grid >
<Button Height="50" Width="100" Content="window_ClickMe" Click="btnUserManage_Click"></Button>
<ContentControl Name="cont2" Visibility="Hidden">
</ContentControl>
</Grid>
</Window>
and code behind page of window,
private void btnUserManage_Click(object sender, RoutedEventArgs e)
{
UC_UserMgmt mw = new UC_UserMgmt();
cont2.Content = mw;
cont2.Visibility = Visibility.Visible;
}
then, this is usercontrol page with popup,
<UserControl x:Class="WpfApplication1.UC_UserMgmt"
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="400" Background="Blue">
<Grid>
<Grid Name="g1">
<Button Content="usercontrol_ClickMe" Height="50" Width="150" Margin="150,0,0,250" Click="btnShow_Click"></Button>
</Grid>
<Popup Name="MyPopup" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
HorizontalOffset="-150" Placement="Mouse" StaysOpen="{Binding ElementName=g1,Path=IsMouseOver}"
VerticalOffset="20"
AllowsTransparency="True">
<StackPanel>
<Border BorderBrush="Black" Background="Brown" BorderThickness="1" Width="300" Height="100" >
<Grid>
<TextBox x:Name="txtUName" HorizontalAlignment="Center" Height="28" Width="223" TextWrapping="Wrap" VerticalAlignment="Top" Margin="10,26,64.6,0" />
<Button Content="Open" Height="30" Width="50" Margin="238,24,9.6,43.6" Click="btnOpen_Click"/>
</Grid>
</Border>
</StackPanel>
</Popup>
</Grid>
</UserControl>
and this is code behind page of usercontrol,
private void btnOpen_Click(object sender, EventArgs e)
{
MyPopup.IsOpen = true;
System.Windows.Forms.OpenFileDialog fDialog = new System.Windows.Forms.OpenFileDialog();
fDialog.Title = "Select file to be zip";
if (fDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
txtUName.Text = fDialog.FileName.ToString();
}
}
private void btnShow_Click(object sender, EventArgs e)
{
MyPopup.IsOpen = true;
}
The problem is, when user click on OPEN button, an openFileDialog is opening and when it is opened, the popup seems disappear. How can I solve this problem?
I believe using Popup is not the right choice in your situation. Take a look at Popup.StaysOpen Property, in particular the part that says
When the StaysOpen property is set to true, Popup stays open until it is explicitly closed by setting the IsOpen property to false. When StaysOpen is false, the Popup control intercepts all mouse and keyboard events to determine when one of these events occurs outside the Popup control.
I can't think of a clean way of keeping that popup open when showing the dialog box, because the dialog box is going to create a mouse/keyboard event when you interact with it
But to answer your question, I can think of two choices
1) You can simply move MyPopup.IsOpen = true; in btnOpen_Click to the end of the code block. I assume this will cause the popup to reopen after the dialog box is closed
2) You can create a boolean property or dependency property in your code behind set it to true when showing the popup and bind your Popup's StaysOpen to it, and maybe set it to false in LostFocus for g1 or something like that. Or even set StaysOpen to true and use IsOpen to close your Popup in g1's LostFocus
Edit - Second Solution How-To Don't use it, dirty code, bad practices, and the popup remains on top of dialogbox
In the Popup set StaysOpen="True"
In every control in the UserControl except the UserControl itself and the textbox set Focusable="False"
Add private bool dont; to the UserControl's code-behind and set it to True in the beginning of btnOpen_Click and set it to False in its end
In the UserControl and the TextBox add LostFocus="UserControl_LostFocus"
And then add this function in UserControl code-behind
private void UserControl_LostFocus(object sender, RoutedEventArgs e)
{
if(!dont && !txtUName.IsFocused && !IsFocused)
MyPopup.IsOpen = false;
}

How to prevent a UserControl from taking focus away from its parent?

I am using MVVM for my application and have a form that allows the user to enter basic personnel information. The form includes a UserControl which is, basically, an ItemsControl that includes textBoxes that can be dynamically created. This is a simplified version:
<ItemsControl x:Name="items" ItemsSource="{Binding MyItemsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="row">
<TextBox x:Name="textBox" Text="{Binding ContactInfo, ValidatesOnExceptions=True}" extensions:FocusExtension.IsFocused="{Binding IsFocused}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Name="NewItemButton" Command="{Binding AddItemToMyCollectionCommand}" />
I want the TextBox that has just been created to receive focus, therefore I added an attached property. This is part of it:
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
In the form that contains the UserControl there are several other text boxes before and after. The UserControl has its own ViewModel, which I set as the DataContext of the control through a property in the container's ViewModel. Basically, a simplified version of the container looks like this:
<StackPanel Orientation="Horizontal" />
<TextBox x:Name="firstName" />
<TextBox x:Name="lastName" />
<local:DynamicFormUserControl
x:Name="phones"
DataContext="{Binding PhonesViewModel}" />
<local:DynamicFormUserControl
x:Name="emails"
DataContext="{Binding EmailsViewModel}" />
<TextBox x:Name="address" />
</StackPanel>
My problem is that I want the firstName TextBox to get the focus when the form is loaded for the first time, but the form keeps on placing the focus on the first TextBox of the phones UserControl. I tried to override it by using firstName.Focus() on the Loaded event of the form, but this didn't work, and no matter what I tried the focus is still on the phones userControl instead of the first element in the form that contains it.
Does anybody have any idea how to solve this?
Thanks.
Here you go
add FocusManager.FocusedElement="{Binding ElementName=firstName}" to your stack panel
<StackPanel Orientation="Horizontal"
FocusManager.FocusedElement="{Binding ElementName=firstName}"/>
<TextBox x:Name="firstName" />
<TextBox x:Name="lastName" />
<local:DynamicFormUserControl
x:Name="phones"
DataContext="{Binding PhonesViewModel}" />
<local:DynamicFormUserControl
x:Name="emails"
DataContext="{Binding EmailsViewModel}" />
<TextBox x:Name="address" />
</StackPanel>
also notice that you may need to prevent items control in the user control from focusing itself
<ItemsControl x:Name="items" Focusable="False" >
<ItemsControl.ItemTemplate>
I guess I managed to find a solution. The problem was that the form I created was itself a user control inside a window, and never got focus. I didn't think that would be relevant so I didn't mention it in my previous post- sorry. I found in this solution for forcing focus to a user control.
Basically, when I have a UserControl inside a window it doesn't get focus even if I try to set the focus with either Focus() or FocusedElement. So to overcome this problem I found on a different post a workaround. Basically I added it to the code-behind of the UserControl that contains the firstName TextBox. If we call the UserControl, say, PersonalInfoUserControl, the constructor of the control would look like this:
public PersonalInfoUserControl()
{
InitializeComponent();
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(UserControl_IsVisibleChanged);
}
I added an event handler to the IsVisibleChanged event of the control. The method would look like this:
void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
firstName.Focus();
}));
}
}

WPF Setting focus Issues

Basis: I am using the MVVM pattern or a subset of it.
In my main window I have a button that opens up a usercontrol with a new DataContext the function looks kinda like this:
public void SetUserControl()
{
UCDatacontext = new UCViewModel(this);
base.OnPropertyChanged("UCDatacontext");
UCViewVisibilty = Visibility.Visible;
UCDatacontext.IniFocus();
}
And then when I am done I close the usercontrol and Dispose of the DataContext. Now the problem I am having is that I can't seem to get the focus setting to work properly, I have a Textbox in the usercontrol that I want to set focus to when the view becomes Visible. However on the first time that I attempt to set focus it only fills the text box with an unblinking caret, which after investigation leads me to believe that it is because the TB isn't getting the Keyboard focus (only logical focus), however even after explicity setting the keyboard focus I still get the unblinking caret, and it is only after clicking in the TB that it is getting focus. The method I am using to set focus is similar to method described here.
If in the view I do some writeline debugging by printing out in the FocusSet Event for the textbox it does get set, however only on the first time I call SetUserControl(). If I call SetUserControl() again it does nothing, except making the View Visible but doesn't trigger the Focus Set Event.
Below is the lines of code from the MainWindow:
<Grid Grid.ColumnSpan="5" Grid.RowSpan="5" Visibility="{Binding Path=UCViewVisibilty }" x:Name="UCGrid" >
<Grid.Effect>
<DropShadowEffect />
</Grid.Effect>
<View:UCView DataContext="{Binding Path=UCDatacontext}" />
</Grid>
And UserControl Grid:
<Grid >
<TextBox Uid="UCTB" localExtensions:FocusExtension.IsFocused="{Binding Path=UCTBFocus}" Height="23" HorizontalAlignment="Left" Margin="113,56,0,0" Name="UCTB" VerticalAlignment="Top" Width="165" Text="{Binding Path=UCTBContent, UpdateSourceTrigger=PropertyChanged}" GotFocus="UCTB_GotFocus" />
</Grid >
The Focus is set in the UserControlViewModel, and is set after the Usercontrol is rendered.
it like this Set focus one by one from top to bottom.
InitializeComponent();
FocusManager.SetFocusedElement(this, TabItem); //this is Window , TabItem is UserControl in this Window
FocusManager.SetFocusedElement(TabItem, TextBox); // TabItem is UserControl and TextBox is Control in TabItem UC
I hope this will help.
As it turns out, after fiddling around with the code, the reason why the focus wasn't being set properly in the View was because the binding in the View Model was this:
bool _tBfocus;
public bool UCTBFocus
{
get { return _tBfocus; }
set
{
_tBfocus= value;
base.OnPropertyChanged("UCTBFocus");
}
instead of:
bool _tBfocus;
public bool UCTBFocus
{
get { return _tBfocus; }
set
{
if (_tBfocus == value)
return;
_tBfocus= value;
base.OnPropertyChanged("UCTBFocus");
}
}
After changing it everything worked fine :/ but if someone could explain to me why this annoyance I was having was caused by that I would be truly grateful :)

Listbox bound to ObservableCollection empty

I have the following xaml:
<Window x:Class="Retail_Utilities.Dialogs.AdjustPriceDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner" Name="Adjust_Price"
Title="Adjust Price" Background="#ee0e1c64" AllowsTransparency="True" WindowStyle="None" Height="330" Width="570" KeyDown="Window_KeyDown" Loaded="Window_Loaded">
<Grid Height="300" Width="550">
<ListBox HorizontalAlignment="Right" Margin="0,110,35,60" Name="lstReasons" Width="120" VerticalAlignment="Stretch"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window, AncestorLevel=1}, Path=reasons}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=POS_Price_Change_Reason}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Here is the relevant c#:
namespace Retail_Utilities.Dialogs
{
public partial class AdjustPriceDialog : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Twr_POS_Price_Change_Reason> reasons; ...
and finally, here is the code from another page that opens this window:
AdjustPriceDialog apd = new AdjustPriceDialog();
apd.Owner = (Window)this.Parent;
apd.reasons = new ObservableCollection<Twr_POS_Price_Change_Reason>();
var pcr = from pc in ctx.Twr_POS_Price_Change_Reasons where pc.Deactivated_On == null select pc;
foreach (Twr_POS_Price_Change_Reason pc in pcr)
{
apd.reasons.Add(pc);
}
apd.AdjustingDetail = (Twr_POS_Invoice_Detail)lstDetails.SelectedItem;
if (apd.ShowDialog() == true)
{
}
When the dialog box opens, my lstReasons list is empty. I don't get any errors and when I place a stop in the code, I see that the reasons collection gets populated with the items from the table.
Reasons needs to be a Property (add { get; set;} ). Also, look at Visual Studio Output - it shows Binding errors, there should be some info about failed binding to reasons.
The problem seems to be How you are creating the property.
I know you put your prperty as an observable collection but this doesn't mean it is by it self observalble!
so you need to notify the UI when this property is changed by doing something in the setter like this:
public ObservableCollection<Twr_POS_Price_Change_Reason> reasons
{
get{....}
set
{
Notify('reasons')
}
}
I don't remember the exact code because I didn't use WPF for a while but it is a method in INotifyPropertyChanged, good luck!
It seems your binding path is set to POS_Price_Change_Reason, while the name of your property is reasons. Unless you didn't include POS_Price_Change_Reason in your example code and reasons is the backing field for this property.
Also, keep in mind that you can only bind to public properties, not fields. Additionally, if you change the value of the property, you need to notify the view of this change, by invoking your PropertyChangedEventHandler event for that property:
PropertyChanged(new PropertyChangedEventArgs("YourPropertyName"));

Categories