How to get Button as Parent on MenuItemFlyoutItem_Click? - c#

I'm building a WinRT Universal app and I have a button and a MenuFlyout attached to it - I'm trying to get the Name and Tag of the button.
XAML:
<MenuFlyout x:Key="FlyOutResource">
<MenuFlyoutItem Text="pin to start" Click="PinToStart_Click"/>
</MenuFlyout>
<Button x:Name="ButtonName" Tag="BUTTON TAG" FlyoutBase.AttachedFlyout="{StaticResource FlyOutResource}" Holding="Button_Holding"/>
C#:
private void Button_Holding(object sender, Windows.UI.Xaml.Input.HoldingRoutedEventArgs e)
{
FlyoutBase.ShowAttachedFlyout(sender as FrameworkElement);
}
private void PinToStart_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
var menuFlyoutItem = sender as MenuFlyoutItem;
if (menuFlyoutItem != null)
{
????
}
}
How do I get the name of the button (of which the FlyOut is attached to)? DataContext doesn't work.
Kind regards,
Niels

I am in UWP but am hitting the same issue and do not see a way around it from within the PinToStart_Click Handler.
In your solution, you at least see which button opened the flyout within the Button_Holding handler. I'd recommend storing a reference to the sender in the Button_Holding handler and then access that reference within the PinToStart click handler.

var tempParent = Windows.UI.Xaml.Media.VisualTreeHelper.GetParent(child as FrameworkElement);
I hope this will work for you.

For future reference, I leave a solution
If you name your MenuFlyout:
<Button.Flyout>
<MenuFlyout x:Name="MenuFlyoutContainer">
<MenuFlyoutItem Tapped="OnMenuFlyoutItem"/>
</MenuFlyout>
</Button.Flyout>
Then on the tapped event you can search for it:
private void OnDeletePressed(object sender, TappedRoutedEventArgs e)
{
var item = (sender as MenuFlyoutItem);
var itemDataContext = item.DataContext;
FrameworkElement parent = (item.FindName("MenuFlyoutContainer") as MenuFlyout);
}
And voila you have your MenuFlyout instance.
Tested inside DataTemplates on UWP.

Related

How to hide menu item of AttachedFlyout menu?

I am able to display a right clicked menu using the below code programmatically, showing/hiding required menu-items is easy, since we can create MenuFlyoutItem as we wish.
private void MyGrid_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
MenuFlyout myFlyout = new MenuFlyout();
MenuFlyoutItem mnu1 = new MenuFlyoutItem { Text = "Rename" };
MenuFlyoutItem mnu2 = new MenuFlyoutItem { Text = "Delete" };
myFlyout.Items.Add(mnu1);
myFlyout.Items.Add(mnu2);
mnu1.Click += mnu1_Click;
mnu2.Click += mnu2_Click;
FrameworkElement senderElement = sender as FrameworkElement;
myFlyout.ShowAt(sender as UIElement, e.GetPosition(sender as UIElement));
}
But, When I create menu using xaml as AttachedFlyout, I am unable to hide a particular menu item. here is my code:
<ListView.ItemTemplate>
<DataTemplate >
..............
<Grid Name="GrdForFolderMenu">
<FlyoutBase.AttachedFlyout >
<MenuFlyout Placement="Right">
<MenuFlyoutItem Name="MenuFolderCreate" Text="New Folder" Click="MenuFolderCreate_Click" DataContext="Folders"/>
<MenuFlyoutItem Name="MenuFolderRename" Text="Rename" Click="MenuFolderRename_Click"/>
<MenuFlyoutItem Name="MenuFolderExport" Text="Export" Click="MenuFolderExport_Click" />
<MenuFlyoutSeparator />
<MenuFlyoutItem Name="MenuFolderDelete" Text="Delete" Click="MenuFolderDelete_Click" />
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
......
........
Here is my right click event:
private void myListView_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
FlyoutBase.ShowAttachedFlyout((FrameworkElement)sender);
}
Struggling for a long to hide particular menu item, in code! Please help!
You just need to get the MenuFlyout and either set the Visibility of the specific MenuFlyoutItem to Collapsed or remove it from the list.
if (FlyoutBase.GetAttachedFlyout((FrameworkElement)sender) is MenuFlyout menuFlyout)
{
// Hide 'MenuFolderExport' MenuFlyoutItem
//menuFlyout.Items[2].Visibility = Visibility.Collapsed;
// Or, remove 'MenuFolderExport' MenuFlyoutItem
menuFlyout.Items.Remove(menuFlyout.Items[2]);
}
FlyoutBase.ShowAttachedFlyout((FrameworkElement)sender);
I'd also attach the RightTapped to your GrdForFolderMenu and replace all Name="xxx" with x:Name="xxx".

