Hiding a Dependency Property - wpf

Just wondering if this is a good practice or if it could cause any troubles in the long run. To be honest, I'm surprised it even works - it does the job, but I'm not sure if it's risky.
Basically we created a NumericTextBox that derives from TextBox, and we overrode the Text property with the new keyword to remove commas from the text:
public class NumericTextBox : TextBox
{
public new string Text
{
get
{
return base.Text.Replace(",", String.Empty);
}
set
{
base.Text = value;
}
}
}
What I don't like about it is that I know Text is a dependency property and we're overriding it, but surprisingly we can still bind to it in XAML:
<this:NumericTextBox x:Name="textBox"
Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=SomeText, Converter={StaticResource debugConverter}}" />
Then in C# when we call textBox.Text we do get the values without commas.
What do you guys think?

Perhaps you should add your class as an owner of the dependency property and override the getter and setter there:
public class NumericTextBox : TextBox
{
public NumericTextBox() { }
public static readonly DependencyProperty NumericTextProperty = TextBox.TextProperty.AddOwner(typeof(NumericTextBox), new PropertyMetadata(null));
public new string Text
{
get { return ((string)this.GetValue(NumericTextProperty )).Replace(",", String.Empty); }
set { this.SetValue(NumericTextProperty , value); }
}
}
You also have the possibility of overriding the metadata of the dependency property to hook in a custom validation callback method.
You approach doesn't work, because WPF doesn't actually use the class properties to change the value, but the dependency property system. It simply calls the SetValue method as you do in your property setter. You can try it out by setting a breakpoint in the setter, and changing the bound property in the gui. The setter breakpoint will never be hit. But you can hook into the events provided by the dependency property metadata.

Related

Silverlight DataBinding to a ReadOnlyObservableCollection only fires the first time

in my Silverlight 4 project, I like to bind an ReadOnlyObservableCollection to a Button Property. The collection is part of a class, which binds to the DataContect of the UserControl, that contains the Button.
<Button Visibility="{Binding Children, Converter={StaticResource ConvertHasListItems2Visibility}}" />
The converter is called the first time, when the control is created, but after that, it isn't called when I add or remove items to the collection. I checked this with breakpoints. I even tried to bind to an ObservableCollection, but there is the same problem.
Binding to another property of my class works (my class implements INotifyPropertyChanged).
Is there anything special with binding to a (ReadOnly)ObservableCollection, that should notify, when its elements changed ( added or removed, to be exact)?
Thanks in advance,
Frank
Edith includes the declaration of the Collection(s):
public class MyClass
{
private ObservableCollection<IServiceItemVMBase> _children;
private ReadOnlyObservableCollection<IServiceItemVMBase> _childrenReadOnly;
public ViewModelBase(IServiceObjectBase serviceObject, IServiceItemVMBase parent)
{
_children = new ObservableCollection<IServiceItemVMBase>();
_childrenReadOnly = new ReadOnlyObservableCollection<IServiceItemVMBase>(_children);
}
public ReadOnlyObservableCollection<IServiceItemVMBase> Children
{
get { return _childrenReadOnly; }
}
}

WPF Custom binding Collection to non dependency property

I've created my own custom binding class and added a property to it:
public BindingGroupCollection BindingGroups
{
get { return validationResultGroup; }
set { validationResultGroup = value; }
}
public class BindingGroupCollection : ObservableCollection<BindingGroup> { }
In my xaml class i declared the objects and collection:
<local:BindingGroup x:Key="BG1"/>
<local:BindingGroup x:Key="BG2"/>
<local:BindingGroupCollection x:Key="BindingGroups1">
<StaticResourceExtension ResourceKey="BG1"/>
<StaticResourceExtension ResourceKey="BG2"/>
</local:BindingGroupCollection>
And i want to use this in my binding eg.:
<TextBox Text="{local:CustomBinding BindingGroups={Binding Source={StaticResource BindingGroups1}}}"/>
But i get an error that the target is not a dependeny object. Any help?
You can't do that, because a Binding isn't a DependencyObject, so it can't have have dependency properties.
However, in your case you don't need a binding, you can use the StaticResource directly:
<TextBox Text="{local:CustomBinding BindingGroups={StaticResource BindingGroups1}}"/>

Caliburn Micro: how to set binding UpdateSourceTrigger?

I've been exploring the Caliburn Micro MVVM Framework just to get a feel for it, but I've run into a bit of a problem. I have a TextBox bound to a string property on my ViewModel and I would like the property to be updated when the TextBox loses focus.
Normally I would achieve this by setting the UpdateSourceTrigger to LostFocus on the binding, but I don't see any way to do this within Caliburn, as it has setup the property binding for me automatically. Currently the property is updated every time the content of the TextBox changes.
My code is very simple, for instance here is my VM:
public class ShellViewModel : PropertyChangeBase
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyOfPropertyChange(() => Name);
}
}
}
And inside my view I have a simple TextBox.
<TextBox x:Name="Name" />
How to I change it so the Name property is only updated when the TextBox loses focus, instead of each time the property changes?
Just set the binding explictly for that instance of the TextBox and Caliburn.Micro won't touch it:
<TextBox Text="{Binding Name, UpdateSourceTrigger=LostFocus}" />
Alternatively, if you want to change the default behaviour for all instances of TextBox, then you can change the implementation of ConventionManager.ApplyUpdateSourceTrigger in your bootstrapper's Configure method.
Something like:
protected override void Configure()
{
ConventionManager.ApplyUpdateSourceTrigger = (bindableProperty, element, binding) =>{
#if SILVERLIGHT
ApplySilverlightTriggers(
element,
bindableProperty,
x => x.GetBindingExpression(bindableProperty),
info,
binding
);
#else
if (element is TextBox)
{
return;
}
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
#endif
};
}

