Notifications
Clear all

[Closed] Docked .Net controller redraw issue

Hi!
I got a problem with a docked custom .net control not being redrawn when I do operations like opening the material editor, converting objects to editable poly, opening the curve editor etc. It looks like the whole max UI is updating itself when I do operations like that. If I undock the rollout with the controller, it redraws itself normally when doing the aforementioned operations. Is there a way to remedy this issue?

Håvard

17 Replies

what control are talking about?
it’s known problem… there are some solutions but they are different for different controls.
if you post a snippet it will help.

usually a timer to force the redraw works for command panel redraw issues. the timer calls the redraw/invalidate method on the control itself. hackilicious!


rollout Trackbot "" width:170 height:249
(
dotNetControl TBbtn1 "button" pos:[1,3] width:156 height:54
dotNetControl TBbtn2 "button" pos:[1,61] width:156 height:54
dotNetControl TBbtn3 "button" pos:[1,119] width:156 height:54
dotNetControl TBbtn4 "button" pos:[1,174] width:156 height:54
timer refresh "" interval:1
on Trackbot open do refresh.active = true

on refresh tick do
	(	
	for i in Trackbot.controls where classof i == dotNetControl do i.refresh()
	refresh.active = false
	)
)
createdialog trackbot

The control inherits usercontrol, and it has multiple other controls that inherits usercontrol, treeview etc. It is written in c#, and the control is in a rollout which is in a docked rolloutfloater. I don’t know what code I can show that would help.

But since I have access to the MaxNotificationListener I thought I could use that, but I need the right system notification code and I dont know which one:
http://download.autodesk.com/global/docs/3dsmaxsdk2012/en_us/cpp_ref/group___notification_codes.html

Using a timer to redraw would have to be my last option though, but I would be cool to find a more subtle approach

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

does it mean that only written by you controls don’t redraw? i don’t think so. make a simple rolloutfloater with couple .net controls, which being docked doesn’t redraw its controls.

That right, like this snippet shows, controls under other controls don’t redraw themselves after certain operations in max. Run this code and open the material editor or convert an object to editable mesh/poly. The button is not redrawn.

(
	height = (filterString (getINIsetting (cui.getConfigFile()) "Command Panel" "FRect") " ")[3] as integer
	::rf = newRolloutFloater "rf" 250 height
		
	rollout rol "rol"
	(
		dotnetcontrol treeview "system.windows.forms.treeview" width:220 height:(height - 100)
		dotnetcontrol panel "system.windows.forms.panel" width:220 height:50
		on rol open do
		(
			b = dotnetobject "system.windows.forms.button"
			panel.controls.add b
			--b.dock = (dotnetclass "system.windows.forms.dockstyle").fill
		)
	)		
	addRollout rol rf 
	cui.registerDialogBar rf style:#(#cui_dock_all, #cui_floatable, #cui_handles, #cui_dock_horz, #cui_max_sized)	
	cui.dockDialogBar rf #cui_dock_left
)

I don’t know if this is a general case or just on my machine

I haven’t tried your example code yet, but this issue reminds me of reports I got from several Outliner users. They reported that the treeview occasionally stopped redrawing. I never found a solution for it, since I expected it to be an error in my implementation, not a fault with max.
But this might change that idea, so I’ll follow this closely

i confirm the issue. the controls don’t redraw in max 2012, and redraw in max 2010.
both my maxs are in Direct3D 9.0 mode. So it’s probably not a driver issue.

1 Reply
(@haavard)
Joined: 11 months ago

Posts: 0

Nice find, I prefer the last solution!

Any guesses on why these two versions are different in this matter?

and the rollout might not be docked. just enough to registerDialogBar… the controls stop redrawing.

here is a solution with using of the NativeWindow to catch WM_PAINT message and send WindowRedraw to handled window:


fn CreateWindowOps =
(

source = ""
source += "using System;
"
source += "using System.Windows.Forms;
"
source += "using System.Runtime.InteropServices;
"
source += "namespace WindowOps
"
source += "{
"
source += "	public class MessageEventArgs : EventArgs
"
source += "	{
"
source += "		public MessageEventArgs(Message message) { this.message = message; }
"
source += "		public Message message;
"
source += "	}
"
source += "	public class WindowHooker : NativeWindow
"
source += "	{
"
source += "		private const int WM_PAINT = 0x0F;
"
source += "		public WindowHooker() { }
"
source += "		public event EventHandler MessageReceived;
"
source += "		protected override void WndProc(ref Message m)
"
source += "		{
"
source += "			switch (m.Msg)
"
source += "			{
"
source += "				case WM_PAINT:
"
source += "					MessageReceived(this, new MessageEventArgs(m));
"
source += "					break;
"
source += "				default:
"
source += "					break;
"
source += "			}
"
source += "			base.WndProc(ref m);
"
source += "		}
"
source += "	}
"
source += "}
"

	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"

	compilerParams.ReferencedAssemblies.AddRange #("System.dll", "System.Windows.Forms.dll")

	compilerParams.GenerateInMemory = true
	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
		
	compilerResults.CompiledAssembly
)
global WindowOps = CreateWindowOps()

(
	try
	(
		cui.unregisterDialogBar DialogBar
		closeRolloutFloater DialogBar
	)
	catch()
	
	global DialogBar = newRolloutFloater "rf#" 250 300
		
	rollout redrawRol "Redraw Test"
	(
		local hook 
		dotnetcontrol lv "ListView" width:220 height:100
		dotnetcontrol pn "UserControl" width:220 height:24
		
		on redrawRol open do
		(
			lv.view = lv.view.Details
			lv.Columns.add "Name" 100
			lv.Columns.add "ID" 100
			
			bt = dotnetobject "Button"
			bt.text = "Button"
			bt.Dock = bt.Dock.Left
			
			lb = dotnetobject "Label"
			lb.text = "Label"
			lb.Dock = lb.Dock.Right
			
			pn.controls.addrange #(lb,bt)

			hook = WindowOps.createInstance "WindowOps.WindowHooker"
			hook.AssignHandle (dotnetobject "IntPtr" (windows.getchildhwnd 0 "rf#")[1])
			
			fn onMessageReceived s e =
			(
				RDW_INVALIDATE	= 0x0001
				RDW_ALLCHILDREN	= 0x0080
				
				format "hook: % %
" (formattedPrint e.message.hwnd format:"#x") (formattedPrint e.message.msg format:"#x")
				user32.redrawallwindow s.handle (RDW_INVALIDATE	+ RDW_ALLCHILDREN)
			)
			dotnet.addEventHandler hook "MessageReceived" onMessageReceived
		)
		on redrawRol close do
		(
			hook.ReleaseHandle()
		)
	)		
	addRollout redrawRol DialogBar
	cui.registerDialogBar DialogBar style:#(#cui_dock_all, #cui_floatable, #cui_handles, #cui_dock_horz, #cui_max_sized)	
)

if you comment hook’s event handler you will see that the controls will stop redrawing.
Dialog itself receives PAINT message but for some reason doesn’t notify its .net children.
i’m forcing redraw sending INVALIDATE message to window and all it’s children.

you can send the redraw message without Event System, by doing it from WndProc.

oops… i didn’t give user32.redrawallwindow function.
it’s from my library. i forgot that it’s not integrated into MAX yet.
to late for today but i will fix it tomorrow.

Page 1 / 2