.NET: How to place my window near the notification area (systray)? - c#

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

Related

Valid friendly monitor names with C#

My main development machine is a laptop with 2 screens: an internal screen and an external Samsung monitor.
Generic PnP Monitor= 1366x768, Top: 0, Left: 1920 -> secondary display
SF350_S24F350FH / S24F352FH / S24F354FH (HDMI)= 1920x1080, Top: 0, Left: 0 -> main display
And my codes are:
Dispay.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
internal class Display
{
private Rectangle _bounds;
private DisplayOrientation _orientation;
private Rectangle _workingArea;
private string _name,_deviceId;
private static Display[] _displays;
public Rectangle Bounds
{
get
{
return _bounds;
}
}
public DisplayOrientation Orientation
{
get
{
return _orientation;
}
}
public Rectangle WorkingArea
{
get
{
return _workingArea;
}
}
public string DeviceId
{
get
{
return _deviceId;
}
}
public string Name
{
get
{
return _name;
}
}
public static DisplayImpl[] Displays
{
get
{
if (_displays == null) QueryDisplayDevices();
return _displays;
}
private static void QueryDisplayDevices()
{
List<Display> list = new List<Display>();
WinApi.MonitorEnumDelegate MonitorEnumProc = new WinApi.MonitorEnumDelegate((IntPtr hMonitor, IntPtr hdcMonitor, ref WinApi.RECT lprcMonitor, IntPtr dwData) => {
WinApi.MONITORINFOEX mi = new WinApi.MONITORINFOEX() { Size = Marshal.SizeOf(typeof(WinApi.MONITORINFOEX)) };
if (WinApi.GetMonitorInfo(hMonitor, ref mi))
{
WinApi.DISPLAY_DEVICE device = new WinApi.DISPLAY_DEVICE();
device.Initialize();
if (WinApi.EnumDisplayDevices(mi.DeviceName.ToLPTStr(), 0, ref device, 0))
{
Display display = new Display()
{
_name = device.DeviceString,
_deviceId = mi.DeviceName,
_bounds=new Rectangle(mi.Monitor.Left,mi.Monitor.Top,mi.Monitor.Right-mi.Monitor.Left,mi.Monitor.Bottom-mi.Monitor.Top),
_workingArea = new Rectangle(mi.WorkArea.Left, mi.WorkArea.Top, mi.WorkArea.Right - mi.WorkArea.Left, mi.WorkArea.Bottom - mi.WorkArea.Top),
};
list.Add(display);
}
}
return true;
});
WinApi.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumProc, IntPtr.Zero);
_displays=list.ToArray();
}
}
}
WinApi.cs:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
internal class WinApi
{
#region DISPLAY_DEVICE struct
[StructLayout(LayoutKind.Sequential)]
internal struct DISPLAY_DEVICE
{
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public DisplayDeviceStateFlags StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
public void Initialize()
{
cb = 0;
DeviceName = new string((char)32, 32);
DeviceString = new string((char)32, 128);
DeviceID = new string((char)32, 128);
DeviceKey = new string((char)32, 128);
cb = Marshal.SizeOf(this);
}
}
#endregion
#region RECT struct
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
#endregion
#region MONITORINFOEX struct
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MONITORINFOEX
{
public int Size;
public RECT Monitor;
public RECT WorkArea;
public uint Flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
}
#endregion
#region DisplayDeviceStateFlags enum
[Flags()]
public enum DisplayDeviceStateFlags : int
{
/// <summary>The device is part of the desktop.</summary>
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
/// <summary>The device is part of the desktop.</summary>
PrimaryDevice = 0x4,
/// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
MirroringDriver = 0x8,
/// <summary>The device is VGA compatible.</summary>
VGACompatible = 0x10,
/// <summary>The device is removable; it cannot be the primary display.</summary>
Removable = 0x20,
/// <summary>The device has more display modes than its output devices support.</summary>
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000,
}
#endregion
public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);
[DllImport("User32.dll")]
internal static extern bool EnumDisplayDevices(byte[] lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);
public static byte[] ToLPTStr(this string str)
{
var lptArray = new byte[str.Length + 1];
var index = 0;
foreach (char c in str.ToCharArray())
lptArray[index++] = Convert.ToByte(c);
lptArray[index] = Convert.ToByte('\0');
return lptArray;
}
}
Then I try to debug
if(Display.Displays !=null) { }
And I got these results:
Display 0:
Name: Generic PnP Monitor
DeviceId: \\\\.\\DISPLAY1
Bounds:
Top: 0
Left: 0
Width: 1920
Height: 1080
Display 1:
Name: SF350_S24F350FH / S24F352FH / S24F354FH (HDMI)
DeviceId: \\\\.\\DISPLAY2
Bounds:
Top: 0
Left: 1920
Width: 1366
Height: 768
Based on screen resolution and top-left values, Display 0 should be "SF350_S24F350FH / S24F352FH / S24F354FH (HDMI)", and why it got swapped with Display 1?
It depends on which monitor is defined as the main one. It does not matter the position of each monitor.
This library does the job very well if you want : WindowsDisplayAPI
According to the document of the EnumDisplayDevices
To obtain information on a display monitor, first call
EnumDisplayDevices with lpDevice set to NULL. Then call
EnumDisplayDevices with lpDevice set to
DISPLAY_DEVICE.DeviceName from the first call to
EnumDisplayDevices and with iDevNum set to zero. Then
DISPLAY_DEVICE.DeviceString is the monitor name.
The samples:
private static void QueryDisplayDevices()
{
DISPLAY_DEVICE device = new DISPLAY_DEVICE();
device.Initialize();
uint DispNum = 0;
while (EnumDisplayDevices(null, DispNum, ref device, 0))
{
DISPLAY_DEVICE dev = new DISPLAY_DEVICE();
dev.Initialize();
if (EnumDisplayDevices(device.DeviceName, 0, ref dev, 0))
{
Console.WriteLine("Device Name:" + dev.DeviceName);
Console.WriteLine("Monitor name:" + dev.DeviceString);
}
DispNum++;
device.Initialize();
}
}
With #Dmo 's clues, I use this library
Rectangle rect;
Display display;
foreach (PathInfo pi in PathInfo.GetActivePaths())
{
if (!pi.TargetsInfo[0].DisplayTarget.IsAvailable) continue;
rect=System.Windows.Forms.Screen.GetWorkingArea(new Rectangle(pi.Position, pi.Resolution));
display = new DisplayImpl()
{
_name = string.IsNullOrEmpty(pi.TargetsInfo[0].DisplayTarget.FriendlyName)? "Generic PnP Monitor" : pi.TargetsInfo[0].DisplayTarget.FriendlyName,
_deviceId = pi.DisplaySource.DisplayName,
_devicePath=pi.TargetsInfo[0].DisplayTarget.DevicePath,
_bounds = new Rectangle(pi.Position,pi.Resolution),
_workingArea = rect,
};
list.Add(display);
}
It produces the correct monitor name and settings pair! 👍

