Mouse Events on WPF Page - On sub controls only? - c#

I am working on a C# WPF Project. The MainWindows holds only a Frame Control that is used to navigate to different Pages.
On one Page I use a Button to show a PopUp. Now I would like to hide this Popup when the Mouse leaves it and moves above the Page.
<Page x:Class="Name.SpaceMainPage"
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"
Title="MainPage"
MouseMove="Page_MouseMove">
<StackPanel>
<Label Content="Some Label"/>
<Label Content="Other Label"/>
<Button Name="PopupButton" Content="Popup" Click="MenuButtonClick" />
<Label Content="Third Label"/>
<Popup Name="ExtrasPopup" PlacementTarget="{Binding ElementName=PopupButton}" Placement="Top" PopupAnimation="Scroll">
<StackPanel Background="LightGray">
...
</StackPanel>
</Popup>
</Grid>
</Page>
public partial class MainPage : Page {
...
private void MenuButtonClick(object sender, RoutedEventArgs e) {
ExtrasPopup.IsOpen = true;
}
private void Page_MouseMove(object sender, MouseEventArgs e) {
System.Diagnostics.Debug.WriteLine("Move: " + e.MouseDevice.GetPosition(this));
}
}
The Problem: The Page_MouseMove is only executed when the mouse moves above of of the child controls (the labels or the button). If the mouse moves above the empty space of the page, the event does not fire.
The same is true for any other mouse event such as MouseDown, MouseEnter, MouseLeave, etc... This is quite strange behaviour. Is this intended or am I making any mistake?

It would be a better idea to register to the MouseLeave event on the Button or whatever control you want to use.
<Button Name="PopupButton" Content="Popup" Click="MenuButtonClick" MouseLeave="Menu_MouseLeave" />
And in the code behind simply hide the popup when mouse leaves the button:
private void Menu_MouseLeave(object sender, MouseEventArgs e)
{
ExtrasPopup.IsOpen = false;
}

Related

Wpf PasswordBox must show characters when I click CheckBox

xaml
<Window x:Class="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 VerticalAlignment="Center" Width="300">
<PasswordBox x:Name="PasswordBox1" Height="30" PasswordChar="*" Password="12345"/>
<CheckBox x:Name="CheckBox1" Content="Show Password"/>
</StackPanel>
</Window>
vb.net
Class MainWindow
Private Sub CheckBox1_Checked(sender As Object, e As RoutedEventArgs) Handles CheckBox1.Checked
PasswordBox1.PasswordChar = CChar("")
End Sub
Private Sub CheckBox1_Unchecked(sender As Object, e As RoutedEventArgs) Handles CheckBox1.Unchecked
PasswordBox1.PasswordChar = CChar("*")
End Sub
End Class
Run the above codes and click CheckBox1 in order to understand what is happening.
How can PasswordBox show characters which are 12345 when I click CheckBox?
So, following line need to be repaired.
PasswordBox1.PasswordChar = CChar(" ")
This will work for what you are looking for although it will expose your passwords in memory. We have a textbox and a password box in the same place on our UI and when the user checks the Show Password checkbox we collapse the passwordbox and show the hidden textbox, at the same time updating the text. You will need to check you are using the password from the visible ui control when you send the password.
Xaml code:
<StackPanel Orientation="Horizontal">
<Grid Width="300" Height="40">
<PasswordBox Name="passwordBox" PasswordChar="*" />
<TextBox Name="passwordTxtBox" Visibility="Collapsed" />
</Grid>
<CheckBox Margin="10" Name="showPassword" Unchecked="ShowPassword_Unchecked" Checked="ShowPassword_Checked" />
</StackPanel>
Code behind:
private void ShowPassword_Checked(object sender, RoutedEventArgs e)
{
passwordTxtBox.Text = passwordBox.Password;
passwordBox.Visibility = Visibility.Collapsed;
passwordTxtBox.Visibility = Visibility.Visible;
}
private void ShowPassword_Unchecked(object sender, RoutedEventArgs e)
{
passwordBox.Password = passwordTxtBox.Text;
passwordTxtBox.Visibility = Visibility.Collapsed;
passwordBox.Visibility = Visibility.Visible;
}
I recommend Using MahApps.Metro ... after installing it from nuget.org ... you must import 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

Several problems with Popup (does not hide)

