Is there a WPF check box control toggle event? - c#

Considering WPF controls, how do I know if a check box's value has changed (toggled)?
I know there are the common Checked, Unchecked, Clicked events, but how about an event for when the value changes, regardless of how it was changed?
I looked through the events and I didn't find anything, but maybe I'm missing the obvious (as it has happened many times in the past).

You can just bind IsChecked Dependency Property to a boolean. On that binded property setter you can manipulate what you want (independently if it's setting it to true or false). That works just as expected.
On your view:
<Grid>
<CheckBox ... IsChecked="{Binding ShowPending}"/>
</Grid>
On your DataContext ViewModel or CodeBehind.
private bool showPending = false;
public bool ShowPending
{
get { return this.showPending; }
set
{
//Here you mimic your Toggled event calling what you want!
this.showPending = value;
}
}

I know this already has an accepted answer, but binding is a bit overkill on this.
Just write one event handler and wire it to both the Checked and Unchecked events then check the IsChecked property inside your event handler.

Going off Randolf's answer, just create a class representing your window.
In the new class, create a property called BlahIsChecked. Implement the INotifyPropertChangedEvent in the class and in the setter of the the new property, fire the event with the property name.
class Blah : INotifyPropertyChanged
{
// Used for triggering the event
public event PropertyChangedEventHandler PropertyChanged;
// Called when the property changes
protected void OnPropertyChanged(String propertyName)
{
// Retrieve handler
PropertyChangedEventHandler handler = this.PropertyChanged;
// Check to make sure handler is not null
if(handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool _blahIsChecked;
public bool BlahIsChecked
{
get {
return _blahIsChecked;
}
set {
_blahIsChecked = value;
OnPropertyChanged("BlahIsChecked);
}
}
}
Now, go to your wpf class and say this.DataContext = new MainModel(); You can do this in WPF or c#.
Now in your checkbox xaml do the following
<checkbox Checked="{Binding BlahIsChecked, Mode=TwoWay}"/>
I did this from memory but should get you started. Good luck.

Your best option is probably the IsChecked property. But if you require an event, you can look at creating a DependencyPropertyDescriptor and registering a handler with the AddValueChanged method.
I think this is about as close as you'll get to an evented notification that the check box's value has changed. Creating the descriptor and adding the handler looks something like this:
var dpd = DependencyPropertyDescriptor.FromProperty(CheckBox.IsChecked, typeof(CheckBox));
dpd.AddValueChanged(...);

Related

Manually invoking OnPropertyChanged for custom DependencyProperty

So I've got a custom DependencyProperty like this:
public class PatientLookUp : TextBox
{
public static readonly DependencyProperty PatientProperty = DependencyProperty.Register("Patient", typeof(Patient), typeof(PatientLookUp), new FrameworkPropertyMetadata { BindsTwoWayByDefault = true });
public Patient Patient
{
get { return (Patient)GetValue(PatientProperty); }
set { SetValue(PatientProperty, value); }
}
//some logic for getting the Patient...
}
Edit: It is defined within a CustomControl. Another control using this CustomControl binds the Patient property and listens to it's changed event. The problem is, that I make some network request stuff and if I get a null Patient as a result, I still want to notify the other control that CustomControl has done it's stuff, even if the Patient hasn't really changed.
So now I want to invoke the PropertyChanged event manually. Is there any way to do this?
Hope you can help me, thanks.
Edit2: My XAML binding the Patient (shortened):
<Window Name="MyWindow">
<Grid>
<llc:PatientLookUp Patient="{Binding Patient}" MaxLength="10" Text="{Binding SocialSecurityNumber}" />
</Grid>
<Window>
Now I need to notify the ViewModel of MyWindow in case the Patient object of the PatientLookUp has changed. The LookUp has a request logic fired on pressing the enter key. After the request has finished, I want to notify MyWindow's ViewModel, even if it couldn't find anyone and the value is still null.
You're deriving from a standard WPF TextBox and putting something that is totally out of place.
You're approaching this in very inconvenient manner.
Instead: use a regular TextBox, bind to a PatientSearchViewModel.SearchString and when that SearchString is changed, fire the search (from the ViewModel) and if found, set the PatientSearchViewModel.Patient property and NotifyPropertyChange() so that the UI is notified of the change.
You're going like this:
DataModel -> View -> ViewModel
while the most straightforward and convenient approach is this:
DataModel -> ViewModel -> View
Your class needs to inherit from INotifyPropertyChanged interface and implement it's classes. Once that's done, you should be able to call OnPropertyChanged method on your property's set.
Inheriting INotifyPropertyChanged:
public partial class MainWindow : INotifyPropertyChanged
Implementation of INotifyPropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
Calling NotifyPropertyChanged:
public Patient Patient
{
get { return (Patient)GetValue(PatientProperty); }
set
{
SetValue(PatientProperty, value);
OnPropertyChanged();
}
}

Custom Event in Custom Control (on variable changed)

I'm making a custom control including three buttons. Every button represents a tab. When I click a button to change to the tab corresponding to that tab a variable for the controll is uppdated, this variable is called "selectedIndex". How can I make a custom event that triggers when this index is changed?
I've seen solutions for custom events here but all of them is copies of already existing events such as Click-events. My problem involvs having an event on variable change".
Regards!
One option is to have your object implement the very standard INotifyPropertyChanged interface. There's no rule which states that every property on an object needs to raise the PropertyChanged event, so just raise it for the one property in question. Something like this:
public class MyObject : INotifyPropertyChanged
{
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
If your .NET version doesn't support CallerMemberName then you can remove that and supply the property name manually:
NotifyPropertyChanged("MyProperty");
At this point any calling code can subscribe to the public PropertyChanged event on any instance of this object. This interface is particularly handy because it's commonly used in lots of UI technologies for updating data-bound UI elements. All the interface enforces, really, is a standard name for the event. If you'd prefer, you can forgo the interface and just create a custom public event. And just invoke that event any time your object logically needs to. The structure of exposing an event and invoking it would be the same.

Binding to TabControl SelectedIndex

I have a tab control on a page; its items are bound back to my ViewModel, which also exposes an ActiveTabItemIndex which is bound (two way) to the SelectedIndex property in my xaml, and which implements INotifyPropertyChanged so that my TabControl knows when to update.
This is (I understand) the MVVM-correct way to do things, and works 99% properly.
class MainWindowViewModel : BaseViewModel, INotifyPropertyChanged
{
ObservableCollection<TabItemViewModel> _TabItems;
int _ActiveTabItemIndex;
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
void _TabItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
_ActiveTabItemIndex = _TabItems.IndexOf((TabItemViewModel)e.NewItems[0]);
RaisePropertyChanged("ActiveTabItemIndex");
}
public ObservableCollection<TabItemViewModel> TabItems
{
get
{
if (_TabItems == null)
{
_TabItems = new ObservableCollection<TabItemViewModel>();
_TabItems.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_TabItems_CollectionChanged);
}
return _TabItems;
}
}
public int ActiveTabItemIndex
{
get
{
return _ActiveTabItemIndex;
}
set
{
_ActiveTabItemIndex = value;
}
}
}
This way any manipulations I make to my TabItems collection are reflected on the TabControl, and when I add a new item, it is automatically selected. This works a treat; however, when adding the very first item to an empty tab control, it looks like this:
The tab contents are displayed, but the tab is not selected. I need to manually click the tab to get it to look right:
It is as though there is some kind of disconnect between the drawing of the tabs and the drawing of their contents. I know the binding is working because subsequent tabs are handled correctly and if I remove the binding altogether then the first page does not show its contents until the tab is manually selected. If anyone has seen this or can shed some light it would be very much appreciated! Thank you all :)
Only raise your property change events in the setter; you can think of it as allowing the property itself to dictate what "changed" means, and by extension, giving it control over when the event is fired (and makes it do what you expect):
public int ActiveTabItemIndex
{
get{ return _ActiveTabItemIndex; }
set
{
if(_ActiveTabItemIndex != value)
{
_ActiveTabItemIndex = value;
RaisePropertyChanged("ActiveTabItemIndex");
}
}
}
Just change
_ActiveTabItemIndex = _TabItems.IndexOf(...);
to
ActiveTabItemIndex = _TabItems.IndexOf(...);
and remove the RaisePropertyChanged call from _TabItems_CollectionChanged
There will be times when you'll need raise property changed notifications outside a property's setter but that is for a far more complicated day :)
As an aside, INotifyPropertyChanged should be implemented on your BaseViewModel. Check out the absolutely fantastic MVVM Light Toolkit - it has all the code you'd otherwise have to duplicate in every project that you use MVVM in.

How to have bindable properties of a UserControl which work with OnPropertyChanged

I have a simple usercontrol (WinForms) with some public properties. When I use this control, I want to databind to those properties with the DataSourceUpdateMode set to OnPropertyChanged. The datasource is a class which implements INotifyPropertyChanged.
I'm aware of the need to create bindings against the properties and I'm doing that.
I assumed that my usercontrol would have to implement an interface, or the properties would need to be decorated with some attribute, or something along those lines.But my research has come up blank.
How should this be accomplished? At the moment I'm doing it by calling OnValidating() in my usercontrol whenever a property changes, but that doesn't seem right.
I can get validation to happen if I set the CausesValidation to true on the usercontrol, but that's not very useful to me. I need to validate each child property as it changes.
Note this is a WinForms situation.
EDIT: Evidently I have no talent for explanation so hopefully this will clarify what I'm doing. This is an abbreviated example:
// I have a user control
public class MyControl : UserControl
{
// I'm binding to this property
public string ControlProperty { get; set; }
public void DoSomething()
{
// when the property value changes, the change should immediately be applied
// to the bound datasource
ControlProperty = "new value";
// This is how I make it work, but it seems wrong
OnValidating();
}
}
// the class being bound to the usercontrol
public class MyDataSource : INotifyPropertyChanged
{
private string sourceProperty;
public string SourceProperty
{
get { return sourceProperty; }
set
{
if (value != sourceProperty)
{
sourceProperty = value;
NotifyPropertyChanged("SourceProperty");
}
}
}
// boilerplate stuff
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public class MyForm : Form
{
private MyControl myControl;
public MyForm()
{
// create the datasource
var dataSource = new MyDataSource() { SourceProperty = "test" };
// bind a property of the datasource to a property of the usercontrol
myControl.DataBindings.Add("ControlProperty", dataSource, "SourceProperty",
false, DataSourceUpdateMode.OnPropertyChanged); // note the update mode
}
}
(I have tried this using a BindingSource, but the result was the same.)
Now what I want to happen is that when the value of MyControl.ControlProperty changes, the change is immediately propagated to the datasource (the MyDataSource instance). To achieve this I call OnValidating() in the usercontrol after changing the property. If I don't do that, I have to wait until validation gets triggered by a focus change, which is the equivalent of the "OnValidation" update mode, rather than the desired "OnPropertyUpdate" validation mode. I just don't feel like calling OnValidating() after altering a property value is the right thing to do, even if it (kind of) works.
Am I right in assuming the calling OnValidating() is not the right way to do this? If so, how do I notify the datasource of the ControlProperty change?
I think I've got this figured out. I didn't understand how change notifications were sent from control to bound datasource.
Yes, calling OnValidating() is the wrong way.
From what I've pieced together, there are two ways a control can notify the datasource that a property has changed.
One way is for the control to implement INotifyPropertyChanged. I had never done this from the control side before, and I thought only the datasource side of the binding had to implement it.
When I implemented INotifyPropertyChanged on my user control, and raised the PropertyChanged event at the appropriate time, it worked.
The second way is for the control to raise a specific change event for each property. The event must follow the naming convention: <propertyname>Changed
e.g. for my example it would be
public event EventHandler ControlPropertyChanged
If my property was called Foo, it would be FooChanged.
I failed to notice the relavent part of the MSDN documentation, where it says:
For change notification to occur in a
binding between a bound client and a
data source, your bound type should
either:
Implement the INotifyPropertyChanged
interface (preferred).
Provide a change event for each
property of the bound type.
This second way is how all existing WinForms controls work, so this is how I'm doing it now. I use INotifyPropertyChanged on my datasource, but I raise the Changed events on my control. This seems to be the conventional way.
Implementing the INotifyPropertyChanged interface is very simple. Here is a sample that shows an object with a single public field...
public class Demo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
private string _demoField;
public string DemoField
{
get {return demoField; }
set
{
if (value != demoField)
{
demoField = value;
NotifyPropertyChanged("DemoField");
}
}
}
}
Then you would create a Binding instance to bind a control property to a property (DemoField) on your source instance (instance of Demo).

INotifyPropertyChange ~ PropertyChanged not firing when property is a collection and a new item is added to the collection

I have a class that implements the INotifyPropertyChanged interface. Some of the properties of the class are of type List. For example:
public List<string> Answers
{
get { return _answers; }
set
{
_answers = value;
onPropertyChanged("Answers")
}
}
...
private void onPropertyChanged(string propertyName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
If I assign a new List<string> to Answer, then the PropertyChanged event fires as expected; but if I add a string string to the Answer list using the List Add method, then PropertyChanged event doesn't fire.
I was considering adding an AddAnswer() method to my class, which would handle calling the lists's Add method and would call onPropertyChanged() from there, but is that the right way to do it? Is there a more elegant way of doing it?
Cheers,
KT
You should expose an ObservableCollection<string>, which implements the INotifyCollectionChange interface to raise its own change events.
You should also remove the property setter; Collection properties should be read only.
You should not raise the PropertyChanged event when the contents of the collection change.
Ok I just finally experienced this issue, and there are NO complete answers on the Internet afaikt, so here is the missing piece that no one mentions (maybe because the assume that we are not complete morons and have NOT deleted the default constructor, or have alteast extended the default constructor) anyhow:
Make certain that you DID NOT delete the InitializeComponent(); call in the constructor of your View.
Without this call BEFORE you set DataContext of the view, NotifyPropertyChanged Event will ALWAYS BE NULL. I spent about 2 hours trying to figure out what was different between two different MVVM userControls. I guess my mind is so used to seeing InitializeComponent(); that it did not register that it was missing. I added that back and viola!
Hope This Helps Other Dummies Like Me!
Cheers,
Code Warrior Malo
It's not firing because the collection reference isn't changing, just the contents. You'd need the collection's Add() method to fire the event to be able to see it.
have a look at System.Collections.ObjectModel.ObservableCollection. http://msdn.microsoft.com/en-us/library/ms668604.aspx
It can be used like a List but has events built in for when its contents change.
ObservableCollection is your answer. If you want to fire on collection property changes, you'll need to implement INotifyPropertyChanged for each of the properties you'd like to track.
You should add an event listener to your _answers collection. Unfortunately, List<T> doesn't provide such an event.
As a suggestion, manage _answers as an ObservableCollection<string>, and properly attach/detach an event handler for the CollectionChanged event, as follows:
public ObservableCollection<string> Answers
{
get { return _answers; }
set
{
// Detach the event handler from current instance, if any:
if (_answers != null)
{
_answers.CollectionChanged -= _answers_CollectionChanged;
}
_answers = value;
// Attatach the event handler to the new instance, if any:
if (_answers != null)
{
_answers.CollectionChanged += _answers_CollectionChanged;
}
// Notify that the 'Answers' property has changed:
onPropertyChanged("Answers");
}
}
private void _answers_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
onPropertyChanged("Answers");
}

Categories

Resources