问题描述
我似乎拿到了一张"不清楚我在问什么"的选票。我想自定义绘制一个组合框样式的控件。弹出打开部分需要在控件本身的边界之外绘制。我不能使用组合框-想象一下类似于Word功能区中的图库控件。
我想了两种方法:
- 将弹出的打开面板向上传递到要呈现的窗体。
- 使用无边框、无框架窗体或NativeWindow。
后者还允许下拉菜单脱离窗口的边界,这可能有用,但不是绝对必要的。
有没有其他方法可以解决这个问题?您认为哪种方法最好?
谢谢。
原问题:
我正在为一个小项目编写一个基于WinForms的自定义绘图UI库。大体上一切都很顺利,但我在离开Graphics
对象边界的下拉列表中遇到了一些轻微的结构问题。
大多数控件使用纯自定义绘制和重绘事件模型完成,但总体界面使用WinFormsDock
、Width
、Height
等进行布局。
(我本希望在So上找到类似的东西,但没有成功。)
我已经解决了这个问题,让窗体控制下拉覆盖图的绘制,但是使用自定义鼠标处理程序和其他所有东西,窗体开始感觉负担过重。
我曾尝试存储对Graphics
对象的引用,但发现在被引发的OnPaint
之外使用它们是..喜怒无常。
当前模型的简化代码示例如下。此代码不能单独以任何有用的方式运行,但显示了用于显示覆盖的方法。
public interface IDropDownOverlay
{
DropDown DropDown { get; }
/// <summary>can only link to a single form at once - not a problem.</summary>
DropDownDrawForm Form { get; set; }
void MouseUpdate(MouseEventArgs e);
void Render(Graphics gfx);
void Show();
}
public class DropDown
{
private DropDownOverlay overlay;
}
public class DropDownDrawForm : Form
{
/* lots of other things... */
private List<IDropDownOverlay> overlays;
public void HideOverlay(IDropDownOverlay overlay)
{
if (this.overlays.Contains(overlay))
{
this.overlays.Remove(overlay);
this.Invalidate();
}
}
public void ShowOverlay(IDropDownOverlay overlay)
{
if (!this.overlays.Contains(overlay))
{
overlay.Form = this;
this.overlays.Add(overlay);
this.Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
foreach (IDropDownOverlay overlay in this.overlays)
{
overlay.Render(e.Graphics);
}
}
private void MouseUpdate(MouseEventArgs e)
{
foreach (IDropDownOverlay overlay in this.overlays)
{
overlay.MouseUpdate(e);
}
}
}
public class DropDownOverlay : IDropDownOverlay
{
public DropDown DropDown { get; }
public DropDownDrawForm Form { get; set; }
public void Hide()
{
this.Form.HideOverlay(this);
}
public void MouseUpdate(MouseEventArgs e)
{
// Informs the form to redraw the area of the control.
if (stateChanged)
{
this.Invalidate(); // (method to invalidate just this area)
}
}
public void Show()
{
this.Form.ShowOverlay(this);
}
public void Render(Graphics gfx)
{
}
}
很明显,这里遗漏了很多内容,但它至少应该显示我正在使用的方法。
有什么建议可以防止我在表单之间来回传递此信息?
谢谢
更新:
明确地说,问题是绘制下拉列表的"Popup"部分,而不是下拉列表本身。(此处使用组合框进行演示)我还记得,小窗口会强制组合框超出窗口的边界。
其上的投影在我看来与CreateParams
中的CS_DROPSHADOW
可疑-我是否可以使用NativeWindow
子类来处理此问题?
推荐答案
我想我已经确定了第二个选项,它使用第二个表单来显示下拉面板。我使用的是扩展的窗体类,而不是NativeWindow。我只是想我应该分享结果,以防其他人尝试同样的事情并发现这个。
选择下拉列表后,我使用PointToScreen
设置表单以获取坐标。它还设置了以下属性:
this.ShowIcon = false;
this.ControlBox = false;
this.MinimizeBox = false;
this.MaximizeBox = false;
this.ShowInTaskbar = false;
this.FormBorderStyle = FormBorderStyle.None;
只是为了确保它不会出现在任何地方。我还添加了以下事件处理程序:
this.LostFocus += delegate
{
this.dropdown.BlockReopen(200);
this.dropdown.Close();
};
这意味着一旦失去焦点,它就会关闭,并且还会调用一个方法来阻止下拉菜单在200毫秒内重新打开。我对此并不完全满意,但它一下子解决了这么多问题,可能会持续一段时间。我还通过覆盖CreateParams添加了一个投影:
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ClassStyle |= Win32Message.CS_DROPSHADOW;
return createParams;
}
}
我的快速测试床应用程序的最终结果:
通过这种方式,我也可以使下拉菜单脱离窗口的边界:
我现在唯一的问题--当你打开每个窗口时,窗口框架就会失去焦点,这有点刺耳。我可以通过重写ShowWithoutActivation
返回TRUE来解决这个问题,但这样LostFocus
处理程序就不起作用了。
现在更烦人了,但非常欢迎您提出解决这个问题的建议!
这篇关于控制边界外的自定义绘制下拉面板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!