Custom paint Splitter control in C# WinForms - c#

I am trying to paint the split line that appears when you drag a splitter control:
As you can see from this image, the default splitter is a checkerboard.
...this doesn't work:
public partial class MockForm : Form
{
public MockForm()
{
InitializeComponent();
this.splitter1.Paint += splitter1_Paint;
}
private void splitter1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Red);
}
}
this just paints the background of the control but not the splitter when it's dragged.
Any ideas?

The answer posted by LarsTech is really good, But the handler flickers are somehow annoying. Instead of showing the control in Form, if you show a Form as splitter handler and show it above the Container of splitter, the flickers will be gone.
HighLight f = new HighLight() { BackColor = Color.Red };
private void splitter1_SplitterMoving(object sender, SplitterEventArgs e)
{
this.splitter1.Parent.Refresh();
f.Location = this.splitter1.Parent.PointToScreen(new Point(e.SplitX, e.SplitY));
f.Size = this.splitter1.Size;
if (!f.Visible)
f.ShowInactiveTopmost();
}
private void splitter1_SplitterMoved(object sender, SplitterEventArgs e)
{
f.Hide();
}
Here is the form which I used as highlight:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class HighLight : Form
{
public HighLight()
{
Opacity = 0;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
}
protected override void OnDeactivate(EventArgs e)
{
base.OnDeactivate(e);
this.Hide();
}
private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(int hWnd, int hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public void ShowInactiveTopmost()
{
ShowWindow(this.Handle, SW_SHOWNOACTIVATE);
SetWindowPos(this.Handle.ToInt32(), HWND_TOPMOST,
this.Left, this.Top, this.Width, this.Height,
SWP_NOACTIVATE);
this.Opacity = 1;
}
}
To see a custom splitter which supports transparent handler take a look at this related post. In the other post I created a new splitter control using source codes of original splitter, but changed rendering the highlight:
Change Splitter Highlighting/Resize Line

The old Splitter control uses a private painting method to produce that checkerboard effect, so there isn't any thing you can override to replace that.
You can fake it by dragging your own control in the space of the checkerboard control you see on the screen. This may produce some flicker:
Control draggingControl = new Control { BackColor = Color.Green, Visible = false };
public MockForm() {
InitializeComponent();
this.Controls.Add(draggingControl);
splitter1.SplitterMoving += splitter1_SplitterMoving;
splitter1.SplitterMoved += splitter1_SplitterMoved;
}
void splitter1_SplitterMoving(object sender, SplitterEventArgs e) {
draggingControl.Bounds = new Rectangle(new Point(e.X - (e.X - e.SplitX), 0),
splitter1.Size);
if (!draggingControl.Visible) {
draggingControl.Visible = true;
draggingControl.BringToFront();
}
this.Refresh();
}
void splitter1_SplitterMoved(object sender, SplitterEventArgs e) {
draggingControl.Visible = false;
this.Refresh();
}
The Splitter control was deprecated in favor of the SplitContainer control.

Related

How to draw my own tooltip without shadow?

I am trying to draw my own tooltip. But I cant get rid of the standard shadow.
Its a standard WinForm application, with lots of forms. So therefore is
Application.EnableVisualStyles();
called, and needed, when the application starts. If I comment out this line, it works. I made a minimal WinForm app below. If EnableVisualStyles is commented out, it draws a red rectangle only. When I uncomment it, it draws a red rectangle with a shadow.
Does anyone know ho to solve this? How to have Application.EnableVisualStyles(), and have a tooltip 100% OwnerDrawn, without any standard shadows?
Minimal WinForm app is here:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ToolTipExample
{
public class MainForm : Form
{
[STAThread]
static void Main()
{
// Comment out below line and it works.
Application.EnableVisualStyles();
Application.Run(new MainForm());
}
private ToolTip toolTip;
private Button button;
public MainForm()
{
toolTip = new ToolTip();
toolTip.OwnerDraw = true;
toolTip.Draw += new DrawToolTipEventHandler(toolTip1_Draw);
toolTip.Popup += new PopupEventHandler(toolTip1_Popup);
button = new Button();
button.Location = new Point(25, 25);
button.Text = "Button";
toolTip.SetToolTip(button, "Button tip text");
Controls.AddRange(new Control[] { button });
}
private void toolTip1_Popup(object sender, PopupEventArgs e)
{
e.ToolTipSize = new Size(100, 100);
}
private void toolTip1_Draw(System.Object sender, DrawToolTipEventArgs e)
{
e.Graphics.FillRectangle(new SolidBrush(Color.Red), e.Bounds);
}
}
}
You can get the class style of the ToolTip using GetClassLong and then remove CS_DROPSHADOW style from it and set the class style for the ToolTip again:
//using System.Runtime.InteropServices;
public const int GCL_STYLE = -26;
public const int CS_DROPSHADOW = 0x20000;
[DllImport("user32.dll", EntryPoint = "GetClassLong")]
public static extern int GetClassLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetClassLong")]
public static extern int SetClassLong(IntPtr hWnd, int nIndex, int dwNewLong);
private void toolTip1_Popup(object sender, PopupEventArgs e)
{
e.ToolTipSize = new Size(100, 100);
var hwnd = (IntPtr)typeof(ToolTip).GetProperty("Handle",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance).GetValue(toolTip);
var cs = GetClassLong(hwnd, GCL_STYLE);
if ((cs & CS_DROPSHADOW) == CS_DROPSHADOW)
{
cs = cs & ~CS_DROPSHADOW;
SetClassLong(hwnd, GCL_STYLE, cs);
}
}

Windows Forms MouseEnter not firing after control moved

I want to color the BackColor property of a PictureBox when the mouse enters it.
I turn the BackColor into Yellow when then MouseEnter event fires, and I reset to transparent in MouseLeave.
Then when I click on a PictureBox, I change its position, so I also have a Move event which resets it in transparent.
The problem is, once I moved it, I need to enter the PictureBox twice with the mouse to fire the MouseEnter event!
It's a very graphical problem, so I uploaded a little video to show you what's happening, it surely will explain my problem better than I do.
I tried another way, changing the color not in MouseEnter but in MouseHover. In this case, it works well, except that I have a 500ms delay before firing the Move event, which is obviously not what I want.
I have no viable solution right now.
For the code, it's very simple, for the moment I have :
private void pictureBoxMouseUp(object sender, MouseEventArgs e)
{
    // I move the PictureBox here
}
 
private void pictureBoxMove(object sender, EventArgs e)
{
    (sender as PictureBox).BackColor = Color.Transparent;
}
 
private void pictureBoxMouseEnter(object sender, MouseEventArgs e)
{
    (sender as PictureBox).BackColor = Color.LightYellow;
}
 
private void pictureBoxMouseLeave(object sender, MouseEventArgs e)
{
    (sender as PictureBox).BackColor = Color.Transparent;
}
In the Designer.cs, my events for each PictureBox are like :
this.pictureBox2.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBoxMouseDown);
this.pictureBox2.MouseEnter += new System.EventHandler(this.pictureBoxMouseEnter);
this.pictureBox2.MouseLeave += new System.EventHandler(this.pictureBoxMouseLeave);
this.pictureBox2.MouseUp += new System.Windows.Forms.MouseEventHandler(this.pictureBoxMouseUp);
this.pictureBox2.Move += new System.EventHandler(this.pictureBoxMove);
EDIT : To answer my question, this is the code I use now : (comments are in french)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Diagnostics;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using EmoTEDTherapeute;
namespace ControlSceneImage {
public class SceneImage : PictureBox {
public static readonly int defaultWidth = 100;
public static readonly int defaultHeight = 100;
static readonly int activePbPosY; // Position en Y des scènes actives
static readonly Dictionary<string, Point> scenesPos = null; // Invariant, stocke la position initiale des PictureBox
static readonly List<Point>[] activeScenesPos = null; // Invariant, stocke les positions des PictureBox en fonction du nombre de scènes actives
static List<SceneImage> activeScenes = null;
const int maxActiveScenes = 5;
const int ecart = 80;
const int decalage = 15;
const int panelScenesWidth = 909;
const int panelScenesHeight = 154;
const int panelScenesLocationX = 35;
const int panelScenesLocationY = 36;
bool isActive;
static SceneImage() {
// Constructeur initialisant tous les membres statiques, n'est appelé qu'une seule fois, avant tout le reste
activePbPosY = (panelScenesLocationY + panelScenesHeight - (int)(0.6 * defaultHeight)) / 2;
scenesPos = new Dictionary<string, Point>();
activeScenesPos = new List<Point>[maxActiveScenes];
for (int i = 0; i < maxActiveScenes; i++) {
activeScenesPos[i] = CalcActiveScenesPos(i+1);
}
activeScenes = new List<SceneImage>();
}
public SceneImage() {
MouseEnter += new EventHandler(OnMouseEnter);
MouseDown += new MouseEventHandler(OnMouseDown);
MouseUp += new MouseEventHandler(OnMouseUp);
MouseLeave += new EventHandler(OnMouseLeave);
BorderStyle = BorderStyle.FixedSingle;
Size = new Size(defaultWidth, defaultHeight);
SizeMode = PictureBoxSizeMode.Zoom;
isActive = false;
}
private static List<Point> CalcActiveScenesPos(int nbActiveScenes) {
List<Point> ret = new List<Point>();
for (int i = 0; i < nbActiveScenes; i++) {
ret.Add(new Point((panelScenesLocationX + panelScenesWidth + ecart) / 2 + (int)((ecart + defaultWidth) * (i - nbActiveScenes / 2.0)) + decalage, activePbPosY));
}
return ret;
}
private void UpdateScenesPos() {
for(int i = 0; i < activeScenes.Count; i++) {
activeScenes[i].Location = activeScenesPos[activeScenes.Count - 1][i];
}
}
private void OnMouseEnter(object sender, EventArgs e) {
BackColor = Color.LightYellow;
Cursor = Cursors.Hand;
}
private void OnMouseDown(object sender, MouseEventArgs e) {
BorderStyle = BorderStyle.Fixed3D;
}
private void OnMouseUp(object sender, MouseEventArgs e) {
if (!scenesPos.ContainsKey(Name)) {
// Si ce n'est pas déjà fait, on stocke la position initiale de notre PictureBox
scenesPos.Add(Name, Location);
// Et on crée un Panel vide sous elle
Panel panel = new Panel();
panel.Location = Location;
panel.Size = Size;
panel.BorderStyle = BorderStyle.Fixed3D;
// On ajoute notre panel à notre form
Form1.AddInSeance(panel);
}
if (!isActive) {
activeScenes.Add(this);
} else {
Location = scenesPos[Name];
activeScenes.Remove(this);
}
isActive = !isActive;
UpdateScenesPos();
}
private void OnMouseLeave(object sender, EventArgs e) {
BorderStyle = BorderStyle.FixedSingle;
BackColor = Color.Transparent;
}
}
}
I use the same method as before, and for an unknown reason, now it works.
Thanks to all of you for helping me :)
The problem is that when you relocate the PictureBox, it will not receive the mouse leave event. Maybe you have already noticed that, that's why you set the BackColor in the Move event, too.
After you moved the control, it will not receive a second MouseEnter when you hover it again, only after you moved the mouse off and on again.
Try to send a mouse leave event manually (I haven't tested it):
private const int WM_MOUSELEAVE = 0x02A3;
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private void pictureBoxMouseUp(object sender, MouseEventArgs e)
{
// move the PictureBox...
SendMessage(((PictureBox)sender).Handle, WM_MOUSELEAVE, IntPtr.Zero, IntPtr.Zero);
}
I will avoid to move the PictureBox. Clearly your bug come from that.
When you move the component, the mouse is no more in it but it status is not updated.
You can swim deep in the windows form reference code or you can just says you have N small preview (the pictures on the bottom line) and one big preview (the upper one).
Create N+1 picture box and don't change it. Just change their image property.
When a small preview is clicked, switch it's image property with the big preview.
Also, a good MVC pattern is recommended.

Size RichTextBox according to contents

This code automatically sizes a RichTextBox according to it's contents. I'm having issues, especially with tables. \t may be ignored. I tried a managed solution, now I'm trying platform invoke. Current Output:
[DllImport("gdi32.dll")]
static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int cbString, out SIZE lpSize);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetDC(IntPtr hWnd);
[StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;
public SIZE(int cx, int cy)
{
this.cx = cx;
this.cy = cy;
}
}
public static void Main()
{
Form form = new Form();
RichTextBox rtfBox = new RichTextBox();
rtfBox.Rtf = #"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil Arial;}}\viewkind4\uc1\trowd\trgaph100\cellx1000\cellx2000\pard\intbl\lang1033\f0\fs20 hi\cell bye\cell\row\intbl one\cell two\cell\row\pard\par}";
rtfBox.ScrollBars = RichTextBoxScrollBars.None;
string sInput = "hi\t bye\t\n";// one\t two\t\n";
SIZE CharSize;
form.Controls.Add(rtfBox);
IntPtr hdc = GetDC(IntPtr.Zero);//Context for entire screen
GetTextExtentPoint32(hdc, sInput, sInput.Length, out CharSize);
rtfBox.Width = CharSize.cx;
rtfBox.Height = CharSize.cy;
form.Visible = false;
form.ShowDialog();
}
(Note, for simplicity this is a console application with a reference to System.Windows.Forms.dll)
Have you looked at the ContentsResized event? Add the following method to be called when the event fires:
private void richTextBox_ContentsResized(object sender, ContentsResizedEventArgs e)
{
var richTextBox = (RichTextBox) sender;
richTextBox.Width = e.NewRectangle.Width;
richTextBox.Height = e.NewRectangle.Height;
}
When the RTF content is changed (using Rtf), the RichTextBox should be resized to match its contents. Make sure you also set the WordWrap property to false.
I've tried it with your table example and it does appear to work (albeit with a little offset, which you could possibly solve by adding a few pixels of width to the adjusted size - not sure why that happens):
P.Brian.Mackey EDIT
This answer worked for me. To clarify, here's the final code including border fix:
public static void Main()
{
string sInput = "hi\t bye\t\n";// one\t two\t\n";
SIZE CharSize;
Form form = new Form();
RichTextBox rtfBox = new RichTextBox();
rtfBox.ContentsResized += (object sender, ContentsResizedEventArgs e) =>
{
var richTextBox = (RichTextBox)sender;
richTextBox.Width = e.NewRectangle.Width;
richTextBox.Height = e.NewRectangle.Height;
rtfBox.Width += rtfBox.Margin.Horizontal + SystemInformation.HorizontalResizeBorderThickness;
};
rtfBox.WordWrap = false;
rtfBox.ScrollBars = RichTextBoxScrollBars.None;
rtfBox.Rtf = #"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil Arial;}}\viewkind4\uc1\trowd\trgaph100\cellx1000\cellx2000\pard\intbl\lang1033\f0\fs20 hi\cell bye\cell\row\intbl one\cell two\cell\row\pard\par}";
form.Controls.Add(rtfBox);
form.ShowDialog();
}
It's much easier to use GetPreferredSize, as described in this answer. Then you don't need to wait for a ContentsResized event,
What about the Height ?
I added
richTextBox.Height += richTextBox.Margin.Vertical +
SystemInformation.VerticalResizeBorderThickness;
at the end.
It also looks like a good candidate for an extension method :
static public class RichTextBoxResizer {
static public void ResizeToContents(this RichTextBox richTextBox, ContentsResizedEventArgs e) {
richTextBox.Width = e.NewRectangle.Width;
richTextBox.Height = e.NewRectangle.Height;
richTextBox.Width += richTextBox.Margin.Horizontal +
SystemInformation.HorizontalResizeBorderThickness +
SystemInformation.HorizontalScrollBarThumbWidth;
richTextBox.Height += richTextBox.Margin.Vertical +
SystemInformation.VerticalResizeBorderThickness;
}
static public void ResizeToContentsHorizontally(this RichTextBox richTextBox, ContentsResizedEventArgs e) {
richTextBox.Width = e.NewRectangle.Width;
richTextBox.Width += richTextBox.Margin.Horizontal +
SystemInformation.HorizontalResizeBorderThickness +
SystemInformation.HorizontalScrollBarThumbWidth;
}
static public void ResizeToContentsVertically(this RichTextBox richTextBox, ContentsResizedEventArgs e) {
richTextBox.Height = e.NewRectangle.Height;
richTextBox.Height += richTextBox.Margin.Vertical +
SystemInformation.VerticalResizeBorderThickness;
}
}
So the event sink looks like :
private void rtfBox_ContentsResized(object sender, ContentsResizedEventArgs e) {
RichTextBox rtb = (RichTextBox)sender;
rtb.ResizeToContents(e);
}