How can I prevent a ToggleButton from being Toggled without setting IsEnabled

I have a list of ToggleButtons being used as the ItemTemplate in a ListBox similar to this answer using the MultiSelect mode of the Listbox. However I need to make sure at least one item is always selected.
I can get the proper behavior from the ListBox by just adding an item back into the ListBox's SelectedItems collection on the ListBox.SelectionChanged event but my ToggleButton still moves out of its toggled state so I think I need to stop it earlier in the process.
I would like to do it without setting IsEnabled="False" on the last button Selected because I'd prefer to stay with the Enabled visual style without having to redo my button templates. Any ideas?
You can override the OnToggle method to prevent toggling the state, by not calling the base implementation :
public class LockableToggleButton : ToggleButton
{
protected override void OnToggle()
{
if (!LockToggle)
{
base.OnToggle();
}
}
public bool LockToggle
{
get { return (bool)GetValue(LockToggleProperty); }
set { SetValue(LockToggleProperty, value); }
}
// Using a DependencyProperty as the backing store for LockToggle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LockToggleProperty =
DependencyProperty.Register("LockToggle", typeof(bool), typeof(LockableToggleButton), new UIPropertyMetadata(false));
}
Have you tried using RadioButtons instead? It normally can't be deselected without selecting another one. It can also be styled to look like a ToggleButton:
<RadioButton Style="{StaticResource {x:Type ToggleButton}}"/>
Or, if you already have a Style for it, just make it BasedOn="{x:Type ToggleButton}". Note that the Visual Studio Editor shows an error in the first case, but it compiles and works fine.
This is hackey, but if you don't want custom code you could always use the property "IsHitTestVisible", when you don't want them to uncheck it, simply set IsHitTestVisible equal to false. However, they may be able to tab to the control and toggle it using the space bar.
Thomas's answer works fine, but you don't even need the extra dependency property. Your button will update correctly if you have the class inherit from ToggleButton so you can override the OnToggle method, and you change the IsChecked bound property on the ViewModel.
Xaml:
<myControls:OneWayFromSourceToTargetToggle x:Name="MyCustomToggleButton"
Command="{Binding Path=ToggleDoStuffCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}"
IsChecked="{Binding Path=ToggleIsCheckedConditionVar,
Mode=OneWay}"
/>
Added ToggleButton Class:
public class OneWayFromSourceToTargetToggle : ToggleButton
{
/// <summary>
/// Overrides the OnToggle method, so it does not set the IsChecked Property automatically
/// </summary>
protected override void OnToggle()
{
// do nothing
}
}
Then in the ViewModel just set bool ToggleIsCheckedCondition to true or false. This is a nice way to do it because you are following good MVVM practices.
ViewModel:
public bool ToggleIsCheckedCondition
{
get { return _toggleIsCheckedCondition; }
set
{
if (_toggleIsCheckedCondition != value)
{
_toggleIsCheckedCondition = value;
NotifyPropertyChanged("ToggleIsCheckedCondition");
}
}
}
public ICommand ToggleDoStuffCommand
{
get {
return _toggleDoStuffCommand ??
(_toggleDoStuffCommand = new RelayCommand(ExecuteToggleDoStuffCommand));
}
}
private void ExecuteToggleDoStuffCommand(object param)
{
var btn = param as ToggleButton;
if (btn?.IsChecked == null)
{
return;
}
// has not been updated yet at this point
ToggleIsCheckedCondition = btn.IsChecked == false;
// do stuff
}
}
Adding a little bit to #Joachim-Mairböck's great answer in case you want to do the same programmatically:
new RadioButton {
...
GroupName = "myButtonGroup"
Style = Application.Current.TryFindResource(typeof(ToggleButton)) as Style
...
}

Binding new property on Custom Control to View Model

How do i extend an existing control (ComboBox in my case) to include a new property which i can bind to a property on my view model??
I have a Dependancy Property on the control's class as follows:
public class MyComboBox : ComboBox
{
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register("MyText", typeof(string), typeof(MyComboBox));
public string MyText
{
get
{
return GetValue(MyComboBox.MyTextProperty).ToString();
}
set
{
SetValue(MyComboBox.MyTextProperty, value);
}
}
And want to bind to it declaratively from XAML like this:
<MyComboBox MyText="{Binding MyTextOnViewModel,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
The Binding just won't work, any ideas why??
Thanks.
Your getter and setter reference TestTextProperty while the property is declared as MyTextProperty.
Your getter should also be casting instead of calling .ToString()
return (string)GetValue(MyTextProperty);
See this page for more complete instructions.

Resources