Search code examples
c#wpfmvvmwindows-community-toolkit

How to Toggle Visibility Between a Button and a Stack Panel Containing Two Buttons


I am having difficulty figuring out how to toggle the visibility between three buttons. Here is the scenario:

  • I have 3 buttons on a user control, an Edit button, an OK button, and a Cancel button.
  • The Ok and Cancel buttons are grouped together in a stack panel.
  • The Edit button is by itself.
  • I would like when the Edit button is pressed, that it (the Edit button) is hidden and the stack panel containing the Ok and Cancel buttons are shown.
  • When either the Cancel or Ok buttons are pressed, they are hidden, and the Edit button is shown again.
  • There will be 7 lines on this form that are all very similar, with a label, text box, and an edit button.
  • Is it possible to use only a few methods to control the visibility of all of the buttons/ stack panels.
  • i.e. Can I have one Edit method and show the stack panel depending on the text box control name/ binding, instead of having to right 7 methods for showing the stack panel, 7 Ok methods and 7 cancel methods?

Here is the line on the form with the Edit button:

Edit Button

And here is the line on the form with the Ok and Cancel buttons:

Ok and Cancel Buttons

Here is the XAML code that I've come up with for this line:

<StackPanel
        Orientation="Horizontal"
        HorizontalAlignment="Center"
        Grid.Row="2"
        Grid.Column="0"
        Grid.ColumnSpan="4">
    <Label
        Style="{StaticResource DeviceInfoPropertyLabelStyle}">
        CONTROLLER NAME:
    </Label>
    <TextBox
        Text="{Binding ControllerName}"
        Style="{StaticResource DeviceInfoTextBoxStyle}" />
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <StackPanel
            Orientation="Horizontal"
            Grid.Column="0">

            <Button
                Command="{Binding EditCommand}"
                Visibility="{Binding IsEditButtonVisible, Converter={StaticResource BoolToVisibilityConverter}, FallbackValue=Collapsed}"
                Style="{StaticResource DeviceInfoEditButtonStyle}">
                Edit
            </Button>
        </StackPanel>
        <StackPanel
            x:Name="EditControllerNameStackPanel"
            Orientation="Horizontal"
            Grid.Column="0"
            Visibility="{Binding IsOkCancelButtonVisible, Converter={StaticResource BoolToVisibilityConverter}, FallbackValue=Visible}">
            <Button
                Command="{Binding OkEditCommand}"
                Style="{StaticResource DeviceInfoEditOkButtonStyle}">
                OK
            </Button>
            <Button
                Command="{Binding  CancelEditCommand}"
                Style="{StaticResource DeviceInfoEditCancelButtonStyle}">
                CANCEL
            </Button>
        </StackPanel>
    </Grid>
</StackPanel>

Here is the code in the ViewModel that I have so far. It's only a skeleton at this point:

public bool IsEditButtonVisible
{
    get
    {
        bool output = false;
        if (true)
        {
            return true;
        }

        return output;
    }
}

public bool IsOkCancelButtonVisible
{
    get => true;
}


[RelayCommand]
private void Edit()
{
    if (true)
    {

    } 
}

[RelayCommand]
private void OkEdit()
{

}

[RelayCommand]
private void CancelEdit()
{

}

Note that I am using the MVVM Community Toolkit.

Let me know if I need to provide any additional information.

Thanks


Solution

  • You can use just one boolean to toggle the visibility. Change BoolToVisibilityConverter code a bit..

    public class BoolToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool b)
            {
                if (parameter is string str && str == "Inverse")
                    return b ? Visibility.Hidden : Visibility.Visible;
                return b ? Visibility.Visible : Visibility.Hidden;
            }
    
            return Visibility.Visible;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    Edit button

    <Button
        Visibility="{Binding IsEditButtonVisible, Converter={StaticResource BoolToVisibilityConverter}}">
    

    OK/Cancel stackpanel

    <StackPanel
        Visibility="{Binding IsEditButtonVisible, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=Inverse}">
    

    ViewModel (sorry I don't use MVVM Community Toolkit)

    public class ViewModel : INotifyPropertyChanged
    {
        
        private bool _isEditButtonVisible;
    
        public bool IsEditButtonVisible
        {
            set
            {
                _isEditButtonVisible = value;
                OnPropertyChanged(nameof(IsEditButtonVisible));
            }
            get => _isEditButtonVisible;
        }
        
        private void Edit()
        {
            IsEditButtonVisible = false;
        }
    
        private void Ok()
        {
            IsEditButtonVisible = true;
        }
    
        private void Cancel()
        {
            IsEditButtonVisible = true;
        }
        
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        
        
        // other code
    }
    

    To apply the same logic over the other 7 similar groups, you must have at least 7 booleans, but you can have 3 commands only, where you can pass the name of the group to the command, and the command will toggle the appropriate group based on the passed parameter

    In View

    <Button
        Command="{Binding OkCommand}"
        CommandParameter="group1">
        OK
    </Button>
    

    In ViewModel

    private void Edit(string commandParameter)
    {
        IsEdit1ButtonVisible = commandParameter != "group1";
        IsEdit2ButtonVisible = commandParameter != "group2";
        IsEdit3ButtonVisible = commandParameter != "group3";
        // etc...
    }
    
    private void Ok(string commandParameter)
    {
        IsEdit1ButtonVisible = commandParameter == "group1";
        IsEdit2ButtonVisible = commandParameter == "group2";
        IsEdit3ButtonVisible = commandParameter == "group3";
        // etc...
    }
    
    private void Cancel(string commandParameter)
    {
        IsEdit1ButtonVisible = commandParameter == "group1";
        IsEdit2ButtonVisible = commandParameter == "group2";
        IsEdit3ButtonVisible = commandParameter == "group3";
        // etc...
    }