How do I position a window in WPF in an exact location? - c#

I'm trying to display a window that aligns with an existing component. In this example I want to align it to a button. When i click the button I would like the window to position itself so that it's bottom is just above the button, and it's width is the same as the button. The left of the window should be the same as the left of the button.
To achieve this I use the following xaml:
<Window x:Class="WindowPositioningTest.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:WindowPositioningTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Name="MyButton" Content="Click me to see window!" Width="300" Height="50" Click="Button_Click"/>
</Grid>
The onclick function looks like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
var myButtonLocation = MyButton.PointToScreen(new Point(0, 0));
window.Width = MyButton.ActualWidth;
window.Height = 300;
window.Left = myButtonLocation.X;
window.Top = myButtonLocation.Y - window.Height;
window.Show();
}
When I click the button a window is displayed like in the picture below.
My question is: why is the window not as wide as the button and why is it not in the right position? It's almost as if there's an invisible frame around the window.

Try this:
private void Button_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
var myButtonLocation = MyButton.PointToScreen(new Point(0, 0));
window.Width = MyButton.ActualWidth + 16;
window.Height = 300;
window.Left = myButtonLocation.X - 8;
window.Top = myButtonLocation.Y - window.Height;
window.Show();
}
It happens beacuse of window border. As you know, window is a composite element. I think when you set Width, you set a width of the working space, not the width of the whole window.

Related

Can't maximize MahApps.Metro window in WPF

Сan't maximize window when using MahApps.Metro window, the size does not change. Example of my window:
<controls:MetroWindow x:Class="Example.View.HelpView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="clr-namespace:Example.UserControls"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d"
Height="843"
Width="1258"
WindowStyle="None" Background="Transparent">
</controls:MetroWindow>
Edit
I created button for this and trying to change the state as follows:
private void UserControl_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Window _window = Window.GetWindow(this);
if (_window.WindowState == WindowState.Maximized)
{
_window.WindowState = WindowState.Normal;
}
else
{
_window.WindowState = WindowState.Maximized;
}
}
Edit
When I expand the window to full screen, it behaves as follows, takes up the same space as in normal mode, but the rest of the window is locked
enter image description here
lastly do you try this :) I think "_window" not seting correctly now
xaml
<controls:MetroWindow
x.name="MetroWindow"
</controls:MetroWindow>
c#
Window _window = MetroWindow;
or
c#
Window _window = (Window)sender;

WPF ScrollViewer and Panning

