2010-03-31 6 views
6

그래서 데이터 세트의 각 항목이 이미지를 생성 할 데이터 집합에서 이미지의 톤을 생성하는 WPF 사용자 정의 컨트롤을 사용하려고 해요 ...는 이미지

I에 데이터 바인딩으로 WPF UserControl을 그리기 내가 WPF 데이터 바인딩을 사용할 수 있고 데이터 셋의 각 항목에 대해 내 사용자 정의 컨트롤의 인스턴스를 만들고 내 데이터 항목에 해당하는 종속성 속성을 설정 한 다음 사용자 컨트롤을 이미지로 가져 오는 데 문제가 있습니다. 모든 작업을 수행하는 데 문제가 있습니다. 데이터 바인딩 또는 이미지 그리기가 내 문제인지 여부는 알 수 없습니다.

대용량 코드 덤프는 죄송합니다. 몇 시간 동안 일하고 WPF 그냥 날 좋아하지 않는다 (...하지만 어떤 점에서 배울 수있다)

내 사용자 컨트롤은 다음과 같습니다 : I가 유형 "예약"의 종속성 속성을 추가 한

<UserControl x:Class="Bleargh.ImageTemplate" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:c="clr-namespace:Bleargh" 
x:Name="ImageTemplateContainer" 
    Height="300" Width="300"> 
<Canvas> 
    <TextBlock Canvas.Left="50" Canvas.Top="50" Width="200" Height="25" FontSize="16" FontFamily="Calibri" Text="{Binding Path=Booking.Customer,ElementName=ImageTemplateContainer}" /> 
    <TextBlock Canvas.Left="50" Canvas.Top="100" Width="200" Height="25" FontSize="16" FontFamily="Calibri" Text="{Binding Path=Booking.Location,ElementName=ImageTemplateContainer}" /> 
    <TextBlock Canvas.Left="50" Canvas.Top="150" Width="200" Height="25" FontSize="16" FontFamily="Calibri" Text="{Binding Path=Booking.ItemNumber,ElementName=ImageTemplateContainer}" /> 
    <TextBlock Canvas.Left="50" Canvas.Top="200" Width="200" Height="25" FontSize="16" FontFamily="Calibri" Text="{Binding Path=Booking.Description,ElementName=ImageTemplateContainer}" /> 
</Canvas> 
</UserControl> 

그리고

public partial class ImageTemplate : UserControl 
{ 
    public static readonly DependencyProperty BookingProperty = DependencyProperty.Register("Booking", typeof(Booking), typeof(ImageTemplate)); 
    public Booking Booking 
    { 
    get { return (Booking)GetValue(BookingProperty); } 
    set { SetValue(BookingProperty, value); } 
    } 

    public ImageTemplate() 
    { 
    InitializeComponent(); 
    } 
} 

내가 컨트롤 렌더링하려면 다음 코드를 사용하고 있습니다 :

