none
ObservableCollection 问题 RRS feed

  • 问题

  •  在很多时候用ObservableCollection<T>就直接刷新整个ObservableCollection<T>.
    但奇怪的事为什么无法触发改变事件.通知绑定的控件
    1 public partial class TestDatagrid : UserControl  
    2     {  
    3         public TestDatagrid()  
    4         {  
    5             InitializeComponent();  
    6             listpeople = new ObservableCollection<people>();  
    7             DispatcherTimer timer = new DispatcherTimer();  
    8             timer.Interval = TimeSpan.FromSeconds(2);  
    9             timer.Tick += new EventHandler(timer_Tick);  
    10             timer.Start();  
    11         }  
    12  
    13         void timer_Tick(object sender, EventArgs e)  
    14         {  
    15             WebClient webclient = new WebClient();  
    16             webclient.OpenReadCompleted += new OpenReadCompletedEventHandler(webclient_OpenReadCompleted);  
    17             webclient.OpenReadAsync(new Uri("ITMS.xml", UriKind.Relative));  
    18               
    19         }  
    20  
    21         void webclient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)  
    22         {  
    23             XElement xml = XElement.Load(System.Xml.XmlReader.Create(e.Result));  
    24             var query = from x in xml.DescendantsAndSelf("people")  
    25                         select new people  
    26                         {  
    27                             name = x.Attribute("name").Value,  
    28                             value = x.Attribute("value").Value,  
    29                         };  
    30  
    31             ObservableCollection<people> ops = new ObservableCollection<people>();  
    32             foreach (people p in query.ToList())  
    33             {  
    34                 ops.Add(p);   //那为什么我换成listpeople.add();又可以刷新绑定的控件呢. 如果想整个刷新该如何处理  
    35             }  
    36             listpeople = ops;//listpeople更新之后无法通知DataGrid dg 刷新新的数据.有时候还报错(往上看)  
    37                 
    38         }  
    39         ObservableCollection<people> listpeople;  
    40         private void createDatagrid_Click(object sender, RoutedEventArgs e)  
    41         {  
    42             DataGrid dg = new DataGrid();  
    43             DataGridTextColumn namecol = new DataGridTextColumn();  
    44             namecol.Header = "姓名";  
    45             namecol.Binding = new System.Windows.Data.Binding("name");  
    46             DataGridTextColumn agecol = new DataGridTextColumn();  
    47             agecol.Header = "年龄";  
    48             agecol.Binding = new System.Windows.Data.Binding("value");  
    49             dg.AutoGenerateColumns = false;  
    50             dg.Columns.Add(namecol);  
    51             dg.Columns.Add(agecol);  
    52  
    53             listpeople = new ObservableCollection<people>(){new people { name = "Mick", value = "24" },  
    54             new people { name = "jack", value = "20" },  
    55             new people { name = "tom", value = "29" }};  
    56               
    57  
    58             dg.ItemsSource = listpeople;  
    59  
    60             datagridcontainer.Children.Add(dg);  
    61  
    62         }  
    63     }  
    64     public class people  
    65     {  
    66         public string name { getset; }  
    67         public string value { getset; }  
    68     } 
    前台xaml
    <StackPanel HorizontalAlignment="Left">
            <Button x:Name="createDatagrid" Width="150" Height="20" Margin="10,10,0,0" Click="createDatagrid_Click">
                <TextBlock Text="用我来动态创建datagrid"></TextBlock>
            </Button>
            <Canvas x:Name="datagridcontainer" Background="Red" Margin="5,5,0,0" Height="200" Width="200">
                
            </Canvas>
    2009年2月22日 16:12

答案

  • 你要这么绑定,可以用OneWay绑定模式,这样在你更改数据源的时候,DataGrid就可以自动更新了,实现OneWay绑定,首先要
    dtColumn.Binding.Mode = System.Windows.Data.BindingMode.OneWay;

    其次,你的绑定源泛型类需要继承 INotifyPropertyChanged 接口

    拿先前那个类为例
    1     public class BindString : INotifyPropertyChanged  
    2     {  
    3         public event PropertyChangedEventHandler PropertyChanged;  
    4         public void NotifyPropertyChanged(string propertyName)  
    5         {  
    6             if (PropertyChanged != null)  
    7             {  
    8                 PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));  
    9             }  
    10         }   
    11  
    12         public string A0  
    13         {  
    14             get { return strs[0]; }  
    15             set 
    16             {  
    17                 strs[0] = value;  
    18                 NotifyPropertyChanged("A0");  
    19             }  
    20         }  
    21         public string A1  
    22         {  
    23             get 
    24             {  
    25                 return strs[1];  
    26             }  
    27             set 
    28             {  
    29                 strs[0] = value;  
    30                 NotifyPropertyChanged("A1");  
    31             }  
    32         }  
    33  
    34         public string[] strs = new string[1];  
    35     } 

    在你修改A1的属性的时候DataGrid就会自动更新了:)
    • 已标记为答案 worldman 2009年2月26日 15:00
    2009年2月24日 8:27
    版主

