WPF Popup disappear on Custom contextmenu opening - wpf

I have a TextBox in a PopUp. On clicking a ToggleButton, the PopUp Window opens.
Toggle Button Code
<ToggleButton Name="popupButton" Content="Click Me To Open Popup!" >
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border Name="bg" RenderOptions.BitmapScalingMode="NearestNeighbor" RenderOptions.EdgeMode="Aliased" Background="Transparent" Width="17" Height="15">
<StackPanel HorizontalAlignment="Right" Width="17" >
<Path RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased" Data="M186,52 C208,52 230,52 252,52" Fill="Black" Margin="0,1.5" Stretch="Fill" Stroke="#457faa" StrokeThickness="2" />
<Path RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased" Data="M186,52 C208,52 230,52 252,52" Fill="Black" Margin="0,1.5" Stretch="Fill" Stroke="#457faa" StrokeThickness="2" />
<Path RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased" Data="M186,52 C208,52 230,52 252,52" Fill="Black" Margin="0,1.5" Stretch="Fill" Stroke="#457faa" StrokeThickness="2" />
</StackPanel>
</Border>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
On clicking the ToggleButton it will open following the PopUp Window which contains the TextBox.
Popup Window Code
<Popup Name="testPopup" Placement="Bottom" PlacementTarget="{Binding ElementName=popupButton}" IsOpen="{Binding IsChecked, ElementName=popupButton}" StaysOpen="False">
<StackPanel Orientation="Horizontal">
<Label Content="Username: " Background="White"/>
<TextBox Text="Please RightClick to copy the text." ContextMenuOpening="TextBox_ContextMenuOpening">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Copy"/>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</StackPanel>
</Popup>
When a user clicks on the TextBox in the PopUp I want to call the ContextMenu opening event which is as following:
CodeBehind for ContextMenuOpening Event
private void OpenContextMenu(FrameworkElement e)
{
if (e.ContextMenu != null)
{
e.ContextMenu.IsOpen = true;
}
}
private void TextBox_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
e.Handled = true;
this.OpenContextMenu(sender as FrameworkElement);
}
Purpose to Call ContextMenuOpening
I'm calling its opening event because I want to stop the parent contextmenu to open by e.Handled = true. Then I want to open the ContextMenu of the TextBox (source element). Here comes the issue.
Issue
As soon as I open the ContextMenu from code-behind, it opens the ContextMenu but the PopUp disappears as StayOpen property is set to False. I want to use StayOpen to False, as it should close as soon as the focus is lost.

Related

WPF Tooltip binding and open programatically does not work

I have a Custom tooltip icon which also has its content bound to Datacontext. My need was to open Tooltop on mouse hover as well as on click event. So I used following code
<Image Source="/MainUI;component\Images\home\tooltip_info.png"
Width="24" Height="24" Stretch="Fill" HorizontalAlignment="Right"
Name="ImageToolTip"
Margin="0,0,0,0" MouseUp="ImageToolTip_MouseUp" MouseLeave="ImageToolTip_MouseLeave">
<Image.ToolTip>
<ToolTip BorderBrush="Transparent" Background="Transparent" HorizontalOffset="-142">
<TextBlock TextWrapping="WrapWithOverflow"
Style="{StaticResource ExcalFont-MSFD300}"
FontSize="14" Text="{Binding Tips}"
Width="300" Padding="15,15,15,15">
<TextBlock.Background>
<ImageBrush ImageSource="Images\home\popupbox.png" />
</TextBlock.Background>
</TextBlock>
</ToolTip>
</Image.ToolTip>
</Image>
Code Behind:
private void ImageToolTip_MouseUp(object sender, MouseButtonEventArgs e)
{
((ToolTip)((FrameworkElement)sender).ToolTip).IsOpen = true;
}
private void ImageToolTip_MouseLeave(object sender, MouseEventArgs e)
{
((ToolTip)((FrameworkElement)sender).ToolTip).IsOpen = false;
}
Now the issue is on mouse up It opens Tooltip but it does not bind the text.
This is working fine if I am using static text instead of Binding. What am I doing wrong?
In case I mouse hover on icon then it works fine and shows the content too. Thereafter everytime mouse click also shows the tooltip content. Not sure why mouse click do not work initially. –
ToolTip is not part of the VisualTree.
A problem that is similar to the problem you have is described here:
RelativeSource binding from a ToolTip or ContextMenu
One option to solve your problem would be something like this:
<Image Source="/MainUI;component\Images\home\tooltip_info.png"
Width="24" Height="24" Stretch="Fill" HorizontalAlignment="Left"
Name="ImageToolTip"
Margin="0,0,0,0" MouseUp="ImageToolTip_MouseUp" MouseLeave="ImageToolTip_MouseLeave" Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=Self}}">
<Image.ToolTip>
<ToolTip BorderBrush="Transparent" Background="Transparent" HorizontalOffset="-142" >
<TextBlock TextWrapping="WrapWithOverflow"
FontSize="14" Text="{Binding PlacementTarget.Tag.Tips,
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToolTip}}"
Width="300" Padding="15,15,15,15">
<TextBlock.Background>
<ImageBrush ImageSource="Images\home\popupbox.png" />
</TextBlock.Background>
</TextBlock>
</ToolTip>
</Image.ToolTip>
</Image>

