2009-08-19 7 views
12

मान लीजिए कि मुझे एक WPF नियंत्रण पर एक अस्पष्टता मुखौटा सेट करने की आवश्यकता है जो सटीक स्थिति में इसके एक हिस्से को हाइलाइट करता है (50x50 वर्ग (50; 50) स्थिति पर मान लें)। ऐसा करने के लिए मैं एक ड्रॉइंग ग्रुप बनाता हूं जिसमें 2 ज्यामिति ड्रॉइंग ऑब्जेक्ट्स हैं: नियंत्रण के पूरे वास्तविक आकार के लिए 1 सेमी-पारदर्शी आयताकार और हाइलाइट किए गए क्षेत्र के लिए 1 अपारदर्शी आयताकार। फिर मैं इस ड्रॉइंग ग्रुप से ड्रॉइंग ब्रश बना देता हूं, इसे स्ट्रैच प्रॉपर्टी को किसी को भी सेट नहीं करता हूं और इस ब्रश को नियंत्रण के ओपेसिटीमास्क के रूप में सेट करता हूं जिसे मास्क किया जाना चाहिए।सटीक अस्पष्टता मास्क

यह सब ठीक काम करता है जबकि कुछ भी नियंत्रण के सीमाओं से बाहर "चिपकने" नहीं होता है। लेकिन अगर नियंत्रण इसकी सीमा के बाहर कुछ खींचता है तो बाहरी बिंदु एक प्रारंभिक बिंदु बन जाता है जहां से अस्पष्टता मुखौटा लागू होता है (यदि ब्रश उस तरफ गठबंधन होता है) और पूरे मास्क उस दूरी से बदल जाता है जिसके परिणामस्वरूप अप्रत्याशित व्यवहार होता है।

मुझे नियंत्रण की सीमाओं से मास्क को लागू करने के लिए मजबूर करने का कोई तरीका नहीं दिख रहा है या कम से कम नियंत्रण की वास्तविक सीमाएं (भागों को चिपकाने सहित) प्राप्त कर सकते हैं ताकि मैं तदनुसार अपना मुखौटा समायोजित कर सकूं।

किसी भी विचार की अत्यधिक सराहना की!

अद्यतन:

<Border Padding="20" Background="DarkGray" Width="240" Height="240"> 
    <Border Background="LightBlue"> 
     <Canvas> 
      <Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50" 
         Stroke="Red" StrokeThickness="2" 
         Fill="White" 
         /> 
     </Canvas> 
    </Border> 
</Border> 

यहाँ:

हम ऊपर उल्लेख किया वर्ग के साथ पिछले एक में 2 नेस्टेड बॉर्डर्स और कैनवास है: यहाँ एक सरल परीक्षण मामले XAML और इस मुद्दे का प्रदर्शन स्क्रीनशॉट है यह कैसे दिखता है:

no mask http://devblog.ailon.org/devblog/_stuff/maskissue/square-no-mask.gif

अब हम एक OpacityM जोड़ने दूसरी सीमा के आग्रह करें कि हमारे वर्ग को छोड़कर यह के हर हिस्से अर्द्ध पारदर्शी है:

<Border.OpacityMask> 
    <DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top"> 
     <DrawingBrush.Drawing> 
      <DrawingGroup> 
       <GeometryDrawing Brush="#30000000"> 
        <GeometryDrawing.Geometry> 
         <RectangleGeometry Rect="0,0,200,200" /> 
        </GeometryDrawing.Geometry> 
       </GeometryDrawing> 
       <GeometryDrawing Brush="Black"> 
        <GeometryDrawing.Geometry> 
         <RectangleGeometry Rect="50,50,50,50" /> 
        </GeometryDrawing.Geometry> 
       </GeometryDrawing> 
      </DrawingGroup> 
     </DrawingBrush.Drawing> 
    </DrawingBrush> 
</Border.OpacityMask> 

सब कुछ उम्मीद के रूप में दिखता है:

masked http://devblog.ailon.org/devblog/_stuff/maskissue/square-mask.gif

