|
Hello,
first of all I just need a hint on how to approach this topic in the most sensible way.
I have an older application that can only determine its coordinates correctly when it is running on only one monitor. Now the application needs to run in environments with multiple screens with different resolutions. Example: Notebook with 1920x1080 + 3840×2160 + 1920x1200 (main screen). In addition, screens with a scaling other than just 100% can be run (e.g. 175%). As you can see here, a colourful mixture.
I would like to determine the exact position and window size of the program.
I am primarily a backend developer, so I have never had anything to do with this topic before. For this reason, I am turning to you in the hope of getting a push before I go the wrong direction.
Many thanks in advance and best regards
René
|
|
|
|
|
Hi René!
You are going to have to give more detail as to what exactly you need help with - I have three monitors here, all different resolutions (1920x1080, 1080x1920, and 1280x1024 @ 100%), plus my Surface Go 2 (1920x1280 @ 150%) which I can write / test apps on (plus gawd knows what once the software is released) and generally it's not a problem.
Why does you app need to "know where it is"? What is it doing that it needs this? What will it do with the info? What's the problem in finding out?
My apps generally "remember" where they were, and restore their position on that particular system:
#region Event Handlers
#region Form
private void FrmMain_Load(object sender, EventArgs e)
{
if ((ModifierKeys & Keys.Shift) == 0)
{
this.LoadLocation();
}
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if ((ModifierKeys & Keys.Shift) == 0)
{
this.SaveLocation();
}
}
Is part of my standard "new form" code, along with the support methods:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Data;
using System.Drawing;
using System.Diagnostics;
namespace OneOffJobs
{
public static class Storage
{
#region Constants
private const int SW_RESTORE = 9;
#endregion
#region DLL Imports
[DllImport("User32")]
private static extern int SetForegroundWindow(IntPtr hwnd);
[DllImportAttribute("User32.DLL")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
#endregion
#region Fields
#region Internal
#endregion
#region Property bases
#endregion
#endregion
#region Properties
public static Guid AppGuid
{
get
{
Assembly asm = Assembly.GetEntryAssembly();
object[] attr = (asm.GetCustomAttributes(typeof(GuidAttribute), true));
return new Guid((attr[0] as GuidAttribute).Value);
}
}
public static Guid AssemblyGuid
{
get
{
Assembly asm = Assembly.GetExecutingAssembly();
object[] attr = (asm.GetCustomAttributes(typeof(GuidAttribute), true));
return new Guid((attr[0] as GuidAttribute).Value);
}
}
public static string UserDataFolder
{
get
{
Guid appGuid = AppGuid;
string folderBase = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string dir = string.Format(@"{0}\{1}\", folderBase, appGuid.ToString("B").ToUpper());
return CheckDir(dir);
}
}
public static string UserRoamingDataFolder
{
get
{
Guid appGuid = AppGuid;
string folderBase = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string dir = string.Format(@"{0}\{1}\", folderBase, appGuid.ToString("B").ToUpper());
return CheckDir(dir);
}
}
public static string AllUsersDataFolder
{
get
{
Guid appGuid = AppGuid;
string folderBase = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
string dir = string.Format(@"{0}\{1}\", folderBase, appGuid.ToString("B").ToUpper());
return CheckDir(dir);
}
}
public static string LocationsStorageFile { get; private set; }
#endregion
#region Regular Expressions
#endregion
#region Enums
#endregion
#region Constructors
static Storage()
{
LocationsStorageFile = UserDataFolder + "control.storage.locations";
}
#endregion
#region Events
#region Event Constructors
#endregion
#region Event Handlers
#endregion
#endregion
#region Public Methods
public static void SaveLocation(this Control control, string instance = null)
{
string controlName = control.GetType().Name;
if (!File.Exists(LocationsStorageFile))
{
CreateBlankLocationFile();
}
if (!(control is Form f) || (f.Visible && f.WindowState == FormWindowState.Normal))
{
DataTable dt = ReadXML(LocationsStorageFile);
if (dt.Columns.Count >= 6)
{
bool ignoreInstance = string.IsNullOrWhiteSpace(instance);
DataRow current = dt.NewRow();
current["ControlName"] = controlName;
current["Instance"] = instance ?? "";
foreach (DataRow row in dt.Rows)
{
if (row["ControlName"] as string == controlName && (ignoreInstance || row["Instance"] as string == instance))
{
dt.Rows.Remove(row);
break;
}
}
current["LocationX"] = control.Location.X;
current["LocationY"] = control.Location.Y;
current["SizeW"] = control.Size.Width;
current["SizeH"] = control.Size.Height;
dt.Rows.Add(current);
WriteXML(dt, LocationsStorageFile);
}
}
}
public static void LoadLocation(this Control control, string instance = null)
{
string controlName = control.GetType().Name;
if (!File.Exists(LocationsStorageFile))
{
CreateBlankLocationFile();
}
DataTable dt = ReadXML(LocationsStorageFile);
if (dt.Columns.Count >= 6)
{
bool ignoreInstance = string.IsNullOrWhiteSpace(instance);
DataRow current = dt.NewRow();
current["ControlName"] = controlName;
current["Instance"] = instance ?? "";
current["LocationX"] = control.Location.X;
current["LocationY"] = control.Location.Y;
current["SizeW"] = control.Size.Width;
current["SizeH"] = control.Size.Height;
foreach (DataRow row in dt.Rows)
{
if (row["ControlName"] as string == controlName && (ignoreInstance || row["Instance"] as string == instance))
{
current = row;
if (int.TryParse(current["LocationX"].ToString(), out int x) &&
int.TryParse(current["LocationY"].ToString(), out int y) &&
int.TryParse(current["SizeW"].ToString(), out int w) &&
int.TryParse(current["SizeH"].ToString(), out int h))
{
control.Location = new Point(x, y);
control.Size = new Size(w, h);
}
break;
}
}
}
}
public static void SingleInstance(this Process thisProcess)
{
foreach (Process proc in Process.GetProcessesByName(thisProcess.ProcessName))
{
if (proc.Id != thisProcess.Id)
{
ShowWindow(proc.MainWindowHandle, SW_RESTORE);
SetForegroundWindow(proc.MainWindowHandle);
thisProcess.Kill();
}
}
}
#endregion
#region Overrides
#endregion
#region Private Methods
private static string CheckDir(string dir)
{
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
return dir;
}
private static DataTable ReadXML(string file)
{
using (DataSet ds = new DataSet())
{
ds.ReadXml(file);
DataTable dt;
if (ds.Tables.Count > 0)
{
dt = ds.Tables[0];
ds.Tables.Remove(dt);
}
else
{
dt = new DataTable("Empty");
}
return dt;
}
}
private static void WriteXML(DataTable dt, string file)
{
using (DataSet ds = new DataSet())
{
ds.Tables.Add(dt);
ds.WriteXml(file);
ds.Tables.Remove(dt);
}
}
private static void CreateBlankLocationFile()
{
DataTable dtNew = new DataTable("Locations");
dtNew.Columns.Add("ControlName", typeof(string));
dtNew.Columns.Add("Instance", typeof(string));
dtNew.Columns.Add("LocationX", typeof(int));
dtNew.Columns.Add("LocationY", typeof(int));
dtNew.Columns.Add("SizeW", typeof(int));
dtNew.Columns.Add("SizeH", typeof(int));
DataRow dr = dtNew.NewRow();
dr["ControlName"] = "%%SAMPLE%%";
dr["Instance"] = "%%INSTANCE%%";
dr["LocationX"] = 0;
dr["LocationY"] = 0;
dr["SizeW"] = 200;
dr["SizeH"] = 200;
dtNew.Rows.Add(dr);
WriteXML(dtNew, LocationsStorageFile);
}
#endregion
}
}
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Hello "OriginalGriff"
first of all, thank you for your post. With my poor English, I'll try to explain the problem a little better.
We have an application that was programmed with SAL. SAL is a programming language developed by the Gupta company in the 90s. Gupta has a colourful history: first it was called Gupta, then Centura, later Unify and today it is owned by OpenText.
As far as window management is concerned, SAL applications unfortunately only work correctly with one screen. Working with several different screens causes SAL applications major problems.
Now SAL can integrate and use .NET DLLs developed with Framework 4.8. This is where I would like to start in order to take over the window management myself. I want to be able to determine and restore the window position and size of the SAL application on any screen, regardless of resolution and scaling.
Thank you again and best regards
René
|
|
|
|
|
THe screen resolutions and DPI settings don't matter. All you need is the Top, Left, Width, and Height properties of the window. That's it.
How you do that is a custom programming language nobody has ever heard of is entirely on you. You may be able to use .NET DLL's, but the windows are not being created by those DLL's, I assume? In that case, you're going to have to get the window handle of your apps main window somehow, then use that to call the GetWindowRect function[^] to get the screen coordinates and size rectangle.
|
|
|
|
|
Why do screen resolutions and DPI settings not matter?
The windows are not created by .NET. But I have a window handle that I could use.
|
|
|
|
|
Because you're going to get screen coordinates no matter what the resolution and DPI are.
|
|
|
|
|
Yes.
I have looked into the subject more closely. Here I find that Windows always ensures that the dpi number remains the same with different scaling. To do this, Windows changes the number of pixels. Here is an example from my Dell XPS with 3840x2400 pixels: with a scaling of 175%, Windows calculates with a screen resolution of 2194x1371. This means that the scaling at this resolution remains 100% at 96 dpi.
Nevertheless, our old software gets confused with this. I assume that it calculates the scaling factor internally incorrectly - actually, this should not be taken into account at all as long as Windows programmatically delivers a scaling of 100% at 96 dpi.
Here is a programme code that shows me (and confirms) everything essential in the debugger:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace multimonitor1
{
internal class Program
{
static void Main(string[] args)
{
List<(float, float, int)> screenDpiScales = new List<(float, float, int)>();
while (true)
{
foreach (Screen screen in Screen.AllScreens)
{
using (var form = new Form() { Bounds = screen.Bounds, TopMost = true })
{
form.Show();
Graphics g = Graphics.FromHwnd(form.Handle);
float dpiX = g.DpiX;
float dpiY = g.DpiY;
int scale = (int) Math.Round(dpiX / 96.0 * 100);
screenDpiScales.Add((dpiX, dpiY, scale));
form.Hide();
}
}
int appLeft = 0;
int appTop = 0;
int appWidth = 0;
int appHeight = 0;
for (int i = 0; i < Screen.AllScreens.Length; i++)
{
Screen screen = Screen.AllScreens[i];
(float, float, int) dpiScale = screenDpiScales[i];
Rectangle screenBounds = screen.Bounds;
float dpiX = dpiScale.Item1;
float dpiY = dpiScale.Item2;
int scale = dpiScale.Item3;
appLeft = (int) (appLeft * screenBounds.Width / (dpiX / 96.0 * scale));
appTop = (int) (appTop * screenBounds.Height / (dpiY / 96.0 * scale));
appWidth = (int) (appWidth * screenBounds.Width / (dpiX / 96.0 * scale));
appHeight = (int) (appHeight * screenBounds.Height / (dpiY / 96.0 * scale));
Console.WriteLine($"Left: {appLeft}");
Console.WriteLine($"Top: {appTop}");
Console.WriteLine($"Width: {appWidth}");
Console.WriteLine($"Height: {appHeight}");
}
Console.ReadLine();
}
}
}
}
This solves the case for me.
Many thanks and best regards
René
|
|
|
|
|
If it can "use and integrate .NET", you could bolt a WPF or Window Forms (or UWP?) "UI" onto SAL. Whether that's output only, can't tell from your post.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
I have the following models:
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string? ImageUrl { get; set; }
public virtual List<Request> Requests { get; set; }
public virtual List<Message> Messages { get; set; }
}
public class Request
{
[Key]
public int RequestId { get; set; }
public DateTime RequestTime { get; set; } = DateTime.Now;
public bool? AcceptStatus { get; set; }
public User TargetUserRef { get; set; }
public User SenderUserRef { get; set; }
}
I want the Request table to have two foreign keys. I tried to use the following fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Request>()
.HasOne(m => m.SenderUserRef)
.WithMany(t => t.Requests)
.HasForeignKey(m => m.RequestId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Request>()
.HasOne(m => m.TargetUserRef)
.WithMany(t => t.Requests)
.HasForeignKey(m => m.RequestId)
.OnDelete(DeleteBehavior.Restrict);
}
When I try to create migration, the following error is shown:
Quote: Cannot create a relationship between 'User.Requests' and 'Request.TargetUserRef' because a relationship already exists between 'User.Requests' and 'Request.SenderUserRef'. Navigations can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'Request.TargetUserRef' first in 'OnModelCreating'.
How can I solve this problem?
|
|
|
|
|
This is where you build the (test) "database first" and have EF generate the model it undertstands; instead of having to guess what your "model building" code is trying to do. Then you can try again with having some idea of what works.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
To me this is why relying on these sorts of APIs leads to problems.
Any API that attempts to simplify something always reduces complexity by removing features. That is of course the definition of simplification.
That works only as long as the needed usage is in fact simple. And more importantly that it is expected that over time it will remain simple.
If not then it will start to fail at some time. Then attempting to then use the API to solve the problem becomes a mess. Or perhaps even worse the API itself will introduce odd constructs with perhaps even odd restrictions and rules in an attempt to work around the original limitations. Those themselves will lead to problems over time.
|
|
|
|
|
You can't use the same collection navigation property to represent the requests where the user is the sender and the requests where the user is the target.
You also can't use the RequestId property as the foreign key to the users table - you're not creating a one-to-one relationship, so the request ID and the user ID will be different.
public class User
{
...
public List<Request> SentRequests { get; set; }
public List<Request> ReceivedRequests { get; set; }
...
}
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Request>()
.HasOne(m => m.SenderUserRef)
.WithMany(t => t.SentRequests)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Request>()
.HasOne(m => m.TargetUserRef)
.WithMany(t => t.ReceivedRequests)
.OnDelete(DeleteBehavior.Restrict);
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Is it possible to highlight all rows in listbox between two index values?
If it possible how is it done?
|
|
|
|
|
Member 14055879 wrote: Is it possible to highlight all rows in listbox between two index values? Yes
Member 14055879 wrote: If it possible how is it done? By writing code. Beyond that answer, we can't really support you as there are too many open questions. What technology are you using here? WPF? WinForms? Maui? Platform x...? What do you mean by highlighting? Bolding the text? Changing the background colour? Causing the rows to blink?
When you ask a question of us, you have to remember that, unless you give us details, we don't know exactly what you want to achieve. Try to add as much detail as you can and that should help you get an answer closer to the one you want.
|
|
|
|
|
Surely someone as proficient at getting a
@ tag to make his post text stick out would have no troubles translating VBNET into C#: 50[^]
Wowie!
|
|
|
|
|
Message Closed
modified 16-Mar-23 3:48am.
|
|
|
|
|
If I copy'n'paste your code into an online C# compiler:
using System;
public class Program
{
public static void Main()
{
string inputString = "A1+0001852Kg 054";
string weightValueString = inputString.Substring(3, inputString.IndexOf("Kg") - 3);
Console.WriteLine(weightValueString);
double weightKg = double.Parse(weightValueString) / 1000;
string formattedWeight = weightKg.ToString("N2");
Console.WriteLine(formattedWeight);
}
} And run it, I get what I expect:
0001852
1.85 Which means that the problem isn't the code: it's the input string that you are actually processing - and we have no access to that, to it's source hardware, or to the code that reads it from the machine.
So, it's going to be up to you.
Fortunately, you have a tool available to you which will help you find out what is going on: the debugger. If you don't know how to use it then a quick Google for "Visual Studio debugger" should give you the info you need.
Put a breakpoint on the first line in the function, and run your code through the debugger. Then look at your code, and at your data and work out what should happen manually. Then single step each line checking that what you expected to happen is exactly what did. When it isn't, that's when you have a problem, and you can back-track (or run it again and look more closely) to find out why.
Sorry, but we can't do that for you - time for you to learn a new (and very, very useful) skill: debugging!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
modified 16-Mar-23 3:19am.
|
|
|
|
|
|
You do have to wonder sometimes, don't you?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I'd say for the OP to use N3, rather than N2, but as I can't see the question on the page right now, I can't directly answer it.
|
|
|
|
|
That bit of code without the initial string assignment and the usual "It don't work" message I'm afraid.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Ah, the usual "here are all the steps I've tried, debugging operations I've undertaken, logs and traces", where the OP has reached out as a last resort eh?
|
|
|
|
|
And even delete his own question! Cleans up after himself.
|
|
|
|
|
I have an update process in my app that downloads an installer and runs it. If the user cancels the installer it throws a Win32Exception. The problem is that in the Catch all I get is the Exception with the message "The operation was cancelled by the user". Other than the text of the message, there's really no way to know for sure what exception occurred. I don't want to rely on the exception message.
Any way to trap this specific issue?
Here's my code:
private static void StartInstaller()
{
try
{
_ = Process.Start(_localSetupFile);
Process[] processes;
do
{
processes = Process.GetProcessesByName(_processName);
Thread.Sleep(1000);
}
while (processes.Length == 0);
}
catch (Win32Exception e)
{
}
catch (Exception e)
{
throw;
}
}
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Is the installer an .MSI?
|
|
|
|
|