WPF - Лучший способ удалить объект из ItemsSource

Я пишу пользовательское ItemsControl (контейнер документа с вкладками), куда каждый объект (вкладка) может удалить себя из UI, когда пользователь закрывает его. Однако я не могу удалить его непосредственно из ItemsControl.Items набор, потому что объекты могут быть связаны с данными. Таким образом, я должен удалить его из ItemsSource, который может быть чем-либо (ICollection, DataTable, DataSourceProvider...).

В контексте моего приложения я знаю что фактический тип ItemsSource будет, но я хочу, чтобы то управление было более универсальным так, чтобы я мог снова использовать его позже.

Таким образом, я ищу способ удалить объект из источника данных, не зная его тип. Я мог использовать отражение, но это чувствует себя грязным... До сих пор лучшее решение, которое я предложил, использует 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());
        }
    }

Но я не действительно доволен им, я уверен, что должен быть лучший путь. Любые предложения ценились бы!

5
задан Thomas Levesque 17 July 2010 в 13:06
поделиться

4 ответа

Хорошо, я нашел решение ...

  • Если ItemsSource привязан к данным, я либо инициирую событие (для использования с выделенным кодом), либо вызываю команда (для использования с ViewModel) для удаления элемента из коллекции ItemsSource .

  • Если это не привязка к данным, я вызываю событие, чтобы запросить у пользователя подтверждение, и удаляю контейнер непосредственно из Items

     public static readonly DependencyProperty CloseTabCommandProperty =
    DependencyProperty.Register (
     "CloseTabCommand",
    typeof (ICommand),
    typeof (TabDocumentContainer),
    новый UIPropertyMetadata (null));
    public ICommand CloseTabCommand
    {
    получить {возврат (ICommand) GetValue (CloseTabCommandProperty); }
    установить {SetValue (CloseTabCommandProperty, значение); }
    }
    открытое событие EventHandler  RequestCloseTab;
    открытое событие EventHandler  TabClosing;
    внутренняя пустота CloseTab (TabDocumentContainerItem tabDocumentContainerItem)
    {
    if (ItemsSource! = null) // Привязка данных
     {
    объектный элемент = ItemContainerGenerator.ItemFromContainer (tabDocumentContainerItem);
    if (item == null || item == DependencyProperty.UnsetValue)
     {
    возвращение;
     }
    если (RequestCloseTab! = ноль)
     {
    var args = новый RequestCloseTabEventArgs (элемент);
    RequestCloseTab (это, аргументы);
     }
    иначе, если (CloseTabCommand! = null)
     {
    если (CloseTabCommand.CanExecute (элемент))
     {
    CloseTabCommand.Выполнить (элемент);
     }
     }
     }
    else // Не привязка к данным
     {
    если (TabClosing! = ноль)
     {
    var args = new TabClosingEventArgs (tabDocumentContainerItem);
    TabClosing (это, аргументы);
    если (args.Cancel)
    возвращение;
     }
    Items.Remove (tabDocumentContainerItem);
     }
    }
    
2
ответ дан 14 December 2019 в 08:39
поделиться

ItemCollection , возвращенный ItemsControl.Items выиграл ' t позволяет вам вызывать Remove напрямую, но он реализует IEditableCollectionView и позволяет вам вызывать метод Remove в этом интерфейсе.

Это будет работать, только если представление коллекции, привязанное к ItemsSource , реализует сам IEditableCollectionView . Представление коллекции по умолчанию будет для большинства изменяемых коллекций, но не для объектов, реализующих ICollection , но не IList .

IEditableCollectionView items = tabControl.Items; //Cast to interface
if (items.CanRemove)
{
    items.Remove(tabControl.SelectedItem);
}
9
ответ дан 14 December 2019 в 08:39
поделиться

Как вы обнаружили, ваш ItemsControl не обладает внутренними знаниями о связываемых элементах - эти типы предоставляются потребителями вашего элемента управления. И вы не можете изменять коллекцию напрямую, поскольку она может быть связана с данными.

Хитрость заключается в том, чтобы каждый элемент был обернут пользовательским классом (контейнером элементов) по вашему выбору. В вашем ItemsControl вы можете обеспечить это в методе GetContainerForItemOverride.

Отсюда вы можете определить свойства вашего пользовательского контейнера элементов, которые вы затем свяжете с вашим шаблоном по умолчанию. Например, у вас может быть свойство под названием Состояние, которое изменяется между Докированным, Плавающим и Закрытым. Ваш шаблон будет использовать это свойство, чтобы определить, как - и нужно ли - показывать элемент.

Таким образом, на самом деле вы не будете изменять базовый источник данных. Вместо этого вы измените специфический для элемента управления слой поверх базовых элементов данных, который даст вам информацию, необходимую для реализации вашего элемента управления.

-1
ответ дан 14 December 2019 в 08:39
поделиться

Практика проектирования требует, чтобы вы действительно знали, что такое ваш ItemsSource и иметь возможность удалить его оттуда напрямую. Привязка, конечно же, автоматически обновляет вид.

Однако, если вы абсолютно намерены использовать какую-то общую функциональность для удаления, приведите свой ItemsSource к ICollection или ICollection , а затем вызовите Удаление звучит как лучший / более надежный способ, чем использование динамических функций .NET.

-3
ответ дан 14 December 2019 в 08:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: