Each .NET control has several methods that enable developers that extend that control to either interact with, or alter its functionality. Note that these are not events, but protected methods, that are called at specific points throughout the control's lifetime. As we have already seen in Chapter 5, Using the Right Controls for the Job, each .NET control extends a number of base classes, with each providing certain additional functionality.
In a similar way, each base class also provides a number of these protected methods, that enable us to interact with the control internally. In this chapter, we will also show how we can create our own methods that enable developers that extend our own control classes to adapt or extend their functionality.
Let's first take a look at the protected methods of the Window class:
protected override Size ArrangeOverride(Size arrangeBounds); protected override Size MeasureOverride(Size availableSize); protected virtual void OnActivated(EventArgs e); protected virtual void OnClosed(EventArgs e); protected virtual void OnClosing(CancelEventArgs e); protected override void OnContentChanged(object oldContent, object newContent); protected virtual void OnContentRendered(EventArgs e); protected override AutomationPeer OnCreateAutomationPeer(); protected virtual void OnDeactivated(EventArgs e); protected virtual void OnLocationChanged(EventArgs e);
protected override void OnManipulationBoundaryFeedback(ManipulationBoundaryFeedbackEventArgs e); protected virtual void OnSourceInitialized(EventArgs e); protected virtual void OnStateChanged(EventArgs e); protected internal sealed override void OnVisualParentChanged(DependencyObject oldParent);
You may notice that they are all marked with either the virtual or override keywords, indicating that they can be overridden in extending classes. Apart from the ArrangeOverride and MeasureOverride methods, that we discovered in Chapter 5, Using the Right Controls for the Job, you should see that their names all start with the prefix On. This signifies that they are called upon some action having taken place.
For example, the OnActivated method is called when the Window becomes the active window on the computer, while the OnDeactivated method is called when the Window loses focus. These methods are usually used together to pause and resume animations, or other processes, while the Window is not in focus.
As expected, the OnClosed method is called upon the Window being closed and gives us a chance to dispose of any resources, or to save user preferences before closing the application. Conversely, The OnClosing method is called before the Window is closed and gives us a chance to cancel the close operation.
Therefore, the OnClosing method would be a good method from which to display a dialog, asking the user to confirm the close operation. Let's take a quick look at how we might achieve this in a class that extends the Window class:
using System.ComponentModel; using System.Windows; ... protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); MessageBoxResult result = MessageBox.Show("Are you sure you want to close?",
"Close Confirmation", MessageBoxButton.OKCancel, MessageBoxImage.Question); e.Cancel = result == MessageBoxResult.Cancel; }
In this simple example, we override the OnClosing method and in it, we first call the base class method, to ensure that any base class routines are run as expected. We then display a message box to the user, asking them to confirm their close operation.
With the resulting value attained from the user via the message box buttons, we set the Cancel property of the CancelEventArgs object that is passed into the method. If the returned value is Cancel, the Cancel property is set to true and the close operation is canceled, otherwise, it is set to false and the application is closed.
Returning to the Window class now, we see the OnLocationChanged method, which is called whenever the Window is moved or resized in a manner that moves its top left corner. We could use this method to save the last position of the Window, so that it could be returned there the next time the user opened their application. However, this operation is more typically performed upon the user closing the application.
The OnSourceInitialized method is called after the window source is created, but before it is shown and the OnStateChanged method is called when the WindowState property is changed. So you see, these methods provide us with opportunities to perform actions at specific points throughout each control's lifetime.
Each base class adds its own collection of these protected methods for us to take advantage of, and ones of interest are overridden in the extending classes. Looking at the Window class declaration, we see that it extends the ContentControl class. Notice that its OnContentChanged method is marked with the override keyword.
This is because this method, which is actually declared in the ContentControl class, has been overridden in the Window class so that it could add its own code after the base class functionality has been executed. Let's have a look at the source code for this method from the Window class. The comments in the source code have been removed for brevity:
protected override void OnContentChanged(object oldContent, object newContent) { base.OnContentChanged(oldContent, newContent); SetIWindowService(); if (IsLoaded == true) { PostContentRendered(); } else { if (_postContentRenderedFromLoadedHandler == false) { this.Loaded += new RoutedEventHandler(LoadedHandler); _postContentRenderedFromLoadedHandler = true; } } }
The method starts by calling the base class version of the method, which is always a good practice unless we want to stop the existing functionality from being performed. Next, it calls the SetIWindowService method, which just sets the Window object to the IWindowServiceProperty Dependency Property, and then it checks if the Window has passed the loading stage or not.
If it has, then it calls the PostContentRendered method, which basically invokes the OnContentRendered method using the Dispatcher object. Otherwise, if the _postContentRenderedFromLoadedHandler variable is false, it attaches an event handler to the Loaded event and sets the variable to true, to ensure that it is not attached more than once.
Returning to our investigation now, we see that the Window class adds protected methods relating to the Window and the ContentControl class adds protected methods relating to the content of the control. Let's see the protected methods of the ContentControl class now:
protected virtual void AddChild(object value); protected virtual void AddText(string text); protected virtual void OnContentChanged(object oldContent, object newContent); protected virtual void OnContentStringFormatChanged(string oldContentStringFormat, string newContentStringFormat); protected virtual void OnContentTemplateChanged(DataTemplate oldContentTemplate, DataTemplate newContentTemplate); protected virtual void OnContentTemplateSelectorChanged(DataTemplateSelector oldContentTemplateSelector, DataTemplateSelector newContentTemplateSelector);
Apart from the first two methods, which can be used to add a specified object or text string to the ContentControl element, the remaining four methods are all called in response to a change in the content, or the format of the content of the control.
Moving on now, the ContentControl class extends the Control class, which introduces the concept of the ControlTemplate. As such, it provides a protected OnTemplateChanged method, which is called when the ControlTemplate value is changed:
protected override Size ArrangeOverride(Size arrangeBounds); protected override Size MeasureOverride(Size constraint); protected virtual void OnMouseDoubleClick(MouseButtonEventArgs e); protected virtual void OnPreviewMouseDoubleClick(MouseButtonEventArgs e); protected virtual void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate);
The Control class extends the FrameworkElement class, which provides framework-level methods and events. These include a mouse, keyboard, stylus, touch, and focus-related protected methods, along with several others:
protected virtual Size ArrangeOverride(Size finalSize); protected override Geometry GetLayoutClip(Size layoutSlotSize); protected override Visual GetVisualChild(int index); protected virtual Size MeasureOverride(Size availableSize); protected virtual void OnContextMenuClosing(ContextMenuEventArgs e); protected virtual void OnContextMenuOpening(ContextMenuEventArgs e); protected override void OnGotFocus(RoutedEventArgs e); protected virtual void OnInitialized(EventArgs e); protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e); protected virtual void OnToolTipClosing(ToolTipEventArgs e); protected virtual void OnToolTipOpening(ToolTipEventArgs e);
Perhaps by now you will have noticed that many of these method names relate closely to the names of events raised by each class. In fact, there is a .NET Framework programming guideline for having protected virtual methods that raise events, to allow derived classes to override the event invocation behavior and we'll see an example of this later in this chapter.
When overriding these methods, we are therefore required to call the base class method in order to raise the corresponding event. When in doubt, it's usually best to call the base class version of the method to ensure that default functionality is not lost. However, it's good practice to view the base class method source code on the www.referencesource.microsoft.com website, to check if we need to call it or not.
You may be wondering what the difference between handling the events and overriding the related protected methods is and there are a few answers to this, depending upon the method in question. The first thing to point out is that in order to override a protected method, we need to declare a subclass of the class that declares the method.
So, assuming that we already have a class that extends a base class, what are the differences? For some methods, such as the OnClosing method that we explored, there is little difference. We could implement the same functionality in an event handler that is attached to the Closing event, although without the call to the base class method. In fact, this is the only real difference.
When overriding the OnClosing method, we are in control of when or if the base class method is called. When handling the event, we have no control over this. So, if we need to perform some action before the base class routine is executed or if we want to stop it from executing, then we will need to override the OnClosing method.
So, the appearance of the OnClosing method is there, purely for convenience, for us to be able to alter the default behavior of the Closing event. Other methods, however, such as the OnContextMenuClosing method, introduce a way for us to perform class-wide handling for the related events.
Sometimes though, we have no alternative to overriding these protected methods. Typically, these types of methods do not start with the prefix On and do not relate to any event. Occasionally, to perform a particular operation, we may need to extend a class, just so that we can provide a new implementation for one of these methods.
Let's look at an example using the GetLayoutClip method from the FrameworkElement class that we just saw.