С#. WinApi. Рисование на окне

350
05 мая 2017, 15:21

Нужно написать на С# программу, которая будет определять над каким окном Windows сейчас находится курсор и обводить это окно рамкой (указывая пользователю, над каким элементом находится курсор, ведь в WinApi, окном является не только само окно, содержащее в себе множество других элементов, но и эти элементы тоже являются окнами).

Handler окна под курсором получить легко:

    ...
    Point point;
    WinApi.GetCursorPos(out point);
    WinApi.WindowFromPoint(point);
    ...

Но вот нарисовать что то на окне не выходит, хоть убейте.

    public static void drawSelectionRectangle(IntPtr handler)
    {
        Rectangle rectangle;
        WinApi.GetWindowRect(handler, out rectangle);
        WinApi.PAINTSTRUCT paintProperties;
        IntPtr paintContext = WinApi.BeginPaint(handler, out paintProperties);
        IntPtr pen = WinApi.CreatePen(WinApi.PenStyle.PS_SOLID, 5, (uint) ColorTranslator.ToWin32(Color.Red));
        WinApi.SelectObject(paintContext, pen);
        WinApi.Rectangle(paintContext, rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
        WinApi.ValidateRect(handler, IntPtr.Zero);
        WinApi.EndPaint(handler, ref paintProperties);
    }

drawSelectionRectangle(IntPtr handler) вызывал и одиночно (по нажатию кнопки) и в цикле (так как предполагал, что отрисовка может выполнятся за один кадр, после чего, исчезать). Ни так ни так не работает.

Подскажите, пожалуйста, в чем проблема... Скоро курсовой сдавать, а ничего не готово :(

Answer 1

Проблема в неверном подходе. Во-первых, забудьте про функцию BeginPaint (вне обработки сообщения WM_PAINT), во-вторых, рисовать надо не в контексте целевого окна, а в контексте его родительского окна (контекст окна позволяет рисовать только в его клиентской области, а рамка-то нам нужна снаружи).

Я предлагаю сделать как-то так:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace DrawingTest
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetCursorPos(out POINT lpPoint);
        [DllImport("user32.dll")]
        static extern IntPtr WindowFromPoint(POINT p);                
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
        [DllImport("user32.dll")]
        static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);        
        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        public static extern IntPtr GetParent(IntPtr hWnd);
        public Form1()
        {
            InitializeComponent();
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            //получаем окно в текущей позиции курсора  
            POINT pt;
            GetCursorPos(out pt);
            IntPtr hwnd = WindowFromPoint(pt);
            //получаем родительское окно
            IntPtr hwnd_p = GetParent(hwnd);
            //получаем границы окна   
            RECT rc;
            GetWindowRect(hwnd, out rc);
            //перевод из экранных координат в клиентские
            POINT pt1=new POINT(rc.Left,rc.Top), pt2=new POINT(rc.Right,rc.Bottom);
            ScreenToClient(hwnd_p, ref pt1);
            ScreenToClient(hwnd_p, ref pt2);
            RECT rc_client = new RECT();
            rc_client.Left = pt1.X-1; rc_client.Top = pt1.Y-1;
            rc_client.Right = pt2.X; rc_client.Bottom = pt2.Y;
            //формируем структуру для GDI+
            Rectangle rect = new Rectangle(rc_client.Left, rc_client.Top, rc_client.Right - rc_client.Left, rc_client.Bottom - rc_client.Top);
            //получаем контекст окна            
            Graphics g = System.Drawing.Graphics.FromHwnd(hwnd_p);
            using (g)
            {
                //рисуем прямоугольник
                g.DrawRectangle(Pens.Red, rect);
            }
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;
        public POINT(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
        public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { }
        public static implicit operator System.Drawing.Point(POINT p)
        {
            return new System.Drawing.Point(p.X, p.Y);
        }
        public static implicit operator POINT(System.Drawing.Point p)
        {
            return new POINT(p.X, p.Y);
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;        // x position of upper-left corner
        public int Top;         // y position of upper-left corner
        public int Right;       // x position of lower-right corner
        public int Bottom;      // y position of lower-right corner
    }
}

Если целевое окно перекрывается другими окнами, соответствующая часть рамки будет скрыта. Чтобы рисовать рамку поверх всех окон, код рисования нужно изменить, используя контекст рабочего стола:

        //получаем окно в текущей позиции курсора  
        POINT pt;
        GetCursorPos(out pt);
        IntPtr hwnd = WindowFromPoint(pt);
        //получаем границы окна   
        RECT rc;
        GetWindowRect(hwnd, out rc);
        //формируем структуру для GDI+
        Rectangle rect = new Rectangle(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top);
        //получаем контекст рабочего стола
        Graphics g = System.Drawing.Graphics.FromHwnd((IntPtr)0);
        using (g)
        {
            //рисуем прямоугольник
            g.DrawRectangle(Pens.Red, rect);
        }

Иллюстрация различия между этими двумя методами:

READ ALSO
InvalidCastException при преобразовании IQueryable в List

InvalidCastException при преобразовании IQueryable в List

Есть БД SQLiteВ ней есть таблица Studios со следующей структурой:

408
Нули при отображении даты (date) в БД MSSQL

Нули при отображении даты (date) в БД MSSQL

У меня есть значение даты в формате 405

284
null при отправке модели

null при отправке модели

Всем здравствуйтеСтолкнулся с проблемой:мне в приложении надо добавить товар в БД, при этом заранее не известно сколько у него будет разных...

328
Работа атрибута DataSource в #

Работа атрибута DataSource в #

Не могу найти хорошего объяснения работы атрибута [DataSource]Везде работа с таблицами

270