【问题标题】:Why DependencyProperty.UnsetValue is being passed into Equals()为什么 DependencyProperty.UnsetValue 被传递到 Equals()
【发布时间】:2015-09-09 22:28:31
【问题描述】:

我正在开发一个简单的 WPF 应用程序。目前,该应用程序仅管理 ComboBox 中的视频游戏列表。游戏列表与 XML 文件进行序列化。

目前被破坏的功能是能够从 ComboBox 中选择一个游戏,使其成为要管理的“活动”游戏。活动游戏将存储为应用程序设置。为此,我使用双向数据绑定并将 ComboBox 中的 SelectedItem 绑定到 ViewModel 中的 SelectedGame。

当我运行应用程序并从 ComboBox 中选择一个项目时,我在 Game.Equals(Game other) 方法上得到一个空引用异常。在调试时,我看到 DependencyProperty.UnsetValue 作为参数传递给覆盖的 Game.Equals(object obj) 方法,这导致 obj as Game 使 obj 为 null。

我不明白为什么当我所做的只是从 ComboBox 值中选择一个项目时,为什么首先调用 Equals,所以我猜它是 WPF 原生的东西。在前往 Equals 方法之前,该代码似乎没有遇到任何其他断点,所以我什至不确定如何调试该问题。我也不明白 DependencyProperty.UnsetValue 来自哪里。我完全迷失了这里,希望有任何见解,包括我如何进一步调试。

编辑: 正如 Glen 所暗示的,Equals 必须由 WPF 的某些底层组件调用。至少目前,我的解决方案是简单地向我的 Equals 覆盖添加一个空检查。

模型类

public class Game : IEntity, IEquatable<Game>
{
    [XmlElement("Name")]
    public string Name { get; set; }

    [XmlElement("ExecutablePath")]
    public string ExecutablePath { get; set; } 

    public Game(string name, string executablePath)
    {
        Name = name;
        ExecutablePath = executablePath;
    }

    private Game() { } // Required for XML serialization.

    public bool Equals(Game other)
    {
        return Name.EqualsIgnoreCase(other.Name);
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Game);
    }
}

视图模型

public class GamesViewModel
{
    // The GameRepository retrieves the game collection from the XML file.
    private readonly GameRepository _gameRepository;

    public ObservableCollection<Game> Games
    {
        get { return new ObservableCollection<Game>(_gameRepository.Items); }
    }

    public Game SelectedGame
    {
        get { return Settings.Default.ActiveGame; }
        set
        {
            if (!Settings.Default.ActiveGame.Equals(value))
            {
                Settings.Default.ActiveGame = value;
                Settings.Default.Save();
            }
        }
    }

    public GamesViewModel()
    {
        _gameRepository = RepositorySingletons.GameRepository;
    }
}

查看

<UserControl x:Class="ENBOrganizer.UI.Views.GamesView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ENBOrganizer.UI.ViewModels" 
             mc:Ignorable="d" >
    <UserControl.DataContext>
        <local:GamesViewModel />
    </UserControl.DataContext>
    <Grid>
        <ComboBox Name="GamesComboBox" ItemsSource="{Binding Games}" SelectedItem="{Binding SelectedGame}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                        <TextBlock Text="{Binding Name}" VerticalAlignment="Center" Padding="5,0,0,0" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </Grid>
</UserControl>

【问题讨论】:

  • 您的 Equals 方法非常懒惰。您应该能够在不抛出 NRE 的情况下处理此问题。
  • 我可以接受空检查。我只是想了解为什么首先要传入 null。

标签: c# wpf mvvm combobox


【解决方案1】:

SelectedItem 从视图中更改为 ComboBox 时,您会发现源属性 (SelectedGame) 可能会设置两次,一次设置为 null(因为上一项不再是selected),然后为新选择的项目执行一次。

如果您的代码依赖于不为空的对象,通常处理这种情况的方式是使用简单的空值检查:

if (value != null)
{
    // Code that relies on value not being null
}

【讨论】:

  • 此时,我真的没有专门依赖空检查的代码。将来我可能需要它,但出于早期开发目的,我在 ComboBox 中只有 2 款游戏,所以我的选择永远不会为空。您可能知道为什么在我所做的只是选择 ComboBox 中的一个项目时调用 Equals 方法吗?
  • 这是我的第一个猜测。我在 SelectedGame 属性的设置器上设置了一个断点,但 Equals 方法在设置器被命中之前就抛出了异常。
  • Object.Equals 在 .NET 中用于幕后的许多不同的事情,并且应该非常小心地覆盖该方法。见this guidance
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多