您的位置:首页 > 编程语言 > C#

<Pro WPF 4.5 in C#> - Note-02

2016-04-19 12:28 911 查看
Chapter 7: The Application

The Application

The Application Life Cycle

Application Tasks

Assembly Resources

Localization

Chapter 8: Element Binding

Binding Elements Together

The Binding Expression

<TextBlock Margin="10" Text="Simple Text" Name="lblSampleText" FontSize="{Binding ElementName=sliderFontSize, Path=Value}" >
</TextBlock>


Binding Modes

<TextBlock Margin="10" Text="Simple Text" Name="lblSampleText" FontSize="{Binding ElementName=sliderFontSize, Path=Value, Mode=TwoWay}" >
</TextBlock>


NameDescription
OneWayThe target property is updated when the source property changes.
TwoWayThe target property is updated when the source property changes, and the source property is updated when the target property changes.
OneTimeThe target property is set initially based on the source property value. However, changes are ignored from that point onward (unless the binding is set to a completely different object or you call BindingExpression.UpdateTarget(),
as described later in this chapter). Usually, you'll use this mode to reduce overhead if you know the source property won't change.
OneWayToSourceSimilar to OneWay but in reverse. The source property is updated when the target property changes (which might seem a little backward), but the target property is never updated.
DefaultThe type of binding depends on the target property. It's either TwoWay (for usersettable properties, such as the TextBox.Text) or OneWay (for everything else). All bindings use this approach unless you specify otherwise.
Creating Bindings with Code

Binding binding = new Binding();
binding.Source = sliderFontSize;
binding.Path = new PropertyPath("Value");
binding.Mode = BindingMode.TwoWay;
lblSampleText.SetBinding(TextBlock.FontSizeProperty, binding);


BindingOperations.ClearAllBindings(lblSampleText);


Retrieving Bindings in Code

Binding binding = BindingOperations.GetBinding(lblSampleText, TextBlock.FontSize);


BindingExpression expression = BindingOperations.GetBindingExpression(lblSampleText, TextBlock.FontSize);
// Get the source element.
Slider boundObject = (Slider)expression.ResolvedSource;
// Get any data you need from the source element, including its bound property.
string boundData = boundObject.FontSize;


Multiple Bindings

<ListBox Margin="3" Grid.Row="3" Name="lstColors">
<ListBoxItem Tag="Blue">Blue</ListBoxItem>
<ListBoxItem Tag="DarkBlue">Dark Blue</ListBoxItem>
<ListBoxItem Tag="LightBlue">Light Blue</ListBoxItem>
</ListBox>

<TextBlock Margin="3" Name="lblSampleText"
FontSize="{Binding ElementName=sliderFontSize, Path=Value}"  Grid.Row="4"
Text="{Binding ElementName=txtContent, Path=Text}"
Foreground="{Binding ElementName=lstColors, Path=SelectedItem.Tag}"
>
</TextBlock>


Binding Updates

When you use OneWay or TwoWay binding, the changed value is propagated from the source to the target immediately. However, changes that flow in the reverse direction—from the target to the source—don't necessarily happen immediately.
Instead, their behavior is governed by the Binding.UpdateSourceTrigger property:

NameDescription
PropertyChangedThe source is updated immediately when the target property changes.
LostFocusThe source is updated when the target property changes and the target loses focus.
ExplicitThe source is not updated unless you call the BindingExpression.UpdateSource() method.
DefaultThe updating behavior is determined by the metadata of the target property (technically, its FrameworkPropertyMetadata.DefaultUpdateSourceTrigger property). For most properties, the default behavior is PropertyChanged, although the TextBox. Text property
has a default behavior of LostFocus.
If you choose the UpdateSourceTrigger.Explicit mode, it's up to your code to manually trigger the update. The BindingExpression object provides two methods for triggering an immediate update for one
part of the binding: UpdateSource() and UpdateTarget().

// Get the binding that's applied to the text box.
BindingExpression binding = txtFontSize.GetBindingExpression(TextBox.TextProperty);
// Update the linked source (the TextBlock).
binding.UpdateSource();


Binding Delays

Binding to Objects That Aren't Elements

To draw the data from a nonvisual object, the only requirement is that the information you want to display must be stored in public properties. The WPF data-binding infrastructure won't pick up private information or public fields.

