Xamarin Create square layout using screen size - c#

I have a problem. I want to create a StackLayout that has the width and height of the screen size, so it will be a square. I already tried this:
public Page1()
{
InitializeComponent();
imageLayout.HeightRequest = Application.Current.MainPage.Width;
imageLayout.WidthRequest = Application.Current.MainPage.Width;
}
But that gave me the error that Application.Current.MainPage is null.
How can I fix this?

I think I would use the SizeChanged Event from your page:
public Page1()
{
InitializeComponent();
SizeChanged += (s,a) =>
{
imageLayout.HeightRequest = this.Width;
imageLayout.WidthRequest = this.Width;
};
}

You should override OnSizeAllocated for these purposes.
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
imageLayout.WidthRequest= width;
imageLayout.HeightRequest = width;
}
Much cleaner way

Related

How do I get the height and width from a DockPanel?

I am using a DockPanel to host some image enhancement controls. The image to be enhanced is in the 'center' dock position. I would like to have the image fill the entire area of the DockPanel. I have a custom class that implements the WPF Canvas control and takes a windows.Rect as input. This Rect will set the area for the image render function which I have overridden.
I have tried to get the DockPanel.ActualHeight and .ActualWidth. both are NaN and the DockPanel.Width and Height = 0.
public MainWindow()
{
ImageDataModel = new ImageDataModel();
DataContext = ImageDataModel;
InitializeComponent();
WindowState = WindowState.Maximized;
var tmpWindow = Window.GetWindow(BaseDockPanel);
Rect rect = new Rect(0, 0, BaseDockPanel.ActualWidth, BaseDockPanel.ActualHeight);
bitmapCanvas = new BitMapCanvas(rect);
BaseDockPanel.Children.Add(bitmapCanvas);
}
I would like to be able to create a windows.Rect that is the actual dimension of the DockPanel.center height and width. Currently the height and width for the control are 0.
At the point of the construction of MainWindow, the child controls within the Window, in your case BaseDockPanel has not been loaded yet.
That is why you're getting a 0 for it's ActualWidth and ActualHeight.
What you can do is add an EventHandler for the Window's Loaded event as shown below.
public MainWindow()
{
ImageDataModel = new ImageDataModel();
DataContext = ImageDataModel;
InitializeComponent();
WindowState = WindowState.Maximized;
this.Loaded += Window_Loaded;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var tmpWindow = Window.GetWindow(BaseDockPanel);
// You will get your ActualWidth and ActualHeight here.
Rect rect = new Rect(0, 0, BaseDockPanel.ActualWidth, BaseDockPanel.ActualHeight);
bitmapCanvas = new BitMapCanvas(rect);
BaseDockPanel.Children.Add(bitmapCanvas);
}

Trying to set a background image that always covers the form, but has a static/fixed aspect ratio