और अब हम कैनवास में रेखा जोड़ने कि हमारे सीमा के बाईं तरफ के 10 पिक्सल बाहर चिपक जाता है:

<Line X1="-10" Y1="150" X2="120" Y2="150" 
     Stroke="Red" StrokeThickness="2" 
     /> 

और मुखौटा बाईं ओर 10 पिक्सल बदलाव:

shifted mask http://devblog.ailon.org/devblog/_stuff/maskissue/square-line-mask.gif

Update2: मैं सीमा के बाहर एक हास्यास्पद बड़े पारदर्शी आयत जोड़ सकते हैं और उसके अनुसार अपना मुखौटा समायोजित लेकिन यह है कि एक बहुत बुरा समाधान नहीं है समाधान के लिए।

अद्यतन 3: नोट: आयत और रेखा के साथ कैनवास कुछ वस्तु के उदाहरण के रूप में है जिसमें इसके बाहर कुछ है। इस नमूने के संदर्भ में इसे किसी प्रकार का ब्लैक बॉक्स माना जाना चाहिए। आप सामान्य समस्या को हल करने के लिए इसकी संपत्तियों को नहीं बदल सकते हैं। यह लाइन को स्थानांतरित करने जैसा ही होगा, इसलिए यह बाहर नहीं निकलता है।

+3

यह अपने कोड को देखने के लिए मददगार होगा इसके लिए। – Charlie

+1

विस्तृत प्रजनन केस जोड़ने के लिए नमूना कोड और स्क्रीनशॉट –

+1

+1 जोड़ा गया। –

उत्तर

8

दिलचस्प मुद्दा वास्तव में - यहाँ है कि मैं क्या समझ गए होंगे: प्रभाव आप अनुभव कर रहे (पूरी तस्वीर के लिए भी Viewbox देखें) TileBrush की Viewport अवधारणा/व्यवहार द्वारा निर्धारित किया जा रहा है। स्पष्ट रूप से एक FrameworkElement (यानि आपके मामले में कैनवास) के अंतर्निहित bounding box को सूक्ष्म तरीके से सीमाओं से चिपकने वाले तत्वों द्वारा प्रभावित/विस्तारित किया जाता है, यानी बॉक्स के आयाम विस्तारित होते हैं लेकिन बॉक्स की समन्वय प्रणाली स्केल नहीं होती है, बल्कि सीमाओं की दिशा में भी फैलता है।

ऐसा नहीं है कि वर्णन करने के लिए रेखांकन आसान हो सकता है, लेकिन समय की कमी की वजह से मैं सिर्फ पहली बार एक समाधान की पेशकश करेंगे और आपको प्राप्त करने के लिए चरणों का मैं पल के लिए लिया है समझा जाएगा शुरू कर दिया:


समाधान:

<Border Background="LightBlue" Width="198" Height="198"> 
    <Border.OpacityMask> 
     <DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center" 
         Viewport="-10,0,222,202" ViewportUnits="Absolute"> 
      <DrawingBrush.Drawing> 
       <DrawingGroup> 
        <GeometryDrawing Brush="#30000000"> 
         <GeometryDrawing.Geometry> 
          <RectangleGeometry Rect="-10,0,220,200" /> 
         </GeometryDrawing.Geometry> 
        </GeometryDrawing> 
        <GeometryDrawing Brush="Black">...</GeometryDrawing> 
       </DrawingGroup> 
      </DrawingBrush.Drawing> 
     </DrawingBrush> 
    </Border.OpacityMask> 
    <Canvas x:Name="myGrid">...</Canvas> 
</Border> 

कृपया ध्यान दें कि मैं जानते हुए भी जहां ऑफसेट का जन्म बिना पिक्सेल परिशुद्धता के लिए +/- 2 पिक्सेल यहाँ और वहाँ से इकाइयों समायोजित किया है, लेकिन मैं इस उद्देश्य के लिए अनदेखा किया जा सकता लगता है टी का उदाहरण के बाद वह उदाहरण और हल हो गया।


स्पष्टीकरण:

