【问题标题】:ComboBox with IEnumerable<Brush> as ItemsSource and SelectedItem exception以 IEnumerable<Brush> 作为 ItemsSource 和 SelectedItem 异常的 ComboBox
【发布时间】:2010-12-08 09:41:50
【问题描述】:

我遇到了使用IEnumerable&lt;Brush&gt; 作为 ItemsSource 的 ComboBox 的问题;问题出在我(以编程方式)尝试设置 SelectedItem 时。这是描述问题的代码:

private readonly List<Brush> colors_;
private Brush white_;

ViewModelConstructor()
{
    colors_ = (from p in brushes_type.GetProperties()
               select (Brush)converter.ConvertFromString(p.Name)).ToList();

    white_ = colors_.Single(b => b.ToString() == "#FFFFFFFF");
}

public IEnumerable<Brush> Colors
{
    get { return colors_; }
}

public Brush White
{
    get { return white_; }
    set
    {
        if (white_ != value)
            white_ = value;
    }
}

这里是 xaml 代码:

<ComboBox ItemsSource="{Binding Path=Colors}"
          SelectedItem="{Binding Path=White}">

    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">

                <Border BorderThickness="1"
                        BorderBrush="Black"
                        Width="20"
                        Height="12"
                        SnapsToDevicePixels="True"
                        Margin="0,0,4,0">

                    <Border Background="{Binding}"/>

                </Border>

                <TextBlock Text="{Binding}"/>

            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

在调用 White 属性的 get 后,我​​得到一个异常:无法在对象“#FFFFFFFF”上设置属性,因为它处于只读状态。如果我将 White (white_) 保留为 null,则一切正常。

【问题讨论】:

标签: c# wpf exception mvvm combobox


【解决方案1】:

使用 Reflector 我已经追踪到问题出现的位置。在Selector.ItemSetIsSelected 中,它采用新的SelectedItem 并执行以下操作:

  • 如果元素的容器DependencyObject,它会将容器上的IsSelectedProperty设置为true
  • 否则,如果 元素DependencyObject,它会将 元素 上的 IsSelectedProperty 设置为 true

第二部分是失败的地方,您选择的Brush 对象是只读的。因为Selector.ItemSetIsSelected 在这种情况下有些损坏,所以您有两种选择。

选项 1,您只需在 Converter 返回的 Brush 对象上调用 .Clone()。

colors_ = (from p in typeof(Brushes).GetProperties()
    select ((Brush)converter.ConvertFromString(p.Name)).Clone()).ToList();

编辑:您应该选择选项 1...选项 2 是解决问题的冗长方法

选项 2,您可以将 Brush 对象包装到另一个对象中:

public class BrushWrapper
{
    public Brush Brush { get; set; }
}

然后更新数据模板路径:

<Border Background="{Binding Path=Brush}" />

<TextBlock Text="{Binding Path=Brush}" />

最后,你更新ViewModel

private readonly List<BrushWrapper> colors_;
private BrushWrapper white_;

public ColorViewModel()
{
    colors_ = (from p in typeof(Brushes).GetProperties()
               select new BrushWrapper {
                   Brush = (Brush)converter.ConvertFromString(p.Name)
               }).ToList();

    white_ = colors_.Single(b => b.Brush.ToString() == "#FFFFFFFF");
}

public List<BrushWrapper> Colors
{
    get { return colors_; }
}

public BrushWrapper White
{
    get { return white_; }
    set
    {
        if (white_ != value)
            white_ = value;
    }
}

【讨论】:

  • 没问题。我强烈推荐使用 Reflector,因为它对 WPF 的异常有很大帮助。
  • WPF 有时可能是反常的。 :)
【解决方案2】:

这里只是在黑暗中拍摄(目前不能尝试这个),但是绑定 SelectedValue 而不是 SelectedItem 怎么样?

【讨论】:

  • 奇数..正如六个字母变量所问:异常的调用堆栈可能会有所帮助。
  • 是的,这很奇怪...我已经建立了调用堆栈,但是每一行(除了一行)都来自框架。它不仅是组合框,还有列表框(我认为它与任何 ItemsControl 相同)。
猜你喜欢
  • 1970-01-01
  • 2011-07-11
  • 2013-10-02
  • 2014-07-27
  • 1970-01-01
  • 2021-09-26
  • 2016-03-12
  • 1970-01-01
  • 2014-07-21
相关资源
最近更新 更多