I've created an application that can read an XML file and display the rail network specified within a WPF Canvas.
I've been struggling for a while (months) to get the following functionality working and looking for any clue on how to proceed:
Zoom using mousewheel (zooming in makes the icons and tracks bigger whilst zooming out can let me see all the network).
Panning - Using the middle mouse button to drag the view screen.
I ended up having to disable the mouse wheel as it just moved the scrollviewer. In order for me to scroll currently, I have to use the scrollbar arrows.
Usually I would upload code samples but I've also uploaded everything to GitHub with everything needed in case you need to dive into the code or compile.
https://github.com/Legolash2o/NMViewer
XAML
<ScrollViewer x:Name="svCanvas" Grid.Row="2" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" MouseMove="CCanvas_OnMouseMove" PreviewMouseWheel="SvCanvas_OnPreviewMouseWheel" MouseWheel="SvCanvas_OnPreviewMouseWheel" MouseDown="SvCanvas_OnMouseDown">
<Canvas x:Name="cMain" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Width="36000" Height="58000" >
<Canvas.RenderTransform>
<ScaleTransform x:Name="st"/>
</Canvas.RenderTransform>
</Canvas>
</ScrollViewer>
C#
private void SvCanvas_OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
e.Handled = true;
}
private void SvCanvas_OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.MiddleButton == MouseButtonState.Pressed)
{
st.ScaleX = 1;
st.ScaleY = 1;
}
}
private void CCanvas_OnMouseMove(object sender, MouseEventArgs e)
{
if (drawing)
return;
lblBar.Content =
$"H[{svCanvas.HorizontalOffset}, V{svCanvas.VerticalOffset}] Mouse: {Mouse.GetPosition(cMain)}";
}
Related
I'm trying to get my border focused after the user clicks on it.
Currently it is possible to focus the border via tabs, but via click would be way more convenient for the user.
<Border x:Name="BorderFileInfo" Focusable="True" BorderBrush="LightGray" BorderThickness="1">
<Grid Margin="3,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<!-- CONTENT CTRL -->
</Grid>
</Border>
I saw in another post that there is a possability to catch the click event with an InputBinding but I don't know how to focus the border afterwards without using a command.
Stackoverflow: Why doesnt WPF border control have a mousedoubleclick event?
Is there an easy way to do that other than having to create commands ?
The app is pretty small so I don't want to use commands if I don't have to.
One easy way is to handle PreviewMouseDown or similar mouse events and set the focus:
private void Border_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Keyboard.Focus(sender as Border);
}
edit
note that you can create Click by handling PreviewMouseLeftButtonDown and PreviewMouseLeftButtonUp in this way:
_isdown =false;
private void Border_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_isdown =true;
}
private void Border_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if ( _isdown)
{
_isdown = false;
Keyboard.Focus(sender as Border);
}
}
What I want to do is make it so when the user clicks the 'rectangle' shape it creates a white border around the box like so....
If the user clicks in an area where no rectangle is present then it deselects any selected rectangles. I'm assuming this may need some additional changes in the code, any suggestions are welcome!
I found a solution for you. Let me know if this works for you. If not I will try and modify it. It is basic, but not that hard to implement I believe.
Here is the XAML. Note you will have to do all the positioning of the rectangles and such yourself. This just selects and deselects with a black border.
<Grid Background="#00000000" MouseDown="Grid_MouseDown" >
<Border Visibility="Hidden" x:Name="border" BorderBrush="Black" BorderThickness="1" Margin="230,135,142,58">
</Border>
<Rectangle x:Name="rect" Fill="Blue" MouseDown="Rectangle_MouseDown" Margin="250,148,160,74"/>
</Grid>
And here is the code behind.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Rectangle_MouseDown(object sender, MouseButtonEventArgs e)
{
border.Visibility = Visibility.Visible;
}
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.MouseDevice.DirectlyOver == rect)
{
}
else
{
border.Visibility = Visibility.Hidden;
}
}
}
I have created a WPF Grid. That grid contains Textbox and Button controls.Here i want to relocate the grid at run time .How its possible please answer this if you know.Like a movable window.
I don't know if you want to relocate after mouse events or in code after some different event? If second you can change grid margins in event/method where you want to do it and call UpdateLayout() method after it. If first here is my answer for similar question:
Check time after a mousebuttondown before the mousebuttonup
I found this solution on stackoveflow
<Grid x:Name="grid" Background="Blue"
Width="100" Height="100"
MouseDown="Grid_MouseDown" MouseMove="Grid_MouseMove" MouseUp="Grid_MouseUp">
<Grid.RenderTransform>
<TranslateTransform x:Name="tt"/>
</Grid.RenderTransform>
Place it in a window
<Window x:Name="window" ...>
<Grid x:Name="grid"...
And code:
Point m_start;
Vector m_startOffset;
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
m_start = e.GetPosition(window);
m_startOffset = new Vector(tt.X, tt.Y);
grid.CaptureMouse();
}
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
if (grid.IsMouseCaptured)
{
Vector offset = Point.Subtract(e.GetPosition(window), m_start);
tt.X = m_startOffset.X + offset.X;
tt.Y = m_startOffset.Y + offset.Y;
}
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
grid.ReleaseMouseCapture();
}
So let's say I have stackpanel on the left edge with width 200px. Now I want to handle horizontal swipe from left to right on this panel and show additional panel. Then handle swipe from right to left to hide it.
I tried handling page's ManipulationStarted and ManipulationDelta events, but it doesn't seem to have any effect at least with mouse. Any ideas, what could be an easy way to implement that?
What I've tried:
Handle page's swipe events and on the beginning of swipe check, if it was started in bounds of stackpanel, else I ignore it.
If swipe's delta was more then positive 40, looks like swipe was from left to right.
My XAML file:
// standard stuff of page
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" Width="200" HorizontalAlignment="Left" Background="White" x:Name="Panel"/>
</Grid>
// continue standard stuff
C# file:
public MainPage()
{
this.InitializeComponent();
this.ManipulationDelta += MainPage_ManipulationDelta;
this.ManipulationStarted += MainPage_ManipulationStarted;
}
private void MainPage_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("heh");
if (e.Position.X < 200)
{
initialPoint = e.Position;
isSwiping = true;
}
}
private void MainPage_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
if (e.IsInertial && isSwiping)
{
Point currentPoint = e.Position;
if (currentPoint.X - initialPoint.X >= 40)
{
isSwiping = false;
e.Complete();
System.Diagnostics.Debug.WriteLine("finished swipe :)");
}
}
}
private Point initialPoint;
private Boolean isSwiping;
(Again omitted default empty page)
You need to set ManipulationMode on a control e.g. ManipulationMode="TranslateX" and have the control respond to hit-testing (i.e. if it does not have a background - set the Background to Transparent) to receive manipulation events.
Then again - why not just use a ListView that has built-in support for swipes?
Is it possible to implement mouse click and drag selection box in WPF. Should it be done through simply drawing a rectangle, calculating coordinates of its points and evaluating position of other objects inside this box? Or are there some other ways?
Could you give a bit of sample code or a link?
Here is sample code for a simple technique that I have used in the past to draw a drag selection box.
XAML:
<Window x:Class="DragSelectionBox.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
>
<Grid
x:Name="theGrid"
MouseDown="Grid_MouseDown"
MouseUp="Grid_MouseUp"
MouseMove="Grid_MouseMove"
Background="Transparent"
>
<Canvas>
<!-- This canvas contains elements that are to be selected -->
</Canvas>
<Canvas>
<!-- This canvas is overlaid over the previous canvas and is used to
place the rectangle that implements the drag selection box. -->
<Rectangle
x:Name="selectionBox"
Visibility="Collapsed"
Stroke="Black"
StrokeThickness="1"
/>
</Canvas>
</Grid>
</Window>
C#:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
bool mouseDown = false; // Set to 'true' when mouse is held down.
Point mouseDownPos; // The point where the mouse button was clicked down.
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
// Capture and track the mouse.
mouseDown = true;
mouseDownPos = e.GetPosition(theGrid);
theGrid.CaptureMouse();
// Initial placement of the drag selection box.
Canvas.SetLeft(selectionBox, mouseDownPos.X);
Canvas.SetTop(selectionBox, mouseDownPos.Y);
selectionBox.Width = 0;
selectionBox.Height = 0;
// Make the drag selection box visible.
selectionBox.Visibility = Visibility.Visible;
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
// Release the mouse capture and stop tracking it.
mouseDown = false;
theGrid.ReleaseMouseCapture();
// Hide the drag selection box.
selectionBox.Visibility = Visibility.Collapsed;
Point mouseUpPos = e.GetPosition(theGrid);
// TODO:
//
// The mouse has been released, check to see if any of the items
// in the other canvas are contained within mouseDownPos and
// mouseUpPos, for any that are, select them!
//
}
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown)
{
// When the mouse is held down, reposition the drag selection box.
Point mousePos = e.GetPosition(theGrid);
if (mouseDownPos.X < mousePos.X)
{
Canvas.SetLeft(selectionBox, mouseDownPos.X);
selectionBox.Width = mousePos.X - mouseDownPos.X;
}
else
{
Canvas.SetLeft(selectionBox, mousePos.X);
selectionBox.Width = mouseDownPos.X - mousePos.X;
}
if (mouseDownPos.Y < mousePos.Y)
{
Canvas.SetTop(selectionBox, mouseDownPos.Y);
selectionBox.Height = mousePos.Y - mouseDownPos.Y;
}
else
{
Canvas.SetTop(selectionBox, mousePos.Y);
selectionBox.Height = mouseDownPos.Y - mousePos.Y;
}
}
}
}
I wrote an article about this:
https://www.codeproject.com/Articles/148503/Simple-Drag-Selection-in-WPF
You can get this functionality pretty easily by adding an InkCanvas and set its EditingMode to Select. Although it's primarily intended for Tablet PC ink collection and rendering, it's very easy to use it as a basic designer surface.
<Window Width="640" Height="480" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<InkCanvas EditingMode="Select">
<Button Content="Button" Width="75" Height="25"/>
<Button Content="Button" Width="75" Height="25"/>
</InkCanvas>
</Window>
This project created a custom MultiSelector which supports several selection methods including a rectangular "lasso" style:
Developing a MultiSelector by Teofil Cobzaru
It is far too long to reproduce here. The key elements of the design, IIRC, were to create a custom ItemContainer which knows how to interact with its MultiSelector parent. This is analagous to ListBoxItem / ListBox.
This is probably not the simplest possible approach, however if you are already using some type of ItemsControl to host the items which may need to be selected, it could fit into that design pretty easily.
MouseDown logic:
MouseRect.X = mousePos.X >= MouseStart.X ? MouseStart.X : mousePos.X;
MouseRect.Y = mousePos.Y >= MouseStart.Y ? MouseStart.Y : mousePos.Y;
MouseRect.Width = Math.Abs(mousePos.X - MouseStart.X);
MouseRect.Height = Math.Abs(mousePos.Y - MouseStart.Y);