Binding 是前台UI(显示层)和后台代码(数据层)的桥梁。理论上当后台的数据变动时,显示的数据或样式应该随之而变。这些是动态的。
对于Binding的设置可以在前台Xaml,也可以在后台Code里面定义,但是既然Xaml可以做很多事情,那么所有对UI的操作我们都可以交给它。
其实,WPF的本身是一种数据驱动UI的设计模式,使用了MVVM(Model-View-ViewModel)的模式。
以下是绑定的基本思路:
目标(依赖对象(依赖属性)) <=====Binding =====> 源(CLR 对象(属性))
实验1:
设计一个圆形的球,包括红球和篮球,有编号。初始状态为灰色,当选中时,红球颜色显示为红,篮球颜色显示为蓝。再次选中时,颜色返回为初始状态(灰色)
另外,当球被选中时,可以处理一下数据,如读取编号。
思路:
1.首先设计一个Ball 类 包含了IsSelected, Type, Index 属性,并继承了INotifyPropertyChanged接口,当IsSelected属性变更的时候,产生了PropertyChanged的Event。
2.创建了一个BallControl的 User Control (WPF).
3.bindingBall类到BallControl上。对binding做一些设定,从而实现实验1的需求。
实现:
1. 新建Ball类. 实现INotifyPropertyChanged 接口,当属性IsSelected值变化的时候,调用PropertyChanged event
public class Ball : INotifyPropertyChanged { /// <summary> /// Fired whenever a property changes. Required for /// INotifyPropertyChanged interface. /// </summary> public event PropertyChangedEventHandler PropertyChanged; private bool selected; private BallType type; private int index; private Point size; public Ball() :this(false, BallType.gray, 0, new Point(40,40)) { } public Ball(bool ballIsSelected, BallType ballType, int ballIndex, Point ballSize) { this.selected = ballIsSelected; this.type = ballType; this.index = ballIndex; this.size = ballSize; } public bool IsSelected { get { return this.selected; } set { if (value != this.selected) { this.selected = value; if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs("IsSelected")); } } } } public BallType Type { get { return this.type; } } public int Index { get { return this.index; } } } public enum BallType { gray, Red, Blue, }
2. 新建一个User Control (WPF), 名为BallControl,依次放入Grid, Border, Ellipse, TextBlock 控件. 完成 UI 布局。
在后台为鼠标点击动作添加一个Event,当Ball控件被点击选择的时候,会做一些需要的处理。
<UserControl x:Name="myBallControl" x:Class="BallSelection.BallControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:BallSelection" mc:Ignorable="d" Height="100" Width="100"> <Grid Height="100" Width="100" VerticalAlignment="Top" MouseDown="UIElement_OnMouseDown"> <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Width="100" Height="100" RenderTransformOrigin="0.481,0.183"> <Ellipse x:Name="circle" Stroke="Black" Margin="6,6,6,6"> </Ellipse> </Border> <TextBlock x:Name="tbIndex" Text="0" FontSize="58" VerticalAlignment="Center" HorizontalAlignment="Center" /> </Grid> </UserControl>
/// <summary> /// Interaction logic for BallControl.xaml /// </summary> public partial class BallControl : UserControl { public event EventHandler<EventArgs> SelectionChanged; public BallControl() { InitializeComponent(); } private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e) { if (this.SelectionChanged != null) { this.SelectionChanged(this, EventArgs.Empty); } } }
4. 设计MainWindow的UI,依次放入Grid,WrapStack,和BallControl。
<Window x:Class="BallSelection.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:BallSelection" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid HorizontalAlignment="Left" Height="227" Margin="51,38,0,0" VerticalAlignment="Top" Width="396"> <WrapPanel x:Name="wpBalls" HorizontalAlignment="Left" Height="227" VerticalAlignment="Top" Width="386"> <local:BallControl x:Name="redBallControl" /> <local:BallControl x:Name="blueBallControl" /> </WrapPanel> </Grid> </Grid> </Window>
5. 进行Binding, 有两种方法:一种是在后台Code里面进行binding, 另外一种是在前台Xaml里面进行binding。
方法一:在后台Code里面进行binding
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.InitializeBalls(); } private void InitializeBalls() { Ball redBall = new Ball(false, BallType.Red, 1, new Point(50,50)); Binding bindingIndex = new Binding() { Source = redBall, Path = new PropertyPath("Index") }; // this is equal to: this.blueBallControl.tbIndex.SetBinding(TextBlock.TextProperty, bindingIndex); BindingOperations.SetBinding(this.redBallControl.tbIndex, TextBlock.TextProperty, bindingIndex); Binding bindingSelect = new Binding() { Source = redBall, Path = new PropertyPath("IsSelected"), Converter = new BooleanToColor(), }; BindingOperations.SetBinding(this.redBallControl.circle, Ellipse.FillProperty, bindingSelect); // Handle selection changed event. this.redBallControl.SelectionChanged += ((s, e) => { redBall.IsSelected = !redBall.IsSelected; MessageBox.Show(string.Format("BallControl Index {0} is selected. . Ball Type:{1}, Index:{2}, IsSelected:{3}.", this.redBallControl.tbIndex.Text, redBall.Type.ToString(), redBall.Index, redBall.IsSelected)); }); } }
这里在binding的过程中,当ball control被选中的时候, 颜色会自动进行转换,所以需要一个Converter。
public class BooleanToColor : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (targetType != typeof(Brush)) throw new InvalidOperationException("The target must be a Brush"); if (value == null) return Brushes.Gray; return (bool)value ? Brushes.Red : Brushes.Gray; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException(); } }
方法二:在前台Xaml里面进行binding
(待续)