I have a Window with a ScrollViewer and inside the ScrollViewer there is a Rectangle. Now I added code to drag the Rectangle which works fine. But I have no idea how to show the Scrollbars when the Rectangle is moved outside the view. I thought this will happen automatically which is not the case?
Here is my XAML:
<Window x:Class="WpfApp4.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:WpfApp4"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<ScrollViewer Name="_scrollViewer" CanContentScroll="True"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Rectangle Name="_myRect" Width="100" Height="100" Fill="Blue"/>
</ScrollViewer>
</Window>
And the code behind:
public partial class MainWindow : Window
{
private Point _origin;
private Point _start;
private ScaleTransform _scaleTransform = new ScaleTransform();
private TranslateTransform _translateTransform = new TranslateTransform();
public MainWindow()
{
InitializeComponent();
var group = new TransformGroup();
group.Children.Add(_scaleTransform);
group.Children.Add(_translateTransform);
_myRect.RenderTransform = group;
// Hook up events
_myRect.MouseLeftButtonDown += _myRect_MouseLeftButtonDown;
_myRect.MouseLeftButtonUp += _myRect_MouseLeftButtonUp;
_myRect.MouseMove += _myRect_MouseMove;
}
private void _myRect_MouseMove(object sender, MouseEventArgs e)
{
if (_myRect.IsMouseCaptured)
{
Vector v = _start - e.GetPosition(this);
_translateTransform.X = _origin.X - v.X;
_translateTransform.Y = _origin.Y - v.Y;
}
}
private void _myRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_myRect.ReleaseMouseCapture();
this.Cursor = Cursors.Arrow;
}
private void _myRect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_start = e.GetPosition(this);
_origin = new Point(_translateTransform.X, _translateTransform.Y);
Cursor = Cursors.Hand;
_myRect.CaptureMouse();
}
}
[UPDATED]:
So based on the input I got I change XAML and the code behind to the following - but still no scrollbars?
<Window x:Class="WpfApp4.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:WpfApp4"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<ScrollViewer Name="_scrollViewer" CanContentScroll="True"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
<Canvas Name="_myCanvas">
<Rectangle Name="_myRect" Width="100" Height="100" Fill="Blue" Canvas.Left="305" Canvas.Top="129"/>
</Canvas>
</ScrollViewer>
</Window>
Code Behind:
public partial class MainWindow : Window
{
private Point _start;
public MainWindow()
{
InitializeComponent();
// Hook up events
_myRect.MouseLeftButtonDown += _myRect_MouseLeftButtonDown;
_myRect.MouseLeftButtonUp += _myRect_MouseLeftButtonUp;
_myRect.MouseMove += _myRect_MouseMove;
}
private void _myRect_MouseMove(object sender, MouseEventArgs e)
{
if (_myRect.IsMouseCaptured)
{
var canvasRelativePosition = e.GetPosition(_myCanvas);
Debug.WriteLine($"New Position: {canvasRelativePosition}");
Canvas.SetTop(_myRect, canvasRelativePosition.Y - _start.Y);
Canvas.SetLeft(_myRect, canvasRelativePosition.X - _start.X);
}
}
private void _myRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_myRect.ReleaseMouseCapture();
this.Cursor = Cursors.Arrow;
}
private void _myRect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_start = e.GetPosition(_myRect);
Debug.WriteLine($"Start Position: {_start}");
Cursor = Cursors.Hand;
_myRect.CaptureMouse();
}
}
If you want your transform to affect layout, you have to use LayoutTransofrm. RenderTransform only changes apperance.
Any transformations associated with an elements LayoutTransform
property will have an impact on the subsequent Measure and Arrange
steps. Whereas a RenderTransform will not have any impact on the
layout process and will only effect rendering.
Read more here.
However, LayoutTransform ignores TranslateTransform
LayoutTransform ignores TranslateTransform operations. This is because
the layout system behavior for child elements of a FrameworkElement
auto-corrects any offsets to the position of a scaled or rotated
element into the layout and coordinate system of the parent element.
Read more here
All that means, to achieve moving your element, you can not use Transforms. You could try to change position of your element by hand (Margin, Canvas.Left/Right or other ideas).

C# WPF Can't click button after defining margin

I have created a button in my WPF Application using the following code:
Button EditButton = new Button();
EditButton.Margin = new System.Windows.Thickness(Location[0], Location[1], 0, 0);
EditButton.Height = double.Parse("20");
EditButton.Width = double.Parse("20");
EditButton.Cursor = System.Windows.Input.Cursors.Hand;
EditButton.Content = "TEST!";
EditButton.Click += new System.Windows.RoutedEventHandler(Edit_Click);
Grid.Children.Add(EditButton);
Location[1] += 17;
The button works perfectly when I have not defined EditButton.Margin but as soon as I define it I can't click it and the cursor does not change. I have searched the internet around for an answer and none of them seemed to work. Thanks in advance.
If you cannot click the control you have created, then it is generally being caused by other control being on top of it.
I would suggest altering your code slightly and move on from that point:
var stackPanel = new StackPanel();
var button = new Button();
button.Content = "Your Button";
button.Click += new System.Windows.RoutedEventHandler(Edit_Click);
stackpanel.Children.Add(button);
I suggest using StackPanel as it automatically arrange your control and thus prevents it from overlapping you can start from this point on see whether issue was caused by grid or some other component.
Button will stretch by default to its content, so will StackPanel.
Not sure what 'Location' is in your code, and I assume 'Grid' is the name of the grid. The below works.
public MainWindow()
{
InitializeComponent();
Button EditButton = new Button();
EditButton.Margin = new System.Windows.Thickness(10, 10, 0, 0);
EditButton.Height = double.Parse("20");
EditButton.Width = double.Parse("20");
EditButton.Cursor = System.Windows.Input.Cursors.Hand;
EditButton.Content = "TEST!";
EditButton.Click += new System.Windows.RoutedEventHandler(Edit_Click);
Grid.Children.Add(EditButton);
// Location[1] += 17;
}
private void Edit_Click(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}
XAML -
<Window x:Class="WpfApplication6.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="Grid">
</Grid>
</Window>
It looks like you want to do this programmatically, but if you define it in XAML, you could set the button's Panel.ZIndex property to some high number to bring it to the front:
<Button Content="TEST!" Panel.ZIndex="1000" Height="20" Width="20" Cursor="Hand" Click="Edit_Click" />
Hope that helps somebody...

