I'm new to Silverlight and I'm working on drawing a rectangle on the screen in the run time,
I have a class ( SelectionBox ), and on the main page, I have a canvas,
when clicking on that Canvas I have the following function called
private void BeginDrawing(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_initXPos = e.GetPosition(drawingArea).X;
_initYPos = e.GetPosition(drawingArea).Y;
_selectionBox = new SelectionBox ();
drawingArea.Children.Add(_selectionBox);
_isDrawing = true;
}
and that Handler has been added through blend Events (MouseLeftButtonDown)
when the mouse is moving, the following method is called, also added through Blend events,
private void UpdateSelectionBox(object sender, System.Windows.Input.MouseEventArgs e)
{
if(!_isDrawing &&)
return;
double rectWidth, rectHeight, rectXPos, rectYPos;
if (e.GetPosition(drawingArea).X >= _initXPos)
{
rectWidth = e.GetPosition(drawingArea).X - _initXPos;
rectXPos = _initXPos;
}
else
{
rectWidth = _initXPos - e.GetPosition(drawingArea).X;
rectXPos = e.GetPosition(drawingArea).X;
}
if (e.GetPosition(drawingArea).Y >= _initYPos)
{
rectHeight = e.GetPosition(drawingArea).Y - _initYPos;
rectYPos = _initYPos;
}
else
{
rectHeight = _initYPos - e.GetPosition(drawingArea).Y;
rectYPos = e.GetPosition(drawingArea).Y;
}
_selectionBox.Width = Math.Abs(rectWidth - 20);
_selectionBox.Height = Math.Abs(rectHeight - 20);
Canvas.SetLeft(_selectionBox, Math.Abs(rectXPos - 20));
Canvas.SetTop(_selectionBox, Math.Abs(rectYPos - 20));
}
when the mouseLeftButtonUp is triggered, the following Handler should work,
private void StopDrawingAndSelect(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_isDrawing = false;
drawingArea.Children.Remove(_selectionBox);
}
but unfortunately it never triggers, I put a break point and try to debug it but it never reaches,, I'm not sure Why, this is the XAML for the SelectionBox class
<UserControl
...
<Grid x:Name="LayoutRoot">
<Rectangle Fill="#00F4F4F5" Stroke="Black" StrokeDashArray="1 2"/>
</Grid>
</UserControl>
And this is the XAML of the MainPage
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SelectionBoxTraining"
x:Class="SelectionBoxTraining.MainPage"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" Background="White">
<Canvas Margin="165,0,0,0" x:Name="drawingArea" MouseLeftButtonDown="BeginDrawing" Background="#FF959FD6" MouseMove="UpdateSelectionBox" MouseLeftButtonUp="StopDrawingAndSelect" />
</Grid>
</UserControl>
Please can anybody help me? I tried a lot of things to do but that didn't work
I hope I find help here,
NOTE: The properties are not included in the XAML code upthere to save space,
Thank You.
I would guess that the reason is that your rectangle is getting the UP event and (somehow?) not bubbling it up to the canvas. So try to set ishitestvisible to false:
private void BeginDrawing(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_initXPos = e.GetPosition(drawingArea).X;
_initYPos = e.GetPosition(drawingArea).Y;
_selectionBox = new SelectionBox ();
_selectionBox.IsHitTestVisible = false;
drawingArea.Children.Add(_selectionBox);
_isDrawing = true;
}
`
It's now working, even though I'm not convinced of the answer,
but I changed the OnMouseUp Function by double-click on Blend's events place for MouseUp, and it became
private void drawingArea_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_isDrawing = false;
drawingArea.Children.Remove(_selectionBox);
}
does anyone has an explanation for what caused the problem? does the name of the handler has that effect? I Wonder..
Related
I have a project that uses a System.Windows.Controls.Primatives.Popup to drag a 'tooltip' like control along with a mouse.
Whenever the drag crosses a horizontal line the popup 'wraps' to the bottom of the screen - despite having sane values for the VerticalOffset. The point at which this wrapping occurs appears to be tied to the HEIGHT of the window, but not it's position.
Here's the code from the sandbox project I have created that also exhibits the same behavior:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MainGrid.MouseDown += Grid_MouseDown;
this.MainGrid.MouseUp += Grid_MouseUp;
this.MainGrid.MouseMove += (s, e) => { if (this.Popup.IsOpen) { Popup_Drag(s, e); } };
this.Popup.MouseMove += Popup_Drag;
}
private void Popup_Drag(object sender, MouseEventArgs e)
{
Popup.HorizontalOffset = e.GetPosition(this.Popup).X;
Popup.VerticalOffset = e.GetPosition(this.Popup).Y;
this.Status_Top.Text = String.Format("Height/Top: {0}/{1} Width/Left: {2}/{3}", this.Height, this.Top, this.Width, this.Left);
this.Status.Text = String.Format("Vertical Offset: {0} Horizontal Offset: {1}", Popup.VerticalOffset, Popup.HorizontalOffset);
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
this.Popup.IsOpen = false;
}
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
this.Popup.IsOpen = true;
Popup_Drag(sender, e);
}
}
And the Window XAML:
<Window x:Class="WpfSandbox.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 x:Name="MainGrid" Background="Purple">
<TextBlock x:Name="Status_Top"></TextBlock>
<Popup x:Name="Popup" Cursor="Hand" HorizontalAlignment="Left"
VerticalAlignment="Bottom" IsOpen="True">
<TextBlock Background="Blue" Foreground="White">
<TextBlock x:Name="Status">TEXT</TextBlock></TextBlock>
</Popup>
</Grid>
</Window>
I was able to fix this by adding Placement="RelativePoint" to the Popup attributes. Apparently this is the default in Silverlight, but not WPF.
I've seen answers to the question of how to break out of a while loop with a keypress for a console app and a winforms app but not a WPF app. So, uh, how do you do it? Thanks.
Okay, let's elaborate:
Something like this doesn't work in a WPF (non-console) app. It throws a runtime error:
while(!Console.KeyAvailable)
{
//do work
}
MainWindow.xaml:
<Window x:Class="WpfApplication34.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350">
<Grid>
<TextBlock x:Name="tb" />
</Grid>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow:Window {
private int _someVal = 0;
private readonly CancellationTokenSource cts = new CancellationTokenSource();
public MainWindow() {
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object sender, RoutedEventArgs routedEventArgs) {
KeyDown += OnKeyDown;
while (!cts.IsCancellationRequested) {
await Task.Delay(1000); // Some Long Task
tb.Text = (++_someVal).ToString();
}
}
private void OnKeyDown(object sender, KeyEventArgs keyEventArgs) {
if (keyEventArgs.Key == Key.A)
cts.Cancel();
}
}
It's just a rough demo, just take the concept. The only thing specific to WPF here is the manner of capturing the key-press. Everything else relating to breaking the while loop is the same across a console app or wpf or winforms.
You can create na event in the KeyDown Event in the MainWindow and get the KeyEventArgs e to know what key was pressed.
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.A)
{
// set a flag to break the loop
}
}
Here is a setup: I have a textbox with a numberic value. According to the requirements every time anybody changes that value an accompanying comment needs to be provided. So visually there must be another textbox for the comment that should be displayed right next to the first one. Ideally the comment textbox needs to be placed in a callout that originates from the value textbox and displayed on the right from it overlaying anything what's underneath of it just like on this picture:
I know how to do easily it in CSS and HTML.
I have to do the same in Silverlight now.
Unfortunately I am not very strong in it, so what I am specifically asking about is how having 2 textboxes make one of them appear next to another (on the right overlaying whatever controls are underneath it) with as less XAML and code as possible.
Use a ToolTip, and set the Placement such that it appears to the right. in XAML, you can template your ToolTip to look however you want, even if that means mimicking the TextBox appearance.
This is the purpose of the ToolTip, and I feel strongly that you should always use the right tool for the right job. :)
I hope this helps. Let us know if you need code samples.
EDIT: Added the following code samples:
<TextBox ToolTipService.Placement="Right">
<ToolTipService.ToolTip>
<TextBox Text="{Binding CalloutText, Mode=OneWay}" IsReadOnly="True"/>
</ToolTipService.ToolTip>
</TextBox>
Ok, I ended up writing my own behaviour
namespace MyNamespace
{
public class CommentBehavior : Behavior<TextBox>
{
private readonly TimeSpan howLongWeWaitBeforePopupCloses = TimeSpan.FromMilliseconds(200);
private DispatcherTimer popupClosingTimer;
public static DependencyProperty PopupProperty = DependencyProperty.Register("Popup", typeof(Popup), typeof(CommentBehavior), new PropertyMetadata(null));
public Popup Popup
{
get { return (Popup)this.GetValue(PopupProperty); }
set { this.SetValue(PopupProperty, value); }
}
protected override void OnAttached()
{
this.popupClosingTimer = new DispatcherTimer();
this.popupClosingTimer.Stop();
this.popupClosingTimer.Interval = howLongWeWaitBeforePopupCloses;
this.popupClosingTimer.Tick += this.ClosePopup;
this.AssociatedObject.GotFocus += this.GotFocus;
this.AssociatedObject.LostFocus += this.LostFocus;
this.Popup.Child.GotFocus += PopupChild_GotFocus;
this.Popup.Child.LostFocus += PopupChild_LostFocus;
}
private void PopupChild_LostFocus(object sender, RoutedEventArgs e)
{
this.popupClosingTimer.Start();
}
private void PopupChild_GotFocus(object sender, RoutedEventArgs e)
{
this.popupClosingTimer.Stop();
}
protected override void OnDetaching()
{
this.AssociatedObject.GotFocus -= this.GotFocus;
this.AssociatedObject.LostFocus -= this.LostFocus;
this.Popup.GotFocus -= PopupChild_GotFocus;
this.popupClosingTimer.Tick -= this.ClosePopup;
this.popupClosingTimer = null;
}
private void ClosePopup(object sender, EventArgs e)
{
this.Popup.IsOpen = false;
this.popupClosingTimer.Stop();
}
protected void GotFocus(object sender, RoutedEventArgs e)
{
this.popupClosingTimer.Stop();
this.Popup.IsOpen = true;
var at = this.CalculatePopupPosition();
this.Popup.HorizontalOffset = at.X;
this.Popup.VerticalOffset = at.Y;
}
private Point CalculatePopupPosition()
{
var owner = this.AssociatedObject;
var transformation = owner.TransformToVisual(Application.Current.RootVisual);
var at = transformation.Transform(new Point(owner.ActualWidth, 0));
return at;
}
protected void LostFocus(object sender, RoutedEventArgs e)
{
this.popupClosingTimer.Start();
}
}
}
And the following XAML
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="Red">
<TextBox Width="200" Text="0.01">
<i:Interaction.Behaviors>
<local:CommentBehavior>
<local:CommentBehavior.Popup>
<Popup>
<TextBox Text="Comment" />
</Popup>
</local:CommentBehavior.Popup>
</local:CommentBehavior>
</i:Interaction.Behaviors>
</TextBox>
</StackPanel>
</Grid>
I have a Canvas that contains a button which I want to be able to drag and drop into another canvas. I want to copy the button to the other Canvas. Here is the code I am using:
The XAML:
<Window>
<Grid>
<Canvas
Height="300"
Width="500"
Background="Gray">
<Canvas
Name="cnvToolBox"
Canvas.Left="10"
Canvas.Top="10"
Background="AliceBlue"
Width="100"
Height="200">
<Button
Content="Drag Me!"
PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown"
PreviewMouseMove="Button_PreviewMouseMove"></Button>
</Canvas>
<Rectangle
Canvas.Left="119"
Canvas.Top="9"
Width="102"
Height="202"
StrokeDashArray="0.5 1.0 0.3"
Stroke="Black"
StrokeThickness="2"/>
<Canvas
Name="cnvButtonDropZone"
Canvas.Left="120"
Canvas.Top="10"
Width="100"
Height="200"
Background="LightGreen"
AllowDrop="True"
DragEnter="Canvas_DragEnter"
Drop="Canvas_Drop">
</Canvas>
</Canvas>
</Grid>
</Window>
Here's the Code Behind:
public partial class MainWindow : Window
{
private Point startPoint;
public MainWindow()
{
InitializeComponent();
}
private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(null);
}
private void Button_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point currentPosition = e.GetPosition(null);
Vector diff = startPoint - currentPosition;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
Button button = sender as Button;
DataObject dragData = new DataObject("myFormat", button);
DragDrop.DoDragDrop(button, dragData, DragDropEffects.Copy);
}
}
private void Canvas_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("myFormat") || sender == e.Source)
{
e.Effects = DragDropEffects.None;
}
}
private void Canvas_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("myFormat"))
{
Button button = e.Data.GetData("myFormat") as Button;
Canvas canvas = sender as Canvas;
canvas.Children.Add(button);
}
}
}
When I drop the button I get the following exception when I'm adding the button to the canvas:
Specified element is already the logical child of another element. Disconnect it first.
I'm just trying to learn how to drag and drop controls and not really sure what that error means and how to resolve it. I don't know where I'm going wrong. Any suggestions would be welcome.
Thanks!
The button is owned by its parent cnvToolBox. You need to remove it from cnvToolBox before adding it to the canvas.
cnvToolBox.Children.Remove(button);
var canvas = sender as Canvas;
canvas.Children.Add(button);
This moves the button from your toolbox to the canvas. If you actually want to clone the item you want something like:
if (e.Data.GetDataPresent("myFormat"))
{
var contentControl = (ContentControl)e.Data.GetData("myFormat");
var constructorInfo = contentControl.GetType().GetConstructor(new Type[] {});
if (constructorInfo != null)
{
var newElement = (UIElement)constructorInfo.Invoke(new object[]{});
var newContentControl = newElement as ContentControl;
if(newContentControl != null)
{
newContentControl.Content = contentControl.Content;
}
((Panel)sender).Children.Add(newElement);
}
}
It's because the Button already has a parent associated with it; the previous Canvas.
You can set the parent of the Button to null; which will essentially remove it from the logical relationship.
button.Parent = null;
You will then be able to add that Button to another Canvas as you have done in your code behind.
You can also remove the Button from the Children property directly if you prefer and then add it accordingly within the new Canvas.
Canvas.Children.Remove(button);
I'm working with WPF 4 and VB.net 2010. My project consists of full-screen windows with a 640x480 grid in the center.
In this project, I want to have various image boxes (which will have the item .png images in them), that the user can drag around and drop in various places on the grid.
In essence, I need to be able to make it possible for the item to be clicked and dragged around the grid, with the image box still visible and the same size as the user moves it around. It should never be able to leave the grid. I also need to be able to determine if the object is over another object, so when the mouse button is released, the dragged object is "dropped", and it triggers a particular block of code.
How do I do this?
Something similar to your 'project', but using C#
From http://pastie.org/1498237
// XAML
<Window x:Class="MyProject.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="500" Width="500">
<Grid>
<Canvas x:Name="mycanv">
<Image Width="150" x:Name="myimg" Source="some_source.png"/>
</Canvas>
</Grid>
</Window>
//C#
private Point mouseClick;
private double canvasLeft;
private double canvasTop;
public Window1()
{
InitializeComponent();
foreach (object obj in mycanv.Children)
{
try
{
Image img = (Image)obj;
img.PreviewMouseDown += new MouseButtonEventHandler(myimg_MouseDown);
img.PreviewMouseMove += new MouseEventHandler(myimg_MouseMove);
img.PreviewMouseUp += new MouseButtonEventHandler(myimg_MouseUp);
img.TextInput += new TextCompositionEventHandler(myimg_TextInput);
img.LostMouseCapture += new MouseEventHandler(myimg_LostMouseCapture);
img.SetValue(Canvas.LeftProperty, 0.0);
img.SetValue(Canvas.TopProperty, 0.0);
}
catch
{
//do something
}
}
}
void myimg_LostMouseCapture(object sender, MouseEventArgs e)
{
((Image)sender).ReleaseMouseCapture();
}
void myimg_TextInput(object sender, TextCompositionEventArgs e)
{
((Image)sender).ReleaseMouseCapture();
}
void myimg_MouseUp(object sender, MouseButtonEventArgs e)
{
((Image)sender).ReleaseMouseCapture();
}
void myimg_MouseMove(object sender, MouseEventArgs e)
{
if (((Image)sender).IsMouseCaptured)
{
Point mouseCurrent = e.GetPosition(null);
double Left = mouseCurrent.X - canvasLeft;
double Top = mouseCurrent.Y - canvasTop;
((Image)sender).SetValue(Canvas.LeftProperty, canvasLeft + Left);
((Image)sender).SetValue(Canvas.TopProperty, canvasTop + Top);
canvasLeft = Canvas.GetLeft(((Image)sender));
canvasTop = Canvas.GetTop(((Image)sender));
}
}
void myimg_MouseDown(object sender, MouseButtonEventArgs e)
{
mouseClick = e.GetPosition(null);
canvasLeft = Canvas.GetLeft(((Image)sender));
canvasTop = Canvas.GetTop(((Image)sender));
((Image)sender).CaptureMouse();
}