WPF button binding to a property Convertback IMultiValueConverter does not update value - c#

I have a binding from a toggle button hooked up in code behind.
I want to bind the isChecked state from one button to 4 video controls to toggle the mute function. I am using multibinding to bind the toggle button to 4 different controls. My problem is using breakpoints i can see everything is triggered right up to the property mute property on each object, but the property "value" parameter never gets updated. It stays at the default setting when the controls are instantiated.
So first i create the bindings in code behind
IMultiValueConverter converter = new EmptyMultiValueConverter();
MultiBinding myMultiBinding = new MultiBinding();
myMultiBinding.Converter = converter;
myMultiBinding.UpdateSourceTrigger = UpdateSourceTrigger.Default;
myMultiBinding.Mode = BindingMode.OneWayToSource;
myMultiBinding.NotifyOnSourceUpdated = true;
for (int i = 1; i < _maxNumberofPlayers; i++)
{
VideoPlayer player = new VideoPlayer()
{
Mute = false
};
myMultiBinding.Bindings.Add(new Binding("Mute") { Source = player
});
}
btnMuteToggle.SetBinding(SimpleButton.IsCheckedProperty, myMultiBinding);
This all seems to work because when i click on the button i can see in the multivalue converter the correct isChecked button status arrives at the breakpoints, at ConvertBack below i can confirm the value is the correct bool that reflects the toggle button state.
public class EmptyMultiValueConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object
parameter, System.Globalization.CultureInfo culture)
{
// gets from the object source
return (bool)values[0];
}
public object[] ConvertBack(object value, Type[] targetTypes, object
parameter, System.Globalization.CultureInfo culture)
{
return new Object[] {value,value,value,value};
}
#endregion
}
At this point i can confirm it hits the Mute property, and triggers the SET 4 times, but as i trace through it the value parameter remains at the previously set value, and does not update to reflect the value passed to it via ConvertBack
// mute property in the media player user control
public bool Mute
{
get { return _media.IsMuted; }
set
{
if (_media.IsMuted == value)
return;
else
{
_media.IsMuted = value;
NotifyPropertyChanged("Mute");
}
}
}
Can anyone help please.
Have been tearing my hair out for 3 days.
It seemed to me using multibinding is an efficient way to hook up 4 seperat controls players and bind them to one button click.

well I have tried several options again and none of them work except code in a click button event
Multibinding just does not work at all.
I can see in break points that the Mute property is being called 4 times for 4 controls but the value parameter is never updated to the new property value.
I also tried binding the one button to 4 different controls
foreach(Player player in lsPlayers)
{
btnMuteToggle.SetBinding(SimpleButton.IsCheckedProperty, new Binding("Mute")
{
Source = player,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
Mode = BindingMode.OneWayToSource
});
}
This just results in one player object mute property being called.
You cant bind more than one control to the same button.
It just does not work.
so I just called the 4 objects in a click event.

I believe that in this case you may want to bind those 4 properties to the mute property individually and make sure each is set Mode to TwoWay. But if MultiValueConverter supports it, I can't particularly argue with your approach.
It appears that you have not made a Dependency Property for Mute. this is vital to the WPF Binding Workflow and tends to scale easier than NotifyPropertyChanged as NotifyPropertyChanged can tend towards requiring you to manage more. A DependencyProperty will abstract away both sending and receiving the updates to your property and the Xaml editor support for it is better.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/how-to-implement-a-dependency-property
I find WPF Binding to be nice in theory, but still requires a lot more leg work than it needs to. Have a function. I add it to all of my WPF viewmodels and controls.
/// <summary>
/// simplify wpf binding
/// </summary>
/// <param name="name"></param>
/// <param name="type"></param>
/// <param name="preferNull">Fair Warning. if you create an instance, you will have also created a singleton of this property for all instances of this class. </param>
/// <returns></returns>
private static DependencyProperty AddDp(string name, Type type, bool preferNull = true)
{
return DependencyProperty.Register(name, type, typeof(Setting),
new PropertyMetadata((!preferNull || type.IsValueType) ? Activator.CreateInstance(type) : null));
}
Just inherit from DependencyObject and you can make mute look more like this:
public static readonly DependencyProperty MuteDp = AddDp(nameof(Mute), typeof(bool));
public bool Mute
{
get => (bool)GetValue(MuteDp);
set { if(value != Mute) SetValue(MuteDp, value); }
}
But Be Warned!
Sometimes WPF Binding to a Dependency Property updates the Dependency Property's internal values without entering the setter of its corresponding property! This was the case on a MultiValueBinding I used.
meaning it may never touch the Mute::Set accessor. This can be pretty problematic, to counter this issue you can use the various callbacks available to a DependencyObject!
private static DependencyProperty AddDp(string name, Type type, bool preferNull = true)
{
var dp = DependencyProperty.Register(name, type, typeof(Setting),
new PropertyMetadata(
(!preferNull || type.IsValueType) ? Activator.CreateInstance(type) : null
,new PropertyChangedCallback((dobj, dpe)=>
{
//after property changed.
//forcing usage of set accessor
((MuteContainerObj)dobj).Value = ((strong text)dobj).Value;
//or forcibly use an update function
((MuteContainerObj)dobj).MuteUpdated();
}),new CoerceValueCallback((dobj, o)=> {
//before property changed
return o;
})), new ValidateValueCallback((o)=> {
//before property change events
return true;
}));
return dp;
}

