Two Problems with scroll bars in C# - c#

I hate two richTextBoxs.
I have two problems:
no matter how I define the scroll bar - it doesn't apear. if I write more than the length of the line - the cursor goes to the line below - I want everything to be writen in the same row and the user could move right-left with the scroll bar.
I want one scroll bar to control both richtextboxes.
one horizontal for both, and one vertical for both.

You'll need to set the MultiLine property to false (the default is true for RichTextBoxes) if you just want one line, or else set the WordWrap property to false (this also defaults to true).
I guess you'll need to handle the HScroll and VScroll events of each RichTextBox, and in the event handlers scroll the other textbox by the same amount. Not sure how easy this would be, though...

Best way to solve your second Problem is to use GetScrollInfo and SetScrollInfo. I think you should be able to handle the HScrolland VScrollevents, when they fire you'll have to set the new ScrollInfo in the second RichTextBox.
You'll need WM_SendMessage too for this job. (All usuable with imports of User32.dll)
Usual work to do:
Getting informed when the user scrolls (Scrollevents), and setting the new ScrollInfo to the second RichTextBox. After setting the ScrollInfo you'll need to send a Message to the Scrollbar / Control to Scroll.
First of all you need your "own" SCROLLINFO struct
[StructLayout(LayoutKind.Sequential)]
struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
Then get the Scrollinfo at ScrollingEvent in the first TextBox:
GetScrollInfo(this.Handle, SB_VERT, ref _si);
(where _si is your SCROLLINFO-instance). Then send the message to the scrollbar
_si.nPos = 0815; //Your wanted new ScrollbarPosition (I think you can use the value of the first Scrollbar)
//// Reposition scroller
SetScrollInfo(Handle, SB_VERT, ref _si, true);
//// Send a WM_VSCROLL scroll message using SB_THUMBTRACK as wParam
//// SB_THUMBTRACK: low-order word of wParam, si.nPos high-order word of wParam
IntPtr ptrWparam = new IntPtr(SB_THUMBTRACK + 0x10000 * _si.nPos);
IntPtr ptrLparam = new IntPtr(0);
SendMessage(Handle, WM_VSCROLL, ptrWparam, ptrLparam);
Where HANDLE is the Handle of your RichTextBox that has to be scrolled - you can get the handle at anytime using
RichTextBox.Handle
You should really have a look at http://msdn.microsoft.com/en-us/library/bb787537%28VS.85%29.aspx and http://msdn.microsoft.com/en-us/library/ms644950%28VS.85%29.aspx
There are also some good examples at pinvoke.net (for example GetScrollInfo)

Related

How do I know when the user can scroll a RichTextBox with forced ScrollBars

