Resettable RangeBase (Slider) in WPF - c#

I've implemented a custom Slider that derives from RangeBase that has an added ResetValue property that can be used to reset the Value to the initial value. Unfortunately this requires double binding the value source and also use the OneTime binding mode for ResetValue.
<ButtonSlider
Minimum="0" Maximum="500" TickFrequency="1"
Value="{Binding Path=MaxVoltageUv}"
ResetValue="{Binding Path=MaxVoltageUv, Mode=OneTime}" />
Ideally it would have a boolean dependency property CanReset that would capture the ResetValue on first bind if it were enabled. I've tried implementing it by capturing the first time Value is set, but for some reason this only works some of the time.
It first overrides the metadata on the ValueProperty:
ValueProperty.OverrideMetadata(typeof(ButtonSlider), new FrameworkPropertyMetadata(OnValueChanged, OnCoerceValue));
And then implements custom coerce and value changed handlers.
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var buttonSlider = ((ButtonSlider)d);
if (buttonSlider != null)
{
if (!buttonSlider.valueInitialized && e.NewValue is double)
{
buttonSlider.resetValue = (double)e.NewValue;
buttonSlider.valueInitialized = true;
}
buttonSlider.CheckResetEnabled();
}
}
private static object OnCoerceValue(DependencyObject d, object baseValue)
{
var buttonSlider = ((ButtonSlider)d);
if (buttonSlider != null)
{
if(baseValue is double)
return Math.Max(Math.Min((double)baseValue, buttonSlider.Maximum), buttonSlider.Minimum);
}
return DependencyProperty.UnsetValue;
}
For some reason the OnValueChanged is called with a Value of 0 while the initial bind should have been 2. This seems to be related to data templating. Controls that are not on a data template work correctly.
Is there any other way to capture an initial value of a dependency property?

My thoughts
Verify that MaxVoltageUv which you bind to is a double.
Remove the dependency property ResetValue it is just creating noise in trying to resolve this.
0 could be the default value of the dependency property which gets set before the actual binding. Why not put in a -1.0 as the default along with the logic in the OnValueChanged to detect that scenario and ignore until a proper value comes through.

If both the data context and the binding are set in XAML, you can use the Loaded event of your custom control to store the current value as the reset value. This will also work if your control is in a data template for instance.
If the data context is set from code, this will only work if it is set before the control has been added to the logical tree.

Related

How to avoid flickering when using an asynchronous binding

I've created an example to illustrate my problem.
ViewModel:
public class VM : INotifyPropertyChanged
{
private double _value = 1;
public double Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged();
}
}
public VM()
{
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromTicks(1);
timer.Tick += (s, e) => { Value += 1; };
timer.Start();
}
// OnPropertyChanged stuff ...
}
}
View:
<Window.DataContext>
<namespace:VM/>
</Window.DataContext>
<Grid>
<TextBox Text="{Binding Value, IsAsync=True, FallbackValue=Test}"/>
</Grid>
When running my application the text in the textbox flickers. During the update process the FallbackValue is displayed, which makes no sense to me.
Does anyone knows the purposes or what are the benefits that during the update process the FallbackValue is displayed? Is there a way to display the old Value during an async update process?
This seems normal to me, given that you are using IsAsync=True in your binding. From the documentation:
While waiting for the value to arrive, the binding reports the FallbackValue, if one is available
When the PropertyChanged event is raised, WPF initiates the process of updating the target of the binding. Normally this would happen synchronously, with the property getter called immediately to update the value.
But you are using IsAysnc=True, so instead WPF fills in the target with the fallback value, and starts an asynchronous request to retrieve the actual property value later. Until that request has completed, the fallback value is displayed.
Does anyone knows the purposes or what are the benefits that during the update process the FallbackValue is displayed?
Per the documentation, the intent behind the IsAsync=True setting is that it's used when the property getter is, or could be, slow. Your code has told WPF that the property value has changed, so it knows the old value is no longer valid. Your code has also told (via the IsAsync in the XAML) that the property getter could take some time to provide the new value, so it defers retrieving that value until later.
In the meantime, what should WPF display? That's what the fallback value is there for.
Is there a way to display the old Value during an async update process?
If you don't want the behavior that is designed for this feature in WPF, you should just retrieve the new data asynchronously yourself, and update the property via the setter when you have it. It's not a good idea for a property getter to be slow anyway, so this would be a better design in any case.
I had the same problem but with an image source. I've removed the IsAsync on the binding and I have made my getter async:
// This field hold a copy of the thumbnail.
BitmapImage thumbnail;
// Boolean to avoid loading the image multiple times.
bool loadThumbnailInProgress;
// I'm using object as the type of the property in order to be able to return
// UnsetValue.
public object Thumbnail
{
get {
if (thumbnail != null) return thumbnail;
if (!loadThumbnailInProgress) {
// Using BeginInvoke() allow to load the image asynchronously.
dispatcher.BeginInvoke(new Action(() => {
thumbnail = LoadThumbnail();
RaisePropertyChanged(nameof(Thumbnail));
}));
loadThumbnailInProgress = true;
}
// Returning UnsetValue tells WPF to use the fallback value.
return DependencyProperty.UnsetValue;
}
}
Sometimes a binding will fail, failure is important to consider. Fallback value option presents users a message if an error occurs, rather than nothing happening. If you would like your fallbackvalue to display the previous value contained, I could think of a few ways of trying : possibly saving the value in a reference string and/or to another control, then binding to that control
But if you don't want the fallbackvalue displayed at all, you need to do a code inspection to see how your binding is failing/or is slow, and contain it in your code behind
I've found an approach to avoid flickering by just inheriting from textbox and overriding it's textproperty-metadata.
Custom TextBoxControl
public class CustomTextBox : TextBox
{
static CustomTextBox()
{
TextProperty.OverrideMetadata(typeof(CustomTextBox), new FrameworkPropertyMetadata(null, null, CoerceChanged));
}
private static object CoerceChanged(DependencyObject d, object basevalue)
{
var tb = d as TextBox;
if (basevalue == null)
{
return tb.Text;
}
return basevalue;
}
}
View
<Window.DataContext>
<namespace:VM/>
</Window.DataContext>
<Grid>
<namespace:CustomTextBox Text="{Binding Value, IsAsync=True}"/>
</Grid>
It's important to have the text-binding without a fallbackvalue. So during update process the text is set to the textproperty defalut value - so in this case to null.
The CoerceChanged handler checks whether the new value is null. If it's so he returns the old value so that during update process there is still the old value displayed.

