WPF: reverting brush to default/original - wpf

I'm a complete newbie at WPF.
At the moment I'm making a usercontrol for form elements called "LabeledTextbox" which contains a label, a textbox and a textblock for errormessages.
When the using code adds an errormessage, I want to put the border of the textbox in red. But, when the errormessage gets removed, I'd like to turn back to the default bordercolor of the textbox.
I feel there must be a very easy way to do this.
My code:
(in public partial class LabeledTextbox : UserControl)
public string ErrorMessage
{
set
{
if (string.IsNullOrEmpty(value))
{
_textbox.BorderBrush = Brushes.Black; //How do I revert to the original color in the most elegant way?
}
else
{
_textbox.BorderBrush = Brushes.Red;
}
_errorMessage.Text = value;
}
}

You could use
_textBox.ClearValue(TextBox.BorderBrushProperty);
That will remove the directly assigned value and go back to the value defined by the style or template.

You can grab the default colours from the class SystemColors
Here is the list of all system colours:
http://msdn.microsoft.com/de-de/library/system.windows.systemcolors.aspx
Default background colour of the client area:
_textbox.Background = SystemColors.WindowBrush;
Default text colour inside the client area:
_textbox.SystemColors.WindowTextBrush

I may be late to the party, but for future readers, you can also use Button.BackgroundProperty.DefaultMetadata.DefaultValue for this purpose. This is especially useful when you're using a Converter where you need to return a value and therefore cannot use ClearValue() call.

Does this work? Setting it to black is better than using the ClearValue method
public string ErrorMessage
{
set
{
if (string.IsNullOrEmpty(value))
{
_textbox.Background = Brushes.Black;
}
else
{
_textbox.Background = Brushes.Red;
}
_errorMessage.Text = value;
}
}

Just store the default settings. Here a code excample.
System.Windows.Media.Brush save;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Store the default background
save = testButton.Background;
}
private void ChangeBackground(){
testButton.Background = Brushes.Red;
}
private void restoreDefaultBackground(){
//Restore default Backgroundcolor
testButton.Background = save;
}

Related

Quickest way to hide an array of pictureboxes

I have an array of pictureboxes named from B11 (co-ords 1,1) to B55 (co-ords 5,5). I would like to hide these all on startup (and in the middle of running). I was thinking of making an array of the names manually but would it be the best solution?
If they all have a common parent control, such as a panel or groupbox (or even the form):
Parent.SuspendLayout()
For Each pbox As PictureBox in Parent.Controls.OfType(Of PictureBox)()
pbox.Visible = False
Next pbox
Parent.ResumeLayout()
The Suspend/Resume-Layout() is to avoid flickering as you modify a bunch of controls at once.
You could extend the PictureBox class and use event handling to accomplish this by:
Adding a public property to the form to tell if the picture boxes should be shown or hidden.
Adding an event to the form that is raised when the show/hide picture box property is changed.
Extending the PictureBox class so that it subscribes to the event of the parent form.
Setting the visible property of the extended PictureBox class to the show/hide property of the parent form.
When the show/hide flag is changed on the parent form all of the picture boxes will change their visibility property accordingly.
Form Code:
public partial class PictureBoxForm : Form {
public PictureBoxForm() {
InitializeComponent();
this.pictureBoxesAdd();
}
private void pictureBoxesAdd() {
MyPictureBox mp1 = new MyPictureBox();
mp1.Location = new Point(1, 1);
MyPictureBox mp2 = new MyPictureBox();
mp2.Location = new Point(200, 1);
this.Controls.Add(mp1);
this.Controls.Add(mp2);
}
public event EventHandler PictureBoxShowFlagChanged;
public bool PictureBoxShowFlag {
get { return this.pictureBoxShowFlag; }
set {
if (this.pictureBoxShowFlag != value) {
pictureBoxShowFlag = value;
if (this.PictureBoxShowFlagChanged != null) {
this.PictureBoxShowFlagChanged(this, new EventArgs());
}
}
}
}
private bool pictureBoxShowFlag = true;
private void cmdFlip_Click( object sender, EventArgs e ) {
this.PictureBoxShowFlag = !this.PictureBoxShowFlag;
}
}
Extended PictureBox Code:
public class MyPictureBox : PictureBox {
public MyPictureBox() : base() {
this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.ParentChanged += new EventHandler(MyPictureBox_ParentChanged);
}
private void MyPictureBox_ParentChanged( object sender, EventArgs e ) {
try {
PictureBoxForm pbf = (PictureBoxForm)this.Parent;
this.Visible = pbf.PictureBoxShowFlag;
pbf.PictureBoxShowFlagChanged += new
EventHandler(pbf_PictureBoxShowFlagChanged);
} catch { }
}
private void pbf_PictureBoxShowFlagChanged( object sender, EventArgs e ) {
PictureBoxForm pbf = (PictureBoxForm)sender;
this.Visible = pbf.PictureBoxShowFlag;
}
}
...or just put 'em all on a Panel, and change the panel's visibility.

