DependencyProperties: PropertyChangedCallBack only called once - c#

I created a control, derived from Canvas, that should plot a live diagram, given values that are passed via a binding to a DependencyProperty. The simplified version is this:
public class Plotter : Canvas
{
public float Value { get { return (float)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(float), typeof(Plotter),
new PropertyMetadata(0f, new PropertyChangedCallback(ValueChangedCallBack)));
public static void ValueChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
Plotter plotter = (Plotter)property;
plotter.Value = (float)args.NewValue; //<-- Removed this line to get it to work
// Actually draw the value into the canvas geometry
plotter.PlotValue(plotter.Value);
}
}
I bound the control like this:
<mystuff:Plotter Value="{Binding MyViewModelProperty}" Height="50" Width="200" />
My ViewModel implements INotifyPropertyChanged and calls PropertyChanged correctly. If I bind MyViewModelProperty to a textbox, it correctly updates every time. Only if I bind it to my own control, my ValueChangedCallBack is only called once as the page is loaded, and then never again.
What am I not seeing here? Thanks for any help!
Solved: I dont have to set the Value explicitly in the callback.

You set the property Value on the callback for the property Value changing. That doesn't make much sense in any case. But is that locally set value overriding the binding value, causing your binding to no longer be set on the dependency property?

Do you need to set the Mode of your binding to TwoWay?

Should you not be using DependencyProperty.Register instead of DependencyProperty.RegisterAttached?

Related

Getting IDataErrorInfo validation to update in child UserControl when bound ViewModel OnPropertyChanged is called but the value is the same

I have a reusable UserControl defined that will be used multiple times within the parent form, to represent different instances of a configured object. This UserControl has several TextBoxes representing configurable properties. For one of these properties, the value must be unique across all instances of the reusable UserControl.
My parent form utilizes these usercontrols like this:
<namespace:ReusableControl
Property1="{Binding Path=ViewModelProperty1a, Mode=TwoWay}"
Property2="{Binding Path=ViewModelProperty2a, Mode=TwoWay}"
UniqueProperty="{Binding Path=VMUniquePropertya, Mode=TwoWay}"/>
<namespace:ReusableControl
Property1="{Binding Path=ViewModelProperty1b, Mode=TwoWay}"
Property2="{Binding Path=ViewModelProperty2b, Mode=TwoWay}"
UniqueProperty="{Binding Path=VMUniquePropertyb, Mode=TwoWay}"/>
And the UserControl property looks like this:
<TextBox
x:Name="UniquePropertyTextBox"
Text="{Binding Path=UniqueProperty,
RelativeSource={RelativeSource AncestorType=local:ReusableControl},
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
/>
The codebehind for the UserControl contains IDataErrorInfo validation:
public string this[string columnName]
{
get
{
string error = string.Empty;
switch (columnName)
{
case nameof(UniqueProperty):
if (!((MyViewModel)DataContext).UniquePropertiesAreUnique())
{
error = "not unique";
}
break;
//Other cases here, omitted from example
}
return error;
}
}
//-------------------------------
//Just to show the codebehind for the property:
public string UniqueProperty
{
get { return (string)GetValue(UniquePropertyDP); }
set { SetValue(UniquePropertyDP, value); }
}
public static readonly DependencyProperty UniquePropertyDP=
DependencyProperty.Register(
"UniqueProperty",
typeof(string),
typeof(ReusableControl),
new PropertyMetadata(string.Empty));
Everything appears to be wired up and bound correctly; the values update when the UI is changed as desired. If I change one of the unique property values such that it is no longer unique, I get the red border around that text box, but this is where the issue comes in - the red border only appears around the text box I just changed, not both of the instances of UniqueProperty. In the ViewModel, when either of the UniqueProperty values are changed, it triggers OnPropertyChanged for the other, but this still isn't causing the validation border to appear. If I replace OnPropertyChange with an explicit call to update the value i.e:
//In the setter for VMUniquePropertyb:
var temp = VMUniquePropertya;
VMUniquePropertya = null;
VMUniquePropertya = temp;
Then I do get the validation border to appear on both text boxes when that value is changed to match the other, and both borders disappear when either value is changed to be unique again. Of course, this is a hack, and also will cause an infinite loop if used on both properties. How can I accomplish the same result with OnPropertyChanged?
I found a solution that works for me. There may be better ways to do this, but this works fairly well.
By using the CoerceValueCallback on the DependencyProperty, we can execute code whenever the value of the property is re-evaluated, not just when it actually changes. This will fire when the PropertyChange event occurs in the ViewModel because the binding is re-evaluated. This looks like this:
public string UniqueProperty
{
get { return (string)GetValue(UniquePropertyDP); }
set { SetValue(UniquePropertyDP, value); }
}
public static readonly DependencyProperty UniquePropertyDP=
DependencyProperty.Register(
"UniqueProperty",
typeof(string),
typeof(ReusableControl),
new PropertyMetadata(string.Empty, null, UniquePropertyCoerceValueCallback));
private static object UniquePropertyCoerceValueCallback(DependencyObject d, object value)
{
((ReusableControl)d).UniquePropertyTextBox.GetBindingExpression(TextBox.TextProperty)
.UpdateTarget();
return value;
}
When the value of one of the unique properties changes and the ViewModel fires the PropertyChange event for the other unique property in the ViewModel, the DependencyProperty in the UserControl will be re-coerced, triggering this callback and updating validation.