Array value binding in WPF

I have an object with an Array of doubles property like this:
private double[] _axes;
public double[] Axes
{
get
{
return _axes;
}
set
{
_axes = value;
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes[]");
}
}
}
This Array is always assigned as a whole, because i get an Array of double out of my database object and set it to the Axes property like this:
object.Axes = GetDataRow(i);
(this is just to demonstrate that i in fact assign the whole Array)
In my XAML now i bind it like this:
<Label Content="{Binding Path=Axes[0], UpdateSourceTrigger=PropertyChanged}"
I set the DataContext of the window to the Object that has the Axes property and the UI receives one value from it at program start that i assign in the constructor of the object.
However, even though the "Axes[]" event is raised constantly (which i checked from another object), no further values arrive in the UI.
So my question is: CAN this even work? Does Axes[] not correspond to Axes[0]? what could be the problem?
Your property name is incorrect in the notification
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes");
}
Remove the brackets. This should solve your problem in this case. However, it is recommended to use ObservableCollection for binding a list of objects.
The binding system won't look "into" the array for updates. You just need to notify it that the property itself changed. You're currently saying that a property called Axes[] changed, but you don't have a property with that name. It's just Axes
public double[] Axes
{
get
{
return _axes;
}
set
{
_axes = value;
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes");
}
}
}
If you really need to know if an individual item in the array is changing, then you should be using ObservableCollection, which the binding system does know how to look "into".
No.
You have no property named Axes[], so that event has no effect.
You should not bind to arrays; instead, use ObservableCollection, and make the property read-only.

Can a PropertyChangedCallback be persuaded to run when the bound property's value is unchanged?

