How to get the screen resolution which was used at design time? - vb.net

I can get the current screen resolution using the code below; however, I would like to get the screen resolution that was used during the design of the forms. Is there anyway to have that information?
Why do I need this?
I have developed a program to automatically scale the controls and their font sizes based on form's last and current size. When the form size at design time is higher than the current screen resolution, some of the controls get cropped. I understand that this behaviour is a .Net framework limitation where any form size cannot be greater the screen resolution. I can overcome this limitation if I get the screen resolution used at design time.
Public Function ScreenResolution() As String
Dim intX As Integer = Screen.PrimaryScreen.Bounds.Width
Dim intY As Integer = Screen.PrimaryScreen.Bounds.Height
Return intX & " × " & intY
End Function

Related

Word VBA: Get table that is on the display screen at the moment

Suppose you have a microsoft word file (.DOCX), you open it and start to view it, using the mouse scrolling, from page 1 towards the last page.
Then, suppose you see a table, for instance, on page 4.
Now I ask: is it possible for Word-VBA to say to you what is the index or name of that table that is on the screen at the moment, no matter where is cursor located?
I want word-VBA fullfil a table that I am seeing at that moment, no matter where the cursor is located.
I hope I was clear enough...
Word doesn't have a direct way to get what's visible on-screen. It can be calculated, sort of, but not with 100% accuracy.
The following code sample does the trick for me, on my machine. It may need some tweaking to work on a different set up.
The object model does return the co-ordinates of the application window (ActiveWindow, here), the height of that Window and the UsableHeight - the height of the actual document working space. That can be used to get an estimated position.
There's also a Windows API function equivalent - RangeFromPoint - for a Window object that returns the a Range in the document for the given screen co-ordinates.
This code calculates a left and top position for the start of the visible part of the document, as well as for the end of the visible document. (In my test, it was a bit more, but not much). It then checks whether there is one or more tables within that scope. If it is, it takes the first one ( Set tbl = rngTargetStart.Tables(1)) - this returns the object your code needs to work with. As a "bonus", the code prints the index number of the table in the document and the page it's on to the Immediate Window.
Sub CheckForTableOnPage()
Dim WordWindowTop As Long 'in points
Dim WordWindowLeft As Long 'in points
Dim windowUsableHeight As Long 'in points
Dim rngTargetStart As Range
Dim rngTargetEnd As Range
Dim pageNumberTarget As Long
Dim tbl As Table
WordWindowTop = ActiveWindow.height
WordWindowLeft = ActiveWindow.left
windowUsableHeight = ActiveWindow.UsableHeight
RibbonFactor = 200
Set rngTargetStart = ActiveWindow.RangeFromPoint(WordWindowLeft, WordWindowTop - windowUsableHeight)
Set rngTargetEnd = ActiveWindow.RangeFromPoint(WordWindowLeft, WordWindowTop + windowUsableHeight)
rngTargetStart.End = rngTargetEnd.End
If rngTargetStart.Tables.Count >= 1 Then
pageNumberTarget = rngTargetStart.Information(wdActiveEndPageNumber)
Set tbl = rngTargetStart.Tables(1)
rngTargetStart.Start = ActiveDocument.Content.Start
Debug.Print "The table on page " & pageNumberTarget & " is number: " & rngTargetStart.Tables.Count
End If
End Sub

Screen capture of a particular instance of multiple application windows