How to limit child window's movement within parent boundaries?

I was wondering is it possible to limit child window's ability to be moved around to only within parent's panel boundaries ? Suppose I create a child window with a button click:
<UserControl x:Class="ChildWindowTest.MainPage"
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:Child="clr-namespace:ChildWindowTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" >
<Grid x:Name="LayoutRoot" >
<StackPanel Width="500" Height="500">
<Button Width="100" Height="25" Click="Button_Click" Content="Child Window"/>
</StackPanel>
</Grid>
</UserControl>
<controls:ChildWindow
x:Class="ChildWindowTest.ChildWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Width="400" Height="300" Title="ChildWindow1" >
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
</Grid>
</controls:ChildWindow>
I can move the generated child to left, right and down off screen (clipped off). I want to avoid that, basically set up a boundary within which child window is allowed to be moved (within StackPanel boundary)
Thank you for any suggestion ..
I tried to keep my solution as simple as possible, but still it was quite challenging to achieve the desired behaviour.
Basically, if you leave the ChildWindow's ControlTemplate alone, the following code should work for you:
private void Button_Click(object sender, RoutedEventArgs e)
{
ChildWindow wnd = new ChildWindow();
wnd.Width = 800;
wnd.Height = 600;
wnd.Title = "Test";
wnd.MouseLeftButtonUp += wnd_MouseLeftButtonUp;
wnd.Loaded += wnd_Loaded;
wnd.Show();
}
private void wnd_Loaded(object sender, RoutedEventArgs e)
{
var wnd = sender as ChildWindow;
myApplicationActualWidth = Application.Current.Host.Content.ActualWidth;
myApplicationActualHeight = Application.Current.Host.Content.ActualHeight;
//This call might be necessary to make sure the visual tree of wnd is constructed and can be inspected
wnd.UpdateLayout();
//wnd is guaranteed to have at least one child here
myRoot = (FrameworkElement)VisualTreeHelper.GetChild(wnd, 0);
myContentRoot = myRoot.FindName("ContentRoot") as FrameworkElement;
//this is the title bar part
myChrome = myRoot.FindName("Chrome") as FrameworkElement;
myChrome.AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(wnd_MouseLeftButtonUp), true);
myTransform = (myContentRoot.RenderTransform as TransformGroup).Children.OfType<TranslateTransform>().First();
}
private void wnd_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Point rootMousePosition = e.GetPosition(sender as ChildWindow);
Point contentRootMousePosition = e.GetPosition(myContentRoot);
Point currentOffset = new Point(rootMousePosition.X - contentRootMousePosition.X, rootMousePosition.Y - contentRootMousePosition.Y);
TransformChildWindowToValidPosition(currentOffset);
}
private void TransformChildWindowToValidPosition(Point currentPosition)
{
// handle left side
if (currentPosition.X < 0)
{
myTransform.X = myTransform.X - currentPosition.X;
}
// handle top
if (currentPosition.Y < 0)
{
myTransform.Y = myTransform.Y - currentPosition.Y;
}
// handle right side
if (currentPosition.X + myContentRoot.ActualWidth > ActualWidth)
{
myTransform.X = myTransform.X - (currentPosition.X + myContentRoot.ActualWidth - myApplicationActualWidth);
}
// handle bottom
if (currentPosition.Y + myContentRoot.ActualHeight > ActualHeight)
{
myTransform.Y = myTransform.Y - (currentPosition.Y + myContentRoot.ActualHeight - myApplicationActualHeight);
}
}
private TranslateTransform myTransform;
private FrameworkElement myRoot;
private FrameworkElement myContentRoot;
private FrameworkElement myChrome;
private double myApplicationActualWidth;
private double myApplicationActualHeight;
This code basically does not allow you the move your ChildWindow outside the boundaries of the current SL application, but it should be a piece of cake to get your hands on the dimensions of the "parent" control of the window and readjust the values for all the edges.
Hope I could help...

