• 0

ScribBox - Line Numbered RichTextBox


Question

hey, all, i remember a long time ago, i posted asking if someone knew how to apply line-numbering using pure win32 api to modify the non-client area.

i have finally done it. of course, there are some menial performance drawbacks, but if anyone is willing to help fix them, please pm or email me.

this control inherits from .net's richtextbox and is named ScribBox for the program for which it was made (Scrib).

attached is a picture, and, yes, there are 20 lines in the client area.

post-72514-1128815937.png

Edited by Ianmac45
Link to comment
https://www.neowin.net/forum/topic/382661-scribbox-line-numbered-richtextbox/
Share on other sites

6 answers to this question

Recommended Posts

  • 0

here's a quick rundown of the problems i have with it.

+ you can turn the line numbering on and off, but it doesn't clear the side bar when turning it off.

+ painting performance

here's a quick rundown of what awesome cool features this control has

+ a premade working context menu

+ extra properties such as FirstVisibleLine, LastVisibleLine, CurrentLine

+ Delete functionality (as if you pressed the Delete key)

+ Go To Line functionality

it's really awesome.

  • 0

ok, since no one has actually replied to this post. i'm simply going to post the main part of the code. the only things i know are missing are some properties/variables and all win32 objects/methods. this exact code produced the picture above.

protected override void WndProc(ref Message m)
{
	//this method mainly controls how the line numbers are painted
	//so we gotta skip it if it's not wanted or if it's designtime
	if(this.DesignMode || !showNumbers)// || !System.Diagnostics.Debugger.IsAttached)
	{
  base.WndProc(ref m);
  return;
	}


	switch(m.Msg)
	{
  case (int)Win32.WM.NCCALCSIZE:
  	if(m.WParam.ToInt32() != 0)
  	{
    Win32.NCCALCSIZE_PARAMS p = (Win32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(Win32.NCCALCSIZE_PARAMS));
    p.rgrc0.Left = sideWidth;
    Marshal.StructureToPtr(p, m.LParam, true);
  	}

  	break;

  case (int)Win32.WM.NCPAINT:
  	IntPtr hDC = Win32.GetWindowDC(m.HWnd);
  	Graphics g = Graphics.FromHdc(hDC);

  	PaintLineNumbers(g);

  	//cleanup
  	g.Dispose();
  	Win32.ReleaseDC(m.HWnd, hDC);

  	//need to redraw client area, because sometimes it simply disappears
  	base.Refresh();

  	break;
	}

	base.WndProc(ref m);
}

private void PaintLineNumbers(Graphics g)
{
	//set graphics environment
	g.Clear(this.BackColor);

	//init drawing utensils
	Color numColor = Color.FromArgb(0, 128, 128);
	Brush br = new SolidBrush(numColor);
	Pen pen = new Pen(numColor);
	int height = 3;

	//draw visible line numbers only
	for(int i = FirstVisibleLine; i <= LastVisibleLine; i++)
	{
  //gets line number as right-justified string
  string text = " " + (i + 1).ToString().PadLeft(this.Lines.Length.ToString().Length);
  g.DrawString(text, this.Font, br, 0, height);
  height += this.Font.Height;

  //if there is more than one line, this statement is necessary 
  //to prevent printing one more line number than needed
  if(i == LastVisibleLine - 1)
  	break;
	}

	//draw line seperating numbers from text
	pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
	g.DrawLine(pen, sideWidth - 1, 1, sideWidth - 1, this.Height);
}

anyway, i really need help with debugging and improving it's speed. i'd also like to implement some other features, but if anyone is willing to help, i'd really appreciate it.

  • 0

Speed improvements:

+ use the bounding box of the HRGN that you'll get thru the WM_NCPAINT to only update the necessary parts.

+ fall back to plain old GDI calls.

+ don't do an invalidate on the whole clientarea in the WM_NCPAINT, but figure out what's going wrong (might need to not pass the WM_NCPAINT to the old WNDPROC).

+ check if caching makes things faster (could also raise a costly active pagefault).

  • 0

i still have to look at the first two, but i think the first item kinda ties in with the third item

excluding the call to the old wndproc for ncpaint did speed it up a bit, but then the scrollbars weren't painted properly. also, just like the code says, i need the base.Refresh() call so it works with the rest of my app.

  • 0

WM_NCPAINT:

a) clip your update rectangle with the HRGN being passed, creating a new HRGN.

b) pass that new to base.wndProc(WM_NCPAINT, reinterpret_cast<WPARAM>(yourNewHrgn), 0L);

c) do your painting.

GDI calls:

GDI calls are much faster than GDI+ calls in current implementations. Since GDI is used to draw the richeditbox in the first place, the looks will also be more consistent.

HRGN that you get:

see WM_NCPAINT.

base.Refresh() call:

seems like a fundamental flaw in your application then. There is no reason that you should invalidate the client-area in the non-client area painting proc.

  • 0

ok, i updated the ncpaint case to this (not much change, just a little here and there from msdn and what andreas told me)