override the super class methods in wpf Window

I wanna make a customized window base. So I made a custom window which inherits from Window.
For example:
public class MyWindowBase : Window
{
...
...
}
I wanna override the different Brushes of the super class Window for my own purpose.
In my previous experience, to override methods/properties with no abstract or virtual in the super class need the key word "new".
For example:
public new void DoSomething() { ........ base.DoSomething() ....... }
public new string SomeText { get { ... } set {......} }
It works in my previous work.
However, in this time of dealing with WPF Window, it doesn't work.
I tried to override the different Brushes of the super class Window as follows:
public new Brush BorderBrush
{
get { ... }
set { myBorderBrush = value; base.BorderBrush = null }
}
public new Brush Background
{
get { ... }
set { myBackground = value; base.Backgound = null; }
}
.....
.....
.....
I tried the change the value of the above Brushes in MyWindowBase, it just change the value of the super class Window, it doesn't change the value of myBorderBrush and myBackground.
So, how could I override the methods and properties of the super class Window?
Actually, I want to override the base background so that it will be null or transparent forever, but the changed value will be applied on my own custom Background.
Thank you very much!
If you only want to set the value, then you can set it using
this.BorderBrush = Brushes.Blue;
this.Background = Brushes.Red;
If you wish to overwrite the Property Metadata (for things like default value, property changed logic, or validation), you can use OverrideMetadata
Window.BackgroundProperty.OverrideMetadata(
typeof(MyWindowBase), myPropertyMetadata);
If you simply want to add some logic on Changed, you can use a DependencyPropertyDescriptor
var dpd = DependencyPropertyDescriptor.FromProperty(
Window.BackgroundProperty, typeof(Window));
dpd.AddValueChanged(this.Value, new EventHandler(BackgroundChanged));
private void BackgroundChanged(object sender, EventArgs e)
{
//do the code here
}
And if you're looking to override a Method, and not a Property, then you can use the override keyword
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
}

How to make CheckBox focus border appear when calling CheckBox.Focus()?

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

Custom Winforms Controls (a button specifically)

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));
}
}
// ...

Winforms DataBind to Control's Visible Property

