先回忆一下aspx中的处理:
在aspx中,可以直接在后台定义一个变量,然后前台就可以用<%=xxx%>来将其"绑定"html控件上,比如下面这样,实在是很方便:
namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
protected string _Test = DateTime.Now.ToString();
protected void Page_Load( object sender, EventArgs e)
{
}
}
}
<! DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Transitional//EN " " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd " >
< html xmlns = " http://www.w3.org/1999/xhtml " >
< head runat = " server " >
< title ></ title >
</ head >
< body >
< form id = " form1 " runat = " server " >
< input id = " Text1 " type = " text " value = " <%=_Test %> " />
</ form >
</ body >
</ html >
但到了Silverlight中,要想直接将后台的变量绑定到某个控件上却是行不通的,通常我们得先定义一个类,然后在类里定义属性,才能把类实例的属性绑定到控件:
简单绑定:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Shapes;
namespace BindVariable
{
public partial class MainPage : UserControl
{
public class MyClass { public string Test { set ; get ; } }
public MyClass TestClass;
public MainPage()
{
InitializeComponent();
TestClass = new MyClass();
TestClass.Test = " 123 " ;
this .textBox1.DataContext = TestClass;
}
}
}
< TextBox x:Name ="textBox1" Text =" {Binding Test} " />
</ StackPanel >
这样就完成了功能最简单的绑定,还想玩得更深入一点,比如实现OneWay,TwoWay方式的绑定(不清楚绑定模式的朋友,建议先参看http://www.cnblogs.com/yjmyzz/archive/2009/11/09/1599058.html),这样仍然不行,比如我们稍微把刚才的代码改一下:
"自动更新"的绑定:
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d ="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc ="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class ="BindVariable.MainPage"
>
< StackPanel Orientation ="Vertical" >
< TextBox x:Name ="textBox1" Text =" {Binding Test} " />
< Button x:Name ="btnChange" Content ="Change Test" Click ="btnChange_Click" ></ Button >
</ StackPanel >
</ UserControl >
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Shapes;
namespace BindVariable
{
public partial class MainPage : UserControl
{
public class MyClass { public string Test { set ; get ; } }
public MyClass TestClass;
public MainPage()
{
InitializeComponent();
TestClass = new MyClass();
TestClass.Test = " 123 " ;
this .textBox1.DataContext = TestClass;
}
private void btnChange_Click( object sender, RoutedEventArgs e)
{
this .TestClass.Test = " 456 " ;
}
}
}
运行后,点击按钮,发现textbox1中的内容并无变化,原因是:要想实现源与目标的数据自动关联更新,MyClass得实现INotifyPropertyChanged接口,我们把MyClass的定义改成下面这样:
public event PropertyChangedEventHandler PropertyChanged;
private string _test;
public string Test
{
set {
_test = value;
if (PropertyChanged != null )
{
PropertyChanged( this , new PropertyChangedEventArgs( " Test " ));
}
}
get { return _test; }
}
}
再次运行,发现点击按钮后,textbox1的内容变成了456,达到预期的效果了.
绑定集合(数据集):
很多应用场合中,数据来源不仅只有一个实例(或一条记录)--比如从数据库中检索的记录,这时如果想绑定数据并实现自动更新,应使用集合绑定(类似于aspx中的DataSet或DataTable)。要注意的是,使用集合绑定并实现自动更新,除了要实现 INotifyPropertyChanged 外,还要实现 INotifyCollectionChanged。幸好.net框架已经有一个ObservableCollection<T> 类,该类具有 INotifyCollectionChanged 和 INotifyPropertyChanged 的内置实现。可以直接拿来用:
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d ="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc ="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class ="BindVariable.MainPage"
xmlns:local ="clr-namespace:BindVariable"
>
< StackPanel Orientation ="Vertical" >
< ListBox x:Name ="lst" >
< ListBox.ItemTemplate >
< DataTemplate >
< StackPanel >
< TextBlock Text =" {Binding Test} " ></ TextBlock >
</ StackPanel >
</ DataTemplate >
</ ListBox.ItemTemplate >
</ ListBox >
< Button x:Name ="btnChange" Content ="Change Test" Click ="btnChange_Click" ></ Button >
</ StackPanel >
</ UserControl >
using System.Windows;
using System.Windows.Controls;
namespace BindVariable
{
public partial class MainPage : UserControl
{
public class MyClass { public string Test { set ; get ; } }
ObservableCollection < MyClass > oc = new ObservableCollection < MyClass > (){
new MyClass() { Test = " 1 " },
new MyClass() { Test = " 2 " },
new MyClass() { Test = " 3 " }
};
public MainPage()
{
InitializeComponent();
this .lst.ItemsSource = oc;
}
private void btnChange_Click( object sender, RoutedEventArgs e)
{
oc.Add( new MyClass() { Test = " 4 " });
}
}
}
IValueConverter:
上述的绑定,都是将数据原封不动的绑定并显示,如果我们希望在绑定时,能对数据的输出做一些变化,比如:代表性别的"1,0"输出时希望变成"男,女",该怎么办呢?(silverlight中可不允许象aspx那样用<%# Eval("Sex").ToString()=="1"?"男":"女"%>来搞定) 答案:IValueConverter
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace BindVariable
{
public partial class MainPage : UserControl
{
public class MyClass
{
public string Test { set ; get ; }
public bool Sex { set ; get ; }
}
ObservableCollection < MyClass > oc = new ObservableCollection < MyClass > (){
new MyClass() { Test = " 1 " ,Sex = true },
new MyClass() { Test = " 2 " ,Sex = false },
new MyClass() { Test = " 3 " ,Sex = true }
};
public MainPage()
{
InitializeComponent();
this .lst.ItemsSource = oc;
}
private void btnChange_Click( object sender, RoutedEventArgs e)
{
oc.Add( new MyClass() { Test = " 4 " , Sex = false });
}
}
/// <summary>
/// bool转化为性别字符串
/// </summary>
public class BoolToSexConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
return (( bool )value) ? " 男 " : " 女 " ;
}
catch
{
return " ? " ;
}
}
// 只有TwoWay模式下,才需要实现该方法,否则可以不用理
public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d ="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc ="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class ="BindVariable.MainPage"
xmlns:local ="clr-namespace:BindVariable"
>
< StackPanel Orientation ="Vertical" >
< StackPanel.Resources >
< local:BoolToSexConverter x:Key ="Bool2Sex" ></ local:BoolToSexConverter >
</ StackPanel.Resources >
< ListBox x:Name ="lst" >
< ListBox.ItemTemplate >
< DataTemplate >
< StackPanel Orientation ="Horizontal" >
< TextBlock Text =" {Binding Test} " ></ TextBlock >
< TextBlock Text ="," ></ TextBlock >
< TextBlock Text =" {Binding Path=Sex, Converter={StaticResource Bool2Sex}} " ></ TextBlock >
</ StackPanel >
</ DataTemplate >
</ ListBox.ItemTemplate >
</ ListBox >
< Button x:Name ="btnChange" Content ="Change Test" Click ="btnChange_Click" ></ Button >
</ StackPanel >
</ UserControl >
也许您注意到了IValueConverter的Convert方法中,还能传入参数!我们可以利用这个玩点小花样,比如界面上有三个矩形,其中"矩形2的宽度"等于"矩形1的宽度"+"一个任意指定的固定值",矩形3的宽度矩形1与矩形2的宽度总和,不允用 rect2.width = rect1.width,rect3.width = rect2.width + rect1.width 来处理:) 您会怎么做呢?
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace BindVariable
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
}
public class DoubleAddConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
return double .Parse(value.ToString()) + double .Parse(parameter.ToString());
}
catch
{
return 50.0 ;
}
}
// 只有TwoWay模式下,才需要实现该方法,否则可以不用理
public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class FrameworkElementAddConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
return double .Parse(value.ToString()) + ((App.Current.RootVisual as FrameworkElement).FindName(parameter.ToString()) as FrameworkElement).Width;
}
catch
{
return 50.0 ;
}
}
// 只有TwoWay模式下,才需要实现该方法,否则可以不用理
public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d ="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc ="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class ="BindVariable.MainPage"
xmlns:local ="clr-namespace:BindVariable"
>
< StackPanel Orientation ="Vertical" HorizontalAlignment ="Left" >
< StackPanel.Resources >
< local:DoubleAddConverter x:Key ="DoubleAdd" ></ local:DoubleAddConverter >
< local:FrameworkElementAddConverter x:Key ="FEAdd" ></ local:FrameworkElementAddConverter >
</ StackPanel.Resources >
< Rectangle x:Name ="rect1" Width ="100" Height ="50" Fill ="Gray" HorizontalAlignment ="Left" ></ Rectangle >
< Rectangle x:Name ="rect2" Height ="50" Width =" {Binding Width, Converter={StaticResource DoubleAdd}, ConverterParameter=100, ElementName=rect1, Mode=OneWay} " Fill ="Red" HorizontalAlignment ="Left" ></ Rectangle >
< Rectangle x:Name ="rect3" Height ="50" Fill ="Blue" Width =" {Binding Width, Converter={StaticResource FEAdd}, ConverterParameter=rect2, ElementName=rect1, Mode=OneWay} " ></ Rectangle >
</ StackPanel >
</ UserControl >
这样就搞定了,也许有人会问:为啥不用 rect2.width = rect1.width,rect3.width = rect2.width + rect1.width 呢?不是更简单吗?
存在即合理,这样的好处是不必用硬编码把逻辑写死,我们可以把常用的转换处理抽象出来,比如封装成一个单纯的dll程序集,以后需要用到的地方,直接引用就可以了,能有效的重用代码。
转自 http://www.cnblogs.com/yjmyzz/archive/2009/12/07/silverlight-binding.html