Consider moving theme-specific converters to theme assemblies
Is your feature request related to a problem? Please describe.
As discussed in https://github.com/AvaloniaUI/Avalonia/pull/18718#issuecomment-2984454059, Avalonia currently has several converters used by only its themes, Fluent and Simple. Examples of such converters: MenuScrollingVisibilityConverter and MarginMultiplierConverter.
These converters currently live in the Avalonia.Controls assembly so they can easily be shared.
However, we shouldn't "bloat" that assembly with unnecessary types that aren't used outside of themes and are effectively part of the public Avalonia.Controls API.
Plus, the behavior of these converters isn't really documented: we might very well adjust them depending on theme requirements.
Describe the solution you'd like
Move the converters to the Avalonia.Themes.Fluent and Avalonia.Themes.Simple projects.
The file would be linked between the two projects to avoid source code duplication, but the type would effectively exist in both theme assemblies.
While not ideal, this is what WPF does (see ProgressBarBrushConverter). This allows the theme to be self-contained.
Describe alternatives you've considered
Do nothing and keep the converters where they are.
Additional context
No response
We might also consider making all the converters sealed. A converter really doesn't need to be open for extensibility.
I think some applications have both fluent and simple installed, will the linkage cause any conflict?
I think some applications have both fluent and simple installed, will the linkage cause any conflict?
It might. We should probably change the converters' namespace to match the current assembly, avoiding conflicts.
That said, even if they share the same namespace, problems will occur only if both themes are referenced and the converter is explicitly used in C#, which seems a bit unlikely (but not impossible).
Another option I randomly thought about - to continue our approach/consistency with static converters:
public static class ControlConverters
{
public static IMultiValueConverter MenuScrollingVisibility = /* */;
public static IValueConverter TreeIndent = /* */;
}
public static class MarginConverters
{
public static IValueConverter Left = /* */;
public static IValueConverter Top= /* */;
public static IValueConverter Right = /* */;
public static IValueConverter Bottom = /* */;
/* duplicate for the any possible combination */
}
public static class CornerRadiusConverters
{
public static IValueConverter TopLeft = /* */;
public static IValueConverter TopRight= /* */;
public static IValueConverter BottomRight = /* */;
public static IValueConverter BottomLeft= /* */;
/* duplicate for the any possible combination */
}
public static class StringConverters
{
// replacement for Avalonia.Controls/Converters/StringFormatConverter.cs
public static IMultiValueConverter DynamicFormat = /* */;
}
Goal here is to make these converters less theme specific, and more generally applicable controls. Except ControlConverters, but we don't have many of these.
Also,
- Replace EnumToBoolConverter with ObjectConverters.Equal
- Replace PlatformKeyGestureConverter with TemplateBindng.StringFormat (should work?)
- We can combine CornerRadiusFilterConverter and CornerRadiusToDoubleConverter by checking target type. Or just split into different static converters.
- Remove StringFormatValueConverter (I think it was used internally for old Bindings implementation, but not anymore)
- Same with StringFormatMultiValueConverter, or make it internal (it's still used internally for MultiBinding)
- Converters from Avalonia.Controls.ColorPicker also can be incorporated into Color/BrushConverters static class as much as possible.
Instead of class ControlConverters, we also can move these converters as static properties of the control themselves (i.e., static TreeViewItem.IntentConverter).
Also, it was always possible to combine markup extensions and converters, allowing to pass extra arguments and context (which value converters commonly lack). Not sure if we want to do that in our stable API, but as an option to consider.
Example from my older code, not an actual API for Avalonia, but a technique:
<Image Source="{Binding IconResources, Converter={converters:ResourceConverter}}" />