WPF scrollviewer overview control - wpf

in my WPF application I use Scrollviewer to show a diagram.
The diagram can be pretty big and complex and usually you can see only a smart part of a diagram in the Scrollviewer control.
To facilitate the diagram navigation I will create a control which should contain the whole diagram picture. If you click on specific place in this control the Scrollviewer should scroll the diagram to this place.
Something like that
What is the easiest way to do that?
Many thanks and sorry for my terrible English.

XAML:
<Grid>
<ScrollViewer x:Name="TheScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Image x:Name="FullImage" Source="Test.jpg" Stretch="None"></Image>
</ScrollViewer>
<Border BorderBrush="Red" BorderThickness="1" VerticalAlignment="Bottom" HorizontalAlignment="Right">
<Image x:Name="ThumbnailImage" Height="100" Source="Test.jpg" MouseDown="ThumbnailImage_OnMouseDown"/>
</Border>
</Grid>
Code-behind:
private void ThumbnailImage_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var position = e.GetPosition(ThumnailImage);
var horOffset = position.X / ThumnailImage.ActualWidth * FullImage.ActualWidth;
var verOffset = position.Y / ThumnailImage.ActualHeight * FullImage.ActualHeight;
TheScrollViewer.ScrollToHorizontalOffset(horOffset);
TheScrollViewer.ScrollToVerticalOffset(verOffset);
}

Related

How to fit the image to to border after rotation in WPF

I have one border inside which am having an image. on button click am rotating that image to 90 degrees.
this is my original image
The below is after rotation
As you can after rotation my image doesn't fit into the border. I need it to be fill the border completely. What is am missing here?
I think, you are using RenderTransform to rotate the image.
Instead, use LayoutTransform.
See the sample:
<StackPanel>
<Border Width="500" Height="300" BorderBrush="Black" BorderThickness="1">
<Image Source="sombrero.jpg" Stretch="Fill" x:Name="img" HorizontalAlignment="Center" VerticalAlignment="Center">
</Image>
</Border>
<Button Content="Rotate" Click="ButtonBase_OnClick"></Button>
</StackPanel>
Codebehind:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
img.LayoutTransform = new RotateTransform(90);
}
Before:
After:
Hope this helps.

Creating a custom Stackpanel with Transparent "Windows"

Good evening. I have been researching my issue for about two days now, and have tried at least a dozen variations of the problem. Every single last one of them has been a complete total failure, so I won't bother posting any code here.
What I need is a StackPanel for a Border-less Window. The StackPanel needs to have an opaque background, and the children need to have a transparent background. I need the transparency to carry ALL the way through the child element, StackPanel, and underlying main window.
Does anyone have any idea as to how to go about this, and where I should start? I am an intermediate level programmer, and have hit a solid wall here. I have tried to inherit from Panel Class, over-riding the usual suspects; MeasureOverride, ArangeOveride, OnRender, etc..
One of the thoughts I also had, was using some form or another of CombinedGeometry between the parent, and all child nodes.
Thanks in advance!!
What you are probably missing is Window.AllowsTransparency="True". Sad to say that you have to build your own window title and border because WindowStyle property must be set to None.
I've created a sample for you:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
AllowsTransparency="True"
Background="Transparent"
WindowStyle="None"
WindowStartupLocation="CenterScreen">
<Border BorderBrush="Black" BorderThickness="1">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Application">
<MenuItem Header="_Close" Click="MenuItem_Click"/>
</MenuItem>
</Menu>
<ToolBar DockPanel.Dock="Bottom">
<Button>sample button</Button>
</ToolBar>
<StackPanel>
... your whiteboard ...
</StackPanel>
</DockPanel>
</Border>
</Window>
.
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
Ok, so after many, MANY, hours of brute force "hacking," I have managed to figure out a means to accomplish what I need.
<Border Name="OuterBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderBrush="Orange" BorderThickness="1.5">
<Border Name="FillerStrip" BorderBrush="Black" BorderThickness="20">
<Border Name="InnerBorder" BorderBrush="Orange" BorderThickness="1.5">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent">
<local:GlassBoardControl BorderBrush="Orange" BorderThickness="2" AxisBrush="LawnGreen" AxisThickness="15" x:Name="screen" />
</StackPanel>
</Border>
</Border>
</Border>
Now my only question is how on earth do I go about creating a custom panel with this as it's border? Any ideas as to where I should start would be much appreciated.

wpf ScrollViewer and SizeChanged event

Can anyone explain to me the relationship between ScrollViewer and SizeChanged event? Whenever I put a scrollViewer around a grid, numerous SizeChanged event gets fired. What is the relationship between the two? Thanks a lot.
EDIT:
From mdm20's comment, I noticed that the ActualWidth and ActualHeight of the grid increases continuously if I wrap the grid around a ScrollViewer. Can anyone explain why this is the case? Do I need to have hard values for the width and height of the grid?
EDIT #2:
The resizing is done through code posted below. Thanks for looking into this
private void chartGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
textCanvas.Width = chartGrid.ActualWidth;
textCanvas.Height = chartGrid.ActualHeight;
legendCanvas.Children.Clear();
chartCanvas.Children.RemoveRange(1, chartCanvas.Children.Count - 1);
textCanvas.Children.RemoveRange(1, textCanvas.Children.Count - 1);
AddChart();
}
Corresponding XAML code is below:
<ScrollViewer Name="chartScrollViewer">
<Grid Margin="0" x:Name ="chartGrid" Grid.Column="1" Grid.Row="1" ClipToBounds="True" Background="Transparent" SizeChanged="chartGrid_SizeChanged">
<Canvas Margin="2" Name="textCanvas" ClipToBounds="True" Grid.Column="1" Grid.Row="1" Height="1200">
<Canvas Name="chartCanvas" ClipToBounds="True">
<Canvas Name="legendCanvas" Background="Transparent" />
</Canvas>
</Canvas>
</Grid>
</ScrollViewer>
You are getting into a loop. I think what is happening is that when you change the canvas size, it prompts the grid to do a layout pass, which causes the ScrollViewer to do a layout pass, which causes the grid to resize itself, which starts the cycle over again.

