首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Silverlight数据绑定/IValueConverter学习笔记

Silverlight数据绑定/IValueConverter学习笔记

作者头像
菩提树下的杨过
发布2018-01-23 11:58:37
8750
发布2018-01-23 11:58:37
举报

先回忆一下aspx中的处理:

在aspx中,可以直接在后台定义一个变量,然后前台就可以用<%=xxx%>来将其"绑定"html控件上,比如下面这样,实在是很方便:

using System;

namespace WebApplication1
{
 public partial class _Default : System.Web.UI.Page
    {
 protected string _Test = DateTime.Now.ToString();

 protected void Page_Load(object sender, EventArgs e)
        {

        }
    }
}

代码

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>

<!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;
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;      
        }
 
    }   
}
 <StackPanel> 
 <TextBox x:Name="textBox1" Text="{Binding Test}"/> 
 </StackPanel>

这样就完成了功能最简单的绑定,还想玩得更深入一点,比如实现OneWay,TwoWay方式的绑定(不清楚绑定模式的朋友,建议先参看https://cloud.tencent.com/developer/article/1027125),这样仍然不行,比如我们稍微把刚才的代码改一下:

"自动更新"的绑定:

代码

<UserControl
 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;
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 class MyClass:INotifyPropertyChanged {
 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 的内置实现。可以直接拿来用:

代码

<UserControl
 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.Collections.ObjectModel;
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;
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();
        }
    }
}
<UserControl
 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;
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();
        }
    }
 
}
<UserControl
 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程序集,以后需要用到的地方,直接引用就可以了,能有效的重用代码。

转载请注明来自菩提树下的杨过

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2009-12-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档