How to get DPI device to PCL in Xamarin. Forms? - xamarin

I need to get DPI device in my Xamarin class PCL. I do not want to use Xamarin.Essentials. Can I do this using Native interfaces, if its possible, how can I do it?

in your pcl create a new interface called IDisplayInfo:
public interface IDisplayInfo
{
int GetDisplayWidth();
int GetDisplayHeight();
int GetDisplayDpi();
}
In your android implementation, add a new class:
[assembly: Dependency(typeof(DisplayInfo))]
namespace YourAppNamespace.Droid
{
public class DisplayInfo : IDisplayInfo
{
public int GetDisplayWidth()
{
return (int)Android.App.Application.Context.Resources.DisplayMetrics.WidthPixels;
}
public int GetDisplayHeight()
{
return (int)Android.App.Application.Context.Resources.DisplayMetrics.HeightPixels;
}
public int GetDisplayDpi()
{
return (int)Android.App.Application.Context.Resources.DisplayMetrics.DensityDpi;
}
}
}
and in the iOS implementation, add the same class:
[assembly: Dependency(typeof(DisplayInfo))]
namespace YourNamespace.iOS
{
public class DisplayInfo : IDisplayInfo
{
public int GetDisplayWidth()
{
return (int)UIScreen.MainScreen.Bounds.Width;
}
public int GetDisplayHeight()
{
return (int)UIScreen.MainScreen.Bounds.Height;
}
public int GetDisplayDpi()
{
return (int)(int)UIScreen.MainScreen.Scale;
}
}
}
Now in your shared code, you can call
int dpi = DependencyService.Get<IDisplayInfo>().GetDisplayDpi();
and should be good to go. Note that i also added methods for getting screen width and height, basically because i already had them in my code and since you are probably going to need them sooner or later anyways.

Currently Device Display Information available via official Xamarin.Essentials nuget package, see:
https://learn.microsoft.com/en-us/xamarin/essentials/device-display
// Get Metrics
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
// Orientation (Landscape, Portrait, Square, Unknown)
var orientation = mainDisplayInfo.Orientation;
// Rotation (0, 90, 180, 270)
var rotation = mainDisplayInfo.Rotation;
// Width (in pixels)
var width = mainDisplayInfo.Width;
// Height (in pixels)
var height = mainDisplayInfo.Height;
// Screen density
var density = mainDisplayInfo.Density;

I think it will help you. Here you have it described
enter link description here

I have a static class Core to store some shared stuff defined the shared code.
On app start it's receiving values for later use everywhere:
Android MainActivity OnCreate:
Core.IsAndroid = true;
Core.DisplayDensity = Resources.DisplayMetrics.Density;
iOS AppDelegate FinishedLaunching:
Core.IsIOS = true;
Core.DisplayDensity = (float)(UIScreen.MainScreen.NativeBounds.Width / UIScreen.MainScreen.Bounds.Width);

Related

How can I get VisualElement coordinates on screen iOS Xamarin

I need to know VisualElement coordinates in device screen coordinate space on iOS in Xamarin project.
Android has its own method GetLocationOnScreen
But iOS hasn't
I found this solution:
public static Point GetScreenCoords(this VisualElement view)
{
var result = new Point(view.X, view.Y);
while (view.Parent is VisualElement parent)
{
result = result.Offset(parent.X, parent.Y);
view = parent;
}
return result;
}
but properties X ad Y of VisualElement are relatives to parents bounds and don't provide required values.
You can use DependencyService to implement the function. I wrote a simple case to get the coordinates of VisualElement. In my simple, I need to get the border of the element relative to the parent element (the child element is inside the parent element). If no parent element is specified, the method returns the bounds of the element relative to the window. You can refer to this:
Create a dependent interface:
public interface IMyLocation
{
RectangleF GetCoordinates(VisualElement element, VisualElement parentElement);
}
iOS implementation:
[assembly: Xamarin.Forms.Dependency(typeof(MyLocation))]
namespace FormsDemoWang.iOS
{
public class MyLocation : IMyLocation
{
public RectangleF GetCoordinates(VisualElement element, VisualElement parentElement)
{
IVisualElementRenderer renderer = Platform.GetRenderer(element);
UIView elementNativeView = renderer.NativeView;
UIView parentNativeView = null;
if (parentNativeView != null) {
parentNativeView = Platform.GetRenderer(parentElement).NativeView;
}
CGRect rect = elementNativeView.ConvertRectToView(elementNativeView.Frame, parentNativeView);
float x = (float)Math.Round(rect.X);
float y = (float)Math.Round(rect.Y);
float width = (float)Math.Round(rect.Width);
float height = (float)Math.Round(rect.Height);
return new RectangleF(x, y, width, height);
}
}
}
Used in Forms:
var coordinates = DependencyService.Get<IMyLocation>().GetCoordinates(myElement, parentElement);
var xlable = new Label { Text = $"X:{coordinates.X}" };
var ylable = new Label { Text = $"Y:{coordinates.Y}" };

