Data-Binding Property Value "Flickers" - c#

Problem:
When I change the value of "LuxVoltage" in the GUI via the Slider or the NumericUpDown the Value jumpes from "default value" (in this case 0) to the "actual value". Assuming I set the Value to 1000 and print out every set this is what the output looks like (it kind of "flickers"):
Output:
0
1000
0
1000
[repeat]
XAML: (Using MahApps.Metro "NumericUpDown")
<metro:NumericUpDown
Value="{Binding LuxVoltage, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Minimum="0"
Maximum="65535"
/>
<Slider
Value="{Binding LuxVoltage, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Minimum="0"
Maximum="65535"
/>
C#: (Using Prisms "BindableBase")
private ushort _luxVoltage = 0;
public ushort LuxVoltage
{
get { return _luxVoltage; }
set { SetProperty(ref _luxVoltage, value); }
}
Requirement:
I need to manipulate the same value from two controls. The "slider" to simply change the value fast, and the "NumericUpDown" tho provide precision

I don't know why you have a problem with flickering, but when I've had two GUI things chained from one value in the past I've always gone for the approach (using your case) of binding the NumericUpDown to the property in your viewmodel, and then binding the Slider to the NumericUpDown property. It might work for you.

The type of the Value of Slider is double. As you are binding to a ushort this causes multiple updates even though the slider does not move. Which then might cause additional changes fired from the other control, try adding this to the Slider binding so it when you drag the slider it will only increment it by the tick frequency which is defaulted to "1.0".
IsSnapToTickEnabled="True"

seems like that the actual problem lives somewhere else in my codebase.
- Binding from 2 Controls to one Property schould just work fine.
- Or consider binding one of the Controls to the value of the other, whose value is bound to the Property*

Try checking the incoming value on the setter for a meaningful change before you call SetProperty.
private ushort _luxVoltage = 0;
public ushort LuxVoltage
{
get { return _luxVoltage; }
set
{
if (_luxVoltage != value)
{
SetProperty(ref _luxVoltage, value);
}
}
}

Related

WPF PropertyChanged in UserControl DependencyProperty not fired every time

I've been researching for many hours the following situation: I have a xaml defined window that makes use of a usercontrol (ToggleButton) with some dependency properties.
The underlying viewmodel of the window contains some boolean objects that represent the state of devices (on/off) and others represent a request to toggle a device with a true/false flank (a PLC is connected to these and communication works fine).
Hence there are 2 DP's on the usercontrol:
The one to toggle the devices (binding mode OneWayToSource with an UpdateSourceTrigger.Explicit work fine (indicating to me that basics like shared DataContext is fine and not "disrupted" anywhere).
However the binding indicating the other DP (device state with binding mode OneWay) shows the following symptoms:
The (PLC-)device is off (false) before starting the program
Result: The DeviceState property is at the default value of false.
Set is called the first time when the device is switched on
(underlying viewmodel object changes to true, reports this via
PropertyChanged notification) and the DependencyPropertyChanged is
being called correctly. Further switches to off/on (false/true)
again don't result in "set" being called again (although
PropertyChanged on the underlying object is again called).
The device is on (true) before starting the program
Result: The DP Handler is triggered at
the start of the program and no change to
false or true lets it be called again.
What I've tried already for tracking this down is:
Implemented a DummyDebugConverter.
Result: I see that it's fired also only once. So giving me no further clue
Analyzed the Output Window and found the following message:
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=bLightState.Value; DataItem='ControlPanelModel' (HashCode=45596481); target element is 'AdsButton' (Name='btnLight'); target property is 'DeviceState' (type 'Boolean')
Debugging this didn't give me a clue. My breakpoints e.g. in the debugging converter or the set method never showed me a null-value anywhere. All values in the viewmodel constructor are initialized with default values. But I see the message always just one single time and I assume it relates to the problem somehow.
Used the same binding expression for testing purposes on some other elements (a label and a toggle button) besides my usercontrol. They work nicely and are updating their values as expected as soon as the object in the viewmodel changes (desired behaviour). The message in 2 disappers if I remove my usercontrol.
So I come to the conclusion that the error is in my definition of the DP's.
Here are the relevant code snippets:
AdsButton.xaml.cs
[Description("When set to true the device is shown as on"), Category("Default")]
public bool DeviceState
{
get { return (bool)GetValue(DeviceStateProperty); }
set { SetValue(DeviceStateProperty, value); }
}
public static readonly DependencyProperty DeviceStateProperty =
DependencyProperty.Register(
"DeviceState", typeof(bool),
typeof(AdsButton),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.None,
DeviceStateChanged,
CoerceDeviceStateProperty,
true,
UpdateSourceTrigger.Explicit));
private static void DeviceStateChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
(d as AdsButton).DeviceState = (bool) e.NewValue;
}
private static object CoerceDeviceStateProperty(DependencyObject d, object value)
{
return value ?? false;
}
ControlPanel.xaml
<src:AdsButton x:Name="btnLight"
Value="{Binding Path=bLight.Value, Mode=OneWayToSource}"
DeviceState="{Binding Path=bLightState.Value, Mode=OneWay}" />
<Label Content="{Binding bLightState.Value, Mode=OneWay}" />
<ToggleButton Content="Button" IsChecked="{Binding bLightState.Value, Mode=OneWay}" />
So does anybody know: Why is my own DP reacting differently from the ones in standard controls?
Thanks to the initial comment by Roger... the answer is obvious:
Setting the DP itself in the setter method overwrites the binding with a fixed value (which effectively removes it), but only if the new value is different from the old one.

