Silverlight UI不会从PropertyChanged事件中取消订阅 [英] Silverlight UI not unsubscribing from PropertyChanged events

查看:124
本文介绍了Silverlight UI不会从PropertyChanged事件中取消订阅的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个数据绑定UI。当我更改基础数据时,UI更新正常,但控件似乎保持订阅我的PropertyChanged事件。



我有以下内容:




  • 一个ItemsControl绑定到ListA


  • 每个项目ListA包含一个子列表
    使用另一个
    显示的ListB ItemsControl


  • 每个项目ListB都使用
    TextBox绑定到字符串属性
    通过INotifyPropertyChanged




这是XAML:

 < UserControl x:Class =SilverlightBindingTest.MainPage
xmlns =http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x =http://schemas.microsoft.com/winfx/2006/xaml>

< UserControl.Resources>

< DataTemplate x:Key =DataTempl>
< TextBox Text ={Binding Value,Mode = TwoWay}/>
< / DataTemplate>

< DataTemplate x:Key =PageTempl>
< ItemsControl ItemsSource ={Binding Datas}ItemTemplate ={StaticResource DataTempl}>< / ItemsControl>
< / DataTemplate>

< /UserControl.Resources>
< Grid x:Name =LayoutRootBackground =White>
< StackPanel>
< ItemsControl ItemsSource ={Binding Pages}ItemTemplate ={StaticResource PageTempl}>< / ItemsControl>
< Button Content =交换列表Click =ButtonClick1>< / Button>
< Button Content =更改基础数据Click =ButtonClick2>< / Button>
< / StackPanel>
< / Grid>
< / UserControl>

这是代码:

 使用System.ComponentModel; 
使用System.Diagnostics;
使用System.Windows;
使用System.Collections.Generic;

命名空间SilverlightBindingTest
{
public partial class MainPage:INotifyPropertyChanged
{
private List< DataGroupClass> pages1 = new List< DataGroupClass>();
private List< DataGroupClass> pages2 = new List< DataGroupClass>();

public MainPage()
{
InitializeComponent();
列表< DataClass> group = new List< DataClass>();
group.Add(new DataClass(value 1));
group.Add(new DataClass(value 2));
group.Add(new DataClass(value 3));
pages1.Add(new DataGroupClass(group));

group = new List< DataClass>();
group.Add(new DataClass(value 4));
group.Add(new DataClass(value 5));
group.Add(new DataClass(value 6));
pages2.Add(new DataGroupClass(group));
DataContext = this;
}

私人列表< DataGroupClass> pages = new List< DataGroupClass>();
public List< DataGroupClass> Pages
{
get {return pages; }
set
{
pages = value;
PropertyChangedEventHandler h = PropertyChanged;
if(h!= null)
{
h(this,new PropertyChangedEventArgs(Pages));
}
}
}

private void ButtonClick1(object sender,RoutedEventArgs e)
{
Debug.WriteLine(--- ---------------- \\\
);
if(Pages == pages1)
{
Pages = pages2;
}
else
{
Pages = pages1;
}
}

私人bool切换;
private void ButtonClick2(object sender,RoutedEventArgs e)
{
if(toggle)
{
pages1 [0] .Datas [0] .Value =tim ;
pages2 [0] .Datas [0] .Value =lajos;
}
else
{
pages1 [0] .Datas [0] .Value =joe;
pages2 [0] .Datas [0] .Value =james;
}
toggle =!toggle;
}

#region INotifyPropertyChanged成员

public event PropertyChangedEventHandler PropertyChanged;

#endregion
}

public class DataClass:INotifyPropertyChanged
{
private string value;
public string Value
{
get
{
Debug.WriteLine(Get Value:+ value);
返回值;
}
set
{
Debug.WriteLine(Set Value:+ this.value +=>+ value);
this.value = value;
PropertyChangedEventHandler h = PropertyChanged;
if(h!= null)
{
h(this,new PropertyChangedEventArgs(Value));
}
}
}

public DataClass(s​​tring value)
{
Value = value;
}

public event PropertyChangedEventHandler PropertyChanged;
}

public class DataGroupClass
{
private List< DataClass>数据;
public List< DataClass>数据
{
get
{
Debug.WriteLine(Get Datas);
返回数据;
}
set
{
Debug.WriteLine(Set Datas);
datas = value;
}
}

public DataGroupClass(List< DataClass> datas)
{
数据=数据;
}
}
}

