This should do the trick:
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
var multiBinding = new MultiBinding()
{
Converter = FallbackColorConverter.Instance,
Mode = BindingMode.TwoWay,
Bindings =
{
new Binding()
{
Source = this,
Path = new PropertyPath(CustomForegroundBackingProperty),
Mode = BindingMode.TwoWay
},
new Binding()
{
Source = this,
Path = new PropertyPath(ForegroundProperty),
Mode = BindingMode.OneWay
},
},
};
SetBinding(CustomForegroundProperty, multiBinding);
}
public Brush CustomForeground
{
get => (Brush)GetValue(CustomForegroundProperty);
set => SetValue(CustomForegroundProperty, value);
}
public static readonly DependencyProperty CustomForegroundProperty =
DependencyProperty.Register(nameof(CustomForeground), typeof(Brush), typeof(MyControl), new PropertyMetadata(null));
private static readonly DependencyProperty CustomForegroundBackingProperty =
DependencyProperty.Register("CustomForegroundBacking", typeof(Brush), typeof(MyControl), new PropertyMetadata(null));
private class FallbackColorConverter : IMultiValueConverter
{
public static readonly FallbackColorConverter Instance = new FallbackColorConverter();
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0] ?? values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new object[] { value };
}
}
}
We set up two DependencyProperties. CustomForegroundBackingProperty acts as the actual store for any CustomForeground values the user sets. CustomForegroundProperty just acts as a sort of proxy.
When then set up a MultiBinding from ForegroundProperty and CustomForegroundBackingProperty to CustomForegroundProperty. The MultiBinding is set to TwoWay (so change any change to CustomForegroundProperty triggers the binding, as does any change to ForegroundProperty or CustomForegroundBackingProperty).
We set the binding to CustomForegroundBackingProperty to TwoWay (as we want writes to CustomForegroundProperty to affect CustomForegroundBackingProperty), but we set the binding to ForegroundProperty to OneWay, as we don't want this to happen.
Then we put a converter on the MultiBinding. When the converter writes to CustomForegroundProperty, it looks at both CustomForegroundBackingProperty and ForegroundProperty, and chooses ForegroundProperty if CustomForegroundBackingProperty is null
. This is triggered if either ForegroundProperty or CustomForegroundBackingProperty changes.
When the converter writes the other way -- i.e. the user writes to CustomForegroundProperty -- then we just return the value they set. Because of the modes on the bindings in the MultiBinding, this means this value gets set to CustomForegroundBackingProperty.