I have created a button programmatically and i have passed it into a listbutton
I am trying to get the hover method to work on it but it does not work. when i make the button manually and pass it through the button list it works.
Design
<Window x:Class="KinectButton.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:Coding4Fun.Kinect.Wpf.Controls;assembly=Coding4Fun.Kinect.Wpf"
Title="MainWindow" Height="700" Width="1000" Background="#FFAD4747" Loaded="Window_Loaded">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="261*" />
<ColumnDefinition Width="717*" />
</Grid.ColumnDefinitions>
<Canvas Background="#FF371780" Name="canvas" Grid.ColumnSpan="2">
<Controls:HoverButton Margin="0" Padding="0" x:Name="kinectButton" ImageSize="50"
ImageSource="/Images/RightHand.png"
ActiveImageSource="/Images/RightHand.png"
TimeInterval="2000" Panel.ZIndex="1000" Canvas.Left="0" Canvas.Top="0" />
<Button x:Name="button1" Content="Button1" Height="152" Canvas.Left="128" Canvas.Top="162" Width="320" Background="#FF64C02F" FontSize="53.333" Click="button1_Click" BorderThickness="4" Foreground="White" />
<Button x:Name="button2" Content="Button2" Height="152" Canvas.Left="514" Canvas.Top="162" Width="320" Background="#FFFFF600" FontSize="53.333" Click="button2_Click" BorderThickness="4" Foreground="Black" />
<Label Canvas.Left="322" Canvas.Top="34" Content="Kinect Buttons Demo" Height="58" Name="label1" FontSize="28" Width="302" Foreground="White" />
<Label Canvas.Left="38" Canvas.Top="524" Content="" Height="66" Name="message" Width="530" Foreground="White" FontSize="40" />
<Button Canvas.Left="804" Canvas.Top="23" Content="Quit" Height="91" Name="quitButton" Width="137" FontSize="28" Background="#FFFF3838" Foreground="White" Click="quitButton_Click" />
<Controls:HoverButton ActiveImageSource="/Images/RightHand.png" Canvas.Left="71" Canvas.Top="6" ImageSize="50" ImageSource="/Images/RightHand.png" Name="kinectButton2" Padding="0" Panel.ZIndex="1000" TimeInterval="2000" />
<StackPanel Height="621" Name="stackPanel1" Width="935" Canvas.Left="71" Canvas.Top="70" />
</Canvas>
<Image Height="128" HorizontalAlignment="Left" Margin="489,500,0,0" Name="videoStream" Stretch="Fill" VerticalAlignment="Top" Width="191" Grid.Column="1" />
</Grid>
</Window>
Code:
namespace KinectButton
{
public partial class MainWindow : Window
{
System.Windows.Controls.Button newBtn = new Button();
private KinectSensor _Kinect;
private WriteableBitmap _ColorImageBitmap;
private Int32Rect _ColorImageBitmapRect;
private int _ColorImageStride;
private Skeleton[] FrameSkeletons;
List<Button> buttons;
static Button selected;
float handX;
float handY;
float handX2;
float handY2;
public MainWindow()
{
InitializeComponent();
InitializeButtons();
kinectButton.Click += new RoutedEventHandler(kinectButton_Click);
this.Loaded += (s, e) => { DiscoverKinectSensor(); };
this.Unloaded += (s, e) => { this.Kinect = null; };
}
private void InitializeButtons()
{
newBtn.Content = "Hello";
newBtn.Width = 500;
newBtn.Height = 500;
newBtn.Click += new RoutedEventHandler(newBtn_Click);
buttons = new List<Button> { button1, button2, quitButton,newBtn};
}
private void DiscoverKinectSensor()
{
KinectSensor.KinectSensors.StatusChanged += KinectSensors_StatusChanged;
this.Kinect = KinectSensor.KinectSensors.FirstOrDefault(x => x.Status == KinectStatus.Connected);
}
private void KinectSensors_StatusChanged(object sender, StatusChangedEventArgs e)
{
switch (e.Status)
{
case KinectStatus.Connected:
if (this.Kinect == null)
{
this.Kinect = e.Sensor;
}
break;
case KinectStatus.Disconnected:
if (this.Kinect == e.Sensor)
{
this.Kinect = null;
this.Kinect = KinectSensor.KinectSensors.FirstOrDefault(x => x.Status == KinectStatus.Connected);
if (this.Kinect == null)
{
MessageBox.Show("Sensor Disconnected. Please reconnect to continue.");
}
}
break;
}
}
public KinectSensor Kinect
{
get { return this._Kinect; }
set
{
if (this._Kinect != value)
{
if (this._Kinect != null)
{
UninitializeKinectSensor(this._Kinect);
this._Kinect = null;
}
if (value != null && value.Status == KinectStatus.Connected)
{
this._Kinect = value;
InitializeKinectSensor(this._Kinect);
}
}
}
}
private void UninitializeKinectSensor(KinectSensor kinectSensor)
{
if (kinectSensor != null)
{
kinectSensor.Stop();
kinectSensor.ColorFrameReady -= Kinect_ColorFrameReady;
kinectSensor.SkeletonFrameReady -= Kinect_SkeletonFrameReady;
}
}
private void InitializeKinectSensor(KinectSensor kinectSensor)
{
if (kinectSensor != null)
{
ColorImageStream colorStream = kinectSensor.ColorStream;
colorStream.Enable();
this._ColorImageBitmap = new WriteableBitmap(colorStream.FrameWidth, colorStream.FrameHeight,
96, 96, PixelFormats.Bgr32, null);
this._ColorImageBitmapRect = new Int32Rect(0, 0, colorStream.FrameWidth, colorStream.FrameHeight);
this._ColorImageStride = colorStream.FrameWidth * colorStream.FrameBytesPerPixel;
videoStream.Source = this._ColorImageBitmap;
kinectSensor.SkeletonStream.Enable(new TransformSmoothParameters()
{
Correction = 0.5f,
JitterRadius = 0.05f,
MaxDeviationRadius = 0.04f,
Smoothing = 0.5f
});
kinectSensor.SkeletonFrameReady += Kinect_SkeletonFrameReady;
kinectSensor.ColorFrameReady += Kinect_ColorFrameReady;
kinectSensor.Start();
this.FrameSkeletons = new Skeleton[this.Kinect.SkeletonStream.FrameSkeletonArrayLength];
}
}
private void Kinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame frame = e.OpenColorImageFrame())
{
if (frame != null)
{
byte[] pixelData = new byte[frame.PixelDataLength];
frame.CopyPixelDataTo(pixelData);
this._ColorImageBitmap.WritePixels(this._ColorImageBitmapRect, pixelData, this._ColorImageStride, 0);
}
}
}
private void Kinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
using (SkeletonFrame frame = e.OpenSkeletonFrame())
{
if (frame != null)
{
frame.CopySkeletonDataTo(this.FrameSkeletons);
Skeleton skeleton = GetPrimarySkeleton(this.FrameSkeletons);
if (skeleton == null)
{
kinectButton.Visibility = Visibility.Collapsed;
}
else
{
Joint primaryHand = GetPrimaryHand(skeleton);
TrackHand(primaryHand);
}
}
}
}
//track and display hand
private void TrackHand(Joint hand)
{
if (hand.TrackingState == JointTrackingState.NotTracked)
{
kinectButton.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
kinectButton.Visibility = System.Windows.Visibility.Visible;
DepthImagePoint point = this.Kinect.MapSkeletonPointToDepth(hand.Position, DepthImageFormat.Resolution640x480Fps30);
handX = (int)((point.X * LayoutRoot.ActualWidth / this.Kinect.DepthStream.FrameWidth) - (kinectButton.ActualWidth / 2.0));
handY = (int)((point.Y * LayoutRoot.ActualHeight / this.Kinect.DepthStream.FrameHeight) - (kinectButton.ActualHeight / 2.0));
handX2 = (int)((point.X * LayoutRoot.ActualWidth / this.Kinect.DepthStream.FrameWidth) - (kinectButton2.ActualWidth / 2.0));
handY2 = (int)((point.Y * LayoutRoot.ActualHeight / this.Kinect.DepthStream.FrameHeight) - (kinectButton2.ActualHeight / 2.0));
Canvas.SetLeft(kinectButton, handX);
Canvas.SetTop(kinectButton, handY);
Canvas.SetLeft(kinectButton2, handX2);
Canvas.SetTop(kinectButton2, handY2);
if (isHandOver(kinectButton, buttons)) kinectButton.Hovering();
else kinectButton.Release();
kinectButton.ImageSource = "/Images/RightHand.png";
kinectButton.ActiveImageSource = "/Images/RightHand.png";
kinectButton.ImageSource = "/Images/LeftHand.png";
kinectButton.ActiveImageSource = "/Images/LeftHand.png";
}
}
//detect if hand is overlapping over any button
private bool isHandOver(FrameworkElement hand, List<Button> buttonslist)
{
var handTopLeft = new Point(Canvas.GetLeft(hand), Canvas.GetTop(hand));
var handX = handTopLeft.X + hand.ActualWidth / 2;
var handY = handTopLeft.Y + hand.ActualHeight / 2;
foreach (Button target in buttonslist)
{
Point targetTopLeft = new Point(Canvas.GetLeft(target), Canvas.GetTop(target));
if (handX > targetTopLeft.X &&
handX < targetTopLeft.X + target.Width &&
handY > targetTopLeft.Y &&
handY < targetTopLeft.Y + target.Height)
{
selected = target;
return true;
}
}
return false;
}
//get the hand closest to the Kinect sensor
private static Joint GetPrimaryHand(Skeleton skeleton)
{
Joint primaryHand = new Joint();
if (skeleton != null)
{
primaryHand = skeleton.Joints[JointType.HandLeft];
Joint rightHand = skeleton.Joints[JointType.HandRight];
if (rightHand.TrackingState != JointTrackingState.NotTracked)
{
if (primaryHand.TrackingState == JointTrackingState.NotTracked)
{
primaryHand = rightHand;
}
else
{
if (primaryHand.Position.Z > rightHand.Position.Z)
{
primaryHand = rightHand;
}
}
}
}
return primaryHand;
}
//get the skeleton closest to the Kinect sensor
private static Skeleton GetPrimarySkeleton(Skeleton[] skeletons)
{
Skeleton skeleton = null;
if (skeletons != null)
{
for (int i = 0; i < skeletons.Length; i++)
{
if (skeletons[i].TrackingState == SkeletonTrackingState.Tracked)
{
if (skeleton == null)
{
skeleton = skeletons[i];
}
else
{
if (skeleton.Position.Z > skeletons[i].Position.Z)
{
skeleton = skeletons[i];
}
}
}
}
}
return skeleton;
}
void newButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Yay");
}
void kinectButton_Click(object sender, RoutedEventArgs e)
{
selected.RaiseEvent(new RoutedEventArgs(Button.ClickEvent, selected));
}
private void newBtn_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("hey");
}
private void button2_Click(object sender, RoutedEventArgs e)
{
message.Content = "Button 2 clicked!";
Window w = new Window();
Button b = new Button();
b.Content = "Button A";
canvas.Children.Add(newBtn);
// stackPanel1.Children.Add(newBtn);
stackPanel1.Children.Remove((UIElement)this.FindName("Button0"));
}
}
}
The follow code would recreate your first button in code:
Button btn = new Button
{
Name = "button1",
Content = "Button1",
Height = 152,
Width = 320,
Foreground = "#FF64C02F",
FontSize = 53.333,
BorderThickness = 4
};
btn.Click += button1_Click;
canvas.Children.Add(btn);
Canvas.SetLeft(btn, 128);
Canvas.SetTop(btn, 162);
That said... it is not preferable that you do this in code. If you have 20 buttons (as you mention in your question comments) you should create them in XAML.
If you truly want to deal with them strictly in code, you should set up an MVVM-style application. These types of models can be very robust, but are complex to learn at first.
Related
Want the background of grid be changed every half second with provided r,g,b values and still be able to interact with the buttons inside the grid.
Problem is that neither the background is changing on regualr interval nor the user is allowed to interact with the two buttons inside grid. The color of grid changes only when I switch to any other application or task manager. How to acheive?
<Grid Name="MainGrid" Height="{Binding ElementName=MainWindow1, Path=ActualHeight}" Loaded="MainGrid_Loaded">
<StackPanel VerticalAlignment="Center">
<Label Height="50" Name="xCoordinate" />
<Label Height="50" Name="yCoordinate" />
<StackPanel HorizontalAlignment="Center" Height="50" Orientation="Horizontal">
<Button Content="Red" Width="100" Name="xBtn" Click="xBtn_Click" Margin="0,0,10,0"/>
<Button Content="Blue" Width="100" Name="yBtn" Click="yBtn_Click" />
</StackPanel>
</StackPanel>
</Grid>
public partial class MainWindow : Window
{
Thread thread;
public MainWindow()
{
InitializeComponent();
thread = new Thread(new ThreadStart(ChangeGridColor));
thread.Start();
}
private void xBtn_Click(object sender, RoutedEventArgs e)
{
xCoordinate.Background = new SolidColorBrush(Colors.Red);
}
private void yBtn_Click(object sender, RoutedEventArgs e)
{
yCoordinate.Background = new SolidColorBrush(Colors.Blue);
}
private void MainWindow_MouseMove(object sender, MouseEventArgs e)
{
Point point = Mouse.GetPosition(Application.Current.MainWindow);
xCoordinate.Content = point.X;
yCoordinate.Content = point.Y;
}
byte r=0,g=0,b = 0;
public void ChangeGridColor()
{
while (true)
{
this.Dispatcher.Invoke((Action)(() =>
{//this refer to form in WPF application
MainGrid.Background = new SolidColorBrush(Color.FromRgb(r, g, b));
r += 1;
g += 1;
b += 1;
}));
Thread.Sleep(1000);
}
}
}
You don't need a second thread just to sleep. You could use a timer. A timer calls a callback in a given interval.
public partial class MainWindow : Window
{
private readonly System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer()
public MainWindow()
{
InitializeComponent();
_timer.Interval = 500; // 500ms.
_timer.Elapsed += ElapsedCallback;
_timer.Start();
}
private void ElapsedCallback(object sender, EventArgs e)
{
MainGrid.Background = new SolidColorBrush(Color.FromRgb(r, g, b));
r = ++r % 0xFF; // prevent overlfow.
g = ++g % 0xFF;
b = ++g % 0xFF;
}
private void xBtn_Click(object sender, RoutedEventArgs e)
{
xCoordinate.Background = new SolidColorBrush(Colors.Red);
}
private void yBtn_Click(object sender, RoutedEventArgs e)
{
yCoordinate.Background = new SolidColorBrush(Colors.Blue);
}
private void MainWindow_MouseMove(object sender, MouseEventArgs e)
{
Point point = Mouse.GetPosition(Application.Current.MainWindow);
xCoordinate.Content = point.X;
yCoordinate.Content = point.Y;
}
}
Your code should behave as you would expect if you execute your loop on a background thread:
byte r = 0, g = 0, b = 0;
public void ChangeGridColor()
{
Task.Run(() =>
{
while (true)
{
this.Dispatcher.Invoke((Action)(() =>
{
MainGrid.Background = new SolidColorBrush(Color.FromRgb(r, g, b));
}));
r += 1;
g += 1;
b += 1;
Thread.Sleep(1000);
}
});
}
I'm trying to make a simple clock and it isn't working because sin and cos give negative values which wpf doesnt have!
namespace Desenho
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
[DllImport("Kernel32")]
public static extern void AllocConsole();
[DllImport("Kernel32")]
public static extern void FreeConsole();
private Timer timer1;
int i = 0;
public MainWindow()
{
InitializeComponent();
AllocConsole();
Console.Clear();
InitTimer();
}
public void InitTimer()
{
timer1 = new Timer();
timer1.Elapsed += new System.Timers.ElapsedEventHandler(timer1_Tick);
timer1.Interval = 1; // in miliseconds
timer1.Start();
/*for(int a = 0; a>-200; a--)
{
Console.WriteLine("Angulo: {0}; Cos: {1}; Sen: {2}; Rad: {3};", a, Math.Cos(toRad(a)), Math.Sin(toRad(a)), toRad(a));
}*/
}
private void timer1_Tick(object sender, EventArgs e)
{
/*if(i == 5)
{
i = 0;
}
mudaCores(i);
i++;*/
float x = 100+(float)Math.Cos(toRad(i));
float y = 100+(float)Math.Sin(toRad(i));
desenhaLinha(x, y);
i--;
Console.WriteLine("Angulo: {0}; X: {1}; Y: {2};", i, x, y);
}
private async void desenhaLinha(float x, float y)
{
await Task.Run(() =>
{
panel.Dispatcher.BeginInvoke((Action)(() => panel.linhas.Add(new Cordenadas(new Pen(new SolidColorBrush(Colors.Red), 2), new Point(100, 100), new Point(x, y)))));
panel.Dispatcher.BeginInvoke((Action)(() => panel.InvalidateVisual()));
});
}
private async void mudaCores(int i)
{
Color[] cores = new Color[] { Colors.Red, Colors.Black, Colors.Blue, Colors.Green, Colors.Yellow, Colors.Violet };
try
{
await Task.Run(() => { panel.Dispatcher.BeginInvoke((Action)(() => panel.color = cores[i]));
panel.Dispatcher.BeginInvoke((Action)(() => panel.InvalidateVisual()));
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
double toRad(double graus)
{
double rad;
rad = graus * Math.PI / 180;
return rad;
}
}
}
My drawLine func only takes one point as argument cause it assumes the center one as 100, 100. How can i make this work?
IMO you should throw away all your code and write a MVVM application with separation between view and view model.
First create a view model for your clock, e.g. with three angle properties for the hour, minute and second hand of your clock, and a DispatcherTimer that updates these properties from the current time (DateTime.Now) in the UI thread.
public class ClockViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ClockViewModel()
{
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
timer.Tick += TimerTick;
timer.Start();
}
private void TimerTick(object sender, EventArgs e)
{
var t = DateTime.Now.TimeOfDay;
HoursAngle = t.TotalHours * 30 % 360; // fractional hours
MinutesAngle = t.Minutes * 6; // full minutes
SecondsAngle = t.Seconds * 6; // full seconds
}
private double hoursAngle;
public double HoursAngle
{
get { return hoursAngle; }
set
{
hoursAngle = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(HoursAngle)));
}
}
private double minutesAngle;
public double MinutesAngle
{
get { return minutesAngle; }
set
{
minutesAngle = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(MinutesAngle)));
}
}
private double secondsAngle;
public double SecondsAngle
{
get { return secondsAngle; }
set
{
secondsAngle = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SecondsAngle)));
}
}
}
Then create a view with three Lines for the clock hands, each of which has a RotateTransform with a data-bound Angle property. The view shown below puts the clock in the upper left corner of the bottom right cell of a 2xx Grid, in order to center the Lines in the Grid.
The DataContext of the Window is set to an instance of the view model.
<Window.DataContext>
<local:ClockViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Canvas Grid.Column="1" Grid.Row="1">
<Line Y2="-50"
Stroke="Black" StrokeThickness="9"
StrokeStartLineCap="Round" StrokeEndLineCap="Triangle">
<Line.RenderTransform>
<RotateTransform Angle="{Binding HoursAngle}"/>
</Line.RenderTransform>
</Line>
<Line Y2="-100"
Stroke="Gray" StrokeThickness="7"
StrokeStartLineCap="Round" StrokeEndLineCap="Triangle">
<Line.RenderTransform>
<RotateTransform Angle="{Binding MinutesAngle}"/>
</Line.RenderTransform>
</Line>
<Line Y2="-100"
Stroke="Red" StrokeThickness="3"
StrokeStartLineCap="Round" StrokeEndLineCap="Triangle">
<Line.RenderTransform>
<RotateTransform Angle="{Binding SecondsAngle}"/>
</Line.RenderTransform>
</Line>
</Canvas>
</Grid>
My problem is the drag and drop of border become not workin when I wrap the canvas inside the scrollviewer.
1200x1200 canvas put in the 500x500 scrollviewer. I want the canvas is scrollable (touch slide) in the scrollviewer. The drag and drop in canvas is working perfectly before I insert in the scrollviewer.
MainWindow.xaml
<Grid ClipToBounds="True">
<ScrollViewer Name="scbb" Width="500" Height="500" HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" HorizontalAlignment="Left"
VerticalAlignment="Top" Background="LightBlue" ScrollViewer.CanContentScroll="True">
<Canvas x:Name="canvas" Width="1200" Height="1200"
MouseLeftButtonDown="CanvasMouseLeftButtonDown"
MouseLeftButtonUp="CanvasMouseLeftButtonUp"
MouseMove="CanvasMouseMove">
</Canvas>
</ScrollViewer>
<Button x:Name="btnEnable" Content="Enable" Height="50" Width="50" Margin="0,0,115,10" VerticalAlignment="Bottom"
HorizontalAlignment="Right" Click="btnEnable_Click"/>
<Button Content="Add Image" Width="100" Height="100" VerticalAlignment="Bottom"
HorizontalAlignment="Right" Click="AddButtonClick" Margin="0,0,10,10"/>
</Grid>
MainWindow.xaml.cs
private void AddButtonClick(object sender, RoutedEventArgs e)
{
int iNum = SETTING.GetTableRunningNumber();
var borderWrap = new Border();
borderWrap.Width = 100;
borderWrap.Height = 100;
borderWrap.Background = Brushes.Green;
var label1 = new Label();
label1.VerticalAlignment = VerticalAlignment.Center;
label1.HorizontalAlignment = HorizontalAlignment.Center;
label1.Content = "Table " + iNum.ToString();
label1.Name = "Table" + iNum.ToString();
label1.Foreground = Brushes.White;
label1.FontWeight = FontWeights.Bold;
borderWrap.Child = label1;
borderWrap.MouseDown += TableMouseDown;
Canvas.SetLeft(borderWrap, 10);
Canvas.SetTop(borderWrap, 10);
canvas.Children.Add(borderWrap);
iNum += 1;
SETTING.SetTableRunningNumber(iNum);
}
private Point mousePosition;
private Border draggedBorder;
private void TableMouseDown(object sender, MouseButtonEventArgs e)
{
if (!SETTING.GetEnableDrag())
{
var cLabel = e.Source as Label;
var bBorder = e.Source as Border;
if (cLabel != null)
{
MessageBox.Show(cLabel.Name.ToString());
}
}
}
private void CanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (SETTING.GetEnableDrag())
{
var dBorder = e.Source as Border;
var cLabel = e.Source as Label;
if (dBorder == null)
{
dBorder = (Border)cLabel.Parent;
}
if (dBorder != null && canvas.CaptureMouse())
{
mousePosition = e.GetPosition(canvas);
draggedBorder = dBorder;
Panel.SetZIndex(draggedBorder, 1);
}
}
}
async Task PutTaskDelay100()
{
await Task.Delay(100);
}
private async void CanvasMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (SETTING.GetEnableDrag())
{
await PutTaskDelay100();
if (draggedBorder != null)
{
canvas.ReleaseMouseCapture();
Panel.SetZIndex(draggedBorder, 0);
draggedBorder = null;
}
}
}
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
if (SETTING.GetEnableDrag())
{
double canvasSize = 1200;
if (draggedBorder != null)
{
var position = e.GetPosition(canvas);
var offset = position - mousePosition;
mousePosition = position;
double newTop = Canvas.GetTop(draggedBorder) + offset.Y;
double newLeft = Canvas.GetLeft(draggedBorder) + offset.X;
if (newTop < 0)
{
newTop = 0;
}
else if (newTop + draggedBorder.ActualWidth > canvasSize)
newTop = canvasSize - draggedBorder.ActualWidth;
if (newLeft < 0)
{
newLeft = 0;
}
else if (newLeft + draggedBorder.ActualWidth > canvasSize)
newLeft = canvasSize - draggedBorder.ActualWidth;
Canvas.SetLeft(draggedBorder, newLeft);
Canvas.SetTop(draggedBorder, newTop);
}
}
}
I had the problem months ago and solved it in the code behind with these additions.
After the InitializeComponent in the WindowMain.xaml.cs add:
public WindowMain()
{
InitializeComponent();
// your code, etc.
scrollViewer.AllowDrop = true;
scrollViewer.PreviewDragEnter += scrollViewer_PreviewDragEnter;
scrollViewer.PreviewDragOver += scrollViewer_PreviewDragOver;
scrollViewer.Drop += scrollViewer_Drop;
}
void scrollViewer_PreviewDragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Copy;
}
else
{
e.Effects = DragDropEffects.None;
}
}
void scrollViewer_PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
bool IsDataAvailable = false;
void scrollViewer_Drop(object sender, DragEventArgs e)
{
// Get data object
var dataObject = e.Data as DataObject;
// Check for file list
if (dataObject.ContainsFileDropList())
{
// Process file names
StringCollection fileNames = dataObject.GetFileDropList();
bool isIsDataAvailable = false;
try
{
var uri = new Uri(fileNames[0]);
BitmapSource imageSource = new BitmapImage(uri);
isIsDataAvailable = true;
}
catch (Exception error)
{
string errorMessage = error.ToString();
}
finally
{
IsDataAvailable = isIsDataAvailable;
}
}
}
My program just loads a dropped file from the explorer file list I drop on the ScrollViewer . The scbb.AllowDrop=true in the ScrollViewer has to be set. And the routines to handle basic drag and drop. Your code should work once the ScrollViewer allows dropping.
I want to move the label by using the Mouse_Move/Mouse_Down events.
I tried to do it like this:
private void control_MouseDown(object sender, MouseButtonEventArgs e)
{
Label l = e.Source as Label;
if (l != null)
{
l.CaptureMouse();
moving = true;
PositionInLabel = e.GetPosition(l);
}
}
private void control_MouseMove(object sender, MouseEventArgs e)
{
if (moving)
{
Point p = e.GetPosition(null);
DeltaX = p.X - BasePoint.X - PositionInLabel.X;
DeltaY = p.Y - BasePoint.Y - PositionInLabel.Y;
RaisePropertyChanged("XPosition");
RaisePropertyChanged("YPosition");
}
}
Suppose your label is on a Canvas:
public MainWindow()
{
InitializeComponent();
this.label.MouseLeftButtonDown += control_MouseLeftButtonDown;
this.label.MouseMove += control_MouseMove;
this.label.MouseLeftButtonUp += control_MouseLeftButtonUp;
}
// Keep track of the Canvas where this element is placed.
private Canvas canvas;
// Keep track of when the element is being dragged.
private bool isDragging = false;
// When the element is clicked, record the exact position
// where the click is made.
private Point mouseOffset;
private void control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Label l = e.Source as Label;
if (l == null)
return;
// Find the Canvas.
if (canvas == null)
canvas = (Canvas)VisualTreeHelper.GetParent(l);
// Dragging mode begins.
isDragging = true;
// Get the position of the click relative to the element
// (so the top-left corner of the element is (0,0).
mouseOffset = e.GetPosition(l);
// Capture the mouse. This way you'll keep receiving
// the MouseMove event even if the user jerks the mouse
// off the element.
l.CaptureMouse();
}
private void control_MouseMove(object sender, MouseEventArgs e)
{
Label l = e.Source as Label;
if (l == null)
return;
if (isDragging)
{
// Get the position of the element relative to the Canvas.
Point point = e.GetPosition(canvas);
// Move the element.
l.SetValue(Canvas.TopProperty, point.Y - mouseOffset.Y);
l.SetValue(Canvas.LeftProperty, point.X - mouseOffset.X);
}
}
private void control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Label l = e.Source as Label;
if (l == null)
return;
if (isDragging)
{
l.ReleaseMouseCapture();
isDragging = false;
}
}
Let containerCanvas be a Canvas which contains the Label; then you can utilize the _MouseMove(object sender, MouseEventArgs e) to move the label along with your mouse pointer:
Xaml code:
<Canvas Name="containerCanvas" MouseMove="containerCanvas_MouseMove" Background="Aqua" Width="525" Height="350" >
<Label Name="floatingLabel" Height="28" Width="161" Background="AliceBlue"></Label>
</Canvas>
C# code:
private void containerCanvas_MouseMove(object sender, MouseEventArgs e)
{
Point p = Mouse.GetPosition(Application.Current.MainWindow);
floatingLabel.Margin = new Thickness(p.X, p.Y, 0, 0);
}
I have done a sample code. Check this. It should work.
XAML :
<UserControl HorizontalAlignment="Left" x:Class="WPFDiagramDesignerControl.Components.TestApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="100" Width="100" IsEnabled="True">
<Grid >
<Canvas x:Name="MyDesigner">
<TextBox x:Name="txtBox" IsEnabled="True" Background="AntiqueWhite" Margin="10,10,10,10" TextWrapping="Wrap"> </TextBox>
</Canvas>
</Grid>
Code Behind :
public TestApp()
{
InitializeComponent();
txtBox.MouseDoubleClick+=new MouseButtonEventHandler(control_MouseDoubleClick);
txtBox.MouseMove+=new MouseEventHandler(control_MouseMove);
txtBox.PreviewMouseDown+=new MouseButtonEventHandler(control_PreviewMouseDown);
txtBox.PreviewMouseUp+=new MouseButtonEventHandler(control_PreviewMouseUp);
txtBox.Cursor = Cursors.SizeAll;
}
private void control_MouseMove(object sender, RoutedEventArgs e)
{
if (isClicked)
{
Point mousePos = Mouse.GetPosition(parentCanvas);
parentItem = this.Parent as DesignerItem;
parentCanvas = parentItem.Parent as DesignerCanvas;
Point relativePosition = Mouse.GetPosition(parentCanvas);
DesignerCanvas.SetLeft(this, DesignerCanvas.GetLeft(this) - (startPoint.X - mousePos.X));
DesignerCanvas.SetTop(this, DesignerCanvas.GetTop(this) - (startPoint.Y - mousePos.Y));
}
}
private void control_PreviewMouseDown(object sender, RoutedEventArgs e)
{
if (!isClicked)
{
isClicked = true;
parentItem = this.Parent as DesignerItem;
parentCanvas = parentItem.Parent as DesignerCanvas;
startPoint = Mouse.GetPosition(parentCanvas);
}
}
private void control_PreviewMouseUp(object sender, RoutedEventArgs e)
{
isClicked = false;
}
I'm trying to move a rectangle drawn in Canvas by mouse (drag and drop).
When I click on the Rectangle and move the mouse (keep clicking) to a new position it's OK:
But when I click on it again and move the mouse just a bit the Rectangle move to old position:
I don't know why it is. Can anybody give me some explaination?
Here is my code (all parameters have been assigned and initialized).
Thanks in advance!
private void MyCanvas_MouseMove_1(object sender, MouseEventArgs e)
{
if (e.RightButton == MouseButtonState.Pressed && e.OriginalSource is Shape)
{
p2 = e.GetPosition(MyCanvas);
TranslateTransform tf = new TranslateTransform(p2.X - p1.X, p2.Y - p1.Y);
_MyTestRect.RenderTransform = tf;
}
}
private void MyCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is Shape)
{
p1 = e.GetPosition(MyCanvas);
}
}
Mvvm Behavior Approach add the refference to:
System.Windows.Interactivity.
In XAML:
<Canvas x:Name="MyCanvas" Background="#00FFFFFF">
<i:Interaction.Behaviors>
<!--soSandBox is the reffrence to the behavior-->
<soSandBox:MoveElementsInCanvasBehavior/>
</i:Interaction.Behaviors>
<Rectangle x:Name="_MyTestRect1" Fill="Tomato" Width="50" Height="50"/>
<Rectangle x:Name="_MyTestRect2" Fill="Tomato" Width="50" Height="50"/>
<Rectangle x:Name="_MyTestRect3" Fill="Tomato" Width="50" Height="50"/></Canvas>
Behavior code:
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseLeftButtonDown += AssociatedObjectOnMouseLeftButtonDown;
AssociatedObject.MouseLeftButtonUp += AssociatedObjectOnMouseLeftButtonUp;
AssociatedObject.MouseMove += AssociatedObjectOnMouseMove;
}
private void AssociatedObjectOnMouseMove(object sender, MouseEventArgs e)
{
if (_shape != null && _shape.IsMouseCaptured)
{
_p2 = e.GetPosition(AssociatedObject);
_shape.SetValue(Canvas.TopProperty, _p2.Y - _originalOffsetFromTop);
_shape.SetValue(Canvas.LeftProperty, _p2.X - _originalOffsetFromLeft);
}
}
private void AssociatedObjectOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_shape == null) return;
_shape.ReleaseMouseCapture();
_shape = null;
}
private double GetDist(double originalValue, double newValue)
{
if (double.IsNaN(originalValue) || double.IsInfinity(originalValue))
return Math.Abs(newValue - 0);
return Math.Abs(newValue - originalValue);
}
private void AssociatedObjectOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_shape = e.OriginalSource as Shape;
if (_shape != null)
{
_p1 = e.GetPosition(AssociatedObject);
_shape.CaptureMouse();
_originalOffsetFromTop = GetDist((double)_shape.GetValue(Canvas.TopProperty), _p1.Y);
_originalOffsetFromLeft = GetDist((double)_shape.GetValue(Canvas.LeftProperty), _p1.X);
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseLeftButtonDown -= AssociatedObjectOnMouseLeftButtonDown;
AssociatedObject.MouseLeftButtonUp -= AssociatedObjectOnMouseLeftButtonUp;
AssociatedObject.MouseMove -= AssociatedObjectOnMouseMove;
}
Summary:
This way you can put inside the canvas as much elements as you wish, and when you click the element (and when this element is a shape) you will be able to move it.
Translate Transform XAML:
<Canvas x:Name="MyCanvas" Background="#00FFFFFF">
<i:Interaction.Behaviors>
<!--convert event to command mechanism-->
<soSandBox:CanvasMouseEventObservingBehavior
OnMouseMoveAction="{Binding OnMouseMoveAction, UpdateSourceTrigger=PropertyChanged}"
OnMouseRightButtonDownAction="{Binding OnMouseRightButtonDownAction, UpdateSourceTrigger=PropertyChanged}"
OnMouseRightButtonUpAction="{Binding OnMouseRightButtonUpAction, UpdateSourceTrigger=PropertyChanged}"/>
</i:Interaction.Behaviors>
<Rectangle x:Name="_MyTestRect1" Fill="Tomato" Width="50" Height="50">
<Rectangle.RenderTransform>
<TransformGroup>
<TranslateTransform X="{Binding TranslateTransformX, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Y="{Binding TranslateTransformY, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TranslateTransform>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle></Canvas>
Translate Transform view model (translation related code):
private void RightMouseUp(Point obj)
{
_originalPoint = obj;
_currentOffsetByX = GetDist(_currentOffsetByX, obj.X);
_currentOffsetByY = GetDist(_currentOffsetByY, obj.Y);
}
private void RightMouseDown(Point obj)
{
_originalPoint = obj;
_currentOffsetByX = GetDist(_currentOffsetByX, obj.X);
_currentOffsetByY = GetDist(_currentOffsetByY, obj.Y);
}
private void MouseMoved(Point obj)
{
TranslateTransformX = obj.X - _currentOffsetByX;
TranslateTransformY = obj.Y - _currentOffsetByY;
}
private double GetDist(double originalValue, double newValue)
{
if (double.IsNaN(originalValue) || double.IsInfinity(originalValue))
return Math.Abs(newValue - 0);
return Math.Abs(newValue - originalValue);
}
public double TranslateTransformY
{
get { return _translateTransformY; }
set
{
_translateTransformY = value;
OnPropertyChanged();
}
}
public double TranslateTransformX
{
get { return _translateTransformX; }
set
{
_translateTransformX = value;
OnPropertyChanged();
}
}
Regards
You should not create the transform every time otherwise you reset the translation.
Create a TranslateTransform if none exist.
Then cumulate the translations inside.
Regards
Try just using the Canvas.SetTop and Canvas.SetLeft.
Rectangle myRectangle = new Rectangle();
myRectangle += myRectangle_MouseLeftButtonDown;
void myRectangle_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
myRectangle.MouseMove += origin_MouseMove;
}
void myRectangle_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
Point p = new Point()
{
// Just round the coordinates
X = Math.Round(e.GetPosition(canvas).X, 0, MidpointRounding.AwayFromZero),
Y = Math.Round(e.GetPosition(canvas).Y, 0, MidpointRounding.AwayFromZero),
};
Canvas.SetTop(myRectangle, p.Y);
Canvas.SetLeft(myRectangle, p.X);
myRectangle.MouseLeftButtonUp += myRectangle_MouseLeftButtonUp;
}
void myRectangle_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
myRectangle.MouseMove -= origin_MouseMove;
myRectangle.MouseLeftButtonUp -= origin_MouseLeftButtonUp;
}