चित्रण एक आम तौर पर सभी संबंधित गर्भित/ऑटो गुण स्पष्ट पहले करना चाहिए आसान बनाने के लिए।

आंतरिक सीमा को बाहरी सीमा से 240 के ऑटो आयाम प्राप्त होते हैं (240 - 20 पैडिंग - 2 पिक्सेल प्रयोग द्वारा कटौती; उनके मूल को नहीं जानते हैं, लेकिन अभी अनजान हैं), यह है कि यदि आप इसे निम्नानुसार निर्दिष्ट करते हैं , बदलना चाहिए, जबकि अन्य मानों का उपयोग चित्रमय परिवर्तन पैदावार:

<Border Background="LightBlue" Width="198" Height="198">...</Border> 

इसके अलावा डिफ़ॉल्ट Viewport गर्भित और ViewportUnits तो जैसे:

<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top" 
    Viewport="0,0,1,1" ViewportUnits="RelativeToBoundingBox">...</DrawingBrush> 

आप 012,343 अधिभावी द्वारा DrawingBrush आकार को लागू कर रहे हैंNone के साथ, डिफ़ॉल्ट रूप से बेस टाइल की स्थिति और आयाम को रखते हुए और को इसके बाउंडिंग बॉक्स के सापेक्ष रखते हुए। इसके अतिरिक्त आप (समझदारी से) AlignmentX/AlignmentY ओवरराइड कर रहे हैं, जो आधार टाइल के भीतर प्लेसमेंट निर्धारित करता है, जो इसके बाउंडिंग बॉक्स के भीतर है।उन्हें Center के अपने डिफ़ॉल्ट पर रीसेट करने से पहले ही यह कह रहा है: मुखौटा तदनुसार बदलता है, जिसका अर्थ है इसे बाउंडिंग बॉक्स से छोटा होना चाहिए, अन्यथा उनके भीतर केंद्र के लिए कुछ भी नहीं होगा।

इसे ViewportUnits से Absolute में बदलकर आगे ले जाया जा सकता है, जो तब तक ग्राफिक्स नहीं लाएगा जब तक इकाइयों को बिल्कुल सही तरीके से समायोजित नहीं किया जाता है; फिर से, प्रयोग से निम्नलिखित स्पष्ट मान, ऑटो वाले मिलान कर रहे हैं अन्य मूल्यों का उपयोग करते समय पैदावार चित्रमय परिवर्तन:

<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center" 
    Viewport="0,0,202,202" ViewportUnits="Absolute">...</DrawingBrush> 

अब अस्पष्टता मुखौटा पहले से ही नियंत्रण के साथ ठीक से संरेखित करता है। स्पष्ट रूप से एक समस्या बाकी है, क्योंकि मुखौटा अब लाइन को क्लिप कर रहा है, जो इसके आकार और Stretch प्रभाव की अनुपस्थिति को लेकर कोई आश्चर्य नहीं है। इसके आकार और स्थिति का समायोजन तदनुसार इस का समाधान करता है:

<RectangleGeometry Rect="-10,0,220,200" /> 

और

<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center" 
    Viewport="-10,0,222,202" ViewportUnits="Absolute">...</DrawingBrush> 

अंत में अस्पष्टता मुखौटा नियंत्रण सीमा के रूप में वांछित से मेल खाता है!


अनुपूरक:

स्पष्टीकरण ऊपर VisualTreeHelper Class के माध्यम से कार्यावधि में प्राप्त किए जा सकें में कटौती और प्रयोग द्वारा निर्धारित आवश्यक ऑफसेट:

Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid); 

अपने दृश्य तत्व पर निर्भर करता है रचना और जरूरतों के लिए आपको LayoutInformation Class में कारक बनाने की आवश्यकता हो सकती है और सभी अंतर्निहित बाउंडिंग बॉक्स प्राप्त करने के लिए दोनों का संघ बना सकते हैं:

Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid); 
Rect layoutSlot = LayoutInformation.GetLayoutSlot(myGrid); 
Rect boundingBox = descendantBounds; 
boundingBox.Union(layoutSlot); 

दोनों विषयों पर अधिक जानकारी के लिए निम्न लिंक देखें:

