Domanda

Suppongo che ho bisogno di impostare una maschera di opacità su un controllo WPF che mette in evidenza una parte di esso in una precisa posizione (si supponga che un 50x50 piazza (50;50) posizione).Per creare un DrawingGroup contenente 2 oggetti GeometryDrawing:1 semi-trasparente rettangolo per tutta la dimensione effettiva del controllo e 1 opaca rettangolo di area evidenziata.Poi ho creato una DrawingBrush da questo DrawingGroup, impostare la proprietà Stretch a Nessuno, e impostare questo pennello come OpacityMask di controllo che deve essere mascherato.

Tutto questo funziona bene, mentre nulla è "attaccare" al di fuori dei limiti di controllo.Ma se controllo disegna qualcosa al di fuori dei confini esterni punto diventa un punto di partenza da dove l'opacità della maschera viene applicata se il pennello è allineato al lato) e l'intera maschera turni da quella distanza con conseguente comportamento imprevisto.

Io non riesco a trovare un modo per forzare la maschera da applicare dal controllo limiti o almeno ottenere i reali limiti del controllo (tra cui attaccare le parti), così posso regolare la mia maschera di conseguenza.

Tutte le idee molto apprezzata!

Aggiornamento: Ecco un semplice test case di XAML e screenshot per dimostrare il problema:

Abbiamo 2 nidificati Bordi in Tela e in ultimo con la suddetta piazza:

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

Ecco come appare:

no mask
(fonte: ailon.org)

Ora aggiungiamo un OpacityMask per la seconda frontiera, in modo che ogni parte di esso, tranne la nostra piazza è semi-trasparente:

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

Sembra tutto come previsto:

masked
(fonte: ailon.org)

E ora si aggiunge una linea di tela che si attacca di 10 pixel sulla sinistra del nostro confine:

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

E la maschera turni di 10 pixel a sinistra:

shifted mask
(fonte: ailon.org)

Update2:Come soluzione alternativa, aggiungere un ridicolmente grande rettangolo trasparente al di fuori dei limiti e regolare la mia maschera di conseguenza, ma che è davvero brutto soluzione.

Su update3:Nota:La tela con il rettangolo e la linea c'è solo per fare un esempio di un oggetto che è qualcosa al di fuori di limiti.Nel contesto di questo esempio dovrebbe essere considerato come una sorta di scatola nera.Non è possibile modificare le proprietà di risolvere il problema generale.Questo sarebbe lo stesso solo spostando la linea in modo che non sporga.

È stato utile?

Soluzione

Questione interessante, infatti - ecco cosa ho capito:L'effetto che si verificano sembra essere determinato dalla Viewport concetto/comportamento di TileBrush (vedere Viewbox anche per il quadro completo).A quanto pare implicito casella di delimitazione di un FrameworkElement (cioèla Tela nel tuo caso) è interessata/ampliato da elementi che si conficca fuori di confini in un modo sottile, che è, le dimensioni della casella di espandersi, ma il sistema di coordinate della casella di non scala, piuttosto si espande troppo in fuori limite direzione.

Potrebbe essere più facile per illustrare graficamente, ma a causa di vincoli di tempo mi limiterò a offrire una soluzione di prima e si spiega la procedura che ho preso per il momento, per iniziare:


Soluzione:

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

Si prega di notare che ho modificato unità di +/- 2 pixel qua e là per la precisione dei pixel senza sapere dove l'offset origine, ma penso che questo può essere ignorata per lo scopo dell'esempio e risolto più tardi, se necessario.


Spiegazione:

Per semplificare l'illustrazione si dovrebbe di solito fanno tutte le relative implicite/auto di proprietà esplicito prima.

Il bordo interno riceve auto dimensioni di 198 dal bordo esterno (240 - 20 padding - 2 pixel dedotto da esperimento;non conoscere la loro origine, ma ignorabile, per ora), che è se si specificare in questo modo dovrebbe cambiare nulla, mentre l'utilizzo di altri valori produce modifiche grafiche:

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

Ulteriormente il default implicite Viewport e ViewportUnits in questo modo:

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

Si applicano le DrawingBrush dimensione sovrascrivendo Stretch con None, mantenendo la posizione e la dimensione della base di piastrelle a default e rispetto alla sua casella di delimitazione.Inoltre, è (comprensibilmente) hanno la priorità AlignmentX/AlignmentY, per determinare il posizionamento all'interno della base di piastrelle, che è all'interno del rettangolo di selezione.Il ripristino di quei loro valori di default Center è già dicendo:La maschera turni di conseguenza, il significato esso deve essere minore del rettangolo di selezione, altrimenti loro non sarebbe niente di centro.

Questo può essere preso di più, cambiando ViewportUnits per Absolute, che produrrà nessun grafica a tutti fino a quando le unità sono regolati correttamente, naturalmente;di nuovo, in via sperimentale i seguenti valori espliciti sono corrispondenti auto, mentre l'utilizzo di altri valori produce modifiche grafiche:

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

Ora il maschera di opacità già correttamente allineato con il controllo.Ovviamente c'è un problema di sinistra, anche se, come la maschera di ritaglio la linea è ora, il che non sorprende, date le dimensioni e l'assenza di qualsiasi Stretch effetto.Regolare le dimensioni e la posizione di conseguenza si risolve questo:

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

e

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

Infine il maschera di opacità confronta il controllo limiti come desiderato!


Supplemento:

La richiesta offset determinato dalla deduzione e sperimentare la spiegazione di cui sopra possono essere recuperati in fase di runtime mediante l' VisualTreeHelper Class:

Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);

A seconda del vostro elemento visivo composizione e le esigenze, potrebbe essere necessario fattore in LayoutInformation Class e realizzare l'unione di entrambi per ottenere la onnicomprensivo rettangolo di selezione:

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

Vedere il seguente link per maggiori dettagli su entrambi gli argomenti:

Altri suggerimenti

Sul vostro oggetto Canvas aggiungere 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>

Una soluzione che può essere più ideale di quella attuale sarebbe quella di applicare semplicemente il OpacityMask a un livello superiore. Utilizzando questo codice demo per esempio, è possibile togliere la maschera dal Border e applicarlo alla Window invece. Con un po 'di tweaking si inserisce correttamente:

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

Si dovrebbe scrivere del codice per spostare la maschera quando il Window viene ridimensionata, e per questo motivo si può essere meglio generare la maschera dinamicamente nel code-behind.

La mia domanda per voi è, perché avete bisogno di gestire geometrie che vanno al di fuori dei confini della vostra Canvas?

Dal momento che si dispone di parti che sporgono dal controllo, una sola idea è immagine di controllo per separare dalla maschera di controllo.

<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>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top