0
votes

I am trying to set a custom property on a custom control using the Visual State Manager but I'm not having any luck so far. My custom control is just a label with an additional bindable property on it.

public class SelectableLabel : Label
{
    public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableLabel), false);

    public bool IsSelected
    {
        get { return (bool)GetValue(IsSelectedProperty); }
        set
        {
            Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
            SetValue(IsSelectedProperty, value);
        }
    }

I use this control inside a CollectionView to toggle the IsSelected property when the control enters the Selected visual state.

    <CollectionView
        x:Name="cv"
        ItemsSource="{Binding Names}"
        SelectionMode="Multiple"
        SelectedItems="{Binding SelectedNames, Mode=TwoWay}"
        VerticalOptions="Fill">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <local:SelectableLabel
                    x:Name="lblName"
                    Text="{Binding First}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup
                            x:Name="CommonStates">
                            <VisualState
                                x:Name="Normal">
                                <VisualState.Setters>
                                    <Setter
                                        Property="IsSelected"
                                        Value="False" />
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState
                                x:Name="Selected">
                                <VisualState.Setters>
                                    <Setter
                                        Property="IsSelected"
                                        Value="True" />
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </local:SelectableLabel>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>

When I run this on an iOS simulator, I'm not seeing the setter being fired when the visual state changes to Selected. If I change the property in the setter to BackgroundColor or Text, I'm seeing the expected behavior. The problem seems specific to the custom property. I looked at the documentation for the Setter.Property and it states that the Setter can be applied to a BindableProperty which IsSelected is. Am I doing something wrong or does the VSM not support this functionality?


Edit: The CollectionView part of this example is irrelevant. The same issue happens with this code.

    public class SelectableEntry : Entry
    {
        public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableEntry), false);

        public bool IsSelected
        {
            get { return (bool)GetValue(IsSelectedProperty); }
            set
            {
                Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
                var color = value ? Color.LightBlue : Color.LightPink;
                SetValue(IsSelectedProperty, value);
                SetValue(BackgroundColorProperty, color);
            }
        }
    }

Here is the corresponding XAML. The background color changes when the first custom Entry control receives focus while the second does not. I also don't see my WriteLine statement in the console.

        <local:SelectableEntry Text="First">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup
                    x:Name="CommonStates">
                    <VisualState
                        x:Name="Normal">
                        <VisualState.Setters>
                            <Setter
                                Property="BackgroundColor"
                                Value="LightBlue" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState
                        x:Name="Focused">
                        <VisualState.Setters>
                            <Setter
                                Property="BackgroundColor"
                                Value="LightPink" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </local:SelectableEntry>
        <local:SelectableEntry Text="Second">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup
                    x:Name="CommonStates">

                    <VisualState
                        x:Name="Normal">
                        <VisualState.Setters>
                            <Setter
                                Property="IsSelected"
                                Value="False" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState
                        x:Name="Focused">
                        <VisualState.Setters>
                            <Setter
                                Property="IsSelected"
                                Value="True" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </local:SelectableEntry>
1
Hi , if need to Detect property changes , you can use propertyChanged . docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/…Junior Jiang
@JuniorJiang-MSFT I tried adding a propertyChanged method like in the example you linked but it's not being triggered. I guess that's more confirmation that the Visual State Manager is not changing the property when the visual state changes.Mirza Dobric
Yeah , that's right . It seems that Visual State Manager not supports custom Bindable Property .Junior Jiang

1 Answers

2
votes

I have tried with Bindable Property to check whether property be invoked in VisualStateManager . That's regrettable, it can not be invoked even in propertyChanged method . I think maybe bindable property can not work in VisualStateManager.

Custom Entry Code as follow :

public class SelectableEntry : Entry
{
    public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableEntry), false ,propertyChanged:changedMethod);

    private static void changedMethod(BindableObject bindable, object oldValue, object newValue)
    {
        Console.WriteLine($"MDO: {oldValue}.IsSelected_set={newValue}");
    }

    public bool IsSelected
    {
        get { return (bool)GetValue(IsSelectedProperty); }
        set
        {
            Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
            var color = value ? Color.LightBlue : Color.LightPink;
            SetValue(IsSelectedProperty, value);
            SetValue(BackgroundColorProperty, color);
        }
    }
}

Solution:

However , there is a Attached Properties that can be used in Style.Setters. Then you can also have a try with it in VisualState.Setters .It also will work .

Attached property class Code as follow :

public class SelectableEntryStyle
{
    public static readonly BindableProperty IsSelectedProperty =  BindableProperty.CreateAttached("IsSelected", typeof(bool), typeof(SelectableEntryStyle), false,propertyChanged:onChangedMethod);

    private static void onChangedMethod(BindableObject bindable, object oldValue, object newValue)
    {
        Console.WriteLine($"MDO:IsSelected_set={newValue}");
        var color = (bool)newValue ? Color.LightBlue : Color.LightPink;
        var entry = bindable as Entry;
        entry.SetValue(Entry.BackgroundColorProperty, color);
    }

    public static bool GetIsSelected(BindableObject view)
    {
        return (bool)view.GetValue(IsSelectedProperty);
    }

    public static void SetIsSelected(BindableObject view, bool value)
    {
        view.SetValue(IsSelectedProperty, value);
    }

}

The code of Xaml as follow :

<local:SelectableEntry FontSize="18" Placeholder="Bindable Property">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal">
                <VisualState.Setters>
                    <Setter Property="IsSelected"
                            Value="False" />
                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Disabled">
                <VisualState.Setters>
                    <Setter Property="IsSelected"
                            Value="True" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
<Entry Placeholder="Attached Property">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">

            <VisualState x:Name="Normal">
                <VisualState.Setters>
                    <Setter Property="local:SelectableEntryStyle.IsSelected"
                            Value="False" />
                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Focused">
                <VisualState.Setters>
                    <Setter Property="local:SelectableEntryStyle.IsSelected"
                            Value="True" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

Then console can print log when Entry is Normal or Focused .

02-18 14:26:27.360 I/mono-stdout(26014): MDO:IsSelected_set=True

02-18 14:26:28.675 I/mono-stdout(26014): MDO:IsSelected_set=False

The effects :

enter image description here