2014-02-19 22 views
0

我必须向用户显示每隔100毫秒更新一次的信息,这意味着我显示的文本框内容不断被更改,并且如果它们在滚动时通过它们正在更改更新将导致他们松动他们的滚动条位置文本框 - 在文本更新期间保持滚动条位置

如何防止这种情况?通过一次添加所有文本,我减少了很多效果。

当前代码:

string textboxStr = ""; 
foreach (string debugItem in debugItems) 
{ 
    textboxStr += debugItem + Environment.NewLine; 
} 

debugForm.Controls[0].Text = textboxStr; 

更新1:

使用的解决方案下面提供和它不工作,滚动条仍然复位到其默认位置意味着你失去了你的位置,你的指针复位太。

实现:

在类:

[System.Runtime.InteropServices.DllImport("user32.dll")] 
public static extern bool LockWindowUpdate(IntPtr hWndLock); 

在功能:

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); 

更新2: 使用第2个解决方案下面提供和它不工作。滚动条在滚动时仍然会跳到顶部。然后,有时甚至当你不在上面时,滚动条会开始跳跃(每隔100毫秒,当它更新文本时)。

实施: 在类:

[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; 

在功能:

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); 

示例文本:

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 
+0

好吧,如果内容的变化,它真的如果用户停留在同一行文本的关系,行可能甚至不存在了吗?或者你只是添加到文本框? –

+0

线条的数量总是相同的,只是这些线条上的内容会发生变化。 – user1763295

回答

3

可以存储SelectionStart然后使用ScrollToCaret更新后。使用LockWindowUpdate来停止闪烁。事情是这样的:

[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); 

只要文本框不会改变大小(它听起来并不像它会),这应该很好地工作。另一种选择是使用EM_LINESCROLL来存储和设置文本框的滚动条值..但这更涉及。

编辑:

因为这没有工作..这是另一种选择。

首先,某些Windows的API:

[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; 

现在你可以这样做:

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); 

这完全适用于我。我唯一的问题是,它有时纯粹是因为我随机生成的字符串宽度变化很大。只要你的更新在每次更新后大致相似,就应该没问题。

+0

我假设你不想在代码的“锁定”部分花费太多时间? –

+0

这并不重要。它只是意味着在那段时间内控件不会重绘。所以文本框将显示完全没有响应。我猜想它有多好,你想用户体验是多久,你在该块.. –

+0

谢谢,但这仍然是不停止滚动,指针和滚动条仍然重置(请参阅更新#1) – user1763295

1

经过搜索,从来没有找到一个合适的解决方案,可以使用和不使用焦点以及水平和垂直方向,我偶然发现了一个可用的API解决方案(至少对于我的平台 - 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); 
     } 
    } 
} 

随着多文本框(tbx_main)使用,如:

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);