I've got a flowchart-style application drawn on a WPF Canvas. (Basically boxes connected with lines). The boxes are hooked up to various events on the 'model'. I need to able to clear the Canvas, without destroying the model.
What I do is remove all the boxes etc from the Canvas' Children collection. This works fine visually, but the controls seems to live on in limbo somewhere in memory. When I later fire events on the model, the controls crash my application. (the now-invisible boxes 'expect' to be the Child of a Canvas).
How should I indicate the controls are no longer wanted? I want them destroyed, not responding to events and bindings. Are the bindings to the model preventing WPF disposing of them? Would setting their Datacontext to null help?
Cheers, Jeff.
Sounds like you may need to have the boxes implement IDisposable and call Dispose on the boxes as you are removing them. In the dispose method you should unhook your event listeners. Or if you intend to boxes to be used again you may need to just expose some method to RemoveListeners() and then a method to AttachListeners() when you want it back in view.
public void Dispose()
{
model.MyEvent -= LocalListener;
}
Related
I am new to Windows Forms. I have a menu in a form. When a user selects a menu option I want to display some elements like a grid, treeview or a grouped UI elements.
What is the UI design pattern for displaying the view for the selected option? Load a user control dynamically in a pane? Show a form and hide a previous form? I am not using tab control. Content is displayed in a pane in the form. It's not a separate window.
I have searched high and low for sample applications and I couldn't find any which has a menu. Any ideas?
Constructing your form as needed in designer and then hiding and showing sounds like a reasonable approach.
I definitely would avoid dynamic content loading. In WinForms you are most likely relying on events to handle UI interaction. If loading/unloading controls dynamically you would have to take care of hooking/unhooking event handlers. It's easy to keep track of that when you have 3 controls each with single event. But if you have more controls and each controls has to take care of many events the loading/unloading and hooking/unhooking events is going to be error prone. Also unhooked event handlers will result in memory leaks. Other problem is that your complex controls will have many properties. All of them will have to be set up in code. You will end up with dozens of lines listing controls' properties and assigning values to them.
Hiding/showing doesn't expose you to these issues. You design your layout once in the designer. So your main code is not clattered with pure UI construction. Also, you do not create a new instance of a control when you show it so you can subscribe event handlers to events at design time and you do not have to worry about unhooking the handlers when hiding. You create one instance of a control and rely on this instance throughout application lifetime.
Is there a way in WPF that I can get notified whenever a WPF control is created or destroyed?
(For example an event I can register to "Control.ControlCreated"...)
What about Loaded and Initialized events of FrameworkElement class (and their counterparts)?
They are not routed events but with some styling and EventSetters they can be hooked to all required controls. The rest depends on what you need to do in event handler.
Other than that, I don`t see any common control events that can satisfy your requirements.
If I create a ComboBox from scratch, when the window is resized, the content is correctly displayed above/below, etc.
Why this doesn't happen when you create your own Combo-derived class? What am I missing?
Controls aren't magical. They need to be told that things happen (call a method on them) or look for them to happen (register for an event).
Find your Application.RootVisual and register for a SizeChanged event. Then resize your control. If it is in browser you may need to create a javascript bridge (hopefully not) that tells your SL object when the browser resizes.
I have a small WPF application, which has a Canvas and a Grid. I placed some custom user controls on the Grid. Now I would like to capture some mouse events on them. As a consequence of one event I would like to add (or modify) something to the canvas. However in the user control, you don't have a reference to the underlying canvas. First question, is there a way to get this reference, for example like getElementById(..) in JavaScript.
Also I know that you should avoid such references, if you want a clean architecture. In this case, whats a good practice to catch events at a specific user control and then to be able to invoke something on another object.
You do have access to the Canvas, Grid or any other element in your UserControl. The easiest way yo access them is to make sure each one has a name which is done by using the x:Name attribute.
<Grid x:Name="myGrid">
Then within your UserControl you can access it with myGrid. To access a Grid from outside the UserControl you would need to create a method in your UserControl that allowed you to manipulate it.
There is a this.FindName method you can use in a UserControl which is the equivalent of javascript's getElementById but you shouldn't need to use it given you can access objects directly with their name.
WPF has a new event architecture that may help you out here. So called "routed" events may either "tunnel" from the logical root container, through all intermediate containers, to the event source element, or "bubble" from the source element up (i.e. "tunneling" and "bubbling" events propagate in opposite directions).
All that to say that you can typically intercept events from child elements by registering an event handler at the container. Here's an example of intercepting button click events from buttons in a StackPanel:
<StackPanel ButtonBase.Click="HandleButtonClick">
<Button>Foo</Button>
<Button>Bar</Button>
</StackPanel>
And HandleButtonClick might be implemented like this:
private void HandleButtonClick(object sender, RoutedEventArgs e)
{
var button = e.OriginalSource as Button;
if (button != null) MessageBox.Show(button.Content.ToString());
}
Depending on what sort of "custom controls" you are using, this may not be possible. This is because not all events are "routed" events. WPF control events are usually routed events.
I have a custom text box control which raises a routed event when its TEXT property changes. This text property is data bound to a property on our view-model object.
When I place this control on a TabControl page or Expander control, it appears as if data binding only occurs when the control becomes visible for the first time, therefore I never receive any of the routed events until I swap to the tab the control is on or expand the expander.
Is there any way I can force data binding to occur before the control is shown?
Sounds like you relying on the data binding to genreate the routed event is the wrong approach. Instead you need to have your Model or ViewModel generate an event when the text is modified and then you watch this event from an appropriate place in your View.
Not very likely. WPF is a fairly efficient framework and won't do any work that it doesn't absolutely have to. This includes scenarios like data binding. Why bother exercising a collection for a control that might not ever be shown?