How to re-enable close button in windows forms? - winforms

I disabled a form's close button using following code:
virtual property System::Windows::Forms::CreateParams^ CreateParams
{
System::Windows::Forms::CreateParams^ get() override
{
System::Windows::Forms::CreateParams^ cp = Form::CreateParams;
cp->ClassStyle |= 0x200; //CP_NOCLOSE_BUTTON
return cp;
}
}
However, I want to re-enable this close button in a for example foo() function. How can I do?

You need to change window's class style using SetClassLong
There is example in c#, but idea is still the same:
public partial class Form1 : Form
{
protected override CreateParams CreateParams
{
get
{
var cp = base.CreateParams;
//cp.ClassStyle |= 0x200; // note this is off
return cp;
}
}
public Form1()
{
InitializeComponent();
}
// here button is being disabled
private void Form1_Load(object sender, EventArgs e)
{
HandleRef handle = new HandleRef(null, this.Handle);
var cp = CreateParams;
cp.ClassStyle = cp.ClassStyle | (0x200);
IntPtr style = new IntPtr(cp.ClassStyle);
var classLong = Form1.SetClassLong(handle, (int)ClassLongFlags.GCL_STYLE, style);
}
// here is being enabled
private void Form1_DoubleClick(object sender, EventArgs e)
{
HandleRef handle = new HandleRef(null, this.Handle);
var cp = CreateParams;
cp.ClassStyle = cp.ClassStyle & (~0x200);
IntPtr style = new IntPtr(cp.ClassStyle);
var classLong = Form1.SetClassLong(handle, (int)ClassLongFlags.GCL_STYLE, style);
}
}
SetClassLong, ClassLongFlags can be found here http://www.pinvoke.net/
Here is c++-cli version, without pinvoke.
#include <windows.h>
#define GCL_STYLE -26
using namespace System::Windows::Forms;
using namespace System::Runtime::InteropServices;
using namespace System;
public ref class Form1 : public Form
{
public:
Form1()
{
InitializeComponent();
this->Load += gcnew EventHandler(
this, &Form1::Form1_Load);
this->DoubleClick += gcnew EventHandler(
this, &Form1::Form1_DoubleClick);
}
protected:
virtual property System::Windows::Forms::CreateParams^ CreateParams
{
System::Windows::Forms::CreateParams^ get() override
{
System::Windows::Forms::CreateParams^ cp = Form::CreateParams;
//cp->ClassStyle |= 0x200; //CP_NOCLOSE_BUTTON
return cp;
}
}
private:
void Form1_Load(Object^ sender, EventArgs^ e)
{
HandleRef^ handle = gcnew HandleRef(nullptr, this->Handle);
System::Windows::Forms::CreateParams^ cp = Form::CreateParams;
cp->ClassStyle = cp->ClassStyle | (0x200);
IntPtr^ style = gcnew IntPtr(cp->ClassStyle);
::SetClassLong(
(HWND)this->Handle.ToPointer(),
(int)GCL_STYLE,
(LONG)style->ToInt32());
}
// here is being enabled
// possibly, it is gonna be your `foo`
void Form1_DoubleClick(Object^ sender, EventArgs^ e)
{
HandleRef^ handle = gcnew HandleRef(nullptr, this->Handle);
System::Windows::Forms::CreateParams^ cp = Form::CreateParams;
cp->ClassStyle = cp->ClassStyle & (~0x200);
IntPtr^ style = gcnew IntPtr(cp->ClassStyle);
::SetClassLong(
(HWND)this->Handle.ToPointer(),
(int)GCL_STYLE,
(LONG)style->ToInt32());
}
};

Related

ChromiumWebBrowser flash black after hiding without disabling WebGL

