Textbox - Maintain scroll bar position during text updates - c#

I have to show information to the user that updates every 100 milliseconds, this means the contents of the textbox I show it in are constantly being changed and if they are scrolling through them when they are being changed the update will cause them to loose their scroll bar position
How do I prevent this? I've reduced the effect a lot by adding all text at once.
Current code:
string textboxStr = "";
foreach (string debugItem in debugItems)
{
textboxStr += debugItem + Environment.NewLine;
}
debugForm.Controls[0].Text = textboxStr;
Update 1:
Used the solution provided below and it's not working, the scroll bar still resets to its default position meaning you loose your position and your pointer resets too.
Implementation:
In class:
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool LockWindowUpdate(IntPtr hWndLock);
In function:
var originalPosition = ((TextBox)debugForm.Controls[0]).SelectionStart;
LockWindowUpdate(((TextBox)debugForm.Controls[0]).Handle);
debugForm.Controls[0].Text = textboxStr;
((TextBox)debugForm.Controls[0]).SelectionStart = originalPosition;
((TextBox)debugForm.Controls[0]).ScrollToCaret();
LockWindowUpdate(IntPtr.Zero);
Update 2:
Used the 2nd solution provided below and it's not working. The scroll bar still jumps to the top even mid way while scrolling. Then sometimes when you're not even on it the scroll bar will start jumping up and down (Every 100 ms, when it updates the text).
Implementation:
In class:
[DllImport("user32.dll")]
static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
private const int SB_VERT = 0x1;
private const int SB_THUMBPOSITION = 4;
private const int WM_VSCROLL = 0x115;
In function:
var currentPosition = GetScrollPos(debugForm.Controls[0].Handle, SB_VERT);
debugForm.Controls[0].Text = textboxStr;
SetScrollPos(debugForm.Controls[0].Handle, SB_VERT, currentPosition, false);
PostMessageA(debugForm.Controls[0].Handle, WM_VSCROLL, SB_THUMBPOSITION + 65535 * currentPosition, 0);
Example text:
Active Scene: Level0
--------------------------------------------------
Settings
Fps: 60
GameSize: {Width=600, Height=600}
FreezeOnFocusLost: False
ShowCursor: False
StaysOnTop: False
EscClose: True
Title:
Debug: True
DebugInterval: 100
--------------------------------------------------
Entities
Entity Name: Player
moveSpeed: 10
jumpSpeed: 8
ID: 0
Type: 0
Gravity: 1
Vspeed: 1
Hspeed: 0
X: 20
Y: 361
Z: 0
Sprites: System.Collections.Generic.List`1[GameEngine.Sprite]
SpriteIndex: 0
SpriteSpeed: 0
FramesSinceChange: 0
CollisionHandlers: System.Collections.Generic.List`1[GameEngine.CollisionHandler]
--------------------------------------------------
Key Events
Key: Left
State: DOWN
Key: Left
State: UP
Key: Right
State: DOWN
Key: Right
State: UP
Key: Up
State: DOWN
Key: Up
State: UP

You can store the SelectionStart then use ScrollToCaret after you update. Use LockWindowUpdate to stop the flicker. Something like this:
[DllImport("user32.dll")]
public static extern bool LockWindowUpdate(IntPtr hWndLock);
var originalPosition = textBox.SelectionStart;
LockWindowUpdate(textBox.Handle);
// ---- do the update here ----
textBox.SelectionStart = originalPosition;
textBox.ScrollToCaret();
LockWindowUpdate(IntPtr.Zero);
As long as the textbox doesn't change size (which it doesn't sound like it will) this should work fine. The other option is to use EM_LINESCROLL to store and set the scrollbar value for the textbox.. but that's more involved.
EDIT:
Since that didn't work.. here's another option.
First, some Windows APIs:
[DllImport("user32.dll")]
static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
..and some values:
private const int SB_VERT = 0x1;
private const int SB_THUMBPOSITION = 4;
private const int WM_VSCROLL = 0x115;
You can now do this:
var currentPosition = GetScrollPos(textBox.Handle, SB_VERT);
// ---- update the text here ----
SetScrollPos(textBox.Handle, SB_VERT, currentPosition, false);
PostMessageA(textBox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 65535 * currentPosition, 0);
This works perfectly for me. The only problem I have is that it jumps around sometimes purely because my randomly generated string of characters has widely varying widths. As long as yours is roughly similar after each update, it should be fine.