c# Balloon Tip Text: Remove Project text from notification

I am using Balloon tip text in C# and everything is working as expected. However whenever my notifications appear, there will be subtext that correlates to the project name.
How do I remove this text?
Notification image:
notifyIcon1.Icon = new Icon(SystemIcons.Application, 40, 40);
notifyIcon1.Visible = true;
notifyIcon1.Text = "Application Installation";
notifyIcon1.BalloonTipText = "The App installaion has started.";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.BalloonTipTitle = "Test App Installation";
notifyIcon1.ShowBalloonTip(10000);
Okey so i think i have a start for you atleast. As you can see in the image below i managed to remove the name of the exectuable file that it was running from.
I found a example here how to show Balloon tip like Windows 10 Balloon tip without stretching icon and the example from cokeman19 where you can add your own custom icon. Although this does not seem to work when you dont want the exectuable name at the bottom.
If you want to do some changes i recommend you to look at https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx since it provied some nice hints on what to do.
Here is the code that i made a few changes too so we get what we want.
public class NotifyIconLarge : IDisposable
{
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
public static extern int Shell_NotifyIcon(int message, NOTIFYICONDATA pnid);
[DllImport("Comctl32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr LoadIconWithScaleDown(IntPtr hinst, string pszName, int cx, int cy, out IntPtr phico);
[DllImport("user32.dll", SetLastError = true)]
static extern bool DestroyIcon(IntPtr hIcon);
private const int NIF_LARGE_ICON = 0x00000040;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class NOTIFYICONDATA
{
public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
public IntPtr hWnd;
public int uID;
public int uFlags;
public int uCallbackMessage;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szTip;
public int dwState;
public int dwStateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szInfo;
public int uTimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szInfoTitle;
public int dwInfoFlags;
Guid guidItem;
public IntPtr hBalloonIcon;
}
private IntPtr _windowHandle;
private IntPtr _hIcon;
private bool _added;
private int _id = 1;
private string _tipText;
public NotifyIconLarge(IntPtr windowHandle, string tipText)
{
_windowHandle = windowHandle;
_tipText = tipText;
IntPtr result = LoadIconWithScaleDown(IntPtr.Zero, "", 0, 0, out _hIcon);
UpdateIcon(true);
}
private void UpdateIcon(bool showIconInTray)
{
NOTIFYICONDATA nOTIFYICONDATA = new NOTIFYICONDATA();
nOTIFYICONDATA.uCallbackMessage = 2048;
nOTIFYICONDATA.uFlags = 1;
nOTIFYICONDATA.hWnd = _windowHandle;
nOTIFYICONDATA.uID = _id;
nOTIFYICONDATA.hIcon = IntPtr.Zero;
nOTIFYICONDATA.szTip = null;
if (_hIcon != IntPtr.Zero)
{
nOTIFYICONDATA.uFlags |= 1;
nOTIFYICONDATA.hIcon = _hIcon;
}
nOTIFYICONDATA.uFlags |= 4;
nOTIFYICONDATA.szTip = _tipText;
nOTIFYICONDATA.hBalloonIcon = _hIcon;
if (showIconInTray)
{
if (!_added)
{
Shell_NotifyIcon(0, nOTIFYICONDATA);
_added = true;
}
else
{
Shell_NotifyIcon(1, nOTIFYICONDATA);
}
}
else
{
if (_added)
{
Shell_NotifyIcon(2, nOTIFYICONDATA);
_added = false;
}
}
}
public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
{
NOTIFYICONDATA nOTIFYICONDATA = new NOTIFYICONDATA();
nOTIFYICONDATA.hWnd = _windowHandle;
nOTIFYICONDATA.uID = _id;
nOTIFYICONDATA.uFlags = 16;
nOTIFYICONDATA.uTimeoutOrVersion = timeout;
nOTIFYICONDATA.szInfoTitle = tipTitle;
nOTIFYICONDATA.szInfo = tipText;
switch (tipIcon)
{
case ToolTipIcon.None:
nOTIFYICONDATA.dwInfoFlags = NIF_LARGE_ICON;
break;
case ToolTipIcon.Info:
nOTIFYICONDATA.dwInfoFlags = 1;
break;
case ToolTipIcon.Warning:
nOTIFYICONDATA.dwInfoFlags = 2;
break;
case ToolTipIcon.Error:
nOTIFYICONDATA.dwInfoFlags = 3;
break;
}
int ret = Shell_NotifyIcon(1, nOTIFYICONDATA);
}
public void RemoveFromTray()
{
UpdateIcon(false);
if (_hIcon != IntPtr.Zero)
DestroyIcon(_hIcon);
}
~NotifyIconLarge()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
RemoveFromTray();
}
}
So first you declare the NotifyIconLarge class somewhere.
private NotifyIconLarge _nil;
Then to create a notify popup you use the following code:
_nil = new NotifyIconLarge(Handle, "Icon Tip");
_nil.ShowBalloonTip(10000, "Balloon Title", "Balloon Text", ToolTipIcon.None);
When you are finished with the tray remove it:
_nil.RemoveFromTray();
Credits to cokeman19 for most of the code

How to get current keyboard cursor point using c# global keyboard hook event

Im working with a c# application which captures the system events using the global c# mouse and keyboard hooks, But I couldn't get the current keyboard cursor position values while im typing.
Following is my code and it always returns GetCaretPos output as (0,0)
PointDetail curPoint = new PointDetail();
Point position = new Point();
IntPtr hwndFoc;
IntPtr hwndFG = WinApiDelegate.GetForegroundWindow();
uint processID = 0;
uint mainWindowProcessId = 0;
IntPtr activeWindowThreadProcess = WinApiDelegate.GetWindowThreadProcessId(hwndFG, IntPtr.Zero);
IntPtr currWindowThread = IntPtr.Zero;
int thisWindowThread = 0;
this.Invoke(new MethodInvoker(delegate
{
currWindowThread = WinApiDelegate.GetWindowThreadProcessId(this.Handle, IntPtr.Zero);
thisWindowThread = WinApiDelegate.GetWindowThreadProcessId(this.Handle, out mainWindowProcessId);
}));
int activeWindowThread = WinApiDelegate.GetWindowThreadProcessId(hwndFG, out processID);
if (activeWindowThread != Thread.CurrentThread.ManagedThreadId)
{
WinApiDelegate.AttachThreadInput(activeWindowThreadProcess, currWindowThread, true);
hwndFoc = WinApiDelegate.GetActiveWindow();
bool CaretPos = WinApiDelegate.GetCaretPos(ref position);
}
You can find the cursor position using the caretPosition. Try the below code.
[DllImport("user32.dll", EntryPoint = "GetGUIThreadInfo")]
public static extern bool GetGUIThreadInfo(uint tId, out GUITHREADINFO threadInfo);
[StructLayout(LayoutKind.Sequential)] // Required by user32.dll
public struct RECT
{
public uint Left;
public uint Top;
public uint Right;
public uint Bottom;
};
[StructLayout(LayoutKind.Sequential)] // Required by user32.dll
public struct GUITHREADINFO
{
public uint cbSize;
public uint flags;
public IntPtr hwndActive;
public IntPtr hwndFocus;
public IntPtr hwndCapture;
public IntPtr hwndMenuOwner;
public IntPtr hwndMoveSize;
public IntPtr hwndCaret;
public RECT rcCaret;
};
private System.Windows.Point EvaluateCaretPosition()
{
caretPosition = new Point();
try
{
// Fetch GUITHREADINFO
GetCaretPosition();
caretPosition.X = (int)guiInfo.rcCaret.Left; //+ 25;
caretPosition.Y = (int)guiInfo.rcCaret.Bottom; //+ 25;
WinApiDelegate.ClientToScreen(guiInfo.hwndCaret, ref caretPosition);
}
catch (Exception Ex)
{
GenerateConsolidatedErrorLog(Ex);
}
return new System.Windows.Point(caretPosition.X, caretPosition.Y);
}
public void GetCaretPosition()
{
try
{
guiInfo = new WinApiDelegate.GUITHREADINFO();
guiInfo.cbSize = (uint)Marshal.SizeOf(guiInfo);
// Get GuiThreadInfo into guiInfo
WinApiDelegate.GetGUIThreadInfo(0, out guiInfo);
}
catch (Exception Ex)
{
GenerateConsolidatedErrorLog(Ex);
}
}

How to change the gamma ramp of a single display monitor (NVidia Config)?

I try to change my gamma of just one screen and not all my screens.
I use this code to help me
But this SetDeviceGammaRamp(GetDC(IntPtr.Zero), ref s_ramp);
Is for all devices.
[EDIT2] I saw one weird thing : SetDeviceGammaRamp is not the same gamma of the Nvidia Panel Controller (I tried to change my value of SetDeviceGammaRamp, and it's like if i changed the value of brightness and contrast in the Nvidia panel). So i think i must to use NVidia API :/
So, how can i change this code to put my gamma on my first screen, or my second, but not both
[EDIT1] This is what i made :
class Monitor
{
[DllImport("user32.dll")]
static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData);
public delegate int MonitorEnumProc(IntPtr hMonitor, IntPtr hDCMonitor, ref Rect lprcMonitor, IntPtr dwData);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hmon, ref MonitorInfo mi);
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int left;
public int top;
public int right;
public int bottom;
}
/// <summary>
/// The struct that contains the display information
/// </summary>
public class DisplayInfo
{
public string Availability { get; set; }
public string ScreenHeight { get; set; }
public string ScreenWidth { get; set; }
public Rect MonitorArea { get; set; }
public Rect WorkArea { get; set; }
public IntPtr DC { get; set; }
}
[StructLayout(LayoutKind.Sequential)]
struct MonitorInfo
{
public uint size;
public Rect monitor;
public Rect work;
public uint flags;
}
/// <summary>
/// Collection of display information
/// </summary>
public class DisplayInfoCollection : List<DisplayInfo>
{
}
/// <summary>
/// Returns the number of Displays using the Win32 functions
/// </summary>
/// <returns>collection of Display Info</returns>
public DisplayInfoCollection GetDisplays()
{
DisplayInfoCollection col = new DisplayInfoCollection();
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData)
{
MonitorInfo mi = new MonitorInfo();
mi.size = (uint)Marshal.SizeOf(mi);
bool success = GetMonitorInfo(hMonitor, ref mi);
if (success)
{
DisplayInfo di = new DisplayInfo();
di.ScreenWidth = (mi.monitor.right - mi.monitor.left).ToString();
di.ScreenHeight = (mi.monitor.bottom - mi.monitor.top).ToString();
di.MonitorArea = mi.monitor;
di.WorkArea = mi.work;
di.Availability = mi.flags.ToString();
di.DC = GetDC(hdcMonitor);
col.Add(di);
}
return 1;
}, IntPtr.Zero);
return col;
}
public Monitor()
{
}
}
And for SetDeviceGammaRamp, i made this :
GammaRamp gamma = new GammaRamp();
Monitor.DisplayInfoCollection monitors;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Monitor monitor = new Monitor();
monitors = monitor.GetDisplays();
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
int value = trackBar1.Value;
gamma.SetValue(Convert.ToByte(value), monitors[1].DC);
}
GammaRamp class :
public void SetValue(byte value, IntPtr hdc)
{
Ramp gammaArray = new Ramp { Red = new ushort[256], Green = new ushort[256], Blue = new ushort[256] };
for (int i = 0; i < 256; i++)
{
gammaArray.Red[i] = gammaArray.Green[i] = gammaArray.Blue[i] = (ushort)Math.Min(i * (value + 128), ushort.MaxValue);
}
SetDeviceGammaRamp(hdc, ref gammaArray);
}
You can get the DC of another monitor by using EnumDisplayMonitors or GetMonitorInfo functions.
See complete explanations at HMONITOR and the Device Context.
EDIT
As explained in EnumDisplayMonitors,
pass IntPtr.Zero to hdc parameter (values encompasses all displays)
then in MONITORENUMPROC, hdcMonitor should contain the right DC for the current monitor being evaluated
then change your di.DC = GetDC(IntPtr.Zero); to di.DC = GetDC(hdcMonitor);
(passing Zero to GetDC will obviously specify all monitors, not what you want)
EDIT 2
Little confusion with the docs, in fact the 3rd type of call in the remarks of EnumDisplayMonitors should be performed:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
private readonly List<IntPtr> _dcs = new List<IntPtr>();
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var hdc = NativeMethods.GetDC(IntPtr.Zero);
if (hdc == IntPtr.Zero)
throw new InvalidOperationException();
if (!NativeMethods.EnumDisplayMonitors(hdc, IntPtr.Zero, Monitorenumproc, IntPtr.Zero))
throw new InvalidOperationException();
if (NativeMethods.ReleaseDC(IntPtr.Zero, hdc) == 0)
throw new InvalidOperationException();
foreach (var monitorDc in _dcs)
{
// do something cool !
}
}
private int Monitorenumproc(IntPtr param0, IntPtr param1, ref tagRECT param2, IntPtr param3)
{
// optional actually ...
var info = new MonitorInfo {cbSize = (uint) Marshal.SizeOf<MonitorInfo>()};
if (!NativeMethods.GetMonitorInfoW(param0, ref info))
throw new InvalidOperationException();
_dcs.Add(param1); // grab DC for current monitor !
return 1;
}
}
public class NativeMethods
{
[DllImport("user32.dll", EntryPoint = "ReleaseDC")]
public static extern int ReleaseDC([In] IntPtr hWnd, [In] IntPtr hDC);
[DllImport("user32.dll", EntryPoint = "GetDC")]
public static extern IntPtr GetDC([In] IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "GetMonitorInfoW")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorInfoW([In] IntPtr hMonitor, ref MonitorInfo lpmi);
[DllImport("user32.dll", EntryPoint = "EnumDisplayMonitors")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumDisplayMonitors([In] IntPtr hdc, [In] IntPtr lprcClip, MONITORENUMPROC lpfnEnum,
IntPtr dwData);
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int MONITORENUMPROC(IntPtr param0, IntPtr param1, ref tagRECT param2, IntPtr param3);
[StructLayout(LayoutKind.Sequential)]
public struct MonitorInfo
{
public uint cbSize;
public tagRECT rcMonitor;
public tagRECT rcWork;
public uint dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
public struct tagRECT
{
public int left;
public int top;
public int right;
public int bottom;
}
}
You should be able to get per-monitor DC, (cannot 100% confirm since I only have one screen).
If all else fails then maybe that NVidia thing interferes somehow under the hood.

Adding filter boxes to the column headers of a ListView in C# and WinForms

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.

Categories

Resources