AvaloniaUI / Avalonia.Xaml.Behaviors

Port of Windows UWP Xaml Behaviors for Avalonia Xaml.
MIT License
390 stars 46 forks source link

Setting BorderBrush and BorderThickness for a button doesn't seem to work? #59

Closed derekantrican closed 7 months ago

derekantrican commented 3 years ago

I'm on version 0.9.12 of everything. I'm trying to do the following:

<Button Name="GetCalendarsButton" Grid.Column="1" Height="25" Content="Get Calendars" Command="{Binding GetCalendars}" Margin="5">
  <i:Interaction.Behaviors>
    <ia:DataTriggerBehavior Binding="{Binding FlashGetCalendarsButton}" ComparisonCondition="Equal" Value="true">
        <ia:ChangePropertyAction TargetObject="{Binding #GetCalendarsButton}" PropertyName="BorderBrush" Value="Blue"/>
        <ia:ChangePropertyAction TargetObject="{Binding #GetCalendarsButton}" PropertyName="BorderThickness" Value="2"/>
    </ia:DataTriggerBehavior>
    <ia:DataTriggerBehavior Binding="{Binding FlashGetCalendarsButton}" ComparisonCondition="Equal" Value="false">
        <ia:ChangePropertyAction TargetObject="{Binding #GetCalendarsButton}" PropertyName="BorderBrush" Value="Transparent"/>
        <ia:ChangePropertyAction TargetObject="{Binding #GetCalendarsButton}" PropertyName="BorderThickness" Value="0"/>
    </ia:DataTriggerBehavior>
  </i:Interaction.Behaviors>
</Button>

That doesn't seem to work, but - as an alternate test, this does work:

<Button Name="GetCalendarsButton" Grid.Column="1" Height="25" Content="Get Calendars" Command="{Binding GetCalendars}" Margin="5">
  <i:Interaction.Behaviors>
    <ia:DataTriggerBehavior Binding="{Binding FlashGetCalendarsButton}" ComparisonCondition="Equal" Value="true">
        <ia:ChangePropertyAction TargetObject="{Binding #GetCalendarsButton}" PropertyName="Content" Value="Get"/>
    </ia:DataTriggerBehavior>
    <ia:DataTriggerBehavior Binding="{Binding FlashGetCalendarsButton}" ComparisonCondition="Equal" Value="false">
        <ia:ChangePropertyAction TargetObject="{Binding #GetCalendarsButton}" PropertyName="Content" Value="Get Calendars"/>
    </ia:DataTriggerBehavior>
  </i:Interaction.Behaviors>
</Button>
dif-sam commented 2 years ago

As workaround, try add this code before use triggers somewhere in applications initialization:

if (!TypeDescriptor.GetAttributes(typeof(Avalonia.Thickness)).Cast<Attribute>().Any(x => x is TypeConverterAttribute))
{
    TypeDescriptor.AddAttributes(typeof(Avalonia.Thickness), new TypeConverterAttribute(typeof(ThicknessTypeConverter)));
}

/*...*/

    public class ThicknessTypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
        {
            return sourceType == typeof(string) || sourceType == typeof(double) || sourceType == typeof(int);
        }

        public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
        {
            return value switch
            {
                string stringValue => new Avalonia.Thickness(double.Parse(stringValue, culture)),
                double doubleValue => new Avalonia.Thickness(doubleValue),
                int intValue => new Avalonia.Thickness(intValue),
                _ => base.ConvertFrom(context, culture, value)
            };
        }
    }

ChangePropertyAction internally uses a heuristic way of converting types (e.g. string->Avalonia.Thickness and string->Avalonia.Brush in your case) and it falldown to call TypeDescriptor.GetConverter. Using reflection it tries to find custom TypeConverter for destynation type. There is no default converter today, but you can do it yourself.

sam-wheat commented 2 years ago

Same problem but with padding:

<i:Interaction.Behaviors>
    <ia:DataTriggerBehavior Binding="{Binding IsExpanded}" ComparisonCondition="Equal" Value="true">
        <ia:ChangePropertyAction TargetObject="{Binding #btn}" PropertyName="Content" Value="&#xE936;"/>
        <ia:ChangePropertyAction TargetObject="{Binding #btn}" PropertyName="Padding" Value="5,7,5,3"/>

    </ia:DataTriggerBehavior>
    <ia:DataTriggerBehavior Binding="{Binding IsExpanded}" ComparisonCondition="Equal" Value="false">
        <ia:ChangePropertyAction TargetObject="{Binding #btn}" PropertyName="Padding" Value="9,4,1,6"/>
        <ia:ChangePropertyAction TargetObject="{Binding #btn}" PropertyName="Content" Value="&#xE937;"/>
    </ia:DataTriggerBehavior>
</i:Interaction.Behaviors>

When trying to implement the suggested workaround I can cannot find class Avalonia.Padding. Seems like it would be a pretty low leve object and included in the Avalonia library(?):

public class PaddingTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
    {
        return sourceType == typeof(string) || sourceType == typeof(double) || sourceType == typeof(int);
    }

    public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
    {
        return value switch
        {
            string stringValue => new Avalonia.Padding(double.Parse(stringValue, culture)), // error:  .Padding(...) not found
        };
    }
}
maxkatz6 commented 2 years ago

Instead, it should be parsed compile time. But for that some attributes-hints should be created.