I am trying to figure out how to have a new image fade in and simultaneously have the old image fade out.
I initially thought of using two double animations, one for fade in and another for fade out that on completion trigger each other, however that doesn't work for me since I need one image to fade in while the other is fading out and I don't know how to go about doing it.
What I attempted to do:
XAML:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Animation"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid x:Name="gridTest">
<StackPanel x:Name="stkHeaderBar" Grid.Row="0"
Orientation="Horizontal" FlowDirection="RightToLeft">
<Button x:Name="btnChangeImg" Content="Change Image"
Click="btnChangeImage_Click"/>
<Image x:Name="img"></Image>
</StackPanel>
</Grid>
Code behind:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace Animation
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnChangeImage_Click(object sender, RoutedEventArgs e)
{
DoubleAnimation fadeIn = new DoubleAnimation
{
From = 0,
To = 1,
Duration = TimeSpan.FromSeconds(2)
};
DoubleAnimation fadeOut = new DoubleAnimation
{
From = 1,
Duration = TimeSpan.FromSeconds(2),
};
fadeOut.Completed += (o, e) =>
{
img.Source = new BitmapImage(new Uri(#"C:\images.jpg"));
img.BeginAnimation(OpacityProperty, fadeIn);
};
fadeIn.Completed += (o, e) =>
{
img.Source = new BitmapImage(new Uri(#"C:\images.jpg"));
img.BeginAnimation(OpacityProperty, fadeOut);
};
img.Source = new BitmapImage(new Uri(#"C:\images1.jpg"));
img.BeginAnimation(OpacityProperty, fadeIn);
}
}
}
You're over-complicating it, just put both images in a Grid so that they overlap:
<Grid>
<Image x:Name="img1" Stretch="UniformToFill"></Image>
<Image x:Name="img2" Stretch="UniformToFill"></Image>
</Grid>
The first image can have its opacity stay at 1.0 the whole time, and then you just animate the second image to fade in over top of it:
private void btnChangeImage_Click(object sender, RoutedEventArgs e)
{
DoubleAnimation fadeIn = new DoubleAnimation
{
From = 0,
To = 1,
Duration = TimeSpan.FromSeconds(2)
};
img1.Source = new BitmapImage(new Uri(#"C:\image1.jpg"));
img2.Source = new BitmapImage(new Uri(#"C:\image2.jpg"));
img2.BeginAnimation(OpacityProperty, fadeIn);
}
If you want to be thorough then you can use your fadeIn.Completed handler to remove img1 when the animation is complete, but I normally wouldn't worry about that unless it was a resource-critical application.
Related
I am unable to run the animation. I searched the web and on the website, I did hours of changes but the storyboard does not start. I will not use BeginAnimation. I hope that my problem can also serve to someone else to understand the Storyboard.
enter code here
using HelixToolkit.Wpf;
using System;
using System.Windows;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
namespace test_storyboard_02
{
public partial class MainWindow : Window
{
public Storyboard myStoryboard = new Storyboard();
Model3DGroup cubelet;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
LoadObj();
view1.ZoomExtents();
FrameworkContentElement element = new FrameworkContentElement();
NameScope.SetNameScope(element, new NameScope());
AxisAngleRotation3D rotation = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 180);
RotateTransform3D myRotateTransform3D = new RotateTransform3D(rotation, new Point3D(0, 0, 0));
cubelet.Transform = myRotateTransform3D;
element.RegisterName("rotation", rotation);
DoubleAnimation animation = new DoubleAnimation();
animation.By = 5;
animation.Duration = TimeSpan.FromSeconds(0);
Storyboard.SetTarget(animation, rotation);
Storyboard.SetTargetProperty(animation, new PropertyPath("Angle"));
myStoryboard.Children.Add(animation);
myStoryboard.Duration = TimeSpan.FromSeconds(4);
myStoryboard.Begin(element, HandoffBehavior.Compose);
}
private void LoadObj()
{
view1.Children.Clear();
//cubelets = new Model3DGroup[1, 1, 1];
cubelet = new Model3DGroup();
ModelImporter importer = new ModelImporter();
Model3D ModelCube = importer.Load(#"e:\x.obj");
cubelet.Children.Add(ModelCube);
view1.Children.Add(new ModelVisual3D { Content = cubelet });
}
}
}
There are problems in these lines of code :
FrameworkContentElement element = new FrameworkContentElement();
NameScope.SetNameScope(element, new NameScope());
...
element.RegisterName("rotation", rotation);
Changes :
NameScope scope = new NameScope();
FrameworkContentElement element = new FrameworkContentElement();
NameScope.SetNameScope(element, scope);
...
element.RegisterName("rotation", scope);
See this solves your problem.
This is the xaml file that I used.
<Window x:Class="test_storyboard_02.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:test_storyboard_02"
xmlns:HelixToolkit="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<Canvas Name="LayoutRoot" HorizontalAlignment="Left" Height="319" VerticalAlignment="Top" Width="517">
<HelixToolkit:HelixViewport3D x:Name="view1" Background="Gray" Height="278" Canvas.Left="25" Canvas.Top="22" Width="463">
<HelixToolkit:DefaultLights/>
<!--<local:RubikCube x:Name="cube1" /> -->
</HelixToolkit:HelixViewport3D>
</Canvas>
</Grid>
SOLVED Finally the program WORKS, thanks to #AnjumSKhan. I hope that the code that I enclose will also serve to someone else. Thanks also to stackoverflow.
MainWindow.xaml
<Window x:Class="test_storyboard_02.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:test_storyboard_02"
xmlns:HelixToolkit="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
</Window.Resources>
<Grid>
<Canvas Name="LayoutRoot" HorizontalAlignment="Left" Height="319" VerticalAlignment="Top" Width="517">
<HelixToolkit:HelixViewport3D x:Name="view1" Background="Gray" Height="278" Canvas.Left="25" Canvas.Top="22" Width="463">
<HelixToolkit:DefaultLights/>
<!--<local:RubikCube x:Name="cube1" /> -->
</HelixToolkit:HelixViewport3D>
</Canvas>
</Grid>
MainWindow.xaml.cs
using HelixToolkit.Wpf;
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;
namespace test_storyboard_02
{
public partial class MainWindow : Window
{
public Storyboard myStoryboard = new Storyboard();
public Model3DGroup MainModel3Dgroup = new Model3DGroup();
Model3DGroup modelFloor;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
LoadObj();
view1.ZoomExtents();
NameScope scope = new NameScope();
FrameworkContentElement element = new FrameworkContentElement();
NameScope.SetNameScope(element, scope);
// Create a box that will be the target
// of the animation.
// Material material = HelixToolkit.Wpf.MaterialHelper.CreateMaterial(Colors.DarkBlue);
// MeshBuilder meshBuilder = new MeshBuilder();
// meshBuilder.AddBox(new Point3D(0, 0, 0), 200, 200, 200);
// GeometryModel3D modelFloor = new GeometryModel3D(meshBuilder.ToMesh(), material);
// modelFloor.SetName("floor");
// MainModel3Dgroup.Children.Add(modelFloor);
var lights = new DefaultLights();
view1.Children.Add(lights);
ModelVisual3D model_visual = new ModelVisual3D();
model_visual.Content = modelFloor;
view1.Children.Add(model_visual);
view1.ZoomExtents();
AxisAngleRotation3D rotation = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0);
RotateTransform3D myRotateTransform3D = new RotateTransform3D(rotation, new Point3D(0, 0, 0));
modelFloor.Transform = myRotateTransform3D;
element.RegisterName("rotation", rotation);
// Create two DoubleAnimations and set their properties.
DoubleAnimation animation = new DoubleAnimation();
animation.From = 0;
animation.To = 200;
animation.Duration = TimeSpan.FromSeconds(2);
Storyboard.SetTargetProperty(animation, new PropertyPath("Angle"));
Storyboard.SetTargetName(animation, "rotation");
myStoryboard.Children.Add(animation);
myStoryboard.Duration = TimeSpan.FromSeconds(2);
// Make the Storyboard a resource.
this.Resources.Add("unique_id1", myStoryboard);
myStoryboard.Begin(element, HandoffBehavior.Compose);
}
private void LoadObj()
{
view1.Children.Clear();
modelFloor = new Model3DGroup();
ModelImporter importer = new ModelImporter();
Model3D ModelCube = importer.Load(#"e:\x.obj");
modelFloor.Children.Add(ModelCube);
view1.Children.Add(new ModelVisual3D { Content = modelFloor });
}
}
}
I'm trying to create a Paint Application in WPF using Canvas. I want to increase the thickness of my shape while drawing so I try increasing StrokeThickness.
This is how I want it to be:
And this is what I get:
As you can see the outline only extends inside the boundary. How can I make it extends on both side?
Here is my code:
In MouseDown Event:
Rectangle rect = new Rectangle();
rect.Stroke = _color;
rect.StrokeThickness = _size;
Canvas.SetLeft(rect, _startPoint.X);
Canvas.SetTop(rect, _startPoint.Y);
cv_PaintBoard.Children.Add(rect);
isDrawing = true;
In MouseMove Event:
if (isDrawing == true && e.LeftButton == MouseButtonState.Pressed)
{
Canvas canvas = (Canvas)sender;
Rectangle rect = canvas.Children.OfType<Rectangle>().LastOrDefault();
if (rect != null)
{
Point endPoint = e.GetPosition((IInputElement)sender);
Point startPoint = new Point(
Math.Min(endPoint.X, _startPoint.X),
Math.Min(endPoint.Y, _startPoint.Y)
);
rect.Width = Math.Max(endPoint.X, _startPoint.X) - startPoint.X;
rect.Height = Math.Max(endPoint.Y, _startPoint.Y) - startPoint.Y;
Canvas.SetLeft(rect, startPoint.X);
Canvas.SetTop(rect, startPoint.Y);
}
}
You need to update width/height and left/top too for your specific needs.
The below example demonstrates your needs. If it is ok with you, let me know.
MainWindow.xaml
<Window x:Class="WpfDrawing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="447.368" Width="606.579">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="13*"/>
<RowDefinition Height="7*"/>
</Grid.RowDefinitions>
<Canvas Grid.RowSpan="1">
<Rectangle x:Name="Rect1" Fill="Transparent" HorizontalAlignment="Left" Height="70" Canvas.Left="248" Canvas.Top="104" Stroke="Black" Width="94" Opacity="0.5" />
<Rectangle x:Name="Rect2" Fill="#FF52E03C" HorizontalAlignment="Left" Height="70" Canvas.Left="248" Canvas.Top="104" Stroke="Black" Width="94" Opacity="0.5" />
</Canvas>
<TextBox x:Name="tbThickness" HorizontalAlignment="Left" Height="23" Margin="84,27,0,0" Grid.Row="1" TextWrapping="Wrap" Text="5" VerticalAlignment="Top" Width="120"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="237,28,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfDrawing
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
double designWidth;
double designHeight;
double designLeft, designTop;
public MainWindow()
{
InitializeComponent();
designWidth = Rect2.Width;
designHeight = Rect2.Height;
designLeft = Canvas.GetLeft(Rect2);
designTop = Canvas.GetTop(Rect2);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Rect2.StrokeThickness = double.Parse(tbThickness.Text);
Canvas.SetLeft(Rect2,designLeft - (Rect2.StrokeThickness / 2));
Canvas.SetTop(Rect2, designTop - (Rect2.StrokeThickness / 2));
Rect2.Width = designWidth + Rect2.StrokeThickness;
Rect2.Height = designHeight + Rect2.StrokeThickness;
}
}
}
There is no need to do any calculations to correct the size of a rectangle by half of its stroke thickness.
Just use Path controls with RectangleGeometries instead of Rectangle controls:
private Brush stroke = Brushes.Red;
private double strokeThickness = 10;
private Path currentPath;
private Point startPoint;
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
var canvas = (Canvas)sender;
if (canvas.CaptureMouse())
{
startPoint = e.GetPosition(canvas);
currentPath = new Path
{
Data = new RectangleGeometry(new Rect(startPoint, startPoint)),
Stroke = stroke,
StrokeThickness = strokeThickness
};
canvas.Children.Add(currentPath);
}
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (currentPath != null)
{
((RectangleGeometry)currentPath.Data).Rect
= new Rect(startPoint, e.GetPosition((UIElement)sender));
}
}
private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
{
((UIElement)sender).ReleaseMouseCapture();
currentPath = null;
}
This is my first time posting, so be easy on me...
There are definitely posts that show how to connect lines to blocks, but this one is slightly different. The grids are placed on the canvas dynamically. I want a line to connect the grid from where the button is pressed to the new grid that's placed on the canvas. However the below code doesn't work. I've struggled with this one for a very long time.
Solely by accident I discovered a Messagebox thrown in the button event handler will allow it to work. I figured this was from threads running at different times, but after messing with task.delay/thread.sleep/async/await I can't find the solution.
I'm using custom classes because this is a stripped down version of the larger program, I wanted to have similar functionality in my example to reflect possible errors but leave out the unnecessary pieces.
I'm using this as a last resort, thank you in advance for any help you can provide. First the CS code
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Grid1 g = myCanvas.CreateGrid();
ContentControl1 cc = myCanvas.CreateCC();
Button1 b = myCanvas.CreateButton1();
Grid1.SetColumn(cc, 0);
Grid1.SetRow(cc, 0);
Grid1.SetColumn(b, 1);
Grid1.SetRow(b, 1);
g.Children.Add(cc);
g.Children.Add(b);
Canvas1.SetLeft(g, 500);
Canvas1.SetTop(g, 5);
myCanvas.Children.Add(g);
}
}
public class Button1 : Button
{
protected override void OnClick()
{
Grid1 old_g = (Grid1)VisualTreeHelper.GetParent(this as DependencyObject);
Canvas1 cnv = (Canvas1)VisualTreeHelper.GetParent(old_g as DependencyObject);
Grid1 g = cnv.CreateGrid();
ContentControl1 cc = cnv.CreateCC();
Button1 b = cnv.CreateButton1();
Grid1.SetColumn(cc, 0);
Grid1.SetRow(cc, 0);
Grid1.SetColumn(b, 1);
Grid1.SetRow(b, 1);
g.Children.Add(cc);
g.Children.Add(b);
Canvas1.SetLeft(g, 500);
Canvas1.SetTop(g, cnv.Children.Count * 120);
cnv.Children.Add(g);
cnv.ConnectGrids(old_g, g);
}
}
public class Canvas1 : Canvas
{
public Grid1 CreateGrid()
{
Grid1 g = new Grid1() { Width = 100, Height = 20, Background = Brushes.White };
g.HorizontalAlignment = HorizontalAlignment.Center;
g.VerticalAlignment = VerticalAlignment.Center;
g.ShowGridLines = false;
ColumnDefinition colDef1 = new ColumnDefinition();
ColumnDefinition colDef2 = new ColumnDefinition() { Width = new GridLength(20) };
g.ColumnDefinitions.Add(colDef1);
g.ColumnDefinitions.Add(colDef2);
RowDefinition rowDef1 = new RowDefinition();
g.RowDefinitions.Add(rowDef1);
return g;
}
public ContentControl1 CreateCC()
{
ContentControl1 cc = new ContentControl1()
{
VerticalContentAlignment = VerticalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Stretch,
BorderBrush = Brushes.BlueViolet,
};
return cc;
}
public Button1 CreateButton1()
{
Button1 b = new Button1()
{
VerticalContentAlignment = VerticalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Stretch,
BorderBrush = Brushes.Red
};
return b;
}
public void ConnectGrids(Grid1 g1, Grid1 g2)
{
Canvas1 cnv = (Canvas1)VisualTreeHelper.GetParent(g1 as DependencyObject);
Transform transform1 = (Transform)g1.TransformToVisual(cnv as Visual);
Transform transform2 = (Transform)g2.TransformToVisual(cnv as Visual);
Point StartPoint1 = transform1.Transform(new Point(g1.Width, g1.Height / 2.0));
Point EndPoint1 = transform2.Transform(new Point(g2.Width, g2.Height / 2.0));
var lineGeometry = new LineGeometry()
{
StartPoint = StartPoint1,
EndPoint = EndPoint1
};
var path = new Path()
{
Data = lineGeometry,
Stroke = new SolidColorBrush(Colors.Black),
};
cnv.Children.Add(path);
}
}
public class ContentControl1 : ContentControl
{
}
public class Grid1 : Grid
{
}
}
then the xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local ="clr-namespace:WpfApplication1"
Title="MainWindow" Height="1000" Width="1000">
<DockPanel>
<Button DockPanel.Dock="Top" Width="100" Height="20" Content="Start" Click="Button_Click"/>
<ScrollViewer HorizontalScrollBarVisibility="Visible" Width="901" x:Name="_scrollViewer" Margin="0,5,0,25">
<local:Canvas1 Background="Gray" Height="1000" Width="1000" x:Name="myCanvas"/>
</ScrollViewer>
</DockPanel>
</Window>
How can I successfully connect the grid with the path?
I love that I solved this/hate that I have to answer my own dumb question...
Sooo simple. UpdateLayout(). The objects don't change location until it has been rendered, so the line was going to its old location.
I'm very new to WPF and I'm trying to create a picture cropping application. The program has a rectangle that can be dragged to crop the picture (like in Microsoft Paint). The way its set up is that both image and canvas which holds the rectangle are children of grid container. Grid has events for mousemove, mouseleftbuttondown and up to calculate the cropped image. My program is cropping the image fine but the problem occurs when the window is maximized which throws an exception error below:
An unhandled exception of type 'System.ArgumentException' occurred in PresentationCore.dll
Additional information: Value does not fall within the expected range.
This exception is caused by a line in my Go_Click method:
BitmapSource bs = new CroppedBitmap(LoadedImage.Source as BitmapSource, rcFrom);
I stepped into the program and found out that at first it was caused by setting the width and height of Image (LoadedImage.Width and LoadedImage.Height) to auto or NAN. This makes the value NAN which mess up the calculation and throws of the exception. I changed it to ActualWidth (LoadedImage.ActualWidth and ActualHeight) and I only got it work partially since it still throws an exception whenever I crop the image while window is maximized.
Below is source code for my project.
XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<GroupBox Header="Loaded Image:" Grid.Column="0">
<Grid x:Name="LoadedImage" MouseLeftButtonDown="image1_MouseLeftButtonDown" MouseMove="image1_MouseMove" MouseLeftButtonUp="image1_MouseLeftButtonUp" ShowGridLines="True">
<Image x:Name="loaded" Margin="10" Stretch="Fill"/>
<Canvas x:Name="BackPanel" Margin="10">
<Rectangle x:Name="selectionRectangle" Stroke="LightBlue" Fill="#220000FF" Visibility="Collapsed"/>
</Canvas>
</Grid>
</GroupBox>
<StackPanel Grid.Column="1" x:Name="MyPanel" HorizontalAlignment="Left" Orientation="Vertical" Height="340" Width="262">
<GroupBox x:Name="Box2" Header="Preview Box:">
<Image x:Name="PreviewImage" MaxWidth="240" MaxHeight="240" Stretch="Fill" MinWidth="240" MinHeight="240"/>
</GroupBox>
<StackPanel Height="61" Orientation="Horizontal" HorizontalAlignment="Center">
<Button MinWidth="93" Height="32" Click="Import_Click">Import</Button>
<Button Name="Crop" MinWidth="93" Height="32" Margin="10,0,0,0" Click="Go_Click">Crop</Button>
</StackPanel>
</StackPanel>
</Grid>
And this is the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.IO;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool isDragging = false;
private Point anchorPoint = new Point();
public MainWindow()
{
InitializeComponent();
}
//Import Button event handler which opens an open file dialog
//to choose an image file. If the return value from ShowDialog
//is true (user choose image and click's OK)it will store the image to file,
//else it would exit open file dialog.
private void Go_Click(object sender, RoutedEventArgs e)
{
if (loaded.Source != null)
{
Rect rect1 = new Rect(Canvas.GetLeft(selectionRectangle), Canvas.GetTop(selectionRectangle), selectionRectangle.Width, selectionRectangle.Height);
Int32Rect rcFrom = new Int32Rect();
rcFrom.X = (int)((rect1.X) * (loaded.Source.Width) / `enter code here`(loaded.ActualWidth));
rcFrom.Y = (int)((rect1.Y) * (loaded.Source.Height) / (loaded.ActualHeight));
rcFrom.Width = (int)((rect1.Width) * (loaded.Source.Width) / (loaded.ActualWidth));
rcFrom.Height = (int)((rect1.Height) * (loaded.Source.Height) / (loaded.ActualHeight));
BitmapSource bs = new CroppedBitmap(loaded.Source as BitmapSource, rcFrom);
PreviewImage.Source = bs;
}
}
private void image1_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
double x = e.GetPosition(BackPanel).X;
double y = e.GetPosition(BackPanel).Y;
selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);
if (selectionRectangle.Visibility != Visibility.Visible)
selectionRectangle.Visibility = Visibility.Visible;
}
}
private void image1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (isDragging == false)
{
anchorPoint.X = e.GetPosition(BackPanel).X;
anchorPoint.Y = e.GetPosition(BackPanel).Y;
isDragging = true;
}
}
private void image1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
isDragging = false;
if (selectionRectangle.Width > 0)
{
Crop.Visibility = System.Windows.Visibility.Visible;
Crop.IsEnabled = true;
}
if (selectionRectangle.Visibility != Visibility.Visible)
selectionRectangle.Visibility = Visibility.Visible;
}
}
private void RestRect()
{
selectionRectangle.Visibility = Visibility.Collapsed;
isDragging = false;
}
private void Import_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.FileName = "Picture";
dlg.Filter = "All Pictures (*.jpeg)|*.jpg";
if (dlg.ShowDialog() == true)
{
loaded.Source = new BitmapImage(new Uri(dlg.FileName));
}
}
}
}
Like I said i'm new to programming WPF and I apologize if the way i have my code written may not be the best. But any advise would help on how I should handle this issue.
Thanks in advance.
You need to get LoadedImage position relative to GridLoadedImage, and use it when creating crop:
private void Go_Click(object sender, RoutedEventArgs e)
{
if (LoadedImage.Source != null)
{
var imagePosition = loaded.TransformToAncestor(LoadedImage).Transform(new Point(0, 0));
Rect rect1 = new Rect(Math.Max(Canvas.GetLeft(selectionRectangle) - imagePosition.X, 0), Canvas.GetTop(selectionRectangle), selectionRectangle.Width, selectionRectangle.Height);
Int32Rect rcFrom = new Int32Rect();
rcFrom.X = (int)((rect1.X) * (LoadedImage.Source.Width) / (LoadedImage.ActualWidth));
rcFrom.Y = (int)((rect1.Y) * (LoadedImage.Source.Height) / (LoadedImage.ActualHeight));
rcFrom.Width = (int)((rect1.Width) * (LoadedImage.Source.Width) / (LoadedImage.ActualWidth));
rcFrom.Height = (int)((rect1.Height) * (LoadedImage.Source.Height) / (LoadedImage.ActualHeight));
BitmapSource bs = new CroppedBitmap(LoadedImage.Source as BitmapSource, rcFrom);
PreviewImage.Source = bs;
}
}
I have a problem when trying to create writeable bitmap form Silverlight toolkit Graph.
When using textBlock, everything is fine, but after trying to use Chart, generated bitmap is empty :( .
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
Chart chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
This code creates chart with sinusoid in it. Then Im trying to create bitmap from it.
LayoutRoot.Children.Add(chart_); // I tried to add chart_ to visual tree, It doesn't help
//creates bitmap
ScaleTransform t = new ScaleTransform() { ScaleX = 1.0, ScaleY = 1.0 };
//bitmap = new WriteableBitmap(chart_, t); Tried it also with this way
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, t);
texture = new Texture2D(GraphicsDeviceManager.Current.GraphicsDevice, bitmap.PixelWidth, bitmap.PixelHeight, false, SurfaceFormat.Color);
bitmap.CopyTo(texture);
All this code creates Empty Bitmap.But when I use TextBlock or some primitives like Ellipse, everything works. Im sure, that code generating chart is fine, cause chart is generated fine in Silverlight control.
EDIT:
I tried to create bitmap this way, but it dont help.
chart_.InvalidateMeasure();
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, null);
bitmap.Invalidate();
EDIT 2:
I don't want graph to be in visual tree. I just need generate image of it an than use it in XNA part of my application.
Just a couple of small changes, but I suspect the big change is manually adding a reference to System.Windows.Controls after using NuGet to add the Charting package.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Media.Imaging;
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
Chart chart_;
public MainPage()
{
InitializeComponent();
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
LayoutRoot.Children.Add(chart_); // I tried to add chart_ to visual tree, It doesn't help
}
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
//creates bitmap
WriteableBitmap bitmap;
ScaleTransform t = new ScaleTransform() { ScaleX = 1.0, ScaleY = 1.0 };
//bitmap = new WriteableBitmap(chart_, t); Tried it also with this way
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, t);
//texture = new Texture2D(GraphicsDeviceManager.Current.GraphicsDevice, bitmap.PixelWidth, bitmap.PixelHeight, false, SurfaceFormat.Color);
//bitmap.CopyTo(texture);
image1.Source = bitmap;
}
}
}
And
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
<Image Height="150" HorizontalAlignment="Left" Margin="97,109,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="200" />
</Grid>
</UserControl>
That should get you going...
Don't forget to delete all of the attempted workarounds - I don't think any of them are necessary. The only code I did not use from your example had to do with the textures - I didn't know what to do with that and it wasn't the problem you were having anyways...
David
I believe that by the time you are creating WriteableBitmap object & calling Render method, Chart has not be rendered yet. You can check this by moving these lines of code to some other event like Button_Click etc. Have a look at below codes as it is creating the WriteableBitmap and then passes it to image control as source....
XAML Code.....
<UserControl x:Class="TryBitmap.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Height="25" Width="100" Content="Capture" Click="Button_Click"/>
<Grid x:Name="grdGraphs" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image x:Name="img" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
Code Behind....
public partial class MainPage : UserControl
{
private Chart chart_;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
chart_.SetValue(Grid.ColumnProperty, 0);
grdGraphs.Children.Add(chart_);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var bitmap = new WriteableBitmap((int)(chart_.RenderSize.Width), (int)(chart_.RenderSize.Height));
bitmap.Render(chart_, new MatrixTransform());
bitmap.Invalidate();
img.Source = bitmap;
}
}
I'm afraid this is only going to be half an answer. I can solve your immediate problem but I'm afraid you'll just end up with another one I haven't managed to solve.
Creating the bitmap after a call to Dispatcher.BeginInvoke should ensure that your bitmap isn't completely blank, i.e.:
// do stuff with chart_ ...
Dispatcher.BeginInvoke(() =>
{
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, null);
bitmap.Invalidate();
// At this point, the bitmap shouldn't be blank.
});
However, the results of this likely to be less than satisfactory. I ran your code and I found that the LineSeries was missing from the chart image, although the rest of the chart was there. This remained true even after I set all Durations of the various animations in the ControlTemplate for the LineSeries to 0 and additionally set the following properties on the chart:
chart_.AnimationSequence = AnimationSequence.Simultaneous;
chart_.TransitionDuration = new TimeSpan(0);
I tried wrapping the WriteableBitmap operations in further calls to Dispatcher.BeginInvoke(), and after doing this the data points started to appear more clearly. However, I can't believe that an approach like this is the right solution to the problem.