全部回复

  •  
    3         public TestDatagrid()     
    4         {     
    5             InitializeComponent();     
    6             listpeople = new ObservableCollection<people>();     
    7             DispatcherTimer timer = new DispatcherTimer();     
    8             timer.Interval = TimeSpan.FromSeconds(2);     
    9             timer.Tick += new EventHandler(timer_Tick);     
    10             timer.Start();     
    11         }     
    12     
    13         void timer_Tick(object sender, EventArgs e)     
    14         {     
    15             WebClient webclient = new WebClient();     
    16             webclient.OpenReadCompleted += new OpenReadCompletedEventHandler(webclient_OpenReadCompleted);     
    17             webclient.OpenReadAsync(new Uri("ITMS.xml", UriKind.Relative));     
    18                  
    19         }     
    20     
    21         void webclient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)     
    22         {     
    23             XElement xml = XElement.Load(System.Xml.XmlReader.Create(e.Result));     
    24             var query = from x in xml.DescendantsAndSelf("people")     
    25                         select new people     
    26                         {     
    27                             name = x.Attribute("name").Value,     
    28                             value = x.Attribute("value").Value,     
    29                         };     
    30     
    31             ObservableCollection<people> ops = new ObservableCollection<people>();     
    32             foreach (people p in query.ToList())     
    33             {     
    34                 ops.Add(p);   //那为什么我换成listpeople.add();又可以刷新绑定的控件呢. 如果想整个刷新该如何处理     
    35             }     
    36             listpeople = ops;//listpeople更新之后无法通知DataGrid dg 刷新新的数据.有时候还报错(往上看)     
    37                    
    38         }     
     
    首先,你这样Timer刷新是不对的,应该再在ReadCompleted里面置一个是否读取完的开关,如果没有读取完Timer又出发继续读ITMS.xml可能会报错,就是你有时候还会报错的原因
    在37行加一句 dg.ItemsSource = listpeople;重新绑定下试试
    2009年2月22日 16:37
    版主
  • 谢谢版主的意见。
    可是在项目中我不并不希望重新去绑定数据。只是想局部更新。比如在绑定树形结构的时候。
    一刷新那么所有节点处于闭合状态。 (树形结构绑定的是treeview.itemsource=ObservableCollection<T>)
    另外我上面的代码只是想实现客户端实时刷新的效果(文件在服务器上,当时不理解webcilent的使用,调试程序的时候能刷新,一旦部署之后通过客户端访问就没有作用了。一个错误的例子)

    现在2个问题。  1 。ObservableCollection<T>.取交集方法(ObservableCollection<T>)  无法取到2个集合的相同元素
                               (包ObservableCollection<T> A.取差集方法(ObservableCollection<T>B) 和
                                 包ObservableCollection<T> B.取差集方法(ObservableCollection<T>A)) LinQ to object
                          
                          2。实现服务器上实时获取xml的数据。发送到客户端。! 目前想到是用silverlight功能wcf ,但想确认下。如果 使用的话,部署的服务器(考虑部署到linux上)上是不是要安装.netframework框架啊 ?(关于这个问题我在文字上也说不清楚,求一个大概的思想)



                       理解的东西:  dg.ItemsSource = listpeople; 数据控件已经绑定的集合 如何再吧listpeople去new的话
                      貌似开辟了一个新的内存了。所以dg.ItemsSource 数据无法刷新,以为他绑定的是以前那个内存数据
     
                不知道理解是否正确
    2009年2月24日 3:40
  • 你要这么绑定,可以用OneWay绑定模式,这样在你更改数据源的时候,DataGrid就可以自动更新了,实现OneWay绑定,首先要
    dtColumn.Binding.Mode = System.Windows.Data.BindingMode.OneWay;

    其次,你的绑定源泛型类需要继承 INotifyPropertyChanged 接口

    拿先前那个类为例
    1     public class BindString : INotifyPropertyChanged  
    2     {  
    3         public event PropertyChangedEventHandler PropertyChanged;  
    4         public void NotifyPropertyChanged(string propertyName)  
    5         {  
    6             if (PropertyChanged != null)  
    7             {  
    8                 PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));  
    9             }  
    10         }   
    11  
    12         public string A0  
    13         {  
    14             get { return strs[0]; }  
    15             set 
    16             {  
    17                 strs[0] = value;  
    18                 NotifyPropertyChanged("A0");  
    19             }  
    20         }  
    21         public string A1  
    22         {  
    23             get 
    24             {  
    25                 return strs[1];  
    26             }  
    27             set 
    28             {  
    29                 strs[0] = value;  
    30                 NotifyPropertyChanged("A1");  
    31             }  
    32         }  
    33  
    34         public string[] strs = new string[1];  
    35     } 

    在你修改A1的属性的时候DataGrid就会自动更新了:)
    • 已标记为答案 worldman 2009年2月26日 15:00
    2009年2月24日 8:27
    版主
  • 确实如版主所说.这样就会自动更新! dtColumn.Binding.Mode = System.Windows.Data.BindingMode.OneWay;也是默认的绑定方式.


    但我这是这样一个情况.     要从服务器上获取一个实时数据 然后通过获取最新的数据 赋值给 ObservableCollection<T> 来改变刷新绑定的控件

     
    所以好奇的的是 datagrid dg.itemsource = ObservableCollection<T> b 然后把  ObservableCollection<T> b重新赋值 ObservableCollection<T> b =ObservableCollection<T> A;

    为什么datagrid dg.itemsource 还是 ObservableCollection<T> b   这时的ObservableCollection<T> b 已经等于 ObservableCollection<T> A了
    2009年2月24日 14:50
  •  你需要 dg.CleranValue(itemsource); 再赋新值就可以了datagrid dg.itemsource = ObservableCollection<T> A

    b = A你要重载 =方法才能应用哦,两个东西虽然内容一样,但是占的不是一块内存,两个独立的实例
    2009年2月24日 15:05
    版主
  • 哦.  那要是我的控件是TreeView怎么办..如果我刷新平率高..岂不是总处于节点闭合状态.


    其实就是像实现实时刷新.控件Treeview控件上的. 所以我老是在想怎么修改ObservableCollection<T> A中的内容.因为我每次刷新我也不知道哪些值改变了
    哪些值没改.          所以我又想用ObservableCollection<T> C = ObservableCollection<T> A.Intersect<T>(ObservableCollection<T> B)获取他们集合的交集
    ObservableCollection<T> A:表示以前的值
    ObservableCollection<T> B:当前获取的值
    ObservableCollection<T> C:交集集合

    linq to object来进行筛选..然后 从ObservableCollection<T>A中删除ObservableCollection<T>A.Except(ObservableCollection<T> C)的差集
    然后从ObservableCollection<T>A中添加ObservableCollection<T>B.Except(ObservableCollection<T> C)的差集

    最后我就可以不用重新绑定控件了.实现刷新.
    但是linq to object筛选始终无法得到预期的效果..筛选后都为空

    不知道在silverlight tookit的控件上这块能不能帮我一下.

    还有ScatterSeries上貌似有bug不知道能不能向你反映下.
    2009年2月24日 15:53
  •  如果你可以保证你的新读取数据跟原有数据之间存在冗余,那么就不用新建一个泛型,直接单行绑定,对比修改原有泛型内的对应数值就可以了哦
    2009年2月24日 15:56
    版主
  •  Treeview想要保证以前节点打开,必然你新添加的东西要不然为原有打开的子节点,要不然为新的根节点,原有根节点不会动的,也可以全部赋新值,赋值之前把你现有的Treeview的节点打开状态保存一下就好了
    2009年2月24日 15:59
    版主

  •  

    八爪熊版主 - 发布于 4 分钟以前
     如果你可以保证你的新读取数据跟原有数据之间存在冗余,那么就不用新建一个泛型,直接单行绑定,对比修改原有泛型内的对应数值就可以了哦


    是个好注意. 但是个人能力有限.!不能完全理解其中的真谛.

    慢慢琢磨...谢谢您的答复.!!!

    八爪熊版主 - 发布于 6 分钟以前
     Treeview想要保证以前节点打开,必然你新添加的东西要不然为原有打开的子节点,要不然为新的根节点,原有根节点不会动的,也可以全部赋新值,赋值之前把你现有的Treeview的节点打开状态保存一下就好了


    赋值之前把你现有的Treeview的节点打开状态保存一下就好了  ...有这个方法吗?  我去查查去..  
    确实这个问题很棘手..  多花点功夫去消化.呵呵.!

    谢谢为我指了条明路 哈哈.


    2009年2月24日 16:11