Best shown with an image, I can drag the forms off the edge of the MDI parent, and scroll bars appear.
Id much rather that child forms are locked to the area of the Parent form.
Ive done searching for a solution, (A lot of these are answered 2+ years ago, so im hoping they are outdated) and only come across people checking co-ordinates on the Move event.... Great, but what if I have 20 forms.. or 100 forms. I cant code all of them, its a bit silly. Surely there is a property somewhere I can just set on the parent form.
Step 1:
You should create a new class inherited from NativeWindow class and override its WndProc method.
Step 2:
In your MDI form create a new object of this new class and pass the MDIClient control to its constructor.
Step 1 code:
internal class MyNativeMDIclient : NativeWindow
{
private MdiClient mdiClient;
public MyNativeMDIclient(MdiClient parent)
{
mdiClient = parent;
ReleaseHandle();
AssignHandle(mdiClient.Handle);
}
internal void OnHandleDestroyed(object sender, EventArgs e)
{
ReleaseHandle();
}
private const int SB_BOTH = 3;
[DllImport("user32.dll")]
private static extern int ShowScrollBar(IntPtr hWnd, int wBar, int bShow);
protected override void WndProc(ref Message m)
{
ShowScrollBar(m.HWnd, SB_BOTH, 0 /*false*/);
base.WndProc(ref m);
}
}
Step 2 code:
foreach (Control control in this.Controls)
{
if (control is MdiClient)
{
MyNativeMDIclient nw = new MyNativeMDIclient((MdiClient)control);
break;
}
}
you can do something like this...
Step !: you have to make base form( like user control)
and place this code in that form
namespace Controls
{
public partial class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
StartPosition = FormStartPosition.WindowsDefaultLocation;
MaximizeBox = false;
Width = 806;
//Width = 850;
//Height = 760;
Height = 730;
//Width = 790;
//Height = 617;
}
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
//ShowScrollBar(this.Handle, (int)ScrollBarDirection.SB_BOTH, false);
switch (m.Msg)
{
case WM_SYSCOMMAND:
int command = m.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
break;
}
base.WndProc(ref m);
}
}
}
and then in every form you have to specify like this...
public partial class childform : BaseForm
{
.......
}
Make sure all your child forms size is should be size specified in base form
minsize is 0,0
max sixze is also 0,0
startposition - windowsdefaultlocation
windowstate - normal
I hope it will helps you ..
You could always override the move functionality in a parent class, then have all of your forms inherit from that class.
Theres no magic API/property (that I know of, apologies if I'm wrong) to tell an MDI parent to lock children within its bounds.
Related
I'm attempting (for reasons) to use a cue banner textbox in place of the standard textbox in a WinForms dropdown item.
I've gotten it to a state where I can add the new control object to the list of dropdown options, but if I do, it locks up the properties menu for not only it, but also any other options on that same dropdown 'list'. It does not have this issue if I use it elsewhere in the form - just when its used in a dropdown.
I'm posting two pieces of code below:
My CueTextBox class (extend TextBox)
My ToolStripCueTextBox class (derive from ToolStripControlHost for designer availability)
**//1 - Class for box**
public class CueTextBox : TextBox
{
private string mCue;
public string Cue
{
get => mCue;
set
{
mCue = value;
Invalidate();
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
const int WM_PAINT = 0xF;
if (m.Msg == WM_PAINT)
{
if (!Focused && string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Cue))
{
using (var graphics = CreateGraphics())
{
TextRenderer.DrawText(
dc: graphics,
text: Cue,
font: Font,
bounds: ClientRectangle,
foreColor: SystemColors.GrayText,
backColor: Enabled ? BackColor : SystemColors.Control,
flags: TextFormatFlags.Top | TextFormatFlags.Left);
}
}
}
}
**//2 - Class for toolstrip**
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.All)]
public class ToolStripCueTextBoxItem : ToolStripControlHost
{
public ToolStripCueTextBoxItem() : base(new CueTextBox())
{
}
}
I'm new here, but any help would be greatly appreciated!
private void downloader_Succeeded(object sender, EventArgs e)
{
FileDownloader.FileInfo fi = downloader.CurrentFile;
string name = fi.Path;
foreach (ListViewItem lvw in listView1.Items)
{
if (lvw.Text == name)
lvw.ForeColor = Color.Green;
}
label6.Text = countFilesDownloaded++.ToString();
}
Each time when it's coloring the item it's flickering.
I saw some answers but none of them is working.
I saw this answer:
Flickering answer
And this one
Flickering answer
Maybe i didn't use them right. But they didn't work.
Tried to add this to the form1 at the bottom:
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
Then tried to create instance of this each time a item was coloring inside the event.
I had your problem and after doing some research it appears that the ListView control repaints its entire area whenever you modify a single item. The solution is to subclass the ListView and filter out the WM_ERASEBKGND message. This did the trick for me.
For more information see here:
c# flickering Listview on update
public partial class ListViewNF : ListView
{
public ListViewNF()
{
}
public ListViewNF(IContainer container)
{
container.Add(this);
InitializeComponent();
//Activate double buffering
this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
this.SetStyle(ControlStyles.EnableNotifyMessage, true);
}
protected override void OnNotifyMessage(Message m)
{
//Filter out the WM_ERASEBKGND message
if (m.Msg != 0x14)
{
base.OnNotifyMessage(m);
}
}
}
I am creating a custom control that acts like the TabControl. I want the tab headers to be clickable in design time, just like the TabControl. Using the sample code I found on the Microsoft site, I wrote the following code. It works, but there is one problem.
As for the real TabControl, if you click a tab header when the TabControl has not been selected, the selected tab changes, AND the TabControl gets selected showing the resizing border which has a moving handle and a small button for "TabControl Tasks".
Doing the same thing with my code below only changes the selected tab header; it does not make my control be selected. I think I need to tell the Visual Studio Designer to select my control, somehow. But how?
public class MyDesigner : System.Windows.Forms.Design.ControlDesigner
{
private Adorner MyAdorner;
public override void Initialize(IComponent component)
{
base.Initialize(component);
MyAdorner = new Adorner();
this.BehaviorService.Adorners.Add(MyAdorner);
MyAdorner.Glyphs.Add(new MyGlyph(BehaviorService, (MyTabControl)Control));
}
protected override void Dispose(bool disposing)
{
if (disposing && MyAdorner != null)
{
BehaviorService b = BehaviorService;
if (b != null)
{
b.Adorners.Remove(MyAdorner);
}
}
}
class MyGlyph : Glyph
{
MyTabControl TargetControl;
BehaviorService behaviorSvc;
public MyGlyph(BehaviorService behaviorSvc, MyTabControl targetControl)
:base(new ClickingBehaviour())
{
this.behaviorSvc = behaviorSvc;
this.TargetControl = targetControl;
}
private void OnMouseClick(MouseButtons button, Point adornerPoint)
{
var screenPoint = behaviorSvc.AdornerWindowPointToScreen(adornerPoint);
var targetPoint = TargetControl.PointToClient(screenPoint);
TargetControl.DesignTimeClick(button, targetPoint);
}
public override Rectangle Bounds
{
get
{
Point edge = behaviorSvc.ControlToAdornerWindow(TargetControl);
Size size = TargetControl.Size;
Rectangle bounds = new Rectangle(edge, size);
return bounds;
}
}
public override Cursor GetHitTest(Point p)
{
Cursor handled = null;
if (Bounds.Contains(p))
{
var screenPoint = behaviorSvc.AdornerWindowPointToScreen(p);
var targetPoint = TargetControl.PointToClient(screenPoint);
if(TargetControl.IsHeaderArea(targetPoint))
handled = Cursors.Default;
}
return handled;
}
public override void Paint(PaintEventArgs pe)
{
}
class ClickingBehaviour : Behavior
{
public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc)
{
MyGlyph myG = g as MyGlyph;
myG.OnMouseClick(button, mouseLoc);
return base.OnMouseDown(g, button, mouseLoc);
}
}
}
}
I could not figure out how to do it with the approach which uses an Adorner as above. So, I resorted to using Win32 message (I tried to avoid using Win32 for interoperability).
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0201 && m.WParam.ToInt32() == 0x0001)
{
var target = (Control as MyTabControl);
if (true) //how to know if focused?
{
var lparam32 = m.LParam.ToInt32();
int lowWord = lparam32 & 0xffff;
int highWord = lparam32 >> 16;
Point p = new Point(lowWord, highWord);
if (target.IsHeaderArea(p))
{
target.DesignTimeClick(MouseButtons.Left, p);
}
}
}
base.WndProc(ref m);
}
It works, but the only minor problem is that the tab index changes at the first click when the tab is not selected. Clicking a tab on a real TabControl when it is not selected does not change the tab but only selects the tab. But I guess I can live with this minor difference.
Is there any way of creating a tool window in WinForms that as long as the hosting form has focus, the tool window does as well? An example of this is in Paint.NET:
I'm using C# as the backend application language, under .Net 4.0.
The source code for an old version of Paint.Net is available at openpdn Fork of Paint.NET 3.36.7
I tried to extract their methods from that source code into the most concise working example I could muster:
Pinvoking class:
internal static class Win32 {
public const int WM_ACTIVATE = 0x006;
public const int WM_ACTIVATEAPP = 0x01C;
public const int WM_NCACTIVATE = 0x086;
[DllImport("user32.dll", SetLastError = false)]
internal static extern IntPtr SendMessageW(IntPtr hWnd, uint msg,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal extern static bool PostMessageW(IntPtr handle, uint msg,
IntPtr wParam, IntPtr lParam);
}
Base Form:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private bool ignoreNcActivate = false;
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
switch (m.Msg) {
case Win32.WM_NCACTIVATE:
if (m.WParam == IntPtr.Zero) {
if (ignoreNcActivate) {
ignoreNcActivate = false;
} else {
Win32.SendMessageW(this.Handle, Win32.WM_NCACTIVATE, new IntPtr(1), IntPtr.Zero);
}
}
break;
case Win32.WM_ACTIVATEAPP:
if (m.WParam == IntPtr.Zero) {
Win32.PostMessageW(this.Handle, Win32.WM_NCACTIVATE, IntPtr.Zero, IntPtr.Zero);
foreach (Form2 f in this.OwnedForms.OfType<Form2>()) {
f.ForceActiveBar = false;
Win32.PostMessageW(f.Handle, Win32.WM_NCACTIVATE, IntPtr.Zero, IntPtr.Zero);
}
ignoreNcActivate = true;
} else if (m.WParam == new IntPtr(1)) {
Win32.SendMessageW(this.Handle, Win32.WM_NCACTIVATE, new IntPtr(1), IntPtr.Zero);
foreach (Form2 f in this.OwnedForms.OfType<Form2>()) {
f.ForceActiveBar = true;
Win32.SendMessageW(f.Handle, Win32.WM_NCACTIVATE, new IntPtr(1), IntPtr.Zero);
}
}
break;
}
}
protected override void OnShown(EventArgs e) {
base.OnShown(e);
Form2 f = new Form2();
f.Show(this);
}
}
Always active Form2 (unless app is not active):
public partial class Form2 : Form {
internal bool ForceActiveBar { get; set; }
public Form2() {
InitializeComponent();
this.ShowInTaskbar = false;
this.ForceActiveBar = true;
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
if (m.Msg == Win32.WM_NCACTIVATE) {
if (this.ForceActiveBar && m.WParam == IntPtr.Zero) {
Win32.SendMessageW(this.Handle, Win32.WM_NCACTIVATE, new IntPtr(1), IntPtr.Zero);
}
}
}
}
There is no need to set TopMost to true for Form2 since it should be owned by the main form when it gets displayed. Also, Form2 is not an MDI child form.
The tool windows in Paint.NET are just that—tool windows. In Win32 terms, you achieve this by creating the window with the WS_EX_TOOLWINDOW extended window style:
The window is intended to be used as a floating toolbar. A tool window has a title bar that is shorter than a normal title bar, and the window title is drawn using a smaller font. A tool window does not appear in the taskbar or in the dialog that appears when the user presses ALT+TAB.
In WinForms, this is controlled by the FormBorderStyle property. Set it to either FormBorderStyle.FixedToolWindow or FormBorderStyle.SizableToolWindow in your form's constructor.
You also need to make sure that you specify an owner window for the tool window. Its owner should be your main form, the one for which it serves as a tool palette. You generally do this when showing the form, using the overload of the Show method that allows you to specify an owner window.
Finally, another cool effect that Paint.NET has (I think, if I remember correctly) is that the tool windows can never actually receive the focus. You can interact with them, clicking on buttons to select tools, but you can't actually set the focus to a floating palette. It always goes back to the main window. A naive attempt to emulate this behavior might be to reset the focus in one of the focus-changing notifications (e.g., the Activate event), but that's not a good idea for numerous reasons. A better solution would be to add the WS_EX_NOACTIVATE extended window style. I'm not aware of any property that exposes this functionality in WinForms, but you can set it manually during the window's creation by overriding the CreateParams property. For example:
public class MyForm : Form
{
// ... other code ...
protected override CreateParams CreateParams {
get {
const int WS_EX_NOACTIVATE = 0x08000000;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_NOACTIVATE;
return cp;
}
}
}
I don't know if Windows Forms has a built-in feature for this, but you can achieve what you want with the code below:
For main form:
private ToolForm m_toolForm;
private void MainForm_Load(object sender, EventArgs e)
{
m_toolForm = new ToolForm ();
m_toolForm.Show();
}
private void MainForm_Resize(object sender, EventArgs e)
{
switch (WindowState)
{
case FormWindowState.Minimized:
m_toolForm.Hide();
break;
case FormWindowState.Maximized:
m_toolForm.Show();
break;
}
}
For tool form:
You don't need any code, just set the property "TopMost" to true.
For those using the DevExpress RibbonForm, please use the solution in the below link to solve the focus problem. The zip file is in a comment to the solution, not in the solution itself:
http://www.devexpress.com/Support/Center/Question/Details/Q498321
I am writing a custom control derived from Component.
In this control I need to be able somehow to get OS messages WM_DEVICEDCHANGED to create some events from.
Usually I would just override WndProc directly in the applications Form, but it is really important that this functionallity lays directly in the control instead.
Even though the control will always be used on a Form it is important that the OS messages are received on the control that is derived from Component so when dropping the control on a form there is no need to add functionality for it manually IN the form.
I have seen some examples mentioning NativeWindow and other solutions, but I have not been able to find head or tail in any of it, so I hope someone here can help me out.
Thanks...
i want to receive the WM_DEVICECHANGED message
Okay, that just requires subclassing the window of the form on which you drop the component. Any top-level window gets that message. Add a new class to your project and paste the code shown below. Build. Drop the new component from the top of the toolbox onto a form. Add an event handler for the DeviceChange event and add any code that's relevant to the kind of device change notification that you are interested in. You could also put that code in the OnDeviceChange() method to further specialize the notification and raise more specific events. It is up to you to take it from here.
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
public class DeviceChangeNotifier : Component {
public delegate void DeviceChangeDelegate(Message msg);
public event DeviceChangeDelegate DeviceChange;
public DeviceChangeNotifier() {
// Add initialization here
}
public DeviceChangeNotifier(IContainer container) : this() {
// In case you need automatic disposal
container.Add(this);
}
public DeviceChangeNotifier(ContainerControl parentControl) : this() {
// In case you want to use it without the designer
this.ContainerControl = parentControl;
}
public ContainerControl ContainerControl {
// References the parent form
get { return this.parentControl; }
set {
this.parentControl = value;
this.parentControl.HandleCreated += parentControl_HandleCreated;
}
}
private void parentControl_HandleCreated(object sender, EventArgs e) {
// Subclass the form when its handle is created
snooper = new MessageSnooper(this, parentControl.Handle);
}
protected void OnDeviceChange(Message msg) {
// Raise the DeviceChange message
var handler = DeviceChange;
if (handler != null) handler(msg);
}
public override ISite Site {
// Runs at design time, ensures designer initializes ContainerControl
// so we'll have a reference to the parent form without it having to do any work
set {
base.Site = value;
if (value == null) return;
IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (service == null) return;
IComponent rootComponent = service.RootComponent;
this.ContainerControl = rootComponent as ContainerControl;
}
}
private ContainerControl parentControl;
private MessageSnooper snooper;
private const int WM_DESTROY = 0x0002;
private const int WM_DEVICECHANGE = 0x0219;
private class MessageSnooper : NativeWindow {
// Subclasses the parent window
public MessageSnooper(DeviceChangeNotifier owner, IntPtr handle) {
this.owner = owner;
this.AssignHandle(handle);
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_DESTROY) this.ReleaseHandle();
if (m.Msg == WM_DEVICECHANGE) owner.OnDeviceChange(m);
base.WndProc(ref m);
}
private DeviceChangeNotifier owner;
}
}