Source

You can use several approaches for getting the data object:

pull it out of a resource

generate it programmatically

get it with the help of a data provider

Pointing the Source to some static object that's readily available:

<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}"></TextBlock>


Binding to an object that you've previously created as a resource:

<Window.Resources>
<FontFamily x:Key="CustomFont">Calibri</FontFamily>
</Window.Resources>
<TextBlock Text="{Binding Source={StaticResource CustomFont}, Path=Source}"></TextBlock>


RelativeSource

<TextBlock>
<TextBlock.Text>
<Binding Path="Title">
<Binding.RelativeSource>
<RelativeSource Mode="FindAncestor" AncestorType="{x:Type Window}" />
</Binding.RelativeSource>
</Binding>
</TextBlock.Text>
</TextBlock>

<TextBlock Text="{Binding Path=Title, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}} }">
</TextBlock>


DataContext

<StackPanel>
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}"></TextBlock>
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=LineSpacing}"></TextBlock>
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=FamilyTypefaces[0].Style}"></TextBlock>
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=FamilyTypefaces[0].Weight}"></TextBlock>
</StackPanel>

can be simplified as:

<StackPanel DataContext="{x:Static SystemFonts.IconFontFamily}">
<TextBlock Margin="5" Text="{Binding Path=Source}"></TextBlock>


Chapter 9: Commands

Commands

Imagine your program has an application method, which can be triggered in multiple ways, like through a main menu item, a context menu, a keyboard shortcut or a toolbar button. At certain points in your application's lifetime,
you need to temporarily disable the method.

WPF includes a command model that can help you deal with these issues. It adds two key features:

It delegates events to the appropriate commands.
It keeps the enabled state of a control synchronized with the state of the corresponding command.





The WPF Command Model

Commands: A command represents an application task and keeps track of whether it can be executed. However, commands don't actually contain the code that performs the application task.

Command bindings: Each command binding links a command to the related application logic, for a particular area of your user interface. This factored design is important, because a single command might be used in several places
in your application and have a different significance in each place. To handle this, you use the same command with different command bindings.

Command sources: A command source triggers a command. For example, a MenuItem and a Button can both be command sources. Clicking them executes the bound command.

Command targets: A command target is the element on which the command is being performed. For example, a Paste command might insert text into a TextBox, and an OpenFile command might pop a document into a DocumentViewer. The
target may or may not be important, depending on the nature of the command.

The ICommand Interface

public interface ICommand
{
void Execute(object parameter);
bool CanExecute(object parameter);
event EventHandler CanExecuteChanged;
}

It uses the Execute() method to fire off a process that eventually raises an event that's handled elsewhere in your application.

Finally, the CanExecuteChanged event is raised when the state changes. Any controls using the command that they should call the CanExecute() method to check. Hence command sources (such as a Button or MenuItem) can automatically
enable/disable themselves.

The RoutedCommand Class

The RoutedCommand class is the only class in WPF that implements ICommand. In other words, all WPF commands are instances of RoutedCommand (or a derived class).

The RoutedCommand modifies the command so that it can bubble through the WPF element hierarchy to get to the correct event handler.

public void Execute(object parameter, IInputElement target) {...}


This event begins at the target element and bubbles up to higher-level containers until your application handles it to perform the appropriate task.

RoutedCommand also introduces three properties:

Name: the command name

OwnerType: the class that this command is a member of

InputGestures: keystrokes or mouse actions that can also be used to trigger the command

This design is to make sure you can handle this event in one place, even if it's fired by different command sources in the same window, you need the power of event bubbling.

The RoutedUICommand Class

RoutedUICommand class derives from RoutedCommand. It adds a single property—Text—which is the display text for that command.

The Command Library

WPF includes a basic command library that's stocked with more than 100 commands. These commands are exposed through the static properties of five dedicated static classes:

ApplicationCommands
NavigationCommands
EditingCommands
ComponentCommands
MediaCommands

Executing Commands

Command Sources

The easiest way to trigger commands is to hook them up to a control that implements the ICommandSource interface, which includes controls that derive from ButtonBase (Button, CheckBox, and so on), individual ListBoxItem objects,
the Hyperlink, and the MenuItem.

ICommandSource interface defines three properties:

Command