ScrollViewer in TextBlock Template routing the scroll?

i've got a ScrollViewer (name="master"), inside a StackPanel with some example Borders or rectangles and also a TextBox which have more Text than it's displayed, so the TextBox is scrollable. When you scroll the TextBox and came to the top oder bottom border the ScrollViewer which is in the TextBox Template (ScrollViewer x:Name="PART_ContentHost") routed the scrolling to the main ScrollViewer outside ("master").
is it possible to edit the TextBox Template to elimate this behavior?
One way is the combination with another ScrollViewer in the Template, this works fine, but it#s not more possible to see the selection so it's no solution.
Have you any other ideas?
<Style x:Key="TextBoxStyle1" TargetType="{x:Type TextBox}">
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer x:Name="PART_ContentHost" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ScrollViewer Name="master" Height="71" Width="164" HorizontalAlignment="Right" Margin="0,0,38.666,28.833" VerticalAlignment="Bottom">
<StackPanel Orientation="Vertical" Width="121">
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<TextBox Text="TextBox sydxr gs dgh drz h we rths dretghe dtrzuj hwesrtgh
bdnftzh srdztj ser tghbed5rsetzhnrd hserdfgcjmnjs egrfhfn dshgrdxthgj" TextWrapping="Wrap" Height="67" Style="{StaticResource detail_text}" />
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<Rectangle Fill="red" Height="45" Stroke="Black"/>
</StackPanel>
</ScrollViewer>
Thanks a lot.
My suggestion is to capture the PreviewMouseWheelEvent on the textbox itself:
<TextBox Text="TextBox sydxr gs dgh drz h we rths dretghe dtrzuj hwesrtgh
bdnftzh srdztj ser tghbed5rsetzhnrd hserdfgcjmnjs egrfhfn dshgrdxthgj" TextWrapping="Wrap" Height="67" ScrollViewer.VerticalScrollBarVisibility="Auto" PreviewMouseWheel="TextBox_PreviewMouseWheel" />
Note that i included ScrollViewer.VerticalScrollBarVisibility="Auto" also.
Then evaluate if the scroll is being made when either on top or bottom and mark the event as handled if so:
private void TextBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var textbox = sender as TextBox;
var decorator = VisualTreeHelper.GetChild(textbox, 0) as Decorator;
var scrollViewer = decorator.Child as ScrollViewer;
if ((scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight && e.Delta < 0) ||
(scrollViewer.VerticalOffset == 0 && e.Delta > 0))
{
e.Handled = true;
}
}
No need to apply new templates this way, cheers!

WPF Popup control on ImageMouseLeftButtonDown disappears immediately