Related

WPF Dependency Property Based on another dependency property [duplicate]

The code below is my current solution.
A great example of what I am trying to mimic would be the FrameworkElement.ActualWidth property. You know how the ActualWidth property is calculated and reassigned, whenever the Width property changes, or whenever the control is redrawn, or whenever else? ------
From the developer's perspective, it just looks like data-binding hard-at-work.
But ActualWidth is a read-only dependency-property. Does Microsoft really have to go through this gigantic trash-hole of code to make that work? Or is there a simpler way that utilizes the existing functionality of the data-binding system?
public class foo : FrameworkElement
{
[ValueConversion(typeof(string), typeof(int))]
public class fooConverter : IValueConverter
{ public object Convert( object value, Type targetType,
object parameter, CultureInfo culture)
{ ... }
public object ConvertBack( object value, Type targetType,
object parameter, CultureInfo culture)
{ ... }
}
private static readonly fooConverter fooConv = new fooConverter();
private static readonly DependencyPropertyKey ReadOnlyIntPropertyKey =
DependencyProperty.RegisterReadOnly( "ReadOnlyInt", typeof(int),
typeof(foo), null);
public int ReadOnlyInt
{ get { return (int)GetValue(ReadOnlyIntPropertyKey.DependencyProperty); }
}
public static readonly DependencyProperty ReadWriteStrProperty =
DependencyProperty.Register( "ReadWriteStr", typeof(string), typeof(foo),
new PropertyMetadata(ReadWriteStr_Changed));
public string ReadWriteStr
{ get { return (string)GetValue(ReadWriteStrProperty); }
set { SetValue(ReadWriteStrProperty, value); }
}
private static void ReadWriteStr_Changed( DependencyObject d,
DependencyPropertyChangedEventArgs e)
{ try
{ if (d is foo)
{ foo f = d as foo;
f.SetValue( ReadOnlyIntPropertyKey,
fooConv.Convert(f.ReadWriteStr, typeof(int), null,
CultureInfo.CurrentCulture));
}
}
catch { }
}
}
Unfortunately, you'll need most of what you have. The IValueConverter isn't required in this case, so you could simplify it down to just:
public class foo : FrameworkElement
{
private static readonly DependencyPropertyKey ReadOnlyIntPropertyKey =
DependencyProperty.RegisterReadOnly( "ReadOnlyInt", typeof(int),
typeof(foo), null);
public int ReadOnlyInt
{
get { return (int)GetValue(ReadOnlyIntPropertyKey.DependencyProperty); }
}
public static readonly DependencyProperty ReadWriteStrProperty =
DependencyProperty.Register( "ReadWriteStr", typeof(string), typeof(foo),
new PropertyMetadata(ReadWriteStr_Changed));
public string ReadWriteStr
{
get { return (string)GetValue(ReadWriteStrProperty); }
set { SetValue(ReadWriteStrProperty, value); }
}
private static void ReadWriteStr_Changed(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
foo f = d as foo;
if (f != null)
{
int iVal;
if (int.TryParse(f.ReadWriteStr, out iVal))
f.SetValue( ReadOnlyIntPropertyKey, iVal);
}
}
}
It's not as bad as you suggest, IMHO...
You could get rid of the converter : IValueConverter is for bindings, you don't need it for conversions in code-behind. Apart from that, I don't see how you could make it more concise...
Yes, there is a clean way to "make a read-only DependencyProperty reflect the value of another property," but it may require a pretty fundamental shift in the overall property programming model of your app. In short, instead of using the DependencyPropertyKey to push values into the property, every read-only DependencyProperty can have a CoerceValue callback which builds its own value by pulling all the source values it depends on.
In this approach, the 'value' parameter that's passed in to CoerceValue is ignored. Instead, each DP's CoerceValue function recalculates its value "from scratch" by directly fetching whatever values it needs from the DependencyObject instance passed in to CoerceValue (you can use dobj.GetValue(...) for this if you want to avoid casting to the owner instance type).
Try to suppress any suspicion that ignoring the value supplied to CoerceValue may be wasting something. If you adhere to this model, those values will never be useful and the overall work is the same or less than a "push" model because source values that haven't changed are, as always, cached by the DP system. All that's changed is who's responsible for the calculation and where it's done. What's nice here is that calculation of each DP value is always centralized in one place and specifically associated with that DP, rather than strewn across the app.
You can throw away the DependencyPropertyKey in this model because you'll never need it. Instead, to update the value of any read-only DP you just call CoerceValue or InvalidateValue on the owner instance, indicating the desired DP. This works because those two functions don't require the DP key, they use the public DependencyProperty identifier instead, and they're public functions, so any code can call them from anywhere.
As for when and where to put these CoerceValue/InvalidateValue calls, there are two options:
Eager: Put an InvalidateValue call for the (target) DP in the PropertyChangedCallback of every (source) DP that's mentioned in the (target) DP's CoerceValueCallback function, --or--
Lazy: Always call CoerceValue on the DP immediately prior to fetching its value.
It's true that this method is not so XAML-friendly, but that wasn't a requirement of the OPs question. Considering, however, that in this approach you don't ever even need to fetch or retain the DependencyPropertyKey at all, it seems like it might one of the sleekest ways to go, if you're able to reconceive your app around the "pull" semantics.
In a completely separate vein, there's yet another solution that may be even simpler:
Expose INotifyPropertyChanged on your DependencyObject and use CLR properties for the read-only properties, which will now have a simple backing field. Yes, the WPF binding system will correctly detect and monitor both mechanisms--DependencyProperty and INotifyPropertyChanged--on the same class instance. A setter, private or otherwise, is recommended for pushing changes to this read-only property, and this setter should check the backing field to detect vacuous (redundant) changes, otherwise raising the old-style CLR PropertyChanged event.
For binding to this read-only property, either use the owner's OnPropertyChanged overload (for self-binding) to push in the changes from DPs, or, for binding from arbitrary external properties, use System.ComponentModel.DependencyPropertyDescriptor.FromProperty to get a DependencyPropertyDescriptor for the relevant souce DPs, and use its AddValueChanged method to set a handler which pushes in new values.
Of course for non-DP properties or non-DependencyObject instances, you can just subscribe to their INotifyPropertyChanged event to monitor changes that might affect your read-only property. In any case, no matter which way you push changes into the read-only property, the event raised by its setter ensures that changes to the read-only property correctly propagate onwards to any further dependent properties, whether WPF/DP, CLR, data-bound or otherwise.

