Detect system theme change in WPF - c#

I need, for my WPF app, to detect when the DWM is turned on/off or when the system theme changes.
There is such an event in WinForms, but I can't see any in WPF.

I haven't heard of a WinForms event that fires when a WinForms window receives messages from the system, however it has its own WndProc() method that you can override. You're probably confusing window messages for form events. Ah, so it's the StyleChanged event that gets invoked in WinForms windows. The rest of my answer still stands though.
WPF isn't closely tied to the Windows API either as it's a high-level technology that invests a lot of abstraction away from the internals. For one, it draws everything in a window by itself, and doesn't ask the system to do the drawing for it (EDIT: which is why WPF lacks such a StyleChanged event). That said, Windows sends messages to all windows when the DWM is toggled and when the theme changes, and you can still drill down into the low level from the WPF layer to access these messages and manipulate your WPF controls accordingly.
Attach a window procedure to your WPF window's HWND (window handle) as part of your window's SourceInitialized event. In your window procedure, handle the WM_DWMCOMPOSITIONCHANGED and WM_THEMECHANGED window messages respectively.
Here's a quick example (with boilerplate code adapted from this question of mine):
private IntPtr hwnd;
private HwndSource hsource;
private const int WM_DWMCOMPOSITIONCHANGED= 0x31E;
private const int WM_THEMECHANGED = 0x31A;
private void Window_SourceInitialized(object sender, EventArgs e)
{
if ((hwnd = new WindowInteropHelper(this).Handle) == IntPtr.Zero)
{
throw new InvalidOperationException("Could not get window handle.");
}
hsource = HwndSource.FromHwnd(hwnd);
hsource.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_DWMCOMPOSITIONCHANGED:
case WM_THEMECHANGED:
// Respond to DWM being enabled/disabled or system theme being changed
return IntPtr.Zero;
default:
return IntPtr.Zero;
}
}

Unfortunately the accepted solution does not work with Aero color theme changes, and the WM message hex numbers are mixed up - but I agree that it is very useful if you want to catch WM messages in WPF. I've been trying to find a solution to this problem for a while, and I think I've got it solved for all possible cases (for aero and classic themes).
The Aero color change triggers the WM_DWMCOLORIZATIONCOLORCHANGED message.
To detect when the color theme changes you have to use multiple methods. The Form.StyleChanged event is going to detect all theme change, except for Aero color changes. Here is an alternative solution to StyleChanged. (Ok, I know this is WinForms, but you've got the idea. The WPF equivalent is in the accepted answer anyway.)
private const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x320;
private const int WM_DWMCOMPOSITIONCHANGED = 0x31E;
private const int WM_THEMECHANGED = 0x031A;
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case WM_DWMCOLORIZATIONCOLORCHANGED:
case WM_DWMCOMPOSITIONCHANGED:
case WM_THEMECHANGED:
// you code here
break;
default:
break;
}
base.WndProc(ref m);
}
For Aero color themes, the SystemEvents.UserPreferenceChanged event works too (thanks sees!):
Microsoft.Win32.SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
private void SystemEvents_UserPreferenceChanged(object sender, Microsoft.Win32.UserPreferenceChangedEventArgs e)
{
if (e.Category == Microsoft.Win32.UserPreferenceCategory.General)
{
// your code here, compare saved theme color with current one
}
}
As you can see above, it is far from intuitive. Aero color change triggers a 'General' preference change event, even though there are many more suitable ones for this, like 'VisualStyle', etc...
If you want to be thorough, you should compare the saved DWM color to the current DWM color, to make sure it was indeed an Aero color theme that triggered this event (using the DwmGetColorizationParameters API call), and not something else. See these answers on how Aero colors can be retrieved:
Get the active color of Windows 8 automatic color theme

The event SystemEvents.UserPreferenceChanged also does the trick.
UserPreferenceChanged(in Japaense)

Related

How to supress user actions on a ListBox control

