My CustomControl is created with TextBox and ComboBox. And i want to use Validating event for this control. But if i use innerTextBox.Validating this means that will work for TetBox which is ok. But i do not want that this event will fire when i will click on ComboBox which is also part of this UserControl. I want that this UC will be as one. So i can click on TextBox and Combobox and no event will fire becouse they are one together...
innerTextBox is TextBox
innereComboBox is ComboBox
this is my code code event for Validating. What to do that event will not fire when i click on ComboBox?
public new event System.ComponentModel.CancelEventHandler Validating
{
add
{
innerTextBox.Validating += value;
}
remove { innerTextBox.Validating -= value; }
}
Hope you understand my problem.
I think you have to do this yourself. Turn off the CausesValidation property for your inner controls so they DON'T fire, and then run your validating code for the UserControl:
public UserControl1() {
InitializeComponent();
innerTextBox.CausesValidation = false;
innerComboBox.CausesValidation = false;
}
For example, this control requires a non-empty TextBox and a selected item from the ComboBox:
protected override void OnValidating(CancelEventArgs e) {
if (innerTextBox.Text == string.Empty)
e.Cancel = true;
else if (innerComboBox.SelectedIndex == -1)
e.Cancel = true;
base.OnValidating(e);
}
Did you try adding combobox to validating event?
public new event System.ComponentModel.CancelEventHandler Validating
{
add
{
innerTextBox.Validating += value;
innerComboBox.Validating += value;
}
remove
{
innerTextBox.Validating -= value; }
innerComboBox.Validating -= value; }
}
}
Related
I have a custom UserControl called ClosableTabItem which inherits from the TabItem control. I simply added a save button and a close button and I'm trying to wire in some event handlers. When the user clicks on the X (close), I want to invoke a "OnClosing" event with cancellation event arguments so that the user can put in some logic in the OnClosing event and if needed, cancel the close operation just like you can on a windows FormClosing event.
I'm not sure how I can fire the event and wait for a response before removing the tabitem from the collection.
Any ideas?
Thanks.
public class ClosableButtonTabItem : TabItem
{
private readonly cTabButtonHeader _closableTabHeader;
public event EventHandler<TabButtonClickEventArgs> OnTabButtonClick;
public event EventHandler<System.ComponentModel.CancelEventArgs> OnTabClosing;
public event EventHandler OnTabClosed;
public UserControl AttachedForm { get; set; }
public string Title
{
get => ((cTabButtonHeader) this.Header).label_TabTitle.Content.ToString();
set => ((cTabButtonHeader)this.Header).label_TabTitle.Content = value;
}
public ClosableButtonTabItem()
{
_closableTabHeader = new cTabButtonHeader();
Header = _closableTabHeader;
_closableTabHeader.button_close.Source =
ImageHelper.LocalPathToImageSource(ImageHelper.ImageSizes.Size_32x32, "x_off.png");
_closableTabHeader.button_close.MouseEnter += button_close_MouseEnter;
_closableTabHeader.button_close.MouseLeave += button_close_MouseLeave;
_closableTabHeader.button_close.MouseLeftButtonDown += button_close_MouseLeftButtonDown;
_closableTabHeader.label_TabTitle.SizeChanged += label_TabTitle_SizeChanged;
//closableTabHeader.button_group.MouseEnter += button_save_MouseEnter;
//closableTabHeader.button_group.MouseLeave += button_save_MouseLeave;
_closableTabHeader.button_save.MouseLeftButtonDown += button_save_MouseLeftButtonDown;
}
void button_close_MouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
OnTabClosing?.Invoke(this, new CancelEventArgs());
//Code somewhere that if they don't cancel the OnClosing event the run:
((TabControl)Parent).Items.Remove(this); }
}
I found it.
void button_close_MouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
if (OnTabClosing == null)
{
((TabControl)Parent).Items.Remove(this);
OnTabClosed?.Invoke(this, EventArgs.Empty);
return;
}
foreach (var subHandler in OnTabClosing.GetInvocationList())
{
var cea = new TabButtonClosingEventArgs(AttachedForm);
OnTabClosing?.Invoke(this, cea);
if (cea.Cancel) continue;
((TabControl)Parent).Items.Remove(this);
OnTabClosed?.Invoke(this, EventArgs.Empty);
}
}
Today I got a problem in my development.
I have a Windows Form like this :
I need to enable the button "Appliquer" when the content of one of my textbox change.
I know that I can put the KeyPress event on each textbox and enable my button with that. In this window it can be easy to do that because there is only 10 textbox but I have an other window with more of 100 textbox and I think there is a better solution.
I tried to put the Keydown event directly in my windows form but it doesn't work.
So my question is, how can I do this. If someone have an idea ?
Thank you in advance !
Thomas
Since you already have 100+ textboxes in your form. I am assuming performance is not an issue for you.
In your form constructor, call this method. It will attach the event to all the textbox controls present in your form & inside sub controls such as groupbox, panel etc. (if you require)
There could be better ways of iteration..
public Form1()//your constructor
{
InitializeComponent();
AttachEvent(this);
}
void AttachEvent(Control CTrl)
{
foreach (Control c in CTrl.Controls)
{
if (c is TextBox)
{
c.TextChanged += new EventHandler(c_TextChanged);
continue;
}
if (c.HasChildren)
{
AttachEvent(c);
}
}
}
void c_TextChanged(object sender, EventArgs e)
{
//Your Code here btnGo.Enabled = !btnGo.Enabled;
}
What you can do is to extend TextBox make a field ( accessible from the designer ) to bind that TextBox into some other control.
public class MeTextBox
: TextBox
{
public override string Text
{
get { return base.Text; }
set
{
if ( m_DependantControl != null )
{
m_DependantControl.Enabled = !string.IsNullOrWhiteSpace(value);
}
base.Text = value;
}
}
Control m_DependantControl;
[Browsable(true)]
public Control DependantControl
{
get { return m_DependantControl; }
set { m_DependantControl = value; }
}
}
Now you can use MeTextBox as a regular TextBox. And if you want to make it control Enabled flag of some other Control you can just specify DependantControl property which will be accessible in the designer.
Fitting this into your example (code):
// assume you have a Button named btnConfirm
// and want to enable this button only when your `TextBox` has some text
MeTextBox mtb = new MeTextBox();
mtb.DependantControl = btnConfirm;
And if you do not want to make it in the code you can use designer directly.
To make it other way around ( one button dependant on many text boxes ) you can extend Button object :
public class MeButton
: Button
{
List<TextBox> m_DependantOn = new List<Control>();
[Browsable(true)]
public List<TextBox> DependantOn
{
get { return m_DependantOn; }
set { RemoveEvents(); m_DependantOn = value; AssignEvents(); }
}
void RemoveEvents()
{
foreach(TextBox ctrl in m_DependantOn)
ctrl.TextChanged -= WhenTextChanged;
}
void AssignEvents()
{
foreach(TextBox.ctrl in m_DependantOn)
ctrl.TextChanged += WhenTextChanged;
}
void WhenTextChanged(object sender, TextChangedEventArgs e)
{
this.Enabled = true;
}
}
Remove all Default event & property
Remove event Click,Load,MouseClick,DoubleClick in userControle
or hildent event
public event EventHandler Click
{
add { this.Click += value; }
remove { this.Click -= value; }
}
Error !!!>>>An unhandled exception of type 'System.StackOverflowException' occurred in WindowsFormsControlLibrary1.dll
this image sample
enter image description here
You can try overriding them and tagging them not browsable.
Here is an example with Click event :
[Browsable(false)]
public new event EventHandler Click
{
add { base.Click += value; }
remove { base.Click -= value; }
}
I'm having a bit of a problem with WPF property binding. First the code.
C#
public partial class WPFTextBox: UserControl
{
private bool _bold;
public bool Bold
{
get { return _bold; }
set
{
_bold = value;
OnPropertyChanged("Bold");
}
}
private bool _selectionChanged;
public WPFTextBox()
{
InitializeComponent();
DataContext = this;
Bold = true; // <--- This works, the checkbox will be checked
_selectionChanged = false;
}
private void txtDetails_SelectionChanged(object sender, RoutedEventArgs e)
{
var selection = txtDetails.Selection;
_selectionChanged = true;
Bold = selection.FontWeight() == FontWeights.Bold;
// ^-- This doesn't work It will trigger everything, but the checkbox won't
// change value. FontWeight() is an extension I wrote
_selectionChanged = false;
}
private void OnPropertyChanged(string name)
{
if(_selectionChanged)
return; // If the change was brought from the user moving the
// cursor in the textbox, don't change the textbox.
TextRange range = txtDetails.Selection;
switch(name)
{
case "Bold":
// change selection to bold, like I mentioned I does work
break;
default:
break;
}
}
}
XAML
<RichTextBox Name="txtDetails" SelectionChanged="txtDetails_SelectionChanged"/>
<CheckBox Name="chkBold" Content="Bold" IsChecked="{Binding Path=Bold}"/>
I'm creating a textbox with format options. The binding works in the constructor, but not in the selection changed event. I've tried adding a lot of options to the binding such as Mode=TwoWay and different property changed triggers.
The reason I'm using the _selectionChanged bool is because if I don't check for that, if I have a word was different formatting such as hello and I click on it, it will change the formatting for all of the word to either bold or not. I think maybe it's because I'm handling it in selection changed event, but then I'm not sure where else I could change property value.
See the example from here, you can just grab the INPC part.
set
{
_bold = value;
OnPropertyChanged("Bold");
NotifyPropertyChanged();
}
You need to inherit INotifyPropertyChanged interface
and implement PropertyChangedEventHandler
public class WPFTextBox: UserControl,System.ComponentModel.INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And call OnPropertyChanged in the setter of your property
1.You can use UpdateSourceTrigger=PropertyChanged event also.
2.Yes binding work on controls from the Extended WPF Toolkit.
IsChecked="{xcd:Path=Bold}"
I'm making a custom button (Winforms Control Library) and have the code below so that all mouseenter will be added to all controls in my button. When I run it, it causes a stack overflow exception. I have the same code with Click instead of MouseEnter and it works fine. Here is the code:
public new event EventHandler MouseEnter {
add
{
this.MouseEnter += value;
foreach (Control i in Controls)
{
i.MouseEnter += value;
}
}
remove
{
this.MouseEnter -= value;
foreach (Control i in Controls)
{
i.MouseEnter -= value;
}
}
}
here is the click code:
public new event EventHandler Click {
add {
this.Click += value;
foreach (Control i in Controls) {
i.Click += value;
}
}
remove {
this.Click -= value;
foreach (Control i in Controls) {
i.Click -= value;
}
}
}
+= is shorthand for "invoke the adder for this event." You are calling += from your adder. Thus you have unbound recursion, leading to the stack overflow.
Looking at your code, it appears that you are defining the adder yourself in order to add and remove the handler not only from the control, but from all its children as well. This strikes me as a pretty bad idea: subscribers to a given event have the reasonable expectation that they will only be notified when the actual event is fired, and not whenever the event is fired by any number of publishers about which they know nothing.
If you want to create helper methods that do this, that would probably make more sense, since now consumers invoking the methods know exactly what they're getting into. As well, that would get rid of your recursion bug to boot.
Finally, this functionality is probably unnecessary: many events will bubble up from children to parents anyway.
I think you wanted something like this (not sure if its right for the private member):
private EventHandler mouseEnter;
public new event EventHandler MouseEnter {
add
{
this.mouseEnter += value;
foreach (Control i in Controls)
{
i.mouseEnter += value;
}
}
remove
{
this.mouseEnter -= value;
foreach (Control i in Controls)
{
i.mouseEnter -= value;
}
}
}
You have this.MouseEnter calling itself in recursion.
I ended up replaceing this.Click with base.Click.
public new event EventHandler Click {
add {
base.Click += value;
foreach (Control i in Controls) {
i.Click += value;
}
}
remove {
base.Click -= value;
foreach (Control i in Controls) {
i.Click -= value;
}
}
}