Flyout causing the app to crash [Windows 10] C# XAML

I want the program to show the attached Flyout when user Holding the control (on the mobile) or when the user Right-click the control (on PC).
Here is my XAML :
<DataTemplate x:DataType="data:Cards" x:Key="card">
<StackPanel x:Name="cardstack" Holding="cardstack_Holding" KeyDown="cardstack_KeyDown" >
<StackPanel Background="Blue" Height="100" />
<FlyoutBase.AttachedFlyout>
<MenuFlyout x:Name="optionpass">
<MenuFlyoutItem x:Name="delete" Text="Delete" Click="delete_Click"/>
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
</StackPanel>
</DataTemplate>
and this is my C# :
private void cardstack_Holding(object sender, HoldingRoutedEventArgs e)
{
FlyoutBase.ShowAttachedFlyout(sender as FrameworkElement);
}
private void cardstack_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.RightButton)
{
FlyoutBase.ShowAttachedFlyout(sender as FrameworkElement);
}
}
When I tap and Hold the Stackpanel on the mobile simulator, the Holding event works, but when I Right-click on my PC, it crashes! It says that "There are no attached Flyout!". I do not know what is wrong.
"Have you tried RightTapped event? Is it working?"
Yes and No :(
I just found out the solution to solve my problem.
Turns out you have to name the MenuFlyout like my one is x:Name = "option_menu", and the Flyoutbase.AttachedFlyout cannot be in the DataTemplate, means you have to put it anywhere else except in the DataTemplate, so that the .cs file can find the name of the MenuFlyout.
Here is my C# :
public void cardstack_Holding(object sender, HoldingRoutedEventArgs e)
{
option_menu.ShowAt(sender as FrameworkElement);
e.Handled = true;
}
private void cardstack_PointerPressed(object sender, PointerRoutedEventArgs e)
{
Pointer pointr = e.Pointer;
if (pointr.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
{
Windows.UI.Input.PointerPoint pointrd = e.GetCurrentPoint(sender as UIElement);
if (pointrd.Properties.IsRightButtonPressed)
{
option_menu.ShowAt(sender as FrameworkElement);
}
}
e.Handled = true;
}
Notice that before this I use ShowAttachedFlyout, now I use option_menu.ShowAt.
KeyDown event somehow did not work with my app, so I used PointerPressed instead.
Hope this helps. (0w0)/

C# / WPF Unmask password inside the passwordBox

How could I unmasked and masked the password inside the passwordBox whenever I click the checkBox? I'm using C# WPF template.
Here is my .XAML code:
<PasswordBox x:Name="passwordBox_password" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Margin="5" Height="25" />
<CheckBox x:Name="checkBox_showPassword" Grid.Row="3" Grid.Column="1" Margin="5,0,5,5" Content="show password" Checked="checkBox_showPassword_Checked" Unchecked="checkBox_showPassword_Unchecked" />
Here is my .CS code:
private void checkBox_showPassword_Checked(object sender, RoutedEventArgs e)
{
// what to do here ?
}
private void checkBox_showPassword_Unchecked(object sender, RoutedEventArgs e)
{
// what to do here ?
}
Or is there another way to do it in WPF?
It's very simple to do that.
First you should to add the value PasswordChar in your PasswordBox:
<PasswordBox Name="PasswordHidden" PasswordChar="•"/>
Next under the PasswordBox tag you should to add a TextBox with Visibility value setted to Hidden:
<TextBox Name="PasswordUnmask" Visibility="Hidden"/>
And a trigger to show / hide the password, for example a simple text or a button. In my case I'm using a simple text.
<TextBlock Name="ShowPassword"/>
Next you need to add 3 different events in the trigger element, for example (this is valid for TextBlock or Image, if you want to use a Button you should to choose another events):
<TextBlock x:Name="ShowPassword" Text="SHOW" PreviewMouseDown="ShowPassword_PreviewMouseDown" PreviewMouseUp="ShowPassword_PreviewMouseUp" MouseLeave="ShowPassword_MouseLeave"/>
The events are PreviewMouseDown PreviewMouseUp and MouseLeave but you can choose the appropriate event for your situation.
Now in your code you need to program the functions:
private void ShowPassword_PreviewMouseDown(object sender, MouseButtonEventArgs e) => ShowPasswordFunction();
private void ShowPassword_PreviewMouseUp(object sender, MouseButtonEventArgs e) => HidePasswordFunction();
private void ShowPassword_MouseLeave(object sender, MouseEventArgs e) => HidePasswordFunction();
private void ShowPasswordFunction()
{
ShowPassword.Text = "HIDE";
PasswordUnmask.Visibility = Visibility.Visible;
PasswordHidden.Visibility = Visibility.Hidden;
PasswordUnmask.Text = PasswordHidden.Password;
}
private void HidePasswordFunction()
{
ShowPassword.Text = "SHOW";
PasswordUnmask.Visibility = Visibility.Hidden;
PasswordHidden.Visibility = Visibility.Visible;
}
The following link will bring you to the answer you are looking for my good sir. Mr Lamas did a great job of answering the how-to so I'd rather redirect you to the answer :)
showing password characters on some event for passwordbox
I recommend Using MahApps.Metro ... after installing it from nuget.org ... you must use it in the head of your xaml like this
xmlns:controls="http://metro.mahapps.com/winf/xaml/controls"
and then ... just use it's style for your PasswordBox control
<PasswordBox Style="{StaticResource MetroButtonRevealedPasswordBox}" />
you can even change the content for the show icon using the controls:PasswordBoxHelper.RevealButtonContent attached property

Event bubbling and RoutedEventArgs Source Property

I am trying to understand the RoutedEventArgs.Source property in a simple WPF application. Here is the XAML Code
<Window x:Class="BubbleDemo.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">
<StackPanel x:Name="stackPanel1" Button.Click="OnOuterButtonClick">
<Button x:Name="button1" Content="Button 1" Margin="5" />
<Button x:Name="button2" Margin="5" Click="OnButton2">
<ListBox x:Name="listBox1">
<Button x:Name="innerButton1" Content="Inner Button 1" Margin="4" Padding="4" Click="OnInner1" />
<Button x:Name="innerButton2" Content="Inner Button 2" Margin="4" Padding="4" Click="OnInner2" />
</ListBox>
</Button>
<ListBox ItemsSource="{Binding}" />
</StackPanel>
</Window>
And here is the code behind
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace BubbleDemo
{
public partial class MainWindow : Window
{
private ObservableCollection<string> messages = new ObservableCollection<string>();
public MainWindow()
{
InitializeComponent();
this.DataContext = messages;
}
private void AddMessage(string message, object sender, RoutedEventArgs e)
{
messages.Add(String.Format("{0}, sender: {1}; source: {2}; original source: {3}",
message, (sender as FrameworkElement).Name,
(e.Source as FrameworkElement).Name,
(e.OriginalSource as FrameworkElement).Name));
}
private void OnOuterButtonClick(object sender, RoutedEventArgs e)
{
AddMessage("outer event", sender, e);
}
private void OnInner1(object sender, RoutedEventArgs e)
{
AddMessage("inner1", sender, e);
}
private void OnInner2(object sender, RoutedEventArgs e)
{
AddMessage("inner2", sender, e);
e.Handled = true;
}
private void OnButton2(object sender, RoutedEventArgs e)
{
AddMessage("button2", sender, e);
e.Source = sender;
}
}
}
When I click on InnerButton1 the click event is raised and then is executed the OnInner1 handler.
After is executed the OnButton2 Handler which sets the RoutedEventArgs.Source property with the sender parameter.
If you build and execute this code, you can see the output results.
When the event arrives on the OnOuterButtonClick handler, the output in the bottom ListBox should be:
inner1, sender: innerButton1; source: innerButton1; original source: innerButton1
button2, sender: button2; source: innerButton1; original source: innerButton1
outer event, sender: stackPanel1; source: button2; original source: innerButton1
but the output is this
inner1, sender: innerButton1; source: innerButton1; original source: innerButton1
button2, sender: button2; source: innerButton1; original source: innerButton1
outer event, sender: stackPanel1; source: innerButton1; original source: innerButton1
The RoutedEventArgs.Source property reassigned in the OnButton2 hander is changed but returns to reference innerButton1 within the OnOuterButtonClick handler.
Why this happens?
Thanks
This is a really good question, and i had to look into the Source of .net to figure out why it is like this:
The Source Property looks like this:
public object Source
{
get {return _source;}
set
{
if (UserInitiated && InvokingHandler)
throw new InvalidOperationException(SR.Get(SRID.RoutedEventCannotChangeWhileRouting));
...
}
}
This execption s thrown, whenever the User tries to SET the source, while the event is Bubbling or tunneling.
I'm assuming, that the part of the .net Framework, taking care for this behavior is also catching the Exception, so you don't get aware of the problem. In fact, when trying to set the Source Property, while the event is bubbling, the Debugger shows, that is not changed right after setting it.
Unfortunately the source code just shows that Microsoft does not allow to change the Source-Property while the Event is bubbling (or tunneling), but not why.
If you - for whatever Reason - need to get information about the Prior handler that processed an event, you could create your own Extension of RoutedEventArgs and add another property, containing this information.
finally you can extend the button class, and raise your own Event, that contains the appropriate RoutedEventArgsWithHandlerHistory Object :)
This is an interesting question and demanded to reflect the .net Routing engine. So what I found is each UIElement uses RaiseEvent() method to initiate the RoutedEvent. While doing so it first builds the EventRoute. While build EventRoute, it creates the list of Invoke handlers depending on the RoutingStrategy i.e for Bubble and Tunnel it goes up and down the VisualTree to which UIElement belongs and finds out how many Handlers are attached to the given RoutedEvent. As apparent, in your case for innerButton1 and innerButton1 there are three handlers.
Now UIElement got the EventRoute for its RoutedEvent, next it calls InvokeHandlers() on EventRoute. While calling the handlers in the loop, InvokeHandler reset the args.Source to the original value it has like below where it is doing it for the Bubble strategy.
for (int index = 0; index < this._routeItemList.Count; ++index)
{
if (index >= endIndex)
{
object bubbleSource = this.GetBubbleSource(index, out endIndex);
if (!reRaised)
args.Source = bubbleSource ?? source;
}
Hence before each handler call, the Source is reset to its original value therefore changing it inside any handler will not be passed to the next handler.

How to Programmatically change a Tab Control's Index & State (Perhaps using commands?)

I have a WPF TabControl which contains a number of TabItems with child UserControls, like this.
XAML:
<TabControl x:Name="tabsMain" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Grid.Row="0" Grid.RowSpan="3" Background="lightgray">
<TabItem Width="100" Header="Profile" FontSize="16">
<InfoControl:InfoTab x:Name="myInfo" />
</TabItem>
<TabItem Width="120" x:Name="Summary" Header="Summary" FontSize="16">
<SummaryControl:SummaryTab/>
</TabItem>
</TabControl>
Within one of the UserControls, lets say, InfoTab, I have a Button. When this Button is clicked I would like to change the index of the TabControl to the SummaryTab and select a radio button on the SummaryTab page.
My problem is that the InfoTab user control does not have access to the MainUserControl which contains the TabControl shown above. I figured out a kludge which changes the SelectedIndex of the TabControl, but this is a very ugly solution and I'd prefer to do something more clean. Also I cannot currently change the RadioButton on my SummaryTab.
My Current C# hack:
Private void btnSummaryDetails_Click(object sender, RoutedEventArgs e)
{
TabControl tabControl = UIHelper.FindChild<TabControl>(Application.Current.MainWindow, "tabsMain");
tabControl.SelectedIndex = 7;
}
Is it possible to use commands or dependency properties to select the SummaryTab and my desired RadioButton? I'm still new to the WPF world, and would love to learn more about this. Thanks in advance.
See my post here for the UIHelper definition I use in the C# above.
One thought comes to mind that will not require too many changes.
First, add an event to your InfoTab class:
public event EventHandler SummaryButtonClicked;
Then handle that in your main form by replacing the control declaration with:
<InfoControl:InfoTab x:Name="myInfo" SummaryButtonClicked="summaryButtonClicked" />
And give a name to your SummaryTab:
<SummaryControl:SummaryTab x:Name="summaryTab" />
Then add the event handler in your main form:
void MainWindow_SummaryButtonClicked(object sender, EventArgs e)
{
this.summaryTab.SelectRadioButton();
}
And add a method in your SummaryTab class to select your radio button.
public void SelectRadioButton()
{
// TODO: something like
myRadioButton.IsChecked = true;
}
You could probably use WPF routed events to solve your problem. Routed events use the WPF visual tree to send events up to parent controls (bubbling) or down to child controls (tunneling) without excessive coupling. I've tried to give a simple example below because I know that routed events can be a bit hairy to learn at first but it's well worth it...
In your main window, define a routed event and add a handler method:
public partial class MainWindow : Window {
public static RoutedEvent ClickedEvent = EventManager.RegisterRoutedEvent(
"Clicked",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(MainWindow));
public MainWindow() {
InitializeComponent();
this.AddHandler(MainWindow.ClickedEvent, new RoutedEventHandler(OnClickedEvent));
}
public void OnClickedEvent(object sender, RoutedEventArgs e) {
// do your work here
}
}
In your button click handler, raise the event:
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e) {
// raise the event (gets bubbled up to the parent of the control)
this.RaiseEvent(new RoutedEventArgs(MainWindow.ClickedEvent));
}
}
The next step would be to tunnel another event down the visual tree and let the other usercontrol listen for it.
I ended up adding a public method as Jeremy suggested in his post. A simple but effective solution. Thanks Jeremy!
Another key realization was that in order to switch the tabcontrol by index, I can get a reference to the main user control and set the SelectedItem to the TabItem itself, like this:
Private void btnSummaryDetails_Click(object sender, RoutedEventArgs e)
{
//HACK replace this with a command that toggles to a different tab instead of this tab reference
MainUserControl mainUserControl = UIHelper.FindChild<MainUserControl>(Application.Current.MainWindow, "root");
mainUserControl.tabsMain.SelectedItem = mainUserControl.Summary;
mainUserControl.SummaryUserControl.SelectRadioButton();
}
Then as suggested by Jeremy, my solution was something like:
public void SelectRadioButton()
{
// TODO: something like
myRadioButton.IsChecked = true;
}
My XAML structure was like
// my main user control:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
x:Name="root"
>
<TabControl x:Name="tabsMain" ...>
<TabItem x:Name="Summary" ... />
</TabControl>
</UserControl>
I think Andrew Jackson's comments are absolutely valid - long term, I plan to investigate using routed command or routed events to traverse the visual tree, but for now I'm sticking with this "quick and dirty" solution as we're not shipping this product. Based on my investigation Routed Events might be a little overkill for this situation.

Categories

Resources