Move window without border

How do I move a window that does not have a border. There is no empty space on the application, all that is available is a webbrowser and a menustrip. I would like the users to be able to move the window by dragging the menu strip. How do I code this? I have tried a few code blocks I have found online, but none of them worked.
This Code Project article should help you accomplish this. I've used this myself with no problems. This is the jist of it:
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
[DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
}
This will basically "trick" the window manager into thinking that it is grabbing the title bar of the winform.
To apply it to your project, just use the MouseDown event from the MenuStrip.
Here is the .Net Way
private bool dragging = false;
private Point dragCursorPoint;
private Point dragFormPoint;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
dragging = true;
dragCursorPoint = Cursor.Position;
dragFormPoint = this.Location;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
Point dif = Point.Subtract(Cursor.Position, new Size(dragCursorPoint));
this.Location = Point.Add(dragFormPoint, new Size(dif));
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
dragging = false;
}
that's it.
Just put the start point into an 2D Array like this:
public partial class mainForm : Form
{
//Global variables for Moving a Borderless Form
private bool dragging = false;
private Point startPoint = new Point(0, 0);
public mainForm()
{
InitializeComponent();
}
private void mainForm_MouseDown(object sender, MouseEventArgs e)
{
dragging = true;
startPoint = new Point(e.X, e.Y);
}
private void mainForm_MouseUp(object sender, MouseEventArgs e)
{
dragging = false;
}
private void mainForm_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
Point p = PointToScreen(e.Location);
Location = new Point(p.X - this.startPoint.X, p.Y - this.startPoint.Y);
}
}
}
You can fake your menustrip, for example using a panel with a label instead. And then you can handle this manually: when the user clicks the label, a popup menu will open, and when the user drags the label, the window will move. But I would advise against such workarounds, because it's not a standard GUI behavior, and you might get your users confused.
I haven't tried it, but if you can handle the "OnMouseDown" and "onMouseUp" events on the menu bar:
On mouse down - Move the window according to the mouse movement
Stop tracking the mouse movement on mouse up, or mouse out
If you are using a Panel you have to add this in the
YourForm.Designer.cs
this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseDown);
and this in the
YourForm.cs
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
[DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
private void panel1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
}
Mbithi Kioko is on the right track but i would do it this way.
bool dragging = false;
int xOffset = 0;
int yOffset = 0;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
dragging = true;
xOffset = Cursor.Position.X - this.Location.X;
yOffset = Cursor.Position.Y - this.Location.Y;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
this.Location = new Point(Cursor.Position.X - xOffset, Cursor.Position.Y - yOffset);
this.Update();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
dragging = false;
}
I had to use System.Runtime.InteropServices.DllImportAttribute - just thought I would comment and let you all know.

