I'm using a Popup control with following xaml
<Popup AllowsTransparency="True" IsOpen="{Binding PopupContext.IsOpen}"
x:Name="__popup" StaysOpen="False">
<TabControl Grid.Row="1">
<TabItem Header="123"><Button>test</Button></TabItem>
</TabControl>
</Popup>
If I click Button and click outside of the popup, popup is closed normally. But if I click a TabItem and click outside parent window (if I click desktop, popup close normally), it doesn't close popup.
EDIT
I set Popup.IsOpen to true from ContextMenu.Opened event soo UIElement is focused and cant lostfocus. If UIElement.ContextMenu.Closed event fired and MouseLeaved to popup, I set Popup.IsOpen property to false. (soo if not clicked popup, it's close with ContextMenu)
private void viewModelMasa_ContextMenuOpening(MasaViewModel Sender, System.Windows.Controls.ContextMenuEventArgs args)
{
MasaView source = args.Source as MasaView;
ContextMenu menu = source.ContextMenu;
RoutedEventHandler openHandler = null;
RoutedEventHandler closeHandler = null;
this.PopupContext.Position = System.Windows.Controls.Primitives.PlacementMode.Top;
closeHandler = (sender, e) =>
{
if (!this.PopupContext.IsFocus) // Setting from Popup MouseEnter and MouseLeave event;
{
this.PopupContext.IsOpen = false;
}
menu.Closed -= closeHandler;
};
openHandler = (sender, e) =>
{
ContextMenu contextMenu = sender as ContextMenu;
if (contextMenu != null)
{
this.PopupContext.IsFocus = false;
System.Windows.Point point = new System.Windows.Point();
var pos = contextMenu.TranslatePoint(point,this.PopupContext.Parent);
this.PopupContext.Rectangle = new Rect(pos.X, pos.Y, contextMenu.ActualWidth, contextMenu.ActualHeight);
this.PopupContext.IsOpen = true;
}
menu.Opened -= openHandler;
};
menu.Opened += openHandler;
menu.Closed += closeHandler;
}
Related
I made a code in WinForm. It works.
I dragged xml file and dropped to tabPage.
private TextEditorControl AddNewTextEditor(string title)
{
var tab = new TabPage(title);
var editor = new TextEditorControl();
editor.Dock = System.Windows.Forms.DockStyle.Fill;
editor.IsReadOnly = false;
editor.Document.DocumentChanged +=
new DocumentEventHandler((sender, e) => { SetModifiedFlag(editor, true); });
// When a tab page gets the focus, move the focus to the editor control
// instead when it gets the Enter (focus) event. I use BeginInvoke
// because changing the focus directly in the Enter handler doesn't
// work.
tab.Enter +=
new EventHandler((sender, e) => {
var page = ((TabPage)sender);
page.BeginInvoke(new Action<TabPage>(p => p.Controls[0].Focus()), page);
});
tab.Controls.Add(editor);
fileTabs.Controls.Add(tab);
if (_editorSettings == null) {
_editorSettings = editor.TextEditorProperties;
OnSettingsChanged();
} else
editor.TextEditorProperties = _editorSettings;
return editor;
}
But WPF is a bit dirrenet.
Can I change the code for WPF?? or other way..? Thanks for your help.
You can do it this way:
Suppose you have this TabControl with TabItem A and B and a Button for adding TabItem
<StackPanel>
<TabControl x:Name="TabControl">
<TabItem Header="A"/>
<TabItem Header="B"/>
</TabControl>
<Button Content="Add New Tab Item" Click="ButtonBase_OnClick"/>
</StackPanel>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var tabItem = new TabItem { Header = "C" };
TabControl.Items.Add(tabItem);
}
After clicking the button you will be added another TabItem (C)
In WPF I have Window with some buttons. Currently I have this functionality: I click a button and the Popup pops underneath the button.
My goal is when dragging main Window the Popup to move with the Window so Popup remains under the button.
Currently I'm using this code, but it seems it's not working - Popup pops, but when dragging main Window the Popup stays in the same place.
Here is my code:
private void Window_LocationChanged(object sender, EventArgs e)
{
ResetPopUp();
}
private void ResetPopUp()
{
var offsetHor = popup.HorizontalOffset;
var offsetVer = popup.VerticalOffset;
popup.HorizontalOffset = offsetHor + 1;
popup.HorizontalOffset = offsetHor - 1;
popup.VerticalOffset = offsetVer + 1;
popup.VerticalOffset = offsetVer - 1;
}
I will be thankfully for any answers!
Here is what we use in our code. Maybe it can help.
public MainWindow()
{
this.InitializeComponent();
this.SizeChanged += delegate { this.ResetPopupLocation(); };
this.LocationChanged += delegate { this.ResetPopupLocation(); };
}
private static IEnumerable<Popup> GetOpenPopups()
{
return PresentationSource.CurrentSources.OfType<HwndSource>()
.Select(h => h.RootVisual)
.OfType<FrameworkElement>()
.Select(f => f.Parent)
.OfType<Popup>()
.Where(p => p.IsOpen);
}
private void ResetPopupLocation()
{
foreach (var popup in GetOpenPopups())
{
var offset = popup.HorizontalOffset;
popup.HorizontalOffset = offset + 1;
popup.HorizontalOffset = offset;
}
}
you can combine PlacementTarget and Placement properties of popup to achieve it's positioning relative/centered to it's parent
something like below if you want the popup position at center of parent
<Popup
PlacementTarget="{Binding ElementName = "parent_container_windowname"}"
Placement="Center">
</Popup>
if you want the placement of popup at top left corner of parent then you could use
Placement="Relative"
I have a StackPanel with children. The StackPanel children are also StackPanel. Children StackPanel is added by dynamically at runtime. I have a context menu with delete header. When I click the delete menu the selected stack children will be deleted. I don't have any idea to remove the StackPanel children by using the context menu. Please, anyone, guide me to resolve this. My sample code is as below,
<StackPanel x:Name="mainPanel" Background="#F0F0F0">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Click="ParentContextMenu_Click" Header="Add Stackpanel" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
Code behind
public partial class MainView : Window
{
ContextMenu contextMenu;
MenuItem menuItem;
public MainView()
{
InitializeComponent();
contextMenu = new ContextMenu();
menuItem = new MenuItem();
menuItem.Header = "Delete Panel";
menuItem.Click += ChildContextMenu_Click;
contextMenu.Items.Add(menuItem);
}
private void ChildContextMenu_Click(object sender, RoutedEventArgs e)
{
}
private void ParentContextMenu_Click(object sender, RoutedEventArgs e)
{
StackPanel stack = new StackPanel()
{
Name = "childStack"
Height = 100,
Width = 100,
Background = Brushes.White,
Margin = new Thickness(15, 15, 0, 10),
ContextMenu = contextMenu
};
mainPanel.Children.Add(stack);
}
}
I have tried like this also, but is not deleted.
mainPanel.Children.Remove((StackPanel)this.FindName("childStack"));
This should work:
private void ChildContextMenu_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = sender as MenuItem;
if (mi != null)
{
ContextMenu cm = mi.Parent as ContextMenu;
if (cm != null)
{
StackPanel sp = cm.PlacementTarget as StackPanel;
if (sp != null)
{
Panel parentSp = sp.Parent as Panel;
if (parentSp != null)
parentSp.Children.Remove(sp);
}
}
}
}
I am using MahApps and MVVM Light. And I want to make DropDownButton opens on mouse enter. And hide it when mouse cursor leaves button and opened menu. For code simplification, I don't write code with EventToCommand. I just write code behind
XAML
<controls:DropDownButton x:Name="ddbVolume" Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}"
ItemsSource="{Binding AudioControls}"
Icon="{DynamicResource appbar_settings}" BorderThickness="0"
ArrowVisibility="Collapsed"
Loaded="OnDropDownButtonLoaded" MouseEnter="OnDropDownButtonMouseEnter">
</controls:DropDownButton>
and .cs
private void OnDropDownButtonMouseEnter(object sender, MouseEventArgs e)
{
var dropDownButton = sender as DropDownButton;
if (dropDownButton != null && !dropDownButton.IsExpanded)
{
dropDownButton.IsExpanded = true;
}
}
private void OnDropDownButtonLoaded(object sender, RoutedEventArgs e)
{
var dropDownButton = sender as DropDownButton;
if (dropDownButton != null)
{
var template = dropDownButton.Template;
var menu = (ContextMenu)template.FindName("PART_Menu", dropDownButton);
menu.MouseLeave += (o, args) =>
{
if (dropDownButton.IsExpanded && !dropDownButton.IsMouseOver && !menu.IsMouseOver)
{
dropDownButton.IsExpanded = false;
}
};
menu.PreviewMouseMove += (o, args) =>
{
if (!dropDownButton.IsExpanded)
{
return;
}
var x = args.GetPosition(menu).X;
var y = args.GetPosition(menu).Y;
if (x < 0 | y < 0 | x > menu.ActualWidth | y > menu.ActualHeight)
{
menu.ReleaseMouseCapture();
}
};
}
else
{
this._logger.Debug($"Error loading DropDownButton");
}
But it does not work. The DropDownButton is only flicker on mouse over. Please, give me a proper solution, or any usefull advice to solve this problem.
If the menu is appearing at all then your opening logic is good, but then it disappears, meaning that your own code is somehow closing it.
Stick a breakpoint on the line where you set dropDownButton.IsExpanded = false, and you'll see that's it's being called I'm sure. You can then use the debugger to see why it's been invoked and fix the problem in your xaml that's causing the system to think that your mouse has left the menu.
Maybe, you should subscribe the MouseLeave Event. And you could fix your Actions.
I have made a solution. And it works as i expect. The root of the problem was, that DropDownButton uses ContextMenu to show list items. And this control is based on Popup, which uses his own window. And MouseLeave fired not at time, when mouse coursor was not over it, but when it's lost focus.
XAML
<controls:DropDownButton x:Name="ddbVolume" Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}"
ItemsSource="{Binding AudioControls}"
Icon="{DynamicResource appbar_settings}" BorderThickness="0"
ArrowVisibility="Collapsed">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<command:EventToCommand Command="{Binding Source={x:Static commands:CommonCommands.DropDownButtonLoadedCommand}}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseEnter">
<command:EventToCommand Command="{Binding Source={x:Static commands:CommonCommands.DropDownButtonMouseEnterCommand}}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</controls:DropDownButton>
And a ViewModel code (I know it's not a VM, but it works the same way)
In static class I define commands that can be used anywhere in my application.
public static class CommonCommands
{
private static ICommand dropDownButtonLoadedCommand;
private static ICommand dropDownButtonMouseEnterCommand;
public static ICommand DropDownButtonLoadedCommand => dropDownButtonLoadedCommand;
public static ICommand DropDownButtonMouseEnterCommand => dropDownButtonMouseEnterCommand;
static CommonCommands()
{
dropDownButtonLoadedCommand = new RelayCommand<RoutedEventArgs>(DropDownButtonLoaded, x => true);
dropDownButtonMouseEnterCommand = new RelayCommand<MouseEventArgs>(DropDownButtonMouseEnter, x => true);
}
private static void DropDownButtonLoaded(RoutedEventArgs args)
{
var dropDownButton = args.Source as DropDownButton;
if (dropDownButton != null)
{
var template = dropDownButton.Template;
var menu = (ContextMenu)template.FindName("PART_Menu", dropDownButton);
var button = (Button)template.FindName("PART_Button", dropDownButton);
menu.MouseLeave += (o, e) =>
{
if (dropDownButton.IsExpanded && !dropDownButton.IsMouseOver && !menu.IsMouseOver)
{
dropDownButton.IsExpanded = false;
}
};
menu.PreviewMouseMove += (o, e) =>
{
if (!dropDownButton.IsExpanded || !menu.IsOpen)
{
return;
}
var x = e.GetPosition(menu).X;
var y = e.GetPosition(menu).Y;
if (x < 0 | y < -button.ActualHeight | x > menu.ActualWidth | y > menu.ActualHeight)
{
menu.ReleaseMouseCapture();
}
};
}
}
private static void DropDownButtonMouseEnter(MouseEventArgs args)
{
var dropDownButton = args.Source as DropDownButton;
if (dropDownButton != null && !dropDownButton.IsExpanded)
{
dropDownButton.IsExpanded = true;
}
}
}
I know there are some little defects. For example, "expression y < -button.ActualHeight" is not good at all. the proper way is to use button.IsMouseOver in MouseLeave event.
I'm observing that when the Popup control loses focus it returns focus back to the object that initiated the Popup. So basically this sequence of events.
Textbox GotFocus fires and I open the popup
When finished with the Popup user clicks anywhere outside Popup to activate LostFocus
LostFocus activates, but immediately after GotFocus for the object in step 1 (Textbox) executes - relaunching the popup.
My question, is there a way to reset focus to whatever the default is when you first start an app? I want to prevent it from returning focus back to the control that launched the popup.
EDIT: I removed LostFocus event because I found it wasn't necessary in order to remove the popup from view when user taps away.
Popup p;
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
p = new Popup();
p.IsLightDismissEnabled = true;
//p.LostFocus += p_LostFocus;
}
private void m_GotFocus(object sender, RoutedEventArgs e)
{
if (p.IsOpen != true)
{
// Create some content to show in the popup. Typically you would
// create a user control.
Border border = new Border();
border.BorderBrush = new SolidColorBrush(Colors.Black);
border.BorderThickness = new Thickness(0);
StackPanel panel1 = new StackPanel();
panel1.FlowDirection = Windows.UI.Xaml.FlowDirection.LeftToRight;
panel1.Background = new SolidColorBrush(Color.FromArgb(255, 30, 30, 30));
Button button1 = new Button();
button1.Content = ".";
button1.Margin = new Thickness(5.0);
button1.BorderThickness = new Thickness(0);
button1.Background = new SolidColorBrush(Color.FromArgb(255, 90, 90, 90));
button1.Click += new RoutedEventHandler(Feedback_Click);
TextBlock textblock1 = new TextBlock();
textblock1.Text = "";
textblock1.TextWrapping = TextWrapping.WrapWholeWords;
textblock1.Margin = new Thickness(5.0);
panel1.Children.Add(textblock1);
panel1.Children.Add(button1);
border.Child = panel1;
// Set the Child property of Popup to the border
// which contains a stackpanel, textblock and button.
p.Child = border;
// Set where the popup will show up on the screen.
p.VerticalOffset = 400;
p.HorizontalOffset = 100;
// Open the popup.
p.IsOpen = true;
}
}