lachfoy
Topic Author
Posts: 5
Joined: 20 Feb 2024, 06:25

ListBox vs StackPanel of Buttons

15 Mar 2024, 06:17

Consider a settings/options menu, where I have a list of items. Some of the buttons are toggle buttons, some are sliders, etc.
I want to show a description for each option and an image that shows what that option does, so I'd like to have a property for the selected option.

In the quest log sample, there is ListBox for all of the quest items, and the SelectedItem property is bound to the selected quest.
That way, you are able use the data context of the SelectedQuest to populate the quest image and title and whatnot, which is great.

However, using a ListBox with a list of buttons seems to break. The focus floats to the container for the list item, *not* the control inside it.
In the main menu sample, buttons are placed in a StackPanel and avoid this problem.

I've run into this issue several times where I'd like to have a list of buttons and bind to the SelectedItem, but I can't work out any way of doing this easily.
What would be the recommended approach to solving this problem?
 
User avatar
nadjibus
Posts: 32
Joined: 24 Feb 2022, 04:09

Re: ListBox vs StackPanel of Buttons

15 Mar 2024, 20:28

Just use ItemsControl. It's the base control for all Lists. It has no particular style or selection mechanism, and acts just like a Repeater control. By default it stacks the items vertically (using a StackPanel), but you can change to another layout control via ItemsPanel property.
<ItemsControl ItemsSource="{Binding Options}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Content="{Binding Text}" 
                    Command="{Binding DataContext.OptionSelectedCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}" 
                    CommandParameter="{Binding .}" />
         </DataTemplate>
    </ItemsControl.ItemTemplate>
    
</ItemsControl>
ViewModel:
public class MenuViewModel
{
    public MenuViewModel()
    {
        Options = CreateMenuOptions();
        OptionSelectedCommand = new DelegateCommand(OptionSelected);
    }
    
    public MenuOption[] Options { get; }
    public ICommand OptionSelectedCommand { get; }

    private void OptionSelected(object parameter)
    {
        if (parameter is MenuOption option)
            option.Action?.Invoke();
    }
    
    private void Option1Selected()
    {
        // Implement Option 1
    }
    
    private void Option2Selected()
    {
        // Implement Option 2
    }

    private void Option3Selected()
    {
        // Implement Option 3
    }

    private void Option3Selected()
    {
        // Implement Option 3
    }

    private MenuOption[] CreateMenuOptions()
    {
        return new[]
        {
            new MenuOption { Icon = "option1.png", Text = "Option 1", Action = Option1Selected },
            new MenuOption { Icon = "option2.png", Text = "Option 2", Action = Option2Selected },
            new MenuOption { Icon = "option3.png", Text = "Option 3", Action = Option3Selected }
        }
    }

    private class MenuOption
    {
        public string Icon { get; set; }
        public string Text { get; set; }
        public Action Action { get; set; }
    }
}
 
User avatar
sfernandez
Site Admin
Posts: 2997
Joined: 22 Dec 2011, 19:20

Re: ListBox vs StackPanel of Buttons

27 Mar 2024, 20:51

Thanks for the suggestion @nadjibus, I also think it is the right approach here.

You will use an ItemsControl bound to a list of options/settings items exposing all the required information:
  • Name/Label
  • Type of value (you can use an enum here to select what to show on the ItemTemplate, or it can be its own base class with different classes for each type so you can show different editors
    (DataTemplate) depending on the type).
  • Description
  • Image...
And instead of using the SelectedItem (not available in an ItemsControl), you can just rely on the focused option to show the option's description and image.
<DataTemplate>
  <Grid x:Name="root">
    ...
    <Grid x:Name="help">...</Grid>
  </Grid>
  <DataTemplate.Triggers>
    <DataTrigger Binding="{Binding IsKeyboardFocusWithin, ElementName=root}" Value="True">
      <Setter Property="Visibility" Value="Visible" TargetName="help"/>
    </DataTrigger>
  </DataTriggers>
</DataTemplate>
Hope this helps.
 
lachfoy
Topic Author
Posts: 5
Joined: 20 Feb 2024, 06:25

Re: ListBox vs StackPanel of Buttons

04 Apr 2024, 05:41

Thanks @sfernandez and @nadjibus for the help, I appreciate it.

I think this approach will work fine for me :)

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 0 guests