Suppose we have a control (e.g. ListBox) and need to lock it from mouse clicks and keyboard actions. Of course, there is a standard property Enabled, but it causes appearance change, undesirable in my case.
I have found a simple solution using Windows API, see below. Suprisingly I haven't found a similar question exactly for my task so let me share this obvious solution with the community.
Any additions and comments are appreciated. In case someone will propose more appropriate/shorter/nicer answer.
We can use Windows API to lock our control from user actions.
First we should find out what standard WinAPI messages will be supressed while posting to the control. In my particular case of ListBox control I have chosen WM_LBUTTONDOWN, WM_KEYDOWN and WM_SETFOCUS messages (see docs), to lock both mouse and keyboard button presses and prevent my control from focusing.
Second we create a derived control class based on the ListBox, in the same namespace for convenience:
public class LockableListbox : ListBox
{
public bool Locked { get; set; }
const int WM_LBUTTONDOWN = 0x0201;
const int WM_SETFOCUS = 0x0007;
const int WM_KEYDOWN = 0x0100;
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
if (Locked)
switch (m.Msg)
{
case WM_LBUTTONDOWN:
case WM_KEYDOWN:
case WM_SETFOCUS:
return;
}
base.WndProc(ref m);
}
}
Here the standard WndProc method of the control is overridden to stop chosen messages from posting depending on Lock flag state.
Third we just change ListBox to LockableListBox in our Form class designer part where it is required.
Now our modified ListBox will be protected from user actions (mouse clicks, keyboard actions and focusing) when the Lock property is set.

Redirect Direct2D rendering to a WPF Control