这是跟踪输出:



点击交换列表十次(全部可以):

  ------------------- 

获取数据
获取值:值1
获取值:值2
获取值:值3
-------------------

获取数据
获取值:值4
获取值:值5
获取值:值6
-------------------

获取数据
获取值:值1
获取值:值2
获取值:值3
------------------ -

获取数据
获取值:值4
获取值:值5
获取值:值6
-------- -----------

获取数据
获取值:值1
获取值:值2
获取值:值3
-------------------

获取数据
获取值:值4
获取值:值5
获取值:值6
-------------------

获取数据
获取值:值1
获取值:值2
获取Val ue:value 3
-------------------

获取数据
获取值:值4
获取值:值5
获取值:值6
-------------------

获取数据
获取值:值1
获取值:值2
获取值:值3
-------------------

获取数据
获取值:值4
获取值:值5
获取值:值6
------------ - $ -

获取数据
获取值:值1
获取值:值2
获取值:值3

点击更改基本数据(查看多个电话获取值):

  ------------------- 

获取数据
获取值:值1
获取值:值2
获取值:值3
获取数据
设置值:值1 => joe
获取值:joe
获取值:joe
获取值:joe
获取值:joe
获取值:joe
获取值:joe
获取数据
设置值:值4 => james
获取值:james
获取值:james
获取值:james
获取值:james
获取值:james

我假设我错过了一些东西,但是什么?



任何帮助,非常感谢!

解决方案

不,你没有错过任何东西。当您按更改基本数据时,还有一些UI元素在垃圾回收器中未被收集。 UI元素仍然依赖于它们的绑定表达式,这又表示了属性更改的事件。



最终,这些未使用的UI元素将被收集,并将停止收听财产变更事件。



您可以证明这一点(所有,虽然我不建议你做这个真正的代码),放置 GC.Collect ()在你的button1点击代码的末尾。通过强制收集,您将看到您从不会读取 Value 属性。


I have a data bound UI. When I change the underlying data the UI updates fine but the controls appear to remain subscribed to my PropertyChanged events.

I have the following:

  • An ItemsControl bound to ListA

  • Each item ListA contains a sub list ListB that is displayed using another ItemsControl

  • Each item ListB is displayed using a TextBox bound to a string property via INotifyPropertyChanged

Here is the XAML:

<UserControl x:Class="SilverlightBindingTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <UserControl.Resources>

        <DataTemplate x:Key="DataTempl" >
            <TextBox Text="{Binding Value, Mode=TwoWay}"/>
        </DataTemplate>

        <DataTemplate x:Key="PageTempl" >
            <ItemsControl ItemsSource="{Binding Datas}" ItemTemplate="{StaticResource DataTempl}"></ItemsControl>
        </DataTemplate>

    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel>
            <ItemsControl ItemsSource="{Binding Pages}" ItemTemplate="{StaticResource PageTempl}"></ItemsControl>
            <Button Content="Swap list" Click="ButtonClick1"></Button>
            <Button Content="Change base data" Click="ButtonClick2"></Button>
        </StackPanel>
    </Grid>
</UserControl>

Here is the code:

using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Collections.Generic;

namespace SilverlightBindingTest
{
   public partial class MainPage : INotifyPropertyChanged
   {
      private List<DataGroupClass> pages1 = new List<DataGroupClass>();
      private List<DataGroupClass> pages2 = new List<DataGroupClass>();

      public MainPage()
      {
         InitializeComponent();
         List<DataClass> group = new List<DataClass>();
         group.Add(new DataClass("value 1"));
         group.Add(new DataClass("value 2"));
         group.Add(new DataClass("value 3"));
         pages1.Add(new DataGroupClass(group));

         group = new List<DataClass>();
         group.Add(new DataClass("value 4"));
         group.Add(new DataClass("value 5"));
         group.Add(new DataClass("value 6"));
         pages2.Add(new DataGroupClass(group));
         DataContext = this;
      }

      private List<DataGroupClass> pages = new List<DataGroupClass>();
      public List<DataGroupClass> Pages
      {
         get { return pages; }
         set
         {
            pages = value;
            PropertyChangedEventHandler h = PropertyChanged;
            if (h != null)
            {
               h(this, new PropertyChangedEventArgs("Pages"));
            }
         }
      }

