CefSharp 中的可拖动无边框 window
Draggable borderless window in CefSharp
我想在某些 HTML 元素上使用拖动逻辑实现无边框 window。我找到了一些工作示例(比如 frameless window for Chrome),这是我尝试过的:
.title-area
{
-webkit-app-region: drag;
}
<div class='title-area'>
A draggable area
</div>
然后,在 C# 代码中我实现了 IDragHandler class:
internal class PromtDragHandler : IDragHandler
{
bool IDragHandler.OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask)
{
return false;
}
void IDragHandler.OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions)
{
}
}
方法 OnDraggableRegionsChanged 在开始时触发一次,OnDragEnter 在我拖动元素的某些文本时触发 "title-area"。但我不确定下一步该怎么做才能让我的 window 移动?
更新。 如评论中所述,CefTestApp 支持此拖动功能。在源代码中,我们有从 DragHandler 调用的方法 OnSetDraggableRegions:
void RootWindowWin::OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) {
REQUIRE_MAIN_THREAD();
// Reset draggable region.
::SetRectRgn(draggable_region_, 0, 0, 0, 0);
// Determine new draggable region.
std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
for (;it != regions.end(); ++it) {
HRGN region = ::CreateRectRgn(
it->bounds.x, it->bounds.y,
it->bounds.x + it->bounds.width,
it->bounds.y + it->bounds.height);
::CombineRgn(
draggable_region_, draggable_region_, region,
it->draggable ? RGN_OR : RGN_DIFF);
::DeleteObject(region);
}
// Subclass child window procedures in order to do hit-testing.
// This will be a no-op, if it is already subclassed.
if (hwnd_) {
WNDENUMPROC proc = !regions.empty() ?
SubclassWindowsProc : UnSubclassWindowsProc;
::EnumChildWindows(
hwnd_, proc, reinterpret_cast<LPARAM>(draggable_region_));
}
}
我还是不太明白,关于可拖动区域(开始时只触发一次)的信息究竟如何帮助移动window?有人可以向我解释这个逻辑或提供此代码的 C# 等效项吗?
更新2。我做到了。 这是我添加到表单代码中的内容:
IntPtr DragableRegionNative = Native.CreateRectRgn(0, 0, 0, 0);
void RegionsChangedCallback(DraggableRegion[] Regions)
{
Native.SetRectRgn(DragableRegionNative, 0, 0, 0, 0);
if (Regions == null)
return;
foreach (var Region in Regions)
{
var RegionNative = Native.CreateRectRgn(
Region.X, Region.Y,
Region.X + Region.Width,
Region.Y + Region.Height);
Native.CombineRgn(DragableRegionNative, DragableRegionNative, RegionNative,
Region.Draggable ? (int)Native.CombineRgnStyles.RGN_OR : (int)Native.CombineRgnStyles.RGN_DIFF);
Native.DeleteObject(RegionNative);
}
}
Point dragOffset = new Point();
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
dragOffset = this.PointToScreen(e.Location);
dragOffset.X -= Location.X;
dragOffset.Y -= Location.Y;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left)
{
Point newLocation = this.PointToScreen(e.Location);
newLocation.X -= dragOffset.X;
newLocation.Y -= dragOffset.Y;
Location = newLocation;
}
}
void chromewb_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e)
{
if (chromewb.IsBrowserInitialized)
{
ChromeWidgetMessageInterceptor.SetupLoop(chromewb, (m) =>
{
if (m.Msg == (int)Native.WM.WM_LBUTTONDOWN)
{
var point = Native.ParsePoint(m.LParam.ToInt32());
if (Native.PtInRegion(DragableRegionNative, point.X, point.Y))
this.InvokeEx(() => Native.PostMessage(this.Handle, (uint)m.Msg, m.WParam, m.LParam));
}
});
}
}
如你所见,从chrome浏览器拦截WM_LBUTTONDOWN事件就足够了,然后检查鼠标点是否属于标题区域,如果是,则将此消息发送到主窗体.一旦表单获得 WM_LBUTTONDOWN 事件,build-in 表单方法 OnMouseDown 和 OnMouseMove 就会完成其他工作。
看看:https://github.com/qwqcode/CefSharpDraggableRegion
您可以在 CSS 中指定 -webkit-app-region: drag
来告诉 CefSharp 哪些区域是可拖动的(就像 OS 的标准标题栏)。
我想在某些 HTML 元素上使用拖动逻辑实现无边框 window。我找到了一些工作示例(比如 frameless window for Chrome),这是我尝试过的:
.title-area
{
-webkit-app-region: drag;
}
<div class='title-area'>
A draggable area
</div>
然后,在 C# 代码中我实现了 IDragHandler class:
internal class PromtDragHandler : IDragHandler
{
bool IDragHandler.OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask)
{
return false;
}
void IDragHandler.OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions)
{
}
}
方法 OnDraggableRegionsChanged 在开始时触发一次,OnDragEnter 在我拖动元素的某些文本时触发 "title-area"。但我不确定下一步该怎么做才能让我的 window 移动?
更新。 如评论中所述,CefTestApp 支持此拖动功能。在源代码中,我们有从 DragHandler 调用的方法 OnSetDraggableRegions:
void RootWindowWin::OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) {
REQUIRE_MAIN_THREAD();
// Reset draggable region.
::SetRectRgn(draggable_region_, 0, 0, 0, 0);
// Determine new draggable region.
std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
for (;it != regions.end(); ++it) {
HRGN region = ::CreateRectRgn(
it->bounds.x, it->bounds.y,
it->bounds.x + it->bounds.width,
it->bounds.y + it->bounds.height);
::CombineRgn(
draggable_region_, draggable_region_, region,
it->draggable ? RGN_OR : RGN_DIFF);
::DeleteObject(region);
}
// Subclass child window procedures in order to do hit-testing.
// This will be a no-op, if it is already subclassed.
if (hwnd_) {
WNDENUMPROC proc = !regions.empty() ?
SubclassWindowsProc : UnSubclassWindowsProc;
::EnumChildWindows(
hwnd_, proc, reinterpret_cast<LPARAM>(draggable_region_));
}
}
我还是不太明白,关于可拖动区域(开始时只触发一次)的信息究竟如何帮助移动window?有人可以向我解释这个逻辑或提供此代码的 C# 等效项吗?
更新2。我做到了。 这是我添加到表单代码中的内容:
IntPtr DragableRegionNative = Native.CreateRectRgn(0, 0, 0, 0);
void RegionsChangedCallback(DraggableRegion[] Regions)
{
Native.SetRectRgn(DragableRegionNative, 0, 0, 0, 0);
if (Regions == null)
return;
foreach (var Region in Regions)
{
var RegionNative = Native.CreateRectRgn(
Region.X, Region.Y,
Region.X + Region.Width,
Region.Y + Region.Height);
Native.CombineRgn(DragableRegionNative, DragableRegionNative, RegionNative,
Region.Draggable ? (int)Native.CombineRgnStyles.RGN_OR : (int)Native.CombineRgnStyles.RGN_DIFF);
Native.DeleteObject(RegionNative);
}
}
Point dragOffset = new Point();
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
dragOffset = this.PointToScreen(e.Location);
dragOffset.X -= Location.X;
dragOffset.Y -= Location.Y;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left)
{
Point newLocation = this.PointToScreen(e.Location);
newLocation.X -= dragOffset.X;
newLocation.Y -= dragOffset.Y;
Location = newLocation;
}
}
void chromewb_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e)
{
if (chromewb.IsBrowserInitialized)
{
ChromeWidgetMessageInterceptor.SetupLoop(chromewb, (m) =>
{
if (m.Msg == (int)Native.WM.WM_LBUTTONDOWN)
{
var point = Native.ParsePoint(m.LParam.ToInt32());
if (Native.PtInRegion(DragableRegionNative, point.X, point.Y))
this.InvokeEx(() => Native.PostMessage(this.Handle, (uint)m.Msg, m.WParam, m.LParam));
}
});
}
}
如你所见,从chrome浏览器拦截WM_LBUTTONDOWN事件就足够了,然后检查鼠标点是否属于标题区域,如果是,则将此消息发送到主窗体.一旦表单获得 WM_LBUTTONDOWN 事件,build-in 表单方法 OnMouseDown 和 OnMouseMove 就会完成其他工作。
看看:https://github.com/qwqcode/CefSharpDraggableRegion
您可以在 CSS 中指定 -webkit-app-region: drag
来告诉 CefSharp 哪些区域是可拖动的(就像 OS 的标准标题栏)。