Bind same Popup to multiple ElementName - wpf

I have multiple tabs and in each tab, there is a button that triggers same popup. The problem is since I use same IsPopupOpen to check and display popup, all three popups show up everytime.
<Popup IsOpen="{Binding IsPopupOpen}"
PlacementTarget="{Binding ElementName=A}" Placement="Center">
... same contents ...
</Popup>
<Popup IsOpen="{Binding IsPopupOpen}"
PlacementTarget="{Binding ElementName=B}" Placement="Center">
... same contents ...
</Popup>
<Popup IsOpen="{Binding IsPopupOpen}"
PlacementTarget="{Binding ElementName=C}" Placement="Center">
... same contents ...
</Popup>
PlacementTarget="{Binding ElementName={Binding eName}}"
Is there something like this so I can bind a Elementname to change or something?
I used mvvm, so I would prefer a none-code-behind suggestion.

Related

WPF Popup PlacementTarget=0,0 of ToggleButton

I have a popup that appears when I click a togglebutton like so.
<ToggleButton HorizontalAlignment="Right" Template="{StaticResource MyToggleButton}" Name="MyToggleButton" />
<Popup Name="MyPopUp" IsOpen="{Binding ElementName=MyToggleButton, Path=IsChecked}" StaysOpen="False" PlacementTarget="{Binding ElementName=MyToggleButton}" Placement="Center">
// blah blah
</Popup>
How can I get the popup to appear at the 0,0 position of my toggle button? (The top left of my popup is the same position as the top left of the toggle button)
I have tried using PlacementTarget and can get it to appear left/right/top/botton but now exactly over the top of the ToggleButton
Easy: Set Placement to Relative, and leave Popup.VerticalOffset and Popup.HorizontalOffset both at their default value of 0.
<Popup
Name="MyPopUp"
IsOpen="{Binding ElementName=MyToggleButton, Path=IsChecked}"
StaysOpen="False"
PlacementTarget="{Binding ElementName=MyToggleButton}"
Placement="Relative"
>
Out of curiosity, are you doing this to prevent the user from clicking on the ToggleButton while the popup is open?

WPF Popup UI showing black

