【问题标题】:PropertyChanged event handler always null in Win Phone 8 ApplicationWin Phone 8 应用程序中的 PropertyChanged 事件处理程序始终为空
【发布时间】:2016-09-16 17:36:02
【问题描述】:

当我在这个简单的 Win Phone 8 应用程序(使用 VS 2012 Pro 构建 - 我所拥有的)上单击“添加一些东西”按钮时,什么都没有发生。为什么?

此示例代码的 repo 位于 bitbucket.org 上: TestItemsControlInWinPhone8App

MainPage.xaml 包含:

<phone:PhoneApplicationPage
x:Class="TestItemsControlInWinPhone8App.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!--TitlePanel contains the name of the application and page title-->
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
        <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Button x:Name="AddSomeThing"
                    Content="Add Some Thing"
                    Grid.Row="0"
                    Click="AddSomeThing_Click"/>
            <ItemsControl x:Name="LotsOfThingsItemsControl"
                          Grid.Row="1"
                          ItemsSource="{Binding Mode=OneWay}"
                          FontSize="{StaticResource PhoneFontSizeSmall}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Height="Auto" Width="Auto"
                          VerticalAlignment="Center"
                          HorizontalAlignment="Center"
                          Background="Orange">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <TextBlock Grid.Row="0" Text="{Binding Path=Id, Mode=OneWay}"/>
                            <TextBlock Grid.Row="1"
                                       Text="------------------------"/>
                        </Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Grid>
    </StackPanel>

    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

    </Grid>
</Grid>

</phone:PhoneApplicationPage>

请注意,ItemsControl ItemsSource="{Binding Path=Things} 也被尝试为普通的ItemsControl ItemsSource="{Binding}"

两者的结果相同;前五个Thing 对象显示并单击“添加一些东西”按钮将另一个Thing 添加到LotsOfThings

DataTemplate 的内容是TextBlock,实际上是显示前 5 个Thing 对象。

但单击按钮不会更新显示,它仍然只显示原始的 5 个Thing 对象。

(MainPage.xaml.cs) 后面的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using     TestItemsControlInWinPhone8App.Resources;

namespace TestItemsControlInWinPhone8App
{
public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();

        this.DataContext = new LotsOfThings(5);
    }

    private void AddSomeThing_Click(object sender, RoutedEventArgs e)
    {
        LotsOfThings lot = this.DataContext as LotsOfThings;

        lot.Add(new Thing());
    }
}
}

请注意,在Page 构造函数中this.DataContext = new LotsOfThings(5); 有效,当Page 第一次显示时,会显示5 个Thing 对象。

需要明确的是,什么不起作用AddSomeThing_click() 按钮处理程序的后续调用会将另一个Thing 添加到LotsOfThings原来的5个Thing对象显示;仅此而已,即使根据调试器在LotsOfThings 上存在更多Thing 对象。

我在使用调试器时注意到的是,每当OnPropertyChanged(...) 被称为handler 时,就是null。这显然很重要,但我不知道为什么会发生这种情况,此时我已经遵循了我在网上可以找到的所有补救帮助。

为什么?

Thing.cs的内容:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestItemsControlInWinPhone8App
{
public class Thing : INotifyPropertyChanged
{
    private string _Id = Guid.NewGuid().ToString();
    public string Id
    {
        get
        {
            return _Id;
        }
        set { }
    }

    #region Constructor
    public Thing()
    {
        this.OnPropertyChanged( "Id");
    }
    #endregion

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string pPropertyName)
    {
        System.ComponentModel.PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(pPropertyName));
        }
    }
    #endregion
}
}

LotsOfThings.cs的内容:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestItemsControlInWinPhone8App
{
class LotsOfThings : INotifyPropertyChanged, IList<Thing>
{
    private List<Thing> _things = new List<Thing>();
    public List<Thing> Things
    {
        get {
            return _things;
        }
        set { }
    }

    public LotsOfThings( int pNumberOfThings)
    {
        for( int x = 0; x < pNumberOfThings; x++){
            this.Add( new Thing());
        }
        OnPropertyChanged("Things");
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string pName)
    {
        System.ComponentModel.PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(pName));
        }
    }
    #endregion

    #region IList<T> methods
    public int IndexOf(Thing item)
    {
        return _things.IndexOf(item);
    }

    public void Insert(int index, Thing item)
    {
        _things.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
        _things.RemoveAt(index);
    }

    public Thing this[int index]
    {
        get
        {
            return _things[index];
        }
        set
        {
            _things[index] = value;
        }
    }

    public void Add(Thing item)
    {
        _things.Add(item);

        OnPropertyChanged("Things");
    }

    public void Clear()
    {
        _things.Clear();
    }

    public bool Contains(Thing item)
    {
        return _things.Contains(item);
    }

    public void CopyTo(Thing[] array, int arrayIndex)
    {
        _things.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return _things.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(Thing item)
    {
        return _things.Remove(item);
    }

    public IEnumerator<Thing> GetEnumerator()
    {
        return _things.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _things.GetEnumerator();
    }
    #endregion
}
}

