2012-12-13 19 views
2

我堅持用折線的點結合一個ObservableCollection(點):綁定折線點的ObservableCollection不起作用

<UserControl 
x:Class="GL.MainPage" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
mc:Ignorable="d" 
d:DesignHeight="640" d:DesignWidth="840"> 
<Grid x:Name="LayoutRoot" Background="#ff444444"> 
    <Canvas Background="#333333" Width="800" Height="600"> 
     <Polyline x:Name="Linie" Stroke="Yellow" StrokeThickness="2" Canvas.Left="0" Canvas.Top="0" Width="800" Height="600" Fill="Gray" Points="{Binding Punkte}"> 
     </Polyline> 
    </Canvas> 
    <TextBlock Height="55" Name="tb" Foreground="White" FontSize="{Binding Path=TS}" Text="JUST A TEST!" /> 
    <Button Content="Add Point" Height="23" HorizontalAlignment="Left" Margin="745,617,0,0" Name="Button1" VerticalAlignment="Top" Width="75" /> 
</Grid> 

這裏是後面的代碼:

Imports System.Windows 
Imports System.Windows.Media 
Imports System.Collections.ObjectModel 

Partial Public Class MainPage 
    Inherits UserControl 

    Dim r As New Random(345) 
    Private _punkte As New ObservableCollection(Of Point) 
    Public Property Punkte As ObservableCollection(Of Point) 
     Get 
      Return _punkte 
     End Get 
     Set(value As ObservableCollection(Of Point)) 
      _punkte = value 
      SetValue(Punkte_DP, _punkte) 
     End Set 
    End Property 

    Private _ts As Integer 
    Public Property TS As Integer 
     Get 
      Return _ts 
     End Get 
     Set(value As Integer) 
      _ts = value 
      SetValue(TS_DP, _ts) 
     End Set 
    End Property 

    Public Punkte_DP As DependencyProperty = DependencyProperty.Register("Punkte", GetType(ObservableCollection(Of Point)), GetType(MainPage), New PropertyMetadata(New ObservableCollection(Of Point))) 
    Public TS_DP As DependencyProperty = DependencyProperty.Register("TS", GetType(Integer), GetType(MainPage), New PropertyMetadata(New Integer)) 

    Public Sub New() 

     Me.DataContext = Me 
     InitializeComponent() 

     Linie.DataContext = Me.Punkte 
     Punkte.Add(New Point(100, 100)) 
     Punkte.Add(New Point(700, 300)) 

     TS = 25 
    End Sub 

    Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click 
     Punkte.Add(New Point(r.Next(0, 600), r.Next(0, 600))) 
    End Sub 
End Class 

當我運行這個時,FontSize被更新,但沒有一個單獨的點。線正在繪製。每次按鈕被點擊時,集合變得更大,但沒有任何反應。

我在這裏錯過了什麼?謝謝你的幫助!

問候, 羅布

回答

2

爲什麼在多邊形的點不更新是因爲Polygon.Points屬性採用PointCollection類型的值,並且Silverlight是不能轉換的ObservableCollection<Point>PointCollection自身的原因。

你需要做的是添加一個converter,將ObservableCollection<Point>轉換爲PointCollection。像下面這樣的東西應該這樣做:

public class ObservableCollectionToPointCollectionConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     var points = value as ObservableCollection<Point>; 
     if (points == null) 
     { 
      return null; 
     } 

     var collection = new PointCollection(); 
     foreach (Point point in points) 
     { 
      collection.Add(point); 
     } 

     return collection; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     // Not needed for one-way bindings. 
     throw new NotImplementedException(); 
    } 
} 

作爲一個練習,我會留給你把它轉換爲VB.NET。

要連接該轉換器中,添加一個命名空間聲明如

xmlns:myns="clr-namespace:YourNamespaceContainingTheConverter" 

到根<UserControl>標籤,該標籤的末端之後但<Grid>之前添加

<UserControl.Resources> 
    <myns:ObservableCollectionToPointCollectionConverter x:Key="converter" /> 
</UserControl.Resources> 

,並修改Points綁定在您的Polygon

Points="{Binding Punkte, Converter={StaticResource converter}}" 

但是,我的代碼遇到了一些其他問題。首先,當我第一次運行你的代碼時,我得到了一些'值不在預期範圍內'的錯誤。原來,這是因爲TextBlock.FontSize必須具有正值。我將TS的初始化提交到Me.DataContext之前,這些錯誤消失了。

其次,行Linie.DataContext = Me.Punkte是錯誤的。這將Polygon的DataContext設置爲ObservableCollectionPoint s。 Polygon的Points屬性中的綁定{Binding Punkte}告訴Silverlight在數據上下文的名爲Punkte的屬性中查找該集合,即ObservableCollectionPoint s。這將失敗,因爲ObservableCollection類沒有名爲Punkte的屬性。該行應該被刪除 - Polygon將繼承它的父數據上下文,該上下文有一個名爲Punkte的屬性。

第三,我替換了MainPage.xaml中依賴項屬性的使用。VB與'普通'CLR屬性,並使MainPage實施INotifyPropertyChanged。然後屬性看起來如下:

public ObservableCollection<Point> Punkte 
    { 
     get { return _punkte; } 
     set 
     { 
      _punkte = value; 
      FirePropertyChanged("Punkte"); 
     } 
    } 

    public double TS 
    { 
     get { return _ts; } 
     set 
     { 
      _ts = value; 
      FirePropertyChanged("TS"); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void FirePropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

再次,隨意將其轉換爲VB.NET。

我做的最後更改是將FirePropertyChanged("Punkte")添加到Button1_Click事件處理程序。這讓Silverlight知道點的可觀察集合(因此已轉換的PointCollection)已更改,並且必須由UI進行更新。

通常,在視圖模型中對集合使用ObservableCollection就足以使更改通知正常工作。但是,在這種情況下,它不能很好地工作,因爲ObservableCollection在被傳遞到UI層之前被轉換爲另一個對象。結果,由ObservableCollection觸發的CollectionChanged事件未被偵聽。這對你的代碼來說不是問題,它更多的是框架的限制。我確實想看看是否有可能編寫PointCollection的子類,它也實現了INotifyCollectionChanged,但這是不可能的,因爲PointCollectionsealed

而是隻爲Add小號激發此事件中,它可能是更好地處理在MainPageObservableCollectionCollectionChanged事件,並有此事件處理程序調用FirePropertyChanged("Punkte")。這樣,多邊形將隨時更新集合的所有更改。