My application uses the MVVM architecture, with the ViewModel having no knowledge of the View. When a ViewModel object requires a new View be shown it exposes a public ShowNewView property that is an object whose class is based on my ViewModel base class. The WPF View binds a custom DependencyProperty to this and uses the PropertyChangedCallback to construct and show an approperiate Window.
This all works well the first time the ShowNewView property is set. However, if the user closes the window and then attempts to re-open it, the ShowNewView property's value has not changed when the PropertyChanged event is raised and the PropertyChangedCallback is not invoked.
In order to 'trick' the DependencyProperty into detecting that the value has changed (even though the value stored in the ViewModel's property may not have actually changed), I have used the SetCurrentValue method exposed by the Window class to force the DependencyProperty's value to null.
#region ShowNewViewProperty
private static readonly DependencyProperty _ShowNewViewProperty =
DependencyProperty.RegisterAttached
(
"ShowNewView",
typeof(IRootViewModel),
typeof(WpfViewWindow),
new PropertyMetadata(ShowNewViewPropertyChanged)
);
public static DependencyProperty ShowNewViewProperty { get { return _ShowNewViewProperty; } }
public static IRootViewModel GetShowNewView(Window source)
{
return (IRootViewModel)source.GetValue(ShowNewViewProperty);
}
public static void SetShowNewView(Window target, IRootViewModel value)
{
target.SetValue(ShowNewViewProperty, value);
}
private static void ShowNewViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WpfViewWindow window = d as WpfViewWindow;
IRootViewModel newValue = e.NewValue as IRootViewModel;
if ((null != window) && (null != newValue))
{
// Create a child WpfViewWindow. This method is part of my
// framework that uses ResourceDictionary entries, imported by MEF
// to locate the View class corresponding to the ViewModel parameter's
// class.
WpfViewWindow modelessWindow = window.CreateWpfViewWindow(newValue);
if (null != modelessWindow)
{
// Show the new WpfViewWindow.
modelessWindow.Show();
}
// Clear the current value so that the next PropertyChanged event
// is processed even if the underlying value has not actually changed.
window.SetCurrentValue(ShowNewViewProperty, null);
}
}
#endregion
Technically this works, as it results in the callback being run when the PropertyChanged event fires, regardless of whether the value has actually changed or not. However, it results in the callback being called (recursively) twice every time the ViewModel's property is updated: once in response to the ViewModel's event and once in response to the SetCurrentValue method being called.
There are a number of questions here relating to the PropertyChangedCallback not being called, or not being called more than once, in other situations.
PropertyChangedCallback on DependencyProperty Only Firing Once covers the situation where the property is a collection and the collection content changes, but the collection itself does not. However, my property is not a collection and everything is working exactly as documented.
WPF dependency property setter not firing when PropertyChanged is fired, but source value is not changed looks very promising, but the answer only suggests using the callback that I am already.
Is there a more elegant way to achieve this that does not result in the callback being run twice for each PropertyChanged event from the ViewModel? I.e. is there some way to get around the framework's check to verify that the old and new values are different?
Clarification
The View being created isn't necessarily always a WPF Window, for example, in my unit tests it is a mock, and later in the project it may be a sperate logging assembly. Nor are all of the ViewModel objects from the same assembly, it is known that additional functionality will be required in the future, but the specifics are currently undefined. The application allows the user to connect a device by way of a simple network. Initially the network is ModbusRTU over RS-485, however, the end customer may want to use CANOpen or Profinet or some other transport layer, and I have to provide a plug-in mechanism that allows the new functionality to be added without changing the existing code.
To be fair, there are several alternative mechanisms that I could use to achieve the same result (i.e. having the ViewModel request a new View be created), but I'm interested in knowing if there is a way to make a DependencyPropety 'forget' what it's previous value was.
The usual solution to this type of problem is for you to extract the code from your ShowNewViewPropertyChanged method and to put it into a different method:
private void SomeNewMethod(IRootViewModel newValue)
{
// Create a child WpfViewWindow. This method is part of my
// framework that uses ResourceDictionary entries, imported by MEF
// to locate the View class corresponding to the ViewModel parameter's
// class.
WpfViewWindow modelessWindow = CreateWpfViewWindow(newValue);
if (null != modelessWindow)
{
// Show the new WpfViewWindow.
modelessWindow.Show();
}
// Clear the current value so that the next PropertyChanged event
// is processed even if the underlying value has not actually changed.
SetCurrentValue(ShowNewViewProperty, null);
}
Now you can simply call that method from both the ShowNewViewPropertyChanged handler and from wherever else you desire:
private static void ShowNewViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WpfViewWindow window = d as WpfViewWindow;
IRootViewModel newValue = e.NewValue as IRootViewModel;
if ((null != window) && (null != newValue))
{
window.SomeNewMethod(newValue);
}
}

Is data binding faster than modifying the properties of subcontrols manually?