A tooltip or something similar move with cursor in WPF

Is it possible to move a Tooltip or something like that with cursor when mouse goes on a specific control?
I tried TextBlock, but Margin property not work.
private TextBlock tooltip = new TextBlock();
private void imgRoom_MouseEnter(object sender, MouseEventArgs e)
{
Point position = e.GetPosition((IInputElement)sender);
tooltip.Visibility = System.Windows.Visibility.Visible;
tooltip.Margin = new Thickness(position.X, position.Y, 0, 0);
tooltip.Width = 100;
tooltip.Height = 100;
tooltip.Background = new SolidColorBrush(Colors.Red);
}
private void imgRoom_MouseMove(object sender, MouseEventArgs e)
{
Point position = e.GetPosition((IInputElement)sender);
tooltip.Margin = new Thickness(position.X, position.Y, 0, 0);
}
You can achieve the effect using a Popup and some simple properties upon it. From window code...
<Window x:Class="WpfApplication3.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>
<Rectangle Name="rect" Margin="50,50,0,0" Width="100" Height="100" Fill="LightBlue" MouseMove="Rectangle_MouseMove" MouseLeave="Rectangle_MouseLeave" />
<Popup Name="floatingTip" AllowsTransparency="True" Placement="Relative" PlacementTarget="{Binding ElementName=rect}">
<TextBlock>Look At Me</TextBlock>
</Popup>
</Grid>
</Window>
And this is what the codebehind would look like.
...
private void Rectangle_MouseMove(object sender, MouseEventArgs e)
{
if (!floatingTip.IsOpen) { floatingTip.IsOpen = true; }
Point currentPos = e.GetPosition(rect);
// The + 20 part is so your mouse pointer doesn't overlap.
floatingTip.HorizontalOffset = currentPos.X + 20;
floatingTip.VerticalOffset = currentPos.Y;
}
private void Rectangle_MouseLeave(object sender, MouseEventArgs e)
{
floatingTip.IsOpen = false;
}
...
So from the XAML you can see that the popup placement is relative to the rectangle. When you go mousing over the rectangle, it becomes visible, and its position is updated as the mouse moves. Naturally this is a very basic solution, but with some minor tweaks, handling events like 'MouseEnter' and property adjustment you can come up with some really neat effects.
I don't know if this is a best practice, or if it performs well, but you could use an Adorner.
I've created a proof of concept before, but haven't used it in a production scenario (yet). The adorner can be used to create something like this (tooltip), or a custom mouse cursor or drop target icons.
Make sure you set IsHitTestVisible = false if the adorner doesn't need to be hit tested and might appear directly under the mouse location.
UPDATE
Just fully read the description of adorners:
Common applications for adorners include:
Adding functional handles to a UIElement that enable a user to manipulate the element in some way (resize, rotate, reposition, etc.).
Provide visual feedback to indicate various states, or in response to various events.
Overlay visual decorations on a UIElement.
Visually mask or override part or all of a UIElement.
This moves the tooltip around with the mouse cursor.
private void OnMouseMoveHandler(object sender, MouseEventArgs args)
{
if ((sender as FrameworkElement).ToolTip == null)
(sender as FrameworkElement).ToolTip = new ToolTip() { Placement = PlacementMode.Relative };
double x = args.GetPosition((sender as FrameworkElement)).X;
double y = args.GetPosition((sender as FrameworkElement)).Y;
var tip = ((sender as FrameworkElement).ToolTip as ToolTip);
tip.Content = tooltip_text;
tip.HorizontalOffset = x + 10;
tip.VerticalOffset = y + 10;
}

Categories