Question

Supposons que je dois placer un masque d'opacité sur un contrôle WPF qui met en évidence une partie de celui-ci dans une position précise (supposons un carré 50x50 à (50; 50) la position). Pour ce faire, que je crée un DrawingGroup contenant 2 objets GeometryDrawing: 1 rectangle semi-transparent pour toute la taille réelle du contrôle et 1 rectangle opaque pour la zone en surbrillance. Puis-je créer un DrawingBrush de cette DrawingGroup, définissez sa propriété extensible à Aucun et définir cette brosse OpacityMask du contrôle qui doit être masquée.

Tout cela fonctionne très bien alors que rien est « coller » hors limites dudit contrôle. Mais si le contrôle dessine quelque chose à l'extérieur de celui-ci délimite est le point extérieur devient un point de départ à partir duquel le masque d'opacité est appliquée (si la brosse est alignée sur le côté) et l'ensemble des déplacements de masque par cette distance entraînant un comportement inattendu.

Je ne peux pas sembler trouver un moyen de forcer le masque à appliquer à partir des bornes de contrôle ou au moins obtenir les limites réelles du contrôle (y compris le collage des pièces) afin que je puisse régler mon masque en conséquence.

Toutes les idées très appréciés!

Mise à jour: Voici un test simple cas XAML et captures d'écran montrant la question:

Nous avons 2 frontières imbriquées et Canvas dans le dernier carré avec le mentionné ci-dessus:

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

Voilà à quoi il ressemble:


(source: ailon.org )

Maintenant, nous ajoutons un OpacityMask à la deuxième frontière de sorte que chaque partie, sauf notre carré est semi-transparent:

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

Tout semble comme prévu:


(source: ailon.org )

Et maintenant, on ajoute une ligne à la toile qui colle 10 pixels sur la gauche de la frontière:

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

et le masque se déplace de 10 pixels vers la gauche:


(source: ailon.org )

Update2 . Pour contourner ce problème ajouter un rectangle transparent ridiculement grand en dehors des limites et ajuster mon masque en conséquence, mais qui est une solution vraiment méchant

Update3 : Remarque: La toile avec un rectangle et la ligne est là comme un exemple d'un objet qui a quelque chose en dehors de celui-ci limite. Dans le contexte de cet échantillon, il doit être traité comme une sorte de boîte noire. Vous ne pouvez pas modifier ses propriétés pour résoudre le problème général. Ce serait la même chose que tout le déplacement de la ligne de sorte qu'il ne colle pas.

Était-ce utile?

La solution

question intéressant en effet - voici ce que j'ai pensé: l'effet que vous rencontrez semble être déterminé par le concept Viewport / comportement de TileBrush (voir Viewbox aussi pour l'image complète). Apparemment, le boîte englobante de Viewport et comme ViewportUnits si:

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

Vous imposez la taille DrawingBrush en remplaçant Stretch avec None, tout en gardant la position et la dimension de la tuile de base en cas de défaut et par rapport à sa zone de délimitation . En outre, vous (compréhensible) prépondérants AlignmentX / AlignmentY , qui déterminent la mise en place au sein de la tuile de base, qui se trouve dans sa zone de délimitation. Remise à zéro ceux à leurs valeurs par défaut de Center est déjà dit: Les changements de masque en conséquence, ce qui signifie il doit être plus petite que la zone de délimitation , sinon leur serait rien à centrer dans les

.

peut aller plus loin en changeant ViewportUnits à Absolute, qui donnera aucun graphique du tout jusqu'à ce que les unités soient bien réglées bien sûr; encore une fois, par expérience, les valeurs explicites suivantes sont celles qui correspondent aux automobiles, tout en utilisant d'autres valeurs donne des changements graphiques:

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

masque d'opacité déjà prope aligneRLY avec le contrôle . De toute évidence, il y a un problème laissé cependant, comme le masque sature la ligne maintenant, ce qui est pas surprenant compte tenu de sa taille et l'absence de Stretch effet. Réglage de la taille et la position résout en conséquence ceci:

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

et

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

Enfin, le masque d'opacité correspond aux limites de contrôle comme vous le souhaitez!


Complément:

Les compensations nécessaires déterminées par déduction et expérience dans l'explication ci-dessus peuvent être récupérées lors de l'exécution au moyen du VisualTreeHelper Class :

Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);

En fonction de la composition de votre élément visuel et a besoin de vous devrez peut-être tenir compte de la LayoutInformation Class et de construire l'union des deux pour obtenir la boîte englobante qui englobe:

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

Voir les liens ci-dessous pour plus de détails sur les deux sujets:

Autres conseils

Sur votre objet toile ajoutez 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>

Une solution de contournement qui peut être plus idéal que votre actuel serait d'appliquer simplement le OpacityMask à un niveau supérieur. En utilisant ce code de démonstration par exemple, vous pouvez supprimer le masque de la Border et l'appliquer à la Window place. Avec un peu de peaufinage il convient bien:

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

Vous devez écrire un code pour déplacer le masque lorsque le Window est redimensionnée, et pour cette raison, vous pouvez être mieux générer le masque dynamique dans le code-behind.

Ma question est, pourquoi avez-vous besoin de gérer des géométries qui vont en dehors des limites de votre Canvas?

Puisque vous avez des pièces décollées du contrôle, une idée est de séparer l'image de commande du masque de contrôle.

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