I need the ScrollBars to be set to ForcedBoth but I also want to know when the ScrollBars' handles are visible and the user can scroll.
A boolean for both scrollbars would do
Here the user cannot scroll:
Here they can:
I am trying to create a custom scrollbar derived from RichTextBox.
I am doing this by overlaying panels over the original scrollbar and disabling them (so you can use the original scrollbar), while getting different visuals (the overlayed panels).
I got everything working, except that I need to disable one of the panels (the handle panel) when the ScrollBars of the RichTextBox are disabled.
I tried GetScrollInfo from Win32 API but it results in inconsistent results when zooming into the RichTextBox or removing a bunch of lines at once.
If this is actually what you need to know about the state of the ScrollBars of your RichTextBox, use GetScrollBarInfo() to get this information, then test the values stored in the rgstate component of the returned SCROLLBARINFO struct.
When a ScrollBar is disabled, rgstate[0] is set to STATE_SYSTEM_UNAVAILABLE; the value at index 2 (page element), should be set to STATE_SYSTEM_INVISIBLE in this case.
When a ScrollBar is not present, the same value is STATE_SYSTEM_INVISIBLE
Otherwise, it's 0 (here, set to SBIdObj.STATE_SYSTEM_AVAILABLE = 0x00000000)
A sample method that can be used to test the ScrollBars. Call it as, e.g.,
var result = GetScrollBarsState(myRichTextBox.Handle);
It returns a named tuple with the state of both ScrollBars set to the current SBIdObj value
public (SBRgState Vertical, SBRgState Horizontal) GetScrollBarsState(IntPtr controlHandle)
{
var sbi = new SCROLLBARINFO() { cbSize = Marshal.SizeOf<SCROLLBARINFO>() };
bool result = GetScrollBarInfo(controlHandle, SBIdObj.OBJID_VSCROLL, ref sbi);
if (!result) throw new Exception("Failed to retrieve vertical ScrollBar info");
var vert = (SBRgState)sbi.rgstate[0];
result = GetScrollBarInfo(controlHandle, SBIdObj.OBJID_HSCROLL, ref sbi);
if (!result) throw new Exception("Failed to retrieve horizontal ScrollBar info");
var horz = (SBRgState)sbi.rgstate[0];
return (vert, horz);
}
Declarations:
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GetScrollBarInfo(IntPtr hWnd, SBIdObj idObject, ref SCROLLBARINFO psbi);
[StructLayout(LayoutKind.Sequential)]
public struct SCROLLBARINFO {
public int cbSize;
public Rectangle rcScrollBar;
public int dxyLineButton;
public int xyThumbTop;
public int xyThumbBottom;
public int reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public int[] rgstate;
}
// GetScrollBarInfo idObject
public enum SBIdObj : uint {
OBJID_HSCROLL = 0xFFFFFFFA,
OBJID_VSCROLL = 0xFFFFFFFB,
OBJID_CLIENT = 0xFFFFFFFC
}
// SCROLLBARINFO rgstate flags
[Flags]
public enum SBRgState {
STATE_SYSTEM_AVAILABLE = 0x00000000,
STATE_SYSTEM_UNAVAILABLE = 0x00000001,
STATE_SYSTEM_PRESSED = 0x00000008,
STATE_SYSTEM_INVISIBLE = 0x00008000,
STATE_SYSTEM_OFFSCREEN = 0x00010000,
}

Programmatically change custom mouse cursor in windows?