I am using a WPF Popup control, and it is showing the background as black. I put a StackPanel inside it with Background="Transparent", but that does not help.
<Popup PlacementTarget="{Binding ElementName=parentStackPanel}" Placement="Center"
IsOpen="False" Name="m_popWaitNotifier" PopupAnimation="None"
AllowsTransparency="False">
<StackPanel Orientation="Vertical" Background="Transparent">
<uc:CircularProgressBar x:Name="CB" StartupDelay="0"
RotationsPerMinute="20"
Height="25" Foreground="White"
Margin="12"/>
</StackPanel>
</Popup>
How does one make the background on Popup transparent (or any color)?
You need to set the AllowsTransparency="True" Popup Property to True
Here is an example:
<Grid>
<StackPanel>
<Button Click="Button_Click" Width="100" Height="20" Content="Click" />
<Popup x:Name="popup" Width="100" Height="100" AllowsTransparency="True">
<Grid Background="Transparent">
<TextBlock Text="Some Text" />
</Grid>
</Popup>
</StackPanel>
</Grid>
and the click handler
private void Button_Click(object sender, RoutedEventArgs e)
{
popup.Visibility = System.Windows.Visibility.Visible;
popup.IsOpen = true;
}
The base color of a Popup, or a Window for that matter, is black. You rarely see it for a Window because Window has a Background property and it defaults to a solid color, but if you set Window.Background to Transparent it will also be black. But Popup doesn't have a Background property and so, pardon the pun, this problem "pops up".
If you want the Popup to be transparent, you need to set AllowsTransparency="True". However, if you want the Popup to be a solid color, the simplest approach is to make the child of the Popup a Panel that supports the Background property and set that property to the color you desire and then set the child of the Panel to be the content you intended for the Popup in the first place. I suggest Grid as it won't affect the layout of your Popup. It's only effect will be to give you the background color you desire.
Make sure that the allow transparency is set to true, vertical and horizontal alignments are centered, and the height and width are set to Auto.
For example:
<Popup Name="popup1" Placement="Top" PlacementTarget="{Binding ElementName=button2}" AllowsTransparency="True" Height="Auto" Width="Auto" Panel.ZIndex="1" HorizontalOffset="-5" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Height="92" HorizontalAlignment="Left" Margin="93,522,0,0" Name="stackPanelPop" VerticalAlignment="Top" Width="147">
</StackPanel>
</Popup>
Another possible cause:
using IsOpen="True" in markup before AllowTransparency="True"
Switching the order fixes it.
My guess is that the CircularProgressBar is actually causing the Black background. The only other way that this could happen is if there was a Style or something set on one of the controls (Popup or StackPanel or...).
Here is a quick-n-dirty example that shows a TextBlock in a popup when a checkbox is checked. The colors chosen are just to make sure things stand out visually:
<StackPanel x:Name="stackPanelLayout">
<StackPanel.Background>
<RadialGradientBrush Center="0.75, 0.75"
SpreadMethod="Reflect">
<GradientStop Color="LightBlue" Offset="0" />
<GradientStop Color="SeaGreen" Offset="0.5" />
<GradientStop Color="MidnightBlue" Offset="0.75" />
</RadialGradientBrush>
</StackPanel.Background>
<CheckBox x:Name="chkShowPopup"
FontSize="20"
Foreground="White"
Content="Show Popup" />
<Popup PlacementTarget="{Binding ElementName=stackPanelLayout}"
Placement="Center"
IsOpen="{Binding ElementName=chkShowPopup, Path=IsChecked}"
Name="m_popWaitNotifier"
PopupAnimation="Slide"
AllowsTransparency="True">
<StackPanel Orientation="Vertical" Background="Transparent">
<TextBlock Foreground="White" FontSize="30" FontWeight="Bold" Text="PopUp" />
</StackPanel>
</Popup>
</StackPanel>
So, two tests you can do to determine what is happening:
Replace the CircularProgressBar with a simple TextBlock or other control that you don't have a Style applied to.
Put the CircularProgressBar as a standalone control somewhere on your window, or on an otherwise blank test Window.
As per this article Why is my WPF Popup black and how do I get it positioned properly?
:
You need to set the AllowsTransparency property on the Popup to True, and set the PlacementTarget and Placement properties to control the position the Popup opens in.
As per the code in question:
<Popup PlacementTarget="{Binding ElementName=parentStackPanel}" Placement="Center" IsOpen="False" Name="m_popWaitNotifier" PopupAnimation="None" AllowsTransparency="False">
<StackPanel Orientation="Vertical" Background="Transparent">
<uc:CircularProgressBar x:Name="CB" StartupDelay="0" RotationsPerMinute="20" Height="25" Foreground="White" Margin="12"/>
</StackPanel>
</Popup>
the PlacementTarget is set to parentStackPanel, whereas the questioner has mentioned:
Hi Svetlozar: I tried this but it does
not work. For me though I do not have
a StackPanel outside the Popup, but I
have a StackPanel within the Popup
that holds a couple of control on it
The problem could be that Popup could not find the PlacementTarget 'parentStackPanel' because it does not exist.
The problem is that the grid is not orientation places it outside of the popup.
Remove VerticalAlignment and horizontalAlignment from all the controls inside the popup, and it will work correctly
Quite old, but may help someone: Add InitializeComponent(); in the constructor, it solved my problem:
class MyPopupClass : Popup {
/*
...
*/
public MyPopupClass () {
InitializeComponent();
/*
...
*/
}
/*
...
*/
}

WPF- How can I stop a popup in a control template from closing when clicked?

