How Remove the Stroke from Arabic character connection position on WPF? - c#

My project use some font like Arabic character, I also need use stroke on the font.
I already try some way to display the Stroke, like :
https://stackoverflow.com/a/9887123/1900498 (OuterTextBlock)
https://stackoverflow.com/a/97728/1900498 (OutlineText)
The Stroke can display now, but the problem is the Arabic character connection position still display some stroke, So I need to remove it.
the result like this:
So Is there any way I can remove the stroke from the connection position? I mean If the character is connection, just stroke on the full connection outsize, not all character stroke 1 time.
I need the result like this(I'm using PHOTOSHOP to edit the second picture and remove the stroke from character connection position, not WPF, that is just for you understand the correct result must handle by WPF)
UPDATE:
please download the 2 class from the 2 link , then use This code :
<Window x:Class="test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:test"
Title="MainWindow" Height="200" Width="400">
<Grid Background="Black">
<local:OutlinedTextBlock HorizontalAlignment="Left" Height="200" VerticalAlignment="Bottom" FontSize="150" StrokeThickness="1" Fill="White" Stroke="Red" TextDecorations="">ئالما</local:OutlinedTextBlock>
<local:OutlinedText HorizontalAlignment="Right" Height="200" VerticalAlignment="Top" FontSize="150" StrokeThickness="1" Fill="White" Stroke="Red" Text="ئالما"></local:OutlinedText>
</Grid>

Initial observations: The artifacts you are seeing seem to be the actual edges of the single characters. The characters touch and overlap slightly, while you would like to perceive several characters as one consecutive shape.
I have tried my suggestion from the comments by extending the OutlinedTextBlock class from the first linked answer by Kent Boogaart.
The Geometry instance obtained by OutlinedTextBlock from the BuildGeometry method consists of nested GeometryGroup instances (at least, separate such groups are created when incorporating text with several reading directions). After diving through those groups, you will find one PathGeometry instance per character.
N.B.: This is what I figured out when looking at the data. It is probably undocumented (?), and if it ever changes, this solution may not work any more.
By using the Geometry.Combine method, all of these PathGeometry instances can be combined with GeometryCombineMode.Union, which means that overlapping areas will be merged.
First, I have defined a method for finding all the PathGeometry objects. It recursively dives into the hierarchy of GeometryGroup objects and is rather not very efficient, but it serves to demonstrate the point - feel free to optimize this performance-wise:
private IEnumerable<PathGeometry> FindAllPathGeometries(Geometry geometry)
{
var pathGeometry = geometry as PathGeometry;
if (pathGeometry != null) {
yield return pathGeometry;
} else {
var geoGroup = geometry as GeometryGroup;
if (geoGroup != null) {
foreach (var geo in geoGroup.Children) {
foreach (var pg in FindAllPathGeometries(geo)) {
yield return pg;
}
}
}
}
}
Then, I have modified the OutlinedTextBox.EnsureGeometry method. Originally, the geometry retrieved from BuildGeometry was directly displayed:
private void EnsureGeometry()
{
if (this.textGeometry != null) {
return;
}
this.EnsureFormattedText();
this.textGeometry = this.formattedText.BuildGeometry(new Point(0, 0));
}
Instead, I now process that geometry by iterating over all of the contained PathGeometry instances and incrementally combining them with the Union mode. For the sake of convenience (and so you can actually observe the difference), I have made that behaviour optional by adding a MergeShapes property:
private void EnsureGeometry()
{
if (this.textGeometry != null) {
return;
}
this.EnsureFormattedText();
var originalGeometry = this.formattedText.BuildGeometry(new Point(0, 0));
if (MergeShapes) {
PathGeometry newGeo = new PathGeometry();
foreach (var pg in FindAllPathGeometries(originalGeometry)) {
newGeo = Geometry.Combine(newGeo, pg, GeometryCombineMode.Union, null);
}
this.textGeometry = newGeo;
} else {
this.textGeometry = originalGeometry;
}
}
public static readonly DependencyProperty MergeShapesProperty = DependencyProperty.Register("MergeShapes",
typeof(bool),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public bool MergeShapes {
get {
return (bool)GetValue(MergeShapesProperty);
}
set {
SetValue(MergeShapesProperty, value);
}
}

Related

Access GUI object method from ViewModel C#

I'm trying to create a "fake" drag animation on a chart. The goal is to allow the user to manually edit points from a graph by dragging them.
The idea is fairly simple: I put a canvas on top of the graph and upon clicking on one of the graph's point, a red circle becomes visible on the clicked mark value, as well as 2 lines coming from the previous and next graph point. I "just" have to set the opacity of each shape from 0 to 1 and specify their position (XAML code):
<Canvas Name="GraphOverlay" Grid.Row="1">
<Ellipse Canvas.Left="{Binding ElipseCanvasLeft}" Canvas.Top="{Binding ElipseCanvasTop}" Stroke="{Binding ShapesColor}" StrokeThickness="6" Width="10" Height="10" Opacity="{Binding ShapesOpacity}"/>
<Line X1="{Binding leftX1}" X2="{Binding leftX2}" Y1="{Binding leftY1}" Y2="{Binding leftY2}" Stroke="{Binding ShapesColor}" StrokeThickness="3" Opacity="{Binding ShapesOpacity}"/>
<Line X1="{Binding rightX1}" X2="{Binding rightX2}" Y1="{Binding rightY1}" Y2="{Binding rightY2}" Stroke="{Binding ShapesColor}" StrokeThickness="3" Opacity="{Binding ShapesOpacity}"/>
</Canvas>
So far I can easily get the graph values from the point I clicked on as well as the previous point and next point on the graph. However, to define the position of each shape on the cavas (1 circle and two lines), I need to convert these graph values into actual pixel values. For the circle, I just have to get the mouse position when the user clics. I am using this:
<i:Interaction.Behaviors>
<utility:MouseBehaviour MouseX="{Binding PanelX, Mode=OneWayToSource}" MouseY="{Binding PanelY, Mode=OneWayToSource}"/>
</i:Interaction.Behaviors>
And the MouseBehaviour class:
public class MouseBehaviour : System.Windows.Interactivity.Behavior<FrameworkElement>
{
public static readonly DependencyProperty MouseYProperty = DependencyProperty.Register(
"MouseY", typeof(double), typeof(MouseBehaviour), new PropertyMetadata(default(double)));
public double MouseY
{
get { return (double)GetValue(MouseYProperty); }
set { SetValue(MouseYProperty, value); }
}
public static readonly DependencyProperty MouseXProperty = DependencyProperty.Register(
"MouseX", typeof(double), typeof(MouseBehaviour), new PropertyMetadata(default(double)));
public double MouseX
{
get { return (double)GetValue(MouseXProperty); }
set { SetValue(MouseXProperty, value); }
}
protected override void OnAttached()
{
AssociatedObject.MouseMove += AssociatedObjectOnMouseMove;
}
private void AssociatedObjectOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
var pos = mouseEventArgs.GetPosition(AssociatedObject);
MouseX = pos.X;
MouseY = pos.Y;
}
protected override void OnDetaching()
{
AssociatedObject.MouseMove -= AssociatedObjectOnMouseMove;
}
}
And PanelX and PanelY are two properties from the ViewModel. Source
However I am struggling getting the pixel coordinate of the previous and next point. On livechart's website, there is an example that shows how to use both commands and events to access graph values. However, when using events, they have access to the ConvertToPixels method which is associated to the chart object. I don't have access to this method from the viewmodel.
Is there a way around it, or should I manually code that method ?
Edit: Here is a picture that shows what I am aiming for and what I mean by "next point", "previous point", ... Hopefully it helps!
I found a solution, I don't really know how ugly this is, but it keeps my codebehind nice and blank.
It's (very) ugly and should never pass a code review :)
Your view model now has a dependency not only on the App class but also on a particular control in a particular window that must be present on the screen for your code to work. This will for example never work in a unit test and breaks the MVVM pattern badly.
You should inject the view model with an interface and interact with the view using this interface. There is an example of how to do this available here if you are interested.
I found a solution, I don't really know how ugly this is, but it keeps my codebehind nice and blank.
To get access to that UI element, you have to first name it in the XAML:
<lvc:CartesianChart Name="Chart" >
...
</lvc:CartesianChart>
An then look for it in you app windows:
var MyChart = App.Current.Windows[0].FindName("Chart") as Chart;
I am not really at peace with the Windows[0] being hard coded, but this does the trick.

How to flip PathGeometry vertically?

I have a PathGeometry that I want to flip vertically. I have tried the following but it is not working, am I missing something?
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures.Add(myPathFigure);
PathGeometry flipMyPathGeometry = new PathGeometry();
ScaleTransform transform = new ScaleTransform(0, -1);
flipMyPathGeometry = Geometry.Combine(Geometry.Empty, myPathGeometry, GeometryCombineMode.Union, transform);
A big problem there is that your width will be zero.
The X and Y scales are factors. As in multipliers. Anything times Zero is zero.
Hence
ScaleTransform(0, -1);
Will give you something with no width.
You presumably want the same width and hence:
ScaleTransform(1, -1);
That might still have another problem if you want the thing to be flipped about it's centre but at least it ought to show up when you use it.
The CenterY calculation is perhaps less than obvious. You can work out the height of a geometry using it's bounds.
Since you're creating a new pathgeometry, maybe you want to retain the original without any transform.
I put some code together that manipulates a geometry from resources and uses it to add a path to a canvas.
Markup:
<Window.Resources>
<Geometry x:Key="Star">
M16.001007,0L20.944,10.533997 32,12.223022 23.998993,20.421997 25.889008,32 16.001007,26.533997 6.1109924,32 8,20.421997 0,12.223022 11.057007,10.533997z
</Geometry>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="myButton" Click="MyButton_Click">
</Button>
<Canvas Grid.Column="1" Name="myCanvas"/>
</Grid>
Code
private void MyButton_Click(object sender, RoutedEventArgs e)
{
Geometry geom = this.Resources["Star"] as Geometry;
Geometry flipped = geom.Clone();
var bounds = geom.Bounds;
double halfY = (bounds.Bottom - bounds.Top) / 2.0;
flipped.Transform = new ScaleTransform(1, -1, 0, halfY );
PathGeometry pg = PathGeometry.CreateFromGeometry(flipped);
var path = new System.Windows.Shapes.Path {Data=pg, Fill= System.Windows.Media.Brushes.Red };
this.myCanvas.Children.Add(path);
}
Just set the PathGeometry's Transform property:
var myPathGeometry = new PathGeometry();
myPathGeometry.Figures.Add(myPathFigure);
myPathGeometry.Transform = new ScaleTransform(1, -1);
Note that you may also need to set the ScaleTransform's CenterY property for a correct vertical alignment.
Both #Andy and #Clemens gave right answers. The reason why I didn't get the expected shape is because I didn't notice that the shape is outside the screen region. However, I used Andy's solution because I need to keep the original shape. Also, he notified me about creating new bounds. The only thing I changed in his answer is the value of the new bounds because with the one that he used, the shape was still outside the screen region.
double newY = (bounds.Bottom - bounds.Top);

Move object in ViewportControl WP8

I'm using the ViewportControl to scroll around and zoom in and out of my Map. In this map I've got a green ellipse which I wish to move around. Previously I used a ScrollViewer where I set the manipulationMode of the ScrollViewer to control, and thus making it capable of moving my ellipse around. However I can't find a similar way for the ViewportControl. So is there a way to move my ellipse around?
The code I've got so far is:
The xaml part where I have my ViewportControl around my map
<ViewportControl x:Name="ViewPortTestTest" Bounds="0,0,1271,1381.5" Height="480" Width="800" Canvas.ZIndex="1" Grid.Row="1">
<ViewportControl.RenderTransform>
<CompositeTransform x:Name="myTransformTest"/>
</ViewportControl.RenderTransform>
<View:Map x:Name="ZoomableContent" >
<View:Map.RenderTransform>
<CompositeTransform x:Name="myTransform" />
<!-- ScaleX="{Binding Map.imageScale}" ScaleY="{Binding Map.imageScale}"/>-->
</View:Map.RenderTransform>
</View:Map>
</ViewportControl>
It is in the map where I add the ellipse. The viewModel where I manipulate my ellipse
public void ManStart(ManipulationStartedEventArgs e)
{
e.Handled = true;
ViewportControl VP = FindParentOfType<ViewportControl>(ChampViewModelSel);
}
}
public void ManDelta(ManipulationDeltaEventArgs e)
{
e.Handled = true;
Point fingerPosition = e.DeltaManipulation.Translation;
Temp.x = fingerPosition.X;
Temp.y = fingerPosition.Y;
}
}
Where Temp.x and Temp.y is the new position of the ellipse.
I think you could try to use TouchPanel from XNA and Touch.FrameReported event for this purpose. Probably Map and VieportControl handle the manipulation event so it won't fire with your code.
In my proposal Touch_FrameReported will be fired every time you touch the screen, so we have to check only for situations we need - here comes IsGestureAvailable and TouchAction. Simple code can look like this:
public MainPage()
{
InitializeComponent();
TouchPanel.EnabledGestures = GestureType.FreeDrag;
Touch.FrameReported += Touch_FrameReported;
}
private void Touch_FrameReported(object sender, TouchFrameEventArgs e)
{
if (TouchPanel.IsGestureAvailable) // check only dragging
{
// get point relative to Viewport
TouchPoint mainTouch = e.GetPrimaryTouchPoint(ViewPortTestTest);
// check if drag has completed (key up)
if (mainTouch.Action == TouchAction.Up)
{
Temp.x = mainTouch.Position.X;
Temp.y = mainTouch.Position.Y;
}
}
}
You can read more about Gestures here at MSDN and on this blog.
You can also check other GestureTypes and for example check if TouchAction.Down and user clicked on your Ellipse.
These methods give you many possibilities to read TouchPoints and their Actions so maybe it will help.
It took me a while to find a way how to disable Vieport. I've found a little 'hack' for that, it will be easier if your VieportControl was only horizontal or vertical (there is a lock property, but doesn't work for both). Here is this little 'hack' which should disable both horizontal and vertical scrolling:
Rect originalBounds = new Rect();
private void Touch_FrameReported(object sender, TouchFrameEventArgs e)
{
TouchPoint myTouchPoint = e.GetPrimaryTouchPoint(ViewPortTestTest);
// disable scrolling
if (myTouchPoint.Action == TouchAction.Down) // here probably some other statement like if user hit ellipse
{
originalBounds = ViewPortTestTest.Bounds;
ViewPortTestTest.Bounds = ViewPortTestTest.Viewport; // set current view as Bounds
}
// more logic
// enable once again
if (myTouchPoint.Action == TouchAction.Up)
ViewPortTestTest.Bounds = originalBounds;
}
When I want to disable scrolling - I set bounds of the VieportControl to the current view (and I remember original ones). When I want to enable scrolling - I bring back original bounds.
As I've tested it - it is working - of course it needs a little more logic in locking if statement, so that it won't lock every time you touch the screen.