Are there any known issues when databinding to a control's visible property?
The control is always NOT visible regardless of what my property is.
Public ReadOnly Property IsRibbonCategory() As Boolean
Get
Return True
End Get
End Property
I tried the control's text property and other properties and they seem to work correctly.
I am trying to set a Panel's visible property.
I've found that life is better if you assume that binding to a control's Visible property is broken, despite the fact that it sometimes works. See http://support.microsoft.com/kb/327305, which says as much (and while the KB article applies to .NET 1.0 and 1.1, it still seems to be a problem in at least 2.0).
I created a utility class for creating bindings which, among other things, gave me a centralized place to add a work-around. Instead of actually creating a binding on Visible it does two things:
It subscribes to the data source's INotifyPropertyChanged.PropertyChanged event and sets the Visible value as appropriate when the event is raised.
It sets the initial value of Visible according to the current data source value.
This required a little reflection code, but wasn't too bad. It is critical that you don't bind the Visible property and do the work-around or it won't work.
Workaround: Set the Visible property on the BindingComplete event.
I had same issue setting a label's Visible property - always stays false, even though setting the Enabled property works fine.
I just hit this issue in .NET 4.7.1 and Visual Studio 2017. To fix it, I changed the Visible property on my control to be initially set to True, as I had it as False previously.
Things to check:
Be sure you've instantiated the class that has the IsRibbonCategory property
Did you set the datasource of property of the binding source to the instance of the class
The datasource update mode should be on "on validation"
Make sure you didn't set the visible property manually to false on the control
Hope that helps. Can you post more code?
A workaround would be to use a Component to databind to a control's visiblity property instead of directly binding to the control's visibility property.
See below code:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public class ControlVisibilityBinding : Component
{
private static readonly object EventControlChanged = new object();
private static readonly object EventVisibleChanged = new object();
private System.Windows.Forms.Control _control;
private bool _visible = true;
public event EventHandler VisibleChanged
{
add { Events.AddHandler(EventVisibleChanged, value); }
remove { Events.RemoveHandler(EventVisibleChanged, value); }
}
public event EventHandler ControlChanged
{
add { Events.AddHandler(EventControlChanged, value); }
remove { Events.RemoveHandler(EventControlChanged, value); }
}
public ControlVisibilityBinding()
{
}
public ControlVisibilityBinding(IContainer container)
{
container.Add(this);
}
[DefaultValue(null)]
public System.Windows.Forms.Control Control
{
get { return _control; }
set
{
if(_control == value)
{
return;
}
WireControl(_control, false);
_control = value;
if(_control != null)
{
_control.Visible = _visible;
}
WireControl(_control, true);
OnControlChanged(EventArgs.Empty);
OnVisibleChanged(EventArgs.Empty);
}
}
[DefaultValue(true)]
public bool Visible
{
get { return _visible; }
set
{
if(_visible != value)
{
_visible = value;
}
if(Control != null)
{
Control.Visible = _visible;
}
OnVisibleChanged(EventArgs.Empty);
}
}
private void WireControl(Control control, bool subscribe)
{
if(control == null)
{
return;
}
if(subscribe)
{
control.VisibleChanged += Control_VisibleChanged;
}
else
{
control.VisibleChanged -= Control_VisibleChanged;
}
}
private void Control_VisibleChanged(object sender, EventArgs e)
{
OnVisibleChanged(EventArgs.Empty);
}
protected virtual void OnVisibleChanged(EventArgs e)
{
EventHandler subscribers = (EventHandler)Events[EventVisibleChanged];
if(subscribers != null)
{
subscribers(this, e);
}
}
protected virtual void OnControlChanged(EventArgs e)
{
EventHandler subscribers = (EventHandler)Events[EventControlChanged];
if(subscribers != null)
{
subscribers(this, e);
}
}
}
static class Program
{
[STAThread]
static void Main()
{
using(Form form = new Form())
using(FlowLayoutPanel groupBoxLayoutPanel = new FlowLayoutPanel())
using(RadioButton visibleButton = new RadioButton())
using(RadioButton hiddenButton = new RadioButton())
using(GroupBox groupBox = new GroupBox())
using(Label text = new Label())
using(ControlVisibilityBinding visibilityBinding = new ControlVisibilityBinding())
using(TextBox inputTextBox = new TextBox())
{
groupBoxLayoutPanel.Dock = DockStyle.Fill;
groupBoxLayoutPanel.FlowDirection = FlowDirection.LeftToRight;
groupBoxLayoutPanel.AutoSize = true;
groupBoxLayoutPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
visibleButton.Text = "Show Label";
visibleButton.AutoSize = true;
hiddenButton.Text = "Hide Label";
hiddenButton.AutoSize = true;
groupBoxLayoutPanel.Controls.Add(visibleButton);
groupBoxLayoutPanel.Controls.Add(hiddenButton);
inputTextBox.Text = "Enter Label Text Here";
inputTextBox.Dock = DockStyle.Top;
groupBox.AutoSize = true;
groupBox.AutoSizeMode = AutoSizeMode.GrowAndShrink;
groupBox.Controls.Add(groupBoxLayoutPanel);
groupBox.Dock = DockStyle.Fill;
text.AutoSize = true;
text.ForeColor = Color.Red;
text.Dock = DockStyle.Bottom;
text.BorderStyle = BorderStyle.FixedSingle;
text.Font = new Font(text.Font.FontFamily, text.Font.Size * 1.25f, FontStyle.Bold | FontStyle.Italic);
text.DataBindings.Add("Text", inputTextBox, "Text", true, DataSourceUpdateMode.Never);
visibilityBinding.Control = text;
visibleButton.DataBindings.Add("Checked", visibilityBinding, "Visible", true, DataSourceUpdateMode.OnPropertyChanged);
Binding binding = hiddenButton.DataBindings.Add("Checked", visibilityBinding, "Visible", true, DataSourceUpdateMode.OnPropertyChanged);
ConvertEventHandler invertConverter = (sender, e) => e.Value = !((bool)e.Value);
binding.Format += invertConverter;
binding.Parse += invertConverter;
form.Controls.Add(inputTextBox);
form.Controls.Add(text);
form.Controls.Add(groupBox);
Application.Run(form);
}
}
}
}
Here is my turn around, it may be stupid but it worked many times.
I put one Panel control in my form, I make it to Fill my form and I put everything in that Panel. All the controls I bind the Visible property see their visibility change according to the objects in my DataGridView.

Resources