I have two images, as explained in the following XAML code:
<Window x:Class="TestApplicationGestureKinect.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="768" Width="1024" ScrollViewer.VerticalScrollBarVisibility="Disabled" MinWidth="1024" MaxWidth="1024" MinHeight="768" MaxHeight="768">
<Grid Background="Black">
<Image x:Name="img1" HorizontalAlignment="Left" Margin="47,82,0,0" VerticalAlignment="Top" Source="photos/01.jpg" Height="200" RenderTransformOrigin="0.5,0.5" >
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="9.577"/>
<TranslateTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
<Image x:Name="cursorRight" HorizontalAlignment="Left" Margin="757,133,0,0" Width="48" Height="48" VerticalAlignment="Top" Source="cursors/right_open.png" />
</Grid>
</Window>
And the following image shows how this appear:
I need a way to test, from C# code, if the image called cursorRight is onto the area covered by the image called img1, after the transformation.
How could I do? I thought about some consideration on the bounding boxes of the two images, but while for the cursorRight image, could be acceptable to consider the bounding box, this doesn't seem to be a good choice for the other image......
EDIT: The following images show four examples of how I want to do:
cursor on the image:
cursor not on the image:
SOLUTION: The following code is what I used in order to solve the above problem. I considered the bounding box of the cursor rather than the exact shape of it.
private bool isOn(Image img1, Image img2)
{
if (img1 == null || img1.Visibility != System.Windows.Visibility.Visible)
{
return false;
}
double img1_topLeft_X = img1.Margin.Left;
double img1_topLeft_Y = img1.Margin.Top;
double img1_bottomRight_X = img1_topLeft_X + img1.Width;
double img1_bottomRight_Y = img1_topLeft_Y + img1.Height;
Point img1_topLeft = new Point(img1_topLeft_X, img1_topLeft_Y);
Point img1_bottomRight = new Point(img1_bottomRight_X, img1_bottomRight_Y);
HitTestResult result_topLeft = VisualTreeHelper.HitTest(img2.Parent as Grid, img1_topLeft);
HitTestResult result_bottomRight = VisualTreeHelper.HitTest(img2.Parent as Grid, img1_bottomRight);
if (result_topLeft != null && result_bottomRight != null)
{
if (result_topLeft.VisualHit.GetType() == typeof(Image) && result_bottomRight.VisualHit.GetType() == typeof(Image) &&
(result_topLeft.VisualHit as Image).Name.Equals(img2.Name) && (result_bottomRight.VisualHit as Image).Name.Equals(img2.Name))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
However, in this way the cursor is ONTO the image only if the bounding box of it is TOTALLY onto the image. It's not exactly what I needed, but since it works pretty well, I decided to use this method.
An option not quite would be to use the VisualTreeHelper.HitTest(...) methods to check if points overlapped each other. You can read more about it works here.
Use bounding rectangle for your cursor and a "bounding polygon" that defines the area of your larger image and then use polygon intersection algorithm (one explanation here) to solve your problem.
Related
So I am creating a "Map Viewer" for a program I am working on. Basically I want to display a map and have it resize the image to fit in the grid. For this I am using Viewbox to hold the image and resize it. I was attempting to use this code to reveal the map but it does not center the circle on the mouse and it does not retain the revealed portions of the map (it ONLY reveals where the mouse is and not where it has been).
Here is the XAML:
<Viewbox x:Name="MapHolder" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="1" Grid.RowSpan="7" Margin="25,5,25,15">
<Image x:Name="SelectedMap" Source="/wizard_dungeon.jpg" Stretch="Uniform" MouseMove="SelectedMap_MouseMove" >
<Image.OpacityMask>
<VisualBrush Stretch="None" >
<VisualBrush.Visual>
<Ellipse Width="400" Height="400" StrokeThickness="1" Fill="Black"/>
</VisualBrush.Visual>
<VisualBrush.RelativeTransform>
<TransformGroup>
<TranslateTransform x:Name="OpacityFilterTransform" X="1" Y="1"/>
</TransformGroup>
</VisualBrush.RelativeTransform>
</VisualBrush>
</Image.OpacityMask>
</Image>
</Viewbox>
And the code-behind:
private void SelectedMap_MouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(this);
var height = MapHolder.ActualHeight;
var width = MapHolder.ActualWidth;
// with the position values, interpolate a TranslateTransform for the opacity mask
var transX = position.X / width;
var transY = position.Y / height;
OpacityFilterTransform.X = transX - 0.5;
OpacityFilterTransform.Y = transY - 0.5;
}
I want there to basically be a Image under a Black screen and I can erase the black screen to reveal the image in the areas I have erased.
I am building an application like a noteBook in Landscape.
I have two canvas next to each other and I'm changing the Background inside them.
Every time I write on the two canvas and move to another 2 backgrounds, I need to save what I wrote on the two canvas and reload it once I move to these backgrounds once again, and clear them. And do the same for all the backgrounds.
<Grid x:Name="RenderedGrid" Width="1140" Height="770">
<ScrollViewer x:Name="DScrollViewer" MaxZoomFactor="2.0" MinZoomFactor="1.0" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" ZoomMode="Disabled">
<StackPanel Orientation="Horizontal">
<Canvas x:Name="inkCanvas0" HorizontalAlignment="Left" Width="570">
<Canvas.Background>
<ImageBrush x:Name="DImage0" ImageSource="{Binding}"/>
</Canvas.Background>
</Canvas>
<Canvas x:Name="inkCanvas1" HorizontalAlignment="Right" Width="570">
<Canvas.Background>
<ImageBrush x:Name="DImage1" ImageSource="{Binding}"/>
</Canvas.Background>
</Canvas>
</StackPanel>
</ScrollViewer>
</Grid>
I need to bind the Canvas in a way to this class instead of inkCanvas0 & inkCanvas1:
public class MyCanvas
{
public Canvas inkCanvas { get; set; }
}
List<MyCanvas> CanvasList = new List<MyCanvas>();
This is how I am creating Empty Canvas and storing them in CanvasList:
for (int i = 0; i < 24; i++)
{
Canvas canvas = new Canvas();
canvas.Name = "inkCanvas" + i;
MyCanvas mc = new MyCanvas();
mc.inkCanvas = canvas;
CanvasList.Add(mc);
}
I need to Bind these Canvas to the its corresponding page (inkCanvas0 to page0)
but I'm getting an exception in Xaml when I try to bind the Name of the Canvas.
<Canvas x:Name="{Binding}" HorizontalAlignment="Left" Width="570">
Exception: Catastrophic failure The name already exists in the tree.
How can I solve this? Is my logic of creating and loading the Canvas correct?
How can I get the elements of a canvas?
I have this:
<Canvas x:Name="can" HorizontalAlignment="Left" Height="502" Margin="436,0,0,0" VerticalAlignment="Top" Width="336" OpacityMask="#FFC52D2D">
<Canvas.Background>
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ActiveCaptionColorKey}}"/>
</Canvas.Background>
<Button x:Name="btn_twoThreads" Content="Two Threads" Height="32" Canvas.Left="195" Canvas.Top="460" Width="131" Click="btn_twoThreads_Click"/>
<Button x:Name="btn_oneThread" Content="One Thread" Height="32" Canvas.Left="10" Canvas.Top="460" Width="131" Click="btn_oneThread_Click"/>
<Rectangle Fill="#FFF4F4F5" Height="55" Canvas.Left="10" Stroke="Black" Canvas.Top="388" Width="316"/>
</Canvas>
As you can see there are some objects on this canvas in the XAML Code. I need to get the the Rectangle object's details:
Rectangle r;
r = can.Children[2] as Rectangle; //I know this probably doesn't retrieve the rectangle object, but hopefully you can see what I am trying to achieve.
if (r != null)
{
MessageBox.Show("It's a rectangle");
}
I know I could probably just access the Rectangle object by just giving it a variable name in the XAML, but the canvas object is being drawn to in various classes, and I don't want to pass the rectangle to every class if it is already contained within the canvas.
You could try this:
// to show that you'll get an enumerable of rectangles.
IEnumerable<Rectangle> rectangles = can.Children.OfType<Rectangle>();
foreach(var rect in rectangles)
{
// do something with the rectangle
}
Trace.WriteLine("Found " + rectangles.Count() + " rectangles");
The OfType<>() is very useful because it checks the type and only yields an item if it is the right type. (it's casted already)
I have an image control sits inside a Grid control. I already have a button to enable zoom-in to this image. After zoom-in, the Horizontal/vertical scroll bars are displayed. And then I rotate the image contained grid, the image and the grid scroll bar are messed up. How should I incorporate both zoom-in and rotate for the image control? The following are the code that I am using in my project.
The image control zoom-in code I used (x is the image control):
if ((x as Image) != null) { x.Height = x.Height * 1.3; x.Width = x.Width * 1.3; }
The rotation code I used (x is the image control):
if ((x as Image) != null)
{
RotateTransform rotate = new RotateTransform(); rotate.Angle = rotateAngle;
rotate.CenterX = x.Width / 2;
rotate.CenterY = x.Height / 2;
x.RenderTransform = rotate;
};
The XAML is:
<ScrollViewer x:Name="scrollViewer" Height="480" Width="615"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ScrollViewer.Content>
<Grid x:Name="ImageGrid">
<StackPanel x:Name="ImageStackPanel">
<Image Source="..." VerticalAlignment="Center" Width="220" Height="170" ></Image>
</StackPanel>
</Grid>
</ScrollViewer.Content>
</ScrollViewer>
Does anybody have any existing code snippet that I can borrow to resolve this trick?
I think you need to use TransformGroup to use more than one transform at the time:
ScaleTransform myScaleTransform = new ScaleTransform();
myScaleTransform.ScaleY = 3;
RotateTransform myRotateTransform = new RotateTransform();
myRotateTransform.Angle = 45;
// Create a TransformGroup to contain the transforms
// and add the transforms to it.
TransformGroup myTransformGroup = new TransformGroup();
myTransformGroup.Children.Add(myScaleTransform);
myTransformGroup.Children.Add(myRotateTransform);
// Associate the transforms to the image.
x.RenderTransform = myTransformGroup;
This may work for your needs:
<Image x:Name="image" Source="myImageSource" Stretch="Uniform"
HorizontalAlignment="Center" VerticalAlignment="Center"
RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="Rotate"/>
<ScaleTransform x:Name="Scale" />
</TransformGroup>
</Image.RenderTransform>
</Image>
code behind:
Rotate.Angle = 45;
Scale = 0.25;
You may be missing the LayoutTransformer from the Silverlight Toolkit, and the AnimationMediator from one of the Toolkit developers.
With the LayoutTransformer you can set its content to anything, not just images, and apply any transformation with it, and as opposed to the usual RenderTransform, it will affect layout and actual sizes.
I have a similar scenario and I use it like this:
<Grid>
<fs:AnimationMediator x:Name="RotateMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding Angle, ElementName=RotateTransform, Mode=TwoWay}" />
<fs:AnimationMediator x:Name="ScaleXMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding ScaleX, ElementName=ScaleTransform, Mode=TwoWay}" />
<fs:AnimationMediator x:Name="ScaleYMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding ScaleY, ElementName=ScaleTransform, Mode=TwoWay}" />
<tkt:LayoutTransformer x:Name="LayoutTransformer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<tkt:LayoutTransformer.LayoutTransform>
<TransformGroup>
<RotateTransform x:Name="RotateTransform" />
<ScaleTransform x:Name="ScaleTransform" />
</TransformGroup>
</tkt:LayoutTransformer.LayoutTransform>
<Image x:Name="MyImage" Source="mysource.png" Width="600" Height="800" />
</tkt:LayoutTransformer>
</Grid>
Because of the lack of MultiBinding you'd probably additionally have to manually handle the input value (from Slider controls etc) changed events and then set the AnimationValues of RotateMediator etc accordingly.
I am trying to think of the best way to scale two panels proportionally.
If I have a grid that contains two canvases, both stacked horizontally next to each other, I want canvas (A) to scale to the size of canvas (B), proportionally, though.
So, essentially, if canvas (B) increases in size, canvas (A) decreases, and if canvas (A) increases, canvas (B) decreases.
I'm thinking of using a converter to do this, but wanted to know if anyone had any good ideas.
Below is a link that demonstrates the desired behavior. Refer to the pan/zoom control in the lower right corner of the screen. That control represents a preview of the main screen. If you press on the zoom button within the pan/zoom control, the main screen zooms in, and the rectangular "pan" area in the pan/zoom control decreases in size.
http://quince.infragistics.com/#/Search/ViewPattern$pattern=Button+Groups/PatternExamples$guid=289a497a-6632-455a-87b6-74ee70c2d3be
Thanks!
Chris
A converter is probably be the best way to go. You could also use RenderTransform.ScaleX/ScaleY instead of adjusting the Height/Width of the canvas.
Here's an example of binding to a property. Not sure if it'd be better then a converter.
<Canvas Background="Blue">
<Canvas x:Name="canvas1" ClipToBounds="True" Background="Red" Width="100" Height="100">
<Canvas.RenderTransform>
<ScaleTransform ScaleX="{Binding ElementName=slider, Path=Value}" ScaleY="{Binding ElementName=slider, Path=Value}"/>
</Canvas.RenderTransform>
</Canvas>
<Canvas x:Name="canvas2" ClipToBounds="True" Background="Green" Grid.Column="2" Height="100" Width="100" Canvas.Left="200">
<Canvas.RenderTransform>
<ScaleTransform ScaleX="{Binding ScaleValue2}" ScaleY="{Binding ScaleValue2}"/>
</Canvas.RenderTransform>
</Canvas>
<Slider x:Name="slider" Canvas.Top="200" Width="200" Value="{Binding Path=ScaleValue, Mode=TwoWay}" Maximum="2"></Slider>
</Canvas>
Code:
public partial class Window1 : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Window1()
{
InitializeComponent();
this.DataContext = this;
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private double scaleValue = 1;
public double ScaleValue
{
get
{
return scaleValue;
}
set
{
scaleValue = value;
NotifyPropertyChanged("ScaleValue");
NotifyPropertyChanged("ScaleValue2");
}
}
public double ScaleValue2
{
get
{
return slider.Maximum - ScaleValue;
}
}
}