How to Move Rectangle next to Resized parent Rectangle in WPF using C#?

I am new to WPF. I am Adding Rectangle one after other on Canvas on Button Click. When i set the Height of Specific Rectangle from TextBox. It overlaps on Child Rectangle.
Eg:. When there are 3 Rectangles with Height=100,Width=200 & when i set Height of Second Rectangle to 150. then the Child Rectangle must appear after the Second Rectangle & must not Overlap on Third Rectangle. Is it Possible?
static int val=0;
List<UIElement> itemstoremove = new List<UIElement>();
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
int heigt = 0;
int wegt = 0;
if (!string.IsNullOrEmpty(txtHeight.Text) && !string.IsNullOrEmpty(txtWidth.Text))
{
heigt = int.Parse(txtHeight.Text);
wegt = int.Parse(txtWidth.Text);
}
rect = new Rectangle();
rect.Stroke = Brushes.Red;
rect.StrokeThickness = 2;
rect.Height = heigt;
rect.Width = wegt;
Canvas.SetLeft(rect, 10);
Canvas.SetTop(rect, (rect.Height) * val);
rect.Tag = val;
canvasboard.Children.Add(rect);
val = val + 1;
//canvasboard is Canvas object
foreach (UIElement ui in canvasboard.Children)
{
if (ui.GetType() == typeof(Rectangle))
{
itemstoremove.Add(ui);
}
}
}
For Modifying the Height & Width:
private void BtnModify_Click(object sender, RoutedEventArgs e)
{
int heigt = 0;
int wegt = 0;
if (!string.IsNullOrEmpty(txtHeight.Text) && !string.IsNullOrEmpty(txtWidth.Text))
{
heigt = int.Parse(txtHeight.Text);
wegt = int.Parse(txtWidth.Text);
}
Rectangle rectToRemove;
foreach (UIElement ui in itemstoremove)
{
if (ui.GetType() == typeof(Rectangle) && ((Rectangle)ui).Tag.ToString() == txtModifyRect.Text)
{
rectToRemove = ui as Rectangle;
//itemstoremove.Remove(rectToRemove);
rectToRemove.Height = heigt;
rectToRemove.Width = wegt;
//canvasboard.Children.Remove(rectToRemove);
break;
}
}
}
This works fine. Just i want to Prevent Overlapping of Rectangle on one another and must appear one after the other with Auto Adjusting.
Help Appreciated!
In order to get the effect that you want you must change the Canvas to a WrapPanel.
That way when you add the children to the canvas you don't add the location, it arranges the objects on its own.
Basically you want this
<ScrollViewer VerticalScrollBarVisibility="Auto" >
<WrapPanel Name="objList" > </WrapPanel>
</ScrollViewer>
The ScrollViewer will let you scroll if you have more objects than the window can contain.
Does it need to be specifically Canvas..? Even in pure WPF/Desktop there are some layout components that can do exactly this for you, compeltely automatically.
The behaviour of "stacking" the components one after another is exactly what StackPanel class/component/control does:
<StackPanel x:Name="stacker" Orientation="Vertical">
<Rectangle Width="30" Height="100" Background="Green" />
<Rectangle Width="10" Height="50" Background="Blue" />
<Rectangle Width="20" Height="80" Background="Red" Margin="5,10" />
</StackPanel>
Note how the rectangles are positioned one under another according to their different Heights. Note that I didn't have to specify Orientation=Vertical, because the StackPanel's default behaviour is to position them like that. You can switch it to Horizontally if you like, and then it will stack them according to their Widths. You can also fine-tune and add some extra spacing with i.e. Margin as in the third rectange.
If you need to do it by code, then StackPanel named stacker is perfectly suited for it: you can add new elements to it dynamically and it will lay out the added ones just like it does with children defined in XAML: one under another.
With StackPanel you get even more! The StackPanel observes its children. If at some time after placing the children you change the heights/widths of the children, then StackPanel will instantly adjust the positions so none of them overlap (if you make the children smaller, it will squeeze them, if you make children large - it will expand etc).
If you really-really want to use Canvas, then at the moment of adding-a-new-item, you will have to loop over the all existing-items stored in canvas, sum up their heights, and set the Y position of the new item to exactly that sum. That way, it will appear exactly under the last element. That's the most obvious way of doing that. However, it will not account for margins, spacings, and a few other fine details that may happen to be on your Canvas.
A better but less obvious way is to not sum up the heights again and again, but instead to position the new item according to the bottommost existing item. After all, all old items are positioned properly, right? But, what does bottommost mean? Sometimes, a tall item positioned near the top may span further downwards than a not-so-tall item sitting at the center. Therefore, the bottommost does not mean the item with maximum Y position but means the item that has a furthest Bottom, so the maximum Y+Height. So:
loop through existing items
find an item whose Size.Height+Position.Bottom is the largest
set the Position.Y of the new item to that value
Now, you'll get roughly the same thing as StackPanel does - but it will be only a one-time effect. If you later modify the heights of some elements, you will have to recalculate all position and adjust all positions.. (*)
EDIT:
(*) I dont remember exactly, but if X/Y/Bottom properties are DependencyProperty, then you may even try to use Bindings for controlling the positions automatically. Tha might be quite fun thing to do on the Canvas. Just try binding the Y of a latter element to a Bottom of the former and it should all magically layout out them selves and even automatically update when moved/resized. Still, please try StackPanel first!
Sounds like you want a StackPanel for your rectangles. You can have the StackPanel nested in your Grid.
Here is my solution:
<ScrollViewer VerticalScrollBarVisibility="Auto" Width="250" Height="500">
<WrapPanel Name="wrapboard" Orientation="Vertical" ></WrapPanel>
</ScrollViewer>
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
int heigt = 0;
int wegt = 0;
if (!string.IsNullOrEmpty(txtHeight.Text) && !string.IsNullOrEmpty(txtWidth.Text))
{
heigt = int.Parse(txtHeight.Text);
wegt = int.Parse(txtWidth.Text);
}
rect = new Rectangle();
rect.Stroke = Brushes.Red;
rect.StrokeThickness = 2;
rect.Height = heigt;
rect.Width = wegt;
rect.Tag = val;
wrapboard.Children.Add(rect);
val = val + 1;
//wrapboard is WrapPanel object
foreach (UIElement ui in wrapboard.Children)
{
if (ui.GetType() == typeof(Rectangle))
{
itemstoremove.Add(ui);
}
}
}