make a WPF Popup not screen bound

The default behavior of a Popup is if it is placed where it would extend beyond the edge of the screen, the Popup will reposition itself. Is there a way to turn this behavior off?
I have a Popup that the user can drag around the screen. However, when it gets to the edges it gets stuck. It gets stuck on the edge and stays there until the mouse is dragged far from the edge. Also, I have two monitors and when the Popup is dragged to the edge the two monitors share I get flickering. The Popup flickers between the two monitors.
Just use interop to move your popups (drag them)
Here is code for Thumb which will track the drag process
region Thumb
private Thumb mThumb = null;
public Thumb Thumb
{
get { return mThumb; }
set
{
if (mThumb != value)
{
if (mThumb != null)
{
DetachThumb();
}
mThumb = value;
if (mThumb != null)
{
AttachThumb();
}
}
}
}
private void AttachThumb()
{
Thumb.DragStarted += Thumb_DragStarted;
Thumb.DragDelta += Thumb_DragDelta;
Thumb.DragCompleted += Thumb_DragCompleted;
}
private void DetachThumb()
{
Thumb.DragStarted -= Thumb_DragStarted;
Thumb.DragDelta -= Thumb_DragDelta;
Thumb.DragCompleted -= Thumb_DragCompleted;
}
private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
{
mIsThumbDragging = true;
mPreviousDiffX = 0;
mPreviousDiffY = 0;
}
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
if (mIsMoving)
{
return;
}
mIsMoving = true;
try
{
if (mIsThumbDragging)
{
var doubleDetaX = e.HorizontalChange + mPreviousDiffX;
var doubleDetaY = e.VerticalChange + mPreviousDiffY;
var deltaX = (int)doubleDetaX;
var deltaY = (int)doubleDetaY;
mPreviousDiffX = (double)deltaX - doubleDetaX;
mPreviousDiffY = (double)deltaY - doubleDetaY;
HostPopup.Move(deltaX, deltaY);
}
}
finally
{
mIsMoving = false;
}
}
private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
mIsThumbDragging = false;
}
#endregion
The HostPopup class is subclass of Popup, and it hase the following methods using interop to move the window:
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
internal void Move(int deltaX, int deltaY)
{
if (mIsMoving)
{
return;
}
mIsMoving = true;
try
{
if (Child == null)
return;
var hwndSource = (PresentationSource.FromVisual(Child)) as HwndSource;
if (hwndSource == null)
return;
var hwnd = hwndSource.Handle;
RECT rect;
if (!GetWindowRect(hwnd, out rect))
return;
MoveWindow(hwnd, rect.Left + deltaX, rect.Top + deltaY, (int)Width, (int)Height, true);
}
finally
{
mIsMoving = false;
}
}
If you want the popup to behave more like a Window, I'd just make a Window instead of a Popup.
Having a popup that doesn't position itself like a standard popup, and allows you to drag it around the screen, just seems like a recipe for low usability and confusion.

Categories

Resources