When the user tabs into a CheckBox to give it focus, a dotted border appears around the CheckBox to indicate that it has focus.
When the CheckBox gets focused by code calling myCheckBox.Focus(), no such indicator appears (even though pressing the space bar toggles the state).
How can I make the CheckBox focus border appear when I have programmatically focused the CheckBox?
The border is intentionally only shown if you are navigating by the keyboard (Tab key). The MSDN page on this topic has further details:
Focus visual styles act only when the
focus action was initiated by the
keyboard. Any mouse action or
programmatic focus change disables the
mode for focus visual styles.
If you want to show a border, you could use a Trigger on the IsFocused- Property to do some visual changes (although you can't set the border with this) or if you actually want a border, you would have to create your own ControlTemplate.
There is also a thread here on SO on a somewhat related topic where the suggestion is to simulate a key press, but I would suggest not to use this solution for your problem.
By editing the KeyboardNavigationEx file from ControlzEx I managed to solve the issue (full credit goes, as always, to punker76).
Just call the KeyboardHelper.Focus method passing the UIElement that shoud be focused (e.g. KeyboardHelper.Focus(myCheckBox))
Here's the KeyboardHelper class:
public sealed class KeyboardHelper
{
private static KeyboardHelper _Instance;
private readonly PropertyInfo _AlwaysShowFocusVisual;
private readonly MethodInfo _ShowFocusVisual;
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static KeyboardHelper()
{
}
private KeyboardHelper()
{
var type = typeof(KeyboardNavigation);
_AlwaysShowFocusVisual = type.GetProperty("AlwaysShowFocusVisual", BindingFlags.NonPublic | BindingFlags.Static);
_ShowFocusVisual = type.GetMethod("ShowFocusVisual", BindingFlags.NonPublic | BindingFlags.Static);
}
internal static KeyboardHelper Instance => _Instance ?? (_Instance = new KeyboardHelper());
internal void ShowFocusVisualInternal()
{
_ShowFocusVisual.Invoke(null, null);
}
internal bool AlwaysShowFocusVisualInternal
{
get { return (bool)_AlwaysShowFocusVisual.GetValue(null, null); }
set { _AlwaysShowFocusVisual.SetValue(null, value, null); }
}
public static void Focus(UIElement element)
{
element?.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
var keybHack = KeyboardHelper.Instance;
var oldValue = keybHack.AlwaysShowFocusVisualInternal;
keybHack.AlwaysShowFocusVisualInternal = true;
try
{
Keyboard.Focus(element);
keybHack.ShowFocusVisualInternal();
}
finally
{
keybHack.AlwaysShowFocusVisualInternal = oldValue;
}
}));
}
}
'initially set chkCheckBox.Appearance = 1
'on Got Focus set appearance = 0 - Flat
Private Sub chkCheckBox_GotFocus()
chkCheckBox.Appearance = 0
End Sub
'on Lost Focus set appearance = 1 - 3D
Private Sub chkCheckBox_LostFocus()
chkCheckBox.Appearance = 1
End Sub
Related
Imagine the following construction in WinForms .NET. A WinForms form contains a custom control with several buttons, which are instances of the traditional Button class. One of these buttons is the default button for the form. The custom control executes the action associated with the default button when ENTER is pressed. This is done in the redefined ProcessCmdKey method:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Return)
{
buttonOK_Click(null, EventArgs.Empty);
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
The default button must have an additional visual cue telling the user that this is the default button (an extra border inside the button). If we did this in a normal form, we would set its AcceptButton property. However, this approach is not applicable here. Even if we find the parent form using the Control.FindForm method or with an expression like (this.Parent as Form), we cannot set the AcceptButton property of the host form and then clear it the right way without resource leak or similar problems (a lot of technical details to place here and to bloat the question).
The first possible way to solve this task is to redefine or enhance the drawing of the button. Is there a relatively easy way to draw a button as the default button with the corresponding visual cue without implementing full custom painting? In my understanding, we might write a special class for our default button based on the following core:
internal class DefaultButton : Button
{
protected override void OnPaint(PaintEventArgs pevent)
{
Rectangle rc = new Rectangle(0, 0, this.Width, this.Height);
ButtonRenderer.DrawButton(pevent.Graphics, rc, System.Windows.Forms.VisualStyles.PushButtonState.Default);
}
}
However, it should take into account the focused state, whether another button on a form is focused (in this case the default button is not drawn with the visual cue), and the like. I could not find a good example of this to use as a basis for my development.
Another possible way to solve my problem could be setting the protected IsDefault property or/and specifying the BS_DEFPUSHBUTTON flag in the overridden CreateParams method in a class inherited from the Button class, for example:
internal class DefaultButton : Button
{
public DefaultButton() : base()
{
IsDefault = true;
}
protected override CreateParams CreateParams
{
get
{
const int BS_DEFPUSHBUTTON = 1;
CreateParams cp = base.CreateParams;
cp.Style |= BS_DEFPUSHBUTTON;
return cp;
}
}
}
But I could not make this code work. Buttons based on this class are always drawn as normal push buttons without the default button visual cue.
I'm not sure about the original requirement; for example I don't have any idea why a UserControl itself should set the AcceptButton of a Form, or what is the expected behavior if there are multiple instances of such controls on the form. It doesn't seem to be responsibility of the UserControl to set the AcceptButton of the Form and there might be better solutions, like relying on events and setting the AcceptButton.
Anyways, the following code example shows you how to set the AcceptButton of a Form; maybe it helps you to find a solutions. The highlights of the code:
The code uses dispose to set the AcceptButton to null.
The code implements ISupportInitialize to set the accept button after initialization of the control is done. If you create the control instance at run-time with code, don't forget to call EndInit, like this: ((System.ComponentModel.ISupportInitialize)(userControl11)).EndInit(); after adding it to the Form, but if you use designer, the designer will take care of that.
The code calls NotifyDefault(true) just for visual effect in design time when it's hosted on a form.
Here's the example:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public class UserControl1 : UserControl, ISupportInitialize
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
#region Component 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.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(15, 57);
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);
//
// button2
//
this.button2.Location = new System.Drawing.Point(96, 57);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 23);
this.button2.TabIndex = 1;
this.button2.Text = "button2";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(15, 17);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 2;
//
// UserControl1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "UserControl1";
this.Size = new System.Drawing.Size(236, 106);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBox1;
public System.Windows.Forms.Button button1;
public System.Windows.Forms.Button button2;
public UserControl1()
{
InitializeComponent();
//Just for visual effect in design time when it's hosted on a form
button2.NotifyDefault(true);
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("1");
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show("2");
}
public void BeginInit()
{
}
public void EndInit()
{
var f = this.FindForm();
if (f != null)
f.AcceptButton = button2;
}
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
if (disposing)
{
var f = this.FindForm();
if (f != null)
f.AcceptButton = null;
}
base.Dispose(disposing);
}
}
}
Given the following insane setup (a ComboBox inside a UserControl inside a ToolStripControlHost inside a ContextMenuStrip):
there's something odd going on with clicking on different items in the ComboBox popup. If the item is inside the menu bounds (i.e. Amsterdam, Brussel or Luxemburg) the item is selected. If the item is outside the menu bounds (i.e. Berlijn and further) the menu is closed immediately.
Ignoring any spiffy remarks regarding the sheer crazy, anyone know what's going on and how to stop the menu from closing if a distant combobox item is selected?
The problem is due to a check deep in the ToolStripManager determining the mouse event is not on a child.
Basically you need to reject the ContextMenuStrip.OnClosing event if the ComboBox is displaying. There's inevitably a cleaner solution but I didn't see one.
public bool IsDropDownShowing { get; private set; }
private void InitializeContextMenu()
{
var userControl = new ComboMenuUserControl();
var toolStripHost = new ToolStripControlHost(userControl);
contextMenuStrip1.Items.Add(toolStripHost);
userControl.comboBox1.DropDown += (o, args) => IsDropDownShowing = true;
userControl.comboBox1.DropDownClosed += (o, args) => IsDropDownShowing = false;
contextMenuStrip1.Closing += (o, args) =>
{
if (IsDropDownShowing == true)
args.Cancel = true;
};
}
I have a Window with seven buttons; I use it as a menu in a simple game I am working on, but I display it as a dialog. How can I know which button user has pressed, since DialogResult in WPF only offers true, false and null?
If you're making a custom Window in this way, you don't really need to worry about DialogResult.
You can keep track of this in a property within your Window, and just read the property after the dialog is closed.
MyDialog window = new MyDialog();
if (window.ShowDialog() == false)
{
// user closed the window...
}
var choice = window.CustomPropertyContainingChoice;
Define your own enum and offer a static method to display the window that return your enum.
The code below does the same thing it is part of a window that allows users to review their changes and accept or cancel. As I only need true and false I used a bool however it would be trivial to change to an enum.
public static bool DisplayChanges(List<INormalizedMessage> LstMessages)
{
var retlist = LstMessages.Where(( INormalizedMessage NM ) => { return NM.Status != NormalizedMessageStatus.NoChange; });
ReviewChanges RC = new ReviewChanges();
RC.Messages = retlist.ToList();
RC.ShowDialog();
return RC.Result;
}
private void cmdCancle_Click( object sender, RoutedEventArgs e )
{
Result = false;
Hide();
}
private void cmdOK_Click( object sender, RoutedEventArgs e )
{
Result = true;
Hide();
}
My goal is to create a custom TextBlock control that has a new dependency property, SearchText. This property will contain a regular expression. All occurrences of this regular expression in the text of the TextBlock will be highlighted using a custom style (another DP).
My current implementation involves clearing all of the Inline objects in the TextBlock's InlineCollection. I then fill the TextBlock with runs for unhighlighted text and runs for highlighted text with the style applied (this method does not support adding inlines directly to the TextBlock, instead TextBlock.TextProperty has to be used).
Works great, but sometimes I get a strange exception when trying to clear the Inlines: InvalidOperationException: "Cannot modify the logical children for this node at this time because a tree walk is in progress."
This problem seems to be related to this one. I am modifying the inlines in the TextChanged function, but I'm using a flag to avoid infinite recursive edits.
Any thoughts on how to architect this custom control? Is there a better way to do this? How do I get around this exception?
Thanks!
In my implementation, I solved this by just adding another dependency property, called OriginalText. When it's modified, I updated both the Text property and update the highlighting. Here's the code:
public class HighlightTextBlock : TextBlock
{
public string HighlightedText
{
get { return (string)GetValue(HighlightedTextProperty); }
set { SetValue(HighlightedTextProperty, value); }
}
public static readonly DependencyProperty HighlightedTextProperty =
DependencyProperty.Register("HighlightedText", typeof(string), typeof(HighlightTextBlock), new UIPropertyMetadata(string.Empty, UpdateHighlightEffect));
public static readonly DependencyProperty OriginalTextProperty = DependencyProperty.Register(
"OriginalText", typeof(string), typeof(HighlightTextBlock), new PropertyMetadata(default(string), OnOriginalTextChanged));
private static void OnOriginalTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var block = ((HighlightTextBlock)obj);
block.Text = block.OriginalText;
block.UpdateHighlightEffect();
}
public string OriginalText
{
get { return (string)GetValue(OriginalTextProperty); }
set { SetValue(OriginalTextProperty, value); }
}
private static void UpdateHighlightEffect(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (!(string.IsNullOrEmpty(e.NewValue as string) && string.IsNullOrEmpty(e.OldValue as string)))
((HighlightTextBlock)sender).UpdateHighlightEffect();
}
private void UpdateHighlightEffect()
{
if (string.IsNullOrEmpty(HighlightedText)) return;
var allText = GetCompleteText();
Inlines.Clear();
var indexOfHighlightString = allText.IndexOf(HighlightedText, StringComparison.InvariantCultureIgnoreCase);
if (indexOfHighlightString < 0)
{
Inlines.Add(allText);
}
else
{
Inlines.Add(allText.Substring(0, indexOfHighlightString));
Inlines.Add(new Run()
{
Text = allText.Substring(indexOfHighlightString, HighlightedText.Length),
Background = Consts.SearchHighlightColor,
});
Inlines.Add(allText.Substring(indexOfHighlightString + HighlightedText.Length));
}
}
private string GetCompleteText()
{
var allText = Inlines.OfType<Run>().Aggregate(new StringBuilder(), (sb, run) => sb.Append(run.Text), sb => sb.ToString());
return allText;
}
}
Still not sure if there's a better way to do this altogether, but I appear to have found a work around.
I was updating the inlines/runs in a function that was fired by the change notification for the TextProperty and the SearchTextProperty.
Now I'm firing the highlight/update code from a Dispatcher.BeginInvoke() call in the change notification with DispatcherPriority.Normal.
In case anyone wants an example of how to do this, I found this
I'm trying to create a custom button where the foreColor is always crimson and the backColor is always cyan. Ugly color scheme, but I'm just trying to get it so I can create large amounts of controls with a consistent color scheme without setting each control individually. Here's how I coded the button:
public partial class CustomButton : Button
{
private static Color _defaultForeColor = Color.Crimson;
private static Color _defaultBackColor = Color.Cyan;
public CustomButton()
{
InitializeComponent();
base.ForeColor = _defaultForeColor;
base.BackColor = _defaultBackColor;
}
public AutoScaleMode AutoScaleMode { get; set; }
[DefaultValue(typeof(Color), "Crimson")]
override public Color ForeColor
{
get { return base.ForeColor; }
set
{
base.ForeColor = _defaultForeColor;
}
}
[DefaultValue(typeof(Color), "Cyan")]
public override Color BackColor
{
get { return base.BackColor; }
set
{
base.BackColor = _defaultBackColor;
}
}
}
When I drop the custom button onto my form, the background is the regular button color and the text is crimson. If I run the app it's the same also. Now if I try to modify the forecolor or backcolor in the properties window they go right back to their defaults that I set (crimson, cyan) and then they also show up that way when I run my app. How do I get the controls to show up correctly in the designer and at run time?
The problem exists because UseVisualStyleBackColor is automatically set to true and you can't override it. If you change it to false, you'll notice that your button will work correctly.
One option is to override OnControlAdded of the button like this:
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
UseVisualStyleBackColor = false;
}
First time in the designer, the color won't show, but when you run the application it will work correctly.
I suggest you bind fore and back colors to, for example, application settings (Or settings class specific to your controls). Standard button and bind in designer, or use your own descendant and bind in code. In this case you will have consistent color scheme, and, more important, you can change it without recompilation.
public class CustomButton : Button
{
public CustomButton
{
InitializeComponent();
if (!DesignMode)
{
DataBindings.Add(new Binding("ForeColor", Settings.Default, "ButtonForeColor", true, DataSourceUpdateMode.Never));
DataBindings.Add(new Binding("BackColor", Settings.Default, "ButtonBackColor", true, DataSourceUpdateMode.Never));
}
}
// ...