Non-smooth DoubleAnimation

I am using DoubleAnimation for zooming and panning in and out of map. My map is an image with huge resolution (15,000 x 8,438). The problem is that on first time the zoom animation is very faltering and not smooth, at second time it`s getting better and so on. How can I make my animation smoother or make some cashing of the image or animation before performing it, or maybe using other form of animation?
My Code:
namespace AnimationTest
{
public partial class MainWindow : Window
{
ScaleTransform transP;
TranslateTransform trans2P;
DoubleAnimation animP;
DoubleAnimation animYP;
DoubleAnimation animXP;
TransformGroup myTransformGroupP;
public MainWindow()
{
InitializeComponent();
transP = new ScaleTransform();
trans2P = new TranslateTransform();
myTransformGroupP = new TransformGroup();
myTransformGroupP.Children.Add(transP);
myTransformGroupP.Children.Add(trans2P);
animP = new DoubleAnimation(1, 20, TimeSpan.FromMilliseconds(3000));
animXP = new DoubleAnimation(0, -14000, TimeSpan.FromMilliseconds(3000));
animYP = new DoubleAnimation(0, -4000, TimeSpan.FromMilliseconds(3000));
}
private void button1_Click(object sender, RoutedEventArgs e)
{
image1.RenderTransform = myTransformGroupP;
transP.BeginAnimation(ScaleTransform.ScaleXProperty, animP);
transP.BeginAnimation(ScaleTransform.ScaleYProperty, animP);
trans2P.BeginAnimation(TranslateTransform.XProperty, animXP);
trans2P.BeginAnimation(TranslateTransform.YProperty, animYP);
}
}
}
I have not tried your animation approach, i tried to implement my own logic to to this.
First i am inspired by zooming animation used by Picasa. So i tried to implement similar type of animation and this works fine for me on my core2duo processor with image size of 10000x5000 without any lag.
This approach consumed a lot of memory, but when i compared my memory usage with Picasa ImageViewer it was almost same. This approach may increase the loading time of your application but this can be handled and not a problem here.
Here is the Code for Main Window Grid that i have Used.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Height="30" Width="100" Content="Zoom" Click="ButtonZoom_OnClick" />
<Image RenderOptions.BitmapScalingMode="HighQuality" Stretch="Uniform" Width="100" Height="100" Grid.Row="1"
Margin="30" VerticalAlignment="Center" HorizontalAlignment="Center" Source="mad1.jpg" Name="ImageMain"
x:FieldModifier="private" />
</Grid>
Button Click event Code
private void ButtonZoom_OnClick(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
var i = 0;
while (i++ < 100)
{
var i1 = i;
//var i1 = (-0.00092)*(i*i) + (0.092)*i + 0.2;
Dispatcher.Invoke(new Action(() =>
{
if (i1 < 10 || i1 > 90)
{
ImageMain.Height += 0.5;
ImageMain.Width += 0.5;
}
else if (i1 < 30 || i1 > 70)
{
ImageMain.Height += 1;
ImageMain.Width += 1;
}
else
{
ImageMain.Height += 3;
ImageMain.Width += 3;
}
}));
Thread.Sleep(30);
}
});
}
The commented line in this code is a quadratic equation for a smooth animation for acceleration and acceleration of animation. the roots are calculated for starting zooming by 0.2 and half at 2.5 and stops at 0.2 with in range of [0-100]. if you want to create your fully customized animation you may use WolframAlpha to check your animation graph. but the simple approach is to use simple control statements to control your animation.
This code is only for zooming your image, your approach will be similar for zoom out.
Have you looked into Microsoft's DeepZoom technology (this is what they use for Bing Maps)? http://msdn.microsoft.com/en-us/library/cc645050(v=vs.95).aspx#deep_zoom_examples
Since you have not shown any XAML I'll try from the most basic - try to reduce bitmap scaling mode with RenderOptions.BitmapScalingMode="LowQuality" on your image element like this:
<Image x:Name="image1"
Source="huge-image.jpg"
Stretch="Uniform"
RenderOptions.BitmapScalingMode="LowQuality" />
Note, that this is only actual if you targeting .NET 3.0-3.5 since starting from .NET 4.0 the "LowQuality" setting is already set as default, so you have no need to specify it explicitly. But if your zoom-in animation still faltering you could try to change this default scaling from LowQuality to even more lower NearestNeighbor which, according to documentation:
...provides performance benefits over LowQuality mode when the software rasterizer is used. This mode is often used to magnify a bitmap.
Also since you are about to show large image with some loss of quality it may be better to specify UseLayoutRounding="True" on your image or it parent element to improve image quality.
You want to use a cached composition. Render your map but assign a BitmapCache to the CacheMode property and set the RenderAtScale to a value larger than 1. If you zoom into your map 5x you should use the RenderAtScale with this value as it caches the image for this type of zoom.
This may result in a much higher memory consumption but may smooth the scrolling.
Further more Nolonar may be right. You may need to create mipmaps for the image and provide tile rendering to partially load seen tiles as your image is quite large.

Categories

Resources