Drop shadow is above the text - xamarin

I'm adding a drop shadow to a Xamarin.UWP project (but the question is not really Xamarin-specific but UWP in general):
bool IsShadowSupported => ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 3); // SDK >= 14393
if (IsShadowSupported) {
var compositor = ElementCompositionPreview.GetElementVisual(Control).Compositor;
dropShadow = compositor.CreateDropShadow();
if (Control is Windows.UI.Xaml.Controls.TextBlock textBlock)
dropShadow.Mask = textBlock.GetAlphaMask();
shadowVisual = compositor.CreateSpriteVisual();
shadowVisual.Shadow = dropShadow;
ElementCompositionPreview.SetElementChildVisual(Control, shadowVisual);
...
dropShadow.Offset = new Vector3((float)Shadow.GetDistanceX(Element), (float)Shadow.GetDistanceY(Element), -5f);
}
It runs and the shadow appears—but above the text, not beneath it. At first I thought this would be determined by the Z coordinate of the offset, but no negative, positive or zero value there changes anything. The shadow looks like this:
Which is not a bad effect on its own but it's not what was requested: white text and a dark grey shadow beneath it.

The problem is that the SetElementChildVisual sets the visual as the last child of the given element, which will make the shadow appear above the TextBlock. Unfortunately not even the parent of the TextBlock is enough, you should instead have a adjacent element that will host the shadow:
<Grid x:Name="ShadowHost" />
<TextBlock x:Name="Hello" Text="Hello" />
Now use ShadowHost instead of Control in your code except for the GetAlphaMask call where you should use the TextBlock instead.
Of course this is quite some work to make shadows work, which is why you can try to use the Windows Community Toolkit's DropShadowPanel instead - see documentation for more info.

Related

UWP - positioning ruler using matrix transform inside zoomable ScrollViewer

I have InkCanvas inside a zoomable ScrollViewer:
<ScrollViewer x:Name="ScrollViewer" ZoomMode="Enabled">
<Border Height="5000" Width="5000" BorderBrush="Black" BorderThickness="1">
<InkCanvas x:Name="inkCanvas" />
</Border>
</ScrollViewer>
I want to position ruler to the top left corner.
Ruler is positioned on the InkCanvas and state of ScrollViewer is defined by by HorizontalOffset, VerticalOffset and ZoomFactor
I've found this code (sample)
void OnBringIntoView(e)
{
// Set Ruler Origin to Scrollviewer Viewport origin.
// The purpose of this behavior is to allow the user to "grab" the
// ruler and bring it into view no matter where the scrollviewer viewport
// happens to be. Note that this is accomplished by a simple translation
// that adjusts to the zoom factor. The additional ZoomFactor term is to
// make ensure the scale of the InkPresenterRuler is invariant to Zoom.
Matrix3x2 viewportTransform =
Matrix3x2.CreateScale(ScrollViewer.ZoomFactor) *
Matrix3x2.CreateTranslation(
ScrollViewer.HorizontalOffset,
ScrollViewer.VerticalOffset) *
Matrix3x2.CreateScale(1.0f / ScrollViewer.ZoomFactor);
ruler.Transform = viewportTransform;
}
in short:
viewport = Scale(zoom) * Translate(offset) * Scale(1/zoom)
This works, but I'm a bit lost.
What does the first scale do and what does the second? Why can't I use juct TranslateTransform?
Why can't I use juct TranslateTransform?
Because the InkPresenterRuler.Transform property is designed to be Matrix3x2.
What does the first scale do and what does the second?
Scale(zoom)*Scale(1/zoom) aiming at letting the ruler be invariant to Zoom. No matter what your current zoom level is, the ruler will return to it's original size.
Translate(offset) changes the translation values(offsetX and offsetY) of the transform Matrix.
For details of the transformation theory you can refer to:
Remarks of Matrix Transform
Affine transformations section of Transformation matrix Wiki.

Scrollable drawing in Gtk::Layout

