I have an VSTO Outlook Add-in and I would like to popup a notification (floating window) with a custom text message for a specific time (5 seconds for example) and then disappear automatically. I want to show this notification from within the compose window. How can I achieve this? some example will highly appreciated.
UPDATED: I would like the notification window to be a child of the compose window.
UPDATE 29.06.2022:
I have done the following:
Outlook.Inspector currentInspector = this.Window as Outlook.Inspector;
IOleWindow ioleWnd = (IOleWindow)currentInspector;
ioleWnd.GetWindow(out IntPtr phwnd);
NativeWindow nativeWnd = new System.Windows.Forms.NativeWindow();
nativeWnd.AssignHandle(phwnd);
// frm is my notification window, borderless and without maximize, minimize and close buttons and without title bar.
Form frm = new Form();
frm.ControlBox = false;
frm.FormBorderStyle = FormBorderStyle.None;
NativeMethods.Rect rect = new NativeMethods.Rect();
NativeMethods.GetWindowRect(phwnd, ref rect);
frm.Left = rect.Right - 85;
frm.Top = rect.Bottom - 55;
frm.Width = 80;
frm.Height = 50;
frm.Location = new System.Drawing.Point(rect.Right - 85, rect.Bottom - 55);
frm.BackColor = System.Drawing.Color.Red;
TextBox txtBox = new TextBox();
txtBox.BackColor = System.Drawing.Color.Red;
txtBox.ForeColor = System.Drawing.Color.White;
txtBox.Location = new System.Drawing.Point(5, 5);
txtBox.Visible = true;
txtBox.Text = "This is a notification message";
frm.Controls.Add(txtBox);
frm.Show(nativeWnd);
What happens with above code is below:
Notification window is not positioned on the bottom right hand
corner of the compose window.
If I move compose window, notification window keeps in the same position and it is not moving while I move the compose window.
In windows task bar it appears as the notification window is a different process/program, not being part of the same compose window, maybe I need to do something like frm.Owner = nativeWnd but it is not working.
Any ideas?
Use a timer which fires the Tick event on the main thread (UI) where you can call the Close method of your form. The System.Windows.Forms.Timer's event is raised from the UI thread by the message loop when it receives a WM_TIMER message. So, you are good to calling the Close method.
Note, the timer can be run and the form is closed from your code outside of the form or inside the form (built-in to the form).
You can create a custom Windows form that closes itself automatically after a timeout.
To make your form a child of an Outlook inspector, Q/cast the Inspector object (e.g. from Application.ActiveInspector) to the IOleWindow interface, call IOleWindow.GetWindow to get the HWND. Create an instance of the System.Windows.Forms.NativeWindow class and call NativeWindow.AssignHandle. You can then pass the NativeWindow object to Form.Show or Form.ShowModal (NativeWindow object implements the IWin32Window interface).
Related
I am currently developing a program where I can view and send data to an Arduino however I am unsure how to create a window Application that I can move the tabe inside it.
This Tab should be scaleable, pinnable, closeable, and can snap into corners or between other modules. Will I have to create this system From scratch or are there already packages out there that do this ?.
Example of what i try to do
all the telemetry data are in single tab, which can be moved or closed. I am trying to build a win form to do that Task
First of all I am trying to make a moveable Tabe , then scale it up with many modules at once. I am having trouble finding information about moveable Tabe in a winform so if you know of any information to help me out please let me know!
In Winforms, maybe you can try MDI. The link: Multiple-Document Interface (MDI) Applications is the document to create MDI Parent&Child you can refer to.
First, we need to set the "Main Form" as an MDIContainer.
public MainForm()
{
InitializeComponent();
this.IsMdiContainer = true;
}
Then, set the "Child Form"s' MdiParent to the "Main Form".
private void MainForm_Load(object sender, EventArgs e)
{
// Form1
MdiChildForm newMDIChild = new MdiChildForm();
newMDIChild.Size = new Size(200, 200);
newMDIChild.MdiParent = this;
newMDIChild.Show();
newMDIChild.Location = new Point(0, 0);
// Form2
MdiChildForm2 newMDIChild2 = new MdiChildForm2();
newMDIChild2.Size = new Size(500, 200);
newMDIChild2.MdiParent = this;
newMDIChild2.Show();
newMDIChild2.Location = new Point(210, 0);
}
The test result,
I create a global hot key to show a window by PInvoking RegisterHotKey(). But to do this I need that window's HWND, which doesn't exist until the window is loaded, that means shown for the first time. But I don't want to show the window before I can set the hot key. Is there a way to create a HWND for that window that is invisible to the user?
If you are targeting .NET 4.0 you can make use of the new EnsureHandle method available on the WindowInteropHelper:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(thanks to Thomas Levesque for pointing this out.)
If you are targeting an older version of the .NET Framework, the easiest way is to show the window to get to the HWND while setting a few properties to make sure that the window is invisible and doesn't steal focus:
var window = new Window() //make sure the window is invisible
{
Width = 0,
Height = 0,
WindowStyle = WindowStyle.None,
ShowInTaskbar = false,
ShowActivated = false
};
window.Show();
Once you want to show the actual window you can then set the Content, the size and change the style back to a normal window.
You can also change the window into a so called message-only window. As this window type does not support graphical elements it will never be shown. Basically it comes down to calling:
SetParent(hwnd, (IntPtr)HWND_MESSAGE);
Either create a dedicated message window which will always be hidden, or use the real GUI window and change it back to a normal window when you want to display it. See the code below for a more complete example.
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hwnd, IntPtr hwndNewParent);
private const int HWND_MESSAGE = -3;
private IntPtr hwnd;
private IntPtr oldParent;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource != null)
{
hwnd = hwndSource.Handle;
oldParent = SetParent(hwnd, (IntPtr)HWND_MESSAGE);
Visibility = Visibility.Hidden;
}
}
private void OpenWindowMenuItem_Click(object sender, RoutedEventArgs e)
{
SetParent(hwnd, oldParent);
Show();
Activate();
}
For me the solution of setting the width, height to zero and style to none didn't work out, as it still showed a tiny window, with an annoying shadow of what seems to be the border around a 0x0 window (tested on Windows 7). Therefore I'm providing this alternative option.
This is a dirty hack, but it should work, and doesn't have the downsides of changing the opacity :
set the WindowStartupLocation to Manual
set the Top and Left properties to somewhere outside the screen
set ShowInTaskbar to false so that the user doesn't realize there is a new window
Show and Hide the window
You should now be able to retrieve the HWND
EDIT: another option, probably better : set ShowInTaskBar to false and WindowState to Minimized, then show it : it won't be visible at all
I had already posted an answer to that question, but I just found a better solution.
If you just need to make sure that the HWND is created, without actually showing the window, you can do this:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(actually the EnsureHandle method wasn't available when the question was posted, it was introduced in .NET 4.0)
I've never tried to do what you are doing, but if you need to show the Window to get the HWND, but don't want to show it, set the Window Opacity to 0. This will also prevent any hit testing from occurring. Then you could have a public method on the Window to change the Opacity to 100 when you want to make it visible.
I know absolutely nothing about WPF, but could you create a message only window using other means (PInvoke for example) to receive the WM_HOTKEY message? If yes, then once you receive the WM_HOTKEY, you could launch the WPF window from there.
I've noticed that the last thing that happens when the window is being initialized, is the change of WindowState, if it differs from normal. So, you can actually make use of it:
public void InitializeWindow(Window window) {
window.Top = Int32.MinValue;
window.Left = Int32.MinValue;
window.Width = 0;
window.Height = 0;
window.ShowActivated = false;
window.ShowInTaskbar = false;
window.Opacity = 0;
window.StateChanged += OnBackgroundStateChanged;
window.WindowStyle = WindowStyle.None;
}
public void ShowWindow(Window window) {
window.Show();
window.WindowState = WindowState.Maximized;
}
protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
if (isStateChangeFirst) {
isStateChangeFirst = false;
window.Top = 300;
window.Left = 200;
window.Width = 760;
window.Height = 400;
window.WindowState = WindowState.Normal;
window.ShowInTaskbar = true;
window.Opacity = 1;
window.Activate();
}
}
That works fair enough for me. And it does not require working with any handles and stuff, and, more importantly, does not require to have a custom class for a window. Which is great for dynamically loaded XAML. And it is also a great way if you are making a fullscreen app. You do not even need to change its state back to normal or set proper width and height. Just go with
protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
if (isStateChangeFirst) {
isStateChangeFirst = false;
window.ShowInTaskbar = true;
window.Opacity = 1;
window.Activate();
}
}
And you're done.
And even if I am wrong in my assumption that change of state is last thing done when window is being loaded, you can still change to any other event, it does not really matter.
The WindowInteropHelper class should allow you to get the HWND for the WPF window.
MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);
IntPtr hwnd = helper.Handle;
MSDN Documentation
Another option in a similar vein to setting the opacity to 0, is to set the size to 0 and set the position to be off the screen. This won't require the AllowsTransparency = True.
Also remember that once you have shown it once you can then hide it and still get the hwnd.
Make the size of the window 0 x 0 px, put ShowInTaskBar to false, show it, then resize it when needed.
I've created extension method for showing invisible window, next Show calls will behave OK.
public static class WindowHelper
{
public static void ShowInvisible(this Window window)
{
// saving original settings
bool needToShowInTaskbar = window.ShowInTaskbar;
WindowState initialWindowState = window.WindowState;
// making window invisible
window.ShowInTaskbar = false;
window.WindowState = WindowState.Minimized;
// showing and hiding window
window.Show();
window.Hide();
// restoring original settings
window.ShowInTaskbar = needToShowInTaskbar;
window.WindowState = initialWindowState;
}
}
Start Wpf Window in Hidden mode:
WpfWindow w = new WpfWindow() { Visibility = Visibility.Hidden };
Start Wpf Window in Visible mode:
WpfWindow w = new WpfWindow();
w.Show();
I have a WPF application that consists of a long running main window. In the MainWindow constructor I define a Notify icon:
private System.Windows.Forms.NotifyIcon notifyIcon;
public MainWindow()
{
InitializeComponent();
System.Windows.Forms.ContextMenu notifyMenu = new System.Windows.Forms.ContextMenu();
System.Windows.Forms.MenuItem notifyMenuItem = new System.Windows.Forms.MenuItem();
notifyMenuItem.Text = "Exit MainWindow";
notifyMenuItem.Click += new EventHandler(notifyMenuItem_Click);
notifyMenu.MenuItems.Add(notifyMenuItem);
notifyIcon = new System.Windows.Forms.NotifyIcon();
notifyIcon.BalloonTipText = "MainWindow has been minimized. Click the tray icon to show.";
notifyIcon.BalloonTipTitle = "MainWindow";
notifyIcon.Text = "MainWindow";
notifyIcon.Icon = new System.Drawing.Icon("some.ico");
notifyIcon.Visible = true;
notifyIcon.Click += new EventHandler(notifyIcon_Click);
notifyIcon.ContextMenu = notifyMenu;
}
This is all good and well, except that sometimes the NotifyIcon simply disappears from the tray. Absolutely nowhere in the code is notifyIcon.Visible = false; set as the NotifyIcon should also be visible. My application (by requirement) does not show up in the taskbar and uses a single instance manager (if another instance starts up, the newer instance is required to abort immediately). So this leads to a situation where the application was minimized to the tray, the NotifyIcon disappears and the user things the application is not running (it is) and they cannot start a new one (single instance mechanism).
I've seen some SO posts on this matter but they seem to blame the NotifyIcon being instantiated somewhere other than the main windows, which does not apply to my case. Are there other reasons for flaky NotifyIcon behavior, as well as remedies?
This question already has answers here:
Showing a Windows form on a secondary monitor?
(9 answers)
Closed 9 years ago.
I have an application in which there is a form which I want to show on second screen.
Mean If application is running on screen A and when I click on menu to show Form it should display on Screen B
and same with if application is running on screen B and when I click on menu to show Form it should display on Screen A.
You need to use the Screen class to find a screen that the original form is not on, then set the second form's Location property based on that screen's Bounds.
For example:
var myScreen = Screen.FromControl(originalForm);
var otherScreen = Screen.AllScreens.FirstOrDefault(s => !s.Equals(myScreen))
?? myScreen;
otherForm.Left = otherScreen.WorkingArea.Left + 120;
otherForm.Top = otherScreen.WorkingArea.Top + 120;
This will work for any number of screens.
Note that it is possible that the video card is configured so that Windows sees one large screen instead of two smaller ones, in which case this becomes much more difficult.
Below is a function allowing you to display a form on any monitor. For your current scenario you can call this showOnMonitor(1);.
Essentially you have to get screen information from Screen.AllScreens and then get the dimensions of each, then place your form where you need it
function void showOnMonitor(int showOnMonitor)
{
Screen[] sc;
sc = Screen.AllScreens;
Form2 f = new Form2();
f.FormBorderStyle = FormBorderStyle.None;
f.Left = sc[showOnMonitor].Bounds.Left;
f.Top = sc[showOnMonitor].Bounds.Top;
f.StartPosition = FormStartPosition.Manual;
f.Show();
}
Note don't forget to do validation to ensure you actually have two screens etc else an exception will be thrown for accessing sc[showOnMonitor]
On the OnLoad method change the Location of the window.
protected void Form1_OnLoad(...) {
showOnMonitor(1);
}
private void showOnMonitor(int showOnMonitor)
{
Screen[] sc;
sc = Screen.AllScreens;
if (showOnMonitor >= sc.Length) {
showOnMonitor = 0;
}
this.StartPosition = FormStartPosition.Manual;
this.Location = new Point(sc[showOnMonitor].Bounds.Left, sc[showOnMonitor].Bounds.Top);
// If you intend the form to be maximized, change it to normal then maximized.
this.WindowState = FormWindowState.Normal;
this.WindowState = FormWindowState.Maximized;
}
I used this for an XNA 4 Dual Screen Application (Full Screen XNA Game Window + WinForm)
In the Form_Load() method, place the following code:
var primaryDisplay = Screen.AllScreens.ElementAtOrDefault(0);
var extendedDisplay = Screen.AllScreens.FirstOrDefault(s => s != primaryDisplay) ?? primaryDisplay;
this.Left = extendedDisplay.WorkingArea.Left + (extendedDisplay.Bounds.Size.Width / 2) - (this.Size.Width / 2);
this.Top = extendedDisplay.WorkingArea.Top + (extendedDisplay.Bounds.Size.Height / 2) - (this.Size.Height / 2);
I am trying to make a form move (using the titlebar) from a button click.
I thought this would be simple using SendMessage:
Const WM_LBUTTONDOWN As Integer = &H201
Button1.Capture = False
Cursor.Position = Me.Location + New Size(50, 8)
SendMessage(Me.Handle, WM_LBUTTONDOWN, CType(1, IntPtr), IntPtr.Zero)
However, although this sends the message if the cursor is in the forms client area, it does not seem to send it to the forms titlebar (the form captures the event somehow, despite the cursor being on the titlebar not in the client area).
I have tried the above code in both mousedown and click events on the button, moving the cursor and then pressing on the button1.
Any suggestions?
You would need WM_NCLBUTTONDOWN (and pass HTCAPTION as wParam). I'm still not entirely sure this would accomplish what you're trying to do, though.
Typically, the way to allow the user to move your form when clicking somewhere other than the title bar is to process the WM_NCHITTEST message and return HTCAPTION when the cursor is over the area from which you'd like to initiate moving. However, if this area is occupied by a child control, you also have to process WM_NCHITTEST from the child control and return HTTRANSPARENT.
Incidentally, an easier—if slightly less correct—way to accomplish this is to do as Mehrdad Afshari suggested, and just set the form's Location property. You commented to him that "it needs to move on the mouse move", and that's exactly what you can and should do.
class MyForm : Form{
Point downAt;
MyForm(){
Label lbl = new Label();
lbl.AutoSize = true;
lbl.BackColor = Color.Blue;
lbl.ForeColor = Color.White;
lbl.Location = new Point(50, 50);
lbl.Text = "Drag me to move this form.";
lbl.Parent = this;
lbl.MouseDown += (s, e)=>downAt = e.Location;
lbl.MouseMove += (s, e)=>{if(lbl.Capture) Location += (Size)e.Location - (Size)downAt;};
}
}
The problem with this approach is that it bypasses Windows' code for moving a top-level window. This means that if the user has not selected the "Show window contents while dragging" option in the Display Properties dialog, this will effectively ignore that setting (it won't show a drag outline). There may be other drawbacks that I haven't thought of as well.
On the whole, though, this is a simple, easy way to accomplish this that is a fully .NET solution which doesn't rely on any platform invoke (so it should be portable to Mono on Unix).
Oops. I just realized that I gave you C# example code, but your code seems to be VB.NET. I guess what you would need would be:
Sub New()
Dim lbl As New Label
lbl.AutoSize = True
lbl.BackColor = Color.Blue
lbl.ForeColor = Color.White
lbl.Location = New Point(50, 50)
lbl.Text = "Drag me to move this form."
lbl.Parent = Me
AddHandler lbl.MouseDown, Function(ByVal s As Object, ByVal e As MouseEventArgs)
Me.downAt = e.Location
End Function
AddHandler lbl.MouseMove, Function(ByVal s As Object, ByVal e As MouseEventArgs)
If lbl.Capture Then
Me.Location = Me.Location + DirectCast(e.Location, Size) - DirectCast(Me.downAt, Size)
End If
End Function
End Sub
This may not be the most succinct way to express this in VB.NET. I used Reflector to help me translate it.
The LParam value for the wm_LButtonDown message receives the mouse position in client coordinates. The title bar is in the non-client area, so use the wm_NCLButtonDown message. I've seen that message given as an answer to this question before, but there's a more direct route that I would have expected to work: Send a wm_SysCommand message to the window, and specify sc_Move flag.
Mehrdad is right, no need to do this. The mouse is captured so you can never move it too quickly. Sample code:
Point mLastPos;
private void button1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
this.Location = new Point(this.Location.X + e.X - mLastPos.X,
this.Location.Y + e.Y - mLastPos.Y);
}
// NOTE: else is intentional!
else mLastPos = e.Location;
}