I can't use a Winforms Control created with a Class

I was looking for a Circular Picture Box for my app and I stumbled across this code (IT IS NOT MINE) and I've tried as many times as I could but I can't find any mistake. I have followed every step that was made in the tutorial for this Rounded Picture Box so it can't be a miscopy because it was working perfectly in the tutorial.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
namespace New_Radio_Barcelona.Controls
{
class RashiCircularPictureBox : PictureBox
{
private int border = 2;
private Color colorBorder = Color.RoyalBlue;
private Color colorBorder2 = Color.HotPink;
private DashStyle borderstyle = DashStyle.Solid;
private DashCap borderCap = DashCap.Flat;
private float gradiant = 50f;
public RashiCircularPictureBox()
{
this.Size = new Size(95, 95);
this.SizeMode = PictureBoxSizeMode.StretchImage;
}
public int Border
{
get
{
return border;
}
set
{
border = value;
this.Invalidate();
}
}
public Color ColorBorder
{
get
{
return colorBorder;
}
set
{
colorBorder = value;
this.Invalidate();
}
}
public Color ColorBorder2
{
get
{
return colorBorder2;
}
set
{
colorBorder2 = value;
this.Invalidate();
}
}
public DashStyle Borderstyle
{
get
{
return borderstyle;
}
set
{
borderstyle = value;
this.Invalidate();
}
}
public DashCap BorderCap
{
get
{
return borderCap;
}
set
{
borderCap = value;
this.Invalidate();
}
}
public float Gradiant
{
get
{
return gradiant;
}
set
{
gradiant = value;
this.Invalidate();
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Size = new Size(this.Width, this.Width);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
var graphic = pe.Graphics;
var rect = Rectangle.Inflate(this.ClientRectangle, -1, -1);
var rectborder = Rectangle.Inflate(rect, -border, -border);
var size = border > 0 ? border * 3 : 1;
using (var bordercolorG = new LinearGradientBrush(rectborder, colorBorder, colorBorder2, gradiant))
using (var path = new GraphicsPath())
using (var pen = new Pen(this.Parent.BackColor, border))
using (var penborder = new Pen(bordercolorG, size))
{
graphic.SmoothingMode = SmoothingMode.AntiAlias;
penborder.DashStyle = borderstyle;
penborder.DashCap = borderCap;
path.AddEllipse(rect);
this.Region = new Region(path);
graphic.DrawEllipse(pen, rect);
if (border > 0)
{
graphic.DrawEllipse(penborder, rectborder);
}
}
}
}
}
I compile the project and then try to add it to the Design tab as shown in the tutorial. It says it could not be loaded. I was trying to understand what is not working properly but I still do not find the mistake. Some help plis?
Another aspect to take into consideration is the fact that in class RashiCircularPictureBox : PictureBox puts 1 reference above the code and in public RashiCircularPictureBox() it says 0 references. It may be for this but I'm no expert on Classes and I'm stuck in this stupidity. if anyone could clear my mind about this issue I would be so grateful about it
The designer in most versions of Visual Studio up until recently has been a 32-bit process. So if the control was built as 64-bit, it wouldn’t be able to load it at design-time, but VS would still be able to create 64-bit applications that can use the 64-bit control at runtime.
This means if you build your control as 32-bit or AnyCPU, it should solve the design-time loading problem.
The release notes of Visual Studio 2022 version 17.0.0 state that “devenv.exe is now 64-bit only”. I haven’t tried this myself, but it probably means you can now use 64-bit controls at design time with the newer versions of VS.
In all cases, AnyCPU should work.

Xamarin: Detect device rotation when orientation is locked? For iOS and/or Android?

I am working on a Xamarin.Forms app that plays videos similar to a YouTube type app. I want the video to go full screen when the device rotates (like youtube does) but I also want the orientation to be locked in to portrait. Every post or tutorial I've found points to using custom renders for detecting orientation change to determine when the device rotates, but when orientation is locked those events do not fire.
Is there a way to detect device rotation without depending on orientation changing?
On iOS you would get device orientation with:
var orientation = UIDevice.CurrentDevice.Orientation;
On Android you need to ask the Window Manager:
var windowManager = ApplicationContext.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
var orientation = windowManager.DefaultDisplay.Rotation;
You don't need a custom renderer, but you could suffice with a service you register in the service locator. This could looks something like.
In shared code:
public enum Orientation
{
None,
PortraitUp,
PortraitDown,
LandscapeLeft,
LandscapeRight
}
public interface IOrientationService
{
Orientation GetCurrentOrientation();
}
On Android:
[assembly: Dependency(typeof(AndroidOrientationService))]
public class AndroidOrientationService : IOrientationService
{
private readonly IWindowManager _windowManager;
public AndroidOrientationService()
{
_windowManager = ApplicationContext.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
}
public Orientation GetCurrentOrientation()
{
switch (_windowManager.DefaultDisplay.Rotation)
{
case SurfaceOrientation.Rotation0:
return Orientation.PortraitUp;
case SurfaceOrientation.Rotation180:
return Orientation.PortraitDown;
case SurfaceOrientation.Rotation90:
return Orientation.LandscapeLeft;
case SurfaceOrientation.Rotation270:
return Orientation.LandscapeRight;
default:
return Orientation.None;
}
}
}
Similarly on iOS:
[assembly: Dependency(typeof(IosOrientationService))]
public class IosOrientationService : IOrientationService
{
public Orientation GetCurrentOrientation()
{
switch (UIDevice.CurrentDevice.Orientation)
{
case UIDeviceOrientation.LandscapeLeft:
return Orientation.LandscapeLeft;
case UIDeviceOrientation.LandscapeRight:
return Orientation.LandscapeRight;
case UIDeviceOrientation.Portrait:
return Orientation.PortraitUp;
case UIDeviceOrientation.PortraitUpsideDown:
return Orientation.PortraitDown;
default:
return Orientation.None;
}
}
}
Then in your code you should be able to get the orientation like:
var orientationService = DependencyService.Get<IOrientationService>();
var orientation = orientationService.GetCurrentOrientation();
EDIT: detecting orientation changes
If you want to detect orientation changes on iOS you can do that by adding an observer for UIDeviceOrientation.
UIDevice.Notifications.ObserveOrientationDidChange(OnOrientationChanged);
Similarly on Android you can use SensorManager to listen to SensorType.Orientation changes. It has a bit more moving parts but looks something like follows.
You need to create a ISensorEventListener class:
class MyOrientationListner : Java.Lang.Object, ISensorEventListener
{
public event EventHandler OrientationChanged;
public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy)
{
}
public void OnSensorChanged(SensorEvent e)
{
OrientationChanged?.Invoke(this, EventArgs.Empty);
}
}
Then you need to get the sensor manager from the current Context and start listening to orientation change events:
_sensorManager = context.GetSystemService(Context.SensorService).JavaCast<SensorManager>();
var sensor = _sensorManager.GetDefaultSensor(SensorType.Orientation);
var listener = new MyOrientationListner();
listener.OrientationChanged += OnOrientationChanged;
_sensorManager.RegisterListener(listener, sensor, SensorDelay.Normal);
private void OnOrientationChanged(object sender, EventArgs e)
{
OrientationChanged?.Invoke(this, GetCurrentOrientation());
}
Where OrientationChanged is a event in the IOrientationService:
event EventHandler<Orientation> OrientationChanged;
Then you can listen to that event where needed.
For iOS
In AppDelegate.cs override the below method
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application,UIWindow forWindow)
{
if (Xamarin.Forms.Application.Current == null || Xamarin.Forms.Application.Current.MainPage == null)
{
return UIInterfaceOrientationMask.Portrait;
}
var mainPage = Xamarin.Forms.Application.Current.MainPage;
if (mainPage is YourPage || (mainPage is NavigationPage &&
((NavigationPage)mainPage).CurrentPage is YourPage) || (mainPage.Navigation != null &&
mainPage.Navigation.ModalStack.LastOrDefault() is YourPage))
{
if (Configuration.IsFullScreen)
{
return UIInterfaceOrientationMask.Landscape;
}
}
return UIInterfaceOrientationMask.Portrait;
}
In a Dependency Service write the below method
public void ChangeLandscapeOrientation()
{
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.LandscapeLeft), new NSString("orientation"));
UINavigationController.AttemptRotationToDeviceOrientation();
}
Call the ChangeLandscapeOrientation method wherever you need it.
For Android
In a Dependency Service write the below method to change the orientation to Landscape
public void ChangeLandscapeOrientation()
{
var activity = (Activity)Xamarin.Forms.Forms.Context;
{
activity.RequestedOrientation = ScreenOrientation.Landscape;
var attrs = activity.Window.Attributes;
_originalFlags = attrs.Flags;
attrs.Flags |= Android.Views.WindowManagerFlags.Fullscreen;
activity.Window.Attributes = attrs;
}
}
Below code to change the orientation to Portrait
public void ChangePortraitOrientation()
{
var activity = (Activity)Xamarin.Forms.Forms.Context;
{
activity.RequestedOrientation = ScreenOrientation.Portrait;
var attrs = activity.Window.Attributes;
attrs.Flags = _originalFlags;
activity.Window.Attributes = attrs;
}
}
Hope it helps!