TextBlock with binding not updated on property change [duplicate]

This question already has an answer here:
Databindings don't seem to refresh
(1 answer)
Closed 5 years ago.
In my UWP app I have a TextBlock, which should display a (formatted) date, that is bound to a property in the view model:
<TextBlock Style="{StaticResource summaryTextStyleHighlight}" Margin="0,10,0,0"
Text="{x:Bind ViewModel.CurrentDisplayDay, Converter={StaticResource DateFormatConverter}, ConverterParameter=d, Mode=OneWay}"
Name="lblCurrentDate" />
The converter is "configured" in the XAML like this:
<local:DateFormatConverter x:Key="DateFormatConverter" />
And the converter class is as followed:
public class DateFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
DateTime dt = DateTime.Parse(value.ToString());
if (parameter != null)
{
return dt.ToString((string)parameter, Utils.GetCurrentCultureInfo());
}
return dt.ToString("g", Utils.GetCurrentCultureInfo());
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
The ViewModel has a simple property for the DateTime value that is bound:
public DateTime CurrentDisplayDay
{
get;
private set;
}
But when I update the value in the ViewModel, the value won't get updated in the TextBlock on the (Main)Page.
I tried to move the property to the page, but that didn't help. If I refresh the page (navigate to it again), then the updated value is being displayed but I don't want to navigate to it, it should show the updated value through the binding.
What could be the issue?
#Patric You seem to be doing almost everything correctly, but you have forgotten about one step.
Is there any notification indicating that your property has been updated, when its value changes? You need to propagate a notification to the UI indicating that your ViewModel property has been altered, because otherwise even though the Text Dependency property is actively listening for any notification from the "source" (you have defined the binding as One-Way), you are simply not communicating anything to it.
Your ViewModel should implement the INotifyPropertyChanged Interface, which exposes the PropertyChanged event.
The property changed event will be responsible for communicating the update.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
On your property setter, you simply have to invoke this method, which will consequently be responsible for invoking the PropertyChanged event, with the appropriate event data.
Edit:
In order to use the CallerMemberName Attribute (which allows you to get the name of the property which called the method) use the following namespace System.Runtime.CompilerServices