+1

विस्तृत और वास्तव में काम करने वाले समाधान के लिए धन्यवाद! मुझे अभी तक यह पता लगाना नहीं है कि यह क्यों काम करता है (व्यूपोर्ट अवधारणाओं पर पढ़ें) लेकिन ऐसा लगता है कि यह पूरी तरह से काम करता है (और सीमा आयामों को निर्दिष्ट किए बिना)। मैं अब आपका जवाब स्वीकार करने जा रहा हूं। यद्यपि एक अधूरा समस्या है हालांकि: वर्तमान में मुझे इस बात पर कोई फर्क नहीं पड़ता कि लाइन को फिसल रहा है और आपका समाधान अब मेरे लिए पूरी तरह से काम करता है, लेकिन सामान्य स्थिति में मुझे अभी भी यह निर्धारित करने का कोई तरीका नहीं पता कि आवश्यक "-10" पिक्सेल स्वचालित रूप से ऑफसेट हो जाता है। –

+1

किसी भी तरह से मुझे इस शेष मुद्दे की उम्मीद है;) मुझे कुछ मिनट मिल गए हैं और कम से कम रनटाइम पर इन ऑफ़सेट को निर्धारित करने के लिए मेरे उत्तर को एक विधि के साथ पूरक किया है। आप इसे अंततः बाइंडिंग के माध्यम से वहां से ले जा सकते हैं, लेकिन उल्लिखित लेखों में उल्लिखित प्रतिपादन प्रक्रिया की जटिलताओं को देखते हुए यह अन्य मुद्दों को भी ट्रिगर कर सकता है, उदाहरण के लिए प्रदर्शन के अनुसार। –

0

एक वर्कअराउंड जो आपके वर्तमान से अधिक आदर्श हो सकता है, वह उच्च स्तर पर OpacityMask को लागू करना होगा। उदाहरण के लिए इस डेमो कोड का उपयोग करके, आप Border से मास्क को हटा सकते हैं और इसे Window पर लागू कर सकते हैं।यह फेरबदल का एक सा ठीक से फिट बैठता है के साथ:

<Window.OpacityMask> 
    <DrawingBrush AlignmentX="Left" AlignmentY="Top" Stretch="None"> 
    <DrawingBrush.Drawing> 
     <DrawingGroup> 
     <GeometryDrawing Brush="#30000000"> 
      <GeometryDrawing.Geometry> 
      <RectangleGeometry Rect="0,0,300,300"/> 
      </GeometryDrawing.Geometry> 
     </GeometryDrawing> 
     <GeometryDrawing Brush="Black"> 
      <GeometryDrawing.Geometry> 
      <RectangleGeometry Rect="92,82,50,50"/> 
      </GeometryDrawing.Geometry> 
     </GeometryDrawing> 
     </DrawingGroup> 
    </DrawingBrush.Drawing> 
    </DrawingBrush> 
</Window.OpacityMask> 

आप मुखौटा ले जाने के लिए जब Window का आकार बदलने पर कुछ कोड लिखने के लिए होता है, और इस कारण आप code- में गतिशील रूप से मुखौटा पैदा करने से बेहतर हो सकता है पीछे।

आपके लिए मेरा प्रश्न है, आपको अपने Canvas की सीमाओं के बाहर जाने वाली ज्यामिति को संभालने की आवश्यकता क्यों है?

+0

सामान्य स्थिति में मैं अपने नियंत्रण में से उच्च स्तर के कंटेनर के साथ गड़बड़ नहीं कर सकता। मैं उच्च स्तर की वस्तुओं का "मालिक" नहीं हूं और अपनी संपत्तियों के साथ गड़बड़ नहीं कर सकता जैसे कि मैं फिट देखता हूं।और वैसे भी इस उदाहरण में रेखा खिड़की की सीमाओं के बाहर भी चिपक सकती है और मुझे यकीन नहीं है कि एक ही मुद्दा नहीं होगा। आपके प्रश्न के लिए कई संभावित परिदृश्य हैं लेकिन इसे आसान बनाने के लिए मैं आपको एक सुपर सरल दे सकता हूं: यदि आप (0; 0) से मोटी रेखा खींचते हैं तो यह लाइन के रास्ते के बावजूद बायीं ओर रह जाएगा WPF में डिफ़ॉल्ट रूप से खींचा गया। –

