2014-02-19 50 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);