      private void ButtonClick1(object sender, RoutedEventArgs e)
      {
         Debug.WriteLine("-------------------\n");
         if (Pages == pages1)
         {
            Pages = pages2;
         }
         else
         {
            Pages = pages1;
         }
      }

      private bool toggle;
      private void ButtonClick2(object sender, RoutedEventArgs e)
      {
         if (toggle)
         {
            pages1[0].Datas[0].Value = "tim";
            pages2[0].Datas[0].Value = "lajos";
         }
         else
         {
            pages1[0].Datas[0].Value = "joe";
            pages2[0].Datas[0].Value = "james";
         }
         toggle = !toggle;
      }

      #region INotifyPropertyChanged Members

      public event PropertyChangedEventHandler PropertyChanged;

      #endregion
   }

   public class DataClass : INotifyPropertyChanged
   {
      private string value;
      public string Value
      {
         get
         {
            Debug.WriteLine("Get Value:" + value);
            return value;
         }
         set
         {
            Debug.WriteLine("Set Value: " + this.value + " => " + value);
            this.value = value;
            PropertyChangedEventHandler h = PropertyChanged;
            if (h != null)
            {
               h(this, new PropertyChangedEventArgs("Value"));
            }
         }
      }

      public DataClass(string value)
      {         
         Value = value;
      }

      public event PropertyChangedEventHandler PropertyChanged;
   }

   public class DataGroupClass
   {
      private List<DataClass> datas;
      public List<DataClass> Datas
      {
         get
         {
            Debug.WriteLine("Get Datas");
            return datas;
         }
         set
         {
            Debug.WriteLine("Set Datas");
            datas = value;
         }
      }

      public DataGroupClass(List<DataClass> datas)
      {         
         Datas = datas;
      }
   }
}

Here is the trace output:

Click "Swap list" ten times (all looks OK):

 -------------------

 Get Datas
 Get Value:value 1
 Get Value:value 2
 Get Value:value 3
-------------------

 Get Datas
 Get Value:value 4
 Get Value:value 5
 Get Value:value 6
-------------------

 Get Datas
 Get Value:value 1
 Get Value:value 2
 Get Value:value 3
-------------------

 Get Datas
 Get Value:value 4
 Get Value:value 5
 Get Value:value 6
 -------------------

 Get Datas
 Get Value:value 1
 Get Value:value 2
 Get Value:value 3
 -------------------

 Get Datas
 Get Value:value 4
 Get Value:value 5
 Get Value:value 6
 -------------------

 Get Datas
 Get Value:value 1
 Get Value:value 2
 Get Value:value 3
 -------------------

 Get Datas
 Get Value:value 4
 Get Value:value 5
 Get Value:value 6
 -------------------

 Get Datas
 Get Value:value 1
 Get Value:value 2
 Get Value:value 3
 -------------------

 Get Datas
 Get Value:value 4
 Get Value:value 5
 Get Value:value 6
 -------------------

 Get Datas
 Get Value:value 1
 Get Value:value 2
 Get Value:value 3

Click "Change base data" (See multiple calls into Get Value):

    -------------------

    Get Datas 
    Get Value:value 1 
    Get Value:value 2 
    Get Value:value 3 
    Get Datas 
    Set Value: value 1 => joe 
    Get Value:joe  
    Get Value:joe 
    Get Value:joe 
    Get Value:joe 
    Get Value:joe 
    Get Value:joe 
    Get Datas 
    Set Value: value 4 => james 
    Get Value:james 
    Get Value:james 
    Get Value:james 
    Get Value:james 
    Get Value:james 

I assume I have missed something out but what?

Any help greatly appreciated!

解决方案

Nope you haven't missed something out. There are still some UI elements that remain uncollected by the garbage collector at the time you press "change base data". The UI elements still hold on to their binding expression which in turn holds on the property changed event.

Eventually those unused UI elements will be collected and will cease to listen to the property changed events.

You can prove this (all though I certainly wouldn't recommend that you do this real code) by placing GC.Collect() at the end of your button1 click code. By forcing the collection you'll see you never get more than one read of the Value property.

这篇关于Silverlight UI不会从PropertyChanged事件中取消订阅的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