I am trying to hide my ChromiumWebBrowser behind images, video, etc... But every time it changes from a ChromiumWebBrowser to anything else than a blank panel or another ChromiumWebBrowser it flashes black for a few frames.
Exemple of my problem
hardware:
i7-8559U
intel IRI plus Graphics 655
CefSharp Version 79.1.350 for a Winform Program
Here is what I tried:
BringToFront other PictureBox
SendToback the ChromiumWebBrowser
Panel visibility
Panel doubleBuffed
I also enable Cef.EnableHighDPISupport(); but to no success.
The only thing that worked so far is to ADD
SetOffScreenRenderingBestPerformanceArgs();
But unfortunately, it disables WebGL implementation :/ and I would like to keep it for later purposes.
static class Program
{
/// <summary>
/// Point d'entrée principal de l'application.
/// </summary>
[STAThread]
static void Main()
{
Cef.EnableHighDPISupport();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public partial class Form1 : Form
{
private static ChromiumWebBrowser chrome;
private PictureBox ImageBox = new PictureBox();
private Panel pPictureBox = new Panel();
private Panel pChromium = new Panel();
Timer timer = new Timer();
public Form1()
{
InitializeComponent();
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
ImageBox.Image = Properties.Resources._3080;
ImageBox.SizeMode = PictureBoxSizeMode.StretchImage;
pPictureBox.Controls.Add(ImageBox);
ImageBox.Dock = DockStyle.Fill;
pPictureBox.Dock = DockStyle.Fill;
pPictureBox.Size = this.Size;
this.Controls.Add(pPictureBox);
pPictureBox.BringToFront();
InitializeChromium();
timer.Interval = 7000;
timer.Start();
timer.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
if (pChromium.Visible)
{
pChromium.Hide();
}
else
{
pChromium.Show();
}
}
private void InitializeChromium()
{
pChromium.Dock = DockStyle.Fill;
pChromium.Size = this.Size;
CefSettings settings = new CefSettings();
//Work but disable WebGL
//settings.SetOffScreenRenderingBestPerformanceArgs();
//settings.DisableGpuAcceleration();
Cef.Initialize(settings);
chrome = new ChromiumWebBrowser("https://www.apple.com/ca/airpods-pro/");
pChromium.Controls.Add(chrome);
this.Controls.Add(pChromium);
chrome.Dock = DockStyle.Fill;
pChromium.BringToFront();
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(1904, 1041);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Cef.Shutdown();
}
}
Do you guys have any solution?
Here is my final code for any one interested
Links of the command recommanded by #amaitland
https://peter.sh/experiments/chromium-command-line-switches/#use-angle
https://peter.sh/experiments/chromium-command-line-switches/#in-process-gpu
both command works individualy
Cef.EnableHighDPISupport(); is not required but is recommanded
static void Main()
{
Cef.EnableHighDPISupport();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public partial class Form1 : Form
{
private static ChromiumWebBrowser chrome;
private PictureBox ImageBox = new PictureBox();
private Panel pPictureBox = new Panel();
private Panel pChromium = new Panel();
Timer timer = new Timer();
public Form1()
{
InitializeComponent();
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
//any image here
ImageBox.Image = Properties.Resources._3080;
ImageBox.SizeMode = PictureBoxSizeMode.StretchImage;
pPictureBox.Controls.Add(ImageBox);
ImageBox.Dock = DockStyle.Fill;
pPictureBox.Dock = DockStyle.Fill;
pPictureBox.Size = this.Size;
this.Controls.Add(pPictureBox);
pPictureBox.BringToFront();
InitializeChromium();
timer.Interval = 7000;
timer.Start();
timer.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
if (pChromium.Visible)
{
pChromium.Hide();
}
else
{
pChromium.Show();
}
}
private void InitializeChromium()
{
pChromium.Dock = DockStyle.Fill;
pChromium.Size = this.Size;
CefSettings settings = new CefSettings();
//-------------------------------------------------------------------------
settings.CefCommandLineArgs.Add("in-process-gpu");
//got best FPS with this renderer on "my machine"
settings.CefCommandLineArgs.Add("use-angle", "gl");
//-------------------------------------------------------------------------
//Work but disable WebGL
//settings.SetOffScreenRenderingBestPerformanceArgs();
//settings.DisableGpuAcceleration();
Cef.Initialize(settings);
chrome = new ChromiumWebBrowser("https://alteredqualia.com/three/examples/webgl_pasta.html");
pChromium.Controls.Add(chrome);
this.Controls.Add(pChromium);
chrome.Dock = DockStyle.Fill;
pChromium.BringToFront();
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(1904, 1041);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Cef.Shutdown();
}
}

How to enable and disable dragging a button?

I have a form. On this form there is a button and a panel. Both have the same parent: the form. I want to be able to enable or disable the dragging operation of the button. The code I am using is:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace DraggableControls
{
public partial class Form2 : Form
{
private bool isDragged;
private Point pointOffset;
public Form2()
{
InitializeComponent();
this.Size = new System.Drawing.Size(800, 800);
this.AllowDrop = false;
Button button1 = new Button();
button1.Location = new Point(50, 50);
button1.BackColor = Color.Red;
button1.Size = new System.Drawing.Size(80, 80);
button1.Parent = this;
button1.AllowDrop = false;
button1.Draggable(false);
this.Controls.Add(button1);
isDragged = false;
// For the object that will be dragged:
button1.MouseDown += new MouseEventHandler(button1_MouseDown);
button1.MouseMove += new MouseEventHandler(button1_MouseMove);
button1.MouseUp += new MouseEventHandler(button1_MouseUp);
Panel panel1 = new Panel();
panel1.SuspendLayout();
panel1.Location = new System.Drawing.Point(100, 100);
panel1.Name = "panel1";
panel1.TabIndex = 0;
panel1.Size = new Size(500, 500);
panel1.BackColor = Color.Green;
panel1.AllowDrop = true;
panel1.Parent = this;
this.Controls.Add(panel1);
panel1.ResumeLayout(false);
// For the receiving object:
panel1.DragEnter += new
System.Windows.Forms.DragEventHandler(control_DragEnter);
this.DragEnter += new
System.Windows.Forms.DragEventHandler(control_DragEnter);
this.ResumeLayout(false);
}
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (sender is Button)
{
Button senderButton = (Button)sender;
if (e.Button == MouseButtons.Left)
{
isDragged = true;
Point ptStartPosition = senderButton.PointToScreen(new
Point(e.X, e.Y));
pointOffset = new Point();
pointOffset.X = senderButton.Location.X - ptStartPosition.X;
pointOffset.Y = senderButton.Location.Y - ptStartPosition.Y;
}
else
{
isDragged = false;
}
}
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (sender is Button)
{
Button senderButton = (Button)sender;
if (isDragged)
{
Point newPoint = senderButton.PointToScreen(new Point(e.X,
e.Y));
newPoint.Offset(pointOffset);
senderButton.Location = newPoint;
}
}
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
if (sender is Button)
{
Button senderButton = (Button)sender;
Point newPoint = senderButton.PointToScreen(new Point(e.X,
e.Y));
isDragged = false;
}
}
private void control_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.None;
}
/// <summary>
/// Prevents Window and any of the controls from being dragged by means
of the mouse.
/// </summary>
/// <param name="message"></param>
protected override void WndProc(ref Message message)
{
int WM_NCLBUTTONDOWN = 0xA1;
int WM_SYSCOMMAND = 0x112;
int HTCAPTION = 0x02;
int SC_MOVE = 0xF010;
if (message.Msg == WM_SYSCOMMAND && message.WParam.ToInt32() ==
SC_MOVE)
{
return;
}
if (message.Msg == WM_NCLBUTTONDOWN && message.WParam.ToInt32() ==
HTCAPTION)
{
return;
}
base.WndProc(ref message);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace DraggableControls
{
public static class ControlDragExtension
{
private static Dictionary<Control, bool>
controlDraggabilityByControl = new Dictionary<Control, bool>(); // TKey
is
control to drag, TValue is a Boolean
flag used to determine the dragging state.
private static System.Drawing.Size mouseOffset;
/// <summary>
/// Enable/Disable control dragging.
/// </summary>
public static void Draggable(this Control control, bool enable)
{
if (enable) // Enable control drag feature.
{
if (controlDraggabilityByControl.ContainsKey(control))
{
return; // Return if control is already draggable.
}
controlDraggabilityByControl.Add(control, false); // 'false' -
Initial state is 'control not draggable'.
// Assign required event handlers.
control.MouseDown += new MouseEventHandler(Control_MouseDown);
control.MouseUp += new MouseEventHandler(Control_MouseUp);
control.MouseMove += new MouseEventHandler(Control_MouseMove);
}
else // Disable control drag feature.
{
if (!controlDraggabilityByControl.ContainsKey(control))
{
return; // Return if control is not draggable.
}
// Remove event handlers.
control.MouseDown -= Control_MouseDown;
control.MouseUp -= Control_MouseUp;
control.MouseMove -= Control_MouseMove;
controlDraggabilityByControl.Remove(control);
}
}
static void Control_MouseDown(object sender, MouseEventArgs e)
{
mouseOffset = new System.Drawing.Size(e.Location);
controlDraggabilityByControl[(Control)sender] = true; // Turn
control dragging feature on.
}
static void Control_MouseUp(object sender, MouseEventArgs e)
{
controlDraggabilityByControl[(Control)sender] = false; // Turn
control dragging feature off.
}
static void Control_MouseMove(object sender, MouseEventArgs e)
{
if (controlDraggabilityByControl[(Control)sender] == true) // Only
if dragging is turned on.
{
// Calculation of control's new position.
System.Drawing.Point newLocationOffset = e.Location -
mouseOffset;
((Control)sender).Left += newLocationOffset.X;
((Control)sender).Top += newLocationOffset.Y;
}
}
}
}
Even though I have the statements: this.AllowDrop = false; , panel1.AllowDrop = false; and button1.Draggable(false); and also the override void WndProc method that should disable dragging,
the button1 is always draggable.
I changed the event handlers thus:
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (sender is Button)
{
Button senderButton = (Button)sender;
if (enableDragOperations)
{
if (e.Button == MouseButtons.Left)
{
isDragged = true;
Point ptStartPosition = senderButton.PointToScreen(new Point(e.X, e.Y));
pointOffset = new Point();
pointOffset.X = senderButton.Location.X - ptStartPosition.X;
pointOffset.Y = senderButton.Location.Y - ptStartPosition.Y;
}
else
{
isDragged = false;
}
}
}
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (sender is Button)
{
Button senderButton = (Button)sender;
if (enableDragOperations)
{
if (isDragged)
{
Point newPoint = senderButton.PointToScreen(new Point(e.X, e.Y));
newPoint.Offset(pointOffset);
senderButton.Location = newPoint;
}
}
}
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
if (sender is Button)
{
Button senderButton = (Button)sender;
if (enableDragOperations)
{
Point newPoint = senderButton.PointToScreen(new Point(e.X, e.Y));
isDragged = false;
}
}
}
I defined a global field: bool enableDragOperations and set it to false.

wpf, how can i get template element on custom-controls, after new instance immediately

questions:
1、i wanna get template elements when calling the constructor, but return null, any way?
2、i found it, get not null obj after loaded event, but i don't want this way.
snippet code:(to see my comments)
using System;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Input;
namespace WpfPropertyGrid_Demo
{
public class MyButton : Control
{
public static readonly DependencyProperty IsMouseDownProperty;
static MyButton()
{
IsMouseDownProperty = DependencyProperty.Register(
"IsMouseDown",
typeof(bool),
typeof(MyButton),
new FrameworkPropertyMetadata(false)
);
}
public bool IsMouseDown
{
get { return (bool)GetValue(IsMouseDownProperty); }
set { SetValue(IsMouseDownProperty, value); }
}
private BitmapImage _normalImg;
private BitmapImage _overImg;
private BitmapImage _clickImg;
private BitmapImage _disabledImg;
public MyButton()
{
_normalImg = new BitmapImage(new Uri("../../Images/ScrollerBtnBg.png", UriKind.RelativeOrAbsolute));
_overImg = new BitmapImage(new Uri("../../Images/ScrollerThumbnailBtnBg.png", UriKind.RelativeOrAbsolute));
_clickImg = new BitmapImage(new Uri("../../Images/ScrollerThumbnailBg.png", UriKind.RelativeOrAbsolute));
_disabledImg = _clickImg;
var style = new Style(typeof(MyButton));
var controlTemplate = new ControlTemplate();
var gridFactory = new FrameworkElementFactory(typeof(Grid));
var imgFacotry = new FrameworkElementFactory(typeof(Image));
imgFacotry.Name = "Image";
imgFacotry.SetValue(Image.SourceProperty, _normalImg);
gridFactory.AppendChild(imgFacotry);
controlTemplate.VisualTree = gridFactory;
var overTrigger = new Trigger();
overTrigger.Property = UIElement.IsMouseOverProperty;
overTrigger.Value = true;
overTrigger.Setters.Add(new Setter(Image.SourceProperty, _overImg, "Image"));
var disabledTriiger = new Trigger();
disabledTriiger.Property = UIElement.IsEnabledProperty;
disabledTriiger.Value = false;
disabledTriiger.Setters.Add(new Setter(Image.SourceProperty, _disabledImg, "Image"));
var downTrigger = new Trigger();
downTrigger.Property = MyButton.IsMouseDownProperty;
downTrigger.Value = true;
downTrigger.Setters.Add(new Setter(Image.SourceProperty, _clickImg, "Image"));
controlTemplate.Triggers.Add(overTrigger);
controlTemplate.Triggers.Add(disabledTriiger);
controlTemplate.Triggers.Add(downTrigger);
style.Setters.Add(new Setter(Control.TemplateProperty, controlTemplate));
this.PreviewMouseDown += new System.Windows.Input.MouseButtonEventHandler(MyButton_PreviewMouseDown);
this.PreviewMouseUp += new System.Windows.Input.MouseButtonEventHandler(MyButton_PreviewMouseUp);
this.MouseLeave += new System.Windows.Input.MouseEventHandler(MyButton_MouseLeave);
this.MouseEnter += new MouseEventHandler(MyButton_MouseEnter);
Style = style;
// 1、why cann't find "Image" element, it return null obj, i wanna get it immediately, any way?
// 2、return not null after loaded event
// var image = controlTemplate.FindName("Image", this );
this.Loaded += new RoutedEventHandler(MyButton_Loaded);
}
void MyButton_MouseEnter(object sender, MouseEventArgs e)
{
IsMouseDown = e.LeftButton == MouseButtonState.Pressed;
}
void MyButton_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
IsMouseDown = false;
}
void MyButton_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
IsMouseDown = false;
}
void MyButton_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
IsMouseDown = true;
}
void MyButton_Loaded(object sender, RoutedEventArgs e)
{
// return not null after loaded event
var image = this.Template.FindName("Image", this) as Image;
}
}
}
Try Visual Helper Class here I hope this will help

