I'm creating a custom control that has a PasswordBox in it. How do I hook up a DependencyProperty of my custom control to the Password property of the PasswordBox?
From all the examples I see, hooking it up the password in the template using TemplateBinding should do the trick, but this doesn't seem to be working. What am I missing?
generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CustomControlBinding="clr-namespace:CustomControlBinding">
<Style TargetType="CustomControlBinding:PasswordBoxTest">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CustomControlBinding:PasswordBoxTest">
<Grid Background="Transparent">
<PasswordBox Password="{TemplateBinding Text}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
PasswordBoxTest.cs
namespace CustomControlBinding
{
public class PasswordBoxTest : Control
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof( string ), typeof( PasswordBoxTest ), new PropertyMetadata( OnTextPropertyChanged ) );
public string Text
{
get { return GetValue( TextProperty ) as string; }
set { SetValue( TextProperty, value ); }
}
public PasswordBoxTest()
{
DefaultStyleKey = typeof( PasswordBoxTest );
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
private static void OnTextPropertyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
{
}
}
}
You have to use RelativeSource bindings, for it to work. Or you have to set the DataContext of your UserControl to this.
TemplateBinding is ok. You need to set somehow the binding source (for example through mentioned DataContext or simply in Xaml using Text attribute), but I cannot judge where's the problem since you omitted this code.
I don't know what's the purpose of this class, but probably adding some features to the standard PasswordBox. I would keep both implementations as similar as possible. What I mean the Text property should be called Password etc.
On more remark: The presented template does not need the Grid. Unless you have extra reason for using it, remove it - it just adds on the layout complexity. Note that the default template of the PasswordBox is already wrapped in an identical Grid...
I haven't been able to get this to work no matter what I do. What I did instead that does work is setting up some fake binding in code.
namespace CustomControlBinding
{
public class PasswordBoxTest : Control
{
private PasswordBox passwordBox;
private string passwordSetBeforeTemplateApplied;
public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register( "Password", typeof( string ), new PropertyMetadata( OnPasswordPropertyChanged ) );
public string Password
{
get { return GetValue( PasswordProperty ) as string; }
set { SetValue( PasswordProperty, value ); }
}
public PasswordBoxTest()
{
DefaultStyleKey = typeof( PasswordBoxTest );
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
passwordBox = (PasswordBox)GetTemplateChild( "PasswordElement" );
passwordBox.PasswordChanged += PasswordBoxPasswordChanged;
if( !string.IsNullOrEmpty( passwordSetBeforeTemplateApplied ) )
{
passwordBox.Password = passwordSetBeforeTemplateApplied;
}
}
public static void OnPasswordPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
( (WatermarkPasswordBox)d ).OnPasswordChanged( d, e );
}
private void OnPasswordChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
if( passwordBox == null )
{
passwordSetBeforeTemplateApplied = Password;
return;
}
if( Password != passwordBox.Password )
{
passwordBox.Password = Password;
}
}
private void PasswordBoxPasswordChanged( object sender, RoutedEventArgs e )
{
if( passwordBox.Password != Password )
{
Password = passwordBox.Password;
}
}
}
}
Related
I know there are a thousand questions on the topic, but I cannot find what's wrong with mine...
I have a .xaml.cs file with the standard notify property changed stuff, a public member with the "notify" in the setter. I also set the data context in the constructor. Looks like this:
public partial class SlimERDplot : UserControl
{
public SlimERDplot()
{
DataContext = this;
InitializeComponent();
}
public enum AvailableImaginations { NotSet, Right, Left}
private AvailableImaginations _movementImagination = AvailableImaginations.NotSet;
//This is the one I'll be binding
public string HemisphereUI { get { return Hemisphere.ToString() + " hemisphere"; } }
public AvailableHemispheres Hemisphere
{
get { return _hemisphere; }
set
{
if (_hemisphere != value)
{
_hemisphere = value;
NotifyPropertyChanged();
NotifyPropertyChanged("HemisphereUI"); //tried with or without this,
//makes no difference
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
The XAML simply:
<Label HorizontalAlignment="Right" Content="{Binding Path=HemisphereUI}"></Label
Then from the consumer XAML I use it like this:
<UIControls:SlimERDplot x:Name="ERDRightDown" Hemisphere="Right" MovementImagination="Left" />
But in the label, it always says NotSet (the default value). I debugged, and the thing does get through the setter with the proper value, but the displayed value is never changed
What's missing?
What's missing?
Your UserControl doesn't implement INotifyPropertyChanged:
public partial class SlimERDplot : UserControl, INotifyPropertyChanged
{
...
}
This is required for the view to actually subscribe to the PropertyChanged event.
You have to use DependencyProperty
Here an example
CodeBehind
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public string HemisphereUI
{
get { return (string)GetValue( HemisphereUIProperty ); }
private set { SetValue( HemisphereUIProperty, value ); }
}
// Using a DependencyProperty as the backing store for HemisphereUI. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HemisphereUIProperty =
DependencyProperty.Register( nameof( HemisphereUI ), typeof( string ), typeof( MyUserControl ), new PropertyMetadata( null ) );
public AvailableHemispheres Hemisphere
{
get { return (AvailableHemispheres)GetValue( HemisphereProperty ); }
set { SetValue( HemisphereProperty, value ); }
}
// Using a DependencyProperty as the backing store for Hemisphere. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HemisphereProperty =
DependencyProperty.Register( nameof( Hemisphere ), typeof( AvailableHemispheres ), typeof( MyUserControl ), new PropertyMetadata( AvailableHemispheres.NotSet, OnHemisphereChanged ) );
private static void OnHemisphereChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
MyUserControl control = ( d as MyUserControl )!;
control.HemisphereUI = e.NewValue.ToString() + " hemisphere";
}
}
public enum AvailableHemispheres
{
NotSet,
Right,
Left,
}
XAML
<UserControl
x:Class="WpfApp1.Views.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<Label
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding HemisphereUI, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyUserControl}}}" />
</Grid>
</UserControl>
I tried with a behavior with no luck
Behavior:
public sealed class BubbleDoubleClickEvent : Behavior<UIElement>
{
#region TargetElement
public UIElement TargetElement
{
get { return (UIElement)GetValue( TargetElementProperty ); }
set { SetValue( TargetElementProperty, value ); }
}
// Using a DependencyProperty as the backing store for TargetElement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TargetElementProperty =
DependencyProperty.Register( "TargetElement", typeof( UIElement ), typeof( BubbleDoubleClickEvent ) );
#endregion
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseDown += AssociatedObject_PreviewMouseDown;
}
private void AssociatedObject_PreviewMouseDown( object sender, MouseButtonEventArgs e )
{
if( e.ClickCount == 2 )
{
e.Handled = true;
var e2 = new MouseButtonEventArgs( e.MouseDevice, e.Timestamp, e.ChangedButton );
e2.RoutedEvent = Control.PreviewMouseDoubleClickEvent;
var target = TargetElement ?? AssociatedObject;
target.RaiseEvent( e2 );
}
}
protected override void OnDetaching()
{
AssociatedObject.PreviewMouseDown -= AssociatedObject_PreviewMouseDown;
base.OnDetaching();
}
}
XAML:
<Grid>
<ContentPresenter x:Name="contentPresenter" Content="{Binding}" ContentTemplate="{Binding ..., Path=ItemTemplate }" />
<Thumb x:Name="moveThumb" >
<i:Interaction.Behaviors>
<behaviors:BubbleDoubleClickEvent TargetElement="{Binding ElementName=contentPresenter}"/>
</i:Interaction.Behaviors>
</Thumb>
</Grid>
Any help appreciated
The code above works just well, but the target control did not receive the event OnPreviewMouseDoubleClick so i had to add an instance handler to manage the event like this:
this.AddHandler( Control.PreviewMouseDoubleClickEvent,
new MouseButtonEventHandler( ( target, args ) =>
{
//do stuff
args.Handled = true;
} ), false );
I'm using a Thumb inside a ListBoxItem to move the item around a Canvas.
The thumb cover the entire surface of the ListBoxItem.
Since the ListBoxItem can contain for example a TextBox, i'd like to intercept double clicks (at the moment swallowed by the Thumb) and relay them directly to the Texbox.
I tried with a behavior with no luck (the event is not received by the targetElement)... here's the code, any help?
public sealed class BubbleDoubleClickEvent : Behavior<UIElement>
{
#region TargetElement
public UIElement TargetElement
{
get { return (UIElement)GetValue( TargetElementProperty ); }
set { SetValue( TargetElementProperty, value ); }
}
// Using a DependencyProperty as the backing store for TargetElement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TargetElementProperty =
DependencyProperty.Register( "TargetElement", typeof( UIElement ), typeof( BubbleDoubleClickEvent ) );
#endregion
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseDown += AssociatedObject_PreviewMouseDown;
}
private void AssociatedObject_PreviewMouseDown( object sender, MouseButtonEventArgs e )
{
if( e.ClickCount == 2 )
{
e.Handled = true;
var e2 = new MouseButtonEventArgs( e.MouseDevice, e.Timestamp, e.ChangedButton );
e2.RoutedEvent = Control.PreviewMouseDoubleClickEvent;
var target = TargetElement ?? AssociatedObject;
target.RaiseEvent( e2 );
}
}
protected override void OnDetaching()
{
AssociatedObject.PreviewMouseDown -= AssociatedObject_PreviewMouseDown;
base.OnDetaching();
}
}
The Listbox defines the ItemContainerStyle template similar to this:
<Grid>
<ContentPresenter x:Name="contentPresenter" Content="{Binding}" ContentTemplate="{Binding ..., Path=ItemTemplate }" />
<Thumb x:Name="moveThumb" >
<i:Interaction.Behaviors>
<behaviors:BubbleDoubleClickEvent TargetElement="{Binding ElementName=contentPresenter}"/>
</i:Interaction.Behaviors>
</Thumb>
</Grid>
I've been using code like this
<ComboBox ItemsSource="{Binding Path=CompaniesViewModel.CompaniesCollection}"
SelectedValuePath="CompanyId"
SelectedValue="{Binding Path=CompanyId}"
IsEnabled="False"
DisplayMemberPath="CompanyName"
/>
To display a Company Name in a ComboBox. Notice how the IsEnabled is set to false...that's because I really don't want the user to use the ComboBox. I am just using it as an easy way to convert an ID to string for display purposes.
When I put items in a Grid and there are a lot of them, I think it is really hurting the rendering performance. When I remove the ComboBox it loads in a split second. When the ComboBox is used in the code it can take 20 seconds.
I guess my question is I think I should be using a Label or TextBlock but not sure how to get the binding to work correctly as They don't have an ItemsSource or a SelectedValuePath or SelectedValue.
I thought about writing an IValueConverter but not sure how to bind/pass in the 3 values. I'd have to pass in the collection, the ValuePath and the Value ID.
Any thoughts or suggestions?
Put a
public Company Company {get {return CompaniesCollection.FirstOrDefault(x => x.CompanyId == CompanyId); }}
property in the ViewModel.
I welcome any of you to examine my code to see if you can make it more efficient but this is what I ended up doing.
<cc:LookupLabel
ItemsSource="{Binding Path=CompaniesCollection}"
SelectedValuePath="CompanyId"
SelectedValue="{Binding Path=CompanyId}"
DisplayMemberPath="CompanyName"
/>
And below is the LookupLabel derived from Label and INotifyPropertyChanged. I'm not sure how Microsoft implements this efficiently but this was my best stab at it. In particular the GetContent method listed at the bottom. All the other stuff is just the messy DependencyProperty declarations.
using System;
using System.Collections;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace CustomControls
{
public class LookupLabel : Label, INotifyPropertyChanged
{
public LookupLabel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#region ItemsSource
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(LookupLabel)
, new UIPropertyMetadata(null, LookupLabel.ItemsSourceChanged)
);
private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LookupLabel t = d as LookupLabel;
t.NotifyPropertyChanged("ItemsSource");
t.Content = GetContent(t);
}
[Bindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IEnumerable ItemsSource
{
get
{
return (IEnumerable)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
#endregion ItemsSource
#region SelectedValue
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(object), typeof(LookupLabel)
, new UIPropertyMetadata(null, LookupLabel.SelectedValueChanged)
);
private static void SelectedValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LookupLabel t = d as LookupLabel;
t.NotifyPropertyChanged("SelectedValue");
t.Content = GetContent(t);
}
[Localizability(LocalizationCategory.NeverLocalize)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Bindable(true)]
[Category("Appearance")]
public object SelectedValue
{
get
{
return (object)GetValue(SelectedValueProperty);
}
set
{
SetValue(SelectedValueProperty, value);
}
}
#endregion SelectedValue
#region SelectedValuePath
public static readonly DependencyProperty SelectedValuePathProperty =
DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(LookupLabel)
, new UIPropertyMetadata(string.Empty, LookupLabel.SelectedValuePathChanged)
);
private static void SelectedValuePathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LookupLabel t = d as LookupLabel;
t.NotifyPropertyChanged("SelectedValuePath");
t.Content = GetContent(t);
}
[Localizability(LocalizationCategory.NeverLocalize)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Bindable(true)]
[Category("Appearance")]
public string SelectedValuePath
{
get
{
return (string)GetValue(SelectedValuePathProperty);
}
set
{
SetValue(SelectedValuePathProperty, value);
}
}
#endregion SelectedValuePath
#region DisplayMemberPath
public static readonly DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(LookupLabel)
, new UIPropertyMetadata(string.Empty, LookupLabel.DisplayMemberPathChanged)
);
private static void DisplayMemberPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LookupLabel t = d as LookupLabel;
t.NotifyPropertyChanged("DisplayMemberPath");
t.Content = GetContent(t);
}
[Localizability(LocalizationCategory.NeverLocalize)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Bindable(true)]
[Category("Appearance")]
public string DisplayMemberPath
{
get
{
return (string)GetValue(DisplayMemberPathProperty);
}
set
{
SetValue(DisplayMemberPathProperty, value);
}
}
#endregion DisplayMemberPath
protected static object GetContent(LookupLabel label)
{
if (label.ItemsSource == null)
{
return null;
}
if (string.IsNullOrWhiteSpace(label.SelectedValuePath))
{
return null;
}
if (string.IsNullOrWhiteSpace(label.DisplayMemberPath))
{
return null;
}
if (label.SelectedValue == null)
{
return null;
}
object result = null;
System.Reflection.PropertyInfo valuePropertyInfo = null;
foreach (var item in label.ItemsSource)
{
if (valuePropertyInfo == null)
{
valuePropertyInfo = item.GetType().GetProperty(label.SelectedValuePath);
if (valuePropertyInfo == null)
{
return null;
}
}
if (valuePropertyInfo.GetValue(item, null).Equals(label.SelectedValue))
{
var displayPropertInfo = item.GetType().GetProperty(label.DisplayMemberPath);
if (displayPropertInfo == null)
{
return null;
}
else
{
result = displayPropertInfo.GetValue(item, null);
break;
}
}
}
return result;
}
}
}
I am guessing that your combobox is taking too long to load because your collection has a lot of items.
You shouldn't be loading all your companies if you'll just show one of them, as a general good-practice.
I don't quite grasp your intent in using the combobox. Is it the style? can it be enabled in the future?
If it is only an easy way of displaying the CompanyName then i'd suggest the following:
Bind directly to CompanyName property.
-OR-
in the code-behind, create a calculated property named "CompanyDisplayName" that gets your company name.
Bind to it in the XAML
in the code-behind, whenever the current selected Company instance or the CompanyId changes fire 'OnPropertyChanged("CompanyDisplayName")
Try a TextBlock or a readonly TextBox to enable copy/paste;
For more info on the NotifyPropertyCahnged paradigm read here
I am trying to detect when an item is checked, and which item is checked in a ListBox using Silverlight 4 and the Prism framework. I found this example on creating behaviors, and tried to follow it but nothing is happening in the debugger. I have three questions:
Why isn't my command executing?
How do I determine which item was checked (i.e. pass a command parameter)?
How do I debug this? (i.e. where can I put break points to begin stepping into this)
Here is my code:
View:
<ListBox x:Name="MyListBox" ItemsSource="{Binding PanelItems, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Enabled}" my:Checked.Command="{Binding Check}" />
<TextBlock x:Name="DisplayName" Text="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ViewModel:
public MainPageViewModel()
{
_panelItems.Add( new PanelItem
{
Enabled = true,
DisplayName = "Test1"
} );
Check = new DelegateCommand<object>( itemChecked );
}
public void itemChecked( object o )
{
//do some stuff
}
public DelegateCommand<object> Check { get; set; }
Behavior Class
public class CheckedBehavior : CommandBehaviorBase<CheckBox>
{
public CheckedBehavior( CheckBox element )
: base( element )
{
element.Checked +=new RoutedEventHandler(element_Checked);
}
void element_Checked( object sender, RoutedEventArgs e )
{
base.ExecuteCommand();
}
}
Command Class
public static class Checked
{
public static ICommand GetCommand( DependencyObject obj )
{
return (ICommand) obj.GetValue( CommandProperty );
}
public static void SetCommand( DependencyObject obj, ICommand value )
{
obj.SetValue( CommandProperty, value );
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached( "Command", typeof( CheckBox ), typeof( Checked ), new
PropertyMetadata( OnSetCommandCallback ) );
public static readonly DependencyProperty CheckedCommandBehaviorProperty =
DependencyProperty.RegisterAttached( "CheckedCommandBehavior", typeof( CheckedBehavior ), typeof( Checked ), null );
private static void OnSetCommandCallback( DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e )
{
CheckBox element = dependencyObject as CheckBox;
if( element != null )
{
CheckedBehavior behavior = GetOrCreateBehavior( element );
behavior.Command = e.NewValue as ICommand;
}
}
private static CheckedBehavior GetOrCreateBehavior( CheckBox element )
{
CheckedBehavior behavior = element.GetValue( CheckedCommandBehaviorProperty ) as CheckedBehavior;
if( behavior == null )
{
behavior = new CheckedBehavior( element );
element.SetValue( CheckedCommandBehaviorProperty, behavior );
}
return behavior;
}
public static CheckedBehavior GetCheckCommandBehavior( DependencyObject obj )
{
return (CheckedBehavior) obj.GetValue( CheckedCommandBehaviorProperty );
}
public static void SetCheckCommandBehavior( DependencyObject obj, CheckedBehavior value )
{
obj.SetValue( CheckedCommandBehaviorProperty, value );
}
}
Your sample is not enough for a repro on my PC, but here are the things that I'd correct first:
The bindings in the DataTemplate are missing ", Mode=TwoWay" if you want the Enabled property to be set in your PanelItem
(- The ItemsSource binding does not need the Mode=TwoWay, but this is a minor detail)
The DataContext of the ItemTemplate is the PanelItem instance, so the binding of the Check command seems wrong: there is no Check property on PanelItem. The binding should be:
my:Checked.Command="{Binding ElementName=MyListBox, Path=DataContext.Check}
This kind of stuff is always hard to debug. Look at the output window of VS; binding errors (path not found) are displayed there. When you have a DP change callback (like OnSetCommandCallback), a breakpoint there will tell you how the binding went.
Edit: added after 1st comment (as I can't use the comment feature on the PC I have to use now)
The Command attached property is defined as type CheckBox in the Checked class, but the Check property in the VM is a DelegateCommand. I agree with WPF on the type mismatch :-)
The property declaration is like this:
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached(
"Command", typeof( CheckBox ),
typeof( Checked ), new PropertyMetadata( OnSetCommandCallback ) );
The second parameter should be the property type, so I guess something like ICommand in your case.
Out of curiosity: in OnSetCommandCallback, you don't care for the value set to the Command property (which is in e.NewValue). How do you relate an instance of CheckedBehavior to the Check property of the VM ?
Edit after second comment:
No, the 2nd paragraph above is not related to your question. Maybe it does not make sense. I can't figure out the role of CheckedBehavior.
Concerning the question of which item is checked/unchecked: what do you need more precisely ? You have a PanelItem instance, whose Enabled property is being set to true or false through the biding; so the checked items are the ones with Enabled=true.
Edit after 3rd comment:
Thanks for the explanation of your needs. You're not really using the path parameter of the binding to the attached property, you could write:
my:Checked.Command="{Binding}"
This way, e.NewValue is the bound PanelItem in the OnSetCommandCallback. So it could be given to the CheckedBehavior instance (in its constructor), which could forward it when calling Execute of ICommand.
CheckBehavior.cs:
public class CheckBehavior : CommandBehaviorBase<CheckBox>
{
public CheckBehavior(CheckBox element) : base(element)
{
element.Checked += OnElementChecked;
element.Unchecked += OnElementChecked;
}
private void OnElementChecked(object sender, RoutedEventArgs e)
{
if (sender is CheckBox && ((CheckBox)sender).IsPressed)
{
base.ExecuteCommand();
}
}
}
CheckCommand.cs:
public class CheckCommand
{
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
public static object GetCommandParameter(DependencyObject obj)
{
return obj.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(DependencyObject obj, object value)
{
obj.SetValue(CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(CheckCommand), new PropertyMetadata(OnSetCommandCallback));
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(CheckCommand), new PropertyMetadata(OnSetCommandParameterCallback));
public static readonly DependencyProperty CheckedCommandBehaviorProperty =
DependencyProperty.RegisterAttached("CheckedCommandBehavior", typeof(CheckBehavior), typeof(CheckCommand), null);
private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
CheckBox element = dependencyObject as CheckBox;
if (element != null)
{
CheckBehavior behavior = GetOrCreateBehavior(element);
behavior.Command = e.NewValue as ICommand;
}
}
private static void OnSetCommandParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
CheckBox element = dependencyObject as CheckBox;
if (element != null)
{
CheckBehavior behavior = GetOrCreateBehavior(element);
behavior.CommandParameter = e.NewValue;
}
}
private static CheckBehavior GetOrCreateBehavior(CheckBox element)
{
CheckBehavior behavior = element.GetValue(CheckedCommandBehaviorProperty) as CheckBehavior;
if (behavior == null)
{
behavior = new CheckBehavior(element);
element.SetValue(CheckedCommandBehaviorProperty, behavior);
}
return behavior;
}
public static CheckBehavior GetCheckCommandBehavior(DependencyObject obj)
{
return (CheckBehavior)obj.GetValue(CheckedCommandBehaviorProperty);
}
public static void SetCheckCommandBehavior(DependencyObject obj, CheckBehavior value)
{
obj.SetValue(CheckedCommandBehaviorProperty, value);
}
}
And example:
<CheckBox Commands:CheckCommand.Command="{Binding MyCheckCommand}}"
Commands:CheckCommand.CommandParameter="{Binding}"/>