I'd like to use custom drawing within a Gtk::Layout. That is, I'm using the C++ bindings for Gtk3 (GTKmm 3.14.0), and I have embedded widgets placed on the "canvas", on top of my custom drawing. Basically this works just fine.
Now the problem is related to scrolling. Gtk::Layout can be placed into a Gtk::ScrolledWindow, and when the scrollable area is set to something larger than the visible allocation, scrollbars will show up. Unfortunately, those scrollbars influence only the placement of the embedded widgets, while my custom drawing remains at a fixed position within the window.
This means, both the Gtk::Allocation and the cairo context seem to be related to precisely the visible area, not to the extended virtual "canvas". I could work around that problem by accessing the adjustments from the scrollbars and then translate the cairo context accordingly...
My question is:
is this the proper way to handle such a scrollable drawing?
or is there some way to let the framework do this work for me?
Judging from the source code of gtk+3.0-3.14.5 (which is in Debian/Stable), the Gtk::Layout does nothing to adjust the drawing context. It just invokes the inherited draw() function from GtkWidget. On the other hand, Gtk::Layout is a full-blown container (it inherits from Gtk::Container), and it is scrollable, which together means that it handles gtk_layout_size_allocate() by passing a suitable allocation (screen area) to each of the embedded child widgets -- and in this respect it does handle the moving and clipping related to scrolling the virtual canvas (calls gdk_window_move_resize()).
Thus, if we want to combine the embedded child widgets with custom drawing, we need to bridge this discrepancy manually. This is quite easy actually: all we need to do is to look into the Gtk::Adjusments corresponding to the scrollbars. Because the value of these adjusments is precisely the upper left corner of the visible viewport. Now, if we want our custom drawing to use absolute canvas coordinates, we just have to translate() the given Cairo context. Beware: it is important to save() the state and to restore() it to pristine state when done, otherwise those translations will accumulate.
Here is some example code to demonstrate this custom drawing
we derive a custom container class called Canvas from Gtk::Layout
we override the on_draw() handler, because only there all size allocation to embedded child widgets have been processed
Layering: child widgets are always drawn in the order they have been added to the Gtk::Layout container. Any custom drawing done before invoking the inherited on_draw() function will be below those widgets; any drawing done afterwards will happen on top of them.
if necessary, we can use the foreach(callback) mechanism to visit all child widgets to find out their current position and extension
void
Canvas::determineExtension()
{
if (not recalcExtension_) return;
uint extH=20, extV=20;
Gtk::Container::ForeachSlot callback
= [&](Gtk::Widget& chld)
{
auto alloc = chld.get_allocation();
uint x = alloc.get_x();
uint y = alloc.get_y();
x += alloc.get_width();
y += alloc.get_height();
extH = max (extH, x);
extV = max (extV, y);
};
foreach(callback);
recalcExtension_ = false;
set_size (extH, extV); // define extension of the virtual canvas
}
bool
Canvas::on_draw(Cairo::RefPtr<Cairo::Context> const& cox)
{
if (shallDraw_)
{
uint extH, extV;
determineExtension();
get_size (extH, extV);
auto adjH = get_hadjustment();
auto adjV = get_vadjustment();
double offH = adjH->get_value();
double offV = adjV->get_value();
cox->save();
cox->translate(-offH, -offV);
// draw red diagonal line
cox->set_source_rgb(0.8, 0.0, 0.0);
cox->set_line_width (10.0);
cox->move_to(0, 0);
cox->line_to(extH, extV);
cox->stroke();
cox->restore();
// cause child widgets to be redrawn
bool event_is_handled = Gtk::Layout::on_draw(cox);
// any drawing which follows happens on top of child widgets...
cox->save();
cox->translate(-offH, -offV);
cox->set_source_rgb(0.2, 0.4, 0.9);
cox->set_line_width (2.0);
cox->rectangle(0,0, extH, extV);
cox->stroke();
cox->restore();
return event_is_handled;
}
else
return Gtk::Layout::on_draw(cox);
}

How do I change the color of already drawn InkStrokes in windows universal

I have drawn some ink strokes on an InkCanvas and am now wanting to change the pen colour. I can change the colour of any additional strokes I draw using CopyDefaultDrawingAttributes and UpdateDefaultDrawingAttributes and that works fine. But how do I alter the color of the strokes that are already present StrokeContainer? I've tried:
foreach (InkStroke stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
stroke.DrawingAttributes.Color = strokeColour;
};
This code executes with no exceptions, but stroke.DrawingAttributes.Color still shows the previous colour.
Any ideas?
Thanks...
Robert
You cannot set the DrawingAttributes property of the stroke directly. You must create a copy of the InkDrawingAttributes of the stroke, set the desired values for that InkDrawingAttributes object, and then assign the new InkDrawingAttributes to the DrawingAttributes of the stroke.
So you can code for example like this:
foreach (InkStroke stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
//stroke.DrawingAttributes.Color = Windows.UI.Colors.Yellow;
InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
drawingAttributes.Color = Windows.UI.Colors.Yellow;
stroke.DrawingAttributes = drawingAttributes;
}
For more information, you can refer to InkStroke.DrawingAttributes | drawingAttributes property.

