代码之家  ›  专栏  ›  技术社区  ›  Johan Buret

ComboBox.itemsSource上的StaticResource与DynamicResource行为

  •  2
  • Johan Buret  · 技术社区  · 16 年前

    我注意到当ComboBox从可视化树中取出时,comboBox.itemssource上的静态资源和动态资源之间的行为差异。

    • 在静态示例中,所选目标仍保留
    • 在动态示例中,基础对象得到一个空值

    绑定似乎可以,因为当组合框聚焦并更改其SelectedIndex时,更改会正确通知到其他列表-两个对象都实现InotifyProperty-并且两个列表都是可观察的集合。

    当动态绑定组合框失去焦点时,就会发生奇怪的事情。

    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>
    

    反恐精英

    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 (与2008年SP1相比,.NET 3.5 SP1)

    在本例中,我希望动态资源的行为类似于静态资源,因为我在开始时对其进行了初始化。

    • 我在这里发现一个虫子了吗?
    • 如果不是这样的话,你会怎么解释区别呢?
    1 回复  |  直到 16 年前
        1
  •  3
  •   Ray Burns    16 年前

    我必须查看您的代码才能确定,但我猜您的组合框将其SelectedItem或SelectedValue双向绑定到属性。

    使用staticresource时,资源引用将在XAML加载时解析。使用动态资源时,资源引用稍后会得到解决。因此,可能发生的情况是,组合框启动时没有任何项,这将强制其selectedItem和selectedValue为空。双向绑定使属性使用此值进行更新。

    我个人认为ComboBox无法优雅地处理这种情况是ComboBox设计中的一个bug,而不是一个实现bug。

    对于我自己的项目,我经常使用我创建的组合框和列表框增强功能来解决这个问题:我可以使用其他属性代替selectedValue和selecteditems。在设置itemssource之前,我的新属性接受任何值,此后它将与selectedvalue或selecteditem保持同步。

    您可以使用类似的技术,或者始终确保itemssource在selectedValue或selectedItem之前已绑定/初始化。

    更新

    当从可视化树中移除控件时,一切都将以相反的方式发生:由于父级更改,itemssource立即被清除,然后DataContext被清除。在过渡期间,ComboBox有一个空的SelectedItem,它将传播到绑定属性。

    带有其他SelectedItem和SelectedValue属性的增强型组合框或列表框类也可以解决此问题:当itemssource非空时,它应使SelectedItem/SelecteValue与自定义属性保持同步,并在itemssource为空时将它们分离。