2010-07-17 2 views
5

Ich schreibe eine benutzerdefinierte ItemsControl (ein Container mit Registerkarten), wo jedes Element (Registerkarte) kann sich selbst von der Benutzeroberfläche entfernen, wenn der Benutzer es schließt. Ich kann es jedoch nicht direkt aus der Sammlung ItemsControl.Items entfernen, da die Elemente datengebunden sein können. Also muss ich es aus dem ItemsSource entfernen, was alles sein kann (ICollection, DataTable, DataSourceProvider ...).WPF - Die beste Methode zum Entfernen eines Elements aus der ItemsSource

Im Zusammenhang mit meiner Anwendung weiß ich, was der eigentliche Typ der ItemsSource wird, aber ich möchte, dass die Steuerung generischer ist, so dass ich es später wiederverwenden kann.

So suche ich nach einer Möglichkeit, ein Element aus einer Datenquelle zu entfernen, ohne seinen Typ zu kennen. Ich konnte Reflexion verwenden, aber es fühlt sich schmutzig ... So weit die beste Lösung kam ich mit verwendet dynamic:

internal void CloseTab(TabDocumentContainerItem tabDocumentContainerItem) 
    { 
     // TODO prompt user for confirmation (CancelEventHandler ?) 

     var item = ItemContainerGenerator.ItemFromContainer(tabDocumentContainerItem); 

     // TODO find a better way... 
     try 
     { 
      dynamic items = ItemsSource; 
      dynamic it = item; 
      items.Remove(it); 
     } 
     catch(RuntimeBinderException ex) 
     { 
      Trace.TraceError("Oops... " + ex.ToString()); 
     } 
    } 

Aber ich bin nicht wirklich glücklich mit ihm, ich bin sicher, dass es eine sein muss besserer Weg. Irgendwelche Vorschläge würden geschätzt!

Antwort

2

OK, ich habe eine Lösung gefunden ...

  • Wenn die ItemsSource Databound ist, dass ich entweder heben ein Ereignis (für die Verwendung mit Code-Behind) oder einen Befehl aufrufen (für die Verwendung mit einem Ansichtsmodell), um das Element aus der ItemsSource Sammlung zu entfernen.

  • Wenn es nicht datengebundene ist, hebe ich ein Ereignis die Benutzer zur Bestätigung aufgefordert werden, und entferne ich die Container direkt von Items

    public static readonly DependencyProperty CloseTabCommandProperty = 
        DependencyProperty.Register(
         "CloseTabCommand", 
         typeof(ICommand), 
         typeof(TabDocumentContainer), 
         new UIPropertyMetadata(null)); 
    
    public ICommand CloseTabCommand 
    { 
        get { return (ICommand)GetValue(CloseTabCommandProperty); } 
        set { SetValue(CloseTabCommandProperty, value); } 
    } 
    
    public event EventHandler<RequestCloseTabEventArgs> RequestCloseTab; 
    public event EventHandler<TabClosingEventArgs> TabClosing; 
    
    internal void CloseTab(TabDocumentContainerItem tabDocumentContainerItem) 
    { 
        if (ItemsSource != null) // Databound 
        { 
         object item = ItemContainerGenerator.ItemFromContainer(tabDocumentContainerItem); 
         if (item == null || item == DependencyProperty.UnsetValue) 
         { 
          return; 
         } 
         if (RequestCloseTab != null) 
         { 
          var args = new RequestCloseTabEventArgs(item); 
          RequestCloseTab(this, args); 
         } 
         else if (CloseTabCommand != null) 
         { 
          if (CloseTabCommand.CanExecute(item)) 
          { 
           CloseTabCommand.Execute(item); 
          } 
         } 
        } 
        else // Not databound 
        { 
         if (TabClosing != null) 
         { 
          var args = new TabClosingEventArgs(tabDocumentContainerItem); 
          TabClosing(this, args); 
          if (args.Cancel) 
           return; 
         } 
         Items.Remove(tabDocumentContainerItem); 
        } 
    } 
    
-3

Design-Praxis diktiert, dass Sie wirklich wissen sollten, was Ihr ItemsSource ist und in der Lage sein, es von dort direkt zu entfernen. Die Bindung aktualisiert dann automatisch die Ansicht.

Wenn Sie jedoch auf eine Art generischer Funktionalität für die Entfernung absolut entschlossen sind, Gießen Ihres ItemsSource-ICollection oder ICollection<T> und dann Remove Aufruf klingt wie ein besser/zuverlässiger Weg, um die dynamischen Eigenschaften von .NET zu gehen, als verwenden.

+0

Eigentlich wäre ein gutes Design sorgen für die 'ItemsControl' hat keine Kenntnis der Typen in der ItemsSource überhaupt. –

+0