So I've been looking around on this question quite a bit, but the results always keep coming back to either setting BackgroundImageLayout to "Stretch". Which, of course, doesn't do anything about maintaining the aspect ratio of the original image or to tutorials on how to scale a PictureBox or other loaded images manually while retaining the aspect ratio, but I cannot (very surprisingly to me) find anything about how to get a WinForm to manage a background image that's always centered on the window, stretched to cover it, but maintaining the same aspect ratio as the original.
I tried this:
public class CustomForm : Form
{
protected Size _scale;
protected Size _originalSize;
protected float _aspectRatio;
public CustomForm() : base()
{
this.DoubleBuffered = true;
}
private float AspectRatio() { return this.AspectRatio(this._originalSize); }
private float AspectRatio(int width, int height) { return (float)width / height; }
private float AspectRatio(Size value) { return this.AspectRatio(value.Width, value.Height); }
protected override void OnBackgroundImageChanged(EventArgs e)
{
this._originalSize = new Size(this.BackgroundImage.Width, this.BackgroundImage.Height);
this._aspectRatio = this.AspectRatio(this._originalSize);
base.OnBackgroundImageChanged(e);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (this.BackgroundImage == null) base.OnPaintBackground(e);
else
{
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), ClientRectangle);
int x = (this._scale.Width > this.Width) ? (int)Math.Round((this._scale.Width - this.Width) / 2.0) : 0;
int y = (this._scale.Height > this.Height) ? (int)Math.Round((this._scale.Height - this.Height) / 2.0) : 0;
e.Graphics.DrawImage(this.BackgroundImage, new Rectangle(x, y, this._scale.Width, this._scale.Height));
}
}
protected override void OnSizeChanged(EventArgs e)
{
if ( this._aspectRatio > this.AspectRatio(this.Size))
{
this._scale.Width = this.Width;
this._scale.Height = (int)(this.Width / this._aspectRatio);
}
else
{
this._scale.Height = this.Height;
this._scale.Width = (int)(this.Height * this._aspectRatio);
}
base.OnSizeChanged(e);
}
}
But all it does is fix the image in the top-left corner of the form at it's basic dimensions, regardless of how the form is (re)sized...
Can someone please point me in the direction of a proper solution, or to what I've got / done wrong thus far?
Thanks!
Okay, after a bunch more research, and with some bodging, I finally got this sorted out! Posting this here for anyone who may come looking for it in the future!
public class CustomForm : Form
{
protected Size _scale;
protected Size _originalSize;
public CustomForm() : base()
{
this.InitializeComponent();
}
protected override void OnBackgroundImageChanged(EventArgs e)
{
this._originalSize = new Size(this.BackgroundImage.Width, this.BackgroundImage.Height);
base.OnBackgroundImageChanged(e);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if ((BackgroundImage != null) && (BackgroundImageLayout == ImageLayout.None))
{
Point p = new Point(
(int)((this.Width - this._scale.Width) / 2),
(int)((this.Height - this._scale.Height) / 2)
);
e.Graphics.DrawImage(this.BackgroundImage, new Rectangle(p, this._scale));
}
else
base.OnPaintBackground(e);
}
protected override void OnSizeChanged(EventArgs e)
{
if ((BackgroundImage != null) && (BackgroundImageLayout == ImageLayout.None))
{
float rX = (float) this.Width / this._originalSize.Width;
float rY = (float) this.Height / this._originalSize.Height;
float r = (rX < rY) ? rY : rX;
this._scale.Height = (int)(this._originalSize.Height * r);
this._scale.Width = (int)(this._originalSize.Width * r);
}
base.OnSizeChanged(e);
}
private void InitializeComponent()
{
this._originalSize = new Size(0, 0);
this._scale = new Size(0, 0);
this.SuspendLayout();
//
// CustomForm
//
this.Name = "CustomForm";
this.DoubleBuffered = true; // minimizes "flicker" when resizing
this.ResizeRedraw = true; // forces a full redraw when resized!
this.ResumeLayout(false);
}
}

OnFontChange method

I have my control MyLabel and when I change the Font Size must perform this code in the constructor. How to make this code worked?
protected override void OnFontChanged(EventArgs e)
{
if (AutoSize_)
{
this.AutoSize = true;
remember_size = this.Size;
this.AutoSize = false;
this.Size = new Size(remember_size.Width, remember_size.Height);
remember_size = this.Size;
}
...
this.Invalidate();
}
But don't work. For example this code work:
protected override void OnFontChanged(EventArgs e)
{
if (AutoSize_)
{
this.AutoSize = true;
}
...
this.Invalidate();
}
If your goal is to resize the label so the text is visible, regardless of the font size, the AutoSize property will do this for you.. However if you for some reason wish to handle this with your own code, you could try setting the AutoSize property to false (and not change it..)
The following code method can be called from any Form, UserControl, or Control. It will return the size of the specified text in the specified font.
public static Size MeasureText(Graphics graphicsDevice, String text, Font font)
{
System.Drawing.SizeF textSize = graphicsDevice.MeasureString(text, font);
int width = (int)Math.Ceiling(textSize.Width);
int heigth = (int)Math.Ceiling(textSize.Height);
Size size = new Size(width, heigth);
return size;
}
Now you will need to check that the label has not outgrown the parent container, which would cause some of the labels text to be cut off. Something like the following will accomplish this:
private void ResizeParentAccordingToLabelSize(Label resizedLabel)
{
int necessaryWidth = resizedLabel.Location.X + resizedLabel.Width;
int necessaryHeight = resizedLabel.Location.Y + resizedLabel.Height;
if (necessaryWidth > this.Width)
{
this.Width = necessaryWidth;
}
if (necessaryHeight > this.Height)
{
this.Height = necessaryHeight;
}
}