After searching and never finding a legitimate solution that works with and without focus as well as horizontally and vertically, I stumbled across an API solution that works (at least for my platform - Win7 / .Net4 WinForms).
using System;
using System.Runtime.InteropServices;
namespace WindowsNative
{
/// <summary>
/// Provides scroll commands for things like Multiline Textboxes, UserControls, etc.
/// </summary>
public static class ScrollAPIs
{
[DllImport("user32.dll")]
internal static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
internal static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
internal static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
public enum ScrollbarDirection
{
Horizontal = 0,
Vertical = 1,
}
private enum Messages
{
WM_HSCROLL = 0x0114,
WM_VSCROLL = 0x0115
}
public static int GetScrollPosition(IntPtr hWnd, ScrollbarDirection direction)
{
return GetScrollPos(hWnd, (int)direction);
}
public static void GetScrollPosition(IntPtr hWnd, out int horizontalPosition, out int verticalPosition)
{
horizontalPosition = GetScrollPos(hWnd, (int)ScrollbarDirection.Horizontal);
verticalPosition = GetScrollPos(hWnd, (int)ScrollbarDirection.Vertical);
}
public static void SetScrollPosition(IntPtr hwnd, int hozizontalPosition, int verticalPosition)
{
SetScrollPosition(hwnd, ScrollbarDirection.Horizontal, hozizontalPosition);
SetScrollPosition(hwnd, ScrollbarDirection.Vertical, verticalPosition);
}
public static void SetScrollPosition(IntPtr hwnd, ScrollbarDirection direction, int position)
{
//move the scroll bar
SetScrollPos(hwnd, (int)direction, position, true);
//convert the position to the windows message equivalent
IntPtr msgPosition = new IntPtr((position << 16) + 4);
Messages msg = (direction == ScrollbarDirection.Horizontal) ? Messages.WM_HSCROLL : Messages.WM_VSCROLL;
SendMessage(hwnd, (int)msg, msgPosition, IntPtr.Zero);
}
}
}
With a multiline textbox (tbx_main) use like:
int horzPos, vertPos;
ScrollAPIs.GetScrollPosition(tbx_main.Handle, out horzPos, out vertPos);
//make your changes
//i did something like the following where m_buffer is a string[]
tbx_main.Text = string.Join(Environment.NewLine, m_buffer);
tbx_main.SelectionStart = 0;
tbx_main.SelectionLength = 0;
ScrollAPIs.SetScrollPosition(tbx_main.Handle, horzPos, vertPos);

Related

C# Simulating mouseclicks in game doesn't work

Recently I tried to create a bot for an MMO-Game (called Florensia).
It should click on several positions in the game.
My problem is that it only sets the cursour to the position but the click doesn't work out. If I try it at my desktop or some other programs, it clicks correctly.
The game of course is in windowed mode and I already tried to set delays between the Mouseup and Mousedown.
Also to set the game to foreground window before the click didn't work.
Looking forward to any answers! :)
Try to use WinApi functions like in this example.
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
static void Main(string[] args)
{
const int WM_LBUTTONDOWN = 0x0201;
const int WM_LBUTTONUP = 0x0202;
const int MK_LBUTTON = 0x0001;
uint x = 100;
uint y = 100;
IntPtr handle = new IntPtr(0x00090996); //Your window handle
SetForegroundWindow(handle);
Thread.Sleep(300);
SendMessage(handle, WM_LBUTTONDOWN, new IntPtr(MK_LBUTTON), new IntPtr(y << 16 | x));
Thread.Sleep(300);
SendMessage(handle, WM_LBUTTONUP, new IntPtr(0), new IntPtr(y << 16 | x));
}

