2010-03-31 15 views
6

तो मैं एक WPF उपयोगकर्ता नियंत्रण का उपयोग करने के एक डाटासेट से छवियों जहां डाटासेट में प्रत्येक आइटम एक छवि का निर्माण होगा की एक टन उत्पन्न करने के लिए कोशिश कर रहा हूँ ...एक छवि

मैं करने के लिए DataBinding के साथ एक WPF UserControl ड्राइंग मुझे आशा है कि मैं इसे इस तरह से स्थापित कर सकता हूं कि मैं डब्ल्यूपीएफ डाटाबेसिंग का उपयोग कर सकता हूं, और डेटासेट में प्रत्येक आइटम के लिए, मेरे उपयोगकर्ता नियंत्रण का एक उदाहरण बना सकता हूं, निर्भरता गुण सेट कर सकता हूं जो मेरे डेटा आइटम से मेल खाता है, और फिर ड्रा एक छवि को उपयोगकर्ता नियंत्रण है, यह हो रही है, लेकिन मैं समस्या आ रही है सभी कार्य

बड़े पैमाने पर कोड डंप के लिए खेद है (यकीन नहीं है कि क्या डेटा बाइंडिंग या छवि के लिए ड्राइंग मेरी समस्या है), लेकिन मैं इस प्राप्त करने की कोशिश कर रहा है अब कुछ घंटों के लिए काम कर रहे हैं, और डब्ल्यूपीएफ बस मुझे पसंद नहीं है (हालांकि कुछ बिंदु पर जानने के लिए ...)

मेरे उपयोगकर्ता नियंत्रण इस तरह दिखता है:

<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); 
    } 

    } 

संपादित करें:

मैं जोड़ने चाहिए कि इस प्रक्रिया किसी भी त्रुटियों के बिना काम करता है, लेकिन मैं सादे सफेद छवियों से भरा एक निर्देशिका के साथ खत्म, पाठ या कुछ भी नहीं ... और मैंने पुष्टि की है डिबगर का उपयोग कर कि मेरी बुकिंग वस्तुओं उचित डेटा से भर रहे हैं किया जा रहा ...

संपादित करें 2: मैं एक लंबे समय पहले किया जाना चाहिए

कुछ किया, मेरे कैनवास पर एक पृष्ठभूमि निर्धारित करते हैं, लेकिन यह है कि उत्पादन छवि परिवर्तन नहीं किया बिलकुल नहीं, इसलिए मेरी समस्या निश्चित रूप से मेरे ड्राइंग कोड के साथ किसी भी तरह से करना है (हालांकि मेरे डेटाबेस के साथ कुछ भी गलत हो सकता है)

उत्तर

14

RenderTargetBitmap आपके नियंत्रण की वर्तमान स्थिति प्रदान करता है। आपके मामले में आपका नियंत्रण प्रारंभ नहीं हुआ है, इसलिए यह अभी भी सफेद दिखाई देता है।

अपने कोड प्रारंभ करने में ठीक से पहले प्रस्तुत करने के लिए() आपको तीन कार्य करने की जरूरत है:

  1. सुनिश्चित करें कि आपके नियंत्रण मापा गया है और व्यवस्था की।
  2. यदि आपका नियंत्रण लोड की गई घटनाओं का उपयोग करता है, तो सुनिश्चित करें कि आप प्रस्तुति स्रोत से जुड़े हुए हैं।
  3. सुनिश्चित करें कि सभी डिस्पैचर प्राइरिटी.रेंडर और उपर्युक्त घटनाएं पूरी हो चुकी हैं।

आप इन तीन बातें करते हैं, तो आपके RenderTargetBitmap बाहर हूबहू तरह से करने के लिए जब आप इसे एक खिड़की में जोड़ने के लिए नियंत्रण प्रकट होता आ जाएगा।

एक उपाय जबरदस्ती/अपने नियंत्रण

पर व्यवस्था यह रूप में सरल है:

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

इस कोड बलों उपाय/व्यवस्था। डबल में गुजरना सबसे आसान है। चौड़ाई और ऊंचाई के लिए पॉजिटिव इन्फिनिटी क्योंकि यह आपके UserControl को अपनी चौड़ाई और ऊंचाई चुनने की अनुमति देता है। यदि आप स्पष्ट रूप से चौड़ाई/ऊंचाई निर्धारित करते हैं, इससे कोई फर्क नहीं पड़ता है, लेकिन इस तरह आपके उपयोगकर्ता नियंत्रण में आवश्यक होने पर डेटा आवश्यक होने पर स्वचालित रूप से बढ़ने के लिए WPF के लेआउट सिस्टम का उपयोग करने का विकल्प होता है। एक ही टोकन द्वारा टेम्पलेट का उपयोग करना बेहतर होता है। विशिष्ट आकार में गुजरने के बजाए व्यवस्था के लिए डिज़ाइन आकार।

एक PresentationSource

अटैच किया जा रहा यह केवल आवश्यक है अगर आपके नियंत्रण या आपके नियंत्रण में तत्वों लोडेड घटना पर निर्भर हैं।

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

जब HwndSource बनाया गया है तो टेम्पलेट के दृश्य पेड़ को अधिसूचित किया गया है कि इसे "लोड किया गया" है। "उपयोग" ब्लॉक सुनिश्चित करता है कि "उपयोग" कथन (अंतिम बंद घुंघराले ब्रेस) के अंत में टेम्पलेट "अनलोड" है। एक का उपयोग कर() बयान के लिए एक वैकल्पिक GC.KeepAlive उपयोग करने के लिए होगा:

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

डिस्पैचर कतार निस्तब्धता DispatcherPriority.Render