I'm developing a drawing application in Visual C++ by means of Direct2D.
I have a demo application where:
// create the ID2D1Factory
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
// create the main window
HWND m_hwnd = CreateWindow(...);
// set the render target of type ID2D1HwndRenderTarget
m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRenderTarget
);
And when I receive the WM_PAINT message I draw my shapes.
Now I need to develop a WPF control (a kind of Panel) that represents my new render target (therefore it would replace the main window m_hwnd), so that I can create a new (C#) WPF project with the main window that has as children my custom panel, while the rendering part remains in the native C++/CLI DLL project.
How can I do that? What I have to set as my new render target?
I thought to use the handle of my WPF window:
IntPtr windowHandle = new WindowInteropHelper(MyMainWindow).Handle;
But I need to draw on my panel and not on my window.
Please note that I don't want to use any WPF classes for the rendering part (Shapes, DrawingVisuals...)
You have to implement a class that hosts your Win32 Window m_hwnd. This class inherits from HwndHost.
Moreover, you have to override the HwndHost.BuildWindowCore and HwndHost.DestroyWindowCore methods:
HandleRef BuildWindowCore(HandleRef hwndParent)
{
HWND parent = reinterpret_cast<HWND>(hwndParent.Handle.ToPointer());
// here create your Window and set in the CreateWindow function
// its parent by passing the parent variable defined above
m_hwnd = CreateWindow(...,
parent,
...);
return HandleRef(this, IntPtr(m_hwnd));
}
void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(m_hwnd); // hwnd.Handle
}
Please follow this tutorial: Walkthrough: Hosting a Win32 Control in WPF.
I'll try to answer the question as best I can based on WPF 4.5 Unleashed Chapter 19. If you want to look it up, you can find all information there in the sub-section "Mixing DirectX Content with WPF Content".
Your C++ DLL should have 3 exposed methods Initialize(), Cleanup() and Render().
The interesting methods are Initialize() and InitD3D(), which is called by Initialize():
extern "C" __declspec(dllexport) IDirect3DSurface9* WINAPI Initialize(HWND hwnd, int width, int height)
{
// Initialize Direct3D
if( SUCCEEDED( InitD3D( hwnd ) ) )
{
// Create the scene geometry
if( SUCCEEDED( InitGeometry() ) )
{
if (FAILED(g_pd3dDevice->CreateRenderTarget(width, height,
D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0,
true, // lockable (true for compatibility with Windows XP. False is preferred for Windows Vista or later)
&g_pd3dSurface, NULL)))
{
MessageBox(NULL, L"NULL!", L"Missing File", 0);
return NULL;
}
g_pd3dDevice->SetRenderTarget(0, g_pd3dSurface);
}
}
return g_pd3dSurface;
}
HRESULT InitD3D( HWND hWnd )
{
// For Windows Vista or later, this would be better if it used Direct3DCreate9Ex:
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Set up the structure used to create the D3DDevice. Since we are now
// using more complex geometry, we will create a device with a zbuffer.
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof( d3dpp ) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// Create the D3DDevice
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
// Turn on the zbuffer
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
// Turn on ambient lighting
g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff );
return S_OK;
}
Lets move on to the XAML code:
xmlns:interop="clr-namespace:System.Windows.Interop;assembly=PresentationCore"
<Button.Background>
<ImageBrush>
<ImageBrush.ImageSource>
<interop:D3DImage x:Name="d3dImage" />
</ImageBrush.ImageSource>
</ImageBrush>
</Button.Background>
I've set it as background of a button here, using an ImageBrush. I believe adding it as background is a good way to display the DirectX content. However, you can use the image in any way you like.
To initialize the rendering acquire a handle to the current window and call the Initialize() method of the DLL with it:
private void initialize()
{
IntPtr surface = DLL.Initialize(new WindowInteropHelper(this).Handle,
(int)button.ActualWidth, (int)button.ActualHeight);
if (surface != IntPtr.Zero)
{
d3dImage.Lock();
d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface);
d3dImage.Unlock();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
}
The CompositionTarget.Rendering event is fired just before the UI is rendered. You should render your DirectX content in there:
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
if (d3dImage.IsFrontBufferAvailable)
{
d3dImage.Lock();
DLL.Render();
// Invalidate the whole area:
d3dImage.AddDirtyRect(new Int32Rect(0, 0, d3dImage.PixelWidth, d3dImage.PixelHeight));
d3dImage.Unlock();
}
}
That was basically it, I hope it helps. Now just a few important sidenotes:
Always lock your image, to avoid that WPF draws frames partially
Dont call Present on the Direct 3D device. WPF presents its own backbuffer, based on the surface you passed to d3dImage.SetBackBuffer().
The event IsFrontBufferAvailableChanged should be handled because sometimes the frontbuffer can become unavailable (for example when the user enters the lock screen). You should free or acquire the resources based on the buffer availability.
private void d3dImage_IsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (d3dImage.IsFrontBufferAvailable)
{
initialize();
}
else
{
// Cleanup:
CompositionTarget.Rendering -= CompositionTarget_Rendering;
DLL.Cleanup();
}
}
It would be possible with WINFORMS probably, but not with WPF.
Here is a documentation talking about how WPF uses HWNDs:
How WPF Uses Hwnds
To make the most of WPF "HWND interop", you need to understand how WPF
uses HWNDs. For any HWND, you cannot mix WPF rendering with DirectX
rendering or GDI / GDI+ rendering. This has a number of implications.
Primarily, in order to mix these rendering models at all, you must
create an interoperation solution, and use designated segments of
interoperation for each rendering model that you choose to use. Also,
the rendering behavior creates an "airspace" restriction for what your
interoperation solution can accomplish. The "airspace" concept is
explained in greater detail in the topic Technology Regions Overview.
All WPF elements on the screen are ultimately backed by a HWND. When
you create a WPF Window, WPF creates a top-level HWND, and uses an
HwndSource to put the Window and its WPF content inside the HWND. The
rest of your WPF content in the application shares that singular HWND.
An exception is menus, combo box drop downs, and other pop-ups. These
elements create their own top-level window, which is why a WPF menu
can potentially go past the edge of the window HWND that contains it.
When you use HwndHost to put an HWND inside WPF, WPF informs Win32 how
to position the new child HWND relative to the WPF Window HWND. A
related concept to HWND is transparency within and between each HWND.
This is also discussed in the topic Technology Regions Overview.
Copied from https://msdn.microsoft.com/en-us/library/ms742522%28v=vs.110%29.aspx
I would recomend you to study a way to keep track of your render area and maybe create a "hideous" child window in front of it.
Other research you can maybe do is to try and find/get WPF graphics buffer and inject your rendered scene directly into it using pointers and some advanced memory programming.
https://github.com/SonyWWS/ATF/ might help
This is a level-editor with an direct2d view included

