2010-01-09 2 views
2

TabControl 개체에 프로그래밍 방식으로 동적으로 추가 된 탭이있는 프로그램이 있습니다. 내가하고 싶은 일은 이러한 각 탭의 Content 값을 PNG로 렌더링하는 것입니다. 나는 StackOverflow 나 다른 곳에서 픽업 한 스크립트를 사용하고있다. 내 코드는 다음과 같습니다 : 원하는대로TabControl의 다른 탭에서 콘텐츠 컨트롤 렌더링

if (tabPanel.Items.Count > 0) 
{ 
    SaveFileDialog fileDialog = new SaveFileDialog(); 
    fileDialog.Filter = "PNG|*.png"; 
    fileDialog.Title = "Save Tabs"; 
    fileDialog.ShowDialog(); 

    if (fileDialog.FileName.Trim().Length > 0) 
    { 
     try 
     { 
      string filePrefix = fileDialog.FileName.Replace(".png", ""); 
      int tabNo = 1; 
      foreach (TabItem tabItem in tabPanel.Items) 
      { 
       string filename = filePrefix + "_" + tabNo + ".png"; 

       TabContentControl content = tabItem.Content as TabContentControl; 
       Rect rect = new Rect(content.RenderSize); 
       RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right, (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default); 
       rtb.Render(content); 

       BitmapEncoder pngEncoder = new PngBitmapEncoder(); 
       pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); 

       System.IO.MemoryStream ms = new System.IO.MemoryStream(); 
       pngEncoder.Save(ms); 
       System.IO.File.WriteAllBytes(filename, ms.ToArray()); 
       ms.Close(); 

       tabNo++; 
      } 
     } 
     catch (Exception ex) 
     { 
      // log exception 
     } 
    } 
} 

이 코드는 작동 내가 겪었하고이 코드를 호출하기 전에 렌더링해야 모든 탭을 본 경우. 앞으로 나아가고 TabContentControl에서 렌더링 된 올바른 내용으로 filePrefix_1.png, filePrefix_2.png 등을 만듭니다. 그러나 모든 탭을보기 전에이 코드를 사용하는 핸들러를 호출하면 content.RenderSize{0.0, 0.0}이기 때문에 코드에서 new RenderTargetBitmap(...)에 예외가 발생합니다. 볼 수없는 탭의 렌더링 크기를 한 번 보았을 때 출력 된 PNG의 크기는 정확하지만 완전히 비어 있습니다.

그래서 TabContentControl의 렌더링을 강제로 수행 할 방법이 필요합니다. Render 이벤트는 UIElement를 렌더링해야 할 때와 같이 실행되는 것처럼 보입니다. 이 문제를 해결하기 위해 내가 수행 할 수있는 모든 속임수인가?

나는 또한 탭이 생성 될 때 PAGE_LOADED 이벤트 처리기에 다음 코드를 추가하여 탭의 컨텐츠 그림에에 "트릭"WPF를 시도했다 :

void Page_Loaded(object sender, RoutedEventArgs e) 
{ 
    // irrelevant code 
    foreach (// iterate over content that is added to each tab) 
    { 
     TabItem tabItem = new TabItem(); 
     // load content 
     tabPanel.Items.Add(tabItem); 
     tabItem.IsSelected = true; 
    } 
    // tabPanel.SelectedIndex = 0; 
} 

을 때 Page_Loaded 핸들러의 마지막 행 마지막 탭에 포커스가 있고 내용에 대해 RenderSize 속성이 정의되어 있습니다. 마지막 줄을 주석 처리하지 않으면 첫 번째 탭에 포커스가 있으며 같은 동작을합니다. 다른 탭에는 렌더링 정보가 없습니다.

답변

2

this blog post 덕분에 마침내 알아 냈습니다. 해결책은 렌더링 우선 순위로 빈 대리자를 호출하는 Refresh 메서드를 사용하여 UIElement에 대한 확장 메서드를 만드는 것이 었습니다. 렌더링 우선 순위로 일정을 잡으면 다른 중요한 항목이 모두 실행되어 탭이 변경되었습니다. 경우 블로그 여기에 복제

코드가 삭제됩니다 :

public static class ExtensionMethods 
{ 
    private static Action EmptyDelegate = delegate() { }; 

    public static void Refresh(this UIElement uiElement) 
    { 
     uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate); 
    } 
} 

void Page_Loaded(object sender, RoutedEventArgs e) 
{ 
    // irrelevant code 
    foreach (// iterate over content that is added to each tab) 
    { 
     TabItem tabItem = new TabItem(); 
     // load content 
     tabPanel.Items.Add(tabItem); 
     tabItem.IsSelected = true; 
     tabItem.Refresh(); 
    } 
    // tabPanel.SelectedIndex = 0; 
} 

는이 기능을 사용하는 데 필요한 코드 파일의 확장 네임 스페이스를 포함, 그것을 사용하려면 그것은 방법의 목록에 표시됩니다.

1

이것은 실제로 이상적인 솔루션은 아니지만 탭을 만들어 탭 컨트롤에 추가하면 현재 열려있는 탭을 저장 한 다음 방금 만든 탭으로 전환 한 다음 뒤로. 사용자에게 약간의 깜박 거림이 있지만 실제로는 개체를 그리기 위해 winforms (또는 wpf)를 속였습니다.

+0

비슷한 일을 시도했지만 나에게있어 보이는 탭을 프로그래밍 방식으로 변경하는 방법을 찾지 못했습니다. 'TabControl.SelectedItem'을'TabItem'으로 설정하려고 시도하고'TabControl.SelectedIndex' 속성을 증가 시키려고 시도했습니다. – sohum

+0

사실 ..... 방금 처리기 내에서 저장 버튼을 클릭 한 후 탭을 변경하려고했는데 탭이 다시 그려지는 기회가 없다고 생각했습니다. 탭을 추가 할 때 이렇게하면 문제가되지 않습니다. – sohum

+0

그냥 시도했지만 작동하지 않았습니다. WPF는 최적화 된 것으로 보이고 마지막 탭만 페인트합니다. – sohum