How to create and use WebBrowser in background thread?

How can I create System.Windows.Forms.WebBrowser in background STA thread? I try use some code like this:
var tr = new Thread(wbThread);
tr.SetApartmentState(ApartmentState.STA);
tr.Start();
private void wbThread()
{
CWebBrowser browser = new CWebBrowser();
var text = browser.Navigate("http://site.com", CWebBrowser.EventType.loadCompleted).Body.InnerHtml;
}
CWebBrowser - custom class, wich delegate System.Windows.Forms.WebBrowser object Navigate method and wait until page completed loads. The problem is LoadCompleted event on System.Windows.Forms.WebBrowser object never raises. I found some solution here, but it does not work (can't find method Application.Run() on my WPF app).
public class CWebBrowser : ContentControl
{
public readonly System.Windows.Forms.WebBrowser innerWebBrowser;
private readonly AutoResetEvent loadCompletedEvent;
private readonly AutoResetEvent navigatedEvent;
public enum EventType
{
navigated, loadCompleted
}
public CWebBrowser()
{
innerWebBrowser = new System.Windows.Forms.WebBrowser();
loadCompletedEvent = new AutoResetEvent(false);
navigatedEvent = new AutoResetEvent(false);
System.Windows.Forms.Integration.WindowsFormsHost host = new System.Windows.Forms.Integration.WindowsFormsHost();
host.Child = innerWebBrowser;
Content = host;
innerWebBrowser.DocumentCompleted +=new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(innerWebBrowser_DocumentCompleted);
innerWebBrowser.Navigated += new System.Windows.Forms.WebBrowserNavigatedEventHandler(innerWebBrowser_Navigated);
}
void innerWebBrowser_Navigated(object sender, System.Windows.Forms.WebBrowserNavigatedEventArgs e)
{
navigatedEvent.Set();
}
void innerWebBrowser_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
if (((sender as System.Windows.Forms.WebBrowser).ReadyState != System.Windows.Forms.WebBrowserReadyState.Complete) || innerWebBrowser.IsBusy)
return;
var doc = innerWebBrowser.Document;
loadCompletedEvent.Set();
}
public System.Windows.Forms.HtmlDocument Navigate(string url, EventType etype)
{
if (etype == EventType.loadCompleted)
loadCompletedEvent.Reset();
else if (etype == EventType.navigated)
navigatedEvent.Reset();
innerWebBrowser.Navigate(url);
if (etype == EventType.loadCompleted)
loadCompletedEvent.WaitOne();
else if (etype == EventType.navigated)
navigatedEvent.WaitOne();
System.Windows.Forms.HtmlDocument doc = null;
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(
delegate
{
doc = innerWebBrowser.Document;
}));
return doc;
}
}
Thansk for all advices and sorry for my bad english :o(
Why don't you use the default WebBrowser control like this?
public MainPage()
{
InitializeComponent();
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(startNavigate);
}
void startNavigate()
{
WebBrowser wb = new WebBrowser();
wb.LoadCompleted += new LoadCompletedEventHandler(wb_LoadCompleted);
wb.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(wb_Navigated);
wb.Navigate(new Uri("http://www.google.com"));
}
void wb_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
// e.Content
}
void wb_LoadCompleted(object sender, NavigationEventArgs e)
{
// e.Content when the document finished loading.
}
Edit: You are using old System.Windows.Forms.WebBrowser control, instead System.Windows.Controls.WebBrowser which is part of WPF.