如果您只需要下载应用程序或使用更好的界面查看它,您可以在这里找到它: TestItemsControlInWinPhone8App

谢谢。

PS。我已经阅读并遵循了我在 Stackoverflow 和网络上其他地方可以找到的所有建议,这些建议是关于传递给 OnPropertyChanged() 方法的空处理程序以及我可以找到的 ItemsControl 的用法。

【问题讨论】:

    标签: c# .net wpf xaml windows-phone-8


    【解决方案1】:

    _things 必须是 ObservableCollection&lt;Thing&gt;List&lt;T&gt; 没有实现 INotifyCollectionChanged,因此在其内容更改时不会发出通知。 ObservableCollection&lt;Thing&gt; 确实如此,这将使 UI 知道何时需要将项目添加到列表中。

    简单、容易、标准的做事方式是将ObservableCollection 公开为属性。如果您将整个收藏替换为新收藏,请提出PropertyChanged("Things");添加/删除项目时,ObservableCollection 将引发适当的事件,而您无需执行任何操作。阅读您的代码的有经验的 WPF 人员会知道他们在看什么。

    要让它按照您的想法工作,您必须在更改 Things 集合的方法中调用 OnPropertyChanged("Things");我还没有测试过,但我认为它应该可以工作(它可能不起作用的原因是 Things 返回的实际集合对象没有改变;控件可能会看到并选择不更新;但正如我所说我没有测试过)。然后您可以将Things 绑定到控件上的ItemsSource,也许它应该可以工作。但是你可以让其他类更改Things,因为它是公开的。试图追查所有松散的结局将是一团糟。更容易使用ObservableCollection

    如果你想将LotsOfThings 本身绑定到ItemsSource,你必须在LotsOfThings 上实现INotifyCollectionChanged,这真的很麻烦手动重写所有这些,我不是确定它给你带来了什么。您可以将LotsOfThings 设为ObservableCollection&lt;Thing&gt; 的子类——从一个完整且防弹的INotifyCollectionChanged 实现开始,免费。

    【讨论】:

    • 如果我让很多东西继承 INotifyPropertyChanged 还不够吗?这似乎是 Laurent 和 Jaime 在他们的 MVA 教程“XAML Deep Dive ...”(channel9.msdn.com/Series/xamldeepdive/05) 中所做的,参见第 40-49 分钟。谢谢!
    • 显然这还不够;如果它足够了,你就不会在这里询问如何让它工作。更新我的答案。
    • 艾德,谢谢。我很困惑,因为我看到的这个平台的例子没有说明 IObservable 是必要的。数据绑定不应该负责这个订阅过程吗?
    • @rfreytag 那不是文档,那是第 9 频道。我在工作,所以我不能看视频,但如果他们漏掉了,他们应该尽快从互联网上删除该视频。我一直在考虑编写自己的教程,题为如何开始使用 WPF 而不会完全发疯并用步枪爬上钟楼,现在我又近了一步。
    • @rfreytag 我认为 XAML UI 不会关注IObservable。具有 WPF 视图模型的两大类是 INotifyPropertyChanged 用于 vm 通知 UI 其属性值的更改,INotifyCollectionChanged 用于集合属性以通知 UI 项目被添加到集合中/从集合中删除。跨度>
    【解决方案2】:

    Ed Plunkett 让我走上了正确的道路,但答案有点复杂,所以我在这个答案中列出了我学到的东西。

    首先,我可以使用ObservableCollection&lt;T&gt; - Ed 是对的。但我会失去一些我想要的IList&lt;T&gt; 功能。此外,我试图遵循XAML Deep Dive ... (min 40-49) 的做法,但他们没有使用ObservableCollection&lt;T&gt;

    原来我错误地使用了INotifyPropertyChanged 而不是INotifyCollectionChanged。第二个接口有一个稍微复杂一点的处理程序,记录在关于calling OnCollectionChanged 的 Stackoverflow 问题的答案中。

    在问这个问题之前,我的研究发现了很多方法来获取空事件处理程序。一种是使用拼写错误的属性名称调用处理程序(例如,OnPropertyChanged("thing") 当您应该使用 OnPropertyChanged("Thing") 因为这是实际调用的属性 - 假设您正在处理属性而不是集合。另一种获取 null 事件的方法处理程序是没有将正确的对象绑定到正确的内容或容器控件。这里,看看"stack overflow C# why is my handler null"

    为了最终确定这个问题的核心,我按照 Ed 的提示进行了一些研究,让我更加熟悉 difference between List&lt;T&gt;, ObservableCollection&lt;T&gt; and INotifyPropertyChanged,并发现了这个出色的页面。

    我希望这会有所帮助。并感谢埃德;我所有的赞成票给你。

    附:我已经更新了我的测试代码的存储库以进行修复,git tag-ged original version as broken and the fixed version as fixed

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-03
      • 2017-12-12
      • 1970-01-01
      • 2010-12-03
      • 2013-07-17
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多