Zebrastian
Zebrastian

Reputation: 481

Binding IsVisible to property toggled by Switch

I have a Switch bound to a property of an element in a List. I want to bind IsVisible of a button to the same property, but the button's visibility is not changed when the property is changed by the Switch. What am I missing?

XAML:

<StackLayout>
    <ListView HorizontalOptions="FillAndExpand" ItemsSource="{Binding EquipmentList}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Orientation="Horizontal">
                        <Label Text="{Binding Name}" />
                        <Switch IsToggled="{Binding State}" />
                        <Button
                            Command="{Binding BindingContext.DoCommand, Source={x:Reference TestPage}}"
                            CommandParameter="{Binding .}"
                            IsVisible="{Binding State}"
                            Text="Click" />
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackLayout>

ViewModel:

private Command<Equipment> _doCommand;
    public Command<Equipment> DoCommand => _doCommand ??
(_doCommand = new Command<Equipment>((Equipment obj) => HandleEquipment(obj)));


// Outputs correct Name and State of the list item
private void HandleEquipment(Equipment obj)
{
        System.Diagnostics.Debug.WriteLine(obj.Name + ", " + obj.State);
}

Model:

class Equipment
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool State { get; set; }

    public Equipment(int Id, string Name, bool State)
    {
        this.Id = Id;
        this.Name = Name;
        this.State = State;
    }
}

Upvotes: 2

Views: 1508

Answers (2)

Coops
Coops

Reputation: 301

Instead of binding two things to a property, why not have the single item bound (i.e. the switch) and use XAML to show or hide the button:

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
</Window.Resources>

<StackLayout>
    <ListView HorizontalOptions="FillAndExpand" ItemsSource="{Binding EquipmentList}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Orientation="Horizontal">
                        <Label Text="{Binding Name}" />
                        <Switch Name="toggleSwitch" IsToggled="{Binding State}" />
                        <Button
                        Command="{Binding BindingContext.DoCommand, Source={x:Reference TestPage}}"
                        CommandParameter="{Binding .}"
                        IsVisible="{Binding ElementName=toggleSwitch, Path=IsToggled, Converter={StaticResource BooleanToVisibilityConverter}"
                        Text="Click" />
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackLayout>

It may not be a Window that your StackLayout is in, but if you place a BooleanToVisibilityConverter in your Resources section you'll then be able to use it in your XAML file. This will mean that if the property name changes in the future you only have one place you need to update in the user interface and you're also using the power of the XAML language.

Also as correctly pointed out by everyone, you need to implement INotifyPropertyChanged in the model in order for the Switch to be updated too.

Upvotes: 0

Joehl
Joehl

Reputation: 3701

As Gerald wrote in his first comment: You have to implement the INotifyPropertyChanged interface on your Equipment model (and not just in the ViewModel).

Without this implementation, the elements in the view have no chance to know, that the state changed (in your case the button).

Implementation:

public class Equipment: INotifyPropertyChanged
{
    public bool State
    {
        get => _state;
        set =>
        {
            _state = value;
            OnPropertyChanged();
        }
    }
    private bool _state;

    // OTHER PROPERTIES

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

The call of the method OnPropertyChanged() is important. The IsVisible property of the button recognizes the change and updates his value.

Upvotes: 2

Related Questions