Standalone scroll bar in C# Text box

In my project, I need a stand alone scroll bar for my text box. So I have created a customized scroll bar. Any idea How to use my customized scroll bar(both Horizontal and Vertical) in the text box instead of built-in scroll bar?
Please find my sample code below. (In original code the textbox and the scrollbar is skinnable. I cannot post the actual code here...)
public partial class EditControl : Control
{
int BORDERWIDTH = SystemInformation.Border3DSize.Width;
int SCROLLBARWIDTH = SystemInformation.VerticalScrollBarWidth;
CustomTextBox editCtrl;
VScrollBar vScrollBar = null;
public EditControl()
{
InitializeComponent();
editCtrl = new CustomTextBox();
this.Width = 200 + SCROLLBARWIDTH;
this.Height = 140;
editCtrl.Width = this.Width - SCROLLBARWIDTH;
editCtrl.Height = this.Height;
editCtrl.Multiline = true;
editCtrl.Left = Left;
vScrollBar = new VScrollBar();
vScrollBar.Height = this.Height;
vScrollBar.Location = new Point(editCtrl.Width, 1);
vScrollBar.Scroll += new ScrollEventHandler(vScrollBar_Scroll);
this.Controls.Add(editCtrl);
this.Controls.Add(vScrollBar);
}
private void vScrollBar_Scroll(object sender, ScrollEventArgs e)
{
//Code to scroll the text box
//editCtrl.ScrollTo(position);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
editCtrl.Width = this.Width - SCROLLBARWIDTH;
editCtrl.Height = this.Height;
}
public partial class CustomTextBox : TextBox
{
public CustomTextBox()
{
//InitializeComponent();
}
public void ScrollTo(int Position)
{
//Code to scroll the contents.
}
}
}
}
The problem is solved. I have masked the original scrollbar of textbox by my customized scrollbar. GetScrollInfo API is used to synch the scrollbars.

How to keep the aspect ratio of a UserControl?

Does anyone have an idea how to keep the Height/Width Ratio 1:1 of a UserControl?
E.g. if Height > Width, Width & Height will have the same size and vice versa.
I'm not sure this will work, but if you register a handler for the SizeChanged event and in there put in your code keep the aspect ratio 1:1.
The SizeChangedEventArgs argument has the old size and the new size so you can check which has changed and update the other accordingly.
You might need to introduce a guard variable so that you don't get a cascade of SizeChanged events as a result of updating the Height or Width.
Another alternative:
<local:MyControl Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"/>
Try using a ViewBox and setting its Stretch property to Uniform
i used this code for keeping aspect ratio
inside usercontrol globally define org_width, org_height, org_ratio :
private static double org_width = 77.6;//desired width
private static double org_height = 81.4;//desired height
private static double org_ratio = org_width / org_height;
use this code inside usercontrol in SizeChanged event:
FrameworkElement UCborder = this;
UCborder.Width = UCborder.Height*org_ratio;
and finally your user control code should looks like this:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace yournamespace
{
public partial class YourUserControl : UserControl
{
private static double org_width = 77.6;//desired width
private static double org_height = 81.4;//desired height
private static double org_ratio = org_width / org_height; // width/height
public YourUserControl()
{
InitializeComponent();
}
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
FrameworkElement UCborder = this;
UCborder.Width = UCborder.Height*org_ratio;
}
}
}
good luck
private bool isSizeChangeDefered;
private void uiElement_SizeChanged(object sender, SizeChangedEventArgs e)
{
//Keep Acpect Ratio
const double factor = 1.8;
if(isSizeChangeDefered)
return;
isSizeChangeDefered = true;
try
{
if (e.WidthChanged)
{
driverPan.Height = e.NewSize.Width * factor;
}
if (e.HeightChanged)
{
driverPan.Height = e.NewSize.Width / factor;
}
}
finally
{
// e.Handled = true;
isSizeChangeDefered = false;
}
}
maybe this helps... cheers

Categories