I'm trying to use the OpacityMask property combined with a VisualBrush so that when you drag an image over another control (such as another image, rectangle, or any other control), the part of the image that is over the second control has a different opacity. That is, the image has some non-zero base opacity, and any part of the image that is over another control has a different (again, non-zero) opacity.
Is this possible simply using VisualBrush and OpacityMask? Or is a more complex approach required?
Thanks!
Edit: I'm trying to make the image have some lower opacity (such as 0.5), and the part being dragged over the control have a higher opacity (such as 1.0). I originally left out this detail, which is important to the approach taken.
In addition to ima's answer, I have figured this out using an opacity mask. I use the following code hooked into the LayoutUpdated event for the image.
// Make a visual brush out of the masking control.
VisualBrush brush = new VisualBrush(maskingControl);
// Set desired opacity.
brush.Opacity = 1.0;
// Get the offset between the two controls.
Point offset = controlBeingMasked.TranslatePoint(new Point(0, 0), maskingControl);
// Determine the difference in scaling.
Point scale = new Point(maskingControl.ActualWidth / controlBeingMasked.ActualWidth,
maskingControl.ActualHeight / controlBeingMasked.ActualHeight);
TransformGroup group = new TransformGroup();
// Set the scale of the mask.
group.Children.Add(new ScaleTransform(scale.X, scale.Y, 0, 0));
// Translate the mask so that it always stays in place.
group.Children.Add(new TranslateTransform(-offset.X, -offset.Y));
// Rotate it by the reverse of the control, to keep it oriented correctly.
// (I am using a ScatterViewItem, which exposes an ActualOrientation property)
group.Children.Add(new RotateTransform(-controlBeingMasked.ActualOrientation, 0, 0));
brush.Transform = group;
controlBeingMasked.OpacityMask = brush;
If you want a desired base opacity, use two images; one that's always at the base opacity, and another that uses the opacity mask that sits on top of it. If you want the base opacity to be higher than the masked opacity, then it might be easier to use ima's approach.
One advantage of this solution as opposed to the maskless approach is that if the masking control moves, changes size, etc., this will automatically pick up the change without having to keep another control in sync with it.
Here's how it looks:
(source: yfrog.com)
No masks
Define visual brush for the control
Paint shape right on top of the control with that brush
Drag image between the shape and the control
Set opacity of the brush to achieve desired effect
Related
I'm creating a custom installer and working on the progress bar. I have a timer function that increments the specified rectangle's Height property by +1.
statusBar->Height += 1;
if (statusBar->Height >= 285)
{
StatusBarTimer->Stop();
}
However, the origin point of the shape seems to be located in the top left of the shape, and therefore the Height acts opposite of its controls. I.E. the rectangle extends it's height the opposite direction of where I would like to.
customInstaller
(See arrowed object)
Is there a work-around for this, or better yet, is there a way to change the origin point of an image?
All common modern GUI toolkits position their controls top-down and left-right. The position you specify will be the top-left corner of the control, the size will extend downward and to the right.
If you want a control to grow upward, adjust the top and the height at the same time. Or use an existing progress bar control that supports being vertical.
The System.Drawing.Graphics class has a property CompositionMode with two options: SourceOver (which, based on the alpha component, blends whatever is drawn with the background already existing) or SourceCopy which simply overwrites the background with whatever is being drawn.
Does something similar exist in WPF?
In WPF when i draw a PolyLine for example on top of another the new PolyLine always alphablends with the background. I think that is independent of the container being used. I am using a Canvas but could not find a blend mode property anywhere. What I want to do is what the SourceCopy compositionmode mentioned above does. I.e. the new PolyLine should simply overwrite whatever is already on the Canvas.
Is there a simple way to do that, short of using pixel shaders (which - as far as I understand - wouldn't work anyways because I don't have access to the Canvas backbuffer).
I am not stuck with a Canvas and would be happy to use any container that supports overwrite mode.
I currently have a solution based on a WriteableBitmap for which I obtain a System.Drawing.Graphics context and then manipulate the CompositionMode. It works but since my window is fullscreen that solution has serious performance impacts.
Clarification and example:
The WPF window is fully transparent and so is the Canvas (back ground color(0,0,0,0)). Now I draw a PolyLine with a Color.FromArgb(128,128,0,0). I now have a semi-transparent red polyline. Next I draw the same PolyLine with Color.FromArgb(0,0,0,0). The result is the same as before because of the alpha blending taking place. What I want, however, is that the red polyline is erased with the second polyline (which is exactly what the SourceCopy mode in the Graphics class does.
I think all you need to do is make sure that the brushes used to fill/stroke the PolyLine have fully opaque alpha values (i.e. 255). Then the background shouldn't be blended into it.
You could apply a Clipping Mask, this way you can provide the path to clip over the elements that are below it, but it might be tough to maintain after a lot elements are required to be clipped...
I have a requirement to morph from an image (png) to a shape (polygon) in Silverlight 3 as an effect, but of course there is no built in transition or method to do this.
At the moment the best I have is fade one out and the other in, but can anyone suggest a decent alternative that may work or look better?
Regards
Moo
In Blend:
Create a rectangle. Set stroke to No Brush and Fill to Tile Brush.
For the ImageBrush of the Tile Brush, select your image.
In the object browser, select the rectangle, right click > Path > Convert to Path.
Use the pen tool to add some points to the path.
Add a storyboard.
Add a keyframe at 1 second. Blend will go into record mode
Use the direct selection tool to move the points into the polygon shape you want. Test out your animation.
At this point, the image morphs into a shape, but the image is still there. If you need to remove the image as well as morph it:
In your storyboard, at the keyframe at 1 second, change the opacity to 0.
Create a copy of the rectangle, but make sure fill is set to No Brush and Stroke is set to a color and width. Set the opacity to 0.
Add points, and mimic the animation you just set up for the image's rectangle.
Add a keyframe at 1 second for this element. In record mode, change the opacity to 100%.
The end result will be both paths morphing, the one with the image fading out while the one with no fill fading in.
I'm not a silverlight programmer, and don't know the details of what you want to do, so this is just a shot in the dark, but... if the shape you want the image to morph to is always going to have the same initial visual appearance (or some limited set of appearances) you might try morphing from the original image to an image of that shape, and then swapping the image that's the morph target for the geometry once you're done the morph. Course whether that would work or not is very dependent on the details of what you're doing. Sorry if you've already considered that and ruled it out.
You could possibly morph the image brush to the path of the shape by using an appropriate projection matrix. Or render a shape using the image brush and then morph that shape to the target shape, i.e. go from a rectangle to the target shape but using the image brush as the shape background. You may need to still warp the image brush somehow though.
An example of rendering a warped image is here in Charles Petzold's blog.
I'm adding controls programmatically to a canvas which is all just wonderful...
var newControlPoint = new ControlPoint() { Width = 10, Height = 10 };
newControlPoint.SetResourceReference(Control.TemplateProperty, "ControlPoint");
SetCanvasPosition(newControlPoint, position.X - (newControlPoint.Width / 2), position.Y - (newControlPoint.Height / 2));
canvas.Children.Add(newControlPoint);
newControlPoint.UpdateLayout();
... but I'm coming unstuck when I attempt to remove the hardwired Width and Height settings from the first line...
var newControlPoint = new ControlPoint();
...the canvas positioning doesn't seem to take effect and the newly created control winds up at {0,0}.
Any ideas?
Two problems:
The Width and Height properties won't be set because you haven't explicitly set them. It's ActualWidth and ActualHeight you want, which are set by WPF.
The controls haven't been laid out yet, so ActualWidth and ActualHeight will be zero.
To work around the problem you could:
Use databinding instead of doing a one-off calculation.
Attach the positioning logic to the control's Loaded event so it has been positioned.
Use Dispatcher.BeginInvoke to run the layout logic in a separate message that runs at a lower priority to layout, thus ensuring the ActualWidth and ActualHeight have been calculated and assigned.
We are using the Mindfusion WPF Diagramming Component. I require a Visualbrush which must be able to be used in a third-party component for diagramming.
We have horizontally-stretching lanes on the diagramming canvas. We must define a background color for this lane, and some text on the top-left corner, to be repeated on the x-axis. The text must remain the same size while the rest of the lane is filled with color when the lane is re-sized.
Unfortunately, the only thing we can do is provide a Visualbrush to the diagramming component, which is then set as background for the lane. It seems impossible to work with this restriction because I am unable to define a text with fixed size, repeating on the x-axis, while also having a color brush stretching, all within a single Visualbrush. How can this be done?