So i have several subcontrols that need to take the value of the parent controls dependency property. Is binding the value to the dependency property of the parent going to be faster than just creating a callback method to occur when the parent's dependency property changes?
I was about to code it such that:
ItemsControl ic = this.signal_viewer_item_control;
int count = VisualTreeHelper.GetChildrenCount(ic);
foreach (var item in ic.Items)
{
ContentPresenter container = ic.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
if (container != null)
{
SignalGraph sg = container.ContentTemplate.FindName("signal_graph", container) as SignalGraph;
if (sg != null)
{
sg.GraphPenWidth = GraphPenWidth;
sg.DrawSignals();
}
}
}
so that I just manually modify the subcontrols graphpenwidth. Is it better to make that a dependency property and bind it to the parent's graphpenwidth value? i just thought that creating two dependency properties would be unnecessary overhead, but i'm wondering if there are benefits to having it in this situation
Edit: so i went back and tried to use dependency properties to compare the two, but then I can't seem to get it to work.
<wpfExp:SignalGraph
x:Name="signal_graph"
Height="75"
Signal="{Binding}"
signal_graph_window_width="{Binding ElementName=signal_box, Path=signal_graph_window_width, Mode=OneWay}"
X_Scale="{Binding ElementName=signal_box, Path=X_Scale, Mode=OneWay}"
MaxTimeValue="{Binding Source = {StaticResource ResourceKey=signal_data}, Path = MaxTimeValue, Mode=OneWay}">
<wpfExp:SignalGraph.GraphPenWidth>
<Binding ElementName="signal_box" Path="GraphPenWidth" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnTargetUpdated="True">
</Binding>
</wpfExp:SignalGraph.GraphPenWidth>
</wpfExp:SignalGraph>
I put a two way binding on graph penwidth between the two properties and then registered the new properties like so:
private static readonly DependencyProperty GraphPenWidthProperty =
DependencyProperty.Register("GraphPenWidth",
typeof(int), typeof(SignalGraph),
new FrameworkPropertyMetadata(new int(), new PropertyChangedCallback(GraphPenWidthChanged)));
public int GraphPenWidth
{
get
{
return (int)GetValue(GraphPenWidthProperty);
}
set
{
SetValue(GraphPenWidthProperty, value);
default_pen = new Pen(Brushes.Green, value);
}
}
private static void GraphPenWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SignalGraph sg = d as SignalGraph;
sg.DrawSignals();
}
but on modification the graphpenwidthchanged callback method is never being called. I'm wondering does it have something to do with it being in an itemtemplate? any clue what could cause it not to update?
If you dont have tons of bindings to different dependency properties, difference in performance between binding and manual setter is not noticeable. Anyway, if you are going to use manual setter, searching element in template every time your property is being changed - is not a good idea. What about returning a custom control in the GetContainerForItemOverride and storing its template child (SignalGraph in your case) as a property (you can get it in the OnApplyTemplate using the GetTemplateChild method)?

How to force validation errors update on View from ViewModel using IDataErrorInfo?

I have a MVVM-based Window with many controls, and my Model implements IDataErrorInfo.
There is also a SaveCommand button, which performs validation by analysing Model.Error property.
The view displays the default red border around controls with errors only when I change the value of a particular control, or when I notify about the change of that property using PropertyChanged.
How can I force View to display all Validation errors even when I didn't touch the controls?
All my validation bindings include ValidatesOnDataErrors=True, NotifyOnValidationError=True.
I know one solution is to have an aggregate box with all the errors, but I would prefer to display errors on per-control basis.
I don't want to trigger Model.NotifyPropertyChanged for each bound property from ViewModel.
I use WPF 4.0, not Silverlight, so INotifyDataErrorInfo won't work.
You mention that you don't want to raise property changed for the properties you bind to, but that's really the simplest way to accomplish this. Calling PropertyChanged with no parameter will raise for all properties in your viewmodel.
Alternatively you can update the bindings (and force revalidation) on any control like this:
myControl.GetBindingExpression(ControlType.ControlProperty).UpdateSource();
The best solution I've found so far that works is to change DataContext to null and back to the instance of ViewModel.
This triggers the update for controls on the view that has DataContext bound to InnerViewModel:
public void ForceUpdateErrors() {
var tmpInnerVM = _mainViewModel.InnerViewModel;
_mainViewModel.InnerViewModel = null;
_mainViewModel.InnerViewModel = tmpInnerVM;
}
It's recommended to check if no data is lost after this trick. I had a case that this code triggered source update for ComboBox.SelectedItem with null but I managed to solve it. It was caused by using a resource-based BindingProxy and the order of DataContext=null propagation across control hierarchy.
This 'Hack' worked for me temporarily, to force the InotifyChanged event, just assign that control back it's own content. Do this before evaluating the HasError function of bindings. For example a textbox would be:
((TextBox)child).Text = ((TextBox)child).Text;
And then a complete example(before I hear this is not true MVVM, I directly got a handle on the grid for ease of showing this code snipet)
public bool Validate()
{
bool hasErr = false;
for (int i = 0; i != VisualTreeHelper.GetChildrenCount(grd); ++i)
{
DependencyObject child = VisualTreeHelper.GetChild(grd, i);
if (child is TextBox)
{
bool pp = BindingOperations.IsDataBound(child, TextBox.TextProperty);
if (pp)
{
((TextBox)child).Text = ((TextBox)child).Text;
hasErr = BindingOperations.GetBindingExpression(child, TextBox.TextProperty).HasError;
System.Collections.ObjectModel.ReadOnlyCollection<ValidationError> errors = BindingOperations.GetBindingExpression(child, TextBox.TextProperty).ValidationErrors;
if (hasErr)
{
main.BottomText.Foreground = Brushes.Red;
main.BottomText.Text = BindingOperations.GetBinding(child, TextBox.TextProperty).Path.Path.Replace('.', ' ') + ": " + errors[0].ErrorContent.ToString();
return false;
}
}
}
if (child is DatePicker)
{
...
}
}
return true;
}

Categories

Resources