I made a small test-application to illustrate the behavior I do not understand: 2 buttons, if the mouse enters the left button a popup is shown (works), if the mouse enters the right button the popup should dissapear instantly (does not work). In my real application I have no buttons, only in this example for simple test, so the mouse movement is important and button-clicks cannot be used.
What I see is the following behavior:
If the popup is shown, the right button mouse-enter (hide) does not react at all, it seems that the popup has gotten focus, I have to click in the main window before the right mouse-enter becomes enabled. Setting Popup.Focusable=false (XAML) or trying to give focus to the grid (C# code) just after setting Popup.Isopen=true does not help.
Once I try to hide the popup by setting Popup.IsOpen=false, the popup stays visible and does not disappear. Only when I click the main window topbar, or when I hover the minimize button for example which shows a tooltip, the popup suddenly disappears. Calling UpdateLayout() on several UI elements does not help.
I'd like the following:
Once the popup is shown, the hide button should still react on mouse-enter.
Setting Popup.IsOpen=false should directly let the popup disappear.
See code below, XAML:
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
x:Name="MyWindow"
Title="MainWindow" Height="500" Width="800">
<Grid>
<Grid x:Name="MyGrid" Margin="100" Background="Green">
<TextBox x:Name="MyText" Width="100" Height="50" Margin="0,50" HorizontalAlignment="Center"></TextBox>
<Button Width="75" Height="50" Margin="20,100" HorizontalAlignment="Left"
Content="Show" MouseEnter="MouseEnterShow"></Button>
<Button Width="75" Height="50" Margin="20,100" HorizontalAlignment="Right"
Content="Hide" MouseEnter="MouseEnterHide"></Button>
<Popup x:Name="MyPopup" PlacementTarget="{Binding ElementName=MyGrid}" Placement="RelativePoint"
AllowsTransparency="false" IsOpen="False" StaysOpen="False"
Focusable="False"
Width="200" Height="200"
HorizontalAlignment="Left" VerticalAlignment="Top"
HorizontalOffset="-50" VerticalOffset="-50">
<Grid>
<Canvas Background="BlanchedAlmond" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></Canvas>
</Grid>
</Popup>
</Grid>
</Grid>
</Window>
And C#:
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MouseEnterShow(object sender, MouseEventArgs e)
{
MyPopup.IsOpen = true;
MyGrid.Focus();// Does not help
MyText.Text = "Show";
System.Diagnostics.Debug.WriteLine("Show");
}
private void MouseEnterHide(object sender, MouseEventArgs e)
{
MyPopup.IsOpen = false;
MyGrid.UpdateLayout();// Does not help
MyText.Text = "Hide";
System.Diagnostics.Debug.WriteLine("Hide");
}
}
}
After some brute forcing, I've managed to make it work:
1.Set the Popup property StaysOpen to true, instead of false
2.Code behind
private void MouseEnterShow(object sender, MouseEventArgs e)
{
MyPopup.IsOpen = true;
}
private void MouseEnterHide(object sender, MouseEventArgs e)
{
MyPopup.IsOpen = false;
}

Button inside button (WPF + Caliburn.Micro)

In regular WPF you can have a button inside another button, and to prevent both button events being raised when you click the inner button, you can have the following XAML (As per this question):
<Button Click="OuterClick">
<Grid>
<SomeOtherContent />
<Button Click="InnerClick" />
</Grid>
</Button>
And then in the code-behind use the Handled property on the event so that OuterClick is not raised when the inner button has been clicked:
private void OuterClick(object sender, RoutedEventArgs e) {
// Do something
}
private void InnerClick(object sender, RoutedEventArgs e) {
// Do something else
e.Handled = true;
}
However, following the usual Caliburn.Micro conventions you'll have the following XAML:
<Button x:Name="OuterClick">
<Grid>
<SomeOtherContent />
<Button x:Name="InnerClick" />
</Grid>
</Button>
With this in the ViewModel:
private void OuterClick() {
// Do something
}
private void InnerClick() {
// Do something else
}
How can I make sure that not both InnerClick() and OuterClick() are raised when I click the InnerButton?
EDIT: Or, what other kind of controls can I use instead?
<StackPanel cal:Message.Attach="[Event MouseDown]=[Action OuterClick()]">
<StackPanel Orientation="Horizontal">
<SomeotherContent />
<Button x:Name="InnerClick" />
</StackPanel>
</StackPanel>
In Your viewmodel
public void OuterClick()
{
//Do something
}
public void InnerClick()
{
//Do something
}
use stackpanel and apply event for that one.
This will help you.

creating multiple panels and displaying one panel on button click in wpf