In a Winforms RichTextBox control, how can I make the space BELOW a last-line link NOT clickable?

In a Windows forms C# application, I have a number of RichTextBox controls that display a link as the last line of the text box, with no line break after.
The issue is that ALL of the white space that is physically below the link will be a clickable link. I understand that empty white space below text generally serves as "part" of that line in windows--for example, put your cursor just below this post, and click and drag--you will select the last line. But generally this does not include clickable links. Try it with the title of this post--you can select the title, but your cursor is not the clickable "hand" until you are actually directly over the title.
I could get around this by changing my data to always include a trailing line break, or modify the point where I'm setting the text of the box to always add one. But both of those seem messy. Is there no way to make a RichTextBox's links act more like links in a web browser?
I can reproduce this behavior by creating a sample WinForms application, dropping in a RichTextBox, and using the designer to set the text to "http://www.google.com" Anywhere BELOW the link will show the hand cursor.
I'm using Windows 7 / VS2010 / C# / .net Framework 4.0
Thanks for the advice.
Anywhere BELOW the link will show the hand cursor.
You need to put a line break to see the text cursor below the link not the hand cursor. Its by design.
I could get around this by changing my data to always include a
trailing line break, or modify the point where I'm setting the text of
the box to always add one. But both of those seem messy. Is there no
way to make a RichTextBox's links act more like links in a web
browser?
No. Put a line break after. Or set the RichTexbox DetectUrls property to false. Or as Hans mentioned use a Web Browser. Or use a 3rd party or open source RichTextBox control.
It would be good if the Cursor change event fired when hovering over a hyperlink but it doesn't :(
It would be good if the Cursor change event fired when hovering over a hyperlink but it doesn't :(
Jeremy's comment gave me an idea: surely the native RichTextBox control does receive some type of notification when the user hovers over a hyperlink, it apparently just is not exposed by the WinForms wrapper class.
A bit of research confirms my supposition. A RichTextBox control that is set to detect hyperlinks sends a EN_LINK notification to its parent through the WM_NOTIFY message. By processing these EN_LINK notifications, then, you can override its behavior when a hyperlink is hovered.
The WinForms wrapper handles all of this in private code and does not allow the client to have any direct control over this behavior. But by overriding the parent window's (i.e., your form) window procedure (WndProc), you can intercept WM_NOTIFY messages manually and watch for EN_LINK notifications.
It takes a bit of code, but it works. For example, if you suppress the WM_SETCURSOR message for all EN_LINK notifications, you won't see the hand cursor at all.
[StructLayout(LayoutKind.Sequential)]
struct CHARRANGE
{
public int cpMin;
public int cpMax;
};
[StructLayout(LayoutKind.Sequential)]
struct NMHDR
{
public IntPtr hwndFrom;
public IntPtr idFrom;
public int code;
};
[StructLayout(LayoutKind.Sequential)]
struct ENLINK
{
public NMHDR nmhdr;
public int msg;
public IntPtr wParam;
public IntPtr lParam;
public CHARRANGE chrg;
};
public class MyForm : Form
{
// ... other code ...
protected override void WndProc(ref Message m)
{
const int WM_NOTIFY = 0x004E;
const int EN_LINK = 0x070B;
const int WM_SETCURSOR = 0x0020;
if (m.Msg == WM_NOTIFY)
{
NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nmhdr.code == EN_LINK)
{
ENLINK enlink = (ENLINK)m.GetLParam(typeof(ENLINK));
if (enlink.msg == WM_SETCURSOR)
{
// Set the result to indicate this message has been handled,
// and return without calling the default window procedure.
m.Result = (IntPtr)1;
return;
}
}
}
base.WndProc(ref m);
}
}
Unfortunately, that's the easy part. Now comes the ugly hack where we work around the default behavior of the control that you describe, where it treats the remainder of the control's height as part of the last line if the last line is a hyperlink.
To do this, we need to get the current position of the mouse pointer and compare it against the position of the hyperlink text that the control has detected. If the mouse pointer is within the hyperlinked line, we allow the default behavior and show the hand cursor. Otherwise, we suppress the hand cursor. See the commented code below for a potentially better explanation of the process (obviously, rtb is your RichTextBox control):
protected override void WndProc(ref Message m)
{
const int WM_NOTIFY = 0x004E;
const int EN_LINK = 0x070B;
const int WM_SETCURSOR = 0x0020;
if (m.Msg == WM_NOTIFY)
{
NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nmhdr.code == EN_LINK)
{
ENLINK enlink = (ENLINK)m.GetLParam(typeof(ENLINK));
if (enlink.msg == WM_SETCURSOR)
{
// Get the position of the last line of text in the RichTextBox.
Point ptLastLine = rtb.GetPositionFromCharIndex(rtb.TextLength);
// That point was in client coordinates, so convert it to
// screen coordinates so that we can match it against the
// position of the mouse pointer.
ptLastLine = rtb.PointToScreen(ptLastLine);
// Determine the height of a line of text in the RichTextBox.
//
// For this simple demo, it doesn't matter which line we use for
// this since they all use the same size and style. However, you
// cannot generally rely on this being the case.
Size szTextLine = TextRenderer.MeasureText(rtb.Lines[0], rtb.Font);
// Then add that text height to the vertical position of the
// last line of text in the RichTextBox.
ptLastLine.Y += szTextLine.Height;
// Now that we know the maximum height of all lines of text in the
// RichTextBox, we can compare that to the pointer position.
if (Cursor.Position.Y > ptLastLine.Y)
{
// If the mouse pointer is beyond the last line of text,
// do not treat it as a hyperlink.
m.Result = (IntPtr)1;
return;
}
}
}
}
base.WndProc(ref m);
}
Tested and working… But did I mention that this is an ugly hack? Treat it more like a proof of concept. I certainly don't recommend using it in production code. I'm in fairly strong agreement with Hans and Jeremy that you should either take the simpler approach of adding a line break, or use a more appropriate control designed to display hyperlinks.

