Binding BindableApplicationBarIconButton IsEnabled property to Relaycommand CanExecute Windows Phone 7.1 - silverlight

I am using Phone7.Fx R1
The following works. The system does not react when a user presses the button. This means, than there is no reaction if Stop Game is pressed without a game has been started and vice versa.
However the button looks active. I am aware that I can bind the IsEnabled to a different property, but I would like it to bind to NewGameCanExecute and StopGameCanExecute. Is this possible?
Some XAML code:
<Preview:BindableApplicationBarIconButton Command="{Binding NewGame}" IconUri="/images/icons/appbar.add.rest.png" Text="New game" />
<Preview:BindableApplicationBarIconButton Command="{Binding StopGame}" IconUri="/images/icons/appbar.stop.rest.png" Text="Stop game" />
And the relay commands:
public RelayCommand NewGame { get; private set; }
public RelayCommand StopGame { get; private set; }
//Constructor
NewGame = new RelayCommand(NewGameExecute, NewGameCanExecute);
StopGame = new RelayCommand(StopGameExecute, StopGameCanExecute);
void NewGameExecute()
{
_gameStarted = true;
_gameControlModel.StartNewGame();
StopGame.RaiseCanExecuteChanged();
}
bool NewGameCanExecute()
{
return !_gameStarted;
}
void StopGameExecute()
{
_gameControlModel.StopGame();
_gameStarted = false;
NewGame.RaiseCanExecuteChanged();
}
bool StopGameCanExecute()
{
return _gameStarted;
}
Couple of questions and answers:
Q: Have you tried to set a breakpoint in the CanExecute functions to see if it gets called?
A: Yes. It does get called, but the icon is not grayed out, eventhough false is returned.
The Execute method is not executed, if the CanExecute method returns false. But I want the icon to be grayed out like a normal button.
SOLUTION
I spend some time and came up with a solution, which can be found here:
http://pastebin.com/MM75xACj

This is obviously a bug in whatever BindableApplicationBarIconButton implementation you're using.
Ask the author of it for help, or debug your 3rd party software yourself.

