2017-04-04 62 views
3

我正在構建用於顯示平鋪貼圖的自定義用戶控件,因爲我選擇的基礎類爲ScrollableControl,因爲我想在我的控件中使用滾動條。使用帶有固定位置文本的ScrollableControl的自定義控件

我已經成功創建了繪製邏輯,它只負責繪製所需的元素。

現在我想要添加靜態文本,將在同一個地方總是可見的(在我的情況下,白框在左上角的紅色文本):

enter image description here

這是沒有明確的在上面的gif中可見,但當我使用鼠標或滾動條滾動時,白色框閃爍並跳動一點。

我的問題是我應該如何更改我的代碼,使可滾動內容和固定位置內容位於可滾動內容之上?

ScrollableControl不錯的選擇作爲基類?

下面是我的代碼:

class TestControl : ScrollableControl 
{ 
    private int _tileWidth = 40; 
    private int _tileHeight = 40; 
    private int _tilesX = 20; 
    private int _tilesY = 20; 

    public TestControl() 
    { 
     SetStyle(ControlStyles.ResizeRedraw, true); 
     SetStyle(ControlStyles.UserPaint, true); 
     SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
     SetStyle(ControlStyles.Opaque, true); 
     SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 
     UpdateStyles(); 
     ResizeRedraw = true; 
     AutoScrollMinSize = new Size(_tilesX * _tileWidth, _tilesY * _tileHeight); 

     Scroll += (sender, args) => { Invalidate(); }; 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     base.OnPaint(e); 
     e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle); 
     e.Graphics.TranslateTransform(AutoScrollPosition.X, AutoScrollPosition.Y); 
     e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; 

     var offsetX = (AutoScrollPosition.X * -1)/_tileWidth; 
     var offsetY = (AutoScrollPosition.Y * -1)/_tileHeight; 

     var visibleX = Width/_tileWidth + 2; 
     var visibleY = Height/_tileHeight + 2; 

     var x = Math.Min(visibleX + offsetX, _tilesX); 
     var y = Math.Min(visibleY + offsetY, _tilesY); 

     for (var i = offsetX; i < x; i++) 
     { 
      for (var j = offsetY; j < y; j++) 
      { 
       e.Graphics.FillRectangle(Brushes.Beige, new Rectangle(i*_tileWidth, j*_tileHeight, _tileWidth, _tileHeight)); 
       e.Graphics.DrawString(string.Format("{0}:{1}", i, j), Font, Brushes.Black, new Rectangle(i * _tileWidth, j * _tileHeight, _tileWidth, _tileHeight)); 
      } 
     } 

     using (var p = new Pen(Color.Black)) 
     { 
      for (var i = offsetX + 1; i < x; i++) 
      { 
       e.Graphics.DrawLine(p, i*_tileWidth, 0, i*_tileWidth, y*_tileHeight); 
      } 

      for (var i = offsetY + 1; i < y; i++) 
      { 
       e.Graphics.DrawLine(p, 0, i*_tileHeight, x*_tileWidth, i*_tileHeight); 
      } 
     } 

     e.Graphics.FillRectangle(Brushes.White, AutoScrollPosition.X * -1, AutoScrollPosition.Y * -1, 35, 14); 
     e.Graphics.DrawString("TEST", DefaultFont, new SolidBrush(Color.Red), AutoScrollPosition.X * -1, AutoScrollPosition.Y * -1); 
    } 
} 

編輯:
我搜索了一下,發現用戶控件具有類似的功能 - https://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView和閱讀略偏上控制的作者博客http://objectlistview.sourceforge.net/cs/blog1.html#blog-overlays後,我發現,他正在使用位於控制之上的透明窗體。 我真的很想避免這種情況,但仍然覆蓋在我的控制之上。

回答

2

您正在使用名爲「在拖動時顯示窗口內容」的Windows系統選項進行戰鬥。默認情況下始終打開,this web page顯示如何關閉它。

解決了這個問題,但它不是你可以依賴的東西,因爲它影響所有應用程序中的所有全部可滾動窗口。要求用戶爲你關閉是不現實的,用戶喜歡這個選項,所以他們會忽略你。他們沒有提供關閉某個特定窗口的選項,這是一個相當大的疏忽。這是一個在kiosk應用程序中的好方案。

簡而言之,該選項的工作方式是Windows自身使用ScrollWindowEx() winapi函數滾動窗口內容。使用窗口內容的bitblt來移動像素,並僅爲滾動顯示的窗口部分生成繪畫請求。通常只有幾行像素,因此完成得非常快。問題是,bitblt也會移動你的固定像素。重繪將它們移回。非常明顯,人眼對這種運動非常敏感,避免了過去幾百萬年的獅子午餐。

您必須剔除ScrollWindowsEx(),即使您無法阻止其被調用,也無法移動像素。這需要一個沉重的大錘,LockWindowUpdate()。你會在this post找到代碼。

using System.Runtime.InteropServices; 
... 

    protected override void OnScroll(ScrollEventArgs e) { 
     if (e.Type == ScrollEventType.First) { 
      LockWindowUpdate(this.Handle); 
     } 
     else { 
      LockWindowUpdate(IntPtr.Zero); 
      this.Update(); 
      if (e.Type != ScrollEventType.Last) LockWindowUpdate(this.Handle); 
     } 
    } 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern bool LockWindowUpdate(IntPtr hWnd); 

不那麼漂亮,使用單獨的Label控件應該開始聽起來有吸引力。

+0

非常感謝您的回覆。再次您的代碼幫助(一如既往)!現在,當我使用滾動條時,我可以獲得平滑滾動,並且我的標籤位於頂部。不幸的是,當我用鼠標滾輪滾動沒有任何改變時,我的白色盒子仍然跳動。可以使用鼠標滾輪滾動嗎? – Misiu

+0

我已經爲'OnMouseWheel'添加覆蓋:'LockWindowUpdate(Handle); base.OnMouseWheel(e); LockWindowUpdate(IntPtr.Zero); Update();'這解決了我用鼠標滾輪的問題。請檢查並更新您的答案。這樣,有相同問題的人將有工作解決方案。 – Misiu

+0

我注意到,當在'OnScroll'' e.Type' =='ThumbPosition'裏面時,我調整控件的大小(整個窗體)時會出現奇怪的行爲http://i63.tinypic.com/2zfikbr.gif我已經管理(e.Type!= ScrollEventType.Last)LockWindowUpdate(this.Handle);'如果(e.Type!= ScrollEventType.Last && e.Type!= ScrollEventType.ThumbPosition)LockWindowUpdate(Handle );'你能否再次看看這個並更新你的答案? – Misiu

0

你能爲標籤添加一個標籤嗎(換句話說) - 你不能用它作爲面板嗎?