Blocking the ability of being able to highlight text in a textbox in a C# windows form

I want to create a Windows form in C# with textbox whose text cannot be altered or selected by the user.
I managed to make it unalterable (ReadOnly = true), but I am still able to select and highlight the text.
How can I change this?
Thanks!
-R
This is the expected behavior and is by design. You can see this for yourself if you right-click on an item in Windows Explorer and open its Properties window. All of the dynamic text fields on the "General" tab are selectable, though not modifiable. If this is not the behavior you desire, you should strongly consider using a Label control instead. This is the exact situation for which it was intended—static text that is not selectable by the user.
In case, for whatever reason, you're not sold on why you should use a Label control instead, I'll proceed onward in answering the question you asked. The major problem with trying to override this behavior (like overriding the default behavior of any control) is that there are a lot of ways for the user to accomplish it: clicking and dragging with the mouse, double-clicking the mouse button, using the arrow keys on the keyboard in combination with the Shift key, tabbing into the textbox,right-clicking and choosing "Select All" from the context menu, etc...
Your first instinct might be to override the potentially relevant events exposed by the .NET Framework (such as OnMouseDown, OnKeyDown, etc.) and reset the selected length to 0 so that the text is immediately unselected after it is automatically selected. However, this causes a little bit of a flicker effect in the process, which may or may not be acceptable to you.
Alternatively, you could do the same thing by overriding the control's WndProc method, watching for the relevant window messages, and preventing the messages from being passed on to the control's base class altogether. This will prevent the flicker effect because the base TextBox control never actually receives a message causing it to automatically select its text, but it does have the obvious side effect of preventing the control from taking any action as a result of these common events. It seems like that probably doesn't matter to you in this case, but realize that this is still a pretty drastic hack. I definitely cannot recommend it as being good practice.
That being said, if you're still interested, you could use the following custom UnselectableTextBox class. It prevents the processing of every window message that I could think of which would possibly allow the user to select text. It does indeed work, but fair warning: there may be yet others I haven't thought of.
public class UnselectableTextBox : TextBox
{
public UnselectableTextBox() : base()
{
//Set it to read only by default
this.ReadOnly = true;
}
protected override void OnGotFocus(System.EventArgs e)
{
base.OnGotFocus(e);
//Prevent contents from being selected initally on focus
this.DeselectAll();
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_KEYDOWN = 0x100;
const int WM_LBUTTONDOWN = 0x201;
const int WM_LBUTTONDBLCLK = 0x203;
const int WM_RBUTTONDOWN = 0x204;
if ((m.Msg == WM_KEYDOWN) || (m.Msg == WM_LBUTTONDOWN) ||
(m.Msg == WM_LBUTTONDBLCLK) || (m.Msg == WM_RBUTTONDOWN))
{
this.DeselectAll();
return;
}
base.WndProc(ref m);
}
}
Use label inplace of textbox. I think you only want to show information but not to select, not to copy, using label will be better option...
Is disabling it an option? (Enabled = false)

