У меня есть потенциальное решение, которое может сработать, хотя мне нужно больше информации о том, как работают ваши анимированные элементы управления, чтобы быть уверенным. В моем решении есть один неприятный побочный эффект, заключающийся в том, что свойство DoubleBuffering корректно работает только в контейнерах элементов управления .NET. При размещении в MFC ваши элементы управления будут мерцать при изменении размера и других аналогичных обновлениях отображения. Это может вызвать проблемы с анимированными элементами управления, в зависимости от того, как они выполняют рисование.
Для начала я сначала искал проблемы при размещении .NET UserControl в MFC. После долгого времени чтения кода реализации CWinFormsControl :: CreateControl () и всего остального ничего необычного не пришло. Фактически, за исключением особенностей загрузки управляемых ссылок, код идентичен тому, как загружаются прозрачные элементы управления ActiveX.
Изучив эту информацию, я использовал Spy ++, чтобы посмотреть, создан ли элемент управления .NET с помощью оконного контейнера. Это действительно так. После довольно продолжительного исследования этот контейнер управления, по-видимому, управляется экземпляром служебного класса System.Windows.Forms.Control.AxSourcingSite, у которого нет документации и почти нет видимости. Это было немного удивительно для меня, как обычно, наоборот. MFC и менее используемый WTL имеют отличную поддержку активации на месте, и обычно элементы управления могут работать с любой настройкой хоста, независимо от того, оконная она или нет.
Здесь я проверил, существует ли этот же контейнер, когда элемент управления .NET размещен в контейнере элемента управления .NET. Я предполагал, что, возможно, элемент управления будет иметь свое собственное окно, без каких-либо специальных адаптеров. Оказывается, я был не прав. Элемент управления работает так же, как и встроенные неоконные элементы управления. Это означает, что для сохранения поведения решение должно позволить обычной активации .NET проходить как обычно, а при работе в окне оно должно делать что-то еще.
При внимательном рассмотрении размещенной версии MFC обнаруживается не совсем белый фон, нарисованный .NET UserControl. После большего количества проб и тестирования этот не совсем белый фон определенно прорисовывается скрытым слоем в цепочке обработки сообщений окна. Это означает, что мы можем взломать решение, используя AllPaintingInWmPaint.
Чтобы продемонстрировать это, вот исходный код UserControl, который можно разместить как в .NET, так и в управляемом контейнере MFC. Этот элемент управления основан на следующих вещах для решения проблем прозрачности.
- Добавьте переменную-член m_ReroutePaint, чтобы мы знали, когда нам нужно переопределить стандартное поведение WM_PAINT.
- Переопределить base.CreateParams и добавить флаг WS_EX_TRANSPARENT. Когда это свойство вызывается, установите для m_ReroutePaint значение true. Это свойство не вызывалось при активации элемента управления в контейнере .NET.
- Переопределите метод WndProc () и исправьте WM_PAINT по своему вкусу, если мы перенаправляем действия по рисованию.
- Используйте BeginPaint () / EndPaint () через Interop для настройки / демонтажа WM_PAINT. Используйте предоставленный HDC в качестве инициализатора для графического объекта.
Вот несколько предостережений:
- Цвет фона элемента управления не может быть изменен через свойство BackColor .NET после создания экземпляра элемента управления. Для этого можно добавить обходные пути, но, чтобы образец был коротким и простым, я пропустил код, чтобы сделать это, так как цель - прозрачные элементы управления. Однако, если вы начинаете с непрозрачного цвета фона, обходной путь не требуется. Я оставил код для этого случая.
- При подключении HDC к графическому объекту в обработчике WM_PAINT через Graphics.FromHdc () в документации предлагается вызывать Graphics.ReleaseHdc (). Однако при этом происходит утечка дескриптора GDI. Я оставил это закомментированным здесь, но, возможно, кто-то со знанием внутренних дел GDI + сможет понять это.
Этот UserControl был создан в проекте с именем 'UserCtrlLibrary1'. Элементы DebugPrintStyle () могут быть безопасно удалены. Кроме того, были добавлены обработчики для изменения размера и рисования, которые находятся в отдельном файле дизайнера, но их легко добавить. Значение AllPaintingInWmPaint должно быть истинным в течение всего срока действия элемента управления.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace UserCtrlLibrary1
{
public partial class CircleControl : UserControl
{
public CircleControl()
{
InitializeComponent();
DebugPrintStyle(ControlStyles.SupportsTransparentBackColor, "initial");
DebugPrintStyle(ControlStyles.AllPaintingInWmPaint, "initial");
DebugPrintStyle(ControlStyles.UserPaint, "initial");
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
DebugPrintStyle(ControlStyles.SupportsTransparentBackColor, "current");
DebugPrintStyle(ControlStyles.AllPaintingInWmPaint, "current");
DebugPrintStyle(ControlStyles.UserPaint, "current");
}
public void DebugPrintStyle(ControlStyles cs, string prefix)
{
Debug.Print("{0}: {1}={2}", prefix, cs.ToString(), this.GetStyle(cs).ToString());
}
bool m_ReroutePaint;
const int WS_EX_TRANSPARENT = 0x0020;
protected override CreateParams CreateParams
{
get
{
if (this.BackColor == Color.Transparent)
{
m_ReroutePaint = true;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TRANSPARENT;
return cp;
}
else
{
return base.CreateParams;
}
}
}
private void CircleControl_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
using (SolidBrush b = new SolidBrush(Color.Orange))
{
g.FillEllipse(b, 0, 0, this.Width, this.Height);
}
}
private void CircleControl_Resize(object sender, EventArgs e)
{
this.Invalidate();
}
const int WM_PAINT = 0x000F;
[DllImport("user32.dll")]
static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);
[DllImport("user32.dll")]
static extern bool EndPaint(IntPtr hWnd, [In] ref PAINTSTRUCT lpPaint);
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct PAINTSTRUCT
{
public IntPtr hdc;
public bool fErase;
public RECT rcPaint;
public bool fRestore;
public bool fIncUpdate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] rgbReserved;
}
protected override void WndProc(ref Message m)
{
if ((m.Msg == WM_PAINT) && (m_ReroutePaint))
{
PAINTSTRUCT ps = new PAINTSTRUCT();
BeginPaint(this.Handle, out ps);
using (Graphics g = Graphics.FromHdc(ps.hdc))
{
using (PaintEventArgs e = new PaintEventArgs(g, new Rectangle(ps.rcPaint.Left, ps.rcPaint.Top, ps.rcPaint.Right - ps.rcPaint.Left, ps.rcPaint.Bottom - ps.rcPaint.Top)))
{
this.OnPaint(e);
}
// HACK: This is supposed to be required...
// but it leaks handles when called!
//g.ReleaseHdc(ps.hdc);
}
EndPaint(this.Handle, ref ps);
return;
}
base.WndProc(ref m);
}
}
}
В случае, если кто-либо, кроме OP, хотел бы проверить это, вот подробности, как запустить его в MFC. Я создал проект MFC SDI без архитектуры просмотра документов с поддержкой элементов управления ActiveX. Это приводит к генерации типичного класса «имя-проекта», класса ChildView и классов MainFrm.
Внутри заголовка ChildView.h добавьте следующий материал заголовка перед классом (но один раз после #pragma). Измените имя библиотеки управления .NET, если у вас другое.
#include <afxwinforms.h>
#using "UserCtrlLibrary1.dll"
using namespace UserCtrlLibrary1;
Добавьте переменную-член для управляющего хоста .NET. Произвольно я разместил свой в разделе Атрибуты.
// Attributes
public:
CWinFormsControl<CircleControl> m_Circle;
Также я добавил обработчики для OnCreate () и OnSize (). Общественная / защищенная видимость может быть скорректирована по мере необходимости.
// Generated message map functions
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
В ChildView.cpp я добавил тела функций для всех элементов, перечисленных выше. Карта сообщений также нуждается в обновлениях, если вы не использовали ClassWizard для добавления обработчиков сообщений Windows.
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP()
void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting
RECT rt;
this->GetClientRect(&rt);
rt.right = (rt.right + rt.left)/2;
dc.FillSolidRect(&rt, RGB(0xFF, 0xA0, 0xA0));
}
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
RECT rt;
this->GetClientRect(&rt);
m_Circle.CreateManagedControl(WS_VISIBLE, rt, this, 1);
return 0;
}
void CChildView::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
RECT rt;
this->GetClientRect(&rt);
m_Circle.MoveWindow(rt.left, rt.top, rt.right - rt.left, (rt.bottom - rt.top)/2, TRUE);
}
Эти изменения создают экземпляр UserControl и привязывают его к верхней половине представления. Обработчик OnPaint () рисует розовую полосу в левой половине вида. Вместе прозрачность должна быть видна в верхнем левом квадранте представления.
Чтобы проект MFC компилировался и выполнялся, копию вывода UserCtrlLibrary1 необходимо поместить в то же место, что и исполняемые файлы для UserCtrlMFCHost. Кроме того, другая копия должна быть помещена в тот же каталог, что и файлы исходного кода проекта для оператора #using. Наконец, проект MFC должен быть изменен для использования скрипта компиляции / clr. В разделе «Свойства конфигурации» в подразделе «Общие» этот переключатель указан в разделе «Параметры проекта по умолчанию».
Следует отметить одну интересную вещь: это позволяет суффиксу ^ для доступа к управляемым классам. В некоторых моментах при разработке этого решения я обсуждал добавление методов, которые будут вызываться только при создании экземпляра из MFC, но, учитывая, что существуют способы обнаружения оконной / неоконной активации, в этом не было необходимости. Тем не менее, другие реализации могут нуждаться в этом, поэтому я считаю, что это хорошо, указывать на это.
Как: скомпилировать код MFC и ATL с / clr