I need to screen capture a particular window of a multi-window application.
A good example: I run OUTLOOK with two windows - Mail and Calendar.
When I use Process.GetProcessesByName(ApplicationToWatch).FirstOrDefault() I of course get the "first" window. How do I get access to the "second" or subsequent windows? (Interesting that there is a FirstOrDefault but not another method to get something else - I'm clearly missing something).
You already have some indications on how to get a child window Handle using EnumChildWindows, to be associated with GetClassName.
So I'll propose you another method, using UI Automation.
It's probably not so well known, but in this case it's quite straightforward and it can simplify this task.
You just need to know the Handle of the Main Window whose child you want to enumerate and filter the collection (AutomationElementCollection) of child windows using LINQ's .Where() or .FirstOrDefault() methods.
As a note, the UI Automation enumeration is not as thorough as EnumChildWindows. Also, the returned Class Names can be different, in some cases, from the actual class name of specific UI Elements.
But those are, possibly, UI Elements your not interested in.
To get the Handle of the Outlook main window, we use Process.GetProcessesByName() as usual.
The Automation Element is then aquired using the AutomationElement.FromHandle() method.
To find specific child Elements, we can use this Main Automation Element (source reference) .FindAll() method, filtering the returned collection, if needed, with .Where() - to define a sub-collection - or .FirstOrDefault(), to get the reference of a specific Element Class or Handle (or other known details).
These methods show how to take a screenshot of Outlook's side Calendar panel and the Main Calendar window.
Use the code already discussed in your previous question to take the actual screenshot of the selected Screen bounds.
This code requires to add a reference to: UIAutomationClient
UIAutomationTypes WindowsBase
Imports System.Diagnostics
Imports System.Drawing
Imports System.Windows.Automation
Dim OutLookProc As Process = Process.GetProcessesByName("OUTLOOK").FirstOrDefault()
Dim MainElement As AutomationElement = AutomationElement.FromHandle(OutLookProc.MainWindowHandle)
Dim SmallCalendar As AutomationElement =
MainElement.FindAll(TreeScope.Subtree, Automation.RawViewCondition).
OfType(Of AutomationElement)().
FirstOrDefault(Function(elm) elm.Current.Name.Contains("NUIDocument") AndAlso
(Not String.IsNullOrEmpty(elm.Current.AutomationId)))
Dim CalendarNavigator As AutomationElement =
MainElement.FindAll(TreeScope.Subtree, Automation.RawViewCondition).
OfType(Of AutomationElement)().
FirstOrDefault(Function(elm) elm.Current.ClassName.Contains("TreeDisplayNode"))
If SmallCalendar IsNot Nothing Then
Dim SmallCalendarHeight As Integer = CInt(CalendarNavigator.Current.BoundingRectangle.Y -
SmallCalendar.Current.BoundingRectangle.Y)
Dim CalLocation As Point = New Point(CInt(SmallCalendar.Current.BoundingRectangle.Location.X),
CInt(SmallCalendar.Current.BoundingRectangle.Location.Y))
Dim CalSize As Size = New Size(CInt(SmallCalendar.Current.BoundingRectangle.Width), SmallCalendarHeight)
Dim SmallCalendarBounds As Rectangle = New Rectangle(CalLocation, CalSize)
SmallCalendarBounds.Inflate(-20, 0)
'CopyFormScreen() the SmallCalendarBounds rectangle. Inflate as needed
End If
This part of the code calculates the Screen bounds of Outlook's side Calendar object.
It may not be that useful, but it's a way to show how you can use these classes to parse/inspect the UI Elements of a process Main Window.
This is the result:
The Main Calendar window is simpler to identify and capture.
All Calendars classes names end with ViewWnd, thus, no matter which one is selected/in use, you will always be able to identify it quite easily.
Dim LargeCalendar As AutomationElement =
MainElement.FindAll(TreeScope.Descendants, Automation.RawViewCondition).
OfType(Of AutomationElement)().
FirstOrDefault(Function(elm) elm.Current.ClassName.Contains("ViewWnd"))
If LargeCalendar IsNot Nothing Then
Dim LCalLocation As Point = New Point(CInt(LargeCalendar.Current.BoundingRectangle.Location.X),
CInt(LargeCalendar.Current.BoundingRectangle.Location.Y))
Dim LCalSize As Size = New Size(CInt(LargeCalendar.Current.BoundingRectangle.Width),
CInt(LargeCalendar.Current.BoundingRectangle.Height))
Dim LargeCalendarBounds As Rectangle = New Rectangle(LCalLocation, LCalSize)
End If
This is the result:
This should help:
getting the window position:
How to get and set the window position of another application in C#
getting the window handle:
Get Application's Window Handles
bringing another application into the front:
https://social.msdn.microsoft.com/Forums/en-US/b5a91ac4-4894-45a1-aa66-b4d548ca8163/bring-another-application-to-front?forum=winforms
you could try those tricks and just copy from screen.

Out of memory when opening images using Stream

I hope you can help with what is probably a naive use of Streams in VB.net.
I have a program that batch processes images and am running out of memory making repeated use (in a loop) of the following:
Using str As Stream = File.OpenRead(file_stem + CStr(file_number) + "." + file_extension)
temp_img = Image.FromStream(str)
str.Close()
End Using
PictureBox1.Image = temp_img
bm = PictureBox1.Image.Clone
temp_img is globally declared Dim temp_img As Image. bm is declared in the same Sub routine as the loop Dim bm As Bitmap.
As the program runs I can see in Task Manager the memory usage rising and then it crashes with an out of memory error. It's as if each time I am using the Stream it is keeping the memory used. What am I doing wrong here?
EDIT:
This thread appears to have gone cold now, but I thought I would share my "work around" for this. It would seem that this is a VB.net bug as the way I have fixed it is to add a MsgBox which is shown only one, immediately prior to the first call to the subroutine that processes sets of 60 images. I ran a single job that processed 96 sets of 60 images and the memory usage didn't rise above about 45MB. The important thing that makes me think it is a bug is that I only show the MsgBox before the first set of 60 and all are run in series. Showing an MsgBox shouldn't fix anything in itself anyway!

MSFLEXGRID slow navigation between cells

I am using msflexgrid in my vb6 application, It do work fine,but if any cell has large content then the problem comes. the problem is that when we navigate in grid by using arrow buttons, navigation becomes very slow when the cell with large content comes to visible area.Otherwise it works fine. In this case i am not trying to open the cell content of grid,just navigating from one cell to another. So how this slow performance or slow navigation can be solved or improved?
Set your Redraw property to false
And set it to true again after update completed.
I know that once I had set autosize column rows on and it caused massive speed issues. Now I leave auto-size off normally, turn it on breifly when the data first loads, and make it an option for the user via context menu.
I setup a test harness using the Service Pack 6 (latest) version of the MSFlex grid control as follows:
Private Sub Form_Load()
Dim i, j As Integer
Dim s As String
For j = 1 To 500
For i = 65 To 122
s = s & Chr(i)
Next i
Next j
For i = 1 To 4
For j = 1 To 10
MSFlexGrid.Col = i
MSFlexGrid.Row = j
MSFlexGrid.Text = s
Next j
Next i
End Sub
The MSFlex Grid control had 5 columns and 50 rows in my case.
This generates about 29K (58 chars * 500 repeats) of data per cell. I can navigate fairly quickly from cell to cell with this test harness. I would suggest that you ensure you are using the latest (SP6) version of the control.
If you are using the SP6 version and the amount of data that you are displaying in each cell is so large that it is still causing a performance issue I would suggest switching to another control.
Possibly integrate a 3rd party VB6 control or leverage something you've written in .NET and integrate into your VB6 form.

Creating VB.Net forms via code instead of in design mode

I'm using netzero hardware to manage the contents of a number of monitors. My present solution creates a form in VB.Net that has a pixel offset corresponding to where I've placed the Monitors in display management in the control panel. Each monitor has a dedicated form, and in each form are various objects.
The annoyance is that each form must be individually created (so far as I know) at design time. I can't make an array of forms, coupled with an array of offsets and assign all the properties through code.
There ought to be a way to do this...it would simplify my coding and project management.
What I see on MSDN is either over my head or not helpful.
I haven't tested this in hardware yet, but it does compile w/o error:
Public Sub makeform()
Dim MonitorForm(21) As Form
Dim MPictureBoxes(21) As PictureBox
Dim a As Integer
For i As Integer = 0 To n 'up to 21
MonitorForm(i) = New Form
MonitorForm(i).Name = "Form" & (i + 1)
MonitorForm(i).Text = "Form" & (i + 1)
MonitorForm(i).Controls.Add(MPictureBoxes(i))
MonitorForm(i).Location= new Point (x(i), y(i))
With MPictureBoxes(i)
.Name = "Picture Box " & Convert.ToString(i)
.Image = Image.FromFile(CurrentPic(i))
.Location = New System.Drawing.Point(0, 0)
.Size = New Size(1920, 1080)
' Note you can set more of the PicBox's Properties here
End With
Next
End Sub
Where I had gone wrong in my attempts at this was trying to do it this way
Dim Monitor(21) as New Form
That doesn't work, and the difference between Dim Monitor(21) as Form followed by monitor(i)= new Form
was simply too subtle for my present understand of classes, namespaces etc.
.
Well, I've had to give up on this approach and go back to creating n forms at design time (which means that they have names of form2...form22, putting each of them at manual start positions in design mode. There just doesn't seem to be a way to do this with an array of forms. So the code I have built around the messiness of forms2...forms22 works just fine, it's just going to be messy to maintain and elaborate on.
The solution to this may lie in system.screen classes, but the documentation on this is too advanced for me and I'm not finding good code examples for anything other than extracting data about how many screens there are - nothing about writing to them.
This is very easy in code. You want to make many instances of the same form. In this case, I have created a form in the designer called frmTest and I create many instances in code called frmNew:
Public Sub Main()
For x = 100 To 400 Step 100
For y = 100 To 700 Step 200
Dim frmNew As New frmTest
frmNew.Show()
frmNew.Top = x
frmNew.Left = y
frmNew.Height = 100
frmNew.Width = 200
Next
Next
End Sub
I have just used two loops to increment x and y values, but you could do this from a database or config file easily enough.