I have a popup defined as follows,
<Popup x:Name="popLines"
Placement="Bottom"
IsOpen="False"
Width="145" Height="42"
StaysOpen="False"
PopupAnimation="Fade"
AllowsTransparency="True"
HorizontalOffset="-2" VerticalOffset="0">
<Grid Margin="2">
<Path StrokeThickness="0.7" StrokeLineJoin="Round" Fill="#FFFFFFFF" Stretch="Fill" Stroke="Black" Data="M6.5,0.5 L30.167,0.5 30.167,8.4999992 190.16701,8.4999992 190.16701,44.166001 0.5,44.166001 0.5,8.4999992 6.5,8.4999992 6.5,0.5 z">
</Path>
<Grid>
<StackPanel Orientation="Horizontal">
<TextBox BorderBrush="Black" BorderThickness="0.5" Margin="5,10,2,2" Width="110" Height="20" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ToolTip="Excel File Path"></TextBox>
<Image Source="/App_Desktop;component/Resources/save.png" Margin="2,10,5,2" Width="16" Height="16"></Image>
</StackPanel>
</Grid>
</Grid>
</Popup>
I set IsOpen=true when an image MouseLeftButtonDown event fires.Except, the popup disappears as soon as it appears. What is going wrong?
I think what happens is, that the the MouseLeftButtonUp event fires while the Mouse is not over the Popup. Try holding the mousebutton down and move your mouse so it is above the popup to see if it stays open, and then release the mousebutton.
you can solve this by setting StaysOpen="True" or by setting IsOpen=true in a click eventhandler or MouseButtonUp eventhandler.
Also using your example with the image, you could have a transparent area right above the image in your popup visualtree that 'captures' the MouseUp event when the popup is open. you would have to change the placement option and bind that area to the image's width and height properties.

WPF ComboBox popup placement : Bottom and aligned to the right edge

I'm trying to create a ComboBox with a non-standard dropdown alignment. Basically, I want the dropdown to be below the ComboBox, but aligned with the right edge of the ComboBox instead of the left edge.
What a normal ComboBox looks like, using PlacementMode="Bottom":
What I want:
I tried to play with the Popup.PlacementMode property in the template of my ComboBox, but none of the possible values seem to do what I want. Is there a simple way to do it, preferably in pure XAML?
When I opened Expression Blend, I have come up with the solution within a few seconds:
<Popup Placement="Left" VerticalOffset="{TemplateBinding ActualHeight}"
HorizontalOffset="{TemplateBinding ActualWidth}"
Sometimes this application is more useful than writing xaml by hands, but not so often.
I would use the "Custom" placementmode for the PopUp and declare a callback to place the popup control into the correct position, like it's shown here: WPF ComboBox DropDown Placement
See if an example here would work for you:
public class TestComboBox : ComboBox
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var popup = (Popup)Template.FindName("PART_Popup", this);
popup.Placement = PlacementMode.Custom;
popup.CustomPopupPlacementCallback += (Size popupSize, Size targetSize, Point offset) =>
new[] { new CustomPopupPlacement() { Point = new Point (targetSize.Width-popupSize.Width, targetSize.Height) } };
}
}
hope this helps, regards
Can someone post the complete xaml code please?
I've tried the following:
<ComboBox Grid.Column="1" Height="24" Width="20" HorizontalAlignment="Right"
VerticalAlignment="Top"
Name="comboBox2"
ItemsSource="{Binding Source={StaticResource FilterTypes}}"
SelectedValue="{Binding Path=SelectedType, Mode=TwoWay}" >
<ComboBox.Template>
<ControlTemplate>
<Popup Placement="Left" VerticalOffset="{TemplateBinding ActualHeight}"
HorizontalOffset="{TemplateBinding ActualWidth}" />
</ControlTemplate>
</ComboBox.Template>
</ComboBox>
... after some working and testing I've found a good solution...
<ComboBox.Style>
<Style TargetType="ComboBox" >
<Setter Property="Popup.FlowDirection" Value="RightToLeft"/>
</Style>
</ComboBox.Style>
it's a little hacky, but does work. you just have to change the combobox style.
<Grid Height="40">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<FrameworkElement Name="dummy" Visibility="Collapsed">
<FrameworkElement.RenderTransform>
<TransformGroup x:Name="xformgrp">
<TranslateTransform X="{Binding ElementName=PopupContent, Path=ActualWidth}" />
<ScaleTransform ScaleX="-1" />
<TranslateTransform X="{Binding ElementName=chk, Path=ActualWidth}" />
</TransformGroup>
</FrameworkElement.RenderTransform>
</FrameworkElement>
<CheckBox Name="chk" HorizontalAlignment="Center">checkthisout</CheckBox>
<Popup IsOpen="{Binding IsChecked, ElementName=chk}" PlacementTarget="{Binding ElementName=chk}" Placement="Bottom" HorizontalOffset="{Binding ElementName=dummy, Path=RenderTransform.Value.OffsetX}">
<TextBlock Name="PopupContent" Foreground="Yellow" Background="Blue">yeah long popupcontent</TextBlock>
</Popup>
</Grid>
</Grid>
The popups HorizontalOffset just has to get the value of PopupContent.ActualWidth-PlacementTarget.ActualWidth. To get that value I used this trick from Charles Petzold.

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