7

Ho un controlloWPF - Binding ObservableCollection proprietà di dipendenza all'interno di un UserControl

classe DragGrid: Griglia { ...}

che eredita dalla rete originale e permette il trascinamento e ridimensionando i suoi elementi figli. ho bisogno di impegnare un costume DP nome WorkItemsProperty ad una collezione di tipo osservabile WorkItem (attrezzi INotifyPropertyChanged). Ogni elemento nella griglia è associato a un elemento di raccolta.

Ogni volta che l'utente aggiunge un nuovo elemento dinamicamente in fase di esecuzione (gli elementi non possono essere dichiarati in XAML!) O elimina un elemento da quella raccolta, il WorkItems DP su DragGrid deve essere aggiornato ei bambini nella griglia (dove ogni bambino rappresenta un elemento di raccolta WorkItem).

mia domanda è come fa il DP notificare il controllo su quale elemento figlio nella griglia deve essere rimosso, cambiato ('cambiamento' significa utente ha trascinato un elemento, o ridimensionato con il mouse) o aggiunto e come dovrei identificare quale dei bambini esistenti è quello che deve essere cancellato o modificato. Capisco che è qui che entra in gioco DependencyPropertyChangedCallback. Ma questo viene chiamato solo quando la proprietà DP viene reimpostata, non quando qualcosa all'interno della collezione cambia (come aggiungi, rimuovi elemento). Quindi, alla fine, il controllo DragGrid ha in qualche modo bisogno di iscriversi all'evento CollectionChanged? A che punto collegheremo il gestore di eventi per quello?

* EDIT :: La ragione per usare una griglia, in primo luogo è perché voglio essere in grado di mantenere un delta minimo per quando l'utente trascina o ridimensiona il controllo nella griglia. Un controllo rappresenta un intervallo di tempo e ogni colonna della griglia rappresenta 15 minuti (che è il valore minimo). Fare questo in una tela con i pollici era difficile e bug. L'implementazione di un DragGrid ha risolto i problemi di interazione dell'utente. Inoltre, una tela non è scalabile, quindi gli intervalli temporali dovrebbero essere ricalcolati continuamente. Con la griglia, non ho il problema perché le colonne mi dicono l'ora indipendentemente dalle dimensioni. **

risposta

16

In risposta alla tua domanda effettiva:

Si dovrebbe aggiungere un gestore DepencyPropertyChanged, come lei ha ricordato. In questo gestore, si dovrebbe aggiungere un gestore di eventi alla proprietà CollectionChanged sulla nuova collezione e rimuovere il gestore dalla vecchia collezione, in questo modo:

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 
    } 

Come ha spiegato gehho, suona come non si utilizza la griglia classe come originariamente inteso, anche se potresti essere troppo avanti nello sviluppo per voler ricominciare da capo a questo punto. Le classi derivate da Panel sono in realtà intese solo a disegnare visivamente/organizzare i loro figli, piuttosto che manipolarli e migliorarli. Dai un'occhiata a ItemsControl e allo WPF Content Model per saperne di più.

+0

Josh, grazie per questo. Nell'evento collectionchanged, non posso aggiungere alla raccolta interna di workitems, perché .NET non lo consente. Penso che quello che intendevi qui è aggiungere i controlli alla collezione Bambini della griglia. – John

+1

No, in realtà intendevo aggiungerlo alla TUA collezione + alla collezione Children della griglia o ecc. Non sono sicuro di come funzioni il tuo controllo, ma è ovvio che stai facendo del lavoro per gestire alcuni gruppi di elementi. In base al tuo commento, sembra che tu stia gestendo la raccolta Grid.Children, quindi sì, aggiungila lì. –

+0

Idealmente si dovrebbe prendere un ItemsControl (o eventualmente una classe figlio personalizzata da esso, non sicuro senza una migliore comprensione del problema) e si dovrebbero impostare i WorkItem come figli di ItemsControl. Si utilizzerà quindi un ItemsPanelTemplate per visualizzare gli elementi su una griglia. Si utilizzerà un DataTemplate per generare le immagini effettive per gli elementi di lavoro. DataBinding sul controllo del modello dell'articolo, il controllo del contenitore (generato da ItemsControl) e la griglia consentirebbero di disegnare gli elementi nelle loro posizioni corrette. –

1

Scusa, non ho alcuna soluzione al tuo problema concreto personalizzato Grid, ma ho solo un suggerimento su come potrebbe farlo più facilmente (e, suppongo, come è inteso dai progettisti WPF). In realtà, un Grid non è un controllo per organizzare articoli. È un Panel che dispone Controls. Quindi, suppongo, questo è (uno dei) motivi per cui vi state mettendo nei guai con la vostra soluzione.

Quello che vorrei utilizzare, invece, è uno ItemsControl (ad esempio uno ListBox) con uno Canvas come ItemsPanel.

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

Ora, si definiscono le proprietà appropriate nel vostro WorkItem (o WorkItemViewModel) di classe come XPos e YPos che sarà databound alle proprietà Canvas.Left e Canvas.Top come questo:

<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> 

è possibile utilizzare questo stile articolo assegnando la proprietà ItemContainerStyle allo ListBox:

ItemContainerStyle="{StaticResource WorkItemStyle}" 

Non so come implementare il drag and drop roba perché non l'ho mai fatto, ma ovviamente lo hai già fatto per il tuo custom Grid, quindi non dovrebbe essere un grosso problema usarlo in un ListBox . Tuttavia, se aggiorni le proprietà del tuo WorkItem, dovrebbe riposizionare automaticamente l'elemento. Inoltre, se aggiungi/rimuovi un oggetto alla/dalla tua raccolta (WorkItemsProperty), questo verrà automaticamente aggiunto/rimosso poiché lo ListBox è associato ai dati alla raccolta.

Potrebbe essere necessario modificare il WorkItemStyle a seconda del proprio scenario. Ad esempio, se il ListBox viene ridimensionato in fase di runtime, potrebbe essere necessario rendere le posizioni relative alla dimensione del contenitore (Canvas). Pertanto, è necessario un MultiBinding anziché il semplice Binding. Ma questa è un'altra storia ...

Ora, è una tua decisione se puoi ancora passare a questo approccio o se il tuo Grid è quasi finito e non sei disposto a cambiare. So che è difficile, ma ai miei occhi, l'approccio sopra è quello più pulito (e più facile!)!

Problemi correlati