TreeView in Winforms and focus problem

Can anyone please explain to my why the form in the code below gets out of focus when selecting a treenode in the tree? What should happen is that the form/button should get the focus when the tree disappears like the listview example but it doesn't.
Code example:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FocusTest
{
public partial class Form1 : Form
{
#region Generated
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("Item1");
System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem("Item2");
System.Windows.Forms.ListViewItem listViewItem3 = new System.Windows.Forms.ListViewItem("Item3");
System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("Node0");
System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("Node1");
System.Windows.Forms.TreeNode treeNode3 = new System.Windows.Forms.TreeNode("Node2");
this.button1 = new System.Windows.Forms.Button();
this.listView1 = new System.Windows.Forms.ListView();
this.button2 = new System.Windows.Forms.Button();
this.treeView1 = new System.Windows.Forms.TreeView();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(12, 12);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// listView1
//
this.listView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
listViewItem1,
listViewItem2,
listViewItem3
});
this.listView1.Location = new System.Drawing.Point(12, 41);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(121, 97);
this.listView1.TabIndex = 1;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.Visible = false;
this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged);
this.listView1.View = View.List;
//
// button2
//
this.button2.Location = new System.Drawing.Point(310, 11);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 23);
this.button2.TabIndex = 2;
this.button2.Text = "button2";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// treeView1
//
this.treeView1.Location = new System.Drawing.Point(310, 41);
this.treeView1.Name = "treeView1";
treeNode1.Name = "Node0";
treeNode1.Text = "Node0";
treeNode2.Name = "Node1";
treeNode2.Text = "Node1";
treeNode3.Name = "Node2";
treeNode3.Text = "Node2";
this.treeView1.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode1,
treeNode2,
treeNode3});
this.treeView1.Size = new System.Drawing.Size(121, 97);
this.treeView1.TabIndex = 3;
this.treeView1.Visible = false;
this.treeView1.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView1_AfterSelect);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(760, 409);
this.Controls.Add(this.treeView1);
this.Controls.Add(this.button2);
this.Controls.Add(this.listView1);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.TreeView treeView1;
#endregion
public Form1()
{
InitializeComponent();
}
#region TreeView
private void button2_Click(object sender, EventArgs e)
{
ToggleTreeView();
}
private void ToggleTreeView()
{
if (treeView1.Visible)
{
Controls.Remove(treeView1);
treeView1.Visible = false;
}
else
{
Controls.Add(treeView1);
treeView1.Size = new Size(300, 400);
treeView1.Location = PointToClient(PointToScreen(new System.Drawing.Point(button2.Location.X, button2.Location.Y + button2.Height)));
this.treeView1.BringToFront();
treeView1.Visible = true;
treeView1.Select();
}
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
ToggleTreeView();
}
#endregion
#region ListView
private void button1_Click(object sender, EventArgs e)
{
ToggleListView();
}
private void ToggleListView()
{
if (listView1.Visible)
{
Controls.Remove(listView1);
listView1.Visible = false;
}
else
{
Controls.Add(listView1);
listView1.Size = new Size(300, 400);
listView1.Location = PointToClient(PointToScreen(new System.Drawing.Point(button1.Location.X, button1.Location.Y + button1.Height)));
this.listView1.BringToFront();
listView1.Visible = true;
listView1.Select();
}
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listView1.Visible)
ToggleListView();
}
#endregion
}
}
Here is the problem and the fix.
TreeView re-grabs focus on Ctrl+Click
I can't explain why the form loses focus, but the problem seems to be the Controls.Remove(treeView1); line. If you remove the Controls.Remove and Controls.Add lines, then it seems to behave better.
Is there a reason why you are removing the treeView from the list of control? Just setting the visibility flag to false will cause the treeView to disappear after the user makes their selection.
EDIT:
In response to your comment:
My guess is that after treeView1_AfterSelect is called, the TreeView is setting focus to itself. Which in your code is impossible because you've removed the control from the form.
To test this theory, I added a timer, replaced the code with:
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
timer1.Enabled = true;
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
ToggleTreeView();
timer1.Stop();
timer1.Enabled = false;
}
Now it works fine. So I'm guessing that the TreeView is calling this.Focus after it has fired the AfterSelect event. (That is just a guess, maybe someone else knows more about the internals them me:) )
And the reason why this is working with the ListView, is that it does not set focus to itself after listView1_SelectedIndexChanged.

Resources