How to prevent TextBox auto scrolls when append text?

I have a multi-line TextBox with a vertical scrollbar that logs data from real-time processing. Currently, whenever a new line is added by textBox.AppendText(), the TextBox scrolls to the bottom so you can see the last entry, this great. But I have a checkbox to indicate whether TextBox is allowed to auto-scroll. Is there any way to do this?
Note:
I want to use the TextBox because the added text has multi-lines and alignment by whitespace, so it's not simple to use with a ListBox or a ListView.
I tried to add a new line by textBox.Text += text, but the TextBox constantly scrolls to the top.
If we have a solution to do that, then one more question is how to prevent the TextBox auto scrolls when the user uses the scrollbar to view somewhere else in the TextBox while the TextBox appends text?
private void OnTextLog(string text)
{
if (chkAutoScroll.Checked)
{
// This always auto scrolls to the bottom.
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
// This always auto scrolls to the top.
//txtLog.Text += Environment.NewLine + text;
}
else
{
// I want to append the text without scrolls right here.
}
}
Update 1: As saggio suggests, I also think the solution to this problem is to determine the position of the first character in the current text that is displayed in the TextBox before appending text and restoring it after that. But how to do this? I tried to record the current cursor position like this, but it did not help:
int selpoint = txtLog.SelectionStart;
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
txtLog.SelectionStart = selpoint;
Update 2 (the issue was resolved): I found a solution that can solve my issue here on Stack Overflow. I have optimized their code to suit my case as follows:
// Constants for extern calls to various scrollbar functions
private const int SB_VERT = 0x1;
private const int WM_VSCROLL = 0x115;
private const int SB_THUMBPOSITION = 0x4;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
private void AppendTextToTextBox(TextBox textbox, string text, bool autoscroll)
{
int savedVpos = GetScrollPos(textbox.Handle, SB_VERT);
textbox.AppendText(text + Environment.NewLine);
if (autoscroll)
{
int VSmin, VSmax;
GetScrollRange(textbox.Handle, SB_VERT, out VSmin, out VSmax);
int sbOffset = (int)((textbox.ClientSize.Height - SystemInformation.HorizontalScrollBarHeight) / (textbox.Font.Height));
savedVpos = VSmax - sbOffset;
}
SetScrollPos(textbox.Handle, SB_VERT, savedVpos, true);
PostMessageA(textbox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
}
private void OnTextLog(string text)
{
AppendTextToTextBox(txtLog.Text, Environment.NewLine + text, chkAutoScroll.Checked);
}
Another way:
private const int SB_VERT = 0x1;
private const int WM_VSCROLL = 0x115;
private const int SB_THUMBPOSITION = 0x4;
private const int SB_BOTTOM = 0x7;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
private void AppendTextToTextBox(TextBox textbox, string text, bool autoscroll)
{
int savedVpos = GetScrollPos(textbox.Handle, SB_VERT);
textbox.AppendText(text + Environment.NewLine);
if (autoscroll)
{
PostMessageA(textbox.Handle, WM_VSCROLL, SB_BOTTOM, 0);
}
else
{
SetScrollPos(textbox.Handle, SB_VERT, savedVpos, true);
PostMessageA(textbox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
}
}
I post these solutions for those who have a similar issue. Thanks for cgyDeveloper's source code.
Does anyone have a more straightforward way?
This seems pretty straight forward but I may be missing something. Use append text to scroll to the position if Autochecked is true and just add the text if you do not wish to scroll.
Update...I was missing something. You want to set the selection point and then scroll to the caret. See below.
if (chkAutoScroll.Checked)
{
// This always auto scrolls to the bottom.
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
// This always auto scrolls to the top.
//txtLog.Text += Environment.NewLine + text;
}
else
{
int caretPos = txtLog.Text.Length;
txtLog.Text += Environment.NewLine + text;
txtLog.Select(caretPos, 0);
txtLog.ScrollToLine(txtLog.GetLineIndexFromCharacterIndex(caretPos));
}
You have to do it something like this,
textBox1.AppendText("Your text here");
// this selects the index zero as the location of your caret
textBox1.Select(0, 0);
// Scrolls to the caret :)
textBox1.ScrollToCaret();
Tested and working on VS2010 c# Winforms, i dont know about WPF but google probably has the answer for you.
The desired actions are:
To turn on autoscrolling when the scrollbar is dragged to the bottom.
To turn off autoscrolling when the scollbar is dragged anywhere else.
create the following class
public class AutoScrollTextBox : TextBox
{
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
bool isScrolledToEnd = VerticalOffset + ViewportHeight == ExtentHeight;
base.OnTextChanged(e);
CaretIndex = Text.Length;
if (isScrolledToEnd)
{
ScrollToEnd();
}
}
}
And replace the TextBox with AutoScrollTextBox in your XML and append to the TextToDisplay binding as stuff arrives for display
<local:AutoScrollTextBox Text="{Binding TextToDisplay }" />

Getting a second IE Window to open on a different display

I'm writing a .exe that is supposed to run as a scheduled task to check if I have required IE windows open running .Xbaps on specific monitors. I have code that checks what URL is supposed to run, if it's not I use this code to launch it, then move it to the correct monitor:
Process myProcess = Process.Start("iexplore.exe", "-new -k " + "http://server01:123/software.client.xbap");
myProcess.WaitForInputIdle();
Thread.Sleep(500);
MoveWindowToMonitor(myProcess.MainWindowHandle, 1);
Window Moving code:
private static void MoveWindowToMonitor(IntPtr windowHandler, int monitor)
{
RECT windowRec = new RECT();
GetWindowRect(windowHandler, ref windowRec);
int width = windowRec.Right - windowRec.Left;
int height = windowRec.Top - windowRec.Bottom;
if (width < 0)
width = width * -1;
if (height < 0)
height = height * -1;
SetWindowPos(windowHandler, (IntPtr)SpecialWindowHandles.HWND_TOP, Screen.AllScreens[monitor].WorkingArea.Left,
Screen.AllScreens[monitor].WorkingArea.Top, width, height, SetWindowPosFlags.SWP_SHOWWINDOW);
}
Running a quick test version of this gets the first IE window open, Xbap launched, and then quickly moves it over to my other monitor. When I run it a second time, without closing the first IE window, I always get InvalidOperationException
"Process has exited, so the requested information is not available."
I've checked my Task Manager as this is happening and I actually get two iexplore.exe items under details the first time I run the task, and only one additional iexplorer.exe for each subsequent execution of the task. I also get one PresentationHost.exe per xbap launched.
Anyone have any idea what I'm doing wrong or a better way to do this?
My end goal is to be able to do this:
Launch IE in Kiosk Mode on monitor 1 with specific url X:
Launch IE in Kiosk Mode on monitor 2 with specific url Y:
After you launch an IE process, it does some funny stuff and that process you have launched can occasionally end right away as another takes over the window.
What I would do is, using the methods below, is EnumTheWindows which would step through every visible window running and look for Internet Explorer or my baseURL. Then I would pass that Window Handle to GetURL and get the specific URL that IE window was running. This allowed me to use ConfirmProcessIsOnProperMonitor() and MoveWindowToMonitor() to get the windows on the proper monitor.
Important stuff:
private static bool ConfirmProcessIsOnProperMonitor(IntPtr windowHandler, int monitor)
{
//make sure you don't go to an incorrect monitor
if (monitor >= Screen.AllScreens.Count()) monitor = Screen.AllScreens.Count() - 1;
RECT windowRec = new RECT();
GetWindowRect(windowHandler, ref windowRec);
if (windowRec.Left != Screen.AllScreens[monitor].WorkingArea.Left || windowRec.Top != Screen.AllScreens[monitor].WorkingArea.Top)
return false;
else
return true;
}
private static void MoveWindowToMonitor(IntPtr windowHandler, int monitor)
{
//make sure you don't go to an incorrect monitor
if (monitor >= Screen.AllScreens.Count()) monitor = Screen.AllScreens.Count() - 1;
RECT windowRec = new RECT();
GetWindowRect(windowHandler, ref windowRec);
int width = windowRec.Right - windowRec.Left;
int height = windowRec.Top - windowRec.Bottom;
if (width < 0)
width = width * -1;
if (height < 0)
height = height * -1;
SetWindowPos(windowHandler, (IntPtr)SpecialWindowHandles.HWND_TOP, Screen.AllScreens[monitor].WorkingArea.Left,
Screen.AllScreens[monitor].WorkingArea.Top, width, height, SetWindowPosFlags.SWP_SHOWWINDOW);
}
protected static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0 && IsWindowVisible(hWnd))
{
StringBuilder sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
string windowText = sb.ToString();
if (windowText.ToLower().Contains(_baseURL) || windowText.ToLower().Contains("internet explorer"))
{
string url = GetURL(hWnd);
_windowhandles.Add(hWnd, url);
}
}
return true;
}
private static string GetURL(IntPtr intPtr)
{
foreach (InternetExplorer ie in new ShellWindows())
{
if (ie.HWND == intPtr.ToInt32())
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(ie.FullName);
if ((fileNameWithoutExtension != null) && fileNameWithoutExtension.ToLower().Equals("iexplore"))
{
return ie.LocationURL;
}
else
{
return null;
}
}
}
return null;
}
Hard to read windows API code:
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, StringBuilder msgbody);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool IsWindowVisible(IntPtr hWnd);

