Is there a way to check if the Vertical Scroll Bar is visible on a certain ListView object?
I got a Windows Forms with a listView on it, on the resize event i would like to catch if the listview has its Vertical Scroll Bar visible!
If this is WPF a sample exist here which at the base of the solution hooks on to ListView.LayoutUpdated.
If this is WinForms you could make use of pinvoke and GetWindowLong...
static public class WndInfo
{
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
...
public static bool IsWindowTopMost(IntPtr Handle)
{
return (GetWindowLong(Handle, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
}
...
}
VB code exists using GetWindowLong to check if a ScrollBar exist which you could port to C#.
I have several methods that I use when using Winforms that are based on what type of control I would like to get that information from. Here is my class.
public static class NativeMethods
{
const Int32 LVM_FIRST = 0x1000;
const Int32 LVM_SCROLL = LVM_FIRST + 20;
[DllImport("user32")]
static extern IntPtr SendMessage(IntPtr Handle, Int32 msg, IntPtr wParam, IntPtr lParam);
// offset of window style value
const int GWL_STYLE = -16;
// window style constants for scrollbars
const int WS_VSCROLL = 0x00200000;
const int WS_HSCROLL = 0x00100000;
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
static ScrollBars GetControlVisibleScrollbars(Control ctl)
{
int wndStyle = GetWindowLong(ctl.Handle, GWL_STYLE);
bool hsVisible = (wndStyle & WS_HSCROLL) != 0;
bool vsVisible = (wndStyle & WS_VSCROLL) != 0;
if(hsVisible)
return vsVisible ? ScrollBars.Both : ScrollBars.Horizontal;
else
return vsVisible ? ScrollBars.Vertical : ScrollBars.None;
}
public static ScrollBars GetVisibleScrollbars(this ListView lv)
{
if(lv is null)
{
throw new ArgumentNullException(nameof(lv));
}
return GetControlVisibleScrollbars(lv);
}
public static ScrollBars GetVisibleScrollbars(this ScrollableControl ctl)
{
if(ctl is null)
{
throw new ArgumentNullException(nameof(ctl));
}
if(ctl.HorizontalScroll.Visible)
return ctl.VerticalScroll.Visible ? ScrollBars.Both : ScrollBars.Horizontal;
else
return ctl.VerticalScroll.Visible ? ScrollBars.Vertical : ScrollBars.None;
}
private static void ScrollHorizontal(Form form, int pixelsToScroll)
{
SendMessage(form.Handle, LVM_SCROLL, (IntPtr)pixelsToScroll, IntPtr.Zero);
}
public static void EnsureVisible(this ListViewItem item, int subItemIndex, int margin=10)
{
if(item is null)
{
throw new ArgumentNullException(nameof(item));
}
if( subItemIndex > item.SubItems.Count - 1)
{
throw new IndexOutOfRangeException($"ListView {item.ListView.Name} does not have a SubItem on index {subItemIndex}");
}
// scroll to the item row.
item.EnsureVisible();
Rectangle bounds = item.SubItems[subItemIndex].Bounds;
bounds.Width = item.ListView.Columns[subItemIndex].Width;
ScrollToRectangle(item.ListView,bounds,margin);
}
private static void ScrollToRectangle(ListView listView, Rectangle bounds, int margin)
{
int scrollToLeft = bounds.X + bounds.Width + margin;
if(scrollToLeft > listView.Bounds.Width)
{
ScrollHorizontal(listView.FindForm(),scrollToLeft - listView.Bounds.Width);
}
else
{
int scrollToRight = bounds.X - margin;
if(scrollToRight < 0)
{
ScrollHorizontal(listView.FindForm(),scrollToRight);
}
}
}
Say in your use case you have a splitter panel and you would like to have the left panel wide enough not to have a horizontal scrollbar then you could do this:
var bar= MyListView.GetVisibleScrollbars();
while(bar== ScrollBars.Horizontal || bar== ScrollBars.Both)
{
progressPanels.SplitterDistance += 5;
bar = MyListView.GetVisibleScrollbars();
}
I'm not saying it's the best way, I just say it's an option, especially if dealing with several monitors in different DPI
Related
This is the first code I've written in c#, and my first question to Stackoverflow...apologies up front if I'm doing everything wrong! :-/
I've tried to implement the Public Class RTFScrolledToBottom written by LarsTech that was posted as answered the question here:
Get current scroll position from rich text box control?
In the public Form1() code block, this line is generating a CS1061 error:
rtfScrolledBottom1.ScrolledToBottom += rtfScrolledBottom1_ScrolledToBottom;
object does not contain a definition for ScrolledToBottom and no accessible extension method ScrolledToBottom accepting a first argument of type object could be found (are you missing a using directive or an assembly reference?)
Thanks in advance for any assistance pointing me to what I'm screwing up!!
Cheers!
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private object rtfScrolledBottom1;
public Form1()
{
InitializeComponent();
int rtfScrolledBottom1_ScrolledToBottom = 0;
rtfScrolledBottom1.ScrolledToBottom += rtfScrolledBottom1_ScrolledToBottom;
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
}
}
public class RTFScrolledBottom : RichTextBox
{
public event EventHandler ScrolledToBottom;
private const int WM_VSCROLL = 0x115;
private const int WM_MOUSEWHEEL = 0x20A;
private const int WM_USER = 0x400;
private const int SB_VERT = 1;
private const int EM_SETSCROLLPOS = WM_USER + 222;
private const int EM_GETSCROLLPOS = WM_USER + 221;
[DllImport("user32.dll")]
private static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);
public bool IsAtMaxScroll()
{
int minScroll;
int maxScroll;
GetScrollRange(this.Handle, SB_VERT, out minScroll, out maxScroll);
Point rtfPoint = Point.Empty;
SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref rtfPoint);
return (rtfPoint.Y + this.ClientSize.Height >= maxScroll);
}
protected virtual void OnScrolledToBottom(EventArgs e)
{
if (ScrolledToBottom != null)
ScrolledToBottom(this, e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
if (IsAtMaxScroll())
OnScrolledToBottom(EventArgs.Empty);
base.OnKeyUp(e);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL)
{
if (IsAtMaxScroll())
OnScrolledToBottom(EventArgs.Empty);
}
base.WndProc(ref m);
}
}
}
You have defined rtfScrolledBottom1 as object and object doesn't have any event. You need to define it as RTFScrolledBottom. You also can drop an instance of the RTFScrolledBottom control from toolbox and use it like any other control.
Alternative solution
As an alternative to the solution which you found in the linked post, here is another solution which Works with RichTextBox without creating a derived control, while you can put the logic in a derived control and make it more reusable, like what has done in the linked post.
You can handle VScroll event of the RichTextBox and get the scroll position by calling GetScrollInfo method. Then in the SCROLLINFO if nPage + nPos == nMax, it means the scroll is at bottom:
[StructLayout(LayoutKind.Sequential)]
struct SCROLLINFO {
public int cbSize;
public ScrollInfoMask fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
public enum ScrollInfoMask : uint {
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS),
}
[DllImport("user32.dll")]
private static extern bool GetScrollInfo(IntPtr hwnd, SBOrientation fnBar,
ref SCROLLINFO lpsi);
public enum SBOrientation : int { SB_HORZ = 0x0, SB_VERT = 0x1 }
private void richTextBox1_VScroll(object sender, EventArgs e)
{
var info = new SCROLLINFO() {
cbSize = (Marshal.SizeOf<SCROLLINFO>()),
fMask = ScrollInfoMask.SIF_ALL
};
GetScrollInfo(richTextBox1.Handle, SBOrientation.SB_VERT, ref info);
if (info.nPage + info.nPos == info.nMax)
{
//VScroll is at bottom
}
}
I have ListView which sets the width of columns depending on size:
public class CommonListView : ListView
{
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
int columnWidth = (ClientSize.Width - SystemInformation.VerticalScrollBarWidth - 6) / Columns.Count;
foreach (ColumnHeader column in Columns)
column.Width = columnWidth;
}
}
I added listview on a form and set anchor property to 'All' (Top | Bottom | Left | Right).
When I change the size of a form all working correctly. But when I maximize a form (via maximize box) and minimize after that columns have correct size, but appears horizontal scrollbar which should not be.
If I click on it (without changing the size of columns or listview), it will disappear.
What should I do to make this scrollbar did not appear?
The method that I have found to solve my problem described below.
private FormWindowState _previouseFormWindowState;
private void MainForm_Resize(object sender, EventArgs e)
{
if (_previouseFormWindowState == FormWindowState.Maximized && WindowState != FormWindowState.Maximized)
{
var si = new SCROLLINFO();
if (GetScrollInfo(listView, ref si, ScrollBarDirection.SB_HORZ))
{
if (si.nMax < si.nPage)
{
ShowScrollBar(listView.Handle, (int)ScrollBarDirection.SB_HORZ, false);
}
}
}
_previouseFormWindowState = WindowState;
}
public enum ScrollInfoMask : uint
{
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS),
}
public enum ScrollBarDirection
{
SB_HORZ = 0,
SB_VERT = 1,
SB_CTL = 2,
SB_BOTH = 3
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
public static bool GetScrollInfo(Control ctrl, ref SCROLLINFO si, ScrollBarDirection scrollBarDirection)
{
if (ctrl != null)
{
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (int)ScrollInfoMask.SIF_ALL;
if (GetScrollInfo(ctrl.Handle, (int)scrollBarDirection, ref si))
return true;
}
return false;
}
How do I achieve this in a WinForms container control when the scroll bars are visible?
Highlighted here (Google Chrome browser):
EDIT: This cursor is the only one that is visible on a screenshot. I hope it's clear what i mean.
EDIT:
Tried this on my control. Does not work.
const int WM_MBUTTONDOWN = 0x207;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MBUTTONDOWN)
DefWndProc(ref m);
else
base.WndProc(ref m);
}
Here's what I have so far. It exits "reader mode" if I release the middle button, and I haven't implemented scrolling within the control (I used a textbox), but it may give you something to start with.
[DllImport("comctl32.dll", SetLastError=true, EntryPoint="#383")]
static extern void DoReaderMode(ref READERMODEINFO prmi);
public delegate bool TranslateDispatchCallbackDelegate(ref MSG lpmsg);
public delegate bool ReaderScrollCallbackDelegate(ref READERMODEINFO prmi, int dx, int dy);
[StructLayout(LayoutKind.Sequential)]
public struct READERMODEINFO
{
public int cbSize;
public IntPtr hwnd;
public int fFlags;
public IntPtr prc;
public ReaderScrollCallbackDelegate pfnScroll;
public TranslateDispatchCallbackDelegate fFlags2;
public IntPtr lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public UInt32 message;
public IntPtr wParam;
public IntPtr lParam;
public UInt32 time;
public POINT pt;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int left, top, right, bottom;
}
private bool TranslateDispatchCallback(ref MSG lpMsg)
{
return false;
}
private bool ReaderScrollCallback(ref READERMODEINFO prmi, int dx, int dy)
{
// TODO: Scroll around within your control here
return false;
}
private void EnterReaderMode()
{
READERMODEINFO readerInfo = new READERMODEINFO
{
hwnd = this.textBox1.Handle,
fFlags = 0x00,
prc = IntPtr.Zero,
lParam = IntPtr.Zero,
fFlags2 = new TranslateDispatchCallbackDelegate(this.TranslateDispatchCallback),
pfnScroll = new ReaderScrollCallbackDelegate(this.ReaderScrollCallback)
};
readerInfo.cbSize = Marshal.SizeOf(readerInfo);
DoReaderMode(ref readerInfo);
}
private void textBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Middle)
{
EnterReaderMode();
}
}
The RichTextBox control does it by default when you press the mouse wheel button.
Edit: Sorry I misunderstood and thought you were asking about doing it within a textbox not a container control
In Windows Explorer (at least in Win7) when you hover the mouse over a column header, a filter box with an arrow appears that lets you filter the results in the ListView, so for example you can only show files starting with "A" or files > 128 MB. Can this feature be enabled in the basic ListView control in C# without subclassing or modifying the ListView?
Here's some code to play with. Add a new class to your project and paste the code shown below. Compile. Drop the new ListViewEx control from the top of the toolbox onto your form. In the form constructor, call the SetHeaderDropdown() method to enable the button. Implement the HeaderDropdown event to return a control to display. For example:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
listViewEx1.SetHeaderDropdown(0, true);
listViewEx1.HeaderDropdown += listViewEx1_HeaderDropdown;
}
void listViewEx1_HeaderDropdown(object sender, ListViewEx.HeaderDropdownArgs e) {
e.Control = new UserControl1();
}
}
The below code has a flaw, the popup is displayed in a form. Which can't be too small and takes the focus away from the main form. Check this answer on hints how to implement a control that can be displayed as a toplevel window without needing a form. The code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class ListViewEx : ListView {
public class HeaderDropdownArgs : EventArgs {
public int Column { get; set; }
public Control Control { get; set; }
}
public event EventHandler<HeaderDropdownArgs> HeaderDropdown;
public void SetHeaderDropdown(int column, bool enable) {
if (column < 0 || column >= this.Columns.Count) throw new ArgumentOutOfRangeException("column");
while (HeaderDropdowns.Count < this.Columns.Count) HeaderDropdowns.Add(false);
HeaderDropdowns[column] = enable;
if (this.IsHandleCreated) SetDropdown(column, enable);
}
protected void OnHeaderDropdown(int column) {
var handler = HeaderDropdown;
if (handler == null) return;
var args = new HeaderDropdownArgs() { Column = column };
handler(this, args);
if (args.Control == null) return;
var frm = new Form();
frm.FormBorderStyle = FormBorderStyle.FixedSingle;
frm.ShowInTaskbar = false;
frm.ControlBox = false;
args.Control.Location = Point.Empty;
frm.Controls.Add(args.Control);
frm.Load += delegate { frm.MinimumSize = new Size(1, 1); frm.Size = frm.Controls[0].Size; };
frm.Deactivate += delegate { frm.Dispose(); };
frm.StartPosition = FormStartPosition.Manual;
var rc = GetHeaderRect(column);
frm.Location = this.PointToScreen(new Point(rc.Right - SystemInformation.MenuButtonSize.Width, rc.Bottom));
frm.Show(this.FindForm());
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
if (this.Columns.Count == 0 || Environment.OSVersion.Version.Major < 6 || HeaderDropdowns == null) return;
for (int col = 0; col < HeaderDropdowns.Count; ++col) {
if (HeaderDropdowns[col]) SetDropdown(col, true);
}
}
private Rectangle GetHeaderRect(int column) {
IntPtr hHeader = SendMessage(this.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
RECT rc;
SendMessage(hHeader, HDM_GETITEMRECT, (IntPtr)column, out rc);
return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
}
private void SetDropdown(int column, bool enable) {
LVCOLUMN lvc = new LVCOLUMN();
lvc.mask = LVCF_FMT;
lvc.fmt = enable ? LVCFMT_SPLITBUTTON : 0;
IntPtr res = SendMessage(this.Handle, LVM_SETCOLUMN, (IntPtr)column, ref lvc);
}
protected override void WndProc(ref Message m) {
Console.WriteLine(m);
if (m.Msg == WM_NOTIFY) {
var hdr = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
if (hdr.code == LVN_COLUMNDROPDOWN) {
var info = (NMLISTVIEW)Marshal.PtrToStructure(m.LParam, typeof(NMLISTVIEW));
OnHeaderDropdown(info.iSubItem);
return;
}
}
base.WndProc(ref m);
}
private List<bool> HeaderDropdowns = new List<bool>();
// Pinvoke
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, ref LVCOLUMN lvc);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, out RECT rc);
[DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWnd, IntPtr hParent);
private const int LVM_SETCOLUMN = 0x1000 + 96;
private const int LVCF_FMT = 1;
private const int LVCFMT_SPLITBUTTON = 0x1000000;
private const int WM_NOTIFY = 0x204e;
private const int LVN_COLUMNDROPDOWN = -100 - 64;
private const int LVM_GETHEADER = 0x1000 + 31;
private const int HDM_GETITEMRECT = 0x1200 + 7;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct LVCOLUMN {
public uint mask;
public int fmt;
public int cx;
public string pszText;
public int cchTextMax;
public int iSubItem;
public int iImage;
public int iOrder;
public int cxMin;
public int cxDefault;
public int cxIdeal;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct POINT {
public int x, y;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct RECT {
public int left, top, right, bottom;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct NMHDR {
public IntPtr hwndFrom;
public IntPtr idFrom;
public int code;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct NMLISTVIEW {
public NMHDR hdr;
public int iItem;
public int iSubItem;
public uint uNewState;
public uint uOldState;
public uint uChanged;
public POINT ptAction;
public IntPtr lParam;
}
}
It might be tricky to implement the same type of interface, but you could have your ListView respond to the contents of a TextBox by handling the TextBox's TextChanged event and filtering the list based on the contents. If you put the list in a DataTable then filtering will be easy and you can repopulate your ListView each time the filter changes.
Of course this depends on how many items are in your list.
I'd like to display a little popup window next to the notification area. It's similar to what Outlook/Skype/Live! Messenger/etc does when it displays the notification about a new message. In my case it will have some input controls (textbox, datetimepicker, buttons...) so a simple bubble won't do.
The trick is doing this correctly when the user has multiple monitors and/or the taskbar is not located at the bottom of the screen. I could not find any functions that would let me determine the position and orientation of the taskbar/notification area.
Use WinAPI calls to find the TaskBar position, and position your window according to it
C# Example
class Program
{
static void Main(string[] args)
{
Taskbar taskbar = new Taskbar();
Console.WriteLine("Position: {0}, AlwaysOnTop: {1}; AutoHide: {2}; Bounds: {3}", taskbar.Position, taskbar.AlwaysOnTop, taskbar.AutoHide, taskbar.Bounds);
Console.ReadLine();
}
}
public enum TaskbarPosition
{
Unknown = -1,
Left,
Top,
Right,
Bottom,
}
public sealed class Taskbar
{
private const string ClassName = "Shell_TrayWnd";
public Rectangle Bounds
{
get;
private set;
}
public TaskbarPosition Position
{
get;
private set;
}
public Point Location
{
get
{
return this.Bounds.Location;
}
}
public Size Size
{
get
{
return this.Bounds.Size;
}
}
//Always returns false under Windows 7
public bool AlwaysOnTop
{
get;
private set;
}
public bool AutoHide
{
get;
private set;
}
public Taskbar()
{
IntPtr taskbarHandle = User32.FindWindow(Taskbar.ClassName, null);
APPBARDATA data = new APPBARDATA();
data.cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA));
data.hWnd = taskbarHandle;
IntPtr result = Shell32.SHAppBarMessage(ABM.GetTaskbarPos, ref data);
if (result == IntPtr.Zero)
throw new InvalidOperationException();
this.Position = (TaskbarPosition) data.uEdge;
this.Bounds = Rectangle.FromLTRB(data.rc.left, data.rc.top, data.rc.right, data.rc.bottom);
data.cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA));
result = Shell32.SHAppBarMessage(ABM.GetState, ref data);
int state = result.ToInt32();
this.AlwaysOnTop = (state & ABS.AlwaysOnTop) == ABS.AlwaysOnTop;
this.AutoHide = (state & ABS.Autohide) == ABS.Autohide;
}
}
public enum ABM : uint
{
New = 0x00000000,
Remove = 0x00000001,
QueryPos = 0x00000002,
SetPos = 0x00000003,
GetState = 0x00000004,
GetTaskbarPos = 0x00000005,
Activate = 0x00000006,
GetAutoHideBar = 0x00000007,
SetAutoHideBar = 0x00000008,
WindowPosChanged = 0x00000009,
SetState = 0x0000000A,
}
public enum ABE : uint
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3
}
public static class ABS
{
public const int Autohide = 0x0000001;
public const int AlwaysOnTop = 0x0000002;
}
public static class Shell32
{
[DllImport("shell32.dll", SetLastError = true)]
public static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData);
}
public static class User32
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
[StructLayout(LayoutKind.Sequential)]
public struct APPBARDATA
{
public uint cbSize;
public IntPtr hWnd;
public uint uCallbackMessage;
public ABE uEdge;
public RECT rc;
public int lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
You need to get the actual location of your notification icon, and place your pop-up window near that (or wherever you like).
You need to translate your XY locations relative to desktop(s). AFAIK, there are no direct function, even in Win32 API which can directly give you the answer.
These sites will help you-
1. http://forum.codecall.net/managed-c/262-dual-monitors-window-position.html
2. http://msdn.microsoft.com/en-us/magazine/cc188759.aspx