I would try this using these event interfaces: `IDocumentEvents`, `IActiveViewEvents` and `ILayerEvents`.

When `IDocumentEvents.ActiveViewChanged` fires, refresh your UI with a list of layers, and (re)subscribe, if necessary, to `IActiveViewEvents` on the current map.

When `IActiveViewEvents` fires `ItemAdded`, `ItemDeleted`, update your UI accordingly, and set/unset listeners to `ILayerEvents`.

When `ItemReordered` fires, update your UI, but you shouldn't need to change any `ILayerEvents` subscriptions.

When `ILayerEvents.VisibilityChanged` fires, update the checkbox in your UI for the layer.

To get a feel for when these events fire, I recommend having another test dockablewindow with a multiline textbox in it so you can use it like a console.  Implement event handlers that append an appropriate message to the textbox.  Then use arcmap to go through the steps in a typical workflow and record the events that fire. They are not well documented.