Get the scroll position from a RichTextBox?

I've created a highlighting mechanism for a RichTextBox in Silverlight 4. It'll get character positions and draw rectangle(s) over the text.
The trouble I have now is with scrolling on the RichTextBox. As I scroll all of my precious highlighting gets left behind. Is there any way I can add an event handler to a scroll event and/or a scrolling position of the RichTextBox? Or is there some better way in which I can link the position of the highlighting rectangles to the RichTextBox?
The trick would be to get what ever panel (I guess its a Canvas?) that you are overlaying the RichTextBox with to actually exist within the same ScrollViewer that rich text exists in.
The following is very rough idea but should get you on the path to reasonable solution.
You can do this using a custom style for the RichTextBox. The default style for this control can be found here.
Copy this style into a resource in your containing UserControl and point your RichTextBox Style property at it. So far nothing is different but now you can play about with the template. The relevant portion currently looks like this:-
<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
<ScrollViewer x:Name="ContentElement" Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False" />
</Border>
Now we can tweak it like this:-
<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
<ScrollViewer Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False">
<Grid>
<ContentControl x:Name="ContentElement" />
<Canvas x:Name="HighlightOverlay" />
</Grid>
</ScrollViewer>
</Border>
You'll note that we've moved the name "ContentElement" from the ScrollViewer to the new ContentControl. Having a FrameworkElement called "ContentElement" is the only feature that the RichTextBox stipulates about its template.
Now overlaying this ContentControl we can place a Canvas where you can place your highlighting rectangles. If the user scrolls this RichTextBox the whole Grid containing both the Content and the Highlights will scroll together.
The only remaining trick is acquiring the "HighlightOverlay" so that you can add your rectangle to it. Here is some code that will grab it:-
private Canvas HightlightOverlay;
public MyUserControl()
{
InitializeComponent();
MyRichText.LayoutUpdated += MyRichText_LayoutUpdated;
}
void MyRichText_LayoutUpdated(object sender, EventArgs e)
{
HightlightOverlay = MyRichText.Descendents()
.OfType<Canvas>()
.FirstOrDefault(elem => elem.Name == "HighlightOverlay");
}
You will be wondering where the Descendents method is coming from, it is here.
Anthony W Jones came up with a brilliant solution. There were just a couple tweaks to the XAML I had to make.
As suggested I started with this inside the template:
<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
<ScrollViewer Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False">
<Grid>
<ContentControl x:Name="ContentElement" />
<Canvas x:Name="HighlightOverlay" />
</Grid>
</ScrollViewer>
</Border>
But the ContentControl messed things up somehow and you can't actually type into the RichTextBox anymore. Also, the scroll bars weren't showing up.
But I found the two changes necessary to make this work:
<Border x:Name="MouseOverBorder" BorderBrush="Transparent" BorderThickness="1">
<ScrollViewer BorderThickness="0" IsTabStop="False" Padding="{TemplateBinding Padding}" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid x:Name="ContentElement" />
<Canvas x:Name="HighlightOverlay" />
</Grid>
</ScrollViewer>
</Border>
Adding HorizontalScrollBarVisibility="Auto" and VerticalScrollBarVisibility="Auto" brought the scroll bars back, and simply using Grid instead of the ContentControl made the RichTextBox editable again.

cloning the controls

I am designing a silverlight application in which i will have a rectangle control at the left side, when i click the rectangel and drag a copy of the rectangle control should be created and dragged and dropped in to the page.
Please can anyone help me with the code
For simplicity I'm going to leave out the Drag-Drop stuff since this question seems mainly about the cloning aspect.
The tool needed is the DataTemplate class. You place in a resource dictionary the set of items you want to clone each enclosed in a DataTemplate. You can use ContentPresenter to display instances of these items in say stack panel on the left. You can then use code to create instances of the template content and place them in say a Canvas on the right.
Example.
Xaml:-
<UserControl x:Class="SilverlightApplication1.CloningStuff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<UserControl.Resources>
<DataTemplate x:Key="Rectangle">
<Rectangle Stroke="Blue" StrokeThickness="3" Fill="CornflowerBlue" Width="100" Height="75" />
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel>
<ContentPresenter x:Name="Rectangle" ContentTemplate="{StaticResource Rectangle}" />
</StackPanel>
<Canvas x:Name="Surface" MouseLeftButtonDown="Surface_MouseLeftButtonDown" Grid.Column="1" Background="Wheat">
</Canvas>
</Grid>
</UserControl>
Code:-
public partial class CloningStuff : UserControl
{
public CloningStuff()
{
InitializeComponent();
}
private void Surface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Canvas target = (Canvas)sender;
Point p = e.GetPosition(target);
Rectangle r = (Rectangle)((DataTemplate)Resources["Rectangle"]).LoadContent();
Canvas.SetLeft(r, p.X);
Canvas.SetTop(r, p.Y);
target.Children.Add(r);
}
}
This shows using a ContentPresenter to display your rectangle. In place of drag-dropping (for which there are plenty of examples of elsewhere) this code just creates a Clone of the rectangle whereever the user clicks in the Canvas.

Resources