Binding textbox to Decimal "fallback value" on input error

I was testing the nuances of Data Binding in WPF today, and I came across a strange problem.
In my View, I have
<TextBox Text="{Binding MyInt, UpdateSourceTrigger=LostFocus, StringFormat='{}{##.##}'}"/>
<Label Content="{Binding MyStr2}"/>
In my ViewModel, I have
private decimal myInt;
public decimal MyInt
{
get { return myInt; }
set
{
if (value == myInt) { return; }
myInt = value;
OnPropertyChange();
OnPropertyChange("MyStr2");
}
}
public string MyStr2
{
get
{
return myInt.ToString("N2", CultureInfo.CreateSpecificCulture("en-IN"));
}
}
I want to get the data from the textbox, and format it properly and display it in a label. Simple and easy.
Now, I can only enter decimal data in the textbox, or else the border turns red, indicating an input error.
All is fine when I input decimal data:
But, when I enter garbage data, this happens:
The red border shows there is data input error. But, the label still reflects the older data. I want the label to fall back to 0.00 in case of input error. Is there any way to do that?
I have tried to find a contrived way to do it, and it works, sort of, in an extremely hacky kludgy way.
private string myInt;
private decimal myIntActual;
public string MyInt
{
get
{
return myInt;
}
set
{
if (value == myInt) { return; }
Decimal.TryParse(value.ToString(), out myIntActual);
myInt = myIntActual.ToString();
Decimal.TryParse(myInt, out myIntActual);
OnPropertyChange();
OnPropertyChange("MyStr2");
}
}
public string MyStr2
{
get
{
return myIntActual.ToString("N2", CultureInfo.CreateSpecificCulture("en-IN"));
}
}
What this does is that, it takes the input as a string, tries to parse it to decimal, if it cant, it will return zero. But I have sacrificed the input validation with this code, not to mention that the code looks ugly.
I understand that WPF has a fallback value mechanism when binding fails. Is there any fallback value mechanism in case of input error?
EDIT:
One more thing. After entering the garbage data, the next time I enter valid data, the value somehow reaches the viewmodel, but the textbox becomes blank. And it is a reproducible problem.
Screenshot A:
This is working as is expected.
The garbage data is then entered. Do note that, as the UpdateSourceTrigger is LostFocus, the error does not come up until the focus is lost.
I then enter valid data, and...
on losing focus, the textbox blanks out. Do note, that the label is properly updated. I set up breakpoints to see if the binded property is updated or not, and it is, yet the textbox becomes blank. Why is that?
Here's the trigger idea. I tested this and it's working for me. It works with no changes to your viewmodel or code behind.
<TextBox
x:Name="MyIntTextBox"
Text="{Binding MyInt, UpdateSourceTrigger=LostFocus, StringFormat='{}{##.##}'}"
/>
<Label Content="{Binding MyStr2}">
<Label.Style>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Style.Triggers>
<!--
parens on (Validation.HasError) are important: It's an attached
so its name has a dot in it. The parens tell the binding that.
-->
<DataTrigger
Binding="{Binding (Validation.HasError), ElementName=MyIntTextBox}"
Value="True">
<Setter
Property="Visibility"
Value="Hidden"
/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
As for the blank-out behavior, I see that when I type an invalid string into the MyInt box, tab out, tab back in, type a valid value, and tab back out. It only happens when I tab out on a valid value, when the previous value that I tabbed out on was invalid. I think that's exactly the (mis)behavior you're describing.
I don't have an explanation for that. I don't like to run around yelling "bug" on a framework, but I think that's a bug. The control isn't showing the viewmodel property value it's bound to, and the value hasn't changed. I put a breakpoint in the MyInt setter and it only hits the breakpoint once, with the new valid value (aside from the fact that as far as I know there's no integer value that's displayed as an empty string).
I would ask about that as a separate question if google doesn't turn up any workarounds. I tried "wpf textbox invalid tab out empty" and got nothing.

