PDA

View Full Version : Docked .Net controller redraw issue


haavard
03-26-2012, 02:13 AM
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

denisT
03-26-2012, 04:09 AM
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.

LoneRobot
03-26-2012, 11:54 AM
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

haavard
03-26-2012, 01:12 PM
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 :)

denisT
03-26-2012, 02:16 PM
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.

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.

haavard
03-26-2012, 03:27 PM
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

Pjanssen
03-26-2012, 03:31 PM
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 :)

denisT
03-26-2012, 04:55 PM
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.
I don't know if this is a general case or just on my machine

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.

denisT
03-26-2012, 05:01 PM
and the rollout might not be docked. just enough to registerDialogBar... the controls stop redrawing.

denisT
03-26-2012, 08:30 PM
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;\n"
source += "using System.Windows.Forms;\n"
source += "using System.Runtime.InteropServices;\n"
source += "namespace WindowOps\n"
source += "{\n"
source += " public class MessageEventArgs : EventArgs\n"
source += " {\n"
source += " public MessageEventArgs(Message message) { this.message = message; }\n"
source += " public Message message;\n"
source += " }\n"
source += " public class WindowHooker : NativeWindow\n"
source += " {\n"
source += " private const int WM_PAINT = 0x0F;\n"
source += " public WindowHooker() { }\n"
source += " public event EventHandler MessageReceived;\n"
source += " protected override void WndProc(ref Message m)\n"
source += " {\n"
source += " switch (m.Msg)\n"
source += " {\n"
source += " case WM_PAINT:\n"
source += " MessageReceived(this, new MessageEventArgs(m));\n"
source += " break;\n"
source += " default:\n"
source += " break;\n"
source += " }\n"
source += " base.WndProc(ref m);\n"
source += " }\n"
source += " }\n"
source += "}\n"

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: % %\n" (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.

denisT
03-26-2012, 11:59 PM
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.

haavard
03-27-2012, 12:21 AM
Wow, thanks! This is perfect.

I used this

public static class User32
{
[DllImport("user32.dll")]
static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);
public static bool RedrawAllWindow(IntPtr hWnd, uint flags)
{
return RedrawWindow(hWnd, IntPtr.Zero, IntPtr.Zero, flags);
}
}

denisT
03-27-2012, 12:28 AM
Wow, thanks! This is perfect.

I used this

public static class User32
{
[DllImport("user32.dll")]
static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);
public static bool RedrawAllWindow(IntPtr hWnd, uint flags)
{
return RedrawWindow(hWnd, IntPtr.Zero, IntPtr.Zero, flags);
}
}

absolutely right... i'm doing the same.

so because you are using your own controls it shouldn't be a big deal to add to your list one more - pain-hook native window

LoneRobot
03-27-2012, 09:41 AM
Very nice! :)

denisT
03-27-2012, 05:46 PM
I knew that I was overdoing something...
After a little thinking I guessed that MAX creates dotnetcontrols with some wrong window style flag. And I was right with this idea. MAX sets the WS_CLIPCHILDREN flag supposing that dotnetcontrol can't be a parent. Setting this flag also blocks the redrawing of combined controls like ListView(body and header), NumericUpDown(buttons and textbox), etc.

So the good solution is more simple than using of NativeWindow. The thing that we need is only remove the WS_CLIPCHILDREN flag:

fn CreateWindowLongAssembly =
(
source = "using System;\n"
source += "using System.Runtime.InteropServices;\n"
source += "class WindowLong\n"
source += "{\n"
source += " [DllImport(\"user32.dll\", EntryPoint=\"GetWindowLong\")]\n"
source += " public static extern Int32 GetWindowLong(IntPtr hWnd, Int32 index);\n"
source += " [DllImport(\"user32.dll\", EntryPoint=\"SetWindowLong\")]\n"
source += " public static extern Int32 SetWindowLong(IntPtr hWnd, Int32 index, Int32 newVal);\n"
source += "}\n"

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

compilerParams.GenerateInMemory = on
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)

assembly = compilerResults.CompiledAssembly
assembly.CreateInstance "WindowLong"
)
global WindowLong = CreateWindowLongAssembly()

mapped fn removeCLIPCHILDREN control =
(
GWL_STYLE = -16
WS_CLIPCHILDREN = 0x02000000L

hwnd = control.get_handle asdotnetobject:on
flags = bit.xor (WindowLong.GetWindowLong hwnd GWL_STYLE) WS_CLIPCHILDREN
WindowLong.SetWindowLong hwnd GWL_STYLE flags
)


(
try
(
cui.unregisterDialogBar DialogBar
closeRolloutFloater DialogBar
)
catch()

global DialogBar = newRolloutFloater "rf#" 250 300

rollout redrawRol "Redraw Test"
(
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)

removeCLIPCHILDREN #(lv,pn)
)
)
addRollout redrawRol DialogBar
cui.registerDialogBar DialogBar style:#(#cui_dock_all, #cui_floatable, #cui_handles, #cui_dock_horz, #cui_max_sized)
)


Enjoy!

denisT
03-27-2012, 05:54 PM
being absolutely correct I have to say that MAX doesn't set this flag, it leaves it the way how it was set on control creation. Probably for dialogs in Docked mode MAX takes this flag into account when sends redraw messages.

haavard
03-27-2012, 08:40 PM
Nice find, I prefer the last solution!

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.
Any guesses on why these two versions are different in this matter?

denisT
03-27-2012, 09:12 PM
Nice find, I prefer the last solution!


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

because in max 2012 they using a lot of different type of controls with different nature.
they did something with window messaging. A lot of old windows those never flicker are flickering terribly in 2012. I can't say that all window messaging system is completely broken in 2012 but was irreparably improved for sure.

CGTalk Moderation
03-27-2012, 09:12 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.