I want to add a CComboBoxEx on my form, but I have to align it with other controls and make it the same height.
The problem is that I can't find which is the correct way to get the height of this control.
If I set the window rectangle, that affects the drop down listbox. If I force it to be some fixed size, and the content inside the combo is higher, the control gets clipped at the bottom.
Is there a way to calculate how big the ComboBoxEx will be, based on image and current DPI settings? So that I could at least expand the other controls if I cannot adjust the internal padding of the control. MSDN doesn't touch the sizing issues at all as far as I've looked.
ComboBoxes set the height of the control automatically, and as you've discovered, trying to set your own height actually affects the height of the drop-down list.
To get the real height of the control (so you can resize your other controls) you can use GetWindowRect:
RECT rc;
GetWindowRect(hWndCombo, &rc);
int iComboHeight = rc.bottom - rc.top;
I don't know of a good way to change the height of the Combo control itself, and frankly I don't believe it's possible at least without lots of subclassing. I have implemented my own combo-style control because of this.
Although it probably won't help, you may also like to have a look at the GetComboBoxInfo() function to see if that provides any useful information.
Related
Say you have a form that you can expand to the left to show additional controls:
Collapsed:
Expanded:
The simplest way to achieve this in Delphi is to use alRight as the primary anchor for all controls (instead of alLeft) and then simply adjust the width and X coordinate of the form. Either you can set the Width and Left properties individually, or you can use a function that sets them simultaneously, like
if FCollapsed then
SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
else
SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0)
The problem is that there is quite noticeable flickering in the always-visible part of the form (in this example, the buttons) while expanding or collapsing. Try it yourself!
It is possible for the operating system to resize the form to the left without any flickering at all -- just grab the left edge of the form using the mouse and drag the mouse to the left or right -- but I am unable to find any function in the Windows API that exposes this kind of resizing.
I have tried to use several different Windows API functions to resize and reposition the form, tried their various parameters (for instance, the SWP_* flags), tried LockWindowUpdate, WM_SETREDRAW, TForm.DoubleBuffered etc. to no avail. I also examined the possibility to use the WM_SYSCOMMAND SC_SIZE approach.
I am not yet sure if the problem lies at the OS level or the VCL level.
Any suggestions?
Edit: I am very surprised to see that this Q received close votes. Let me try to clarify:
Create a new VCL forms application.
Add a few buttons to the right side of the main form and a memo to the left. Set Anchors to [alTop, alRight] on all controls. On the OnClick handler of the buttons, add the following code:
if FCollapsed then
SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
else
SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0);
FCollapsed := not FCollapsed;
where FCollapsed is private boolean field of the form (initialized to false).
Now, click the buttons repeatedly. (Or give one of them keyboard focus and hold the Enter key for a few seconds.) You will probably notice that the region with the buttons on your monitor will not display a perfect still image, but will flicker. In addition, you might actually see 'ghosts' of the buttons to the left of the actual column of buttons.
I am unable to capture this millisecond flickering using screen capture, so instead I used a digital camera to record my screen:
https://privat.rejbrand.se/VCLFormExpandFlicker.mp4
In this video clip, it is apparent that the column of buttons isn't a static image on the screen; instead, for a few milliseconds each time the form is resized, this region is something else than it should be. It is equally apparent that there is a 'ghost' column of buttons to the left.
My question is if there is any reasonably simple way to get rid of these visual artefacts (that at least to me are very visible even if you expand/collapse the form a single time).
On my Windows 10/Delphi 10.1 computer at work, the form is resized in a perfect manner when I drag its left-most edge using the mouse: the unaffected client area of the form is perfectly static on the monitor. However, on my Windows 7/Delphi 2009 PC at home, I do see that there is a lot of repositioning going on when I do this.
I can provide some insight about why you see ghost images of the other half of your UI and possibly a way to stop it. The ghost image indicates that someone is copying your client area pixels (and copying them to the wrong place, always flush-left in your window) before you have a chance to redraw them with the correct pixels.
There are likely two different, overlapping sources of these ghost pixels.
The first layer applies to all Windows OSes and comes from a BitBlt inside SetWindowPos. You can get rid of that BitBlt in several ways. You can create your own custom implementation of WM_NCCALCSIZE to tell Windows to blit nothing (or to blit one pixel on top of itself), or alternately you can intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing. This has the same eventual effect of skipping the BitBlt.
However, Windows 8/10 aero adds another, more troublesome layer. Apps now draw into an offscreen buffer which is then composited by the new, evil DWM.exe window manager. And it turns out DWM.exe will sometimes do its own BitBlt type operation on top of the one already done by the legacy XP/Vista/7 code. And stopping DWM from doing its blit is much harder; so far I have not seen any complete solutions.
For sample code that will break through the XP/Vista/7 layer and at least improve the performance of the 8/10 layer, please see:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Since you have multiple child windows, the situation is even a little more complicated. The BitBlt type operations I mentioned above happen on your whole top-level window as a whole (they treat the window as one set of pixels regardless of how many windows are underneath, and regardless of CLIPCHILDREN). But you need to have windows move atomically so that on the next redraw they are all positioned correctly. You may find BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos useful for that (but only go there if the above tricks do not work).
I use a NSStackView to dynamically (added by code) display other NSViews, everything works so far, but the sub views are to small.
They try to be as small as possible, regardless the constrains, if i resize the views in IB, they scale correctly
The Header with the hide button does not fill the available space:
I did set the translatesAutoresizingMaskIntoConstraints property to false on the stackview and all subviews
Well, it's hard to know for sure without more information, but the most likely cause is that the horizontal content hugging priority of the labels is higher than the horizontal hugging priority of the stack view. (Note: the former is a general property of all views while the latter is a stack-view-specific property.)
You also need to have constraints between the stack view and its superview to make the stack view stretch the full width. For example, leading and trailing space constraints.
This is quite old, but in modern times I have found that if you set the alignment to width, everything magically works.
stackView.orientation = .vertical
stackView.alignment = .width
stackView.addArrangedSubview(nowFullWidthSubview)
I have a Notebook and add two panels to it. When I try to get the usable area of one of the panels, it returns (20,20) -- I imagine this is its size before stuff is put in. I want to get this size before I put stuff in because some proportions depend on it and I'd prefer not using absolutes.
I haven't set an actual size, I just sized everything with a boxsizer and set proportions. I'd like to get the actual pixel width of the area allotted to the panel (it doesn't take up the entire program window area).
I've tried calling Layout before GetSize, I've tried GetVirtualSize and Get ClientSize and any other available size returns, still (20, 20) or sometimes (10,40). I've tried getting the parent size, no go. I get the grandparent size and it shows correctly, but this is the size of the entire program window. Is there a way to get the potentially usable area of a panel? If need be, I can post the layout, but it's long.
It sounds like you are trying to do this at the time of the creation of the window, before the initial layout and sizing is done, so everything still has its initial size. There are some alternative ways to deal with this situation. You could delay creating the content of the panel until the first size event. Or you could design your sizer layout to be adaptable to any (resonable) size. Or you could adjust/redo the sizer layout later if needed.
Or perhaps the easiest would be to send a fake size event to the parent or top-level parent so they will do what their normal layout resizing the child windows, which will cascade to your panel. This may not work in all situations, but it is common enough that there is a window method for it, SendSizeEvent.
I'm have some data output text boxes (text boxes with no border and with the same background as the rest of the window) that I'm trying to align with a standard edit control + spin button, to represent a column of numbers, one of which can be edited:
I can manually align these in the Delphi / C++Builder form designer, but when I then view the form on a different version of Windows or at a different DPI, the text is no longer right-justified, presumably because of differences in the spin button spacing, borders, etc.
I can use EM_GETMARGINS to determine the width of the spin button, but how can I determine the size of the edit control's margins and borders? I've tried various combinations of EM_POSFROMCHAR, ClientToScreen, GetSystemMetrics(SM_CXBORDER), GetSystemMetrics(SM_CXEDGE), and GetThemeMargins, but so far, I can't find a combination that works and makes sense.
More details:
Environment is Delphi / C++Builder XE2.
The data output controls are TEdits with BorderStyle bsNone.
The edit + spin button is a TJvSpinEdit. From viewing its source, it uses EM_SETMARGINS to allow space for its spin button.
Use the Win32 API GetWindowRect() and GetClientRect() methods. The bordering will be the difference between the two rectangles. That will only give you the bordering, though. There could be extra spacing inside the client area.
I was looking for a custom image grid and found a similar question that had a really sweet component in an answer.
I downloaded the code and after some fiddling, I managed to get it to compile in DXE2. It looks really cool, but I can't get either scrollbar to show up. I also can't figure out how to dynamically control the images displayed. Or how to update the grid based on keyboard events.
Also, to get it to compile I had to remove the GR32 references; the library I downloaded had too many incompatibilities with DXE2 for me to resolve.
Any help would be greatly appreciated. This really looks like a killer component.
Update from Bill:
Here is a screenshot of incorrect thumbnail painting. I can not get the thumbnails to look like the screenshot from the component in question.
If the thumbnails were painted at the same XY as the rects painted in the first pass they would look much better. Any idea on what is going on?
... but I can't get either scrollbar to show up.
Well, there is no horizontal scrollbar. There is the property ColWidth that controls how much images are drawn in one row, depending on the control's width. You might update ColWidth in an OnResize event handler due to anchor settings, for example.
The vertical scroll bar appears automatically when not all images (incl. spacing) fit in the clientrect. The images are drawn on a TPaintBox and that paint box' size is updated as soon as the image count changes:
procedure TImageGrid.RearrangeImages;
begin
...
FPainter.Height := Max(ClientHeight,
FRowCount * (FRowHeight + FImageSpacing) - FImageSpacing);
The component inherites from TScrollingWinControl, so the scroll bar should modify accordingly. If not, then Bill has a workaround found as commented:
VertScrollBar.Range := FRowCount * (FRowHeight + FImageSpacing) - FImageSpacing;
I understand this obviously also works, but I really wonder why the scroll bar's range should be modified manually. Here in D7 I have no problem with a hidden vertical scroll bar.
... I also can't figure out how to dynamically control the images displayed. ...
The most easy way to fill the component is by assigning the Folder property to a path with images. Only the images with the file formats in the FileFormats property will be loaded. To specify the images manually (e.g. to combine multiple folders), use the FileNames property. When the Folder property is set, then the FileNames property is updated accordingly, but those file names are not stored in the DFM. When you change the file names (e.g. you delete one from the folder), then the Folder property is cleared and the component uses the FileNames property instead.
... Or how to update the grid based on keyboard events. ...
The only keystrokes currently implemented are Up, Down, PageUp, PageDown, Home and End which all scroll the control. What more key actions do you wish? It's a viewer.
Here is a screenshot of incorrect thumbnail painting. I can not get the thumbnails to look like the screenshot from the component in question. ... If the thumbnails were painted at the same XY as the rects painted in the first pass they would look much better.
While loading the images, a temporary rect is drawn with size ColWidth * RowHeight. All images are stretchdrawn within that size, so adjust your ratio of these properties to make the spacing equal everywhere. Note that you can also influence appearance with the ImageHorzAlign and ImageVertAlign properties.
Update:
The component you refer to is recently completely rewritten, and some of the answers above are outdated.
It now has a Propertional property that defaults to True, but when set False, it will stretch up the thumbs to whatever cell size you have set, independent from the original image sizes. Small images could remain narow though, unless you set the new Stretch property to True.
It now distinguishes between RowHeight and CellHeight, and ColWidth and CellWidth. The difference between both is CellSpacing.
The component does not descend from TScrollingWinControl anymore, but from TCustomControl and only the vertical scroll bar is added.