List<Booking> bookings = Booking.GetSome(); 
    for(int i = 0; i < bookings.Count; i++) 
    { 
    ImageTemplate template = new ImageTemplate(); 
    template.Booking = bookings[i]; 

    RenderTargetBitmap bitmap = new RenderTargetBitmap(
    (int)template.Width, 
    (int)template.Height, 
    120.0, 
    120.0, 
    PixelFormats.Pbgra32); 
    bitmap.Render(template); 

    BitmapEncoder encoder = new PngBitmapEncoder(); 
    encoder.Frames.Add(BitmapFrame.Create(bitmap)); 

    using (Stream s = File.OpenWrite(@"C:\Code\Bleargh\RawImages\" + i.ToString() + ".png")) 
    { 
    encoder.Save(s); 
    } 

    } 
0123을 나는 데이터 바인딩 된 값에 대한 원천이 될 것입니다 바라고 내 사용자 컨트롤

편집 :

나는 프로세스가 어떤 오류없이 작동한다는 것을 덧붙여 야하지만 텍스트 나 아무것도 아닌 일반 흰색 이미지로 가득 찬 디렉토리로 끝난다. 그리고 나는 디버거를 사용하여 확인했다. 나는 오래 전에 했어야

뭔가나요, 내 캔버스에 배경을 설정하지만 출력 이미지를 변경하지 않은 : 예약 객체는

EDIT 2 ... 적절한 데이터로 채워되고있다 전혀, 내 문제는 내 드로잉 코드 (비록 내 데이터 바인딩에 문제가있을지라도)와 어떻게 든 관계가있다.

답변

14

RenderTargetBitmap은 컨트롤의 현재 상태를 렌더링합니다. 귀하의 경우에는 귀하의 컨트롤이 초기화되지 않았으므로 여전히 흰색으로 보입니다.

  1. 이 있는지 확인 컨트롤 측정 및 정렬되었습니다

    당신은 세 가지를 할 필요) (코드가 제대로 전에 렌더링 초기화에 도착합니다.
  2. 컨트롤에서로드 된 이벤트를 사용하는 경우 PresentationSource에 연결되어 있는지 확인하십시오.
  3. 모든 DispatcherPriority.Render 및 above 이벤트가 완료되었는지 확인하십시오.

RenderTargetBitmap은 Windows에 컨트롤을 추가 할 때 컨트롤이 나타나는 방식과 동일합니다./배열

template.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 
template.Arrange(new Rect(template.DesiredSize)); 

이 코드 힘 측정 :

측정 값을 강제로 /이 같이 간단 컨트롤

에 정렬합니다. UserControl이 자체 Width와 Height를 선택할 수 있기 때문에 width와 height에 대해 double.PositiveInfinity를 전달하는 것이 가장 간단합니다. 명시 적으로 너비/높이를 설정하면 큰 문제가되지 않지만 UserControl에는 WPF의 레이아웃 시스템을 사용하여 데이터가 예상보다 커지면 필요한 경우 자동으로 커질 수 있습니다. 같은 토큰을 사용하면 특정 크기로 전달하는 대신 배열에 template.DesiredSize를 사용하는 것이 좋습니다. PresentationSource

부착

컨트롤 또는 컨트롤 내의 요소가로드 이벤트에 의존하는 경우에만 필요합니다.

using(var source = new HwndSource(new HwndSourceParameters()) 
         { RootVisual = template }) 
{ 
    ... 
} 

HwndSource가 만들어지면 템플릿의 시각적 트리에 "로드 됨"이라는 알림이 표시됩니다. "using"블록은 "using"문장의 마지막에 템플릿이 "언로드"되었는지 확인합니다 (마지막 닫기 중괄호). 그냥 사용 DispatcherPriority.Render

까지

GC.KeepAlive(new HwndSource(...) { ... }); 

디스패처 큐를 비우기 Dispatcher.Invoke :하십시오 사용() 문에 대한 대안은 GC.KeepAlive를 사용하는 것입니다

Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => {})); 

이렇게하면 모든 렌더링 및 우선 순위가 높은 작업이 완료된 후에 빈 작업이 호출됩니다. Dispatcher.Invoke 메서드는 디스패처 큐가로드 된 수준 (렌더링 바로 아래)에 도달 할 때까지 처리합니다.

많은 WPF UI 구성 요소가 컨트롤을 렌더링 할 준비가 될 때까지 처리를 지연시키기 위해 Dispatcher 큐를 사용하는 것이 필요한 이유입니다. 이렇게하면 바인딩 및 기타 작업 중에 시각적 속성의 불필요한 재 계산이 크게 줄어 듭니다.당신이 당신의 데이터 컨텍스트 (template.Booking = ...) 및 RenderTargetBitmap.Render를 호출하기 전에를 설정 한 후 다음 단계의 세 가지를 모두 추가 코드

를 추가 할 수

.

추가 제안

은 바인딩 작업을 만들 수있는 더 쉬운 방법이있다. 코드에서 예약을 DataContext로 설정하기 만하면됩니다. 이 경우 ElementName 및 예약 속성을 사용할 필요가 제거합니다 DataContext를을 사용하여

foreach(var booking in Booking.GetSome()) 
{ 
    var template = new ImageTemplate { DataContext = booking }; 

    ... code from above ... 
    ... RenderTargetBitmap code ... 
} 

을, 바인딩 텍스트 상자가 크게 간소화된다

<UserControl ...> 
    <Canvas> 
    <TextBlock ... Text="{Binding Customer}" /> 
    <TextBlock ... Text="{Binding Location}" /> 
    <TextBlock ... Text="{Binding ItemNumber}" /> 
    <TextBlock ... Text="{Binding Description}" /> 