Error converting type SurfaceOrientation to int Xamarin Android Visual Studio

I am making a video app in xamarin using visual studio on windows platform.
I have build the app but the video preview is rotated 90 degrees anti-clockwise and having difficulty setting orientation display to rotate 90 degrees clockwise before recording a video. My code is:
namespace XamarinVideoApp
{
[Activity(Label = "XamarinVideoApp", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
MediaRecorder recorder;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
...
}
protected override void OnDestroy()
{
base.OnDestroy();
if(recorder != null)
{
...
}
}
public static void setCameraDisplayOrientation(Activity activity, int cameraId, Android.Hardware.Camera camera)
{
Android.Hardware.Camera.CameraInfo info = new Android.Hardware.Camera.CameraInfo();
Android.Hardware.Camera.GetCameraInfo(cameraId, info);
int rotation = (int) activity.WindowManager.DefaultDisplay.Rotation;
int degrees = 0;
switch(rotation)
{
case SurfaceOrientation.Rotation0: /* Shows Error here: Cannot implicitly convert type 'Android.Views.SurfaceOrientation' to 'int'. An explicit conversion exists (are you missing a cast?) */
degrees = 0;
}
}
}
}
What should I do to remove the error mentioned at the above switch statement?
The issue is that you're casting the activity.WindowManager.DefaultDisplay.Rotation to an int. Simply do the following instead:
Android.Hardware.Camera.CameraInfo info = new
Android.Hardware.Camera.CameraInfo();
Android.Hardware.Camera.GetCameraInfo(0, info);
var rotation = activity.WindowManager.DefaultDisplay.Rotation;
int degrees = 0;
switch (rotation)
{
case SurfaceOrientation.Rotation0:
degrees = 0;
break;
}
Also remember to break out of your switch.
Additionally, please do note that Camera.CameraInfo was deprecated in API 21. You should consider using the SensorOrientation for Camera2 instead.
This can be done as follows in Xamarin.Android:
var info = Android.Hardware.Camera2.CameraCharacteristics.SensorOrientation;