How to hide the vertical scroll bar in a .NET ListView Control in Details mode

I've got a ListView control in Details mode with a single column. It's on a form that is meant to only be used with the keyboard, mostly with the up/down arrows for scrolling and enter to select. So I don't really need to have the scroll bars and would just like them to not show for a cleaner look. However, when I set the ListView.Scrollable property to false, I can still move the selected item up and down, but as soon as it moves to an item not currently in view, the list won't move to show that item. I've tried using EnsureVisible to programmatically scroll the list, but it does nothing when in this mode.
Is there any way to manually move the list up and down to scroll, but without having the scrollbar present?
It's not easy but it can be done. If you try to hide the scroll bar through ShowScrollBar, the ListView will simply put it back again. So you have to do something more devious.
You will have to intercept the WM_NCCALCSIZE message, and in there, turn off the vertical scroll style. Whenever the listview tries to turn it on again, you will turn it off again in this handler.
public class ListViewWithoutScrollBar : ListView
{
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case 0x83: // WM_NCCALCSIZE
int style = (int)GetWindowLong(this.Handle, GWL_STYLE);
if ((style & WS_VSCROLL) == WS_VSCROLL)
SetWindowLong(this.Handle, GWL_STYLE, style & ~WS_VSCROLL);
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
const int GWL_STYLE = -16;
const int WS_VSCROLL = 0x00200000;
public static int GetWindowLong(IntPtr hWnd, int nIndex) {
if (IntPtr.Size == 4)
return (int)GetWindowLong32(hWnd, nIndex);
else
return (int)(long)GetWindowLongPtr64(hWnd, nIndex);
}
public static int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong) {
if (IntPtr.Size == 4)
return (int)SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
else
return (int)(long)SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowLong32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, int dwNewLong);
}
This will give you a ListView without scroll bars that still scrolls when you use the arrow keys to change selection.
i did something more easy. i left scrollable to true and used a custom slider(colorSlider) that i found on codeproject and i drawed the slider over the position where the vscroller would appear and then used the ensureVisible function.
Call the ShowScrollBar API method.
If ShowScrollBar doesn't work, I'm not sure how to do it.
You could put the ListView in a panel and make the ListView wider than the panel so that the scrollbar is cut off (check SystemInformation.VerticalScrollBarWidth), but that's a horrifyingly ugly hack.
Since ShowScrollBar didn't work, maybe this helps:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private const int WM_VSCROLL = 0x115;
private const int SB_LINEDOWN = 1;
public Form1()
{
InitializeComponent();
for (int i = 0; i < 100; i++)
listView1.Items.Add("foo" + i);
listView1.Scrollable = false;
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
SendMessage(listView1.Handle, WM_VSCROLL, SB_LINEDOWN, 0);
}
You can use ListView.Scrollable Property. Set it to false and Scroll bars won't appear!