Get a DependencyProperties source (binding, const, ...) and replace with wrapper

I am trying to set an entire set of controls within a panel to read-only (e.g. if a user has no permission to edit) through data-binding and an attached property.
(I am aware of the fact that setting a panel to disabled also disables its children, but this is too much, since it would also disable hyperlinks, lists, etc.)
Basically, the property changed event handler iterates the visual tree and finds all TextBox children and then sets their IsReadOnly property to either true or false.
This works, but does not cover the case where the TextBox already has a IsReadOnly setting - either const or binding. For example if a TextBox should always be read-only, then the attached property should not change it to true. Also if the TextBox has a binding that restricts the TextBox to read-only in some cases, the attached property should not blindly set true or false, but rather combine the settings, i.e. if attached property AND textbox binding indicate no read-only, then it is editible, otherwise it is readonly.
How can this be done? This would require to somehow get the current IsReadOnly setting (binding, markup, constant value, ...) and replace it with a wrapper which does the AND-combination.
How do I get the current setting/value source for a dependency property? I looked at the following, but don't see how it would address my problem:
TextBox1.GetValue(TextBoxBase.IsReadOnlyProperty);
DependencyPropertyHelper.GetValueSource(TextBox1, TextBoxBase.IsReadOnlyProperty);
TextBox1.GetBindingExpression(TextBoxBase.IsReadOnlyProperty);
Any help would be appreciated.
J.-
EDIT: I am looking for something like
(pseudo-code)
TextBox1.IsReadOnly := OR(TextBox1.IsReadOnly, GlobalIsReadOnly)
which now sets the TextBox1.IsReadOnly to true if the GlobalIsReadOnly flag is set or if the TextBox1.IsReadOnly value indicates read-only, be it a binding, markup or const.
You could use a DependencyPropertyDescriptor on to hook your IsReadonly property changed handler (for all objects).
(beware: a handler added to DependencyPropertyDescriptor is a gcroot... keep that in mind to avoid memory leaks)
This hook would try to get your custom attached property, and if it's found and is set to 'readonly forced', re-set your IsReadOnly property to false if it's value is changed (but store a flag, maybe on another attached property, to know if it must be restored to read-only later).
However, your logic would override any binding on IsReadonly. But the same logic could be applied with binding expressions (and not only values of the property) using GetBindingExpression and storing/restoring binding expressions set on IsReadonly property.
pros: no further code required once this is implemented.
cons: DependencyPropertyDescriptor.AddValueChanged "hides" logic... since there will be no clue that this IsReadonly property will be bound to something in further xaml you will write.
* EDIT : other solution *
Using multibinding, this should work (not tested).
However, this has some requirements:
Bindings/values must1 no be modified
Bindings must be intialized before executing this
var readonlyGlobalBinding = new Binding
{
Source = myRoot, // to fill
Path = new PropertyPath(IsGlobalReadOnlyProperty)
};
var be = box.GetBindingExpression(TextBoxBase.IsReadOnlyProperty);
if (be != null)
{
var mb = new MultiBinding();
mb.Bindings.Add(be.ParentBinding);
mb.Bindings.Add(readonlyGlobalBinding);
mb.Converter = new OrConverter();
box.SetBinding(TextBoxBase.IsReadOnlyProperty, mb);
}else if(!box.IsReadOnly)
box.SetBinding(TextBoxBase.IsReadOnlyProperty, readonlyGlobalBinding);
using class
class OrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.OfType<bool>().Aggregate(false, (a, b) => a || b);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
It's not exactly what you're after, but I'd approach this problem from a different angle. I'd basically add a bool IsReadOnly property to my view model, Bind it to the relevant IsReadOnly properties on the UI controls and then simply set it to true of false dependant upon some UI interaction:
public bool IsReadOnly { get; set; } // Implement INotifyPropertyChanged here
...
<TextBox Grid.Row="0" IsReadOnly="{Binding IsReadOnly}" ... />
...
<TextBox Grid.Row="3" IsReadOnly="{Binding IsReadOnly}" ... />
<TextBox Grid.Row="4" ... /> <!--Never readonly-->
...
IsReadOnly = true;
What I have used in similar scenarios is SetCurrentValue and InvalidateProperty:
This method is used by a component that programmatically sets the
value of one of its own properties without disabling an application's
declared use of the property. The SetCurrentValue method changes the
effective value of the property, but existing triggers, data bindings,
and styles will continue to work.
So, you can set IsReadOnly to true using SetCurrentValue, then later call InvalidateProperty to reset it to the "declared use" (which may be true or false).
However I'm not sure this is what you want. It sounds like you want the attached property to have precedence the entire time it's active. Using this method, if a binding updates sometime between SetCurrent and Invalidate, it would still be applied overriding the attached property.
Another half-solution is to add triggers. If this trigger was declared at the end of the TextBox style's Triggers collection, it would take precedence:
<DataTrigger Binding="{Binding GlobalIsReadonly}" Value="True">
<Setter Property="IsReadOnly" Value="True" />
</DataTrigger>
The problem is that it only takes precedence over other setters (styles or triggers), but not direct setting of the property. Also styles cannot be modified after they are assigned, so to add this programmatically you would need to copy the old style and reassign it.
Unfortunately I think the only complete solution is to listen to property changes on all the textboxes and force another change.