Update XAML control property value from control's code but keep binding

I have written a control with a bindable property. This control also has a method to modify that property's value:
public class MyControl : ContentView // WPF: inherited from UserControl
{
// Xamarin:
public static readonly BindableProperty MyValueProperty = ...
// WPF:
// public static readonly DependencyProperty MyValueProperty = ...
public int MyValue
{
get { return (int) GetValue(MyValueProperty); }
set { SetValue(MyValueProperty, value); }
}
public void Reset()
{
MyValue = 0;
}
}
I am using that control in a normal XAML page and update MyValue via binding:
<local:MyControl x:Name="TheControl"
MyValue="{Binding MyValueSource, Mode=OneWay}" />
The binding initially propagates changes from MyValueSource to MyValue. But as soon as I call the Reset() method once, the binding is overwritten by the 0 and updates to MyValueSource are no longer pulled.
I suppose any direct assignment of MyValue is intended to replace a OneWay binding. With a TwoWay binding, the change is just propagated back to MyValueSource and the binding remains functional.
If Reset() was in the view model, I could do this:
public void Reset()
{
// TheControl.MyValue = 0; // Bad practice, destroys the binding
MyValueSource = 0; // Good practice, preserves the binding
}
I don't want to implement the reset logic (which is more complex than in this reduced example) in every VM though, so it's located in the view/control.
So I wonder - can you assign a bindable property's value from the control's code behind and still preserve a possible OneWay binding? I know this means the VM does not get the changed value; binding OneWay is likely not correct if the control updates the property as well; you should rather use a TwoWay binding then.
But if someone says OneWay in XAML, I'd rather have it behave that way down to the wire than implement some "OneWay until you call Reset()" behavior.
Side note: I am working in Xamarin, but I guess the behavior is the same for WPF.
Taken and fleshed out from #Clemens' comment:
WPF
You can use the SetCurrentValue method on an DependencyObject (i. e. the control) to change the current effective value of a DependencyProperty. Unlike SetValue, with SetCurrentValue any triggers, data bindings and styles to that property remain intact.
public void Reset()
{
// this.SetValue(MyValueProperty, 0); // Replaces the binding
this.SetCurrentValue(MyValueProperty, 0); // Keeps the binding
}
Remember that if you defined a OneWay binding, the view model will not be notified about the changed value, and that any change to the VM's MyValueSource property will override the control's value again (if the property is implemented correctly).
Xamarin
There is currently no proper way to assign a BindableProperty's value without replacing a OneWay binding attached to it. BindableObject (the control's base class) does not have any method comparable to WPF's SetCurrentValue and SetValue will allways replace the binding.
However, if you change the binding to BindingMode.TwoWay, the internal value change is propagated back to the view model. You should probably do this anyway to keep the control and the VM synchronized.
public void Reset()
{
// Replaces any OneWay bindings
// Updates MyValueSource for TwoWay bindings
this.SetValue(MyValueProperty, 0);
}
Here is the Hacky WPF equivalent for Xamarin, for OneWay binding:
public static class BindingObjectExtensions
{
public static Binding GetBinding(this BindableObject self, BindableProperty property)
{
if (self == null)
{
throw new ArgumentNullException(nameof(self));
}
if (property == null)
{
throw new ArgumentNullException(nameof(property));
}
var methodInfo = typeof(BindableObject).GetTypeInfo().GetDeclaredMethod("GetContext");
var context = methodInfo?.Invoke(self, new object[] { property });
var propertyInfo = context?.GetType().GetTypeInfo().GetDeclaredField("Binding");
return propertyInfo?.GetValue(context) as Binding;
}
public static void SetCurrentValue(this BindableObject self, BindableProperty property, object value)
{
if (self == null)
{
throw new ArgumentNullException(nameof(self));
}
if (property == null)
{
throw new ArgumentNullException(nameof(property));
}
var backupBinding = self.GetBinding(property);//backup binding
var backupConverter = backupBinding.Converter;//backup orig. converter
self.SetValue(property,value);//removes the binding.
backupBinding.Converter = new DefaultValueConverter {DefaultValue = value};//change the converter
self.SetBinding(property, backupBinding);//target should be updated to the default value
var converterField = backupBinding.GetType().GetTypeInfo().GetDeclaredField("_converter");
converterField.SetValue(backupBinding, backupConverter);//restore the converter
}
}
//the default value converter class
[ContentProperty(nameof(DefaultValue))]
public class DefaultValueConverter : BindableObject, IValueConverter, IMarkupExtension<DefaultValueConverter>
{
public object DefaultValue
{
get => GetValue(DefaultValueProperty);
set => SetValue(DefaultValueProperty, value);
}
public static readonly BindableProperty DefaultValueProperty =
BindableProperty.Create(nameof(DefaultValue), typeof(object), typeof(DefaultValueConverter));
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return DefaultValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DefaultValue;
}
public DefaultValueConverter ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
{
return ((IMarkupExtension<DefaultValueConverter>) this).ProvideValue(serviceProvider);
}
}