SOLUTION
I spend some time and came up with a solution and edited the applicationbariconbutton class.
namespace Phone7.Fx.Controls
{
public class BindableApplicationBarIconButton : FrameworkElement, IApplicationBarIconButton
{
public int Index { get; set; }
public static DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(BindableApplicationBarIconButton), new PropertyMetadata(null, OnCommandPropertyChanged));
private static void OnCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
((BindableApplicationBarIconButton)d).Command = (ICommand)e.NewValue;
}
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set {
Command.CanExecuteChanged -= ValueOnCanExecuteChanged;
SetValue(CommandProperty, value);
Command.CanExecuteChanged += ValueOnCanExecuteChanged;
}
}
private void ValueOnCanExecuteChanged(object sender, EventArgs eventArgs)
{
ICommand commandSender = sender as ICommand;
if(commandSender != null)
{IsEnabled = commandSender.CanExecute(null);}
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(BindableApplicationBarIconButton), new PropertyMetadata(null, OnCommandParameterPropertyChanged));
private static void OnCommandParameterPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var invokeCommand = d as BindableApplicationBarIconButton;
if (invokeCommand != null)
{
invokeCommand.SetValue(CommandParameterProperty, e.NewValue);
}
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set
{
SetValue(CommandParameterProperty, value);
}
}
public static readonly DependencyProperty CommandParameterValueProperty =
DependencyProperty.RegisterAttached("CommandParameterValue", typeof(object), typeof(BindableApplicationBarIconButton), null);
public object CommandParameterValue
{
get
{
var returnValue = GetValue(CommandParameterValueProperty);
return returnValue;
}
set { SetValue(CommandParameterValueProperty, value); }
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(BindableApplicationBarIconButton), new PropertyMetadata(true, OnEnabledChanged));
private static void OnEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
((BindableApplicationBarIconButton)d).Button.IsEnabled = (bool)e.NewValue;
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.RegisterAttached("Text", typeof(string), typeof(BindableApplicationBarIconButton), new PropertyMetadata(OnTextChanged));
public new static readonly DependencyProperty VisibilityProperty =
DependencyProperty.RegisterAttached("Visibility", typeof(Visibility), typeof(BindableApplicationBarIconButton), new PropertyMetadata(OnVisibilityChanged));
private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
var button = ((BindableApplicationBarIconButton)d);
BindableApplicationBar bar = button.Parent as BindableApplicationBar;
bar.Invalidate();
}
}
public new Visibility Visibility
{
get { return (Visibility)GetValue(VisibilityProperty); }
set { SetValue(VisibilityProperty, value); }
}
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
((BindableApplicationBarIconButton)d).Button.Text = e.NewValue.ToString();
}
}
public ApplicationBarIconButton Button { get; set; }
public BindableApplicationBarIconButton()
{
Button = new ApplicationBarIconButton();
Button.Text = "Text";
Button.Click += ApplicationBarIconButtonClick;
}
void ApplicationBarIconButtonClick(object sender, EventArgs e)
{
if (Command != null && CommandParameter != null)
Command.Execute(CommandParameter);
else if (Command != null)
Command.Execute(CommandParameterValue);
if (Click != null)
Click(this, e);
}
public bool IsEnabled
{
get { return (bool)GetValue(IsEnabledProperty); }
set { SetValue(IsEnabledProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public event EventHandler Click;
public Uri IconUri
{
get { return Button.IconUri; }
set { Button.IconUri = value; }
}
}

Related

WPF - Access DependencyObject inside a CollectionChanged event

I have an user control that combines the values of 2 DepedenciyProperties:
int numberPeople and ingredients Lists<>
I want when either of these values updates to redo the combination.
My current implementation uses a static variable to keep track of the object instance (objectInstance). I am wondering if there is a cleaner way to do this.
private static DependencyObject objectInstance;
public int numberPeople
{
get { return (int)GetValue(numberPeopleProperty); }
set { SetValue(numberPeopleProperty, value); }
}
public static readonly DependencyProperty numberPeopleProperty =
DependencyProperty.Register("numberPeople", typeof(int), typeof(ListDisplayer), new PropertyMetadata(0, Combine));
public ObservableCollection<ListModel> ingredients
{
get { return (ObservableCollection<ListModel>)GetValue(ingredientsProperty); }
set { SetValue(ingredientsProperty, value); }
}
public static readonly DependencyProperty ingredientsProperty =
DependencyProperty.Register("ingredients", typeof(ObservableCollection<ListModel>), typeof(ListDisplayer), new PropertyMetadata(null, AssignCollectionChangedToList));
private static void AssignCollectionChangedToList(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = d as ListDisplayer;
if (e.OldValue != null)
{
var coll = (INotifyCollectionChanged)e.OldValue;
coll.CollectionChanged -= ItemsSource_CollectionChanged;
}
if (e.NewValue != null)
{
instance.ItemsSource = (ObservableCollection<ListModel>)e.NewValue;
objectInstance = instance;
instance.ItemsSource.CollectionChanged += ItemsSource_CollectionChanged;
}
}
private static void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// MY PROBLEM: when a new item is added in this list trigger again Combine(), is there
// another way to trigger the Combine so that it will process the IngredientList and numberPeople attached to the object ?
Combine(objectInstance, new DependencyPropertyChangedEventArgs());
}
private static void Combine(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// process numberPeople and ingredientList
}
Edit: Remove the static keyword from the definition of the ItemsSource_CollectionChanged event handler and hook it up using the "instance" reference:
private static void AssignCollectionChangedToList(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = d as ListDisplayer;
if (e.OldValue != null)
{
var coll = (INotifyCollectionChanged)e.OldValue;
coll.CollectionChanged -= instance.ItemsSource_CollectionChanged;
}
if (e.NewValue != null)
{
instance.ingredients = (ObservableCollection<ListModel>)e.NewValue;
objectInstance = instance;
instance.ingredients.CollectionChanged += instance.ItemsSource_CollectionChanged;
}
}
private void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Combine(this, new DependencyPropertyChangedEventArgs());
}
Also the CLR wrappers of your dependency properties are not correctly implemented. You should pass the dependency property to the GetValue and SetValue methods:
public int numberPeople
{
get { return (int)GetValue(numberPeopleProperty); }
set { SetValue(numberPeopleProperty, value); }
}
public static readonly DependencyProperty numberPeopleProperty =
DependencyProperty.Register("numberPeople", typeof(int), typeof(ListDisplayer), new PropertyMetadata(0, Combine));
public ObservableCollection<ListModel> ingredients
{
get { return (ObservableCollection<ListModel>)GetValue(ingredientsProperty); }
set { SetValue(ingredientsProperty, value); }
}
public static readonly DependencyProperty ingredientsProperty =
DependencyProperty.Register("ingredients", typeof(ObservableCollection<ListModel>), typeof(ListDisplayer), new PropertyMetadata(null, AssignCollectionChangedToList));