CommandParameter

CommandTarget

<Button Command="ApplicationCommands.New">New</Button>

Can be:

<Button Command="New">New</Button>


Command Bindings

// Create the binding.
CommandBinding binding = new CommandBinding(ApplicationCommands.New);
// Attach the event handler.
binding.Executed += NewCommand_Executed;
// Register the binding.
this.CommandBindings.Add(binding);

private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("New command triggered by " + e.Source.ToString());
}


This works through event bubbling. Essentially, when the button is clicked, the CommandBinding.Executed event bubbles up from the button to the containing elements.

The CommandBindings property is actually defined in the base UIElement class. That means it's supported by any element. For greatest flexibility, command bindings are usually added to the top-level window. If you want to use
the same command from more than one window, you'll need to create a binding in both windows.

If you wanted to pass additional information, you would set the CommandParameter property of the command source. And if you wanted to pass a piece of information drawn from another control, you would need to set CommandParameter
by using a data-binding expression.

Binding using XAML:

<Window x:Class="Commands.TestNewCommand"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestNewCommand">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="NewCommand_Executed"></CommandBinding>
</Window.CommandBindings>
<StackPanel Margin="5">
<Button Padding="5" Command="ApplicationCommands.New">New</Button>
</StackPanel>
</Window>


Using Multiple Command Sources

<Menu>
<MenuItem Header="File">
<MenuItem Command="New"></MenuItem>
</MenuItem>
</Menu>


This MenuItem object doesn't set the Header property. It automatically picks up Ctrl+N shortcut, and it appears in the menu alongside the menu text.

Fine-Tuning Command Text

<Button Command="New" Content="{x:Static ApplicationCommands.New}"></Button>


<Button Margin="5" Padding="5" Command="ApplicationCommands.New" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"
</Button>


<Button Margin="5" Padding="5" Command="ApplicationCommands.New" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}">
<Image ... />
</Button>


Invoking a Command Directly

ApplicationCommands.New.Execute(null, targetElement);


The target element is simply the element where WPF begins looking for the command binding. You can use the containing window (which has the command binding) or a nested element (such as the actual element that fired the event).

this.CommandBindings[0].Command.Execute(null);


This method doesn't give you a way to respond to the command's state change, if you...

Disabling Commands

...

Controls with Built-in Commands

Some input controls handle command events on their own. For example, the TextBox class handles the Cut, Copy, and Paste commands, as well as Undo and Redo.

When a control has its own hardwired command logic, you don’t need to do anything to make your command work.

If you place your buttons in a different container (other than a ToolBar or Menu), you won't have this benefit. That means your buttons won't work unless you set the CommandTarget property manually.

<Button Command="Cut" CommandTarget="{Binding ElementName=txtDocument}">Cut</Button>
<Button Command="Copy" CommandTarget="{Binding ElementName=txtDocument}">Copy</Button>
<Button Command="Paste" CommandTarget="{Binding ElementName=txtDocument}">Paste</Button>

Another, simpler option is to create a new focus scope by using the attached FocusManager.

<StackPanel FocusManager.IsFocusScope="True">
<Button Command="Cut">Cut</Button>
<Button Command="Copy">Copy</Button>
<Button Command="Paste">Paste</Button>
</StackPanel>


In some rare cases, you might not want to enable some built-in commands of a control. In this situation, you have three options for supressing the command:

...

Advanced Commands

Custom Commands

The way to do this is pretty straightforward, you don't need to code your command's implementation, it is just a RoutedUICommand instance First of all, you need a class to hold your command:

public class DataCommands
{
private static RoutedUICommand requery;

static DataCommands()
{
InputGestureCollection inputs = new InputGestureCollection();
inputs.Add(new KeyGesture(Key.R, ModifierKeys.Control, "Ctrl+R"));
requery = new RoutedUICommand("Requery", "Requery", typeof(DataCommands), inputs);
}

public static RoutedUICommand Requery
{
get { return requery; }
}
}

The rest is exactly the same as what you do with the normal commands. You can use it in XAML like:

<Window.CommandBindings>
<CommandBinding Command="local:DataCommands.Requery" Executed="RequeryCommand"/>
</Window.CommandBindings>

Of course you will need to implement your handler method for that command:

private void RequeryCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Requery");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: