I have a Form and want to toggle show and hide the RichTextBox.
If I hide the control, the control should give up the space reservation, but actually it doesn't.
it is Removed, but the original space reserved by rtbcontrol is still visible as empty grey space.
It seems, it affects only the Controls, which are anchored to the bottom.
I made a small sample for demonstrating my problem:
Desinger:
namespace HideShowTest
{
partial class Form1
{
/// <summary>
/// Erforderliche Designervariable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Verwendete Ressourcen bereinigen.
/// </summary>
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Vom Windows Form-Designer generierter Code
/// <summary>
/// Erforderliche Methode für die Designerunterstützung.
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
/// </summary>
private void InitializeComponent()
{
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// richTextBox1
//
this.richTextBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.richTextBox1.Location = new System.Drawing.Point(13, 126);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(289, 80);
this.richTextBox1.TabIndex = 0;
this.richTextBox1.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(13, 13);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(92, 23);
this.button1.TabIndex = 3;
this.button1.Text = "top anchor";
this.button1.UseVisualStyleBackColor = true;
//
// button2
//
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.button2.Location = new System.Drawing.Point(13, 75);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(92, 23);
this.button2.TabIndex = 2;
this.button2.Text = "bottom anchor";
this.button2.UseVisualStyleBackColor = true;
//
// button3
//
this.button3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.button3.Location = new System.Drawing.Point(227, 75);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(75, 23);
this.button3.TabIndex = 1;
this.button3.Text = "Hide Rtb";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(314, 226);
this.Controls.Add(this.button1);
this.Controls.Add(this.button2);
this.Controls.Add(this.button3);
this.Controls.Add(this.richTextBox1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.RichTextBox richTextBox1;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button1;
}
}
and Code:
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;
namespace HideShowTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
if (richTextBox1.Visible)
{
richTextBox1.Hide();
this.Height -= richTextBox1.Height;
}
else
{
richTextBox1.Show();
this.Height += richTextBox1.Height;
}
button3.Text = richTextBox1.Visible ? "Hide Rtb" : "Show Rtb";
}
}
}
If I click "Hide Rtb", the bottom anchor button wanders up (it should stay at the same place).
Edit: since you edited the question and asked about a totally different thing:
You have the Anchor property of button2 set to Left and Bottom, so it's clearly going to move when you resize the form in order to maintain the same distance between the button and the bottom of the form.
To avoid that, you can either set the Anchor property to Top or change the location of the button accordingly when changing the size of the form. For example:
int t = button2.Top;
ToggleRtbVisibility();
button2.Top = t;
Original answer:
1) If you want to toggle "show and hide", do hide and show the control, do not remove it.
2) I suspect that you checked the Size property of the form instead of the ClientSize property, but you're changing the value of the ClientSize. Also, don't use fixed numbers for changing the size, calculate them instead.
Try something like this:
private void ToggleRtbVisibility(bool hide)
{
rtbLog.Visible = !hide;
int preservedHeight = rtbLog.Height + rtbLog.Margin.Top + rtbLog.Margin.Bottom;
if (hide)
{
this.Height -= preservedHeight;
}
else
{
this.Height += preservedHeight;
}
}
And if you don't want use a parameter to explicitly specify whether to show or hide the control, you can change the above method to something like:
private void ToggleRtbVisibility()
{
rtbLog.Visible = !rtbLog.Visible;
int preservedHeight = rtbLog.Height + rtbLog.Margin.Top + rtbLog.Margin.Bottom;
if (rtbLog.Visible == false)
this.Height -= preservedHeight;
else
this.Height += preservedHeight;
}
Hope that helps.
Related
Created the following code to workout some bugs in a bigger program. That said, when I click the button to add a row, the next row is added, but the previously created ComboBox (cmboPhoneType) disappears with each new click. What am I missing how to correct? Does this maybe have something to do with the name of the control?
using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using MySql.Data.MySqlClient;
namespace AddNewRowTest
{
public partial class Form1 : Form
{
int leftControl = 1;
ComboBox cmboPhoneType = new ComboBox();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
addLabels();
btn_AddRow.PerformClick();
}
private void btn_AddRow_Click(object sender, EventArgs e)
{
//Connection Information redacted
reConn.Open();
MySqlCommand phoneTypeCommand = new MySqlCommand("Select * from lutable_phonetype order by Type;", reConn);
MySqlDataAdapter phoneTypeAdapter = new MySqlDataAdapter();
DataTable phoneTypeTable = new DataTable();
phoneTypeAdapter.SelectCommand = phoneTypeCommand;
phoneTypeAdapter.Fill(phoneTypeTable);
this.Controls.Add(cmboPhoneType);
cmboPhoneType.Top = leftControl * 36;
cmboPhoneType.Left = 12;
cmboPhoneType.Size = new Size(100, 20);
cmboPhoneType.DataSource = phoneTypeTable;
cmboPhoneType.DisplayMember = "Type";
cmboPhoneType.ValueMember = "TypeID";
cmboPhoneType.DataBindings.Add("SelectedValue", phoneTypeTable, "TypeID");
cmboPhoneType.SelectedIndexChanged += new System.EventHandler(cmboPhoneType_SelectedIndexChanged);
MaskedTextBox msktxtPhone = new MaskedTextBox();
this.Controls.Add(msktxtPhone);
msktxtPhone.Top = leftControl * 36;
msktxtPhone.Left = 116;
msktxtPhone.Size = new Size(100, 20);
msktxtPhone.Mask = "(999) 000-0000";
reConn.Close();
leftControl++;
}
private void addLabels()
{
Label lblPhoneType = new Label();
this.Controls.Add(lblPhoneType);
lblPhoneType.Top = leftControl * 12;
lblPhoneType.Left = 12;
lblPhoneType.Text = "Phone Type";
Label lblPhoneNumber = new Label();
this.Controls.Add(lblPhoneNumber);
lblPhoneNumber.Top = leftControl * 12;
lblPhoneNumber.Left = 116;
lblPhoneNumber.Text = "Phone Number";
}
private void cmboPhoneType_SelectedIndexChanged(object sender, EventArgs e)
{
cmboPhoneType.DataBindings.Clear();
}
}
}
Your Adding a "New" Masked Text Box in your Click Event but no "New" Combo Box. Then after you Declare a new cmbophonetype you will need to move, this.Controls.Add(cmboPhoneType); down, to just before you add the SelectedIndexChanged event handler. That will keep the Combobox visible but you may have some more problems separating all of the events.
I'm drawing the following Shape in a Canvas.
I would like to highlight it when it's selected by changing its color (the easy part) and drawing an small halo around it:
This is how I did using SASS: http://codepen.io/aaromnido/pen/zKvAwd/
How coud I draw in WPF? Remember that I'm drawing using the Shape's OnRender method.
Set some defaults in constructor.
One of these defaults is Shape.Effect, as it will be animated on MouseEnter event.
Construct VisualStates for Normal , and MouseEnter scenarios.
Change the VisualState of the element using VisualStateManager.GoToElementState() in MouseEnter and MouseLeave event handlers.
You can expose various properties using DPs for customization.
NewShape.cs
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows;
using System.Windows.Media.Effects;
namespace WpfStackOverflow.NewShape
{
public class CNewShape : Shape
{
public CNewShape()
{
// setting the defaults
this.Width = 40;
this.Height = 40;
this.Stroke = new SolidColorBrush() { Color = Colors.Red };
this.StrokeThickness = 5;
this.Effect = new DropShadowEffect() {
Color = Colors.Transparent,
BlurRadius = 1,
Direction = -150,
ShadowDepth = 1
};
// constructing the VisualStates
_constructVisualStates();
// event handlers
this.MouseEnter += CNewShape_MouseEnter;
this.MouseLeave += CNewShape_MouseLeave;
}
#region EventHandlers
void CNewShape_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
VisualStateManager.GoToElementState(this, "VSNormal", false);
}
void CNewShape_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
VisualStateManager.GoToElementState(this, "VSMouseEnter", false);
}
#endregion
#region Overrides
// This needs to be implemented as it is abstract in base class
GeometryGroup geo = new GeometryGroup();
protected override Geometry DefiningGeometry
{
get { return geo; }
}
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
Pen pen = new Pen(this.Stroke, StrokeThickness);
drawingContext.DrawEllipse(Brushes.Transparent, pen, new Point(Width/2, Height/2), 40, 40);
drawingContext.DrawEllipse(Stroke, null, new Point(Width / 2, Height / 2), 30, 30);
base.OnRender(drawingContext);
}
#endregion
#region Helpers
private void _constructVisualStates()
{
VisualStateGroup vsg1 = new VisualStateGroup();
#region VSNormal (Normal Visual State)
VisualState stateVSNormal = new VisualState() { Name = "VSNormal" };
Storyboard sbVSNormal = new Storyboard();
ObjectAnimationUsingKeyFrames oa = new ObjectAnimationUsingKeyFrames();
Storyboard.SetTargetProperty(oa, new PropertyPath("Effect"));
DiscreteObjectKeyFrame dokf = new DiscreteObjectKeyFrame(null);
oa.KeyFrames.Add(dokf);
sbVSNormal.Children.Add(oa);
stateVSNormal.Storyboard = sbVSNormal;
vsg1.States.Add(stateVSNormal);
#endregion
#region VSMouseEnter (MouseEnter Visual State)
VisualState stateVSMouseEnter = new VisualState() { Name = "VSMouseEnter" };
Storyboard sbVSMouseEnter = new Storyboard();
ColorAnimation caStrokeColor = new ColorAnimation();
caStrokeColor.To = (Color)ColorConverter.ConvertFromString("#FF24BCDE");
Storyboard.SetTargetProperty(caStrokeColor, new PropertyPath("(Shape.Stroke).(SolidColorBrush.Color)"));
sbVSMouseEnter.Children.Add(caStrokeColor);
ColorAnimation caEffectColor = new ColorAnimation();
caEffectColor.To = (Color)ColorConverter.ConvertFromString("#FFA4E1F3");
Storyboard.SetTargetProperty(caEffectColor, new PropertyPath("(Shape.Effect).(Color)"));
sbVSMouseEnter.Children.Add(caEffectColor);
DoubleAnimation daBlurRadius = new DoubleAnimation();
daBlurRadius.To = 10;
Storyboard.SetTargetProperty(daBlurRadius, new PropertyPath("(Shape.Effect).(BlurRadius)"));
sbVSMouseEnter.Children.Add(daBlurRadius);
DoubleAnimation daDirection = new DoubleAnimation();
daDirection.To = -190;
Storyboard.SetTargetProperty(daDirection, new PropertyPath("(Shape.Effect).(Direction)"));
sbVSMouseEnter.Children.Add(daDirection);
stateVSMouseEnter.Storyboard = sbVSMouseEnter;
vsg1.States.Add(stateVSMouseEnter);
#endregion
VisualStateManager.GetVisualStateGroups(this).Add(vsg1);
}
#endregion
}
}
Usage
<local:CNewShape Canvas.Left="70" Canvas.Top="52" Stroke="#FF374095" StrokeThickness="10" Width="100" Height="100" />
Output
Quality of the image is bad. On screen actual output looks good.
Whatever your trigger is that your control enters the Highlighted state, in that trigger just set the Effect property. For my test the "trigger" is a property:
public static readonly DependencyProperty ShowShadowProperty =
DependencyProperty.Register ("ShowShadow", typeof (bool), typeof (TestShape), new PropertyMetadata (false, ShowShadowChanged));
public bool ShowShadow
{
get { return (bool)GetValue (ShowShadowProperty); }
set { SetValue (ShowShadowProperty, value); }
}
private static void ShowShadowChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TestShape)d).OnShowShadow ();
}
private void OnShowShadow ()
{
if (ShowShadow)
{
Effect = new DropShadowEffect { Direction = 0, ShadowDepth = 20, BlurRadius = 33, Opacity = 1, Color = Colors.Black};
}
else
{
Effect = null;
}
}
Which means you don't need to do anything in OnRender.
I would like to get the form as datasource when i click combo box. How could i achieve this. Please give suggestion.
I've created a PopupComboBox class which inherits from the standard Windows Forms ComboBox. It has a Title property that you can set which will be used on the popup window when the combo is clicked.
public class PopupComboBox : ComboBox
{
private string title;
public string Title
{
get { return this.title; }
set { this.title = value; }
}
public PopupComboBox() : base()
{
}
public PopupComboBox (string title)
: base()
{
this.title = title;
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
// Show the popup form
var popup = new SelectItemForm(this.title, this);
var result = popup.ShowDialog(this);
if (result == DialogResult.OK)
{
// Select this item in the ComboBox
SelectedIndex = this.FindStringExact(popup.SelectedDisplay);
}
}
}
When you click on the combo, a popup form will come up (source code below for SelectItemForm). It uses the DataSource, ValueMember and DisplayMember of the parent PopupComboBox to populate a ListView with the list of items from the combo. When you click on OK, it will save the selected item in the SelectedValue and SelectedDisplay properties so that we can select that item in the ComboBox when the form closes.
public partial class SelectItemForm : Form
{
public object SelectedValue { get; private set; }
public string SelectedDisplay { get; private set; }
public SelectItemForm(string title, PopupComboBox parent)
:base()
{
InitializeComponent();
this.Text = title;
// Add items to the list
foreach (var item in parent.Items)
{
// Get the display and value member properties for this combo box
// and use them for the Code/Name columns in the popup form
var props = item.GetType().GetProperties();
var code = props.Where(p => p.Name == parent.ValueMember).Single().GetValue(item);
var name = props.Where(p => p.Name == parent.DisplayMember).Single().GetValue(item);
listView.Items.Add(new ListViewItem(
new string[] { code.ToString(), name.ToString() }));
}
}
private void btnOk_Click(object sender, EventArgs e)
{
if (listView.SelectedItems != null && listView.SelectedItems.Count > 0)
{
SelectedValue = listView.SelectedItems[0].Text;
SelectedDisplay = listView.SelectedItems[0].SubItems[1].Text;
DialogResult = DialogResult.OK;
}
else
{
MessageBox.Show(this, "Select an item first", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
}
}
Here is the designer part of the form, where you can see what properties I've changed for the ListView to look like it does:
partial class SelectItemForm
{
/// <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()
{
this.listView = new System.Windows.Forms.ListView();
this.btnOk = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.Code = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.Value = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.SuspendLayout();
//
// listView
//
this.listView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.Code,
this.Value});
this.listView.FullRowSelect = true;
this.listView.GridLines = true;
this.listView.Location = new System.Drawing.Point(3, 3);
this.listView.MultiSelect = false;
this.listView.Name = "listView";
this.listView.Size = new System.Drawing.Size(432, 170);
this.listView.TabIndex = 0;
this.listView.UseCompatibleStateImageBehavior = false;
this.listView.View = System.Windows.Forms.View.Details;
//
// btnOk
//
this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOk.Location = new System.Drawing.Point(272, 179);
this.btnOk.Name = "btnOk";
this.btnOk.Size = new System.Drawing.Size(75, 23);
this.btnOk.TabIndex = 1;
this.btnOk.Text = "OK";
this.btnOk.UseVisualStyleBackColor = true;
this.btnOk.Click += new System.EventHandler(this.btnOk_Click);
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Location = new System.Drawing.Point(353, 179);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 2;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// Code
//
this.Code.Text = "Code";
this.Code.Width = 108;
//
// Value
//
this.Value.Text = "Name";
this.Value.Width = 296;
//
// SelectItemForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(440, 214);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOk);
this.Controls.Add(this.listView);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "SelectItemForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Title";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListView listView;
private System.Windows.Forms.Button btnOk;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.ColumnHeader Code;
private System.Windows.Forms.ColumnHeader Value;
}
I've also created a small test form where you can test out the functionality of the PopupComboBox, using a small list of products. You'll need to add a PopupComboBox to the form in the designer, and call it comboPopup.
public partial class TestForm : Form
{
public class Product
{
public string Code { get; set; }
public string Name { get; set; }
}
public TestForm()
{
InitializeComponent();
}
private void TestForm_Load(object sender, EventArgs e)
{
var products = new List<Product>();
products.Add(new Product { Code = "0001", Name = "Coca Cola" });
products.Add(new Product { Code = "0002", Name = "Mountain Dew" });
products.Add(new Product { Code = "0003", Name = "Sprite Zero" });
comboPopup.DataSource = products;
comboPopup.DisplayMember = "Name";
comboPopup.ValueMember = "Code";
}
}
I have a MainWindow with a Border and a ChildWindow as Dialog without a Border. When a child window is open it's not possible to move the mainwindow or to resize it.
I want the application to behave as it is only one Window.
I have tried to use an behavior as in th following link but that is only moving my child window inside of the mainwindow.
DragBahvior
There is a much easier way to enable the dragging, or moving of borderless Windows. Please see the Window.DragMove Method page on MSDN for more details, but in short, you just need to add this line to your code in one of the mouse down event handlers:
public YourWindow()
{
InitializeComponent();
MouseLeftButtonDown += YourWindow_MouseLeftButtonDown;
}
...
private void YourWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove(); // <-- this is all you need to add
}
Users will then be able to click on any area of the Window (dependant upon what you put inside it) and drag it around the screen.
UPDATE >>>
So it seems as though there is more to your requirements than I first noticed. To achieve what you want, there are a number of things that you must do. First, you'll need to position the child Window in a particular place relative to the MainWindow.xaml Window. As you open it, do something like this:
Window window = new Window();
window.Top = this.Top;
window.Left = this.Left;
window.LocationChanged += Window_LocationChanged;
window.ShowDialog();
The child Window position could be offset by some set amount:
Window window = new Window();
window.Top = this.Top + someHorizontalOffsetAmount;
window.Left = this.Left + someVerticalOffsetAmount;
window.LocationChanged += Window_LocationChanged;
window.ShowDialog();
Then you need a handler for the Window.LocationChanged event (which is raised when the child Window is moved):
private void Window_LocationChanged(object sender, EventArgs e)
{
Window window = (Window)sender;
this.Top = window.Top;
this.Left = window.Left;
}
That's it! Now the two Windows will move together. Obviously, if you use an offset in the first example, then you'll need to use the same offset(s) in the Window_LocationChanged handler.
It sounds like your dialog is Modal, ie it was invoked with ShowDialog() and stops you using the rest of the application until it is dismissed, including moving the main window.
If this is not the behaviour you want, then you will need to make your dialog modeless by just calling Show(), or better yet, since you seem to want it to behave as one window, why not use WPF as it was intended and get rid of the dialog altogether?
So I finally found an solution. I wrote an extension to the Windows class and it was quit complicated :)
namespace MultiWindowWPF
{
using System;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Application = System.Windows.Application;
public static class WindowExtensions
{
/// <summary>
/// Shows the Dialog Modal.
/// </summary>
/// <param name="dialogWindow">The dialog window.</param>
public static void ShowModal(this Window dialogWindow)
{
Window window = Application.Current.Windows.OfType<Window>().FirstOrDefault(w => w.IsKeyboardFocusWithin) ?? Application.Current.MainWindow;
IInputElement lastFocused = FocusManager.GetFocusedElement(window);
IInputElement lastKeyboardSelected = Keyboard.FocusedElement;
EventHandler locationChanged = (sender, args) => ParentWndMove(dialogWindow);
SizeChangedEventHandler sizeChanged = (sender, args) => ParentWndMove(dialogWindow);
EventHandler stateChanged = (sender, args) => ParentWndStateChanged(dialogWindow);
window.LocationChanged += locationChanged;
window.SizeChanged += sizeChanged;
window.StateChanged += stateChanged;
EventHandler close = (sender, args) =>
{
if (dialogWindow.Dispatcher.CheckAccess())
{
dialogWindow.Close();
}
else
{
dialogWindow.Dispatcher.Invoke(dialogWindow.Close);
}
window.LocationChanged -= locationChanged;
window.SizeChanged -= sizeChanged;
window.StateChanged -= stateChanged;
};
EventHandler closed = (sender, args) =>
{
Window self = sender as Window;
Enable();
if (self != null)
{
self.Owner = null;
}
};
ExitEventHandler exit = (sender, args) => close(sender, args);
DependencyPropertyChangedEventHandler isEnabledChanged = null;
isEnabledChanged = (o, eventArgs) =>
{
window.Dispatcher.BeginInvoke(
DispatcherPriority.ApplicationIdle,
new Action(
() =>
{
FocusManager.SetFocusedElement(window, lastFocused);
Keyboard.Focus(lastKeyboardSelected);
}));
((Window)o).IsEnabledChanged -= isEnabledChanged;
};
window.IsEnabledChanged += isEnabledChanged;
dialogWindow.Closed += closed;
Application.Current.Exit += exit;
dialogWindow.Show();
ParentWndMove(dialogWindow);
while (Application.Current != null)
{
DoEvents();
}
}
private static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
Thread.Sleep(10);
}
private static void Enable()
{
foreach (Window window in Application.Current.Windows.OfType<Window>().Where(window => !window.OwnedWindows.OfType<Window>().Any()))
{
window.IsEnabled = true;
}
}
/// <summary>
/// Exits the frame.
/// </summary>
/// <param name="f">The f.</param>
/// <returns></returns>
public static object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
/// <summary>
/// Parents the WND state changed.
/// </summary>
/// <param name="dialogWindow">The dialog window.</param>
public static void ParentWndStateChanged(Window dialogWindow)
{
Window owner = dialogWindow.Owner;
if (owner.WindowState != WindowState.Maximized)
{
dialogWindow.WindowState = owner.WindowState;
}
ParentWndMove(dialogWindow);
}
/// <summary>
/// Parents the WND move.
/// </summary>
/// <param name="dialogWindow">The dialog window.</param>
public static void ParentWndMove(Window dialogWindow)
{
Window owner = dialogWindow.Owner;
PresentationSource presentationsource = PresentationSource.FromVisual(dialogWindow);
Matrix m = presentationsource.CompositionTarget.TransformToDevice;
double centerWidth = owner.Left + owner.ActualWidth / 2;
double centerHeight = owner.Top + owner.ActualHeight / 2;
if (owner.WindowState == WindowState.Normal)
{
dialogWindow.Top = centerHeight - dialogWindow.ActualHeight / 2;
dialogWindow.Left = centerWidth - dialogWindow.ActualWidth / 2;
}
if (owner.WindowState == WindowState.Maximized)
{
//there is no current main window position to use, center on working screen
Rectangle frame = Screen.FromPoint(new System.Drawing.Point((int)(dialogWindow.Left * m.M11), (int)(dialogWindow.Top * m.M22))).Bounds;
dialogWindow.Left = frame.X / m.M11 + (frame.Width / m.M11 - dialogWindow.ActualWidth) / 2;
dialogWindow.Top = frame.Y / m.M22 + (frame.Height / m.M22 - dialogWindow.ActualHeight) / 2;
}
}
}
}
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.