How to bind a RelayCommand(MVVM) to a RoutedCommand? (CommandBinding)

I want to create a custom class of a CommandBinding, where a RelayCommand of my ViewModel is executed when the RoutedCommand is executed.
Currently there is only the possibility to create CommandBindings which have Executed methods in the codebehind class.
Example:
<CommandBinding Command="ApplicationCommands.Close" Executed="CloseCommandHandler"
CanExecute="CanExecuteHandler"/>
This needs the CloseCommandHandler methode in the code behind.
I would want to write the following.
<CommandBinding RoutedCommand="ApplicationCommands.Close" Command={Binding Path=CloseCommand}/>
The only problem is that i can't find the bubble down and up event of the RoutedCommands.
There is no
OnPreviewCommand(object command, object commandParammeter)
OnCommand(object command, object commandParammeter)
Where is the RoutedCommand bubble down and up handled?
I came up with a solutions of my own. Its not the most beautiful, but its working.
I derived from the ContentControl. The new Control has a RoutedCommandBindings property, which contains a list of "sort of" CommandBindings between RoutedCommands and RelayCommands.
It can be used like this.
<CSControls:RoutedCommandBinder>
<CSControls:RoutedCommandBinder.RoutedCommandBindings>
<CSControls:RoutedCommandBindingCollection>
<CSControls:RoutedCommandBinding RoutedCommand="{x:Static ApplicationCommands.New}" Command="{Binding Path=AddInstanceCommand}"/>
</CSControls:RoutedCommandBindingCollection>
</CSControls:RoutedCommandBinder.RoutedCommandBindings>
<CSControls:RoutedCommandBinder.Content>
<!-- Every RoutedCommand of type ApplicationCommands.New will execute the binded RelayCommand "AddInstanceCommand-->
</CSControls:RoutedCommandBinder.Content>
</CSControls:RoutedCommandBinder>
Here is the CustomControl code.
public class RoutedCommandBinder : ContentControl
{
public RoutedCommandBinder()
{
this.DataContextChanged += RoutedCommandBinder_DataContextChanged;
}
public static readonly DependencyProperty RoutedCommandBindingsProperty = DependencyProperty.Register("RoutedCommandBindings", typeof(RoutedCommandBindingCollection), typeof(RoutedCommandBinder), new PropertyMetadata(new RoutedCommandBindingCollection(), OnRoutedCommandBindingsChanged));
public RoutedCommandBindingCollection RoutedCommandBindings
{
get { return (RoutedCommandBindingCollection)this.GetValue(RoutedCommandBindingsProperty); }
set { SetValue(RoutedCommandBindingsProperty, value); }
}
private static void OnRoutedCommandBindingsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RoutedCommandBinder binder = (RoutedCommandBinder)d;
binder.CommandBindings.Clear();
SetDataContextForCommandBindings(binder);
if (e.NewValue != null)
{
RoutedCommandBindingCollection bindings = (RoutedCommandBindingCollection)e.NewValue;
foreach (RoutedCommandBinding binding in bindings)
{
binder.CommandBindings.Add(new CommandBinding(binding.RoutedCommand, binder.RoutedCommandExecuted, binder.CanExecuteRoutedEventHandler));
}
}
}
private void RoutedCommandBinder_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
SetDataContextForCommandBindings(this);
}
private static void SetDataContextForCommandBindings(RoutedCommandBinder binder)
{
if (binder.DataContext != null && binder.RoutedCommandBindings != null)
{
foreach (RoutedCommandBinding binding in binder.RoutedCommandBindings)
{
binding.DataContext = binder.DataContext;
}
}
}
private void RoutedCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
RoutedCommandBinding binding = this.RoutedCommandBindings.FirstOrDefault(t => t.RoutedCommand == e.Command);
if (binding != null)
{
binding.Command.Execute(e.Parameter);
}
}
private void CanExecuteRoutedEventHandler(object sender, CanExecuteRoutedEventArgs e)
{
RoutedCommandBinding binding = this.RoutedCommandBindings.FirstOrDefault(t => t.RoutedCommand == e.Command);
if (binding != null)
{
e.CanExecute = binding.Command.CanExecute(e.Parameter);
}
}
}
public class RoutedCommandBindingCollection : List<RoutedCommandBinding>
{
}
public class RoutedCommandBinding : FrameworkElement
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(RoutedCommandBinding), new PropertyMetadata(null));
public ICommand Command
{
get { return (ICommand)this.GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty RoutedCommandProperty = DependencyProperty.Register("RoutedCommand", typeof(RoutedCommand), typeof(RoutedCommandBinding), new PropertyMetadata(null));
public RoutedCommand RoutedCommand
{
get { return (RoutedCommand)this.GetValue(RoutedCommandProperty); }
set { SetValue(RoutedCommandProperty, value); }
}
}

How can I fix the DependencyPropertyDescriptor AddValueChanged Memory Leak on AttachedBehavior?

I know I need to call RemoveValueChanged, but I have not been able to find a reliable place to call this. I'm learning that there probably isn't one.
I looks like I need to find a different way to monitor the change then adding a handler using AddValueChanged. I'm looking for advice on the best way to achieve this. I've seen the recommendation of using a PropertyChangedCallback in the PropertyMetadata, but I'm not sure how to do this when my TextBox and Adorner are not static. Also, the IsFocused property is not a DependencyProperty created in my class.
public sealed class WatermarkTextBoxBehavior
{
private readonly TextBox m_TextBox;
private TextBlockAdorner m_TextBlockAdorner;
private WatermarkTextBoxBehavior(TextBox textBox)
{
if (textBox == null)
throw new ArgumentNullException("textBox");
m_TextBox = textBox;
}
#region Behavior Internals
private static WatermarkTextBoxBehavior GetWatermarkTextBoxBehavior(DependencyObject obj)
{
return (WatermarkTextBoxBehavior)obj.GetValue(WatermarkTextBoxBehaviorProperty);
}
private static void SetWatermarkTextBoxBehavior(DependencyObject obj, WatermarkTextBoxBehavior value)
{
obj.SetValue(WatermarkTextBoxBehaviorProperty, value);
}
private static readonly DependencyProperty WatermarkTextBoxBehaviorProperty =
DependencyProperty.RegisterAttached("WatermarkTextBoxBehavior",
typeof(WatermarkTextBoxBehavior), typeof(WatermarkTextBoxBehavior), new UIPropertyMetadata(null));
public static bool GetEnableWatermark(TextBox obj)
{
return (bool)obj.GetValue(EnableWatermarkProperty);
}
public static void SetEnableWatermark(TextBox obj, bool value)
{
obj.SetValue(EnableWatermarkProperty, value);
}
public static readonly DependencyProperty EnableWatermarkProperty =
DependencyProperty.RegisterAttached("EnableWatermark", typeof(bool),
typeof(WatermarkTextBoxBehavior), new UIPropertyMetadata(false, OnEnableWatermarkChanged));
private static void OnEnableWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != null)
{
var enabled = (bool)e.OldValue;
if (enabled)
{
var textBox = (TextBox)d;
var behavior = GetWatermarkTextBoxBehavior(textBox);
behavior.Detach();
SetWatermarkTextBoxBehavior(textBox, null);
}
}
if (e.NewValue != null)
{
var enabled = (bool)e.NewValue;
if (enabled)
{
var textBox = (TextBox)d;
var behavior = new WatermarkTextBoxBehavior(textBox);
behavior.Attach();
SetWatermarkTextBoxBehavior(textBox, behavior);
}
}
}
private void Attach()
{
m_TextBox.Loaded += TextBoxLoaded;
m_TextBox.TextChanged += TextBoxTextChanged;
m_TextBox.DragEnter += TextBoxDragEnter;
m_TextBox.DragLeave += TextBoxDragLeave;
m_TextBox.IsVisibleChanged += TextBoxIsVisibleChanged;
}
private void Detach()
{
m_TextBox.Loaded -= TextBoxLoaded;
m_TextBox.TextChanged -= TextBoxTextChanged;
m_TextBox.DragEnter -= TextBoxDragEnter;
m_TextBox.DragLeave -= TextBoxDragLeave;
m_TextBox.IsVisibleChanged -= TextBoxIsVisibleChanged;
}
private void TextBoxDragLeave(object sender, DragEventArgs e)
{
UpdateAdorner();
}
private void TextBoxDragEnter(object sender, DragEventArgs e)
{
m_TextBox.TryRemoveAdorners<TextBlockAdorner>();
}
private void TextBoxIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UpdateAdorner();
}
private void TextBoxTextChanged(object sender, TextChangedEventArgs e)
{
var hasText = !string.IsNullOrEmpty(m_TextBox.Text);
SetHasText(m_TextBox, hasText);
}
private void TextBoxLoaded(object sender, RoutedEventArgs e)
{
Init();
}
#endregion
#region Attached Properties
public static string GetLabel(TextBox obj)
{
return (string)obj.GetValue(LabelProperty);
}
public static void SetLabel(TextBox obj, string value)
{
obj.SetValue(LabelProperty, value);
}
public static readonly DependencyProperty LabelProperty =
DependencyProperty.RegisterAttached("Label", typeof(string), typeof(WatermarkTextBoxBehavior));
public static Style GetLabelStyle(TextBox obj)
{
return (Style)obj.GetValue(LabelStyleProperty);
}
public static void SetLabelStyle(TextBox obj, Style value)
{
obj.SetValue(LabelStyleProperty, value);
}
public static readonly DependencyProperty LabelStyleProperty =
DependencyProperty.RegisterAttached("LabelStyle", typeof(Style),
typeof(WatermarkTextBoxBehavior));
public static bool GetHasText(TextBox obj)
{
return (bool)obj.GetValue(HasTextProperty);
}
private static void SetHasText(TextBox obj, bool value)
{
obj.SetValue(HasTextPropertyKey, value);
}
private static readonly DependencyPropertyKey HasTextPropertyKey =
DependencyProperty.RegisterAttachedReadOnly("HasText", typeof(bool),
typeof(WatermarkTextBoxBehavior), new UIPropertyMetadata(false));
public static readonly DependencyProperty HasTextProperty =
HasTextPropertyKey.DependencyProperty;
#endregion
private void Init()
{
m_TextBlockAdorner = new TextBlockAdorner(m_TextBox, GetLabel(m_TextBox), GetLabelStyle(m_TextBox));
UpdateAdorner();
DependencyPropertyDescriptor focusProp = DependencyPropertyDescriptor.FromProperty(UIElement.IsFocusedProperty, typeof(FrameworkElement));
if (focusProp != null)
{
focusProp.AddValueChanged(m_TextBox, (sender, args) => UpdateAdorner());
}
DependencyPropertyDescriptor containsTextProp = DependencyPropertyDescriptor.FromProperty(HasTextProperty, typeof(TextBox));
if (containsTextProp != null)
{
containsTextProp.AddValueChanged(m_TextBox, (sender, args) => UpdateAdorner());
}
}
private void UpdateAdorner()
{
if (GetHasText(m_TextBox) ||
m_TextBox.IsFocused ||
!m_TextBox.IsVisible)
{
// Hide the Watermark Label if the adorner layer is visible
m_TextBox.ToolTip = GetLabel(m_TextBox);
m_TextBox.TryRemoveAdorners<TextBlockAdorner>();
}
else
{
// Show the Watermark Label if the adorner layer is visible
m_TextBox.ToolTip = null;
m_TextBox.TryAddAdorner<TextBlockAdorner>(m_TextBlockAdorner);
}
}
}
AddValueChanged of dependency property descriptor results in memory leak as you already know. So, as described here, you can create custom class PropertyChangeNotifier to listen to any dependency property changes.
Complete implementation can be found here - PropertyDescriptor AddValueChanged Alternative.
Quote from the link:
This class takes advantage of the fact that bindings use weak
references to manage associations so the class will not root the
object who property changes it is watching. It also uses a
WeakReference to maintain a reference to the object whose property it
is watching without rooting that object. In this way, you can maintain
a collection of these objects so that you can unhook the property
change later without worrying about that collection rooting the object
whose values you are watching.
Also for sake of completeness of answer I am posting complete code here to avoid any rot issue in future.
public sealed class PropertyChangeNotifier : DependencyObject, IDisposable
{
#region Member Variables
private readonly WeakReference _propertySource;
#endregion // Member Variables
#region Constructor
public PropertyChangeNotifier(DependencyObject propertySource, string path)
: this(propertySource, new PropertyPath(path))
{
}
public PropertyChangeNotifier(DependencyObject propertySource, DependencyProperty property)
: this(propertySource, new PropertyPath(property))
{
}
public PropertyChangeNotifier(DependencyObject propertySource, PropertyPath property)
{
if (null == propertySource)
throw new ArgumentNullException("propertySource");
if (null == property)
throw new ArgumentNullException("property");
_propertySource = new WeakReference(propertySource);
Binding binding = new Binding
{
Path = property,
Mode = BindingMode.OneWay,
Source = propertySource
};
BindingOperations.SetBinding(this, ValueProperty, binding);
}
#endregion // Constructor
#region PropertySource
public DependencyObject PropertySource
{
get
{
try
{
// note, it is possible that accessing the target property
// will result in an exception so i’ve wrapped this check
// in a try catch
return _propertySource.IsAlive
? _propertySource.Target as DependencyObject
: null;
}
catch
{
return null;
}
}
}
#endregion // PropertySource
#region Value
/// <summary>
/// Identifies the <see cref="Value"/> dependency property
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
typeof(object), typeof(PropertyChangeNotifier), new FrameworkPropertyMetadata(null, OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PropertyChangeNotifier notifier = (PropertyChangeNotifier)d;
if (null != notifier.ValueChanged)
notifier.ValueChanged(notifier, EventArgs.Empty);
}
/// <summary>
/// Returns/sets the value of the property
/// </summary>
/// <seealso cref="ValueProperty"/>
[Description("Returns/sets the value of the property")]
[Category("Behavior")]
[Bindable(true)]
public object Value
{
get
{
return GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
}
}
#endregion //Value
#region Events
public event EventHandler ValueChanged;
#endregion // Events
#region IDisposable Members
public void Dispose()
{
BindingOperations.ClearBinding(this, ValueProperty);
}
#endregion
}
A more lightweight solution for FrameworkElements and FrameworkContentElements is to subscribe to the Unloaded event and remove the handler. This requires a non-anonymous delegate (UpdateAdorner in that case) though:
focusProp.AddValueChanged(m_TextBox, UpdateAdorner);
m_TextBox.Unloaded += (sender, args) => focusProp.RemoveValueChanged(sender, UpdateAdorner);