Hide Title Bar of Program using API

Its possible remove a window console title bar using c# and windows api, if yes howto? Please.
This simple app hides and shows the title bar of the console that it's in. It changes the console title to a guid momentarily to find the window handle. Afterwards it uses ToggleTitleBar to show or hide using the found handle.
public class Program
{
public static void ToggleTitleBar(long hwnd, bool showTitle)
{
long style = GetWindowLong(hwnd, -16L);
if (showTitle)
style |= 0xc00000L;
else
style &= -12582913L;
SetWindowLong(hwnd, -16L, style);
SetWindowPos(hwnd, 0L, 0L, 0L, 0L, 0L, 0x27L);
}
public static void Main()
{
Guid guid = Guid.NewGuid();
string oldTitle = Console.Title;
Console.Title = guid.ToString();
int hwnd = FindWindow("ConsoleWindowClass", guid.ToString());
Console.Title = oldTitle;
Console.WriteLine("Press enter to hide title");
Console.ReadLine();
ToggleTitleBar(hwnd, false);
Console.WriteLine("Press enter to show title");
Console.ReadLine();
ToggleTitleBar(hwnd, true);
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
[DllImport("user32", EntryPoint = "GetWindowLongA")]
public static extern long GetWindowLong(long hwnd, long nIndex);
[DllImport("user32", EntryPoint = "SetWindowLongA")]
public static extern long SetWindowLong(long hwnd, long nIndex, long dwNewLong);
[DllImport("user32")]
public static extern long SetWindowPos(long hwnd, long hWndInsertAfter, long x, long y, long cx, long cy,
long wFlags);
[DllImport("User32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
}
Edit: Sorry, I see that you're looking for a solution for a console application. No, there is no way I know of to do what you're trying to do. It is also not simple to host a console in a WinForms application.
However, if you ARE using a WinForms application or WPF, consider the following.
this.ControlBox = false;
this.Text = string.Empty;
Otherwise, you could set FormBorderStyle to None.
You can also hide the program from the task bar if you need to.
this.ShowInTaskBar = false;
This will probably not work. In theory you could use something like this:
HWND handle = FindWindow(L"ConsoleWindowClass", NULL);
LONG style = GetWindowLong(handle, GWL_STYLE);
style = style & ~WS_CAPTION;
SetWindowLong(handle, GWL_STYLE, style);
This will work for every window except console windows. SetWindowLong returns 0, and GetLastError returns 5 (Access denied), even if you run the application as administrator.
I have some (very) old code I think somehow related; I'd to display Microsoft Excel inside a winform application:
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern int SetParent(int hWndChild, int hWndNewParent);
[DllImport("user32.dll")]
public static extern int MoveWindow(
int hWnd, int x, int y,
int nWidth, int nHeight, int bRepaint);
//
private static int hwnExcel = 0;
private System.Windows.Forms.PictureBox picContainer;
// ...
private void Principal_Resize(object sender, EventArgs e)
{
picContainer.Width = this.Width - 8;
picContainer.Height = this.Height - 45;
User32.SetParent(hwnExcel, 0);
User32.MoveWindow(
hwnExcel, 0, 0,
picContainer.Width, picContainer.Height, 1);
}
use FindWindow to get the handle of the console window then SetWindowLong to modify his properties

Categories