I am trying to change the windows cursors (the default is Windows Custom Scheme) to my custom cursors (It named Cut the rope):
Is there any idea to change all of cursors (Arrow, Busy, Help Select, Link select,...) to my Cut the rope?
If you want to change the default Mouse Cursor theme:
You can just change it in the registry:
There are three main registry keys that come into play.
The registry key HKEY_CURRENT_USER\Control Panel\Cursors contains the active user cursors
1a) The values underneath this are the different types of cursors
1b) The Scheme Source specifies the type of cursor scheme that is currently being used.
The different values are:
"0" – Windows Default
"1" – User Scheme
"2" – System Scheme
The registry key HKEY_CURRENT_USER\Control Panel\Cursors contains the user defined cursor schemes (i.e. Scheme Source = 1)
The registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Schemes contains the system cursor schemes (i.e. Scheme Source = 2)
If you already changed the path to one of the cursor type in HKCU\Control Panel\Cursors and realized that it did not do anything. You are correct, just updating a key – HKCU\Control Panel\Cursors\Arrow, for instance – isn’t enough. You have to tell windows to load the new cursor.
This is where the SystemParametersInfo call comes in. To try this out let’s go ahead and change HKCU\Control Panel\Cursors\Arrow to C:\WINDOWS\Cursors\appstar3.ani (assuming you have this icon) and then make a call to SystemParametersInfo.
In AutoHotKey Script:
SPI_SETCURSORS := 0x57
result := DllCall("SystemParametersInfo", "UInt", SPI_SETCURSORS, "UInt", 0, "UInt", 0, "UInt", '0')
MsgBox Error Level: %ErrorLevel% `nLast error: %A_LastError%`nresult: %result%
Translated to C#:
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);
const int SPI_SETCURSORS = 0x0057;
const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDCHANGE = 0x02;
To call it:
SystemParametersInfo(SPI_SETCURSORS, 0, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
Changing to the Default Windows Cursor
Now the tricky part. If you look at HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Schemes you will notice that “Windows Default” is defined as “,,,,,,,,,,,,,” or in other words no pointers to actual cursors!
What to do now? Don’t worry. All you have to do is set the different cursor types to empty string and then make the SystemParametersInfo call as usual. In fact, you can set any of the cursor type to empty string in any scheme and Windows will default it to it’s equivalent in the “Windows Default” scheme.
REF:
https://thebitguru.com/articles/programmatically-changing-windows-mouse-cursors/3
https://social.msdn.microsoft.com/Forums/vstudio/en-US/977e2f40-3222-4e13-90ea-4e8d0cdf289c/faq-item-how-to-change-the-systems-cursor-using-visual-cnet?forum=csharpgeneral
You can do like this. Get the Cursor.cur file to load custom cursor. On MouseLeave set the Default cursor for form.
public static Cursor ActuallyLoadCursor(String path)
{
return new Cursor(LoadCursorFromFile(path));
}
[DllImport("user32.dll")]
private static extern IntPtr LoadCursorFromFile(string fileName);
Button btn = new Button();
btn.MouseLeave += Btn_MouseLeave;
btn.Cursor = ActuallyLoadCursor("Cursor.cur");
private static void Btn_MouseLeave(object sender, EventArgs e)
{
this.Cursor = Cursors.Default;
}

Synchronized scrolling for two Richtextboxes (C#)

I'm in need of some help.
Currently I am working on a Script Editor in C# and I have two rich text boxes: programTextBox, where the whole text is and linesTextBox which counts and shows the number of lines.
I want them to scroll at the same time. I have done some search and I actually found some code which works, but if has a few problems. Here is the code:
public enum ScrollBarType : uint
{
SbHorz = 0,
SbVert = 1,
SbCtl = 2,
SbBoth = 3
}
public enum Message : uint
{
WM_VSCROLL = 0x0115
}
public enum ScrollBarCommands : uint
{
SB_THUMBPOSITION = 4
}
[DllImport("User32.dll")]
public extern static int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("User32.dll")]
public extern static int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
...
private void programTextBox_VScroll(object sender, EventArgs e)
{
int nPos = GetScrollPos(programTextBox.Handle, (int)ScrollBarType.SbVert);
nPos <<= 16;
uint wParam = (uint)ScrollBarCommands.SB_THUMBPOSITION | (uint)nPos;
SendMessage(linesTextBox.Handle, (int)Message.WM_VSCROLL, new IntPtr(wParam), new IntPtr(0));
}
It works. Kind of. And you may ask: "What's the problem?". Well:
1) My program crashes when the total number of lines becomes about 2500. I get an overflow error.
2) If I move up and down by using the scrollbar instead of the mouse wheel, then my second rich text box (linesTextBox) will not follow the first one unless I release the scrollbar.
If an application scrolls the content of the window, it must also reset the position of the scroll box by using the SetScrollPos function
Have a look at this :
https://msdn.microsoft.com/en-in/library/aa926329.aspx
Also WM_VSCROLL message carries only 16 bits of scroll box position data this could be the reason u should try GetScrollInfo because it has 32 bits of scroll box pos data. This might give solution to both of your problems . Hope this helps...

Combobox Drop Down list

