インターネット上で解決策を見つけることができないと確信しています。ただし、これについてデモを試してみましたが、かなりうまく機能します。
winforms
および他の多くの UI テクノロジでは、ウィンドウ自体の外に何かをレンダリングすることはできません。必要な効果を得るには、ユーザーがウィンドウのサイズを変更する方法に応じて、ウィンドウの外側または内側に何らかの指示的な境界線をレンダリングする必要があります。行き詰まっているように見えますか?
しかし、それを行うための一種のテクニックがあります(私はそれをレイヤーテクニックと呼んでいます)。指標となる境界線をレンダリングするには、透明でフォーカスされていないレイヤーが必要です。このレイヤーは、メイン ウィンドウのand (わずかなオフセット)Size
とLocation
同期されます。レイヤーもデフォルトで表示され、ユーザーがサイズ変更したときにのみ表示され、サイズ変更が終了すると非表示になります。Size
Location
invisible
それは私が言及したテクニックでかなりOKです。ただし、ユーザーがウィンドウのサイズを変更するときにデフォルトのサイズ変更を防止/破棄するにはどうすればよいですか? 幸いなことWin32
に、これを簡単に行うために 2 つのメッセージがサポートされています。
- WM_RESIZING : ユーザーが開始してサイズ変更を続けると、ウィンドウに送信されます。サイズ変更時に現在のウィンドウの構造を保持
LParam
します。RECT
この情報を読み取って、指示境界線を正しくレンダリングします。次に、これRECT
を現在Bounds
のウィンドウに変更して、デフォルトのサイズ変更効果を破棄する必要があります (サイズと位置はすぐに変更されます)。
- WM_EXITSIZEMOVE : サイズ変更または移動が終了したときにウィンドウに送信されます。このメッセージをキャッチして、透明レイヤーの and に基づいてウィンドウの and を割り当て、もちろんレイヤーを非表示にする必要があり
Size
ます。Location
Size
Location
今、問題は完全に解決可能です。これが私が作ったデモコードです。ここには非常に厄介で解決不可能で理解できないバグがあることに注意してください。Top-Left
コーナーのサイズを変更すると発生し、Size
はマウスを放した後に正しく更新されますが、Location
はオフセットで設定されます。デバッグしましたが、うまくいきません。ある時点で、Top
とが明確な理由もなくLeft
予期しない値にジャンプします。ただし、全辺(左、上、右、下)、その他の角のリサイズはOKです。実際、ユーザーがサイズ変更を行うことはほとんどないため、この解決策は受け入れられると思います。Top-Left corner
//Must add using System.Runtime.InteropServices;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//Sizing border initialization
SizingBorderWidth = 3;
SizingBorderStyle = DashStyle.Custom;
SizingBorderColor = Color.Orange;
//layer initialization
layer.Owner = this;//especially this one.
layer.Width = Width + SizingBorderWidth * 2;
layer.Height = Height + SizingBorderWidth * 2;
//Paint the border when sizing
layer.Paint += (s, e) => {
using (Pen p = new Pen(SizingBorderColor) { Width = SizingBorderWidth }) {
if (Use3DSizingBorder) {
ControlPaint.DrawBorder3D(e.Graphics, sizingRect.Left, sizingRect.Top, sizingRect.Width, sizingRect.Height, Border3DStyle.Bump, Border3DSide.All);
}
else {
p.DashStyle = SizingBorderStyle;
p.LineJoin = LineJoin.Round;
if(p.DashStyle == DashStyle.Custom)
p.DashPattern = new float[] { 8f, 1f, 1f, 1f };//length of each dash from right to left
e.Graphics.DrawRectangle(p, sizingRect);
}
}
};
//Bind the Location of the main form and the layer form together
LocationChanged += (s, e) => {
Point p = Location;
p.Offset(-SizingBorderWidth, -SizingBorderWidth);
layer.Location = p;
};
//Set the intial Location of layer
Load += (s, e) =>{
Point p = Location;
p.Offset(-SizingBorderWidth, -SizingBorderWidth);
layer.Location = p;
};
}
//Set this to true to use 3D indicative/preview border
public bool Use3DSizingBorder { get; set; }
//Change the indicative/preview border thickness
public int SizingBorderWidth { get; set; }
//Change the indicative/preview border style
public DashStyle SizingBorderStyle { get; set; }
//Change the indicative/preview border color
public Color SizingBorderColor { get; set; }
//hold the current sizing Rectangle
Rectangle sizingRect;
bool startSizing;
bool suppressSizing;
//This is a Win32 RECT struct (don't use Rectangle)
public struct RECT
{
public int left, top, right, bottom;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x214&&!suppressSizing)//WM_SIZING = 0x214
{
RECT rect = (RECT) m.GetLParam(typeof(RECT));
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
sizingRect = new Rectangle() {X = SizingBorderWidth/2, Y = SizingBorderWidth/2,
Width = w, Height = h};
layer.Left = rect.left-SizingBorderWidth;
layer.Top = rect.top-SizingBorderWidth;
layer.Width = w+2*SizingBorderWidth;
layer.Height = h+2*SizingBorderWidth;
if (!startSizing)
{
layer.Show();
startSizing = true;
}
layer.Invalidate();
//Keep the current position and size fixed
rect.right = Right;
rect.bottom = Bottom;
rect.top = Top;
rect.left = Left;
//---------------------------
Marshal.StructureToPtr(rect, m.LParam, true);
}
if (m.Msg == 0x232)//WM_EXITSIZEMOVE = 0x232
{
layer.Visible = false;
BeginInvoke((Action)(() => {
suppressSizing = true;
Left = layer.Left + SizingBorderWidth;
Top = layer.Top + SizingBorderWidth;
Width = layer.Width - 2 * SizingBorderWidth;
Height = layer.Height - SizingBorderWidth * 2;
suppressSizing = false;
}));
startSizing = false;
}
base.WndProc(ref m);
}
//Here is the layer I mentioned before.
NoActivationForm layer = new NoActivationForm();
}
public class NoActivationForm : Form {
public NoActivationForm() {
//The following initialization is very important
TransparencyKey = BackColor;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
//----------------------------------------------
}
protected override bool ShowWithoutActivation {
get { return true; }
}
}
いくつかのスクリーン ショット:
編集:(この編集はHodaya Shalom
、OP(奇妙な:)によって提案されました
左隅の問題の解決策を見つけました:
BeginInvoke の前に変数を保存し、呼び出しでローカル変数を配置します。
int _top = layer.Top + SizingBorderWidth;
int _left = layer.Left + SizingBorderWidth;
int _width = layer.Width - 2 * SizingBorderWidth;
int _height = layer.Height - SizingBorderWidth * 2;
BeginInvoke((Action)(() => {
suppressSizing = true;
Left = _left;
Top = _top;
Width =_width;
Height =_height;
suppressSizing = false;
}));