StaticResource против поведения DynamicResource в Combobox.ItemsSource - PullRequest
2 голосов
/ 03 марта 2010

Я заметил разницу в поведении между статическим и динамическим ресурсом в ComboBox.ItemsSource, когда ComboBox получает визуальное дерево.

  • в статическом примере выбранный пункт назначения остается
  • в динамическом примере базовый объект получает нулевое значение

Привязка выглядит нормально, потому что, когда комбинированные списки попадают в фокус и их SelectedIndex изменяются, об изменении надлежащим образом уведомляются другой список - оба объекта реализуют INotifyProperty - и оба List являются ObservableCollections.

Это когда комбобокс с динамической привязкой выходит из фокуса, происходят странные вещи

XAML

<Window ... xmlns:me = "clr-namespace:WpfComboBoxBug">
    <Window.Resources>
        <me:ShippingList x:Key="sl" />
        <me:DestinationList x:Key="dl" />
    </Window.Resources>
    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="21" />
            <RowDefinition Height="421*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Custom:DataGrid Grid.Row="1"
            ItemsSource="{StaticResource sl}" x:Name="dg" AutoGenerateColumns="False" Grid.RowSpan="2">
        <Custom:DataGrid.Columns>
            <Custom:DataGridTextColumn Header="Reference" Binding="{Binding Reference}" />
            <Custom:DataGridTemplateColumn Header="Destination">
                <Custom:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Destination.Name}"></TextBlock>
                </DataTemplate>
                </Custom:DataGridTemplateColumn.CellTemplate>
                <Custom:DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{StaticResource dl}" SelectedItem="{Binding Destination,Mode=TwoWay}" DisplayMemberPath="Name"/>
                </DataTemplate>
                </Custom:DataGridTemplateColumn.CellEditingTemplate>
            </Custom:DataGridTemplateColumn>
        </Custom:DataGrid.Columns>
        </Custom:DataGrid>
            <Custom:DataGrid Grid.Column="1" Grid.Row="1" ItemsSource="{StaticResource sl}" x:Name="dg2" AutoGenerateColumns="False" Grid.RowSpan="2">
            <Custom:DataGrid.Columns>
                <Custom:DataGridTextColumn Header="Reference" Binding="{Binding Reference}" />
                <Custom:DataGridTemplateColumn Header="Destination">
                    <Custom:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Destination.Name}"></TextBlock>
                        </DataTemplate>
                    </Custom:DataGridTemplateColumn.CellTemplate>
                    <Custom:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{DynamicResource dynamicdl}" SelectedItem="{Binding Destination,Mode=TwoWay}" DisplayMemberPath="Name"/>
                        </DataTemplate>
                    </Custom:DataGridTemplateColumn.CellEditingTemplate>
                </Custom:DataGridTemplateColumn>
            </Custom:DataGrid.Columns>
        </Custom:DataGrid>
        <TextBox Height="23"  Name="textBox1" VerticalAlignment="Top" Text="Static" />
        <TextBox Height="23"  Name="textBox2"  VerticalAlignment="Top" Text="Dynamic" Grid.Column="2" />
    </Grid>
</Window>

CS

using System;
/* snip */

namespace WpfComboBoxBug
{
    /// <summary>
    /// Logique d'interaction pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            ShippingList sl;
            this.InitializeComponent();
            sl = this.Resources["sl"] as ShippingList;

            ResourceDictionary rd = new ResourceDictionary();
            rd.Add("dynamicdl", this.FindResource("dl"));

            dg2.Resources = rd;


            dg.ItemsSource = CollectionViewSource.GetDefaultView(sl);
            dg2.ItemsSource = CollectionViewSource.GetDefaultView(sl);
        }
    }
}

полный исходный код по адресу: http://dl.free.fr/eI1VtkaB8 (VS 2008 SP1, .NET 3.5 SP1)

Я ожидал, что динамический ресурс будет вести себя как статический ресурс в этом случае, потому что я инициализирую его один раз в начале.

  • Я нашел ошибку здесь?
  • Если это не так, как бы вы объяснили разницу?

1 Ответ

3 голосов
/ 03 марта 2010

Я должен был бы взглянуть на ваш код, чтобы убедиться, но я бы предположил, что ваш ComboBox имеет свой SelectedItem или SelectedValue, двусторонне привязанный к свойству.

При использовании StaticResource ссылка на ресурс разрешается во время загрузки XAML. Когда вы используете DynamicResource, ссылка на ресурс разрешается позже. Так что, вероятно, происходит то, что ваш ComboBox запускается без элементов, что приводит к тому, что его SelectedItem и SelectedValue обнуляются. При двусторонней привязке свойство обновляется с этим значением.

Лично я считаю, что неспособность ComboBox изящно справиться с этой ситуацией является ошибкой в ​​дизайне ComboBox, а не ошибкой реализации.

Для своих собственных проектов я часто использую расширение ComboBox и ListBox, которое я создал для решения этой проблемы: у меня есть дополнительные свойства, которые я могу использовать вместо SelectedValue и SelectedItems. Мои новые свойства принимают любое значение, пока не будет установлен ItemsSource, после чего он будет синхронизирован с SelectedValue или SelectedItem.

Вы можете использовать подобную технику или просто всегда проверять, чтобы ItemsSource был связан / инициализирован перед SelectedValue или SelectedItem.

Обновление

Когда элемент управления удаляется из визуального дерева, все происходит в обратном порядке: ItemsSource очищается немедленно из-за изменения в происхождении, затем DataContext очищается. В промежуточный период ComboBox имеет нулевой SelectedItem, который распространяется на связанное свойство.

Улучшенный класс ComboBox или ListBox с дополнительными свойствами SelectedItem и SelectedValue также может решить эту проблему: он должен синхронизировать SelectedItem / SelecteValue с пользовательскими свойствами, когда ItemsSource не равен NULL, и разъединять их, когда ItemsSource равен NULL.

...