RichTextBox syntax highlighting in real time--Disabling the repaint

I'm creating a function that takes a RichTextBox and has access to a list of keywords & 'badwords'. I need to highlight any keywords & badwords I find in the RichTextBox while the user is typing, which means the function is called every time an editing key is released.
I've written this function, but the words and cursor in the box flicker too much for comfort.
I've discovered a solution--to disable the RichTextBox's ability to repaint itself while I'm editing and formatting its text. However, the only way I know to do this is to override the "WndProc" function and intercept (what I've been about to gather is) the repaint message as follows:
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == 0x00f) {
if (paint)
base.WndProc(ref m);
else
m.Result = IntPtr.Zero;
}
else
base.WndProc(ref m);
}
Where the boolean 'paint' is set to false just before I start highlighting and to true when I finish. But as I said, the function I make must take in a RichTextBox; I cannot use a subclass.
So, is there a way to disable the automatic repainting of a RichTextBox 'from the outside?'
It is an oversight in the RichTextBox class. Other controls, like ListBox, support the BeginUpdate and EndUpdate methods to suppress painting. Those methods generate the WM_SETREDRAW message. RTB in fact supports this message, but they forgot to add the methods.
Just add them yourself. Project + Add Class, paste the code shown below. Compile and drop the control from the top of the toolbox onto your form.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class MyRichTextBox : RichTextBox {
public void BeginUpdate() {
SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)0, IntPtr.Zero);
}
public void EndUpdate() {
SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
this.Invalidate();
}
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
private const int WM_SETREDRAW = 0x0b;
}
Or P/Invoke SendMessage directly before/after you update the text.
I haven't accumulated enough points to amend Hans' recommendation. So I added this Answer to mention that it may be necessary to request a repaint by calling InvalidateRect. Some Begin/End Update implementations do this automatically upon the final release of the update lock. Similarly in .Net, Control.Invalidate() can be called which invokes the native InvalidateRect function.
MSDN: Finally, the application can call the InvalidateRect function to cause the list box to be repainted.
See WM_SETREDRAW
Your best bet to accomplish what you are trying to do is to create a multithreaded application. You'll want to create one thread that checks the text against your list. This thread will put any instances it finds into a queue. You'll also want to create another thread that does the actual highlighting of the words. Because you'll need to use BeginInvoke() and Invoke() to update the UI, you'll want to make sure you throttle the rate at which this gets called. I'd so no more then 20 times per second. To do this, you'd use code like this:
DateTime lastInvoke=DateTime.Now;
if ((DateTime.Now - lastInvoke).TotalMilliseconds >=42)
{
lastInvoke=DateTime.Now;
...Do your highlighting here...
}
This thread will check your queue for words that need to be highlighted or re-highlighted and will constantly check the queue for any new updates. Hope this makes sense!

Categories

Resources