7

我有一個控制一個WPF用戶控件

類DragGrid內綁定一個ObservableCollection依賴項屬性:網格 { ... }

從原始網格繼承並能拖並調整其子元素。 我需要一個自定義的DP名爲WorkItemsProperty結合型WorkItem(實現INotifyPropertyChanged)可觀察到的集合。網格中的每個元素都綁定到一個集合項目。

每當用戶在運行時動態添加新項目(無法在XAML!中聲明項目)或刪除該項目中的項目時,DragGrid上的WorkItems DP應該更新,並且網格中的子項每個孩子代表WorkItem收集項目)。

我的問題是如何將DP通知關於哪個子元素在網格必須除去控制,改變(「變化」是指用戶拖動的元素,或者用鼠標調整大小的話)或添加,以及我將如何確定哪個現有的孩子是需要刪除或更改的孩子。 我明白,這是在DependencyPropertyChangedCallback進來。但是,當DP屬性重新設置,而不是當收集的變化裏面的東西(如添加,刪除項目)僅被調用。所以最後,DragGrid控件是否需要訂閱CollectionChanged事件?在什麼時候我會爲此掛鉤事件處理程序?

*編輯:: 原因首先使用網格是因爲我希望能夠保持當用戶拖動或調整大小在網格中的控制最少三角洲。控件表示時間跨度,每個網格列表示15分鐘(這是最小值)。用拇指畫布做這件事是困難的和越野車。實現DragGrid解決了我的用戶交互問題。另外,畫布不可擴展,所以時間跨度必須一直重新計算。隨着網格,我沒有問題,因爲列告訴我時間無論大小。**

回答

16

在回答您的實際問題:

您應該添加DepencyPropertyChanged處理程序,如你所說。在此處理程序,你應該添加一個事件處理程序CollectionChanged財產上的新的收集和從舊集合中刪除的處理程序,就像這樣:不使用網格

public ObservableCollection<WorkItem> WorkItems 
    { 
     get { return (ObservableCollection<WorkItem>)GetValue(WorkItemsProperty); } 
     set { SetValue(WorkItemsProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for WorkItems. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty WorkItemsProperty = 
     DependencyProperty.Register("WorkItems", typeof(ObservableCollection<WorkItem>), typeof(DragGrid), new FrameworkPropertyMetadata(null, OnWorkItemsChanged)); 

    private static void OnWorkItemsChanged(object sender, DependencyPropertyChangedEventArgs e) 
    { 
     DragGrid me = sender as DragGrid; 

     var old = e.OldValue as ObservableCollection<WorkItem>; 

     if (old != null) 
      old.CollectionChanged -= me.OnWorkCollectionChanged; 

     var n = e.NewValue as ObservableCollection<WorkItem>; 

     if (n != null) 
      n.CollectionChanged += me.OnWorkCollectionChanged; 
    } 

    private void OnWorkCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     if (e.Action == NotifyCollectionChangedAction.Reset) 
     { 
      // Clear and update entire collection 
     } 

     if (e.NewItems != null) 
     { 
      foreach (WorkItem item in e.NewItems) 
      { 
       // Subscribe for changes on item 
       item.PropertyChanged += OnWorkItemChanged; 

       // Add item to internal collection 
      } 
     } 

     if (e.OldItems != null) 
     { 
      foreach (WorkItem item in e.OldItems) 
      { 
       // Unsubscribe for changes on item 
       item.PropertyChanged -= OnWorkItemChanged; 

       // Remove item from internal collection 
      } 
     } 
    } 

    private void OnWorkItemChanged(object sender, PropertyChangedEventArgs e) 
    { 
     // Modify existing item in internal collection 
    } 

由於gehho解釋說,這聽起來像你儘管你可能在開發過程中想要重新開始。來自Panel的類實際上只是爲了直觀地畫出/安排他們的孩子,而不是操縱和加強他們。查看ItemsControlWPF Content Model瞭解更多信息。

+0

喬希,謝謝你。在collectionchanged事件中,我不能添加到工作項的內部集合中,因爲.NET不允許這樣做。我想你在這裏的意思是將控件添加到網格的Children集合中。 – John 2010-05-07 07:58:00

+1

不,實際上我的意思是將它添加到您的收藏集+網格的兒童收藏集等。我不確定您的控件是如何工作的,但顯然您正在做一些工作來管理一些項目集合。根據你的評論,這聽起來像你正在管理Grid.Children集合,所以是的,將它添加到那裏。 – 2010-05-07 12:03:10

+0

理想情況下,您需要一個ItemsControl(或可能是一個自定義的子類,不知道沒有更好地理解您的問題),並且將WorkItems設置爲ItemsControl的子項。然後您將使用ItemsPanelTemplate來顯示項目一個網格。您可以使用DataTemplate爲工作項目生成實際的視覺效果。項目模板控件上的DataBinding,容器控件(由ItemsControl生成)和Grid將允許項目在正確的位置繪製。 – 2010-05-07 12:16:07

1

對不起,我沒有解決您的具體定製Grid問題,但我只有一個建議可以做到這一點更容易(我想,這是WPF設計者的意思)。實際上,一個Grid不安排項目控制。這是一個Panel,它安排了Controls。所以,我想,這是(你)解決問題的原因之一。

我想用的是ItemsControl(例如ListBox),CanvasItemsPanel

<ListBox ItemsSource="{Binding WorkItemsProperty}"> 
    <ListBox.ItemsPanel> 
     <ItemsPanelTemplate> 
      <Canvas IsItemsHost="True"/> 
     </ItemsPanelTemplate> 
    </ListBox.ItemsPanel> 
</ListBox> 

現在,你在你的WorkItem(或WorkItemViewModel)像XPosYPos類將數據綁定到Canvas.LeftCanvas.Top性質類似這樣的定義相應的屬性:

<Style x:Key="WorkItemStyle" TargetType="{x:Type ListBoxItem}"> 
    <Setter Property="Canvas.Left" Value="{Binding XPos, Mode=TwoWay}"/> 
    <Setter Property="Canvas.Top" Value="{Binding YPos, Mode=TwoWay}"/> 
</Style> 

然後,您可以使用此通過分配ItemContainerStyle屬性的項目風格ListBox

ItemContainerStyle="{StaticResource WorkItemStyle}" 

我不知道如何實現拖放的東西,因爲我從來沒有這樣做過,但顯然你已經完成了你的定製Grid,所以在ListBox中使用它也不是什麼大問題。但是,如果更新WorkItem的屬性,它應該自動重新定位元素。另外,如果您向收藏集中添加/刪除項目(WorkItemsProperty),則會自動添加/刪除項目,因爲ListBox已與數據綁定到收藏夾。

根據您的情況,您可能需要更改WorkItemStyle。例如,如果ListBox在運行時調整大小,則可能必須相對於容器的(Canvas')大小創建位置。因此,您需要一個MultiBinding而不是簡單的Binding。但這是另一個故事......

現在,您的決定是否仍然可以採用這種方法,或者您的Grid幾乎完成,而且您不願意更改。我知道這很難,但在我看來,上述方法更清潔(更簡單!)!