I been working with winforms, so my knowledge of WFP is non existent, this is something i am trying to test.
In code I am generating few buttons and placing them on Canvas. Than after clik on any button, i am moving that button around, and after second click button should stay at the position where mouse cursor was when clicked.
If mouse cursor go outside canvas then button will stop follow it.
My problem is, that button is moving, but only when mouse cursor is over that button or any other control, but it is not moving while mouse cursor is traveling over Canvas.
XAML
<Window x:Class="WpfTestDrag.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:WpfTestDrag"
mc:Ignorable="d"
Title="MainWindow" Height="522" Width="909">
<Grid>
<Grid.ColumnDefinitions >
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="130*"/>
<ColumnDefinition Width="33*"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="40" />
<RowDefinition Height="*" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Canvas Grid.Column="1" Grid.Row="1" x:Name="cnvTest" Width="auto" Height="auto" PreviewMouseMove="CnvTest_PreviewMouseMove"/>
<TextBlock x:Name="txbStatus" Grid.Column="1" Grid.Row="2" Grid.ColumnSpan="2"/>
</Grid>
</Window>
C#
public partial class MainWindow : Window
{
Button bts;
Boolean isUsed = false;
public MainWindow()
{
InitializeComponent();
CreateButtons();
}
private void CreateButtons()
{
var xPos = 10.0;
var yPos = 15.0;
var Rnd = new Random();
for (int i = 0; i < 3; i++)
{
var btn = new Button();
btn.Name = "btn" + i;
btn.Content = "Button - " + i;
btn.Tag = "Tag" + i;
btn.Width = 150;
btn.Height = 150;
btn.Click += Btn_Click;
Canvas.SetLeft(btn, xPos);
Canvas.SetTop(btn, yPos);
cnvTest.Children.Add(btn);
xPos = xPos + btn.Width + Rnd.Next(-15,40);
yPos = yPos + btn.Height + Rnd.Next(-15, 40);
}
}
private void Btn_Click(object sender, RoutedEventArgs e)
{
bts = sender as Button;
if (isUsed == false)
{
isUsed = true;
}
else
{
isUsed = false;
}
}
private void CnvTest_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point p = Mouse.GetPosition(cnvTest);
if (isUsed == true)
{
Canvas.SetLeft(bts, p.X);
Canvas.SetTop(bts, p.Y);
txbStatus.Text = bts.Name.ToString() + " isUsed:" + isUsed.ToString() + " -> xPos:" + p.X.ToString() + " yPos:" + p.Y.ToString();
}
}
}
Should I use something else than Canvas for this?
You should set the Background property of the Canvas to Transparent (or any other Brush) for it to respond to the mouse events:
<Canvas Grid.Column="1" Grid.Row="1" x:Name="cnvTest" Width="auto" Height="auto" PreviewMouseMove="CnvTest_PreviewMouseMove"
Background="Transparent"/>
I have wpf solution, where I have created UserControl for trending. This UserControl is used in MainWindow.
The path of trend is painted on method showData() of current class ChartControl. But because I want to have actual picture of path related to main window size, I have add SizeChanged event where this showData() method is called.
My code for event here:
private void OnResize(object sender, SizeChangedEventArgs e)
{
this.showData();
}
Edit:
private List<ChartData> data = new List<ChartData>();
public void showData()
{
double maxVal = this.maxVal();
double minVal = this.minVal();
TimeSpan timeSpan = new TimeSpan();
timeSpan = this.maxTime() - this.minTime();
double stepSize = Area.ActualWidth / timeSpan.TotalSeconds;
setLabels();
Area.Children.Clear();
for (int i = 1; i < this.data.Count; i++)
{
Line lineHorizont = new Line();
lineHorizont.StrokeThickness = 2;
lineHorizont.Stroke = Brushes.Red;
lineHorizont.X1 = (this.data[i].X - this.minTime()).TotalSeconds * stepSize;
lineHorizont.Y1 = Math.Abs(((this.data[i - 1].Y - minVal) / (maxVal - minVal) * Area.ActualHeight) - Area.ActualHeight);
lineHorizont.X2 = lineHorizont.X1;
lineHorizont.Y2 = Math.Abs(((this.data[i].Y - minVal) / (maxVal - minVal) * Area.ActualHeight) - Area.ActualHeight);
Area.Children.Add(lineHorizont);
Line lineVertical = new Line();
lineVertical.StrokeThickness = 2;
lineVertical.Stroke = Brushes.Red;
lineVertical.X1 = (this.data[i - 1].X - this.minTime()).TotalSeconds * stepSize;
lineVertical.Y1 = Math.Abs(((this.data[i - 1].Y - minVal) / (maxVal - minVal) * Area.ActualHeight) - Area.ActualHeight);
lineVertical.X2 = (this.data[i].X - this.minTime()).TotalSeconds * stepSize;
lineVertical.Y2 = lineVertical.Y1;
Area.Children.Add(lineVertical);
}
//Draw cross coordinator
coordX1.StrokeThickness = 1;
coordX1.Stroke = Brushes.Black;
coordX1.X1 = 0;
coordX1.Y1 = Mouse.GetPosition(Area).Y;
coordX1.X2 = Area.ActualWidth;
coordX1.Y2 = coordX1.Y1;
Area.Children.Add(coordX1);
coordX2.StrokeThickness = 1;
coordX2.Stroke = Brushes.Black;
coordX2.X1 = Mouse.GetPosition(Area).X;
coordX2.Y1 = 0;
coordX2.X2 = coordX2.X1;
coordX2.Y2 = Area.ActualHeight;
Area.Children.Add(coordX2);
}
public double maxVal()
{
List<double> data = new List<double>();
for (int i = 0; i < this.data.Count; i++)
{
data.Add(this.data[i].Y);
}
return data.Max<double>();
}
edit2:
xaml content of Main Window
<Grid Margin="0">
<lib:ChartControl x:Name="Trend" Margin="0" Width="Auto" Height="Auto"/>
</Grid>
xaml content of ChartControl
<Grid Grid.Column="1" Margin="0" Grid.Row="1" Background="Black" Cursor="Cross" PreviewMouseMove="OnMouseMove">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Rectangle Fill="#FFF1F1F1" Grid.RowSpan="10"/>
<Rectangle Fill="#FFD4D4D4"/>
<Rectangle Fill="#FFD4D4D4" Grid.Row="2"/>
<Rectangle Fill="#FFD4D4D4" Grid.Row="4"/>
<Rectangle Fill="#FFD4D4D4" Grid.Row="6"/>
<Rectangle Fill="#FFD4D4D4" Grid.Row="8"/>
<Canvas x:Name="Area" Grid.RowSpan="10"/>
</Grid>
After start of program everything is working fine and according expectations, but I am getting Exception in View Designer in Visual studio, which is eliminating any design changes.
In the OnResize event handler you must test that the current instance is not in design mode.
System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)
if you have no data then return...then no more design exception !!
private void OnResize(object sender, SizeChangedEventArgs e) {
if(!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
this.showData();
}
List<double> data = new List<double>();
for (int i = 0; i < this.data.Count; i++)
{
data.Add(this.data[i].Y);
}
return data.Max<double>();
Please, please, please don't name your local variables the same as your outer-scope variables.
As for why this is failing, it's obvious:
this.data is empty when this is called, as such, when you try to perform data.Max<double>() you get an exception.
You're calling MaxVal() at the very beginning of ShowData() and as far as I can see, there's no real discernible place you're adding any ChartData to the data list.
I'm trying to read a file and display the progress on the screen.
It should be displayed on a Progress Bar (progress) and a textbox (progress_text).
This is the code I'm using:
public partial class MainWindow : Window
{
public static List<String> file = new List<String>();
public long currentPosition = 0;
String line;
long length;
public BackgroundWorker worker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
}
private void Start(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bg = sender as BackgroundWorker;
Dispatcher.Invoke(() =>
{
progress.Minimum = 0;
progress.Maximum = 100;
FileInfo fi = new FileInfo(upload.Text);
length = fi.Length;
int percent;
using (StreamReader sr = new StreamReader(upload.Text, System.Text.Encoding.ASCII))
{
while (sr.EndOfStream == false)
{
line = sr.ReadLine();
file.Add(line);
currentPosition += line.Count();
percent = (int)(100.0 / length * currentPosition);
bg.ReportProgress(percent);
}
}
});
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progress.Value = e.ProgressPercentage;
progress_text.Text = "reading " + currentPosition + " out of " + length;
}
}
XAML:
<Window x:Class="ProgressBarUploadFile.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"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Content="Select File" FontSize="15" Click="Upload_file"/>
<TextBox x:Name="upload" Grid.Row="0" Grid.Column="1" Text="" />
<Button Grid.Row="1" Grid.Column="0" Content="Start upload" FontSize="15" Click="Start"/>
<ProgressBar Name="progress" Grid.Row="1" Grid.Column="1" />
<TextBlock Name="progress_text" Grid.Row="1" Grid.Column="1" VerticalAlignment="Bottom"/>
</Grid>
When I run it in debug, it seems to be working.
But the progress bar and textbox are updated only when the file is read completely.
I followed a few tutorials, such as:
https://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx?f=255&MSPPError=-2147217396
http://www.wpf-tutorial.com/misc-controls/the-progressbar-control/
But I can't figure it out..
I think it's something very small, but I can't find it.
Thank you!
Try this code:
private void Start(object sender, RoutedEventArgs e)
{
progress.Minimum = 0;
progress.Maximum = 100;
worker.RunWorkerAsync();
}
BackgroundWorker bg = sender as BackgroundWorker;
FileInfo fi = new FileInfo(#"File");
length = fi.Length;
int percent;
using (StreamReader sr = new StreamReader(#"File", System.Text.Encoding.ASCII))
{
while (sr.EndOfStream == false)
{
line = sr.ReadLine();
file.Add(line);
currentPosition += line.Count();
percent = (int)(currentPosition / length) * 100;
bg.ReportProgress(percent);
Thread.Sleep(100);
}
}
Your code has this problem:
(int)(a / b) * 100 will calculate a/b first then convert it to int and then *100, so before a reaches b, the a/b will always be 0.* and (int)(a/b) will always be 0 so the final value 0*100 will always be 0.
Suddenly when a=b then (int)(a/b) become 1 and the final value changed to 100. That is why your progress bar will not be updated until the file is read completely
So, you should use percent = (int)(currentPosition * 100 / length);
Or as #jmc suggested in the comment: use percent = (int)(100.0 / length * currentPosition);
I have an image and I am clipping the mouse area using an Ellipse in which I am quite successful.
But I want that ellipse to be a part of my user control and the user control should move along with my finger and the clipping Ellipse should be inside the user control .
The complete project can be downloaded from here
My UserControl'sXAML is
<UserControl
x:Class="App78.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="230"
Width="170">
<Grid Height="230" Width="170">
<Path Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z"
Fill="#FFF4F4F5"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
Height="227"
Width="171" ></Path>
<Ellipse x:Name="MagnifierEllipse" x:FieldModifier="public" Opacity="1" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" Width="150" Height="150" Stroke="White" StrokeThickness="3" Margin="11,8,0,0" >
<Ellipse.RenderTransform>
<TranslateTransform x:Name="MagnifierTransform" x:FieldModifier="public"/>
</Ellipse.RenderTransform>
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform x:FieldModifier="public"
x:Name="PositionTransform"/>
<ScaleTransform x:FieldModifier="public"
x:Name="ZoomTransform"/>
<TranslateTransform x:FieldModifier="public"
x:Name="CenterTransform" />
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
</UserControl>
My MainPage.XAML is
<Page
x:Class="App78.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Holding="LayoutGrid_Holding"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<!--<Ellipse
x:Name="MagnifierEllipse"
Opacity="1"
Visibility="Collapsed"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="150"
Height="150"
Stroke="White"
StrokeThickness="3"
Margin="-100">
<Ellipse.RenderTransform>
<TranslateTransform
x:Name="MagnifierTransform"/>
</Ellipse.RenderTransform>
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform
x:Name="PositionTransform"/>
<ScaleTransform
x:Name="ZoomTransform"/>
<TranslateTransform
x:Name="CenterTransform" />
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>-->
<local:Magnifier x:Name="MagnifierTip" Visibility="Visible" />
</Grid>
</Page>
Please see the commented section of Ellipse , If I uncomment , I clearly get a Ellipse that is doing the clip.
My main requirenment is to get the Ellipse inside the user control , please refer the video for more clarification
My MainPage.XAML.CS has the following code
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace App78
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
// MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX = 0;
double offsetY = 0;
double imageScale = 1;
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
//imageScale = 1; - remains
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
MagnifierTip.MagnifierTransform.X = this.pointerX;
MagnifierTip.MagnifierTransform.Y = this.pointerY;
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
MagnifierTip. ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
private void LayoutGrid_Holding(object sender, HoldingRoutedEventArgs e)
{
// MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
}
I think you need to get your MagnifierTransform to the "root" Grid of your Magnifier UserControl.
Also the CenterTransform doesn't seem to be needed in my opinion.
The other last change is in the MainPage, making the User Control aligned to Top/Left so that the TranslateTransforms make sense.
Just as a bonus, I got the updated project here , also with pointer pressed/released events to show/hide the magnifier. Be warned that "Holding" doesn't work with most mouse devices. Source: link
I also added comments on my changes (comments start with DV:).
It's not perfect and there's obviously still a lot for you to implement but hopefully this is what you needed.
For whoever doesn't want to download the project I'll leave the updated code here:
MainPage.xaml
<Page
x:Class="App78.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Holding="LayoutGrid_Holding"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="MagnifierTip" Visibility="Collapsed" />
</Grid>
</Page>
MainPage.xaml.cs
using System;
using System.Diagnostics;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace App78
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
//DV: If pointer is not in contact we can ignore it
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX = 0;
double offsetY = 0;
double imageScale = 1;
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
//imageScale = 1; - remains
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
//DV: This is probably not need anymore
//MagnifierTip.MagnifierTransform.X = this.pointerX;
//MagnifierTip.MagnifierTransform.Y = this.pointerY;
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
//DV: I haven't tested the Scaling/Zoom
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
//DV: I added a GlobalGrid Transform which translates every children
MagnifierTip.MagnifierTransformGrid.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTransformGrid.Y = this.pointerY - (MagnifierTip.ActualHeight); ;
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
//DV: Holding usually only works with touch https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.uielement.holding.aspx?f=255&MSPPError=-2147217396
private void LayoutGrid_Holding(object sender, HoldingRoutedEventArgs e)
{
//
}
//DV: pointer pressed supports both mouse and touch but fires immeadiatley. You'll have to figure out a delay strategy or using holding for touch and right click for mouse
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
//DV: pointer released supports both mouse and touch.
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
}
Magnifier.xaml
<UserControl
x:Class="App78.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="230"
Width="170">
<Grid Height="230" Width="170">
<!-- DV: This is the global transform I added -->
<Grid.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="MagnifierTransformGrid" x:FieldModifier="public"/>
</TransformGroup>
</Grid.RenderTransform>
<Ellipse Opacity="1" Visibility="Visible" Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}" HorizontalAlignment="Center" VerticalAlignment="Top" IsHitTestVisible="False" Width="135" Height="128" StrokeThickness="3" Margin="0,17,0,0" />
<Ellipse x:Name="MagnifierEllipse" x:FieldModifier="public" Opacity="1" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" Width="150" Height="150" Stroke="White" StrokeThickness="3" Margin="11,8,0,0" >
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform x:FieldModifier="public"
x:Name="CenterTransform"/>
<TranslateTransform x:FieldModifier="public"
x:Name="PositionTransform"/>
<ScaleTransform x:FieldModifier="public"
x:Name="ZoomTransform"/>
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z"
Fill="#FFF4F4F5"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
Height="227"
Width="171" ></Path>
</Grid>
</UserControl>
I would like to create a scratch card effect on the Windows Phone. However my current solution seem sluggish and the scratch effect look weird(refer to the screenshot below). The experience is very poor as compared to those that i had seen on iOS.
Would appreciate if someone could guide me toward it. Below are my current code.
<Grid Width="Auto" Height="Auto" Background="Black">
<Viewbox Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid Height="60" Width="60" Background="White">
<InkPresenter x:Name="inkP" Width="60" Height="60">
<InkPresenter.Background>
<ImageBrush Stretch="Fill" ImageSource="/Assets/image.png"/>
</InkPresenter.Background>
<Grid Height="60" Width="60">
<TextBlock x:Name="lblsecretText" HorizontalAlignment="Center" TextWrapping="Wrap" Text="LOL" VerticalAlignment="Center" Width="60" Foreground="Black" TextAlignment="Center" FontSize="5.333"/>
</Grid>
</InkPresenter>
</Grid>
</Viewbox>
</Grid>
Stroke s;
int mycol = 0;
public MainPage()
{
InitializeComponent();
inkP.MouseMove += new MouseEventHandler(inkP_MouseMove);
for (int i = 0; i < 60; i++)
{
for (int l = 0; l < 60; l++)
{
Stroke bigStroke = new Stroke();
bigStroke.StylusPoints.Add(new StylusPoint(i, l));
inkP.Strokes.Add(bigStroke);
}
}
}
StylusPoint _lastPoint;
void inkP_MouseMove(object sender, MouseEventArgs e)
{
StylusPointCollection pointErasePoints = e.StylusDevice.GetStylusPoints(inkP);
pointErasePoints.Insert(0, new StylusPoint(e.GetPosition(inkP).X, e.GetPosition(inkP).Y));
//Compare collected stylus points with the ink presenter strokes and store the intersecting strokes.
StrokeCollection hitStrokes = inkP.Strokes.HitTest(pointErasePoints);
if (hitStrokes.Count > 0)
{
foreach (Stroke hitStroke in hitStrokes)
{
inkP.Strokes.Remove(hitStroke);
}
}
_lastPoint = pointErasePoints[pointErasePoints.Count - 1];
}
Your strokes are beeing removed point by point.
You can adjust this by changing your MainPage constructor to:
public MainPage()
{
InitializeComponent();
inkP.MouseMove += new MouseEventHandler(inkP_MouseMove);
for (int i = 0; i < 60; i++)
{
Stroke bigStroke = new Stroke();
for (int l = 0; l < 60; l++)
{
bigStroke.StylusPoints.Add(new StylusPoint(i, l));
}
inkP.Strokes.Add(bigStroke);
}
}
This will add the strokes line by line.
When you remove them, they will be removed line by line.