I have an image inside a border and I would like to show different parts of the image source in the image box at different times. Specifically, when a certain textbox gets focus, I want to change the image so it zooms to a certain portion of the image content.
Here's the XAML:
<Border BorderBrush="Silver" BorderThickness="1" Grid.Column="5" Height="504"
HorizontalAlignment="Left" Margin="20,116,0,0" Name="border1"
VerticalAlignment="Top" Width="410" ClipToBounds="True">
<Image Height="493" Name="image5" Stretch="Fill" Width="390"
ClipToBounds="True" BindingGroup="{Binding}"
Clip="{Binding ElementName=border1}"
Cursor="Hand" StretchDirection="Both" />
</Border>
For an example, my image source is 2550 x 3320 pixels. I would like the image box to show the source in a rectangle starting at point 1755,300 with width of 650 and height of 230. I do not want to use CloneBitmap to cut that rectangle out and display it because I also have a manual zoom set up for this image where the user can use the mouse wheel to zoom in and out and click & drag to pan the image. I still want to allow that after we zoom to desired area.
UPDATE:
I've tried implementing colinsmith's answer, but whenever I change the scrollviewer's offsets, It chops the image, so if I later move (click and drag to pan) it, it's empty space. I've had this working before with just the image inside the scrollviewer, but now I have an image inside a scrollviewer inside a border. The border is necessary for my zoom and pan as I have it set up now.
My updated XAML:
<Border BorderBrush="Silver" BorderThickness="1" Grid.Column="5" Height="504"
HorizontalAlignment="Left" Margin="20,116,0,0" Name="border1"
VerticalAlignment="Top" Width="410" ClipToBounds="True">
<ScrollViewer x:Name="image5scroll" VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Hidden">
<Image Height="493" Name="image5" Stretch="Fill" Width="390"
ClipToBounds="True" BindingGroup="{Binding}"
Clip="{Binding ElementName=image5scroll}" Cursor="Hand"
StretchDirection="Both" />
</ScrollViewer>
</Border>
And code behind:
public void imageZoom(Element element, int index)
{
if (element.Rectangle.Left - 100 > 0)
{
image5scroll.ScrollToHorizontalOffset(element.Rectangle.Left - 100);
image5scroll.Width = element.Rectangle.Width + 200;
image5scroll.Height = element.Rectangle.Height + 200;
border1.Width = image5scroll.Width;
border1.Height = image5scroll.Height;
image5.Width = image5scroll.Width;
image5.Height = image5scroll.Height;
image5.Stretch = System.Windows.Media.Stretch.None;
}
else
{
image5scroll.ScrollToHorizontalOffset(0);
}
if (element.Rectangle.Top - 100 > 0)
{
image5scroll.ScrollToVerticalOffset(element.Rectangle.Top - 100);
}
else
{
image5scroll.ScrollToVerticalOffset(0);
}
}
You can use a ScrollViewer to wrap your Image...then you can tell the ScrollViewer to go to a vertical and horizontal offset by calling the ScrollToVerticalOffset() and ScrollToHorizontalOffset() methods (as there isn't a property which you can set).
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Border BorderBrush="Silver" BorderThickness="1" Grid.Column="5" Height="504"
HorizontalAlignment="Left" Margin="20,116,0,0" Name="border1" VerticalAlignment="Top" Width="410">
<ScrollViewer x:Name="image5scroll" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden">
<Image Height="493" Name="image5" Stretch="None" Width="390" BindingGroup="{Binding}"
Cursor="Hand" Source="http://www.noaanews.noaa.gov/stories/images/goes-12-firstimage-large081701.jpg"/>
</ScrollViewer>
</Border>
</Grid>
</Page>
However, if you must be be able to set the position of the ScrollViewer using properties instead of code-behind then a solution is to define an Attached Property which does the calls to ScrollToVerticalOffset/ScrollToHorizontalOffset for you underneath.
http://marlongrech.wordpress.com/2009/09/14/how-to-set-wpf-scrollviewer-verticaloffset-and-horizontal-offset/
Related
In a User Control I have below content:
<Grid>
<Popup x:Name="myPopup"
AllowsTransparency="True"
StaysOpen="False"
PlacementTarget="mouse"
Placement="Bottom">
<Grid>
<Rectangle MinWidth="40" Fill="#fff" Stroke="#BEBEBE"/>
<StackPanel Width="Auto" Height="Auto">
<TextBlock FontFamily="Segoe UI"
FontSize="12"
FontWeight="Bold"
Foreground="#666666"
Margin="12,15,12,0"
MinWidth="100"
MaxWidth="150"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
UseLayoutRounding="True"
Text="{Binding Path=someText}"
TextWrapping="Wrap"/>
<TextBlock FontFamily="Segoe UI"
FontSize="12"
FontWeight="SemiBold"
Foreground="#666666"
Margin="12,8,12,10"
MinWidth="100"
MaxWidth="150"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
UseLayoutRounding="True"
Text="{Binding Path=moreText}"
TextWrapping="Wrap" />
<Button x:Name="CloseBtn"
Content="Ok"
Background="White"
BorderBrush="#BEBEBE"
Foreground="Black"
HorizontalAlignment="Right"
Margin="5"
Padding="5"
Click="Close_Click"/>
</StackPanel>
</Grid>
</Popup>
</Grid>
This produces the follwowing window when button is shown:
and this another one when button is not shown:
The button in the window sometimes is visible and sometimes not (making it visible or collapsed respectively), it depends on some conditions. This is make visible or not in code-behind.
When the button is collapsed and not visible, the remaining space at the bottom of the window is removed as expected, the window resizes fine both its height and width according to its content. All works ok. This is just what I want, I mean, the window to be resizable to fit its content without leaving remaining whitespace areas. This is perfect.
Now, instead of a rectangle, I replace it with a path because I want it to be displayed as a speech bubble with an arrow pointing to the target. This path data is created dynamically at run time each time the stackpanel changes its size (I have added a SizeChanged event to the stack panel). See below how it is with this modification (Below code is the same as above but using a path instead of rectangle):
<Grid>
<Popup x:Name="myPopup"
AllowsTransparency="True"
StaysOpen="False"
PlacementTarget="mouse"
Placement="Bottom">
<Grid>
<!-- <Rectangle MinWidth="40" Fill="#fff" Stroke="#BEBEBE"/> -->
<Path x:Name="myPath" Visibility="Hidden"
Fill="White"
Stroke="#BEBEBE"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
UseLayoutRounding="True">
</Path>
<StackPanel x:Name="stackpanel" Width="Auto" Height="Auto"
SizeChanged="StackPanel_SizeChanged">
<TextBlock FontFamily="Segoe UI"
FontSize="12"
FontWeight="Bold"
Foreground="#666666"
Margin="12,15,12,0"
MinWidth="100"
MaxWidth="150"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
UseLayoutRounding="True"
Text="{Binding Path=someText}"
TextWrapping="Wrap"/>
<TextBlock FontFamily="Segoe UI"
FontSize="12"
FontWeight="SemiBold"
Foreground="#666666"
Margin="12,8,12,10"
MinWidth="100"
MaxWidth="150"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
UseLayoutRounding="True"
Text="{Binding Path=moreText}"
TextWrapping="Wrap" />
<Button x:Name="CloseBtn"
Content="Ok"
Background="White"
BorderBrush="#BEBEBE"
Foreground="Black"
HorizontalAlignment="Right"
Margin="5"
Padding="5"
Click="Close_Click"/>
</StackPanel>
</Grid>
</Popup>
</Grid>
In the StackPanel_SizeChanged event I create a path Data in code-behind that then I associate to the path in the xaml view. The code in the StackPanel_SizeChanged event is the following:
private void StackPanel_SizeChanged(object sender, SizeChangedEventArgs e)
{
double width = e.NewSize.Width;
double height = e.NewSize.Height;
string stringPathData = $"M8,7.41 L15.415,0 L22.83,7.41 L{width},7.41 L{width},{height} L0,{height} L0,7.41 L8,7.41";
TypeConverter converter = TypeDescriptor.GetConverter(typeof(Geometry));
this.myPath.Data = (Geometry)converter.ConvertFrom(stringPathData);
}
Path is drawn correctly around the window. But now the window does not adjust to fit its content. It fits the content width correctly but not the height. A whitespace area is kept at the bottom side of the window even when button is collapsed. See below:
I have observed that the window always keeps the largest height. I mean if I put some large text and after that a shorter text in the window, window maintains the largest height (in this case the first correponding to the large text) so this produces a whitespace are at the bottom side of the window. It looks like that when changing from a larger text to a shorter text the StackPanel_SizeChanged event does not get fired (well in fact it does not fire, I have debugged it). So I get the following independently if button is shown or not:
The close button event Close_Click is the following:
private void Close_Click(object sender, RoutedEventArgs e)
{
if (popup != null)
{
this.CloseBtn.Visibility = Visibility.Collapsed;
myPopup.IsOpen = false;
myPopup.StaysOpen = false;
}
}
I need the window to fit its content height and width.
PS. Please, if you have any doubts or questions or it is not clear for you, let me know before you spent your time reproducing it.
So the problem is, that the Stackpanel doesnt get rezised? In that case reloading the Popup when the text is changed should help.
Otherwise you could rezise the Popup manually which then resizes the Stackpanel (https://social.msdn.microsoft.com/Forums/vstudio/en-US/f98226ae-31d9-47da-a69a-687659378c94/how-to-resize-a-popup-in-wpf?forum=wpf).
I'm trying to resize a custom usercontrol through a thumb positioned inside the usercontrol itself.
This is the usercontrol:
<UserControl x:Class="ER.Entity"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ER"
mc:Ignorable="d"
Name="entityRoot">
<Grid Name="entityGrid">
<Thumb x:Name="resizeThumb" Height="10" Width="10" Margin="200,90,-15,-10" DragDelta="Resize" />
<Border Opacity="100" BorderThickness="5" BorderBrush="Black">
<TextBlock x:Name="textBox1" TextWrapping="Wrap" Text="{Binding ElementName=entityRoot, Path=EntityName}" VerticalAlignment="Top" PreviewMouseDown="EntityPreviewMouseDownHandler" PreviewMouseMove="EntityPreviewMouseMoveHandler" PreviewMouseUp="EntityPreviewMouseUpHandler" />
</Border>
</Grid>
And this is the method triggered by the DragDelta event:
private void Resize(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
entityGrid.SetValue(WidthProperty, entityGrid.ActualWidth + e.HorizontalChange);
entityGrid.SetValue(HeightProperty, entityGrid.ActualHeight + e.VerticalChange);
}
The problem is that, when i drag the thumb, the resizing of the usercontrol is much bigger than the mouse movement.
The problem is that because your Thumb object is referenced to the left and top of the Grid, when you change the size of the Grid that in turn causes an effective relative movement of the Thumb in addition to the movement caused by the mouse.
Changing the Thumb alignment to be Right and Bottom, and moving the Thumb positioning from the Thumb.Margin to the Grid's Width and Height properties allows the Thumb to resize the Grid as intended:
<Grid Name="entityGrid" Width="200" Height="90">
<Thumb x:Name="resizeThumb" Height="10" Width="10"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="0,0,-15,-10" DragDelta="Resize" />
<Border Opacity="100" BorderThickness="2" BorderBrush="Black">
<TextBlock x:Name="textBox1" TextWrapping="Wrap"
Text="{Binding ElementName=entityRoot, Path=EntityName}"
VerticalAlignment="Top"
PreviewMouseDown="EntityPreviewMouseDownHandler"
PreviewMouseMove="EntityPreviewMouseMoveHandler"
PreviewMouseUp="EntityPreviewMouseUpHandler" />
</Border>
</Grid>
My code for moving the image control is:
<Grid Name="grid" Width="400" Height="400" ManipulationMode="All"
ManipulationDelta="Grid_ManipulationDelta_1" Margin="0,58,0,182">
<Grid.RenderTransform>
<CompositeTransform x:Name="transform" />
</Grid.RenderTransform>
<Image x:Name="Image1" Source="Assets/SmallLogo.png" Stretch="None"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
Manipulation delta has code:
private void Grid_ManipulationDelta_1(object sender, ManipulationDeltaRoutedEventArgs e)
{
if (e.PointerDeviceType == PointerDeviceType.Touch)
{
this.transform.TranslateX += e.Delta.Translation.X;
this.transform.TranslateY += e.Delta.Translation.Y;
OutText.Text = sender.ToString();
}
else
{
e.Handled = true;
}
}
This works perfect when I move image wherever on my grid.
Now for resizing the image control I have different implementation,
<ScrollViewer x:Name="scrl2" SizeChanged="scrl_SizeChanged" ZoomMode="Enabled"
HorizontalScrollMode="Enabled" VerticalScrollMode="Enabled"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"
MinZoomFactor="0.1" MaxZoomFactor="10" Margin="0" VerticalAlignment="Bottom">
<Image Source="Assets/SmallLogo.png" Stretch="None"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</ScrollViewer>
The scroll viewer can be resized here and image is child so it also can. And it changes size on runtime perfectly, but note that this is different code.
I want to implement both scenarios on image control, which seems logical, so I edit my xaml like this:
<Grid Name="grid" Width="400" Height="400" ManipulationMode="All"
ManipulationStarted="Grid_ManipulationStarted_1"
ManipulationCompleted="Grid_ManipulationCompleted_1"
ManipulationDelta="Grid_ManipulationDelta_1" Margin="0,58,0,182">
<Grid.RenderTransform>
<CompositeTransform x:Name="transform" />
</Grid.RenderTransform>
<ScrollViewer x:Name="scrl2" SizeChanged="scrl_SizeChanged"
ZoomMode="Enabled" HorizontalScrollMode="Enabled"
VerticalScrollMode="Enabled" HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
MinZoomFactor="0.1" MaxZoomFactor="10" Margin="0" VerticalAlignment="Bottom">
<Image x:Name="Image1" Source="Assets/SmallLogo.png" Stretch="None"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</ScrollViewer>
</Grid>
The RenderTransform should take care of movement and ScrollViewer should do pinch thing. But that does not work. In separate code when image is in scroll viewer only - pinch to zoom works, when image is in grid - drag to move works.
In the above code where I put image in ScrollViewer and that ScrollViewer in grid, only pinch to zoom works, movement does not work. I tried to undo the Horizontal Vertical alignment settings (set them to something null) but that is not possible. The image resizes, but remains always in the position it is set in alignment settings.
What is wrong in this implementation?
In my application, I have a StackPanel with the Orientation set to 'horizontal'. In my StackPanel there are 4 Images. When I try to scroll this content horizontally, it only scrolls a few pixels and I cannot see the whole content. When I change the Orientation of my StackPanel to vertical, I can scroll my whole content vertical. Why it isn't possible to scroll it hotizontally? Any ideas how I can solve this problem?
<Grid>
<ScrollViewer>
<StackPanel Orientation="Horizontal" >
<Canvas Margin="120,0,0,0"
Width="310"
Height="390">
<Image Width="310"
Height="390"
Source="ms-appx:///Assets/Image/background_teaser.png"/>
</Canvas>
<Canvas Margin="120,0,0,0"
Width="310"
Height="390">
<Image Width="310"
Height="390"
Source="ms-appx:///Assets/Image/background_teaser.png"/>
</Canvas>
<Canvas Margin="120,0,0,0"
Width="310"
Height="390">
<Image Width="310"
Height="390"
Source="ms-appx:///Assets/Image/background_teaser.png"/>
</Canvas>
<Canvas Margin="120,0,0,0"
Width="310"
Height="390">
<Image Width="310"
Height="390"
Source="ms-appx:///Assets/Image/background_teaser.png"/>
</Canvas>
</StackPanel>
</ScrollViewer>
</Grid
Horizontal scrolling is not enabled per default.
<ScrollViewer HorizontalScrollMode="Auto" HorizontalScrollBarVisibility="Auto">
I had some issues working with Stackpanels in a ScrollViewer. Try wrapping the Stackpanel in another Grid.
And as the others have pointed out, you need to set the HorizontalScrollMode on your ScrollViewer
Pretty straight forward. I have a rectangle with a label over top of it. I'f like to know how to get the rectangle to scale to fit the text.
my XAML:
<Grid x:Name="LayoutRoot" Background="White" Height="158" Width="264">
<Rectangle Height="22" HorizontalAlignment="Left" Name="rectangle1" Stroke="Black" MinWidth="40" StrokeThickness="1" VerticalAlignment="Top" RadiusX="6" RadiusY="6" Fill="#1b6487" Width="64"></Rectangle>
<sdk:Label Margin="9,3,209,0" Name="label1" VerticalAlignment="Top" Content="$999.99" />
</Grid>
Remove your explicit widths and heights.
You've got the Rectangle and the Label in the same cell of the Grid, so by default they will be the same size. You're overriding that and telling them not to be.
Alternatively, you could wrap a Border around your Label. This is the sort of thing that Border is meant for.