The control template is for a custom control.
I have seen this question:
Why does a WPF Popup close when its background area is clicked?
However I could not figure out how to bind a command to an event in the control template. I tried this:
<Popup
Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="True"
PopupAnimation="Slide"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{TemplateBinding PopupClickedCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Popup>
But the command does not fire.
Try the PreviewMouseLeftButtonDown event.

WPF ToolTip containing buttons can not recieve Mouse events, alternative?

I want a formatbar like in Office 2010 with WPF:
Select Text and then click buttons on appearing tooltip to execute a command
How can I make that work?
The ToolTip window can't accept focus, use the Popup control instead. It's a bit more cumbersome, than a tooltip, because many useful properties are set to false by default, here is a tiny example:
<Popup x:Name="samplePopup" PopupAnimation="Fade" Placement="Mouse" AllowsTransparency="True" StaysOpen="False" >
<Popup.Child>
<StackPanel Margin="10" >
<TextBlock Text="Some Text" HorizontalAlignment="Center" />
<Button Content="Close" HorizontalAlignment="Center" />
</StackPanel>
</Popup.Child>
</Popup>

WPF Popup hiding problem

Suppose you have a ToggleButton for opening a Popup, same behaviour as all known elements as ComboBox etc.
... which is this code:
<ToggleButton x:Name="PART_OpenToggleButton"
Focusable="False"
IsChecked="False"
Template="{StaticResource MyToggleButton}">
<Grid>
<Popup x:Name="PART_PopupControl"
Style="{StaticResource MyPopupStyle}"
StaysOpen="False"
VerticalAlignment="Bottom"
IsOpen="False"
PlacementTarget="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToggleButton, AncestorLevel=1}}" />
</Grid>
</ToggleButton>
Then in code behind you work with .IsOpen for Popup and .IsChecked for ToggleButton.
Everything works, but the problem arrives when you Open the Popup and click outside the borders.
The Popup will be closed but the ToggleButton stays checked.
You cannot set in the PopupOnClosed Handler that ToggleButton.IsChecked = false, because when you Click on the ToggleButton to close the Popup, the Popup closes itself, sets ToggleButton.IsChecked = false but at the sime time you clicked on the ToggleButton and it tries to Open the Popup again. So you cannot close it.
1st ToggleButtonClick:
-> ToggleButton IsChecked = true
2nd ToggleButtonClick:
-> ToggleButton IsChecked = false
-> ToggleButton IsChecked = true
So if you click on the Toggle Button while Popup being open, it blinks but stays open.
How would you solve this problem, please ?
EDITED:
Try this in a MyWindow.XAML and add the dependency property IsDropDownOpen
in the code behind, please:
<Grid>
<ToggleButton x:Name="PART_OpenToggleButton"
Focusable="False"
Height="20"
Width="50"
IsChecked="{Binding ElementName=TestWindow, Mode=TwoWay, Path=IsDropDownOpen}">
<Grid>
<Popup x:Name="PART_PopupControl"
Width="100"
Height="100"
StaysOpen="False"
Focusable="False"
VerticalAlignment="Bottom"
IsOpen="{Binding ElementName=TestWindow, Path=IsDropDownOpen}"
PlacementTarget="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToggleButton, AncestorLevel=1}}">
</Popup>
</Grid>
</ToggleButton>
</Grid>
public bool IsDropDownOpen
{
get { return (bool)GetValue(IsDropDownOpenProperty); }
set { SetValue(IsDropDownOpenProperty, value); }
}
public static readonly DependencyProperty IsDropDownOpenProperty =
DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(Window), new UIPropertyMetadata(false));
I found the solution on this post : https://stackoverflow.com/a/5821819/651161
Using the following class will permit to handle the click before the togglebutton is pressed. The popup is closed because of the click but the click is then handled, so it doesn't trigger the ToggleButton click.
public class MyPopup : Popup {
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) {
bool isOpen = this.IsOpen;
base.OnPreviewMouseLeftButtonDown(e);
if (isOpen && !this.IsOpen)
e.Handled = true;
}
}
Ok, here is some code that works for me (those are copy-pasted from working code with some of the not-interesting parts removed):
Here's the content of a ComboBox-like UserControl:
<ToggleButton x:Name="Button" Height="19">
<Grid>
<Label Name="DisplayList" Content="Whatever" />
<Popup Name="SelectionPopup" MinHeight="100" MinWidth="200"
StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=Button}">
</Popup>
</Grid>
</ToggleButton>
And from a custom template to an actual ComboBox:
<ToggleButton
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<Popup
Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
You could just bind the Popups StaysOpen property to the Buttons IsMouseOver property. This way, the popup will close whenever you click something outside the popup (IsMouseOver = false = StaysOpen) and it will close the Popup when clicking the ToggleButton (IsMouseOver = true = StaysOpen).
This way, even the clicks outside the Popup will be handled.
<ToggleButton x:Name="Toggle" />
<Popup x:Name="Popup" IsOpen="{Binding ElementName=Toggle, Path=IsChecked, Mode=TwoWay}"
StaysOpen="{Binding ElementName=Toggle, Path=IsMouseOver}" />
It seems to me, there are a two problems -
one is to adress the problem, that clicks inside the popup are potentially again handled according to it's the position in the visual tree.
The second problem is - autoclose via click - happens really every time you click outside the popup and can trigger additional events. Even you click the "open button" to close. The problem is - you don't know which value was set before at popup.isOpen - becase it always is false for the click eventhandler of the open button.
I don't use a toggleButton for saving memory, but i think the key problem is the same. The toggleButton already is false in the moment you click on it.
My example popup is defined like that:
<Popup Placement="Bottom" PopupAnimation="Slide" Name="PART_Popup" VerticalOffset="3" AllowsTransparency="True" StaysOpen="False">
If you click on the placement target - which was the "open button" the popup closes and at the same time the click event of the button was handled but the popup.IsOpen property was already 'false' - so it was opened again.
What I did to solve this was to subscribe the popups "Closed" event, saved the time - blocking reopen for a second.
DateTime? LastClose = new DateTime?();
private void Popup_Closed(object sender, EventArgs e)
{ LastClose = DateTime.Now; }
public bool AllowReopen
{
get {
if ((popup == null) || (popup.IsOpen)) return false;
//You cannot open, when the template isn't applied or it is already open
return !LastClose.HasValue || (DateTime.Now - LastClose.Value) > new TimeSpan(0,0,1) /*1s*/;
}
}
public void OpenPopup()
{
if (!AllowReopen) return;
popup.IsOpen = true;
}
I would bind both guys to the same property in the ViewModel. You can find good example in Toolbox default template:
<ToggleButton x:Name="OverflowButton"
FocusVisualStyle="{x:Null}"
IsEnabled="{TemplateBinding HasOverflowItems}"
Style="{StaticResource ToolBarHorizontalOverflowButtonStyle}"
IsChecked="{Binding Path=IsOverflowOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<Popup x:Name="OverflowPopup"
AllowsTransparency="true"
Placement="Bottom"
IsOpen="{Binding Path=IsOverflowOpen,RelativeSource={RelativeSource TemplatedParent}}"
StaysOpen="false"
Focusable="false"
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}">
<theme:SystemDropShadowChrome Name="Shdw" Color="Transparent">
<Border Background="{StaticResource ToolBarSubMenuBackground}"
BorderBrush="{StaticResource ToolBarMenuBorder}"
BorderThickness="1">
<ToolBarOverflowPanel x:Name="PART_ToolBarOverflowPanel"
Margin="2"
WrapWidth="200"
Focusable="true"
FocusVisualStyle="{x:Null}"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</theme:SystemDropShadowChrome>
</Popup>
Hope this helps,
Cheers, Anvaka.
To prevent closing the Popup by clicking its background, insert something that will fill it.
In this example, clicking the unfilled space will close the Popup:
<Popup x:Key="MyPop" Width="200" Height="200" StaysOpen="False">
<CheckBox Content="abc" />
</Popup>
In this example, clicking the unfilled space will NOT close the Popup:
<Popup x:Key="MyPop" Width="200" Height="200" StaysOpen="False">
<StackPanel Background="Red" Width="200" Height="200"> <!--EXTRA PANEL -->
<CheckBox Content="abc" />
</StackPanel>
</Popup>

Resources