I'm using syncfusion's RibbonButton along with other Ribbon Controls. Both of these controls have properties such as Label
, LargeIcon
, but they do not share a base class or interface containing these members.
What I'd like to do is define a single style which can be applied to both types of controls. Is this possible? If so, how?
I managed to solve the problem by creating a custom setter class. In order to make it work, I have to use other properties than those of the normal setter:
PropertyName
holds the name of the propertyBinding
holds the binding to be assigned to the propertyValue
holds the static value to be assigned to the propertyOnly one of Binding
and Value
should be set.
The property name and binding/value are combined into a single object, which is the actual value of the setter. The actual target of the setter is a dynamically generated DependencyProperty (one for each actual property).
The key to the solution is that in the PropertyChangedCallback of these properties, the first argument is the actual target object. From this object I get the property through reflection, and assign the binding/value.
Full code:
public class UnsafeSetter : Setter
{
private class ValueContainer
{
public string Property { get; set; }
public object Value { get; set; }
}
private static readonly Dictionary<string, DependencyProperty> _properties = new Dictionary<string, DependencyProperty>();
private DependencyProperty GetOrAddProperty(string name)
{
DependencyProperty res;
if (!_properties.TryGetValue(name, out res))
{
res = DependencyProperty.RegisterAttached(
"Intermediate_" + name,
typeof(ValueContainer),
typeof(UnsafeSetter),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnPropertyChanged)));
_properties.Add(name, res);
}
return res;
}
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var args = (ValueContainer)e.NewValue;
var prop = GetDependencyProperty(d.GetType(), args.Property);
if (prop == null)
{
throw new ArgumentException(string.Format("Dependency property {0} not found on type {1}", args.Property, d.GetType().Name));
}
else
{
var binding = args.Value as BindingBase;
if (binding != null)
{
BindingOperations.SetBinding(d, prop, binding);
}
else
{
d.SetValue(prop, args.Value);
}
}
}
private static DependencyProperty GetDependencyProperty(Type type, string name)
{
if (type == null)
return null;
FieldInfo fieldInfo = type.GetField(name + "Property", BindingFlags.Public | BindingFlags.Static);
if (fieldInfo == null)
return GetDependencyProperty(type.BaseType, name);
return (DependencyProperty)fieldInfo.GetValue(null);
}
private string _unsafeProperty;
public string PropertyName
{
get { return _unsafeProperty; }
set
{
_unsafeProperty = value;
UpdateBaseValue();
base.Property = GetOrAddProperty(value);
}
}
private object _unsafeValue;
public BindingBase Binding
{
get { return _unsafeValue as BindingBase; }
set
{
_unsafeValue = value;
UpdateBaseValue();
}
}
public new object Value
{
get { return _unsafeValue; }
set
{
_unsafeValue = value;
UpdateBaseValue();
}
}
private void UpdateBaseValue()
{
base.Value = new ValueContainer
{
Property = PropertyName,
Value = Value
};
}
}
Usage:
<Style x:Key="ButtonStyle">
<Style.Setters>
<utilities:UnsafeSetter PropertyName="Label" Value="This is a static value" />
<utilities:UnsafeSetter PropertyName="LargeIcon" Binding="{Binding IconKey, Converter={StaticResource ResourceKeyConverter}}" />
<utilities:UnsafeSetter PropertyName="SmallIcon" Binding="{Binding RelativeSource={RelativeSource Self}, Path=LargeIcon}" />
</Style.Setters>
</Style>
<syncfusion:RibbonButton Style="{StaticResource ButtonStyle}" />
<syncfusion:DropDownButton Style="{StaticResource ButtonStyle}" />
<syncfusion:SplitButton Style="{StaticResource ButtonStyle}" />
I hope this can help others that are facing the same issue as me. Feel free to share any feedback or possible improvements as well!