करने के लिए नीचे बस का उपयोग Dispatcher.Invoke:

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

यह सभी रेंडर और उच्च प्राथमिकता कार्यों के पूरा होने के बाद एक खाली कार्रवाई का कारण बनता है। Dispatcher.Invoke विधि प्रेषक कतार को तब तक संसाधित करती है जब तक यह लोड स्तर तक खाली न हो (जो रेंडर के ठीक नीचे है)।

कारण यह आवश्यक है कि कई WPF UI घटक डिस्पैचर कतार का उपयोग प्रसंस्करण में देरी के लिए करते हैं जब तक कि नियंत्रण प्रस्तुत करने के लिए तैयार न हो जाए। यह बाध्यकारी और अन्य परिचालनों के दौरान दृश्य गुणों की अनावश्यक पुन: गणना पर महत्वपूर्ण रूप से कटौती करता है।

इस कोड

जोड़ने के लिए जहां इन सभी तीन चरणों को जोड़ें ताकि आप अपने डेटा संदर्भ (template.Booking = ...) और इससे पहले कि आप RenderTargetBitmap.Render फोन सेट करने के बाद।

अतिरिक्त सुझाव

अपने बाध्यकारी काम करने के लिए एक बहुत ही आसान तरीका है। कोड में, बस एक DataContext के रूप में बुकिंग सेट करें। यह ElementName और बुकिंग संपत्ति का उपयोग करने की जरूरत है निकालता है:

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

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

DataContext का उपयोग करके, टेक्स्ट बॉक्स बंधन बहुत सरल है:

<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 एक के उपयोग का सुझाव देंगे, और आप भी फ़ॉन्ट, लेख आकार और रिक्ति सेट करने के लिए एक शैली का उपयोग पर विचार करना चाहिए:

<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> 

ध्यान दें कि सभी लेआउट द्वारा किया जाता है डब्ल्यूपीएफ के लेआउट ने UserControl आकार और निर्दिष्ट ऊंचाई और मार्जिन दिया। यह भी ध्यान रखें कि टेक्स्टब्लॉक को केवल टेक्स्ट निर्दिष्ट करने की आवश्यकता है - बाकी सब कुछ शैली द्वारा संभाला जाता है।

+0

वाह! जानकारी के लिए धन्यवाद। मैं ऑनलोड का उपयोग नहीं कर रहा हूं, लेकिन अन्य दो संकेतों ने मेरी समस्या तय की है ... धन्यवाद एक टन ... मैं कुछ नया एक्सएएमएल लंच के लिए पूरी तरह से बाहर था , लेकिन मैं इसे प्रतिपादन करने की कोशिश कर रहा था, मैं अब इसमें से कुछ को ठीक कर दूंगा ... धन्यवाद फिर से – LorenVS

+2

अगर मैंने इसे कभी देखा तो एक अच्छी तरह से अर्जित बक्षीस। – RandomEngy

+2

मुझे पता नहीं है। यह उत्तर थोडा छोटा है और नहीं है ' टी बहुत स्पष्ट है। शायद मैं आपने संशोधित किया और इसे थोड़ा बढ़ा दिया ... – Will

0

मुझे लगता है कि समस्या बाध्यकारी में है, जैसा कि आपको संदेह है। Booking प्रॉपर्टी बनाने के बजाय, ImageTemplate इंस्टेंस के DataContext को सेट करने का प्रयास करें, फिर बाइंडिंग पर पथ को उस डेटा ऑब्जेक्ट का केवल उस संपत्ति नाम पर सेट करें जिसका आप उपयोग करना चाहते हैं। यह आपकी समस्या का समाधान नहीं कर सकता है, लेकिन यह बाध्यकारी करने का एक और मानक तरीका है।

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

तुम सब बाध्यकारी काम करने के लिए यदि आप एक Booking उदाहरण के लिए डेटा संदर्भ निर्धारित की जरूरत नहीं होनी चाहिए। इसे आज़माएं और हमें बताएं कि यह काम करता है या नहीं।

+0

मैंने इसे पहले इस तरह से स्थापित किया है, लेकिन यह अभी भी ठीक से काम नहीं कर रहा है ... मुझे इसे बदलने के लिए सटीक xaml याद नहीं है :(मुझे लगता है कि मैं अभी भी कुछ कर रहा हूं हालांकि, ड्रॉइंग सेक्शन में गलत है, क्योंकि मैंने बैकग्राउंड पर बैकग्राउंड पर पृष्ठभूमि सेट की है और मुझे अभी भी एक खाली छवि मिलती है ... – LorenVS

+0

यह आपका ड्राइंग सेक्शन होना चाहिए। दुर्भाग्य से, मुझे वास्तव में इमेजिंग के बारे में कुछ नहीं पता और ऐसे। क्षमा करें। –

2

ठीक है, आपकी समस्याओं में से एक यह है कि आपको प्रस्तुत करने की कोशिश करने से पहले मापने और अपने उपयोगकर्ता नियंत्रण पर व्यवस्थित करने की आवश्यकता है। RenderTargetBitmap वस्तु बनाने से पहले इस रखो:

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

कि कम से कम अपने UserControl प्रतिपादन शुरू करने के लिए मिल जाएगा।

दूसरी समस्या डेटा बाध्यकारी है। मैं उसको क्रैक करने में सक्षम नहीं हूं; बाइंडिंग का मूल्यांकन करने के लिए आपको कुछ अतिरिक्त करने की आवश्यकता हो सकती है। हालांकि, आप इसके आसपास काम कर सकते हैं: यदि आप डेटा बाइंडिंग के बजाए सीधे टेक्स्टब्लॉक सामग्री सेट करते हैं, तो यह काम करता है।