在我目前正在处理的软件产品中,我们有几个3D视图控件。似乎需要在这些3D视图之上提供叠加信息。我不会进入太多的背景细节,因为它不是重点,但这是我们面临的限制:
我们希望能够在这些3D视图之上显示各种叠加信息和控件,因为我们通常通过在大屏幕上显示全屏3D视图来演示我们的产品,而不是显示我们的GUI,它们具有必要的信息和控件。
在我们只使用一种类型的3D视图的日子里,我通过涉及反射的各种黑客来管理我自己的用DirectX编写的覆盖窗口系统(基于WorldWind .NET覆盖小部件,3D视图确实基于在当时的WorldWind上)。 3D View产品的下一个版本对渲染核心代码进行了大量更改,当然也使这些黑客不兼容(是的,我知道了,我知道:-))。此外,我们现在正在使用,因为其他产品的不同需求,另一种类型的3D视图,闭源也是如此。
我强调我们没有源代码,因为我们无法访问渲染循环,因此无法挂钩为3D引擎制作的窗口系统,例如CEGUI(搜索对于你自己,我还不允许发布很多超链接,抱歉)。
因此,我有以下想法:由于我们的3D视图嵌入在winforms控件中,为什么我们不在简单的winforms中编码叠加控件,并将其叠加在3D视图之上?这个解决方案的优势是巨大的:
唯一的轻微(!)问题是我们希望能够管理叠加半透明度,就像我在DirectX中使用以前的系统一样。我们无法承受完全不透明的叠加层,因为它会使视图过于混乱。想象一下像一个几乎看不见的叠加层,当鼠标悬停在它上面时变得更加不透明。
Windows提供了在其他窗口或控件中使用子窗口的可能性(Win32 API在窗口和控件之间并没有真正区别,这几乎是我理解的MFC / WinForms抽象),但是它不是顶级窗口,我们无法调整这些的透明度,所以这不是我们可以使用的东西。我看到here,但这在Windows 8上是可行的,但是很快就无法切换到Windows 8,因为我们的软件部署在很多机器上,运行7。所以我开始了一个激烈的谷歌搜索会议,我怎么能解决这个问题。看来我必须“奴役”#34;顶级窗口到我的3D视图控件。我已经在winforms中直接尝试了类似的东西,通过控件拥有一个表单(不是父级,有明显的区别,在先前链接的MS页面中读取它),并且"跟随"它在屏幕上的动作。正如所料,它有点奏效,但问题很难克服。最重要的是裁剪问题。如果所有者控件的父窗体更改其大小,则叠加窗体仍会完整显示。在我的示例中,我有一个带有菜单的简单表单,以及一个包含日历的黑色面板(用于显示子控件和拥有控件之间的剪辑差异)。我被奴役了#34;包含黑色面板属性网格的无边框表单。它成功地跟随表单移动,但如果我收缩主表单,我得到这个:
请注意日历是如何正确剪辑的(子窗口),而叠加不是(拥有窗口)。在最小化/恢复主窗体时,我也会得到奇怪的效果。实际上,我的叠加在最小化时会消失,但在恢复时,它只会产生"而主要表格的恢复动画正在发生。但这不是一个问题,我想可以通过处理适当的事件(但是哪些事件?)来解决。
根据我的理解,我必须使用win32 API调用和钩子自己处理至少一些剪辑。我已经开始记录自己,但这是相当复杂的事情。 Win32 API是一个真正的混乱,我自己是一个以前的Unix开发人员通过伟大的.NET框架介绍到Windows编程,我没有任何真正的Win32经验,因此不知道在哪里开始吧,以及如何让自己成为丛林中的一条道路......
因此,如果一个winapi大师路过,或者如果有人有其他想法在上述限制条件下实现我的目标,我会很高兴看到它: - )
提前致谢,并且对于成为这样一个stackoverflow" leecher"而道歉。订阅只是问一个问题,但我的工作站上没有直接的互联网访问权限(是的,真的,我必须去特定的计算机),所以参与这个伟大的社区不是那样的对我来说很容易。
最后,这是我的示例代码(如果你问,可以使用设计师代码):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Point _myLoc;
private void formToolStripMenuItem_Click(object sender, EventArgs e)
{
var ctrl = new PropertyGrid();
var obj = this.panel1;
ctrl.SelectedObject = obj;
var form = new Form();
ctrl.Dock = DockStyle.Fill;
form.Controls.Add(ctrl);
form.Opacity = 0.7;
var rect = obj.RectangleToScreen(obj.DisplayRectangle);
form.StartPosition = FormStartPosition.Manual;
form.Location = new Point(rect.Left + 10, rect.Top + 10);
var parentForm = this;
_myLoc = parentForm.Location;
form.FormBorderStyle = FormBorderStyle.None;
parentForm.LocationChanged += (s, ee) => {
int deltaX = parentForm.Location.X - _myLoc.X;
int deltaY = parentForm.Location.Y - _myLoc.Y;
var loc = form.Location;
form.Location = new Point(loc.X + deltaX, loc.Y + deltaY);
_myLoc = parentForm.Location;
};
form.Show(this.panel1);
}
}
答案 0 :(得分:1)
使用Region
属性可以轻松实现剪切。每个窗口都可以有一个关联的Region
对象,它定义了窗口渲染约束:
static void ManualClipping(Control clipRegionSource, Form formToClip)
{
var rect = clipRegionSource.DisplayRectangle;
rect = clipRegionSource.RectangleToScreen(rect);
rect = formToClip.RectangleToClient(rect);
rect = Rectangle.Intersect(rect, formToClip.ClientRectangle);
if(rect == formToClip.ClientRectangle)
{
formToClip.Region = null;
}
else
{
formToClip.Region = new Region(rect);
}
}
用法:
/* ... */
parentForm.SizeChanged += (s, ee) => ManualClipping(panel1, form);
form.Show(this.panel1);