2011-03-01 4 views
1

TabControl 래퍼 컨트롤에서WPF : "기다려주십시오"탭을 표시하는 방법

void AddTab(Func<object> tabContentGenerator) 

함수는 "Please wait"콘텐츠가있는 새 TabItem을 추가 한 다음 tabContentGenerator 함수를 호출하여 표시 할 개체를 가져와야합니다. 해당 TabItem의 Content를 반환 된 객체로 바꿉니다.

BackgrounWorker에서 tabContentGenerator에 대한 호출을 구현하려고했습니다. 그러나 tabContentGenerator 함수는 일반적으로 내용으로 사용할 UserControl을 만들고 BackgroundWorker에서 호출하면 예외가 발생합니다. "STA 스레드에서 생성해야하는"실제 내용으로 나중에 대체되는 "잠시 기다려주십시오"탭 항목을 갖는 데 필요한 동작을 얻는 방법에 대한 다른 아이디어가 있습니까?

답변

1

아마 당신은 백그라운드 작업자가 아닌 UI 스레드에서 프레임 워크 개체를 만들어야한다고 알았습니다. 나는 이것이 질문의 핵심이라고 생각한다.

이것은 어떤 종류의 플러그인 프레임 워크처럼 보입니다. tabContentGenerator이 삽입되었습니다. 그렇다면 장기 실행 작업과 컨트롤을 만드는 두 가지 작업을 사용합니다. 확장 된 TabControl은 첫 번째는 DoWork이고 두 번째는 WorkerCompleted입니다.

(예를 들어, 의사 코드)

public void AddTab(Action backgroundAction, Func<FrameworkElement> constructUiAction) 
{ 
    var tab = ... 

    var worker = new BackgroundWorker(); 

    worker.DoWork += (sender, e) => { backgroundAction(); }; 

    worker.RunWorkerCompleted += (sender, e) => 
     { 
      var ui = constructUiAction(); 
      if (ui != null) tab.Content = ui; 
     }; 


    worker.RunWorkerAsync(); 
} 

다른 옵션이 동작을하는 것이다은 다음 ControlTemplate 통해 UI 스레드에 GUI를 인스턴스화하기 위해 사용하는 FrameworkElementFactory 반환. FrameworkElementFactoryDispatcherObject이 아니며 GUI가 아닌 스레드에서 만들 수 있습니다. 팩토리에서 UI를 만드는 것이 어렵지만 클라이언트가 XAML의 리소스에 컨트롤 템플릿을 지정하면 시각적 트리에서 FrameworkFactoryElement (예 : ((ControlTemplate)FindResource("MyTemplate")).VisualTree)을 가져올 수 있습니다.

public void AddTab(Func<FrameworkElementFactory> tabContentGenerator) 
{ 
    var tab = ... 

    var worker = new BackgroundWorker(); 

    FrameworkElementFactory uiFactory = null; 

    worker.DoWork += (sender, e) => { uiFactory = tabContentGenerator(); } 

    worker.RunWorkerCompleted += (sender, e) => 
     { 
      if (uiFactory != null) 
      { 
       var template = new ControlTemplate(); 
       template.VisualTree = uiFactory; 
       template.Seal(); 
       tab.Content = template.LoadContent(); 
      }; 
     } 

    worker.RunWorkerAsync(); 
} 
+0

이것은 실제로 플러그인 프레임 워크이며 우리는 View-First MVVM 패턴을 사용하고 있습니다. 그러나 뷰 생성을 "View creation"과 "ViewModel creation"으로 분리하여 사용자의 코드를 너무 복잡하게 만들고 싶지 않았습니다. ViewModel은 백그라운드 스레드에서 작성되며 ViewModel 준비가 완료되면보기 만 작성됩니다. 뷰를 만들 때 UI 스레드가 사용 중이고 다른 요청을 처리 할 수 ​​없기 때문에 뷰/ViewModel이 생성되는 동안 앱이 응답하기를 원한다면 실제로 선택할 수있는 방법이 없습니다. 저를 깨끗하게 해주셔서 감사합니다. – splintor

-1

이 작업을 수행하는 간단한 방법은, 뷰 모델에 Waiting 속성을 추가 탭이 기다리고 사용할 수있는 UI 요소를 작성하고 내용에 스타일을 넣어하는 것입니다 예 :

<Grid> 
    <DockPanel> 
    <DockPanel.Style> 
     <Style TargetType="DockPanel"> 
      <Setter Property="Visibility" Value="Visible"/> 
      <DataTrigger Binding="{Binding Waiting}" Value="True"> 
       <Setter Property="Visibility" Value="Collapsed"/> 
      </DataTrigger> 
     </Style> 
     <! -- normal content goes here --> 
    </DockPanel> 
    <DockPanel> 
    <DockPanel.Style> 
     <Style TargetType="DockPanel"> 
      <Setter Property="Visibility" Value="Collapsed"/> 
      <DataTrigger Binding="{Binding Waiting}" Value="True"> 
       <Setter Property="Visibility" Value="Visible"/> 
      </DataTrigger> 
     </Style> 
     <! -- waiting content goes here --> 
    </DockPanel> 
</Grid> 

BackgroundWorker을 시작하는 명령은 DoWork을 호출하기 전에 Waiting을 true로 업데이트하고 개체를 올리십시오 (PropertyChanged). BackgroundWorkerRunWorkerCompleted 이벤트 처리기는 Warning을 false로 설정합니다.

내 탭을 만들 때 AddTab 메서드를 사용하고 있지 않습니다. 대부분의 경우 WPF 객체를 직접 만드는 코드를 작성하면 안됩니다. 선언적으로하는 것이 훨씬 더 생산적입니다. WPF를 배울 때 저는 XAML에서 X를 수행 할 수 없기 때문에 코드에서이 코드를 만들어야한다고 말했습니다. 이에 대한 올바른 대답은 XAML에서 X를 수행하는 방법을 배우는 것입니다. X가 무엇이든 거의 확실하게 할 수 있기 때문입니다.

+0

이것은 일종의 플러그인 환경으로, 다른 팀이 UserControls (XAML에서 생성)을 전달하고 탭에 표시하고 표시해야하지만 사용자가 "배경"에 있어야하므로 처음에는 "로드 중입니다. 잠시만 기다려주세요 ..."라고 표시되고 UI 요소가 준비되면 탭에 표시합니다. – splintor

관련 문제