在 Win Phone 8 应用程序中,PropertyChanged 事件处理程序始终为 null [英] PropertyChanged event handler always null in Win Phone 8 Application
问题描述
当我在这个简单的 Win Phone 8 应用程序(使用 VS 2012 Pro 构建 - 我拥有的)上单击添加一些东西"按钮时,什么也没有发生.为什么?
When I click the 'Add Some Thing' button on this simple Win Phone 8 application (built with VS 2012 Pro - its what I have), nothing happens. Why?
此示例代码的存储库位于 bitbucket.org 上:TestItemsControlInWinPhone8App
The repo of this example code is on bitbucket.org at: 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}"
尝试过.
Please note that ItemsControl ItemsSource="{Binding Path=Things}
has also been tried as just plain ItemsControl ItemsSource="{Binding}"
.
两者都有相同的结果;显示前五个 Thing
对象并单击添加一些事物"按钮将另一个 Thing
添加到 LotsOfThings
.
Both have the same result; the first five Thing
objects display and clicking the "Add Some Thing" button adds another Thing
to LotsOfThings
.
DataTemplate
的内容是一个 TextBlock
,实际上显示前 5 个 Thing
对象.
The contents of the DataTemplate
being a TextBlock
do, in fact, display the first 5 Thing
objects.
但是点击按钮不会更新显示,它仍然只显示原始的 5 个 Thing
对象.
But clicking the button does NOT update the display, which persists in displaying only the original 5 Thing
objects.
后面的代码(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 lotOfThings(5);
起作用,当 Page
首次显示时,5 Thing
对象被显示.
Note that in the Page
constructor this.DataContext = new LotsOfThings(5);
works and when the Page
first displays, 5 Thing
objects are displayed.
需要明确的是,不起作用是 AddSomeThing_click()
按钮处理程序的后一个调用会将另一个 Thing
添加到LotsOfThings
但只显示原来的5个Thing
对象;仅此而已,即使根据调试器在 LotsOfThings
上存在更多 Thing
对象.
To be clear, what doesn't work is that a latter call of the AddSomeThing_click()
button handler will add another Thing
to LotsOfThings
but only the original 5 Thing
objects display; nothing more, even if there are many more Thing
objects present on LotsOfThings
according to the debugger.
我注意到使用调试器时,每当 OnPropertyChanged(...)
被调用时 handler
都是 null
.这显然很重要,但我不知道为什么会发生这种情况,因为此时我已经遵循了我在网络上可以找到的所有补救帮助.
What I notice using the debugger is that whenever OnPropertyChanged(...)
is called handler
is null
. That is clearly important but I have no idea why that is happening having at this point followed all the remedial help I can find searching the web.
为什么?
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
If you need to just download the application or look at it with a better interface you can find it here: TestItemsControlInWinPhone8App
谢谢.
附注.我已经阅读并遵循了我可以在 Stackoverflow 和网络上的其他地方找到的关于传递给 OnPropertyChanged()
方法和 ItemsControl
的使用的空处理程序的所有建议我可以找到.
PS. I have read and, I think, followed all the advice I can find on Stackoverflow and elsewhere on the net about null handlers passed into OnPropertyChanged()
methods and usage of ItemsControl
that I can find.
推荐答案
Ed Plunkett 把我放在右边路径,但答案有点复杂,所以我列出了我在这个答案中学到的东西.
Ed Plunkett put me on the right path but the answer was a little more complicated so I'm laying out what I have learned in this answer.
首先,我可以使用 ObservableCollection
- Ed 是对的.但是我会失去一些我想要的 IList
功能.此外,我试图遵循 XAML Deep Dive ...(40-49 分钟),并且他们没有使用 ObservableCollection
.
First, I could have used ObservableCollection<T>
- Ed is right. But I would have lost some of the IList<T>
features I wanted. Besides I was trying to follow the practice in XAML Deep Dive ... (min 40-49) and they did not use ObservableCollection<T>
.
结果是我错误地使用了 INotifyPropertyChanged
而不是 INotifyCollectionChanged
.第二个接口有一个稍微复杂的处理程序,在关于 这个 Stackoverflow 问题的答案中记录调用 OnCollectionChanged
.
Turns out I had mistakenly used INotifyPropertyChanged
instead of INotifyCollectionChanged
. The second interface has a slightly more complicated handler documented in the answer to this Stackoverflow question about calling OnCollectionChanged
.
我在问这个问题之前的研究发现了很多方法来获得一个空事件处理程序.一种是使用拼写错误的属性名称调用处理程序(例如 OnPropertyChanged("thing")
当您应该使用 OnPropertyChanged("Thing")
因为这就是属性实际调用 - 假设您正在处理属性而不是集合.获得空事件处理程序的另一种方法是不将正确的对象绑定到正确的内容或容器控件.在这里,看看 "堆栈溢出 C# 为什么我的处理程序为空".
My research before asking this question found a bunch of ways to also get a null event handler. One was to call the handler with a misspelled property name (e.g. OnPropertyChanged("thing")
when you should use OnPropertyChanged("Thing")
because that is what the property is actually called - assuming you are dealing with properties and not collections. Another way to get a null event handler was to not bind the right object to the right content or container control. Here, take a look at "stack overflow C# why is my handler null".
为了最终确定这个问题的核心,我按照 Ed 的提示做了一些研究,我对 List
、ObservableCollection
和 INotifyPropertyChanged
之间的区别代码> 并找到了那个优秀的页面.
And to finally put a stake in the heart of this problem I did a little research in the direction of Ed's hint that I become more familiar with the difference between List<T>
, ObservableCollection<T>
and INotifyPropertyChanged
and found that excellent page.
希望有帮助.并感谢埃德;我给你的所有赞成票.
I hope that helps. And thanks Ed; all my up-votes to you.
附言我已经更新了我的测试代码的存储库以进行修复和 git tag
-ged 原始版本为 broken,固定版本为 fixed.
P.S. I have updated the repository of my test code to have the fix and git tag
-ged the original version as broken and the fixed version as fixed.
这篇关于在 Win Phone 8 应用程序中,PropertyChanged 事件处理程序始终为 null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!