How to validate a UI in Silverlight?

My UI is simple. In a Silverlight 5.0 application, I'm using MVVM and I let the user adds many textboxes as he wants to add in a ObservableCollection<Model> and a Button.
Model just have one property and its datatype is an integer.
The data template for this model is just a simply textbox.
<TextBox Text="{Binding Number}" />
So the idea is, when all the textboxes does not have any error, the command is enabled, but if any model has an error, the command should be disabled.
How can I implement this validation?
Thanks in advance.
You can simply throw an exception in appropriate property`s setter:
public int Number
{
get {//...}
set {
if(value >= 10)
throw new Exception("Number should be less than 10");
_number = number;
}
}
And your binding should be:
<TextBox Text="{Binding Number, Mode="TwoWay" ValidateOnExceptions="True"}" />
FrameworkElement has BindingValidationErrorEvent, which can be used for implement enable/disable command logic. Remember to set NotifyOnValidationError to True for your binding.
p.s.Also, i suggest you read about INotifyDataErrorInfo

Databinding with WPF

I want to bind a button's width to some textbox's text value, although I want to always have a button width's that's twice what is written on the textbox. This is:
textBox1.Text = 10
will set
button1.Width = 20
Can I only do this through ValueConverters or is there other way to do it?
Thanks
Using IValueConverter is the easy solution but if you do not wish to do so, then you can try binding textbox1 and button1 with a single variable. For example, let say you have created two controls as seen in below and have binded into a single variable called ButtonText. For simplicity, Content of the button will be modified instead of Width of the button.
In xaml:
<TextBox Text="{Binding ButtonText, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="{Binding ButtonText, Mode=OneWay}"/>
In ViewModel:
public string ButtonText
{
get { return _buttonText; }
set
{
int result;
if (int.TryParse(value, out result))
_buttonText = (result * 2).ToString();
else
_buttonText = value;
OnPropertyChanged("ButtonText");
}
}
private string _buttonText;
Unfortunately, this solution does not work in .NET 4.0 because the way .NET 4.0 handles OneWayToSource, as stated in this article. Basically, the issue is that the Textbox will be updated with the value from ButtonText after it is set by the Textbox although its Mode was configured as "OneWayToSource". This solution will work for .NET 3.5.
To get around this OneWayToSource issue in .NET 4.0, you can use BlockingConverter (type of IValueConverter) to separate each time that the resource is used and set x:Shared="False", as stated in this article. Then again, you are using the IValueConverter but at least you are not using it to modify the value.
Bindings that are not simple assignments, that is what value converters are for.
(No other way to do it.)

Categories

Resources