I am new to WPF and I want to create a WPF application with 5buttons. On the click of each button I want a content to be displayed on another panel. Right now I just want different images to be displayed on my right side panel on the button clicks.
Here's my XAML code:
<Window x:Class="GridButton.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyFirstApp" Height="350" Width="525" Loaded="Window_Loaded">
<Viewbox Stretch="Fill" StretchDirection="Both">
<DockPanel>
<StackPanel DockPanel.Dock="left" Margin="5" Width="Auto" VerticalAlignment="Center" Height="Auto">
<Button Content="1" Name="button2" Click="button2_Click">
</Button>
<Button Content="2" Name="button1" Click="button1_Click_1">
</Button>
<Button Content="3" Name="button3" Click="button3_Click">
</Button>
<Button Content="4" Name="button4" Margin="5">
</Button>
<Button Content="5" Name="button5" Margin="5" Click="button5_Click_1">
</Button>
</StackPanel>
<StackPanel DockPanel.Dock="Right">
<Image Name="img1" Source="Blue Hills.jpg" Stretch="Uniform" Visibility="Hidden" ImageFailed="Image_ImageFailed" Height="257" />
</StackPanel>
</DockPanel>
And my xaml.cs file contains code to display image:
private void button2_Click(object sender, RoutedEventArgs e)
{
img1.Visibility = Visibility.Visible;
}
I could get only this far.
You can set the Source property of the Image control in code:
private void buttonx_Click(object sender, RoutedEventArgs e)
{
string path = ... // path to image file here
img1.Source = new BitmapImage(new Uri(path));
}
You could easily reuse the same Click handler for all Buttons and check which one was pressed:
private void Button_Click(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
string path = null;
if (button == button1)
{
path = ... // path to image file 1 here
}
else if ...
if (path != null)
{
img1.Source = new BitmapImage(new Uri(path));
}
}
If you want to remove the a child Panel (or other control) from a parent Panel and add another one, you would have to modify the Panel's Children property:
<StackPanel Name="parent">
<StackPanel Name="child" />
</StackPanel>
parent.Children.Remove(child);
parent.Children.Add(...); // some other control here
This approach would usually make sense if you wanted to create child panels dynamically. If you want to declare everything in XAML you may put all child panels in a Grid and change their visibility as you did already.
However, you might also change the ZIndex attached property.
<Grid>
<StackPanel Name="child1">
</StackPanel>
<StackPanel Name="child2">
</StackPanel>
<StackPanel Name="child3">
</StackPanel>
</Grid>
child3 is topmost by default, but now you can set ZIndex to some value > 0 to make another child topmost:
private void Button_Click(object sender, RoutedEventArgs e)
{
...
// reset ZIndex on previous topmost panel to 0 before
Panel.SetZIndex(child1, 1);
}
Or completely omit the Button/Grid/Panel design and use a TabControl.

Letting users customize content property of user control at run-time

This is the required behaviour:
I have various controls present on the canvas e.g. Callouts (from Expression Blend .dll), or simple Labels. When the user 'double clicks' (or any other event I decide to tie in) the control should change its appearance to allow the user to edit the control's Content property. Clicking off the control should then turn it back to 'read-only' method.
Any suggestions on how this would be best achieved? Ideally I want to do this all in c# to add this behaviour to the control at runtime (as this control is added dynamically to the canvas)- and avoid XAML altogether.
I reckon I've got to do something with adorners to display a textbox bound to the control's content property on the required event, but some code samples or links elsewhere would be appreciated? :) - I haven't been able to find anything on an existing search, but I reckon it should be fairly simple.
Unfortunately, style triggers do not do anything with IsReadOnly and IsEnabled. You would have to do that from an event.
Here is my sample:
WPF:
<Window x:Class="StateChangingTextbox.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">
<Window.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#eee" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox Width="300" Height="200" TextWrapping="Wrap" IsReadOnly="True"
MouseEnter="TextBox_MouseEnter"
MouseLeave="TextBox_MouseLeave"/>
</Grid>
</Window>
Code-behind:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextBox_MouseEnter(object sender, MouseEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null)
{
textbox.IsReadOnly = false;
}
}
private void TextBox_MouseLeave(object sender, MouseEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null)
{
textbox.IsReadOnly = true;
}
}
}
XAML:
<UserControl
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:ed="http://schemas.microsoft.com/expression/2010/drawing"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
mc:Ignorable="d"
x:Class="ComicWPF.Bubble"
x:Name="UserControl" Height="100" Width="200">
<Canvas LostFocus="this_LostFocus">
<ed:Callout x:Name="callout" Content=""
AnchorPoint="0,1" FontSize="14" Height="100" Width="200"
Fill="Blue"
PreviewMouseDoubleClick="Callout_DoubleClick"
Canvas.Left="0" Canvas.Top="0" />
<TextBox x:Name="textbox"
FontSize="14"
Canvas.Left="30" Height="55" Width="80" Canvas.Top="30"
Visibility="Visible"/>
</Canvas>
</UserControl>
C# Code:
private void Callout_DoubleClick(object sender, MouseButtonEventArgs e)
{
Activate();
}
public void Activate()
{
//set bool activated to true
//make textbox visible and set focus and select all text
}
private void Callout_DeSelect()
{
//set content of callout to the textbox.Text
//Hide textbox
//set bool activated to false
}
private void this_LostFocus(object sender, RoutedEventArgs e)
{
Callout_DeSelect();
}
}

Categories