Pinch To Zoom functionality in windows phone 8

From this post i came to know that there exist some platform improvements for
implementing pinch and zoom functionality. By using this new method(ManipulationDeltaEventArgs.PinchManipulation) how i can implement pinch to zoom functionality in windows phone.
Apart from this i need to implement scrolling feature too to the image control. In my current implementation, i am using Toolkit(gesture listener) for pinch and zoom functionality along with scroll viewer, now it seem both scrolling and and pinching events are overlapping and hence it produces a bad user experience.
Can anyone help me to solve this issue in my application. I am looking some code samples that help me to achieve the functionality.
I am not expected to get Multi touch behavior(codeplex) as answer. The assemblies using in the project are quite old and i heard that many of them are facing issues with marketplace submission only because of this.
As I said in my previous answer if you're building a WP8 exclusive app you can use the new ManipulationDeltaEventArgs.PinchManipulation for pinch & zoom effects. Here's a basic example of how to use ManipulationDeltaEventArgs.PinchManipulation data to scale, move and rotate an image.
First, we'll create a basic image hovering in the middle of a grid:
<Grid x:Name="ContentPanel">
<Image Source="Assets\Headset.png"
Width="200" Height="150"
ManipulationDelta="Image_ManipulationDelta"
x:Name="img"
>
<Image.RenderTransform>
<CompositeTransform CenterX="100" CenterY="75" />
</Image.RenderTransform>
</Image>
</Grid>
Next, we'll handle the ManipulationDelta event, check if it's a Pinch Manipulation and apply the correct Silverlight transformations on our UIElement.
private void Image_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
if (e.PinchManipulation != null)
{
var transform = (CompositeTransform)img.RenderTransform;
// Scale Manipulation
transform.ScaleX = e.PinchManipulation.CumulativeScale;
transform.ScaleY = e.PinchManipulation.CumulativeScale;
// Translate manipulation
var originalCenter = e.PinchManipulation.Original.Center;
var newCenter = e.PinchManipulation.Current.Center;
transform.TranslateX = newCenter.X - originalCenter.X;
transform.TranslateY = newCenter.Y - originalCenter.Y;
// Rotation manipulation
transform.Rotation = angleBetween2Lines(
e.PinchManipulation.Current,
e.PinchManipulation.Original);
// end
e.Handled = true;
}
}
// copied from http://www.developer.nokia.com/Community/Wiki/Real-time_rotation_of_the_Windows_Phone_8_Map_Control
public static double angleBetween2Lines(PinchContactPoints line1, PinchContactPoints line2)
{
if (line1 != null && line2 != null)
{
double angle1 = Math.Atan2(line1.PrimaryContact.Y - line1.SecondaryContact.Y,
line1.PrimaryContact.X - line1.SecondaryContact.X);
double angle2 = Math.Atan2(line2.PrimaryContact.Y - line2.SecondaryContact.Y,
line2.PrimaryContact.X - line2.SecondaryContact.X);
return (angle1 - angle2) * 180 / Math.PI;
}
else { return 0.0; }
}
Here's what we did:
Scaling: PinchManipulation actually tracks scaling for us, so all we had to do is apply PinchManipulation.CumulativeScale to the scaling factor.
Transform: PinchManipulation tracks the original center and the new center (calculated between the two touch points). By subtracting the new center from the old center we can tell how much the UIElement needs to move and apply that to a translate transform. Note that a better solution here would also account for multiple Manipulation sessions by tracking cumulative original centers which this code doesn't.
Rotation: We figured out the angle between the two touch points and applied it as the rotation transform. More on that in this Nokia wiki article # Real-time rotation of the Windows Phone 8 Map Control
Here's a few print screens showing this code runs just fine:
I found the perfect soultion for smooth pinch to zoom and pan. It is actually a Microsoft Code sample at the following link
http://code.msdn.microsoft.com/wpapps/Image-Recipes-0c0b8fee
I just used it as boiler plate code and it worked wonders.
Cheers
If you're looking to roll your own, you might want to check out this article about pinch zooming in Silverlight: Implementing pinch zoom correctly in Silverlight
Telerik also has an out of the box pan and zoom image control, but it does cost money: Telerik Pan and Zoom Control

