I have a UserControl in WPF. I also have a Borderless window. To move it- I use DragMove.
But- to get a click event in the user control- I use the PreviewMouseLeftButtonUp event and capture the mouse on UserControl_MouseEnter.
The problem is- that if I click the control, then move the window- the event can be triggered also when clicking near the control, not on it.
Here is my code:
UserControl1.xaml:
<UserControl x:Class="WpfApplication1.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" MouseEnter="UserControl_MouseEnter" MouseLeave="UserControl_MouseLeave">
<Grid>
</Grid>
</UserControl>
UserControl1.xaml.cs:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void UserControl_MouseEnter(object sender, MouseEventArgs e)
{
CaptureMouse();
}
private void UserControl_MouseLeave(object sender, MouseEventArgs e)
{
ReleaseMouseCapture();
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication1.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" xmlns:my="clr-namespace:WpfApplication1" WindowStyle="None" MouseLeftButtonDown="Window_MouseLeftButtonDown">
<Grid>
<my:UserControl1 Margin="39,29,380,199" Background="Red" PreviewMouseLeftButtonUp="UserControl1_PreviewMouseLeftButtonUp">
</my:UserControl1>
</Grid>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
private void UserControl1_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Hello World");
}
}
If you run this app- you'll see that if you click on the control, then drag the window, then click near the control (the side may vary)- it will trigger the PreviewMouseLeftButtonUp event even though you didn't click on the control itself.
Any ideas how to solve this?
Thanks!
I asked around, and found that to solve the problem, I need to change the User control's events:
Instead of MouseLeave- I use MouseMove.
private void UserControl_MouseEnter(object sender, MouseEventArgs e)
{
if (this.IsEnabled)
CaptureMouse();
}
private void UserControl_MouseMove(object sender, MouseEventArgs e)
{
Point mouseposition = e.GetPosition(this);
if (mouseposition.X < 0 || mouseposition.Y < 0 || mouseposition.X > this.ActualWidth || mouseposition.Y > this.ActualHeight)
ReleaseMouseCapture();
}
Related
I'm trying to get my head around RoutedCommands in WPF. I like how they decrease coupling between different UI elements and the models but I can't seem to make the bindings work for custom controls that are children to the window. I guess this will be some easy creds for any of you WPF wizards out there! :-)
Here's some example code that can be tried out:
The routed command:
using System.Windows.Input;
namespace Wpf.RoutedCommands
{
public static class Commands
{
public static readonly RoutedCommand SayHi = new RoutedCommand();
}
}
Main window XAML:
<Window x:Class="Wpf.RoutedCommands.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:Wpf.RoutedCommands"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<!-- uncommenting the below makes it work but introduces coupling -->
<!--<Window.CommandBindings>
<CommandBinding Command="{x:Static local:Commands.SayHi}" Executed="cmdSayHi" CanExecute="cmdCanSayHi"></CommandBinding>
</Window.CommandBindings>-->
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Bottom" Content="Say Hi!" Command="{x:Static local:Commands.SayHi}" />
<local:Greeter x:Name="Greeter" />
</DockPanel>
Main window code:
using System.Windows.Input;
namespace Wpf.RoutedCommands
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
// these two will be used if you uncomment the command bindings in XAML
private void cmdCanSayHi(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;
private void cmdSayHi(object sender, ExecutedRoutedEventArgs e) => Greeter.SayHi();
}
}
Custom control ("Greeter") XAML:
<UserControl x:Class="Wpf.RoutedCommands.Greeter"
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"
xmlns:local="clr-namespace:Wpf.RoutedCommands"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.CommandBindings>
<CommandBinding Command="{x:Static local:Commands.SayHi}" Executed="cmdSayHi" CanExecute="cmdCanSayHi"/>
</UserControl.CommandBindings>
<Grid>
<Label x:Name="Label" />
</Grid>
Greeter code:
using System.Windows.Input;
namespace Wpf.RoutedCommands
{
public partial class Greeter
{
public Greeter()
{
InitializeComponent();
}
private void cmdSayHi(object sender, ExecutedRoutedEventArgs e) => SayHi();
private void cmdCanSayHi(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;
public void SayHi() => Label.Content = "Hi!";
}
}
If you create a WPF project and use the above you will see that the button in main window is disabled. Debugging it shows that the Greeter.cmdCanSayHi method never gets called.
If you uncomment the dormant XAML in main window everything works. So: Why can I bind to commands from the window but not from its child controls? Is it to do with rendering timing or something?
A RoutedCommand searches the visual tree from the focused element and up for an element that has a matching CommandBinding and then executes the Execute delegate for this particular CommandBinding.
Your UserControl is located below the Button in the element tree.
I have a WPF dialog, that hosts a windowsFormHost control, with something like this
<Window x:Class="WPFSort.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:WPFSort"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<WindowsFormsHost HorizontalAlignment="Left" Height="Auto"
Margin="87,43,0,0" VerticalAlignment="Top" Width="Auto">
<local:SimpleWinControl />
</WindowsFormsHost>
</Grid>
</Window>
And for the SimpleWinControl , it is a WinForm control. When button 1 is clicked, I want
The WPF dialog to be closed
And the data importantdata to be "pass out" to the WPF form that calls the WPF dialog?
public partial class SimpleWinControl : UserControl
{
public SimpleWinControl()
{
InitializeComponent();
}
public object importantdata;
private void button1_Click(object sender, EventArgs e)
{
//how should I write the close and pass the importantdata out
}
}
You could for example add a property to your WinForms control that exposes the Button control:
public partial class SimpleWinControl : UserControl
{
public SimpleWinControl()
{
InitializeComponent();
}
public Button TheButton { get { return button1; } }
...
}
Give the WinForms control an x:Name in your XAML markup:
<WindowsFormsHost HorizontalAlignment="Left" Height="Auto" Margin="87,43,0,0" VerticalAlignment="Top" Width="Auto">
<local:SimpleWinControl x:Name="winFormsControl" />
</WindowsFormsHost>
...and hook up to the Click event of the Button in the code-behind of your WPF dialog window:
public partial class Dialog : Window
{
public Dialog()
{
InitializeComponent();
winFormsControl.TheButton.Click += (s, e) => this.Close();
}
}
The window that opens the dialog could then access the importantdata field once the ShowDialog method returns:
private void ShowDialog_Click(object sender, RoutedEventArgs e)
{
Dialog d = new Dialog();
d.ShowDialog();
object importantData = d.winFormsControl.importantdata;
}
Another option may be to raise an event from the WinForms control: https://msdn.microsoft.com/en-us/library/5z57dxz2(v=vs.90).aspx
I have a project that uses a System.Windows.Controls.Primatives.Popup to drag a 'tooltip' like control along with a mouse.
Whenever the drag crosses a horizontal line the popup 'wraps' to the bottom of the screen - despite having sane values for the VerticalOffset. The point at which this wrapping occurs appears to be tied to the HEIGHT of the window, but not it's position.
Here's the code from the sandbox project I have created that also exhibits the same behavior:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MainGrid.MouseDown += Grid_MouseDown;
this.MainGrid.MouseUp += Grid_MouseUp;
this.MainGrid.MouseMove += (s, e) => { if (this.Popup.IsOpen) { Popup_Drag(s, e); } };
this.Popup.MouseMove += Popup_Drag;
}
private void Popup_Drag(object sender, MouseEventArgs e)
{
Popup.HorizontalOffset = e.GetPosition(this.Popup).X;
Popup.VerticalOffset = e.GetPosition(this.Popup).Y;
this.Status_Top.Text = String.Format("Height/Top: {0}/{1} Width/Left: {2}/{3}", this.Height, this.Top, this.Width, this.Left);
this.Status.Text = String.Format("Vertical Offset: {0} Horizontal Offset: {1}", Popup.VerticalOffset, Popup.HorizontalOffset);
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
this.Popup.IsOpen = false;
}
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
this.Popup.IsOpen = true;
Popup_Drag(sender, e);
}
}
And the Window XAML:
<Window x:Class="WpfSandbox.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">
<Grid x:Name="MainGrid" Background="Purple">
<TextBlock x:Name="Status_Top"></TextBlock>
<Popup x:Name="Popup" Cursor="Hand" HorizontalAlignment="Left"
VerticalAlignment="Bottom" IsOpen="True">
<TextBlock Background="Blue" Foreground="White">
<TextBlock x:Name="Status">TEXT</TextBlock></TextBlock>
</Popup>
</Grid>
</Window>
I was able to fix this by adding Placement="RelativePoint" to the Popup attributes. Apparently this is the default in Silverlight, but not WPF.
I would like to know if someone has a somple example of how to use keybindings with a comboBox control, because I would like to use keybindings to capture the up arrow and down arrow keys.
Thanks.
It is super simple to capture Keys in WPF. You simple add a PreviewKeyDown event in WPF.
<Window x:Class="WpfApplication13.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">
<Grid>
<ComboBox Name="myComboBox" PreviewKeyDown="MyComboBox_PreviewKeyDown"/>
</Grid>
</Window>
Then trigger something based on the event in the code-behind.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
void MyComboBox_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Down)
{
MessageBox.Show("Key Down");
}
else if (e.Key == Key.Up)
{
MessageBox.Show("Key Up");
}
}
}
I need to perform some operations on the keydown event on a wpf hyperlink.
I have a simple richtextbox in which i have a hyperlink. I want Keydown event to be fired only when the focus is on the hyperlink, that is the cursor is on the hyperlink text.
Doing this doesn't work and i couldn't find any explanation of why this doesn't work.
<Hyperlink KeyDown="Hyperlink_KeyDown">
test
</Hyperlink>
I would really appreciate it if you could help me.
Thanks.
Have a good day,
Astig.
it doesn't work because hyperlink isn't recognized like focused, you may catch this event in parent control for example grid but before it will be caught you must click on it.
So you may catch window's keydown event like this:
XAML:
<Window x:Class="WpfApplication3.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"
Name="MW" KeyDown="MW_KeyDown">
<Grid>
<TextBlock>
<Hyperlink Name="HL1" NavigateUri="http://www.google.com/" RequestNavigate="HL1_RequestNavigate">
Focus it and key down
</Hyperlink>
</TextBlock>
</Grid>
and code:
private void MW_KeyDown(object sender, KeyEventArgs e)
{
if (HL1.IsMouseOver == true)
HL1_RequestNavigate(HL1,new RequestNavigateEventArgs(HL1.NavigateUri, HL1.Name));
}
private void HL1_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
Edit
Also you can set focus to hyperlink like that:
XAML:
<Hyperlink Name="HL1" NavigateUri="http://www.google.com/" RequestNavigate="HL1_RequestNavigate" KeyDown="HL1_KeyDown" MouseEnter="HL1_MouseEnter">
code:
private void HL1_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
private void HL1_KeyDown(object sender, KeyEventArgs e)
{
HL1_RequestNavigate(HL1, new RequestNavigateEventArgs(HL1.NavigateUri, HL1.Name));
}
private void HL1_MouseEnter(object sender, MouseEventArgs e)
{
HL1.Focus();
}