Binding A UserControl DependencyProperty, Always DefaultValue?

I've got a UserControl with a DependencyProperty created in its CodeBehind:
public partial class PanelMap_Control : UserControl
{
public ObservableCollection<GMapMarker> Markers
{
get { return (ObservableCollection<GMapMarker>)GetValue(MarkersProperty); }
set { SetValue(MarkersProperty, value); }
}
public static readonly DependencyProperty MarkersProperty = DependencyProperty.Register("Markers", typeof(ObservableCollection<GMapMarker>), typeof(PanelMap_Control), new PropertyMetadata(null));
}
Inside the UserControl is a Map, which contains the original collection of markers (not a DependencyProperty). I need to make this available for binding outside the UserControl, so at the end of the constructor (once the map's Markers are all setup), I set it to the control's DependencyProperty:
public PanelMap_Control()
{
InitializeComponent();
//...map setup...
this.Markers = _map.Markers;
}
Then, users of the UserControl can bind like:
<local:PanelMap_Control Markers="{Binding Path=MapMarkers, Mode=OneWayToSource}"/>
Where in the ViewModel:
public ObservableCollection<GMap.NET.WindowsPresentation.GMapMarker> MapMarkers
{
private get
{
return _mapMarkers;
}
set
{
_mapMarkers = value;
}
}
private ObservableCollection<GMap.NET.WindowsPresentation.GMapMarker> _mapMarkers;
The problem is, the ViewModel's MapMarkers property always ends up with the default value "null." I tried setting breakpoints on the SetValue call in PanelMap_Control and the ViewModel property's setter. The debugger first hits SetValue(), at which point _map.Markers is valid (non-null). THEN it hits the ViewModel's setter, with a value of null - and never reflects the actual valid object I pass to SetValue().
I'm sure I'm just missing something simple, but I can't for the life of me figure out why SetValue(non-null) would be followed by ViewModel.MapMarkers.set(null)...and never again.
(Side note 1: I realize this DependencyProperty won't work for TwoWay binding - i.e. I can't update _map.Markers in the UserControl. That's fine, I only need to read from it externally.)
(Side note 2: The _map.Markers object will never be changed - only the items in the collection - so setting the DependencyProperty to the initial collection should be sufficient.)
Please refer to the following questions.
OneWayToSource Binding seems broken in .NET 4.0
OneWayToSource binding resetting target value
OneWayToSource does actually reset the target property (Markers) initially. This is by design. There is more information about this on the links above.
As a workaround you could try to set up the binding programmatically:
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
DataContext = vm;
vm.MapMarkers = control.Markers;
BindingOperations.SetBinding(control, PanelMap_Control.MarkersProperty, new Binding("MapMarkets") { TargetNullValue = control.Markers, FallbackValue = control.Markers });
}
XAML:
<local:PanelMap_Control x:Name="control" />

Two-way data binding of text box focus with DependencyProperty

In a WPF application built by the MVVM principle, I need to explicitly set the focus to a TextBox from code (reacting on a keyboard event), and also know whether focus has been lost. From another question here I have gathered that appearently the way to do this is with data binding on a DependencyProperty. For that I have picked up the following code:
public static class FocusHelper
{
public static bool GetIsFocused(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached(
"IsFocused", typeof(bool), typeof(FocusHelper),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
}
The TextBox binding looks like this:
<TextBox Text="{Binding Path=RowData.Row.Group}" helper:FocusHelper.IsFocused="{Binding RowData.Row.GroupFocused, Mode=TwoWay}"
This is in a (DevExpress) grid; Row is the actual ViewModel the entire row is bound to.
The corresponding code in the bound ViewModel:
private bool groupFocused;
public bool GroupFocused
{
get { return this.groupFocused; }
set
{
this.groupFocused = value;
this.NotifyOfPropertyChange(() => this.GroupFocused);
}
}
When the keyboard event is handled, GroupFocused is set to true, which sure enough invokes the DependencyProperty callback and sets the focus to the TextBox. When the control loses focus, however, that change is not reflected back on the ViewModel (the DependencyProperty's callback is also not invoked).
Is there any obvious reason for this behavior? Anything wrong with the code?
(I have tried adding UpdateSourceTrigger with both PropertyChanged and LostFocus to the binding, also changing the DependencyProperty's default value to true, none of which changed anything about the behavior. I also tried IsKeyboardFocused instead of IsFocused with no change, and IsKeyboardFocusWithin, which made the application close with no comment - likely a DevExpress exception - on assembling the grid.)
You are not data binding your GroupFocused property to the TextBox.IsFocused property, you are data binding it to the FocusHelper.IsFocused property, which programmatically calls Focus on the TextBox.
There's a distinct difference. Calling the Focus method on the TextBox will result in the TextBox.IsFocused property being set to true, but there is no connection between that property and either your GroupFocused or your FocusHelper.IsFocused properties, so neither will be set to false when the TextBox.IsFocused property is set to false.
Furthermore, you cannot data bind to the TextBox.IsFocused property in an attempt to set the focus because it is read only. See the UIElement.IsFocused Property page on MSDN for more information on that.
However, there is a simple solution. It might not look pretty, but it will certainly work... just set the GroupFocused property to false before you set it to true each time. Perhaps something like this:
private void FocusTextBox()
{
GroupFocused = false;
GroupFocused = true;
}
...
FocusTextBox();

Binding - callback is called twice

I've a simple control with dependency property like this
public class StatusProgress : Control
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress),
new FrameworkPropertyMetadata(null, (d, e) => (d as StatusProgress).TextUpdated(e.OldValue as string)));
private void TextUpdated(string text)
{
Trace.WriteLine("test");
}
}
then I have view model
public class ViewModelPageAnalyse : ViewModelPageBase
{
private string _progressText;
public string ProgressText
{
get { return _progressText; }
set
{
_progressText = value;
OnPropertyChanged(); // base class INotifyPropertyChanged implementation
}
}
}
Then there is a user control (displayed in window with ContentControl). User control is bound to view model with data template (maybe this is important)
<DataTemplate DataType="{x:Type local:ViewModelPageAnalyse}">
<local:UserControlAnalyse/>
</DataTemplate>
And this is the control in user control
<local:StatusProgress Text="{Binding ProgressText}"/>
Now the interesting part. When ProgressText in view model is set/changed, property changed callback is called twice. I see twice "test" in the debugger output window.
More interesting: when view is changed, for some reason callback is again called with e.NewValue = null, while there is nothing directly sets ProgressText to null.
I tried already to check if value is changed in the ProgressText setter before rising event, tried to set binding mode one-way, problem still - callback is called twice with same value, call stack looks same, but there are really a lot of calls within wpf to be really sure.
While double-shot is not a real issue, it bother me. Callback with null value is what my real problem (I think they are related). Anyone knows what is wrong?
Found a reason of the first problem: it was other control with Content. During transition it created a new Model (because Content is ViewModel) instead of reassigning existing user control. Totally my fault. Second problem still and I found this question (with workaround which is not suiting me).
Need help with
PropertyChanged callback is called with default value when ContentControl ViewModel is changed.
Which means null for Text in my case. Anyone? I couldn't figure out why is it called. My guess it is called by DataTemplate manager, if I can say so.
try to change this line:
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress),
new FrameworkPropertyMetadata(null, (d, e) => (d as StatusProgress).TextUpdated(e.OldValue as string)));
with this:
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress)
, new PropertyMetadata(""));

Default value on a custom WPF control

Here is my problem. I recently created a custom control, which works pretty well.
But i have a problem when i use it, i have a little problem :
In my control, i made a property named Value, defined like this :
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(1000));
public int Value
{
get
{
return (int)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
this.ValueText.Text = value.ToString();
}
}
When I do a databinding to this value, the binding works, but the default value is set to 1000, so it first print 1000. But actually, the property bound to Value isn't equal to 1000.
I would like to print in ValueText.Text the value of the bound property when the Value property is created.
Edit : Question is simple, how can I remove that default value and directly print the bound property ?
You should be able to setup a PropertyChanged event in your DependancyProperties metadata to update ValueText when Value changes.
somthing like this:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown),
new PropertyMetadata(1000, (sender, e) => (sender as NumericUpDown).ValueText.Text = e.NewValue.ToString()));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
The property setter will not get called as things change via WPF's data binding, so this technique will not work.
The default, initial value will always be 1000, but data binding may override it. You will need to add a Callback to appropriately notify you when the dependency property value is changed.
For details, see the Dependency Property Callbacks page to see how to implement a property changed callback correctly. This is the appropriate place to set your other (ValueText) property.

Categories