Blackberry support multiple resolutions [duplicate]

This question already has answers here:
Supporting multiple screens - Blackberry
(2 answers)
Closed 8 years ago.
I need to design the UI for a blackberry app. This app should support multiple blackberry resolutions.
One way would be to check the screen_width and screen_height every time, and accordingly fetch images from the res folder. Is there any other more efficient, or a better method to do this? I will also need to do the same for font sizes, of text, according to screen size.
Please help me know the standard method to support multiple BB resolutions
You can try, what I have tried in my applications without any issues so far.
Step 1:
Create a separate package like com.your_app_name.uiconfig which will
contain an abstract class say ModelConfig and different classes for
different resolutions like class BB83xxConfig for resolution 320x240
(width x height), class BB95xxConfig for resolution 360 x 480 (width x
height).
Step 2:
ModelConfig class will provide the concrete implementation of the
methods that will be common to all irrespective of the screen
resolutions and the declaration of abstract methods whose concrete
implementation will be provided in the respective classes based on the
screen resolution.
Step 3: Make each and every class, that is implemented for particular resolution extend ModelConfig and provide concrete
implementation of methods as per requirement.
Step 4: Use singleton pattern to get the instance of ModelConfig, so that it is intantiated only once and that instance is used
throughout.
ModelConfig.java (Just a sample)
public abstract class ModelConfig {
private static ModelConfig modelConfig = null;
public static ModelConfig getConfig() {
if (modelConfig == null) {
if (DeviceInfo.getDeviceName().startsWith("83")) {
modelConfig = new BB83xxConfig();
} else if (DeviceInfo.getDeviceName().startsWith("85")) {
// 85xx also has 360 x 240 same as 83xx device
modelConfig = new BB83xxConfig();
} else if (DeviceInfo.getDeviceName().startsWith("89")) {
modelConfig = new BB89xxConfig();
} else if (DeviceInfo.getDeviceName().startsWith("90")) {
modelConfig = new BB90xxConfig();
} else if (DeviceInfo.getDeviceName().startsWith("95")) {
modelConfig = new BB95xxConfig();
} else if (DeviceInfo.getDeviceName().startsWith("96")) {
modelConfig = new BB96xxConfig();
} else if (DeviceInfo.getDeviceName().startsWith("97")) {
modelConfig = new BB97xxConfig();
} else if (DeviceInfo.getDeviceName().startsWith("99")) {
modelConfig = new BB99xxConfig();
} else if (DeviceInfo.getDeviceName().startsWith("98")) {
// 9800 also has 360 x 480 same as 95xx device
modelConfig = new BB95xxConfig();
}else {
modelConfig = new DefaultConfig();
}
}
return modelConfig;
}
// Font height for the default font used for the application.
public abstract int getApplicationFontHeight();
// Font height for the header label font.
public abstract int getHeaderLabelFontHeight();
// Height for the coloured background of Header.
public abstract int getHeaderBarHeight();
// Height for the individual row in the list.
public abstract int getCustomListRowHeight();
public abstract int getStandardButtonWidth();
public abstract int getStandardLabelWidth();
public abstract int getTitleFontHeight();
// get Background colour for Header.
public int getHeaderBackgroundColor() {
return 0x26406D;
}
// get Bitmap showing Right Arrow.
public Bitmap getBitmapRightArrow() {
return Bitmap.getBitmapResource("right_arrow.png");
}
// get Bitmap rounded black border for editfield.
public Bitmap getBitmapRoundedBorderEdit(){
return Bitmap.getBitmapResource("rounded_border_black.png");
}
// get Bitmap rounded gray border and white background.
public Bitmap getBtmpRoundedBorderBgrnd(){
return Bitmap.getBitmapResource("rounded_border_grey.png");
}
// get Bitmap rounded gray border and white background.
public Bitmap getBtmpTransparentBgrnd(){
return Bitmap.getBitmapResource("img_transparent_background.png");
}
// get Bitmap showing down Arrow.
public Bitmap getBitmapDownArrow(){
return Bitmap.getBitmapResource("down_arrow.png");
}
}
BB95xxConfig.java (just a sample)
/*
* Common resolution 360*480 pixels (width x height)
*/
public class BB95xxConfig extends ModelConfig {
// Font height for the default font used for the application.
// returns Desired height in pixels.
public int getApplicationFontHeight() {
return 18;
}
// Font height for the header label font.
// returns Desired height in pixels.
public int getHeaderLabelFontHeight() {
return 20;
}
// returns Desired height in pixels for the header background.
public int getHeaderBarHeight() {
return Display.getHeight() / 10;
}
public int getCustomListRowHeight() {
return 50;
}
public int getStandardButtonWidth() {
return 108;
}
public int getStandardLabelWidth() {
return 150;
}
public int getTitleFontHeight() {
return 11;
}
}

Resources