I have been chasing a memory leak and have gotten down to a pretty simple case. Whenever the canvas size changes, the window clears the canvas and adds 1,000 random points to the canvas. It also reports the memory usage in the titlebar of the window.
Memory starts out below 2MB.
If you grab the corner of the window and move it around a bit, the memory usage skyrockets to over 100MB in 10 sec. and the UI gets sluggish. Stop moving for 10 sec and the memory creeps back to 11MB (Not shown in the titlebar because there isn't an update but never back to the original <2MB).
Is it possible that simple wpf graphics are this broken? Am I doing something wrong? I call GC.Collect() on every repaint, so why does any memory recovery take so long?
Release/debug mode doesn't make any difference. Making the lines invisible doesn't make any difference. Changing line characteristics doesn't matter Using different color brushes on each line doesn't matter. The leak is proportional to the number of lines on the canvas.
How can it be that simple lines on a canvas aren't collected when the canvas is cleared?
Any ideas greatly appreciated!
Here's the code:
<Window x:Class="SillyLeakTest.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" SizeChanged="Window_SizeChanged">
<Canvas x:Name="theCanvas"/>
</Window>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace SillyLeakTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Random rand = new Random();
public MainWindow()
{
InitializeComponent();
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
GC.Collect();
Title = "Memory Used: " + GC.GetTotalMemory(true).ToString("##,#");
Point windowSize = new Point(theCanvas.ActualWidth, theCanvas.ActualHeight);
Point center = new Point(windowSize.X / 2, windowSize.Y / 2);
theCanvas.Children.Clear();
SolidColorBrush color = new SolidColorBrush(Color.FromArgb(255, (byte)rand.Next(1, 255),
(byte)rand.Next(1, 255), (byte)rand.Next(1, 255)));
double scale = 1;
for (int i = 0; i < 1000; i++)
{
Point p = new Point(rand.Next(-(int)center.X, (int)center.X), rand.Next(-(int)center.Y, (int)center.Y));
theCanvas.Children.Add(new Line
{
X1 = center.X + p.Y * scale,
Y1 = center.Y - p.X * scale,
X2 = center.X + p.Y + 2 * scale,
Y2 = center.Y - p.X + 2 * scale,
StrokeThickness = 1,
//StrokeEndLineCap = PenLineCap.Round,
//StrokeStartLineCap = PenLineCap.Round,
// Stroke = new SolidColorBrush(P1.TheColor)
Stroke = color,
});
}
}
}
}
OK I can confirm. WPF does some weird sht when debugger attached. Start without debugger via CTRL-F5
There is no screen decoration and the app runs smooth and fast , tops out at ~2.8mb.
Attach debugger from VS and suddenly we have a colored border and that strange debug panel at the top of the screen. Now the memory grows exponentially.
see Different behavior of WPF Application: IDE debugging vs directly running the executable
Related
I'm currently trying to create a little plot interactive editor, using WPF.
On maximized window the plot dragging with mouse is not responsive enough because of the plot grid.
I got a path for my plot grid lying inside a Canvas control (render transform just shifts it to the bottom of the canvas)
<Path Name="VisualGrid" RenderTransform="{StaticResource PlotTechnicalAdjust}" Style="{DynamicResource ResourceKey=GridStyle}" Panel.ZIndex="1"/>
Here is how grid is created; _curState has actual camera "viewport" metadata
if (_curState.Changes.ScaleStepXChanged)
{
foreach (TextBlock item in _xLabels)
{
DeleteLabel(item);
}
_xLabels.Clear();
double i = _curState.LeftEdgeLine;
_gridGeom.Children[(int)GridGeomIndexes.VerticalLines] = new GeometryGroup { Transform = _verticalLinesShift};
var verticalLines =(GeometryGroup)_gridGeom.Children[(int)GridGeomIndexes.VerticalLines];
while (i <= _curState.RightEdgeLine * (1.001))
{
verticalLines.Children.Add(new LineGeometry(new Point(i * _plotParameters.PixelsPerOneX, 0),
new Point(i * _plotParameters.PixelsPerOneX,
-_wnd.ContainerGeneral.Height)));
_xLabels.Add(CreateLabel(i, Axis.X));
i += _curState.CurrentScaleStepX;
}
_curState.Changes.ScaleStepXChanged = false;
}
if (_curState.Changes.ScaleStepYChanged)
{
foreach (TextBlock item in _yLabels)
{
DeleteLabel(item);
}
_yLabels.Clear();
double i = _curState.BottomEdgeLine;
_gridGeom.Children[(int)GridGeomIndexes.HorizontalLines] = new GeometryGroup { Transform = _horizontalLinesShift};
var horizontalLines = (GeometryGroup)_gridGeom.Children[(int)GridGeomIndexes.HorizontalLines];
while (i <= _curState.TopEdgeLine * (1.001))
{
horizontalLines.Children.Add(new LineGeometry(new Point(0, -i * _plotParameters.PixelsPerOneY),
new Point(_wnd.ContainerGeneral.Width,
-i * _plotParameters.PixelsPerOneY)));
_yLabels.Add(CreateLabel(i, Axis.Y));
i += _curState.CurrentScaleStepY;
}
_curState.Changes.ScaleStepYChanged = false;
}
Where Transforms are composition of TranslateTransform and ScaleTransform (for vertical lines I only use X components and only Y for horizontal lines).
After beeing created those GeometryGroups are only edited if a new line apears into camera or an existing line exits viewable space. Grid is only recreated when axis graduations have to be changed after zooming.
I have a dragging option implemented like this:
private Point _cursorOldPos = new Point();
private void OnDragPlotMouseMove(object sender, MouseEventArgs e)
{
if (e.Handled)
return;
Point cursorNewPos = e.GetPosition(ContainerGeneral);
_plotView.TranslateShiftX.X += cursorNewPos.X - _cursorOldPos.X;
_plotView.TranslateShiftY.Y += cursorNewPos.Y - _cursorOldPos.Y;
_cursorOldPos = cursorNewPos;
e.Handled = true;
}
This works perfectly smooth with a small window (1200x400 units) for a large amount of points (like 100+).
But for a large window (fullscreen 1920x1080) it happens pretty jittery even without any data-point controls on canvas.
The strange moment is that lags don't appear when I order my GridGenerator to keep around 100+ lines for small window and drag performance suffers when I got less than 50 lines on maximezed. It makes me think that it might somehow depend not on a number of elements inside a geometry, but on their linear size.
I suppose I should mention that OnSizeChanged I adjust the ContainerGeneral canvas' height and width and simply re-create the grid.
Checked the number of lines stored in runtime to make sure I don't have any extras. Tried using Image with DrawingVisual instead of Path. Nothing helped.
Appearances for clearer understanding
It was all about stroke dashes and WPF's unhealthy desire to count them all while getting hit test bounds for DrawingContext.
The related topic is Why does use of pens with dash patterns cause huge (!) performance degredation in WPF custom 2D drawing?
Below is the code for a simple app that draws a rectangle on a canvas in a window and then takes a screen shot of the app using the CopyFromScreen function when any key is pressed. Just before this is called however, I call canvas.Children.Clear(). I would then expect the resultant image to not have the rectangle in it, but it does. It seems that the actual rectangle image isn't removed from the canvas when the function is called but some time after.
I tried putting in a System.Threading.Thread.Sleep(1000); after the Clear() call but the rectangle stays on screen for that full second as well. Clearly it's getting removed after the key press function finishes, is there any way to remove it before the CopyFromScreen call?
To run this you will need to add a reference to System.Drawing.
XAML code
<Window x:Class="CanvasTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="210" Height="240"
KeyDown="keyPressed">
<Window.Background>
<SolidColorBrush Color="White"/>
</Window.Background>
<Grid>
<Canvas Name="canvas"
HorizontalAlignment="Left" VerticalAlignment="Top">
</Canvas>
</Grid>
</Window>
.cs code
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
namespace CanvasTest {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
Left = 0;
Top = 0;
Rectangle rect = new Rectangle {
Stroke = System.Windows.Media.Brushes.Black,
StrokeThickness = 1,
Width = 100,
Height = 100
};
canvas.Children.Add(rect);
Canvas.SetLeft(rect, 50);
Canvas.SetTop(rect, 50);
}
private void keyPressed(object sender, System.Windows.Input.KeyEventArgs e) {
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap((int)Width, (int)Height);
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap);
canvas.Children.Clear();
graphics.CopyFromScreen(0, 0, 0, 0,
new System.Drawing.Size(bitmap.Width, bitmap.Height),
System.Drawing.CopyPixelOperation.SourceCopy);
String path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
path += "\\MuckleEwesPic.png";
bitmap.Save(path, System.Drawing.Imaging.ImageFormat.Png);
}
}
}
How can I clear the canvas and then take a screen shot without this behaviour happening? And no 'don't add the rect' isn't a solution ha, this is just a minimal example of a larger app in which the problem is occurring.
Thanks.
Using something like in ColinSmith's link:
static void WaitForRenderPass()
{
Application.Current.Dispatcher
.BeginInvoke( DispatcherPriority.ApplicationIdle, new Action( () => {} ) )
.Wait();
}
sort of works for waiting on a render pass but the problem is it's not possible (afaik) to tell if that render pass contains everything you want.
In practice depending on system load/video card drivers/... it has to be called multiple times. I've had one machine where it had to be called in a loop up to 3 times, and each of those calls took about 2 frames #60Hz to complete. Only then a call to the above would return immediately indicating the render thread is really idle, which likely means all changes you want have been rendered, which in turn means screen captures contain everything they should have. So we ended up using
for( int i = 0 ; i < 5 ; ++i )
{
WaitForRenderPass();
Thread.Sleep( 10 );
}
It's ugly and it's a hack but it hasn't failed (yet).
Regarding HighCore's comment: you can capture the screen with Wpf-only classes as well, however:
it has the same problem the OP started this questiomn for in the first place so it won't immediately solve anything (except from not using System.Drawing)
you won't be able to get a main window's chrome in the capture
it's markedly slower than CopyScreen
it does not render the exact same pixels as rendered to the screen
I'm busy with a small application in which I want to display information at the location of the cursor when it hoovers over a Canvas. The Canvas in question is a custom one (inherited from Canvas) which provides functionality to add DrawingVisuals (as shown in basically every tutorial on displaying large amounts of geometric shapes on a canvas).
I would like to display a vertical line and horizontal line as well as the local coordinates (p in the code below) which are directly derived from the canvas coordinates (v). At the moment I'm rendering these objects at position (0,0) and use offset during the OnMouseMove event to update their location.
The horizontal and vertical lines are rendered in the DrawingVisual _cursor and the location in local y,z-coordinates in _info.
private void oCanvas_MouseMove(object sender, MouseEventArgs e)
{
#region 1. Get location data
System.Windows.Vector v = (System.Windows.Vector)e.GetPosition(oCanvas);
// point in YZ coordinates
BSMath.DoubleXY p = new BSMath.DoubleXY();
p.X = (oCanvas.OriginY - v.Y) / oCanvas.ZoomFactor;
p.Y = (oCanvas.OriginX - v.X) / oCanvas.ZoomFactor;
#endregion
#region 2. Update cursor and info
if (oSettings.ShowInformation)
{
_info.Info = p.X.ToString("0.0") + " | " + p.Y.ToString("0.0");
_info.Render(0, 0);
_info.Visual.Offset = v;
}
// move cursor
_cursor.Visual.Offset = v;
}
Using the mousemove event seems to be creating a lot of overhead and I can see that there are issues tracking the mouse movements when I move the mouse quickly.
Can anyone recommend a better way of creating the same effect?
example http://www.iccg.be/test/images/canvas.jpg
Edit:
I investigated it a bit further and the problem seems to occur when the resolution of the canvas is bigger. If it is a 600x400 canvas then there is no delay, but when it is around 1000x800 I get the problem with delays when hoovering. The performance also improves if I use user drawn crosshairs instead of the lines that have the full width/ height of the canvas.
I recently have built something similar and haven't had any performance issues.
Did it the very simple way by adding the stuff directly on the canvas.
The drawn items are in a second canvas behind the mouse position canvas. Both reside in a Grid.
This is for sure not the most sophisticated way to solve this, but it works quite well for me.
Here's the code:
private Point _previous;
private Point _current;
private Line _xLine;
private Line _yLine;
private TextBlock _displayTextBlock;
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_current = e.GetPosition(myCanvas);
if (_previous != _current)
{
if (_xLine == null)
{
_xLine = new Line() {X1 = 0, X2 = myCanvas.ActualWidth, Stroke = new SolidColorBrush(Colors.Black)};
_yLine = new Line() {Y1 = 0, Y2 = myCanvas.ActualHeight, Stroke = new SolidColorBrush(Colors.Black)};
_displayTextBlock = new TextBlock();
myCanvas.Children.Add(_xLine);
myCanvas.Children.Add(_yLine);
myCanvas.Children.Add(_displayTextBlock);
}
_displayTextBlock.SetValue(Canvas.TopProperty, _current.Y);
_displayTextBlock.SetValue(Canvas.LeftProperty, _current.X);
_displayTextBlock.Text = _current.X.ToString() + " | " + _current.Y.ToString();
_xLine.Y1 = _current.Y;
_xLine.Y2 = _current.Y;
_yLine.X1 = _current.X;
_yLine.X2 = _current.X;
_previous = _current;
}
}
I have an image viewer created with WPF 3D graphics. Image quality is really WORSE there, so I've started researching this issue, created simple application which shows the image using 2D graphics on the top part of the window, and the same image on the bottom part using 3D graphics. I noticed that image looks much worse on 3D surface than on 2D. The colors on the 3D surface are less saturated and do not have clear boundaries. Note, that I applied linear bitmap scaling mode to the root Grid. Other weird thing is that when I'm changing bitmap scaling mode to 'Fant' or 'NearestNeighbor' it affects 2D graphics, but image on the 3D surface REMAINS THE SAME! I'm using image for this sample with Height = 466px, Width = 490px. I'm zooming out it in the code (both 2D and 3D implementation) a little bit to see the scaling quality degradation. The code is:
<Window x:Class="Scaling3DSample.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="340">
<Grid x:Name="backgroundGrid">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;
namespace Scaling3DSample
{
public partial class Window2 : Window
{
private static double _distanceFromCamera = 0.62618;
public Window2()
{
InitializeComponent();
RenderOptions.SetBitmapScalingMode(backgroundGrid, BitmapScalingMode.Linear);
Create2DGraphics();
// THE SAME IMAGE ON 3D SURFACE LOOKS MUCH WORSE
Create3DGraphics();
}
private void Create2DGraphics()
{
Rectangle exampleRectangle = new Rectangle();
Grid.SetRow(exampleRectangle, 0);
exampleRectangle.Width = 335;
exampleRectangle.Height = 317;
exampleRectangle.Fill = GetBrush();
backgroundGrid.Children.Add(exampleRectangle);
}
private void Create3DGraphics()
{
Viewport3D mainViewPort3D = new Viewport3D();
Grid.SetRow(mainViewPort3D, 1);
mainViewPort3D.Camera = new PerspectiveCamera { LookDirection = new Vector3D(-1, 0, 0), UpDirection = new Vector3D(0, 0, 1), FieldOfView = 77.0942 };
mainViewPort3D.Children.Add(new ModelVisual3D { Content = new AmbientLight() });
MeshGeometry3D geometry3D = new MeshGeometry3D();
Point3D topLeft = new Point3D(-_distanceFromCamera, 0.5, -0.5);
Point3D bottomRight = new Point3D(-_distanceFromCamera, -0.5, 0.5);
geometry3D.Positions.Add(bottomRight);
geometry3D.Positions.Add(new Point3D(-_distanceFromCamera, topLeft.Y, bottomRight.Z));
geometry3D.Positions.Add(new Point3D(-_distanceFromCamera, bottomRight.Y, topLeft.Z));
geometry3D.Positions.Add(topLeft);
geometry3D.TriangleIndices.Add(1);
geometry3D.TriangleIndices.Add(0);
geometry3D.TriangleIndices.Add(2);
geometry3D.TriangleIndices.Add(2);
geometry3D.TriangleIndices.Add(3);
geometry3D.TriangleIndices.Add(1);
geometry3D.TextureCoordinates.Add(new Point(0, 0));
geometry3D.TextureCoordinates.Add(new Point(1, 0));
geometry3D.TextureCoordinates.Add(new Point(0, 1));
geometry3D.TextureCoordinates.Add(new Point(1, 1));
Material material = new DiffuseMaterial(GetBrush());
ModelVisual3D modelForGeometry = new ModelVisual3D { Content = new GeometryModel3D(geometry3D, material) };
mainViewPort3D.Children.Add(modelForGeometry);
backgroundGrid.Children.Add(mainViewPort3D);
}
private ImageBrush GetBrush()
{
// put any other image URI here, image Height = 466px, Width = 490px
ImageBrush brush = new ImageBrush(new BitmapImage(new Uri("lion.jpg", UriKind.Relative)));
brush.Stretch = Stretch.Fill;
return brush;
}
}
}
Thanks in advance for all your help!
There are some other variables to consider, then.
Your graphics card settings could be forcing the interpolation mode down despite WPF's request for something nicer looking. WPF's 3D is hardware accelerated on Tier 2 hardware, so check your drivers' control software. It might not be possible for WPF to request anything better!
Try enabling anti-aliasing in your application and graphics card settings, too.
Just guessing: you did not define any lights nor any normals. Sometimes that will cause a darker image than you would expect.
I have a WPF application and I need to know how to center the wain window programatically (not in XAML).
I need to be able to do this both at startup and in response to certain user events. It has to be dynamically calculated since the window size itself is dynamic.
What's the simplest way to do this? Under old Win32 code, I'd call the system metrics functions and work it all out. Is that still the way it's done or is there a simple CenterWindowOnScreen() function I can now call.
Well, for startup time, you can set the startup location:
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
Later, you'll need to query it. The information (at least for the primary screen) is available via SystemParameters.PrimaryScreenWidth/Height.
private void CenterWindowOnScreen()
{
double screenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
double screenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;
double windowWidth = this.Width;
double windowHeight = this.Height;
this.Left = (screenWidth / 2) - (windowWidth / 2);
this.Top = (screenHeight / 2) - (windowHeight / 2);
}
You can use this method to set the window position to the center of your screen.
Isn't it just as simple to set
WindowStartupLocation="CenterScreen"
In the XAML definition for the window.
I had to combine a few of these answers to cover all bases in my case:
Peter's method to find the current monitor - rather than the just the Primary monitor (seriously who has just 1 monitor at work anymore?)
#Wild_A's method to use the workarea rather than the screen bounds to take into account the space for the taskbar.
I had to add DPI scaling, specifically for a tablet displaying 1280x800 as 1024x640, but which is useful to cover edge cases, for which I found an answer for here. Note the dpiScaling variable is null if called on first load before the UI is displayed (explained here)
//get the current monitor
Screen currentMonitor = Screen.FromHandle(new System.Windows.Interop.WindowInteropHelper(Application.Current.MainWindow).Handle);
//find out if our app is being scaled by the monitor
PresentationSource source = PresentationSource.FromVisual(Application.Current.MainWindow);
double dpiScaling = (source != null && source.CompositionTarget != null ? source.CompositionTarget.TransformFromDevice.M11 : 1);
//get the available area of the monitor
Rectangle workArea = currentMonitor.WorkingArea;
var workAreaWidth = (int)Math.Floor(workArea.Width*dpiScaling);
var workAreaHeight = (int)Math.Floor(workArea.Height*dpiScaling);
//move to the centre
Application.Current.MainWindow.Left = (((workAreaWidth - (myWindowWidth * dpiScaling)) / 2) + (workArea.Left * dpiScaling));
Application.Current.MainWindow.Top = (((workAreaHeight - (myWindowHeight * dpiScaling)) / 2) + (workArea.Top * dpiScaling));
where myWindowWidth and myWindowHeight are variables I used to manually set the size of the window earlier.
Rect workArea = System.Windows.SystemParameters.WorkArea;
this.Left = (workArea.Width - this.Width) / 2 + workArea.Left;
this.Top = (workArea.Height - this.Height) / 2 + workArea.Top;
This takes into account the taskbar size (by using System.Windows.SystemParameters.WorkArea) and position (by adding workArea.Left and workArea.Top)
In case you need to draw a window in an multiple screen environment.
I've made a static class where the following method can be re-used:
public static void PostitionWindowOnScreen(Window window, double horizontalShift = 0, double verticalShift = 0)
{
Screen screen = Screen.FromHandle(new System.Windows.Interop.WindowInteropHelper(window).Handle);
window.Left = screen.Bounds.X + ((screen.Bounds.Width - window.ActualWidth) / 2) + horizontalShift;
window.Top = screen.Bounds.Y + ((screen.Bounds.Height - window.ActualHeight) / 2) + verticalShift;
}
In the constructor of the Window now just call the method:
this.Loaded += (s, a) => Globals.PostitionWindowOnScreen(this, 0, 0)
As a basic solution, you can use the window's StartupLocation property, set it to one of the enum values defined in System.Windows.WindowStartupLocation enumeration, there is one for center of screen:
_wpfWindow.StartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
Unfortunately it's not always quite so simple; you need to account for multiple monitors, taskbars, etc. The "CenterScreen" option opens the window in the center of the screen that has the mouse cursor. See this SO question for a lot of information, or reference the api.
In the window element just add this attribute-value pair:
WindowStartupLocation="CenterScreen"
What I am using in my app, it is working for multiple displays and for different DPI setting
//center a window on chosen screen
public static void CenterWindow(Window w, System.Windows.Forms.Screen screen = null)
{
if(screen == null)
screen = System.Windows.Forms.Screen.PrimaryScreen;
int screenW = screen.Bounds.Width;
int screenH = screen.Bounds.Height;
int screenTop = screen.Bounds.Top;
int screenLeft = screen.Bounds.Left;
w.Left = PixelsToPoints((int)(screenLeft + (screenW - PointsToPixels(w.Width, "X")) / 2), "X");
w.Top = PixelsToPoints((int)(screenTop + (screenH - PointsToPixels(w.Height, "Y")) / 2), "Y");
}
public static double PixelsToPoints(int pixels, string direction)
{
if (direction == "X")
{
return pixels * SystemParameters.WorkArea.Width / System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width;
}
else
{
return pixels * SystemParameters.WorkArea.Height / System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;
}
}
public static double PointsToPixels(double wpfPoints, string direction)
{
if (direction == "X")
{
return wpfPoints * System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width / SystemParameters.WorkArea.Width;
}
else
{
return wpfPoints * System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height / SystemParameters.WorkArea.Height;
}
}
Another short way to make mainwindow render at center of the computer screen using code behind.
this.Left = (System.Windows.SystemParameters.PrimaryScreenWidth - this.Width)/2;
this.Top = (System.Windows.SystemParameters.PrimaryScreenHeight - this.Height)/2;
Based on #Wild_A answer I just subscribed to the SizeChanged event, and added this event handler:
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
try
{
Rect workArea = SystemParameters.WorkArea;
this.Left = (workArea.Width - e.NewSize.Width) / 2 + workArea.Left;
this.Top = (workArea.Height - e.NewSize.Height) / 2 + workArea.Top;
}
catch (Exception ex) { ... Handel exception; }
}
Just use:
WindowStartupLocation="CenterScreen"
And in case you only want to center horizontally / vertically, You can override the OnActivated method and set left or top to zero like this:
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
// to center Vertically
Left = 0;
// or user top = 0 to center Horizontally
//top = 0;
}
Go to property window of MainWindow.xaml
find WindowStartupLocation property from Common category
select CenterScreen option from dropdown
Run the Application
For Full Screen
Go to property window of MainWindow.xaml
find WindowState property from Common category
select Maximized option from dropdown
Run the Application
Copy-paste good quality extension code.
Runtime:
using System;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
namespace Extensions
{
/// <summary>
/// <see cref="Window"/> extensions.
/// </summary>
public static class WindowExtensions
{
/// <summary>
/// Moves the window to the center of the current screen, also considering dpi.
/// </summary>
/// <param name="window"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void MoveToCenter(this Window window)
{
window = window ?? throw new ArgumentNullException(nameof(window));
var helper = new WindowInteropHelper(window);
var screen = Screen.FromHandle(helper.Handle);
var area = screen.WorkingArea;
var source = PresentationSource.FromVisual(window);
var dpi = source?.CompositionTarget?.TransformFromDevice.M11 ?? 1.0;
window.Left = dpi * area.Left + (dpi * area.Width - window.Width) / 2;
window.Top = dpi * area.Top + (dpi * area.Height - window.Height) / 2;
}
}
}
Initial position:
<Window WindowStartupLocation="CenterScreen">
</Window>
You will have to find this line :
Title="MainWindow" Height="450" Width="800"
And you add this line to it :
WindowStartupLocation="CenterScreen"
To become this :
Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterScreen">
Thank me Later ♥
If you it to be maximized at once
this.WindowState = System.Windows.WindowState.Maximized;