2010-07-17 3 views
5

각 항목 (탭)은 사용자가 닫을 때 UI에서 자신을 제거 할 수있는 사용자 정의 ItemsControl을 작성하고 있습니다. 그러나 항목을 데이터 바인딩 할 수 있기 때문에 ItemsControl.Items 컬렉션에서 직접 제거 할 수는 없습니다. 그래서 나는 그것을 ItemsSource에서 제거해야합니다. 어떤 것도 될 수 있습니다 (ICollection, DataTable, DataSourceProvider ...).WPF - ItemsSource에서 항목을 제거하는 가장 좋은 방법

내 응용 프로그램과 관련하여 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()); 
     } 
    } 

을하지만이 있어야 내가 확신 그것으로 정말 행복하지 않다 더 좋은 방법. 모든 제안을 부탁드립니다!

답변

2

좋아, 해결책을 찾았습니다.ItemsSource은 데이터 바인딩 인 경우

  • , I 중 하나는 (코드 숨김 함께 사용) 이벤트를 높이거나 ItemsSource 컬렉션에서 항목을 제거 할 수의 (a 뷰 모델에 사용) 명령을 호출합니다. 이 데이터 바인딩 아니에요 경우

  • , 나는 사용자에게 확인 메시지를 표시하는 이벤트를 발생, 나는 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

디자인 연습에서는 실제로 ItemsSource이 무엇인지 알고 직접 거기에서 제거 할 수 있어야합니다. 그런 다음 바인딩은 자동으로보기를 자동으로 업데이트합니다. 주조 그러나

, 당신은 제거를위한 일반적인 기능의 일종에 절대적으로 의지하는 경우, 귀하의 ItemsSourceICollection 또는 ICollection<T> 다음 .NET의 동적 기능을 사용하는 것보다 갈 수있는 더 나은/더 신뢰할 수있는 방법 같은 소리 Remove를 호출.

+0

에서 직접 컨테이너를 제거, 좋은 디자인은'ItemsControl'이 전혀 보장하지 않을 것이다 'ItemsSource'에있는 타입에 대한 지식. –

+0

해당 ItemsSource 속성을 완전히 제어 할 수 있다면 동의 하겠지만 그렇지는 않습니다. WPF의 일부이고,'object' 타입이며, 열거 형을 지원하는 어떤 것도 될 수 있습니다. 그리고 내 컨트롤은 어떤 것을 받아들이도록 고안되었으므로 특정 유형으로 제한하고 싶지 않습니다. –

+1

왜 투표가 늦습니까? ?? – Noldorin

-1

귀하가 발견 한 바에 따르면 ItemsControl에는 바인딩되는 항목에 대한 고유 한 지식이 없습니다. 이러한 유형은 귀하의 컨트롤 소비자가 제공합니다. 또한 데이터 바인딩이 가능하기 때문에 컬렉션을 직접 수정할 수 없습니다.

트릭은 각 항목이 사용자가 선택한 클래스 (항목 컨테이너)로 묶여 있는지 확인하는 것입니다. ItemsControlGetContainerForItemOverride 방법으로 제공 할 수 있습니다.

거기에서 기본 서식 파일에 바인딩 할 사용자 지정 항목 컨테이너의 속성을 정의 할 수 있습니다. 예를 들어 State이라는 속성이 Docked, FloatingClosed 사이에서 변경 될 수 있습니다. 템플릿은이 속성을 사용하여 항목을 표시하는 방법 및 여부를 결정합니다.

그래서 실제로 기본 데이터 소스를 전혀 변경하지 않을 것입니다. 대신 컨트롤을 구현하는 데 필요한 정보를 제공하는 기본 데이터 항목 위에 컨트롤 관련 레이어를 변경합니다.

+0

켄트, 답해 주셔서 감사합니다. 이미 사용자 지정 컨테이너를 만들고 GetContainerForItemOverride를 재정의했습니다. 그러나 실제로는 기본 컬렉션에서 항목을 제거하고 싶습니다. 숨길 수는 없습니다. 아마도 이벤트 (코드 숨김)를 처리하거나 명령 (ViewModel)을 바인딩하여 "닫기"구현을 사용자에게 위임해야합니다. –

+0

@ 토마스 : 문제가 없습니다. 아이템을 제거해야하는 이유를 설명 할 수 있습니까? 아마도 필터링 된 컬렉션 뷰로 충분할 것입니까? 또는 사용자가 항목을 닫을 때 명령이 실행되어야하며 제거는 사용 코드에 달려 있습니다. 기본 컨트롤을 만들려는 욕구는 나 자신이 직접 제거하면 작동하지 않습니다. –

+0

컨트롤에 열린 문서 (이 경우 SQL 워크 시트) 목록이 표시됩니다. ViewModel에 의해 노출 된 워크 시트 목록에 바인딩됩니다. 탭 (TabDocumentContainerItem 템플릿의 일부)에서 닫기 버튼을 클릭하면 컨테이너는 워크 시트 컬렉션에서 문서를 제거하는 부모 (TabDocumentContainer)에서 CloseTab을 호출합니다. 사실 나는 해결책을 찾았고, 몇 분 후에 게시 할 것입니다. –

9

ItemsControl.Items에 의해 반환 된 ItemCollection은 직접 제거를 호출 할 수 없지만 IEditableCollectionView을 구현하며 해당 인터페이스에서 Remove 메서드를 호출 할 수 있습니다.

ItemsSource에 바인딩 된 컬렉션보기가 IEditableCollectionView 자체를 구현하는 경우에만 작동합니다. 기본 컬렉션 뷰는 ICollection을 구현하지만 IList을 구현하지 않는 객체는 아니지만 대부분의 변경 가능한 컬렉션에 사용됩니다.

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

흥미로운 대답, 나는 그 인터페이스에 대해 몰랐습니다. 그러나 필자가 발견 한 해결책 (필자의 대답 참조)에 충실 할 것이라고 생각한다. 필자의 경우에는 더 적절하기 때문이다. 또한 ItemsSource가 IEnumerable 인 경우 CanRemove는 false를 반환하지만 ViewModel은 실제 컬렉션에 액세스하여 항목을 제거 할 수 있습니다. –

+0

굉장한 해결책 동생. 방금 나를 위해 나쁜 버그를 고쳤습니다 : http://avalondock.codeplex.com/workitem/13168 – basarat

관련 문제