Dependency Property Default Value

I am new with WPF and dependency properties and my question might be totally newbie...
I have the following dependency property:
public static readonly DependencyProperty IsEditableProperty =
DependencyProperty.Register("IsEditable", typeof(bool), typeof(EditContactUserControl),
new FrameworkPropertyMetadata(false, OnIsEditablePropertyChanged));
public bool IsEditable
{
get { return (bool)GetValue(IsEditableProperty); }
set { SetValue(IsEditableProperty, value); }
}
private static void OnIsEditablePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
EditContactUserControl control = source as EditContactUserControl;
bool isEditable = (bool)e.NewValue;
if (isEditable)
control.stackPanelButtons.Visibility = Visibility.Visible;
else
control.stackPanelButtons.Visibility = Visibility.Collapsed;
}
The problem is that I want to have the code in the OnIsEditablePropertyChanged to be executed also for the default value of my property, which doesn't happen.
What am I doing wrong, or how should I do this in your opiniion?
Thank you in advance.
Instead of changing the visibility in code, you should Bind the Visibility property in XAML and use a boolean to Visibility Converter.
If you do this, it doesn't matter if the property is initialized or not.
The OnPropertyChanged callback won't be called on startup: The "default" value is in fact never really "set". Default: The value of the property when it isn't set.
If you want to execute some code at control startup, put it in the ApplyTemplate method override (in the case of a TemplatedControl) or at the end of your constructor (in the case of a UserControl)
Avoid duplicating this code in the constructor and in the property changed callback: Put it in a common method called by both ie:
void OnIsEditableChangedImpl(bool newValue)
{
....
}
I think a much better approach would be to set up stackPanelButtons.Visibility = Visibility.Collapsed in your XAML as default too, in which case you don't need to run all this code on startup!

WPF - Binding in XAML to an object created in the code behind

Can an object created in the code (ie C#) be used for binding in the XAML?
For example:
public class MyForm
{
private MyComplexObject complexObject;
public MyForm()
{
InitializeComponent();
}
public OnButtonClick(object sender, RoutedEventArgs e)
{
complexObject = new MyComplexObject();
}
}
complexObject is not created till a button is clicked. But once that button is clicked I would like to have a text box that is bound to complexObject.ID start showing the Id.
I would like to do that in the XAML if that is possible.
Can this be done? If so, how?
Yes, this can be done, binding to a property that you update with the desired value. I'd suggest you look into the MVVM pattern (Model-View-ViewModel), which is really useful for structuring this nicely working with WPF. Check out this video for a nice overview:
MVVM video
Using MMVM you would create a class which would be the ViewModel class. This one would typically be set to the DataContext of the View. Having done so you could add dynamic references to the other properties of the class, e.g. binding your text field to some property holding the Id og the ComplexObject. If your ViewModel class had a property ComplexObject, which again had a property ID you'd simply bind to the object like this:
<TextBlock Text="{Binding ComplexObject.ID}" />
Having this you could trigger creation of your ComplexObject from mouse click, which you should ideally set up as a command binding. Also note that the ViewModel class (or whoever is holding the ComplexObject needs to notify the View when the object has been set. This can either be done by making the ComplexObject a DependencyProperty or by making the class holding the property implement the INotifyPropertyChanged interface - giving it the PropertyChanged function to trigger the changed event. I prefer the latter.
One possibility would be to have your XAML bind to a property on your code behind. The getter for that property would return complexObject.ID, if complexObject != null. Otherwise, it returns something "default", whether that's null or 0 or default(ID's type). Similarly, the setter for that property would assign value to complexObject.ID if complexObject is, again, not null.
public int ID
{
get
{
if (complexObject != null)
return complexObject.ID;
return 0; // or null or some appropriate default
}
set
{
if (complexObject != null)
complexObject.ID = value;
}
}

Categories

Resources