Ich würde zustimmen, wenn ich die volle Kontrolle über diese ItemsSource-Eigenschaft hätte ... aber ich nicht. Es ist Teil von WPF, hat den Typ "Objekt" und kann alles sein, was Aufzählung unterstützt. Und meine Kontrolle ist darauf ausgelegt, alles zu akzeptieren, ich möchte mich nicht auf einen bestimmten Typ beschränken –

+1

Down-vote why ?? – Noldorin

-1

Wie Sie festgestellt haben, verfügt Ihr ItemsControl über kein intrinsisches Wissen über die zu bindenden Elemente - diese Typen werden von den Benutzern Ihrer Kontrolle bereitgestellt. Und Sie können die Sammlung nicht direkt ändern, da sie möglicherweise datengebunden ist.

Der Trick besteht darin, sicherzustellen, dass jeder Artikel von einer benutzerdefinierten Klasse (Artikelcontainer) Ihrer Wahl umschlossen wird. Ihre ItemsControl können Sie dies in der GetContainerForItemOverride Methode bereitstellen.

Von dort aus können Sie Eigenschaften für Ihren benutzerdefinierten Objektcontainer definieren, an den Sie dann in Ihrer Standardvorlage binden. Sie könnten beispielsweise eine Eigenschaft namens State haben, die zwischen Docked, Floating und Closed wechselt. Ihre Vorlage würde diese Eigenschaft verwenden, um zu bestimmen, wie und ob das Element angezeigt werden soll.

Sie werden also die zugrunde liegende Datenquelle überhaupt nicht ändern. Stattdessen ändern Sie eine kontrollspezifische Ebene über den zugrunde liegenden Datenelementen, die Ihnen die Informationen liefern, die Sie zum Implementieren Ihres Steuerelements benötigen.

+0

Kent, danke für deine Antwort. Ich habe bereits einen benutzerdefinierten Container erstellt und GetContainerForItemOverride überschrieben. Aber Tatsache ist, dass ich das Objekt wirklich aus der zugrunde liegenden Sammlung entfernen möchte, ich möchte es nicht einfach verstecken. Vielleicht sollte ich einfach die Implementierung von "close" an den Benutzer delegieren, indem ich ein Ereignis (Code-Behind) behandle oder einen Befehl bind (ViewModel) –

+0

@Thomas: kein Problem. Kannst du erklären, warum du den Gegenstand entfernen musst? Vielleicht würde eine gefilterte Sammlungsansicht ausreichen? Oder vielleicht sollte Ihr Element einen Befehl ausführen, wenn der Benutzer es schließt, und die Entfernung hängt vom verbrauchenden Code ab. Scheint mir Ihr Wunsch, ein generisches Steuerelement zu erstellen, kann nicht funktionieren, wenn Sie die Entfernung selbst vornehmen. –

+0

Das Steuerelement zeigt eine Liste der geöffneten Dokumente an (in diesem Fall SQL-Arbeitsblätter). Es ist an eine Liste von Arbeitsblättern gebunden, die von einem ViewModel bereitgestellt werden. Wenn ich auf einer Registerkarte (Teil der Vorlage des TabDocumentContainerItem) auf die Schließen-Schaltfläche klicke, ruft der Container CloseTab auf seinem Eltern (dem TabDocumentContainer) auf, das das Dokument aus der Arbeitsblattsammlung entfernt. Eigentlich habe ich eine Lösung gefunden, ich poste es in ein paar Minuten –

9

ItemCollection zurückgegeben von ItemsControl.Items können Sie nicht direkt Remove aufrufen, aber es implementiert IEditableCollectionView und ermöglicht es Ihnen, die Remove-Methode in dieser Schnittstelle aufzurufen.

Dies funktioniert nur, wenn die an ItemsSource gebundene Sammlungsansicht IEditableCollectionView selbst implementiert. Die Standardsammlungsansicht wird für die meisten veränderbaren Auflistungen verwendet, jedoch nicht für Objekte, die ICollection, aber nicht IList implementieren.

IEditableCollectionView items = tabControl.Items; //Cast to interface 
if (items.CanRemove) 
{ 
    items.Remove(tabControl.SelectedItem); 
} 
+1

Interessante Antwort, ich wusste nicht über diese Schnittstelle. Ich denke jedoch, dass ich bei der Lösung bleiben werde, die ich gefunden habe (siehe meine Antwort), weil es in meinem Fall angemessener ist. Wenn ItemsSource ein IEnumerable ist, gibt CanRemove außerdem false zurück, aber das ViewModel hat möglicherweise Zugriff auf die tatsächliche Auflistung und kann das Element entfernen. –

+0

Super Lösung Bro. Du hast gerade einen Fehler behoben: http://avalondock.codeplex.com/workitem/13168 – basarat

Verwandte Themen