당신은 예약하는 DependencyProperty를 사용하기위한 특별한 이유가있는 경우 여전히 <UserControl> 수준에서의 DataContext를 설정하기보다는 ElementName 사용하여 바인딩을 단순화 할 수 있습니다 :

<UserControl ... 
    DataContext="{Binding Booking, RelativeSource={RelativeSource Self}}"> 
    <Canvas> 
    <TextBlock ... Text="{Binding Customer}" /> 

는 또한이 목적을 위해 StackPanel 대신 Canvas (A)의 사용을 권장 것이며, 당신은 또한 글꼴, 텍스트 크기 및 간격 설정하는 스타일을 사용하는 것이 좋습니다 : 모든 레이아웃에 의해 이루어집니다

<UserControl ... 
    Width="300" Height="300"> 

    <UserControl.Resources> 
    <Style TargetType="TextBlock"> 
     <Setter Property="FontSize" Value="16" /> 
     <Setter Property="FontFamily" Value="Calibri" /> 
     <Setter Property="Height" Value="25" /> 
     <Setter Property="Margin" Value="50 25 50 0" /> 
    </Style> 
    </UserControl.Resources> 

    <StackPanel> 
    <TextBlock Text="{Binding Customer}" /> 
    <TextBlock Text="{Binding Location}" /> 
    <TextBlock Text="{Binding ItemNumber}" /> 
    <TextBlock Text="{Binding Description}" /> 
    </StackPanel> 
</UserControl> 

주 WPF의 레이아웃에는 UserControl 크기와 지정된 높이와 여백이 있습니다. TextBlock은 텍스트 만 지정하면됩니다. 다른 모든 것은 스타일로 처리됩니다.

+0

와우! 정보를 제공해 주셔서 감사합니다. OnLoad를 전혀 사용하지 않고 있지만 다른 두 가지 힌트가 내 문제를 해결했습니다 ... 감사합니다. 톤 ... XAML의 새로운 일부는 완전히 점심 식사에 나갔습니다. , 그러나 나는 단지 그것을 렌더링하려고 노력했다. 나는 지금 그 중 일부를 고쳐 놓을 것이다 ... Thanks again again – LorenVS

+2

내가 그것을 본다면 잘 번만 현상금 – RandomEngy

+2

나는이 대답이 좀 짧고 isn ' 어쩌면 내가 분명해. f를 수정하고 조금 확장했습니다 ... – Will

0

나는 문제가 바인딩에 있다고 생각합니다. Booking 속성을 만드는 대신 ImageTemplate 인스턴스의 DataContext을 설정 한 다음 바인딩의 경로를 사용하려는 데이터 개체의 속성 이름으로 설정하십시오. 그것은 일 수 있습니다 귀하의 문제를 해결하지 않지만 더 바인딩 바인딩을하는 방법.

<TextBlock ... Text="{Binding Path=Customer}" /> 

은 당신이 Booking 인스턴스에 대한 데이터 컨텍스트를 설정하는 경우 바인딩 작업을 위해 필요한 모든이어야한다. 시도해보고 작동하는지 알려주십시오.

+0

이전에 이런 식으로 설정했지만 여전히 제대로 작동하지 않는 것 같습니다 ... 정확한 xaml을 다시 기억할 수는 없습니다. (나는 여전히 내가 뭔가를하고 있다고 생각합니다. 드로잉 섹션에서 잘못된, 나는 검정으로 usercontrol에 배경을 설정했기 때문에 나는 여전히 빈 이미지를 얻을 수 ... – LorenVS

+0

불행히도, 나는 정말 이미징에 대해 아무것도 몰라요. –

2

글쎄, 당신의 문제 중 하나는 렌더링을 시도하기 전에 UserControl에서 Measure and Arrange를 호출해야한다는 것입니다. RenderTargetBitmap 객체를 만들기 전에 이것을 넣으십시오 : 적어도 렌더링을 시작하려면 UserControl을 얻게 될 것입니다 :

template.Measure(new Size(template.Width, template.Height)); 
template.Arrange(new Rect(new Size(template.Width, template.Height))); 

두 번째 문제는 데이터 바인딩입니다. 나는 그것을 해칠 수 없었다. 바인딩을 평가하기 위해 수행해야 할 추가 작업이있을 수 있습니다. 그러나이 문제를 해결할 수 있습니다. 데이터 바인딩을 사용하지 않고 TextBlock 내용을 직접 설정하면 제대로 작동합니다.