case (int)Win32.WM.NCPAINT:
	if((int)m.WParam != 1)
  break;

	//painting
	IntPtr hDC = Win32.GetWindowDC(m.HWnd);
	Graphics g = Graphics.FromHdc(hDC);
	PaintLineNumbers(g);

	//cleanup
	g.Dispose();
	Win32.ReleaseDC(m.HWnd, hDC);
	base.DefWndProc(ref m);

	return;

but, for the plain old GDI calls, is there some sort of reference that i can use to figure out what each function does and how to use it?

i sorta tried the caching of the side bar, but it stole system resources like crazy. maybe i'll try it again later, but definitely not now

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Posts

    • Err more info required... Are you using the built-in VPN client? PowerShell > Batch. Either way, the script will most likely use rasdial.
    • The viewing figures in season 2 plummeted after 1 of the main characters died in season 2 episode 1. I think hbo is regretting listening to him so they got rid of him.
    • Google Workspace now lets you use custom AI Gems directly in Docs, Gmail, and more by Paul Hill Google Workspace users can now access Gems from the side panel of Google Docs, Slides, Sheets, Drive, and Gmail. Previously, Gems could only be accessed from the Gemini app directly. For anyone not familiar with Gems, they’re a more advanced feature in Gemini where you can make your own chatbots, powered by Gemini, with custom instructions. If you’re interested in learning more about them, check out my editorial from April, where I argue custom AI bots are the best thing about generative AI and how to create your own bots. The decision to make Gems available across Google Workspace has the potential to significantly speed up people’s workflows if they’ve started using Gems already. If you’ve never made a Gem, Google has several pre-made ones including a Brainstormer, Writing editor, Coding partner, and Learning guide. Google Workspace users can leverage Gems in an almost infinite number of ways. For example, imagine if you’re a teacher in whatever country and you have to make lesson plans for your class that must follow a certain structure, you can use natural language to program a gem to expect certain inputs from you (such as grade, subject, topic etc) and get an output that follows the required guidelines. If you’re a journalist, you could create a gem to quickly strip out the key bits of news from a press release or if you’re a student you can create a bot to break down complicated subjects into something easier to understand. The possibilities are nearly endless and now the Gems you make are even more accessible. Google mentioned that Gems can be accessed via the side panel of all supported Workspace applications and can be used across Workspace capabilities including @ mentioning, accessing files and folders, and more. If you need to create a Gem, you’ll still need to do that on the Gemini website. To get started with Gemini in Google Workspace, just click the “Ask Gemini” (spark button) in the top-right corner. Google said that the Gems feature rollout is an extended rollout which means it might take more than 15 days to get the feature. Admins out there do not need to do anything and there are no specific admin controls in the side panel for Gems or Gemini.
    • Microsoft changes hit Teams Android devices: Disable Entra ID policy to restore sign-in by Paul Hill As part of its Secure Future Initiative, Microsoft has deployed a new Entra ID Conditional Access policy targeting Device Code Flow authentication. Unfortunately, it has led some Microsoft Teams-certified Android devices (Teams Rooms on Android, Teams Phones, Teams Panels, and Teams Displays) to be logged out and signing back in can be a bit fiddly so guidance has been shared. Microsoft said that it shared previous guidance which explained how to exclude Android devices, but it seems some admins didn’t catch this as many devices were not excluded and have been signed out. It’s important to realize that this is not a bug, it’s a security feature. However, the move could have been better communicated. To sign the devices back in, you can do so manually. However, if the devices are remote you’ll need to follow these steps: By disabling the “Block device code flow” policy in step 1, it will change everything back to how it was before Microsoft decided to enable it to boost security. This will allow you to get those affected Android devices logged back in again. Also pay special attention to step 2 which says you might need to reboot your device three times. Once you have your Android devices logged in again, it’s probably a good idea to follow Microsoft’s previous guidance and add these to an exclusion list before re-enabling the “Block device code flow” policy. Microsoft recommends only allowing DCF where it’s absolutely necessary and then blocking it elsewhere. The best thing to do is to add your Teams Android device to the exclusion list - this will allow these devices to operate normally, while boosting overall security. If you’re an admin and have been impacted by this, be sure to take proactive measures to avoid disruptions in the future.
  • Recent Achievements

    • Reacting Well
      SteveJaye earned a badge
      Reacting Well
    • One Month Later
      MadMung0 earned a badge
      One Month Later
    • One Month Later
      Uranus_enjoyer earned a badge
      One Month Later
    • Week One Done
      Philsl earned a badge
      Week One Done
    • Week One Done
      Jaclidio hoy earned a badge
      Week One Done
  • Popular Contributors

    1. 1
      +primortal
      434
    2. 2
      ATLien_0
      158
    3. 3
      +FloatingFatMan
      146
    4. 4
      Nick H.
      65
    5. 5
      +thexfile
      62
  • Tell a friend

    Love Neowin? Tell a friend!