Trouble with OpenLayers Styles

So, tired of always seeing the bright orange default regular polygons, I'm trying to learn to style OpenLayers.
I've had some success with:
var layer_style = OpenLayers.Util.extend({},OpenLayers.Feature.Vector.style['default']);
layer_style.fillColor = "#000000";
layer_style.strokeColor = "#000000";
polygonLayer = new OpenLayers.Layer.Vector("PolygonLayer");
polygonLayer.style = layer_style;
But sine I am drawing my polygons with DrawFeature, my style only takes effect once I've finished drawing, and seeing it snap from bright orange to grey is sort of disconcerting. So, I learned about temporary styles, and tried:
var layer_style = new OpenLayers.Style({"default": {fillColor: "#000000"}, "temporary": {fillColor: "#000000"}})
polygonLayer = new OpenLayers.Layer.Vector("PolygonLayer");
polygonLayer.style = layer_style;
This got me a still orange square--until I stopped drawing, when it snapped into completely opaque black. I figured maybe I had to explicitly set the fillOpacity...no dice. Even when I changed both fill colors to be pink and blue, respectively, I still saw only orange and opaque black.
I've tried messing with StyleMaps, since I read that if you only add one style to a style map, it uses the default one for everything, including the temporary style.
var layer_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
var style_map = new OpenLayers.StyleMap(layer_style);
polygonLayer = new OpenLayers.Layer.Vector("PolygonLayer");
polygonLayer.style = style_map;
That got me the black opaque square, too. (Even though that layer style works when not given to a map). Passing the map to the layer itself like so:
polygonLayer = new OpenLayers.Layer.Vector("PolygonLayer", style_map);
Didn't get me anything at all. Orange all the way, even after drawn.
polygonLayer = new OpenLayers.Layer.Vector("PolygonLayer", {styleMap: style_map});
Is a lot more succesful: Orange while drawing, translucent black with black outline when drawn. Just like when I didn't use a map. Problem is, still no temporary...
So, I tried initializing my map this way:
var style_map = new OpenLayers.StyleMap({"default": layer_style, "temporary": layer_style});
No opaque square, but no dice for the temporary, either... Still orange snapping to black transparent. Even if I make a new Style (layer_style2), and set temporary to that, still no luck. And no luck with setting "select" style, either.
What am I doing wrong? Temporary IS for styling things that are currently being sketched, correct? Is there some other way specific to the drawFeature Controller?
Edit: setting extendDefault to be true doesn't seem to help, either...
var style_map = new OpenLayers.StyleMap({"default": layer_style, "temporary": layer_style}, {"extendDefault": "true"});
I've found two solutions for this problem. In both solution, you have to change some parameters of DrawFeature to get the functionality you wish.
1.Change handler style of the DrawFeature. Function drawFeature in OpenLayers.Handler.Polygon uses parameter style of the handler for the feature. So you have to change this style.
When creating Feature use:
var drawPolygon = new OpenLayers.Control.DrawFeature(polygonLayer, OpenLayers.Handler.Polygon, {handlerOptions:{style:myStyle}});
Later, you can change it by:
drawPolygon.handler.style = myStyle;
2.Change create callback of the DrawFeature. Change style of the newly created temporary feature in create callback.
var drawPolygon = new OpenLayers.Control.DrawFeature(polygonLayer, OpenLayers.Handler.Polygon, {
callbacks:{create: function(vertex, feature) {
feature.style = myStyle;
this.layer.events.triggerEvent("sketchstarted", {vertex:vertex,feature:feature})
}}});
Similarly, you can change the callback later.
If you want all vectors to be of a constant style, but not the boring orange then try this:
vecLayer = new OpenLayers.Layer.Vector(
"Route Layer", //layer name
{styleMap: new OpenLayers.StyleMap({
pointRadius: "6",
fillColor: "#666666"
}),
renderers:renderer}
);
You have loads of properties you can mess about with, have a look at these pages:
dev.openlayers (check the Constants section)
docs.openlayers (more useful info)

Resources