I've made a simple custom control with ClickEvent:
ImageButton.xaml:
<UserControl x:Name="ImgButton" x:Class="WpfApplication1.ImageButton"
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">
<Grid></Grid>
</UserControl>
ImageButton.xaml.cs:
public partial class ImageButton : UserControl
{
private bool mouse_down = false;
private bool mouse_in = false;
public event EventHandler Click;
public ImageButton()
{
this.MouseEnter += new MouseEventHandler(ImageButton_MouseEnter);
this.MouseLeave += new MouseEventHandler(ImageButton_MouseLeave);
this.MouseLeftButtonDown += new MouseButtonEventHandler(ImageButton_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(ImageButton_MouseLeftButtonUp);
InitializeComponent();
}
void ImageButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if ((mouse_down)&&(mouse_in))
{
Click(this, null);
}
mouse_down = false;
}
void ImageButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
mouse_down = true;
}
void ImageButton_MouseLeave(object sender, MouseEventArgs e)
{
mouse_in = false;
}
void ImageButton_MouseEnter(object sender, MouseEventArgs e)
{
mouse_in = true;
}
}
It works correct when I click on the control if I'm handling Click event, otherwise I get crash. So, what should I do?
You must check if handler was added to event beforre rising it:
void ImageButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if ((mouse_down) && (mouse_in) && Click != null)
{
Click(this, null);
}
mouse_down = false;
}
Related
I want to provide search functionality on context menu displayed in the system tray. I am using System.Windows.Forms.NotifyIcon class to display the context menu in the tray. Basically this search should allow the user to find the required menu from the context menu. For that, I added the WPF textbox like this in the below image, but it does not allow me to type the text.
Is there any way to handle the text input on this?
Here is my code,
MainWindow.xaml
<Window x:Class="SysTray_Sample.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:SysTray_Sample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ContextMenu x:Key="NotifierContextMenu"
Placement="MousePoint">
<MenuItem Header="First" StaysOpenOnClick="True"/>
<MenuItem StaysOpenOnClick="True">
<MenuItem.Header>
<TextBox Text="Test" Width="120" />
</MenuItem.Header>
</MenuItem>
<MenuItem Header="Open" Click="Menu_Open"/>
<MenuItem Header="Close" Click="Menu_Close"/>
</ContextMenu>
</Window.Resources>
<Grid>
<Button Height="22" Width="180" Content="Right click over me">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Click="MenuItem_Click" Header="Minimize to system tray"/>
<MenuItem Header="Desktop mode"/>
<TextBox Width="150"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private System.Windows.Forms.NotifyIcon _trayIcon;
public MainWindow()
{
InitializeComponent();
_trayIcon = new System.Windows.Forms.NotifyIcon();
_trayIcon.MouseDown += MyNotifyIcon_MouseDown;
_trayIcon.Icon = new System.Drawing.Icon(#"Resources\add.ico");
_trayIcon.MouseDoubleClick += MyNotifyIcon_MouseDoubleClick;
}
private void MyNotifyIcon_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
ContextMenu menu = (ContextMenu)this.FindResource("NotifierContextMenu");
menu.IsOpen = true;
}
}
private void MyNotifyIcon_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
this.WindowState = WindowState.Normal;
_trayIcon.Visible = false;
this.ShowInTaskbar = true;
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
this.ShowInTaskbar = false;
_trayIcon.BalloonTipTitle = "Minimize Sucessful";
_trayIcon.BalloonTipText = "Minimized the app ";
_trayIcon.ShowBalloonTip(400);
_trayIcon.Visible = true;
this.WindowState = WindowState.Minimized;
}
private void Menu_Open(object sender, RoutedEventArgs e)
{
MessageBox.Show("Open");
}
private void Menu_Close(object sender, RoutedEventArgs e)
{
MessageBox.Show("Close");
}
}
After a long hour of searching, I found the following solution which worked for my case.
https://www.codeproject.com/Questions/184429/Text-box-is-not-working-in-WPF-Popup
[DllImport("USER32.DLL")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ActivateVisual(FrameworkElement contextMenu)
{
var fromVisual = HwndSource.FromDependencyObject(contextMenu);
HwndSource source = (HwndSource)fromVisual;
IntPtr handle = source.Handle;
SetForegroundWindow(handle);
}
Here is my complete MainWindow.xaml.cs code,
public partial class MainWindow : Window
{
private System.Windows.Forms.NotifyIcon _trayIcon;
[DllImport("USER32.DLL")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ActivateVisual(FrameworkElement contextMenu)
{
var fromVisual = HwndSource.FromDependencyObject(contextMenu);
HwndSource source = (HwndSource)fromVisual;
IntPtr handle = source.Handle;
SetForegroundWindow(handle);
}
public MainWindow()
{
InitializeComponent();
_trayIcon = new System.Windows.Forms.NotifyIcon();
_trayIcon.MouseDown += MyNotifyIcon_MouseDown;
_trayIcon.Icon = new System.Drawing.Icon(#"C:\Users\nambi\source\repos\SysTray_Sample\SysTray_Sample\Resources\add.ico");
_trayIcon.MouseDoubleClick += MyNotifyIcon_MouseDoubleClick;
}
private void MyNotifyIcon_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
ContextMenu menu = (ContextMenu)this.FindResource("NotifierContextMenu");
menu.IsOpen = true;
ActivateVisual(this.ContextMenu);
}
}
private void MyNotifyIcon_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
this.WindowState = WindowState.Normal;
_trayIcon.Visible = false;
this.ShowInTaskbar = true;
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
this.ShowInTaskbar = false;
_trayIcon.BalloonTipTitle = "Minimize Sucessful";
_trayIcon.BalloonTipText = "Minimized the app ";
_trayIcon.ShowBalloonTip(400);
_trayIcon.Visible = true;
this.WindowState = WindowState.Minimized;
}
private void Menu_Open(object sender, RoutedEventArgs e)
{
ActivateVisual(this);
MessageBox.Show("Open");
}
private void Menu_Close(object sender, RoutedEventArgs e)
{
ActivateVisual(this);
MessageBox.Show("Close");
}
}
Page1 page1 = new Page1();
Page2 page2 = new Page2();
Page3 page3 = new Page3();
private void btnNextClick(object sender, RoutedEventArgs e)
{
navi.Navigate(page1);
navi.Navigate(page2);
}
As shown in the code above, I want the Frame first to navigate to "page1",and then to navigate to "page2".
As a result,it did navigate to "page2",but I have't see "page1" in Fram.BackStack. So I can't navigate back. And I found Frame.Navigating event trigged twice, and Frame.Navigated trigged once. It seems the first navigate was canceled, and I want know why?
And, Is there another way to achieve this effect?(navigate to page1 and quickly navigate to page2--->then navigate back to page1)
Additional information:
my full c# code:
public partial class MainWindow : Window
{
Page1 page1 = new Page1();
Page2 page2 = new Page2();
Page3 page3 = new Page3();
public MainWindow()
{
InitializeComponent();
navi.Navigated += Navi_Navigated;
navi.Navigating += Navi_Navigating;
}
private void Navi_Navigating(object sender, NavigatingCancelEventArgs e)
{
}
private void Navi_Navigated(object sender, NavigationEventArgs e)
{
}
private void btnPreviousPageClick(object sender, RoutedEventArgs e)
{
if (navi.CanGoBack)
{
navi.GoBack();
}
}
private void btnNextClick(object sender, RoutedEventArgs e)
{
navi.Navigate(page1);
navi.Navigate(page2);
}
private void btnNextClick2(object sender, RoutedEventArgs e)
{
navi.Navigate(page2);
}
private void btnNextClick3(object sender, RoutedEventArgs e)
{
navi.Navigate(page3);
}
}
and my xaml code:
<Window x:Class="mmNavigate.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:mmNavigate"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Frame JournalOwnership="OwnsJournal" NavigationUIVisibility="Visible" x:Name="navi" Height="280"></Frame>
<StackPanel Orientation="Horizontal">
<Button Content="Previous" Click="btnPreviousPageClick"/>
<Button Content="Page1" Margin="20,0,0,0" Click="btnNextClick"/>
<Button Content="Page2" Margin="20,0,0,0" Click="btnNextClick2"/>
<Button Content="Page3" Margin="20,0,0,0" Click="btnNextClick3"/>
</StackPanel>
</StackPanel>
You should wait to navigate to Page2 until the navigation to Page1 has been completed by handling the Navigated event:
private void btnNextClick(object sender, RoutedEventArgs e)
{
navi.Navigated += Navi_Navigated1; ;
navi.Navigate(page1);
}
private void Navi_Navigated1(object sender, NavigationEventArgs e)
{
navi.Navigated -= Navi_Navigated1;
navi.Navigate(page2);
}
I has a requirement that to track down all Window.Show or ShowDialog() in WPF.
The main purpose is I want to know when all Window in App open or close.
Something like, when closing WindowA or ChildWindowA, I want to write AuditLog for which view was opened/closed, I don't want to write code for each Window or ChildWindow and write it in App instance level to handle all open/close Window or ChildWindow in App.
You could create a base class deriving from Window that handles the logging.
public class AuditLoggableWindow : Window
{
public AuditLoggableWindow()
{
Closing += OnClosing;
ContentRendered += OnShown;
}
protected void OnClosing(object o, CancelEventArgs e)
{
// log that window is closing now
}
protected void OnShown(object o, EventArgs e)
{
// log that the window has been shown
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : AuditLoggableWindow
{
public MainWindow()
{
InitializeComponent();
}
}
And in your XAML markup you need to Replace the Window tag with namespace:AuditLoggableWindow. As the namespace of my project is wpfApplication1, the markup would be as follows:
<wpfApplication1:AuditLoggableWindow x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</wpfApplication1:AuditLoggableWindow>
I would like to register an attached property:
public static class WindowLog
{
public static readonly DependencyProperty EnableLogProperty =
DependencyProperty.RegisterAttached(
"EnableLog",
typeof(bool),
typeof(WindowLog),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnEnableWindowLogChanged));
public static void SetEnableWindowLog(Window window, bool value)
{
window.SetValue(EnableLogProperty, value);
}
public static bool GetEnableWindowLog(Window element)
{
return (bool)element.GetValue(EnableLogProperty);
}
private static void OnEnableWindowLogChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
Window window = dependencyObject as Window;
if (window == null)
{
return;
}
if (GetEnableWindowLog(window))
{
Register(window);
}
else
{
Unregister(window);
}
}
private static void Unregister(Window window)
{
window.Closing -= Window_Closing;
window.Activated -= Window_Activated;
window.Closed -= Window_Closed;
}
private static void Register(Window window)
{
window.Closing += Window_Closing;
window.Activated += Window_Activated;
window.Closed += Window_Closed;
}
private static void Window_Closed(object sender, EventArgs e)
{
Window window = (Window)sender;
window.Closing -= Window_Closing;
window.Activated -= Window_Activated;
window.Closed -= Window_Closed;
}
private static void Window_Activated(object sender, EventArgs e)
{
// do something
}
private static void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// do something
}
}
Using
<Window x:Class="Wpf.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:attachments="clr-namespace:Wpf.Attachments"
attachments:WindowLog.EnableWindowLog="true">
<StackPanel>
</StackPanel>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
Uri mapUri = new Uri(#"http://www.google.com");
oneView.Navigate(mapUri);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
//Focus(FocusState.Programmatic);
//this.AddHandler(UIElement.KeyDownEvent, new KeyEventHandler(Window_KeyDown), true);
Window.Current.CoreWindow.KeyUp += Window_KeyUp;
Window.Current.CoreWindow.KeyDown += Window_KeyDown;
}
void Window_KeyUp(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs e)
{
if (e.VirtualKey == VirtualKey.Control) isCtrlKeyPressed = false;
}
void Window_KeyDown(object sender, KeyEventArgs e)
{
var messageDialog = new MessageDialog("");
bool isCtrlKey = CoreWindow.GetForCurrentThread().GetAsyncKeyState(Windows.System.VirtualKey.Control) == CoreVirtualKeyStates.Down;
if (isCtrlKey)
{
switch (e.VirtualKey)
{
case VirtualKey.P:
//video.Play();
messageDialog.Content = "P Key Pressed";
messageDialog.ShowAsync();
break;
case VirtualKey.G:
messageDialog.Content = "G Key Pressed";
messageDialog.ShowAsync();
break;
case VirtualKey.A:
messageDialog.Content = "A Key Pressed";
messageDialog.ShowAsync();
break;
case VirtualKey.T:
messageDialog.Content = "T Key Pressed";
messageDialog.ShowAsync();
break;
}
}
//bool isMenuKey = CoreWindow.GetForCurrentThread().GetAsyncKeyState(Windows.System.VirtualKey.Menu) == CoreVirtualKeyStates.Down;
//if (isMenuKey && e.Key == VirtualKey.S)
//{
// queryTextBox.Focus(FocusState.Keyboard);
// queryTextBox.SelectAll();
//}
}
public bool isCtrlKeyPressed { get; set; }
}
The KeyDown and KeyUP event do not get fired. Here is the XAML I am using:
<Page
x:Class="App5.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App5"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<WebView x:Name="oneView" IsHitTestVisible="False">
</WebView>
</Grid>
</Page>
I see two things you could do:
Use WebViewBrush instead of a WebView.
Handle key presses with JavaScript inside of the WebView and pass them out to the XAML layer through ScripNotify.
I have a WPF window that focuses a textbox when the window starts up via the FocusManager, which works. e.g.
<Window
...
FocusManager.FocusedElement="{Binding ElementName=nameTextBox}">
Upon focus, I'd like to also highlight the text in the textbox. I'd like to do this all in the XAML if possible, as I have no code in code behind and would like to keep it that way.
I know you want to keep .cs out of your project but it'll be quite hard to achieve, you can create a one off class with an attached property inside that can be used over and over in your xaml files.
public class AutoFocusBehavior : DependencyObject
{
public static bool IsAutoFocus(DependencyObject obj)
{
return (bool)obj.GetValue(IsAutoFocusProperty);
}
public static void SetIsAutoFocus(DependencyObject obj, bool value)
{
obj.SetValue(IsAutoFocusProperty, value);
}
public static readonly DependencyProperty IsAutoFocusProperty =
DependencyProperty.RegisterAttached("IsAutoFocus", typeof(bool), typeof(AutoFocusBehavior),
new PropertyMetadata(false, new PropertyChangedCallback((d, de) =>
{
if ((bool)de.NewValue)
{
FrameworkElement frameworkElement = (FrameworkElement)d;
frameworkElement.Unloaded += frameworkElement_Unloaded;
frameworkElement.IsVisibleChanged += new DependencyPropertyChangedEventHandler(frameworkElement_IsVisibleChanged);
frameworkElement.IsEnabledChanged += frameworkElement_IsEnabledChanged;
}
else
{
FrameworkElement frameworkElement = (FrameworkElement)d;
frameworkElement.Unloaded -= frameworkElement_Unloaded;
frameworkElement.IsVisibleChanged -= new DependencyPropertyChangedEventHandler(frameworkElement_IsVisibleChanged);
frameworkElement.IsEnabledChanged -= frameworkElement_IsEnabledChanged;
}
})));
static void frameworkElement_Unloaded(object sender, RoutedEventArgs e)
{
FrameworkElement frameworkElement = (FrameworkElement)sender;
frameworkElement.IsVisibleChanged -= new DependencyPropertyChangedEventHandler(frameworkElement_IsVisibleChanged);
frameworkElement.IsEnabledChanged -= frameworkElement_IsEnabledChanged;
}
static void frameworkElement_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (((bool)e.NewValue))
{
if (sender is System.Windows.Controls.TextBox)
{
System.Windows.Controls.TextBox textBox = sender as System.Windows.Controls.TextBox;
if (textBox != null)
{
textBox.SelectAll();
}
}
((FrameworkElement)sender).Focus();
}
}
static void frameworkElement_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (((bool)e.NewValue))
{
if (sender is System.Windows.Controls.TextBox)
{
System.Windows.Controls.TextBox textBox = sender as System.Windows.Controls.TextBox;
if (textBox != null)
{
textBox.SelectAll();
textBox.Focus();
}
}
((FrameworkElement)sender).Focus();
}
}
}
Then used in any xaml like this.
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Text="testing" local:AutoFocusBehavior.IsAutoFocus="True" Margin="133,87,292,198"/>
</Grid>
</Window>