David Clarke
David Clarke

Reputation: 13256

XAML DataTrigger not updating

I'm trying to change the colour of a button when a property in the datasource changes. Initialising the background colour works correctly based on the initial value of the IsFilterOn property but it isn't updating when the property changes.

The XAML

<FlexLayout BindableLayout.ItemsSource="{Binding Filters}">
    <BindableLayout.ItemTemplate>
        <DataTemplate>
            <Button>
                <Button.Triggers>
                    <DataTrigger Binding="{Binding IsFilterOn}" TargetType="Button" Value="False">
                        <Setter Property="BackgroundColor" Value="{Binding UpColour}" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsFilterOn}" TargetType="Button" Value="True">
                        <Setter Property="BackgroundColor" Value="{Binding DownColour}" />
                    </DataTrigger>
                </Button.Triggers>
            </Button>
        </DataTemplate>
    </BindableLayout.ItemTemplate>
</FlexLayout>

Filters is an ObservableCollection<FilterModel>:

public class FilterModel
{
    public string UpColour { get; set; }
    public string DownColour { get; set; }
    public bool IsFilterOn { get; set; }
}

Tapping a button correctly updates the IsFilterOn property for the button (code not shown) but the UI doesn't change to reflect it. What am I doing wrong?

Upvotes: 1

Views: 1407

Answers (1)

hvaughan3
hvaughan3

Reputation: 11105

We cannot see how you are updating the IsFilterOn property from true to false but if you are directly changing the property of one of the FilterModel instances in the list, then the property changed event will not be invoked.

The ObservableCollection will execute property changed events when you add or remove something from the list, but it is not watching objects' properties.

You have 2 options, either invoke Property Changed events from your FilterModel anytime IsFilterOn is updated or change all of the property values you need to and then manually execute property changed on the list. If you are updating multiple FilterModels at the same time, performance would probably be better with the second option.

Option 1:

public class FilterModel : INotifyPropertyChanged {
    public string UpColour { get; set; }
    public string DownColour { get; set; }

    private bool _isFilterOn = false;
    public bool IsFilterOn {
        get { return _isFilterOn; }
        set {
            _isFilterOn = value;
            NotifyPropertyChanged(); // Anytime this property is changed Property Changed will be executed
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Option 2:

public class FilterViewModel {
    private ObservableCollection<FilterModel> _filters;
    public ObservableCollection<FilterModel> Filters {
        get => _filters ?? (_filters = new ObservableCollection<FilterModel>());
        set {
            _filters = value;
            NotifyPropertyChanged();
        }
    }

    public void ToggleFilters() {
        // some logic

        Filters[0].IsFilterOn = true; // Make all property changes

        NotifyPropertyChanged(nameof(Filters)); // Tell the UI that the list has been updated and to refresh the data
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Upvotes: 1

Related Questions