I have a Form that has a combobox on it.
The combobox is set DropDownList. These drop down items are a descriptive form of an object. This means they can get quite long. The position of the combobox on the screen means when the drop down list is displayed it doesn't all fit on the screen. Some of it is chopped off by the right edge of the screen.
I can't move the combobox.
Is their some way I can move the drop down list part of the control. Perhaps centre it under the control ?
Update
I've attached a screenshot. You can see the form here -
When entering transactions the user fills in the form and clicks Save. A number of the transactions that will be entered for any client though will be recurring transactions. These can be saved to the favourites. The drop down box lists the currently saved favourites and when one is selected the program automatically fills in the transaction fields.
Screenshot 2 showing the whole program and the combobox list running out of space.
I realise from the screenshot I could move the form but I like to keep the forms for entering transactions centered on the screen.
I may have to look at other options for the interface.
Thanks,
maybe you should create your own comboBox, as shown here:
http://msdn.microsoft.com/en-us/library/ms996411
Sorry for late posting :-). Yes You can do that. But you need to create a custom ComboBox and override the WndProc method of base ComboBox;
Its like this;
System.Runtime.InteropServices
private const int SWP_NOSIZE = 0x1;
private const int WM_CTLCOLORLISTBOX = 0x0134;
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int
X, int Y, int cx, int cy, uint uFlags);
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CTLCOLORLISTBOX)
{
// Make sure we are inbounds of the screen
int left = this.PointToScreen(new Point(0, 0)).X;
//Only do this if the dropdown is going off right edge of screen
if (this.DropDownWidth > Screen.PrimaryScreen.WorkingArea.Width - left)
{
// Get the current combo position and size
Rectangle comboRect = this.RectangleToScreen(this.ClientRectangle);
int dropHeight = 0;
int topOfDropDown = 0;
int leftOfDropDown = 0;
//Calculate dropped list height
for (int i = 0; (i < this.Items.Count && i < this.MaxDropDownItems); i++)
{
dropHeight += this.ItemHeight;
}
//Set top position of the dropped list if
//it goes off the bottom of the screen
if (dropHeight > Screen.PrimaryScreen.WorkingArea.Height -
this.PointToScreen(new Point(0, 0)).Y)
{
topOfDropDown = comboRect.Top - dropHeight - 2;
}
else
{
topOfDropDown = comboRect.Bottom;
}
//Calculate shifted left position
leftOfDropDown = comboRect.Left - (this.DropDownWidth -
(Screen.PrimaryScreen.WorkingArea.Width - left));
//when using the SWP_NOSIZE flag, cx and cy params are ignored
SetWindowPos(m.LParam,
IntPtr.Zero,
leftOfDropDown,
topOfDropDown,
0,
0,
SWP_NOSIZE);
}
}
base.WndProc(ref m);
}
The code is obtain from a MSDN article Building a Better ComboBox
Did you try setting in the designer
Combobox.Anchor = Left | Right
Try setting the DropdownWidth of the combo.

.NET ListView row padding

There doesn't seem to be a way to change the padding (or row height) for all rows in a .NET ListView. Does anybody have an elegant hack-around?
I know this post is fairly old, however, if you never found the best option, I've got a blog post that may help, it involves utilizing LVM_SETICONSPACING.
According to my blog,
Initially, you'll need to add:
using System.Runtime.InteropServices;
Next, you'll need to import the DLL, so that you can utilize SendMessage, to modify the ListView parameters.
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
Once that is complete, create the following two functions:
public int MakeLong(short lowPart, short highPart)
{
return (int)(((ushort)lowPart) | (uint)(highPart << 16));
}
public void ListViewItem_SetSpacing(ListView listview, short leftPadding, short topPadding)
{
const int LVM_FIRST = 0x1000;
const int LVM_SETICONSPACING = LVM_FIRST + 53;
SendMessage(listview.Handle, LVM_SETICONSPACING, IntPtr.Zero, (IntPtr)MakeLong(leftPadding, topPadding));
}
Then to use the function, just pass in your ListView, and set the values. In the example, 64 pixels is the image width, and 32 pixels is my horizontal spacing/padding, 100 pixels is the image height, and 16 pixels is my vertical spacing/padding, and both parameters require a minimum of 4 pixels.
ListViewItem_SetSpacing(this.listView1, 64 + 32, 100 + 16);
A workaround is to use an ImageList that is as tall as you want the items to be. Just fill a blank image with the background color. You can even make the image 1 wide so as to not take much space horizontally.

Categories