AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.36k stars 2.2k forks source link

`ComboBox.SelectedItem` lost on control generated in a template #14654

Open TomEdwardsEnscape opened 7 months ago

TomEdwardsEnscape commented 7 months ago

Copy this XAML into any window and run:

<StackPanel Classes="Hello World">
    <StackPanel.Resources>
        <ControlTemplate x:Key="MyTemplate">
            <ComboBox SelectedItem="World" ItemsSource="{Binding $parent[StackPanel].Classes}"/>
        </ControlTemplate>
    </StackPanel.Resources>
    <ComboBox SelectedItem="World" ItemsSource="{Binding $parent[StackPanel].Classes}"/>
    <TemplatedControl Template="{StaticResource MyTemplate}"/>
</StackPanel>

You will see something like this:

image

The ComboBox which was created as a direct child has kept its SelectedItem, as expected. But the one generated from the template, despite having the exact same XAML declaration, has lost its selection.

ComboBox.ItemsSource is correct for both items. The bug is reproducible with any ItemsSource; I only used Classes to keep the repro case as small as possible.

Expected behavior

Both ComboBoxes should display "World" as their selected item.

Environment

TomEdwardsEnscape commented 7 months ago

Possibly the same issue: when the templated ComboBox disconnects from the logical tree, it loses its ItemsSource value and resets any selected value to null.

Gillibald commented 7 months ago

I had a deeper look at this issue and it looks like this is not easy to solve by Avalonia itself.

The moment the template is built the initial selected value is properly applied but at that moment the ItemsSource is not bound yet because there is no parent. So the selection gets reset.

I suggest moving the selected value state to some external state so you will be binding SelectedValue and ItemsSource.

TomEdwardsEnscape commented 7 months ago

We've dodged the issue by moving the affected control out of a template. It's duplicated in a few locations now, but works correctly.

Gillibald commented 7 months ago

Can we close this issue?

TomEdwardsEnscape commented 7 months ago

I don't think so. It's still a nasty bug that can strike at any time.

koustubhmoharir commented 7 months ago

I have had to dodge this issue or variants of it at multiple places, often with ugly workarounds. I think a solution to this is for SelectingItemsControl to track the state of any Binding on ItemsSource. ItemsSource may be null because the Binding has not yet produced a value, or it may be null because the control has been detached from the tree, leading to DataContext becoming null. In these cases, SelectedItem should not be cleared, or at least any TwoWay Binding on SelectedItem should be inactive. I have no idea how easy it is to implement this, but I do think this is an important issue. Currently, using a ComboBox in any non-trivial scenario (inside a TabItem, inside a template, etc) is very iffy.

koustubhmoharir commented 7 months ago

Another possible solution may be to not reset SelectedItem on a change to ItemsSource, even if the SelectedItem is not in the ItemsSource. The idea is that maintaining valid state should be the responsibility of the ViewModel, not the View. The View should not be making changes unless these changes are due to user interaction. The current behavior is that loading and unloading of the View can result in changes.

But such a change to behavior is probably a breaking change.

cboittin commented 3 weeks ago

This bug just bit me too.

The suggestion by Gillibald to bind the SelectedIndex of the ComboBox works. It does not look too dirty but feels a bit like fixing something which should just work, so i'd agree that a fix would be welcome.

DimNightStar commented 3 weeks ago

您好,您的邮件我已经收到,我会尽快给您回复。

timunie commented 3 weeks ago

@DimNightStar can you translate also to English?