Showing posts with label ButtonBase. Show all posts
Showing posts with label ButtonBase. Show all posts

Tuesday, March 8, 2011

Toggle Buttons

Both CheckBox and RadioButton are considered types of "toggle" buttons, as is clear from a complete view of the classes that descend from ButtonBase:

Control
--ContentControl
------ButtonBase (abstract)
------------Button
-------------GridViewColumnHeader
-------------RepeatButton
-------------ToggleButton
---------------------CheckBox
---------------------RadioButton

ToggleButton is not abstract, so you can also create an object of type ToggleButton, which looks just like a regular button except that it toggles on and off.

Consider the example :

ToggleButton btn = new ToggleButton();
btn.Content = "Can _Resize";
btn.HorizontalAlignment =HorizontalAlignment.Center;
btn.VerticalAlignment =VerticalAlignment.Center;
//win is the Window reference in which toggle button lies
btn.IsChecked = (win.ResizeMode ==ResizeMode.CanResize);
btn.Checked += ButtonOnChecked;
btn.Unchecked += ButtonOnChecked;

//Event handler ButtonOnChecked
void ButtonOnChecked(object sender,RoutedEventArgs args)
{
ToggleButton btn = sender as ToggleButton;
win.ResizeMode = (bool)btn.IsChecked ? ResizeMode.CanResize : ResizeMode.NoResize;
}



ToggleTheButton uses the button to switch the ResizeMode property of the window between ResizeMode.CanResize and ResizeMode.NoResize. The button automatically toggles itself on and off as the user clicks it. The IsChecked property indicates the current setting. Notice that the program initializes IsChecked to true if ResizeMode is initially ResizeMode.CanResize and false otherwise. Two events named Checked and Unchecked are triggered as the button is checked and unchecked. You can attach the same event handler to both events and simply use the IsChecked property to distinguish them, which is exactly what ToggleTheButton does.

Notice that the IsChecked property must be cast to a bool before being used in the C# conditional operator. The IsChecked property is actually a nullable bool, and can take on values of true, false, and null. If you set the IsThreeState property of ToggleButton to true, you can set IsChecked to null to display a third, "indeterminate" state of the button. For example, an Italic button in a word processing program might use this indeterminate state if the currently selected text has a mix of both italic and non-italic characters. (However, if the currently selected text is in a font that doesn't allow italics, the program would disable the Italic button by setting its IsEnabled property to false.)

The CheckBox class adds almost nothing to ToggleButton. You can create a CheckBox in this program just by replacing the button creation code with this:

CheckBox btn = new CheckBox();



 

 

You don't need to change the event handler. The program works the same except that the button looks different. You could also change ToggleTheButton's button to a RadioButton, but the program wouldn't work properly. You cannot uncheck a radio button by manually clicking it.

Because a ToggleButton or a CheckBox essentially represents the value of a Boolean, it makes sense that a particular toggle button be associated with a Boolean property of some object. This is called data binding, and it is such an important part of the Windows Presentation Foundation that Chapter 23 is devoted to the topic.

A ToggleButton used with data binding doesn't need to have its IsChecked property initialized, or have handlers installed for the Checked and Unchecked events. What's required instead is a call to the SetBinding method, which the button inherits from the FrameworkElement class. For a ToggleButton or CheckBox object, the call is typically something like this:

btn.SetBinding(ToggleButton.IsCheckedProperty, "SomeProperty");


where the second argument is a string with the name of a property you want associated with the IsChecked state of the button. You specify the object that "SomeProperty" belongs to with the DataContext property of Button.

The first argument to the SetBinding method probably looks even stranger than the second argument. It refers not to the IsChecked property of a ToggleButton object, but instead to something called IsCheckedProperty that appears to be a static member of the ToggleButton class.

ToggleButton does indeed define a static field named IsCheckedProperty that is an object of type DependencyProperty. Chapter 8 is devoted in its entirety to dependency properties. For now, think of it just as a convenient way to refer to a particular property defined by ToggleButton.

So, to what Boolean property do we want to bind the ToggleButton? One convenient Boolean is the Topmost property of the window. As you'll recall, if Topmost is set to true, the window always appears in the foreground of other windows. The SetBinding call looks like this:

btn.SetBinding(ToggleButton.IsCheckedProperty, "Topmost");


The data binding won't try to guess which object in the program is the one whose Topmost property you're referring to, so you need to specify that object by setting the DataContext property of the button to the window object:

btn.DataContext = this;



Command Property

Installing a handler for the button's Click event is certainly the most traditional way for a program to be notified when the user clicks a button. However, it could be that a particular operation in your program can be triggered through several sourcesperhaps a menu item, a toolbar item, and a button. Perhaps you even implement a scripting language that can trigger this same operation. It might make more sense to route all these controls through a single point.

This is the idea behind a property named Command, which is defined by the ButtonBase class and some other classes, including MenuItem.

For common commands, you set the Command property of the Button object equal to a static property of the classes ApplicationCommands, ComponentCommands, MediaCommands, NavigationCommands, or EditingCommands. The first four of these classes are located in the System.Windows.Input namespace; the last is in System.Windows.Documents. The static properties of these classes are all of type RoutedUICommand. You can also create your own objects of type RoutedUICommand.

For example, if you wanted a particular button to perform a clipboard Paste command, you would set the Command property like so:

btn.Command = ApplicationCommands.Paste;




It's not required, but you may also want to set the button text to the standard text for that command:

btn.Content = ApplicationCommands.Paste.Text;




The other step is to associate this particular Paste command with event handlers. This association is called a binding, and it's one of several types of bindings you'll find in the Windows Presentation Foundation. The UIElement class defines (and the Control and Window classes inherit) a property named CommandBindings, which is a collection of CommandBinding objects. You'll probably use the CommandBindings collection of the window, so the statement looks something like this:

CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste,
PasteOnExecute, PasteCanExecute));




PasteOnExecute and PasteCanExecute are two event handlers that are located in your program. (You can name them whatever you want.) In the PasteOnExecute handler, you carry out the Paste command. Sometimes, however, your program can't carry out a Paste command because the clipboard doesn't contain data in the format you want. That's the purpose of the PasteCanExecute handler. This handler checks the clipboard for data of the proper format. If the data isn't available, the handler can set a flag and the Button will automatically be disabled.

If this program also has a menu item for Paste, you just need to assign the Command property of the menu item to ApplicationCommands.Paste, and the same binding will apply.


Button btn = new Button();
btn.HorizontalAlignment = HorizontalAlignment.Center;
btn.VerticalAlignment =VerticalAlignment.Center;
btn.Command = ApplicationCommands.Paste;
btn.Content = ApplicationCommands.Paste.Text;

// Bind the command to the event handlers.
CommandBindings.Add(new CommandBinding
(ApplicationCommands.Paste,
PasteOnExecute,
PasteCanExecute));


Writing the handlers:


void PasteOnExecute(object sender,ExecutedRoutedEventArgs args)
{
Title = Clipboard.GetText();
}
//2nd event handler
void PasteCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
args.CanExecute = Clipboard.ContainsText();
}


The two event handlers PasteOnExecute and PasteCanExecute use static methods from the Clipboard class (located in System.Windows). The static GetText and ContainsText methods both exist in overloaded versions that allow you to be specific about the type of text you wantincluding comma-separated format, HTML, RTF, Unicode, or XAMLbut this program doesn't use those text types. The text it gets from the clipboard is just used to set the window's Title property. Since you may want to experiment with this program a bit, the last method in the program lets you click anywhere in the window to restore the Title text.


Before running the program, you might want to copy some text to the clipboard from Notepad or another text-based application (even Visual Studio) just so the program has something to work with. You'll discover that the button responds not only to the normal keyboard and mouse input, but also to Ctrl+V, the standard keyboard shortcut for Paste. That's one of the benefits of using a command binding rather than just handling the Click event.

Now copy a bitmap into the clipboard. No need to run Paint or PhotoShop! Just press the Print Screen key to copy the screen image into the clipboard. Whenever there's a change in the clipboard, any bound can-execute event handlers (such as PasteCanExecute) are called so that the handler has the opportunity to properly set the CanExecute property of the event arguments. The button is immediately disabled. Now copy some text into the clipboard. The button is enabled again. Behind the scenes, the command binding changes the IsEnabled property that Button inherits from UIElement.