Binding to DependencyProperty of a UserControl

I Create a TimeInput Control Like to Enter Time.
<TextBox Text="{Binding Path=Hours}" />
<TextBox IsReadOnly="True"
Focusable="False"
Text=":" />
<TextBox Text="{Binding Path=Minutes}" />
and
public int Hours {
get { return (int)this.GetValue(HoursProperty); }
set {
this.SetValue(HoursProperty, value);
this.OnPropertyChanged("Hours");
}
}
public static readonly DependencyProperty HoursProperty =
DependencyProperty.Register("Hours", typeof(int), typeof(UserControl1), new UIPropertyMetadata(0, new PropertyChangedCallback(OnHoursChanged)));
private static void OnHoursChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
if (obj != null) {
int newValue = (int)e.NewValue;
}
}
public int Minutes {
get { return (int)this.GetValue(MinutesProperty); }
set {
this.SetValue(MinutesProperty, value);
this.OnPropertyChanged("Minutes");
}
}
// Using a DependencyProperty as the backing store for Minutes. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MinutesProperty =
DependencyProperty.Register("Minutes", typeof(int), typeof(UserControl1), new UIPropertyMetadata(0, new PropertyChangedCallback(OnMinutesChanged)));
private static void OnMinutesChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
if (obj != null) {
int newValue = (int)e.NewValue;
}
}
public Nullable<TimeSpan> Value {
get { return (Nullable<TimeSpan>)this.GetValue(ValueProperty); }
set {
this.SetValue(ValueProperty, value);
this.OnPropertyChanged("Value");
}
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(Nullable<TimeSpan>), typeof(UserControl1), new UIPropertyMetadata(null, new PropertyChangedCallback(OnValueChanged)));
private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
if (obj != null) {
(obj as UserControl1).UpdateTime(e.NewValue as TimeSpan?);
}
}
public void UpdateTime(TimeSpan? newTimeSpan) {
if (newTimeSpan.HasValue) {
this.Hours = newTimeSpan.Value.Hours;
this.Minutes = newTimeSpan.Value.Minutes;
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
While I use this on another UserControl and Bind to a Property It doesn't work and show values.
I use it like this:
<uc:UserControl1 Value="{Binding StartTime}"/>
and
public TimeSpan StartTime
{
get { return new Types.Time(Item.StartTime).ToTimeSpan(); }
set { Item.StartTime = new Types.Time(value).ToShort(); NotifyPropertyChanged("StartTime"); }
}
That Item is an entity read from database and bind and StartTime is short form of hhmm.
i have updated your code, with dependency properties you don't need fire the property changed event explicit.
public partial class UserControl1 : UserControl
{
public UserControl1() {
this.InitializeComponent();
}
public int Hours {
get { return (int)this.GetValue(HoursProperty); }
set { this.SetValue(HoursProperty, value); }
}
public static readonly DependencyProperty HoursProperty =
DependencyProperty.Register("Hours", typeof(int), typeof(UserControl1), new UIPropertyMetadata(0, new PropertyChangedCallback(OnHoursChanged)));
private static void OnHoursChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
var uc = obj as UserControl1;
if (uc != null && e.NewValue != e.OldValue) {
int newValue = (int)e.NewValue;
uc.TimeValue = new TimeSpan(newValue, uc.Minutes, 0);
}
}
public int Minutes {
get { return (int)this.GetValue(MinutesProperty); }
set { this.SetValue(MinutesProperty, value); }
}
// Using a DependencyProperty as the backing store for Minutes. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MinutesProperty =
DependencyProperty.Register("Minutes", typeof(int), typeof(UserControl1), new UIPropertyMetadata(0, new PropertyChangedCallback(OnMinutesChanged)));
private static void OnMinutesChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
var uc = obj as UserControl1;
if (uc != null && e.NewValue != e.OldValue) {
int newValue = (int)e.NewValue;
uc.TimeValue = new TimeSpan(uc.Hours, newValue, 0);
}
}
public Nullable<TimeSpan> TimeValue {
get { return (Nullable<TimeSpan>)this.GetValue(ValueProperty); }
set { this.SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("TimeValue", typeof(Nullable<TimeSpan>), typeof(UserControl1), new UIPropertyMetadata(null, new PropertyChangedCallback(OnValueChanged)));
private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
var uc = obj as UserControl1;
if (uc != null && e.NewValue != e.OldValue) {
uc.UpdateTime(e.NewValue as TimeSpan?);
}
}
public void UpdateTime(TimeSpan? newTimeSpan) {
if (newTimeSpan.HasValue) {
this.Hours = newTimeSpan.Value.Hours;
this.Minutes = newTimeSpan.Value.Minutes;
}
}
}
second, i think you use the StartTime property incorrect, use it as dependency property too, or implement INotifyPropertyChanged.
{
// .....
StartTime = new Types.Time(this.Item.StartTime).ToTimeSpan();
// .....
}
public static readonly DependencyProperty StartTimeProperty =
DependencyProperty.Register("StartTime", typeof(TimeSpan?), typeof(Window1), new PropertyMetadata(default(TimeSpan?), new PropertyChangedCallback(OnStartTimePropertyChanged)));
private static void OnStartTimePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {
if(e.NewValue != e.OldValue) {
(dependencyObject as Window1).Item.StartTime = new Types.Time(e.NewValue).ToShort();
}
}
public TimeSpan? StartTime {
get { return (TimeSpan?)GetValue(StartTimeProperty); }
set { SetValue(StartTimeProperty, value); }
}
hope this helps
You should not have any other code then calling GetValue and SetValue inside getter and setter of dependency property. But this may not resolve you problem. If you want to call some code when value change then do that inside callback method instead of setter.

WPF- Why can't my custom textbox be selected?

I have this custom textbox that I am working on and I can use it in xaml, but when I run my app I cannot select it or type in it. Here is my code:
public class ModdedTextBox : TextBox
{
private bool selectionStartChangeFromUI;
private bool selectionLengthChangeFromUI;
private bool selectedTextChangeFromUI;
static ModdedTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ModdedTextBox), new FrameworkPropertyMetadata(typeof(ModdedTextBox)));
//this.SelectionChanged += this.OnSelectionChanged;
//PropertyDescriptor VerticalOffsetProperty = TypeDescriptor.GetProperties(typeof(ModdedTextBox))["VerticalOffset"];
//VerticalOffsetProperty.AddValueChanged(this, this.OnVerticalOffsetChanged);
}
public static readonly DependencyProperty BindableSelectionStartProperty =
DependencyProperty.Register(
"BindableSelectionStart",
typeof(int),
typeof(ModdedTextBox),
new PropertyMetadata(OnBindableSelectionStartChanged));
public static readonly DependencyProperty BindableSelectionLengthProperty =
DependencyProperty.Register(
"BindableSelectionLength",
typeof(int),
typeof(ModdedTextBox),
new PropertyMetadata(OnBindableSelectionLengthChanged));
public static readonly DependencyProperty BindableSelectedTextProperty =
DependencyProperty.Register(
"BindableSelectedText",
typeof(string),
typeof(ModdedTextBox),
new PropertyMetadata(OnBindableSelectedTextChanged));
public static readonly DependencyProperty DelayedTextProperty =
DependencyProperty.Register(
"DelayedText",
typeof(string),
typeof(ModdedTextBox),
new PropertyMetadata(OnDelayedTextChanged));
public int BindableSelectionStart
{
get
{
return (int)this.GetValue(BindableSelectionStartProperty);
}
set
{
this.SetValue(BindableSelectionStartProperty, value);
}
}
public int BindableSelectionLength
{
get
{
return (int)this.GetValue(BindableSelectionLengthProperty);
}
set
{
this.SetValue(BindableSelectionLengthProperty, value);
}
}
public string BindableSelectedText
{
get
{
return (string)this.GetValue(BindableSelectedTextProperty);
}
private set
{
this.SetValue(BindableSelectedTextProperty, value);
}
}
public string DelayedText
{
get
{
return (string)this.GetValue(DelayedTextProperty);
}
private set
{
this.SetValue(DelayedTextProperty, value);
}
}
private static void OnBindableSelectionStartChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var textBox = dependencyObject as ModdedTextBox;
if (!textBox.selectionStartChangeFromUI)
{
int newValue = (int)args.NewValue;
textBox.SelectionStart = newValue;
}
else
{
textBox.selectionStartChangeFromUI = false;
}
}
private static void OnBindableSelectionLengthChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var textBox = dependencyObject as ModdedTextBox;
if (!textBox.selectionLengthChangeFromUI)
{
int newValue = (int)args.NewValue;
textBox.SelectionLength = newValue;
}
else
{
textBox.selectionLengthChangeFromUI = false;
}
}
private static void OnBindableSelectedTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var textBox = dependencyObject as ModdedTextBox;
if (!textBox.selectedTextChangeFromUI)
{
string newValue = (string)args.NewValue;
textBox.BindableSelectedText = newValue;
}
else
{
textBox.selectedTextChangeFromUI = false;
}
}
private static void OnDelayedTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
}
private void OnSelectionChanged(object sender, RoutedEventArgs e)
{
if (this.BindableSelectionStart != this.SelectionStart)
{
this.selectionStartChangeFromUI = true;
this.BindableSelectionStart = this.SelectionStart;
}
if (this.BindableSelectionLength != this.SelectionLength)
{
this.selectionLengthChangeFromUI = true;
this.BindableSelectionLength = this.SelectionLength;
}
if (this.BindableSelectedText != this.SelectedText)
{
this.selectedTextChangeFromUI = true;
this.BindableSelectedText = this.SelectedText;
}
}
private void OnVerticalOffsetChanged(object sender, EventArgs e)
{
MessageBox.Show("hello the vertical offset works");
}
}
your control needs a style to display itself.
comment out this one line from the constructor, to use default style
DefaultStyleKeyProperty.OverrideMetadata(typeof(ModdedTextBox), new FrameworkPropertyMetadata(typeof(ModdedTextBox)));
Done!
edit: Alternatively this will explicitly make your control use TextBox style
DefaultStyleKeyProperty.OverrideMetadata(
typeof(ModdedTextBox),
new FrameworkPropertyMetadata(typeof(TextBox)));

Resources