I just started learning C# and I want to plot a cosine when user presses the radiobutton using WPF GUI interface. I think I am having trouble how to use call objects within different class. Thanks in advance and below is my code:
namespace WpfApplication2
{
using OxyPlot;
using OxyPlot.Annotations;
using OxyPlot.Axes;
using OxyPlot.Series;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click_2(object sender, RoutedEventArgs e)
{
if (radioButton1.IsChecked == true)
{
MessageBox.Show("Plot Cosine");
//I think solution should be something like this
//MainViewModel.MyModel.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.01, "cos(x)"));
}
}
}
public class MainViewModel : Window
{
//Plotting without any user input
public const double Pi = 3.14159265358979323846;
public const int SpeedOfLight = 3 * 10 ^ 8; // m per sec.
//OxyPlot.Wpf.PlotModel plot = new OxyPlot.Wpf.PlotView();
public MainViewModel()
{
MyModel = new PlotModel { Title = "Your Equation", LegendTitle = "Equations" };
MyModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Distance" });
MyModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Height" });
//Determine your range for the plot
//MyModel.Axes.Add(new LinearAxis(AxisPosition.Bottom, -10, 10));
//MyModel.Axes.Add(new LinearAxis(AxisPosition.Left, -5, 5));
MyModel.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.01, "cos(x)"));
MyModel.Series.Add(new FunctionSeries(Math.Sin, -10, 10, 0.01, "sin(x)"));
LineSeries linePoints = new LineSeries() { };
double x, y;
DataPoint XYpoint = new DataPoint();
for (x = -10; x <= 10; x += 0.01)
{
//Make sure not 1/3 since C# will read it as integer divided by integer hence 1/3=0
//Use Math.Pow for powers
//Definately Matlab is easier to plot stuff XD
y = 1.0 / 2.0 * Math.Pow(x, 2) + 1;
XYpoint = new DataPoint(x, y);
linePoints.Points.Add(XYpoint);
}
MyModel.Series.Add(linePoints);
MyModel.InvalidatePlot(true);
}
public PlotModel MyModel { get; private set; }
}
}
Below is XAML code:
<Window x:Name="plot" x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:oxy="http://oxyplot.org/wpf"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="Plots" Height="450.307" Width="955.532" Background="White">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="73*"/>
<RowDefinition Height="11*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="145*"/>
<ColumnDefinition Width="329*"/>
</Grid.ColumnDefinitions>
<oxy:PlotView Title="{Binding Title}" Margin="4,0,0,0" Model="{Binding MyModel}" Grid.Column="1" >
<oxy:PlotView.Series>
<oxy:LineSeries ItemsSource="{Binding Points}"/>
</oxy:PlotView.Series>
</oxy:PlotView>
<Label x:Name="label" HorizontalAlignment="Left" Height="30" Margin="120,185,0,0" VerticalAlignment="Top" Width="142"/>
<RadioButton x:Name="radioButton1" Content="Plot Cosine" Grid.Column="1" HorizontalAlignment="Left" Height="20" Margin="50,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="85" />
<Button x:Name="button1" Content="Clear" HorizontalAlignment="Left" Height="35" Margin="120,7,0,0" Grid.Row="1" VerticalAlignment="Top" Width="142" Click="button_Click_2"/>
</Grid>
</Window>
C#
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 OxyPlot;
using OxyPlot.Series;
using OxyPlot.Axes;
namespace WpfApplication2
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public PlotModel MyModel { get; private set; }
public MainWindow()
{
InitializeComponent();
MyModel = new PlotModel { Title = "Your Equation", LegendTitle = "Equations" };
MyModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Distance" });
MyModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Height" });
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if (radioButton1.IsChecked == true)
{
//MessageBox.Show("Plot Cosine");
graph();
}
}
public double getValue(int x, int y)
{
return (10 * x * x + 11 * x * y * y + 12 * x * y);
}
//setting the values to the function
public FunctionSeries GetFunction()
{
int n = 100;
FunctionSeries serie = new FunctionSeries();
for (int x = 0; x < n; x++)
{
for (int y = 0; y < n; y++)
{
//adding the points based x,y
DataPoint data = new DataPoint(x, getValue(x, y));
//adding the point to the serie
serie.Points.Add(data);
}
}
//returning the serie
return serie;
}
public void graph()
{
MyModel = new PlotModel { Title = "example" };
MyModel.LegendPosition = LegendPosition.RightBottom;
MyModel.LegendPlacement = LegendPlacement.Outside;
MyModel.LegendOrientation = LegendOrientation.Horizontal;
MyModel.Series.Add(GetFunction());
var Yaxis = new OxyPlot.Axes.LinearAxis();
OxyPlot.Axes.LinearAxis XAxis = new OxyPlot.Axes.LinearAxis { Position = OxyPlot.Axes.AxisPosition.Bottom, Minimum = 0, Maximum = 100 };
XAxis.Title = "X";
Yaxis.Title = "10 * x * x + 11 * x*y*y + 12*x*y";
MyModel.Axes.Add(Yaxis);
MyModel.Axes.Add(XAxis);
this.plot.Model = MyModel;
}
}
}
XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:oxy="http://oxyplot.org/wpf"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="73*"/>
<RowDefinition Height="11*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="145*"/>
<ColumnDefinition Width="329*"/>
</Grid.ColumnDefinitions>
<oxy:PlotView Margin="4,0,0,0" Grid.Column="1" Name="plot" >
<!--<oxy:PlotView.Series>
<oxy:LineSeries ItemsSource="{Binding Points}"/>
</oxy:PlotView.Series>-->
</oxy:PlotView>
<Label x:Name="label" HorizontalAlignment="Left" Height="30" Margin="120,185,0,0" VerticalAlignment="Top" Width="142"/>
<RadioButton x:Name="radioButton1" IsChecked="True" Content="Plot Cosine" Grid.Column="1" HorizontalAlignment="Left" Height="20" Margin="50,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="85" />
<Button x:Name="button1" Content="Clear" HorizontalAlignment="Left" Height="35" Margin="120,7,0,0" Grid.Row="1" VerticalAlignment="Top" Width="142" Click="button1_Click"/>
</Grid>
</Window>
Related
I've been struggling with this for some time now. The problem relates to adding a second legend canvas in a wpf chart. I'm referencing Jack Yu's book Practical WPF Charts and Graphics LineChartWithLegend.xaml file. In the xaml file, I added the new legend canvas named "legendCanvas2". I've changed the code behind to add a second instance of the legend in the AddChart() method. The problem is the second legend does not show inside chartCanvas. I suspect this issue has to do with multiple canvas containers inside chartCanvas but not sure. Any help with alternative ways I can display two legends inside chartCanvas would be appreciated.
XAML
<Window x:Class="LineCharts.LineChartWithLegend"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Line Chart with Legend" Height="400" Width="500">
<Grid Name="grid1" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Name="column1" Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Name="row1" Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Margin="2" x:Name="tbTitle" Grid.Column="1" Grid.Row="0"
RenderTransformOrigin="0.5,0.5" FontSize="14" FontWeight="Bold"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" TextAlignment="Center"
Text="Title"/>
<TextBlock Margin="2" x:Name="tbXLabel" Grid.Column="1" Grid.Row="2"
RenderTransformOrigin="0.5,0.5" TextAlignment="Center"
Text="X Axis"/>
<TextBlock Margin="2" Name="tbYLabel" Grid.Column="0" Grid.Row="1"
RenderTransformOrigin="0.5,0.5" TextAlignment="Center"
Text="Y Axis">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
<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">
<Canvas Name="chartCanvas" ClipToBounds="True">
<Canvas Name="legendCanvas" Background="Transparent" />
<Canvas Name="legendCanvas2" Background="Transparent" />
</Canvas>
</Canvas>
</Grid>
</Window>
Code-Behind
private void AddChart()
{
cs = new ChartStyleGridlines();
lg = new Legend();
lg2 = new Legend();
dc = new DataCollection();
ds = new DataSeries();
cs.ChartCanvas = chartCanvas;
cs.TextCanvas = textCanvas;
cs.Title = "Sine and Cosine Chart";
cs.Xmin = 0;
cs.Xmax = 7;
cs.Ymin = -1.5;
cs.Ymax = 1.5;
cs.YTick = 0.5;
cs.GridlinePattern = ChartStyleGridlines.GridlinePatternEnum.Dot;
cs.GridlineColor = Brushes.Black;
cs.AddChartStyle(tbTitle, tbXLabel, tbYLabel);
// Draw Sine curve:
ds.LineColor = Brushes.Blue;
ds.LineThickness = 1;
ds.SeriesName = "Sine";
for (int i = 0; i < 70; i++)
{
double x = i / 5.0;
double y = Math.Sin(x);
ds.LineSeries.Points.Add(new Point(x, y));
}
dc.DataList.Add(ds);
// Draw cosine curve:
ds = new DataSeries();
ds.LineColor = Brushes.Red;
ds.SeriesName = "Cosine";
ds.LinePattern = DataSeries.LinePatternEnum.DashDot;
ds.LineThickness = 2;
for (int i = 0; i < 70; i++)
{
double x = i / 5.0;
double y = Math.Cos(x);
ds.LineSeries.Points.Add(new Point(x, y));
}
dc.DataList.Add(ds);
// Draw sine^2 curve:
ds = new DataSeries();
ds.LineColor = Brushes.DarkGreen;
ds.SeriesName = "Sine^2";
ds.LinePattern = DataSeries.LinePatternEnum.Dot;
ds.LineThickness = 2;
for (int i = 0; i < 70; i++)
{
double x = i / 5.0;
double y = Math.Sin(x) * Math.Sin(x);
ds.LineSeries.Points.Add(new Point(x, y));
}
dc.DataList.Add(ds);
dc.AddLines(cs);
lg.LegendCanvas = legendCanvas;
lg.IsLegend = true;
lg.IsBorder = true;
lg.LegendPosition = Legend.LegendPositionEnum.NorthWest;
lg.AddLegend(cs.ChartCanvas, dc);
lg2 = new Legend();
lg2.LegendCanvas = legendCanvas2;
lg2.IsLegend = true;
lg2.IsBorder = true;
lg2.LegendPosition = Legend.LegendPositionEnum.NorthEast;
lg2.AddLegend(cs.ChartCanvas, dc);
}
private void chartGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
textCanvas.Width = chartGrid.ActualWidth;
textCanvas.Height = chartGrid.ActualHeight;
legendCanvas.Children.Clear();
legendCanvas2.Children.Clear();
chartCanvas.Children.RemoveRange(2, chartCanvas.Children.Count - 1); // changed index from 1 to 2
textCanvas.Children.RemoveRange(1, textCanvas.Children.Count - 1);
AddChart();
}
So I'm trying to make 8 circles which all have different fill colours and all have different blinking speeds. So far I have been able to make them blink at different speeds, but I'm having trouble with making them all have different colours. My code so far:
private void Appear(Ellipse element, double duration, Brush colour)
{
element.Fill = colour;
DoubleAnimation db = new DoubleAnimation();
db.From = 0.0;
db.To = 1.0;
db.Duration = new Duration(TimeSpan.FromSeconds(duration));
db.RepeatBehavior = RepeatBehavior.Forever;
element.BeginAnimation(Ellipse.OpacityProperty, db);
}
private Brush SetEllipseColour(Ellipse element)
{
Random rnd = new Random();
int red = rnd.Next(0, 255);
int green = rnd.Next(0, 255);
int blue = rnd.Next(0, 255);
Brush fillColour = new SolidColorBrush(Color.FromRgb((byte)red, (byte)green, (byte)blue));
return fillColour;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
List<Ellipse> elements = new List<Ellipse>();
elements.Add(Circle1);
elements.Add(Circle2);
elements.Add(Circle3);
elements.Add(Circle4);
elements.Add(Circle5);
elements.Add(Circle6);
elements.Add(Circle7);
elements.Add(Circle8);
Random rnd = new Random();
foreach (Ellipse element in elements)
{
int r = rnd.Next(1, 10);
double duration = (Double)r / 10;
Appear(element, duration, SetEllipseColour(element));
}
}
And my WPF:
<Canvas Margin="10">
<Ellipse
x:Name="Circle1"
Fill="Black"
Height="100"
Width="100"/>
<Ellipse
x:Name="Circle2"
Fill="Black"
Height="100"
Width="100"
Margin="120,0,0,0"/>
<Ellipse
x:Name="Circle3"
Fill="Black"
Height="100"
Width="100"
Margin="240,0,0,0"/>
<Ellipse
x:Name="Circle4"
Fill="Black"
Height="100"
Width="100"
Margin="360,0,0,0"/>
<Ellipse
x:Name="Circle5"
Fill="Black"
Height="100"
Width="100"
Margin="0,120,0,0"/>
<Ellipse
x:Name="Circle6"
Fill="Black"
Height="100"
Width="100"
Margin="120,120,0,0"/>
<Ellipse
x:Name="Circle7"
Fill="Black"
Height="100"
Width="100"
Margin="240,120,0,0"/>
<Ellipse
x:Name="Circle8"
Fill="Black"
Height="100"
Width="100"
Margin="360,120,0,0"/>
</Canvas>
<Button x:Name="button1" Content="Start" Width="80" Height="20" Margin="0,200,0,0" Click="button1_Click"/>
Note: I know I can compress / change my code to make it neater or better, but for now I just want to get the colours working.
So currently the code I have changes the fill colour of all Ellipse elements, but I want to change it to just affect each Circle. How would I go about doing this?
Edit:
For those who are confused what Im trying to ask, I do not know how to individually change the fill colour of every Circle.
Set the instance of the Random class at the class level, check the below example, by clicking on the the Button that says Blink Em! the animation is triggered.
The XAML
<Window x:Class="Blink.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:Blink"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" WindowState="Maximized">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button x:Name="BlinkEm" Content="Blink Em!" Height="30" Click="BlinkEm_Click"/>
<StackPanel x:Name="Container" Orientation="Horizontal" Loaded="Container_Loaded" Grid.Row="1"/>
</Grid>
</Window>
The code-behind
namespace Blink
{
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public Random random = new Random();
public MainWindow()
{
InitializeComponent();
}
public List<Ellipse> CreateCircles(int count)
{
List<Ellipse> circles = new List<Ellipse>();
for (int i = 0; i < count; i++)
{
var circle = new Ellipse
{
Height = 100,
Width = 100,
Margin = new Thickness(10),
Fill = Brushes.SkyBlue
};
circles.Add(circle);
}
return circles;
}
public void AddCircles()
{
var circles = this.CreateCircles(8);
foreach (var circle in circles)
{
Container.Children.Add(circle);
}
}
private void Container_Loaded(object sender, RoutedEventArgs e)
{
AddCircles();
}
private void BlinkEm_Click(object sender, RoutedEventArgs e)
{
foreach (Ellipse circle in Container.Children)
{
circle.Fill = GetRandomColor();
circle.BeginAnimation(Ellipse.OpacityProperty, GetBlinkAnimation());
}
}
public Brush GetRandomColor()
{
var red = Convert.ToByte(random.Next(0, 255));
var green = Convert.ToByte(random.Next(0, 255));
var blue = Convert.ToByte(random.Next(0, 255));
return new SolidColorBrush(Color.FromRgb(red, green, blue));
}
public DoubleAnimation GetBlinkAnimation()
{
var duration = random.NextDouble();
var animation = new DoubleAnimation
{
From = 0.0,
To = 1.0,
Duration = new Duration(TimeSpan.FromSeconds(duration)),
RepeatBehavior = RepeatBehavior.Forever
};
return animation;
}
}
}
I think your mistake here is that the Random object is being recreated every time. Try putting that into a field and initializing it once, or by sending it as a parameter to your SetEllipseColor method.
Due to random number generators not actually being random, they derive their "initial" random values from a seed value, often the current time. This means if you create a bunch of new Random instances in a very short time, they are likely to end up with the same seed and thus the same value.
(In more general terms, "Appear" and "SetEllipseColor" aren't very good method names. The former is vague and the latter doesn't actually describe what that method is doing.)
I am trying to make a virtual joystick that a user can move around with the mouse in WPF in C#. I am trying to use the polar coordinate system because I want the knob of the joystick to remain in the circle.
I am getting some really weird behavior - if anybody has experience with this sort of thing any tips would be nice. Thank you
EDIT: I got it working. I posted the updated code below. It is by no means a good/professional solution to this but it works. So I hope if somebody in the future is trying to do this same task it may help. I tried to add some comments to explain what was going on. Here you go!
NOTE: If you are trying to use this for your program make note that there are two hardcoded values you must change. The first is the x_starting/y_starting. Thats where your virtual joystick knob should reset to. And next is the radius when calculating the max possible value. Make sure it is half the width of the background joystick.
The code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
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 WpfApplication4
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
double radius;
bool captured = false;
double x_shape, x_canvas, y_shape, y_canvas; //Canvas is used to keep track of where the joystick is on screen,
// shape is used for where the knob is.
UIElement source = null;
double y_starting = 180; //The starting X and Y position for the Knob. (CHANGE TO WHERE UR CANVAS.TOP/CANVAS.LEFT IS FOR THE KNOB)
double x_starting = 105;
private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
string objname = ((Ellipse)sender).Name;
if (objname == "Knob")
{
source = (UIElement)sender;
Mouse.Capture(source);
captured = true;
x_shape = Canvas.GetLeft(reference);
x_canvas = e.GetPosition(Knob).X;
y_shape = Canvas.GetTop(reference);
y_canvas = e.GetPosition(Knob).Y;
}
}
private void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
string objname = ((Ellipse)sender).Name;
Mouse.Capture(null);
captured = false;
if (objname == "Knob") {
// x_shape = x_starting;
// y_shape = y_starting;
Canvas.SetLeft(source, x_starting);
Canvas.SetTop(source, y_starting); //Reset to our starting values
XTextBlock.Text = x_starting.ToString();
YTextBlock.Text = y_starting.ToString();
}
}
private void Ellipse_MouseMove(object sender, MouseEventArgs e)
{
double x = e.GetPosition(reference).X; //Getting mouse pos relative to the center of your joystick (I have an empty textblock there called reference)
double y = e.GetPosition(reference).Y;
double r = Math.Sqrt((x * x) + (y * y)); //Calculate radius..
XMousePos.Text = x.ToString();
YMousePos.Text = y.ToString();
string objname = ((Ellipse)sender).Name;
double theta = Math.Atan2(y, x); //Calculate theta..
Theta.Text = theta.ToString();
double x1 = (r * Math.Cos(theta)); //This converts polar coordinates to cartesian plane coordinates.
double y1 = (r * Math.Sin(theta));
XPolPos.Text = x1.ToString();
YPolPos.Text = y1.ToString();
double xmax = (62.5 * Math.Cos(theta)); //Calculate a max so that your knob stays within the circle. The radius value should be half the width of the
double ymax = (62.5 * Math.Sin(theta)); // background of your joystick.
X2PolPos.Text = xmax.ToString();
Y2PolPos.Text = ymax.ToString();
if (objname == "Knob") {
if (captured)
{
if ((((x1 > 0) && (x1 < xmax)) || ((x1 <= 0) && (x1 > xmax))) && (((y1 > 0) && (y1 < ymax)) || ((y1 <= 0) && (y1 > ymax)))) //Seems like bad way to do it. But this is how i check to see if knob is in bounds.
{
x = e.GetPosition(reference).X; //Get the values and calculate it again.
y = e.GetPosition(reference).Y;
r = Math.Sqrt((x * x) + (y * y));
theta = Math.Atan2(y, x);
x1 = (r * Math.Cos(theta));
y1 = (r * Math.Sin(theta));
x_shape += x1 - x_canvas; //Changing our values and moving the knob.
Canvas.SetLeft(source, x_shape);
x_canvas = x1;
y_shape += y1 - y_canvas;
Canvas.SetTop(source, y_shape);
y_canvas = y1;
XTextBlock.Text = x_shape.ToString();
YTextBlock.Text = y_shape.ToString();
}
}
}
}
}
}
And the XAML just in case:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" MouseMove="Window_MouseMove" KeyDown="Window_KeyDown">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="KNOB POSITION"/>
<TextBlock Name="XTextBlock"/>
<TextBlock Name="YTextBlock"/>
<TextBlock Text="MOUSE POSITION"/>
<TextBlock Name="XMousePos"/>
<TextBlock Name="YMousePos"/>
<TextBlock Text="POLAR COORDINATES"/>
<TextBlock Name="XPolPos"/>
<TextBlock Name="YPolPos"/>
</StackPanel>
<Canvas Name="LayoutRoot" Grid.Column="1">
<Ellipse Fill="#FFF4F4F5" Name ="Joystick" Height="125" Canvas.Left="51" Stroke="Black" Canvas.Top="128" Width="125" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp" MouseMove="Ellipse_MouseMove"/>
<Ellipse Fill="#FFF4F4F5" Name="Knob" Height="15" Canvas.Left="105" Stroke="Black" Canvas.Top="180" Width="15" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp" MouseMove="Ellipse_MouseMove"/>
</Canvas>
</Grid>
When we're talking about joystick the polar coordinate system does not seems to be helpful.
What we need is X and Y offset in range [-1; 1]. We can easily evaluate it knowing Field (big) Radius, Filed Center point and Mouse coordinates.
Here is how it works (remove all events except Ellipse_MouseMove). Member m_vtJoystickPos holds selected Joystick position.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
UpdateKnobPosition();
}
/// <summary>
/// Current joystick position
/// </summary>
Vector m_vtJoystickPos = new Vector();
private void Ellipse_MouseMove(object sender, MouseEventArgs e)
{
double fJoystickRadius = Joystick.Height * 0.5;
//Make coords related to the center
Vector vtJoystickPos = e.GetPosition(Joystick) -
new Point(fJoystickRadius, fJoystickRadius);
//Normalize coords
vtJoystickPos /= fJoystickRadius;
//Limit R [0; 1]
if (vtJoystickPos.Length > 1.0)
vtJoystickPos.Normalize();
XMousePos.Text = vtJoystickPos.X.ToString();
YMousePos.Text = vtJoystickPos.Y.ToString();
//Polar coord system
double fTheta = Math.Atan2(vtJoystickPos.Y, vtJoystickPos.X);
XPolPos.Text = fTheta.ToString(); //Angle
YPolPos.Text = vtJoystickPos.Length.ToString(); //Radius
if (e.LeftButton == MouseButtonState.Pressed)
{
m_vtJoystickPos = vtJoystickPos;
UpdateKnobPosition();
}
}
void UpdateKnobPosition()
{
double fJoystickRadius = Joystick.Height * 0.5;
double fKnobRadius = Knob.Width * 0.5;
Canvas.SetLeft(Knob, Canvas.GetLeft(Joystick) +
m_vtJoystickPos.X * fJoystickRadius + fJoystickRadius - fKnobRadius);
Canvas.SetTop(Knob, Canvas.GetTop(Joystick) +
m_vtJoystickPos.Y * fJoystickRadius + fJoystickRadius - fKnobRadius);
}
}
I've also included Polar CS evaluation (commented). BTW Polar CS is (R, Angle).
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="KNOB POSITION"/>
<TextBlock Name="XTextBlock"/>
<TextBlock Name="YTextBlock"/>
<TextBlock Text="MOUSE POSITION"/>
<TextBlock Name="XMousePos"/>
<TextBlock Name="YMousePos"/>
<TextBlock Text="POLAR COORDINATES"/>
<TextBlock Name="XPolPos"/>
<TextBlock Name="YPolPos"/>
</StackPanel>
<Canvas Name="LayoutRoot" Grid.Column="1">
<Ellipse Fill="#FFF4F4F5" Name ="Joystick" Height="125" Canvas.Left="51" Stroke="Black" Canvas.Top="127" Width="125" MouseMove="Ellipse_MouseMove"/>
<Ellipse Fill="#FFF4F4F5" Name="Knob" Height="16" Canvas.Left="106" Stroke="Black" Canvas.Top="182" Width="15" MouseMove="Ellipse_MouseMove"/>
</Canvas>
</Grid>
</Window>
I need the functionality of an image scale, translate and cropping just like what a photo chooser task do in windows phone 8.1. I can not use photo chooser task because I will not select the photo from media photo library or capture the image. For my purpose the image will be selected from a server saved picture list. I have written some code for this but it's not working as I desire. I am working on it for 4 weeks but hardly find the perfect solution. Here is my code.
This is the xaml code
<phone:PhoneApplicationPage
x:Class="TransformationLearning.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="Transformation" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid Grid.Row="1" Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Name="grid"
Grid.Row="0"
Height="400"
Width="400"
>
<Image x:Name="myImage" Source="/Assets/Lion.jpg"
Stretch="UniformToFill"
ManipulationCompleted="CompleteManipulation"
ManipulationDelta="DeltaManipulation"
Loaded="ImageLoaded"
Grid.Row="1"
>
<Image.RenderTransform>
<CompositeTransform x:Name="myComposite"
/>
</Image.RenderTransform>
</Image>
<Rectangle Width="200"
Height="200"
Name="myRectangle"
Margin="100 100"
Stroke="White"
StrokeThickness="3" Grid.Row="1">
</Rectangle>
</Grid>
<Image Name="myCropPicture" Grid.Row="1"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
and this is the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using TransformationLearning.Resources;
using Microsoft.Xna.Framework;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using Lumia.Imaging;
using Lumia.Imaging.Transforms;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.Storage;
using System.Windows.Controls.Primitives;
using System.Runtime.InteropServices.WindowsRuntime;
namespace TransformationLearning
{
public partial class MainPage : PhoneApplicationPage
{
WriteableBitmap WB_CapturedImage;//for original image
WriteableBitmap WB_CroppedImage;//for cropped image
double width, height;
WriteableBitmap wb;
CompositeTransform transform;
GeneralTransform gt, gt1;
System.Windows.Point absolutePosition, absolutePosition1;
public MainPage()
{
InitializeComponent();
transform = new CompositeTransform();
}
private void ImageLoaded(object sender, RoutedEventArgs e)
{
wb = new WriteableBitmap(myImage, null);
myComposite.CenterX = 150;
myComposite.CenterY = 150;
}
private double BoundaryCheck(double value)
{
if (value < 1.0)
return 1.0;
else if (value > 2.0)
return 2.0;
else
return value;
}
private void DeltaManipulation(object sender, System.Windows.Input.ManipulationDeltaEventArgs e)
{
transform = myComposite;
if (e.DeltaManipulation.Scale.X == 0.0 || e.DeltaManipulation.Scale.Y == 0.0)
{
myComposite.ScaleX *= 1.0;
myComposite.ScaleY *= 1.0;
}
else if (e.DeltaManipulation.Scale.X > 0.0)
{
double value = BoundaryCheck(myComposite.ScaleX*e.DeltaManipulation.Scale.X);
myComposite.ScaleX = myComposite.ScaleY = value;
gt = myImage.TransformToVisual(grid);
absolutePosition = gt.Transform(new System.Windows.Point(0, 0));
gt1 = myRectangle.TransformToVisual(grid);
absolutePosition1 = gt1.Transform(new System.Windows.Point(0, 0));
if ((absolutePosition1.X <= absolutePosition.X)
|| (absolutePosition1.Y <= absolutePosition.Y)
|| (absolutePosition1.X + myRectangle.Width >= absolutePosition.X + wb.PixelWidth)
|| (absolutePosition1.Y + myRectangle.Height >= absolutePosition.Y + wb.PixelHeight))
{
myComposite = transform;
}
}
gt = myImage.TransformToVisual(grid);
absolutePosition = gt.Transform(new System.Windows.Point(0, 0));
gt1 = myRectangle.TransformToVisual(grid);
absolutePosition1 = gt1.Transform(new System.Windows.Point(0, 0));
if (e.DeltaManipulation.Translation.Y > 0.0 && (absolutePosition1.Y >= (absolutePosition.Y + e.DeltaManipulation.Translation.Y)))
{
myComposite.TranslateY += (e.DeltaManipulation.Translation.Y-2);
}
if (e.DeltaManipulation.Translation.X > 0.0 && (absolutePosition1.X >= (absolutePosition.X + e.DeltaManipulation.Translation.X)))
{
myComposite.TranslateX += (e.DeltaManipulation.Translation.X - 2);
}
if (e.DeltaManipulation.Translation.Y < 0.0 && ((absolutePosition1.Y + myRectangle.Height) <= (absolutePosition.Y + e.DeltaManipulation.Translation.Y + wb.PixelHeight)))
{
myComposite.TranslateY += (e.DeltaManipulation.Translation.Y + 2);
}
if (e.DeltaManipulation.Translation.X < 0.0 && ((absolutePosition1.X + myRectangle.Width) <= (absolutePosition.X + e.DeltaManipulation.Translation.X + wb.PixelWidth)))
{
myComposite.TranslateX += (e.DeltaManipulation.Translation.X + 2);
}
wb = new WriteableBitmap(myImage, myComposite);
gt = myImage.TransformToVisual(grid);
absolutePosition = gt.Transform(new System.Windows.Point(0, 0));
}
private void CompleteManipulation(object sender, System.Windows.Input.ManipulationCompletedEventArgs e)
{
App.RootFrame.RenderTransform = new CompositeTransform();
grid.UpdateLayout();
CropImage();
App.RootFrame.RenderTransform = new CompositeTransform();
grid.UpdateLayout();
}
private async void CropImage()
{
var ms = new MemoryStream();
wb.SaveJpeg(ms, wb.PixelWidth, wb.PixelHeight, 0, 100);
ms.Position = 0;
gt1 = myRectangle.TransformToVisual(myImage);
absolutePosition1 = gt1.Transform(new System.Windows.Point(0, 0));
using (var source = new StreamImageSource(ms))
{
using (var filterEffect = new FilterEffect(source))
{
var filter = new CropFilter(new Windows.Foundation.Rect(absolutePosition1.X, absolutePosition1.Y, myRectangle.Width, myRectangle.Height));
filterEffect.Filters = new IFilter[] { filter };
var target = new WriteableBitmap((int)(myRectangle.Width), (int)(myRectangle.Height));
using (var renderer = new WriteableBitmapRenderer(filterEffect, target))
{
try
{
await renderer.RenderAsync();
}
catch (Exception t)
{
System.Diagnostics.Debug.WriteLine(t.InnerException);
System.Diagnostics.Debug.WriteLine(t.Message);
}
}
myCropPicture.Source = target;
}
}
}
}
}
Here I have used Lumia Imaging SDK crop filter for cropping the image. "myImage" is the source image and "myCropPicture" is used for showing the cropped image. For transformation I have used composite transform. When I scale the source image this code can not crop that image accordingly.I need the help badly.
did you check this post. it might come in handy
Image crop control for Silverlight or Windows Phone
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;
}