1

अपने कैनवास ऑब्जेक्ट पर ClipToBounds = "True" जोड़ें।

<Canvas ClipToBounds="True"> 

    <Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50" 
       Stroke="Red" StrokeThickness="2" 
       Fill="White" /> 
    <Line X1="-10" Y1="150" X2="120" Y2="150" 
      Stroke="Red" StrokeThickness="2"/> 

</Canvas> 
+1

हां, यह इस विशेष परीक्षण मामले में इस मुद्दे को हल करता है लेकिन यह मुद्दा नहीं था। आयत और रेखा के साथ कैनवास कुछ वस्तु के उदाहरण के रूप में है जिसमें इसके बाहर कुछ है। इस नमूने के संदर्भ में इसे किसी प्रकार का ब्लैक बॉक्स माना जाना चाहिए। आप सामान्य समस्या को हल करने के लिए इसकी संपत्तियों को नहीं बदल सकते हैं। यह लाइन को स्थानांतरित करने जैसा ही होगा, इसलिए यह बाहर नहीं निकलता है। –

+0

हाँ, मुझे बिल्कुल यकीन नहीं था कि अगर मदद मिलेगी या नहीं, तो मुझे कोई दूसरा समाधान नहीं मिला। सौभाग्य। – Crispy

+0

वाह। हालांकि इसने ओपी की समस्या को हल नहीं किया, लेकिन यह निश्चित रूप से मेरा हल ... और बहुत ही सरल है। +1 – Daniel

0

चूंकि आपके पास नियंत्रण से बाहर निकलने वाले हिस्सों हैं, इसलिए एक विचार नियंत्रण मास्क से नियंत्रण छवि को अलग करना है।

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 

    <Border Padding="20" Background="DarkGray" Width="240" Height="240"> <!-- user container --> 

     <Grid> <!-- the control --> 
      <Border Background="LightBlue" HorizontalAlignment="Stretch"> <!-- control mask--> 
       <Canvas> 
        <Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50" 
           Stroke="Red" StrokeThickness="2" 
           Fill="White" 
           /> 

        <Canvas.OpacityMask> 
         <DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top" TileMode="None"> 
          <DrawingBrush.Drawing> 
           <DrawingGroup> 
            <GeometryDrawing Brush="#30000000"> 
             <GeometryDrawing.Geometry> 
              <RectangleGeometry Rect="0,0,200,200" /> 
             </GeometryDrawing.Geometry> 
            </GeometryDrawing> 
            <GeometryDrawing Brush="Black"> 
             <GeometryDrawing.Geometry> 
              <RectangleGeometry Rect="50,50,50,50" /> 
             </GeometryDrawing.Geometry> 
            </GeometryDrawing> 
           </DrawingGroup> 
          </DrawingBrush.Drawing> 
         </DrawingBrush> 
        </Canvas.OpacityMask> 
       </Canvas> 
      </Border> 

      <Canvas> <!-- control image--> 
       <Line X1="-10" Y1="150" X2="120" Y2="150" Stroke="Red" StrokeThickness="2"/> 
      </Canvas> 
     </Grid> 
    </Border> 
</Window> 
+0

आपने कार्य को फिर से बदलने की कोशिश की है। जैसा कि मैंने इस सवाल में कहा था कि आयत और रेखा के साथ आंतरिक कैनवास कुछ सामग्री का एक उदाहरण है जिसमें भागों के हिस्से हैं जो इसकी सीमाओं से चिपके हुए हैं। इसे एक काले बॉक्स की तरह माना जाना चाहिए जिसका हमारा नियंत्रण नहीं है। मान लीजिए कि हम नहीं जानते कि यह क्या है और हम इसे बदल नहीं सकते हैं। अन्यथा कार्य तुच्छ हो जाता है। –