文档章节

【万里征程——Windows App开发】数据绑定——简单示例、更改通知、数据转换

NoMasp
 NoMasp
发布于 2015/09/08 21:48
字数 3341
阅读 0
收藏 0

简单的数据绑定示例

相比于理论,我更倾向于从实践中开始博客,尤其是对于数据绑定。那么,我们先来看看几个简单的例子。

1.数据绑定到TextBox

我们依旧使用前面的闹钟类来开始。在下面的代码中,我们有属性、构造函数,还有一个ToString()方法的重载。之所以重载这个方法是因为我们想在最后绑定的时候,这三个属性能够在TextBox上显示得更加工整。

    public class Alarm
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public DateTime AlarmTime { get; set; }
        public Alarm() { }
        public Alarm(string title, string description,DateTime alarmTime)
        {
            Title = title;                  
            Description = description;
            AlarmTime = alarmTime;
        }
        public override string ToString()
        {
            return "Title: " + Title +"\n"+ "Time: "+ AlarmTime.ToString("d") + "\n"+ "Description: " + Description;
        }
    }

接下来再在XAML中添加TextBox控件如下,因为TextBox此时是用作显示而非输入,所以建议设置其的只读属性。数据绑定的核心就是Text属性中的那么一个Binding关键字。

<TextBox x:Name="textBox1" FontSize="28" Height="150" Width="400"
                    TextWrapping="Wrap" Text="{Binding}" IsReadOnly="True"/>

但是光这样还不够,我们还需要在后台代码中将数据绑定到textBox1的DataContext(数据上下文)中。

textBox1.DataContext = new Alarm(
                "First Alarm", "I need to study!", new DateTime(2015, 4, 11));

相信大家并不为觉得这个很难,相反我在学数据绑定的时候一上来就是一大堆理论,以至于我对数据一词有了阴影——所以我学数据结构非常痛苦。

这里写图片描述

2.数据绑定到ComboBox

才保存一个闹钟没太大意思,我们多来几个。

        public ObservableCollection<Alarm> UsefulAlarm = new ObservableCollection<Alarm>();
        public MainPage()
        {
            this.InitializeComponent();

            UsefulAlarm.Add(new Alarm("First Alarm", "I need to study!", new DateTime(2015, 4, 11)));
            UsefulAlarm.Add(new Alarm("First Alarm", "Read a magzine!", new DateTime(2015, 4, 12)));
            UsefulAlarm.Add(new Alarm("First Alarm", "Write a blog!", new DateTime(2015, 4, 15)));
            UsefulAlarm.Add(new Alarm("First Alarm", "Travel", new DateTime(2015, 5, 15)));

            textBox1.DataContext = UsefulAlarm;
        }

但是……

这里写图片描述

很显然我们用了ObservableCollection< T >类,它为数据绑定提供了一个集合,这是因为它实现了INotifyPropertyChanged和INotifyCollectionChanged接口。顾名思义,当属性改变时,它可以通知它所绑定的控件,并且如果你希望该空间能够同步更新,则将用于绑定的对象也实现INotifyPropertyChanged接口。这个类好归好,但相对于TextBox而言算有些高端了,以至于它无法显示出来。但是我们可以用ComboBox来代替它,我们的类并不需要修改,前面的UsefulAlarm实例化也都不用改,只需要将textBox1改成comboBox1即可。以下是新的ComboBox代码。

       <ComboBox Name="comboBox1" ItemsSource="{Binding}" FontSize="28" Height="150" Width="400">
            <ComboBox.ItemTemplate>
                <DataTemplate>               
                    <StackPanel Orientation="Vertical" Margin="8">
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding Title}" IsReadOnly="True"/>
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding Description}" IsReadOnly="True"/>
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding AlarmTime}" IsReadOnly="True"/>
                    </StackPanel>                    
                </DataTemplate>
            </ComboBox.ItemTemplate>     
        </ComboBox>

在图示中我们也容易发现TextBox和ComboBox两个控件的Width属性的应用区别。在TextBox中,我们将数据绑定到Text中;而在ComboBox中,我们则是将数据绑定到ItemsSource中,简单的说就是ComboBox拿来所有的数据,再将它们分成小的细节发给它的子对象,这些子对象都在ComboBox的DataTemplate(数据容器)中。

这里写图片描述

在这里我们并没有用到前面所重载的ToString()函数,因为我们已经分别将Title、Description、AlarmTime绑定到相应的TextBox控件了。那图示中又为什么这些数据都是一行一行的表示呢,这都是布局控件StackPanel的功劳,全靠它的Orientation属性。如果将这个属性设置成Horizontal呢,那标题、描述已经时间就是全排在一行了。

这里写图片描述

3.数据绑定到ListBox

听说ListBox和ComboBox很类似哦,它们都是Box……XBox呀。博主我有点懒,那可不可以直接将ComboBox的名字改成ListBox就直接运行呢,答案是可以哦!那么区别到底在哪里呢?看看这张图就知道啦。

这里写图片描述

咦?怎么只有一条闹钟了?别惊慌……拖动右边的滚动条就可以查看到全部的闹钟咯。我真的只把ComboBox改成ListBox还有相应的Name属性(包括后台代码中的名字哦),以下就是完整的代码啦,我会骗你?

        <ListBox Name="listBox1" ItemsSource="{Binding}" FontSize="28" Height="150" Width="400">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical" Margin="8">
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding Title}" IsReadOnly="True"/>
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding Description}" IsReadOnly="True"/>
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding AlarmTime}" IsReadOnly="True"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

4.数据绑定到ListView

看了前面的代码相信我没有骗你吧,童鞋们看到ListBox有没有想到ListView呢?我要是想说还是和前面一样只用改名字等就可以用ListView,你还是不信么?

        <ListView Name="listView1" ItemsSource="{Binding}" FontSize="28" Height="150" Width="400">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical" Margin="8">
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding Title}" IsReadOnly="True"/>
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding Description}" IsReadOnly="True"/>
                        <TextBox Width="350" TextWrapping="Wrap" Text="{Binding AlarmTime}" IsReadOnly="True"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

这里写图片描述

当然了,还是用右边的滚动条来下拉以查看所有的数据。不过ListView君的最佳姿势不是这样哦,将Height改为600才是呢。看下图——这才是高大上的ListView君嘛!

这里写图片描述

好了不玩了,GridView也是可以这样弄得,不信你试试。

再谈数据绑定

1.我们为什么要用数据绑定

很显然,我们不可能把所有的数据全部固定在特定的控件上。比如,游戏的积分、设定的闹钟、天气预报甚至的通讯类的消息,它们都并非是一成不变的。但是也并非所有的控件都需要绑定,比如你的App的名字、发送消息时所用的发送按钮上面的文本等。

2.那数据和UI之间又有哪些关系呢

首先我们得明确,数据的显示和其后台的管理是不一样的。数据与UI绑定之后,我们的数据就可以在这两者之间进行沟通,如果数据发生变化时,绑定到数据的UI则会自动将相应的属性进行调整,不仅仅是前面用到的Text属性,还有FontSize、Width、Foreground、Image属性都可以。

3.数据绑定到底是绑定什么

首先,我们得有绑定源,这些就是我们需要绑定的数据,没有数据,即使你绑定了,它也显示不出来。
其次,我们还需要绑定目标,也就是Framework类的DependencyProperty属性,说得白话文点就是将数据绑定到UI的相应属性上。
最后,我们还需要一个Binding对象,它就像是搬运工,没有它,数据也是无法动弹的。它能够帮助我们将数据从数据源移动到绑定目标,并且将绑定目标的相应消息通知给绑定源。它还有一些巧妙的工具,能够将绑定源的数据加工成特定的格式。

4.绑定源有哪些

所有的公共语言运行时对象,我们前面用的Alarm类就是这种对象,另外UI元素也是哦。

5.听说有的搬运工只能将数据源的数据一次性搬到绑定目标后就不再搬了,而有的搬运工则会在数据修改后再搬一次,甚至还有的能够在绑定目标更改后再将数据搬回到数据源

OneTime绑定:这个搬运工的工作就是第一种,它只负责在创建时将源数据更新到绑定目标。
OneWay绑定:这是系统默认的搬运工,它是第二种,负责在创建时以及源数据发生更改时更新绑定目标。
TwoWay绑定:这个搬运工则是第三种,它能够在绑定源和绑定目标的一边发生更改时同时更新绑定源和绑定目标。但它在一种时候却会偷懒,那就是对于TextBox.Text每次点击之后,它就不会将这个Text属性的更改更新到绑定源。不过如果碰到Boss,它也只能继续搬了。那就是将Binding.UpdateSourceTrigger设置成PropertyChanged。而默认情况下,只有TextBox失去焦点时才会去更新。

以下分别是OneWay和TwoWay的例子:

        <StackPanel Width="240" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <Slider Name="slider1" Minimum="0" Maximum="100"/>
            <TextBox FontSize="30" Text="{Binding ElementName=slider1,Path=Value,Mode=OneWay}" />
        </StackPanel>

拖动滑动条,就可以看到在TextBox中显示它的值的变化了。如果希望它只变化一次,那就将代码中的OneWay改成OneTime即可。

这里写图片描述

        <StackPanel Width="240" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBox FontSize="30" Name="textBox" Height="60" Text ="{Binding ElementName=listBox1, Path=SelectedItem.Content, Mode=TwoWay}">
            </TextBox>   
            <ListBox FontSize="30" Name="listBox1">
                <ListBoxItem Content="Item 1"/>
                <ListBoxItem Content="Item 2"/>
                <ListBoxItem Content="Item 3"/>
                <ListBoxItem Content="Item 4"/>
            </ListBox>
        </StackPanel>

如下图所示,点击Item 1后TextBox则会显示相应的Item 1,将TextBox中的Item 1修改为Item 5后再ListBox中也自动修改成了Item5。

这里写图片描述

这里写图片描述

简单示例:Foreground的数据绑定

前面已经说到了Foreground也可以绑定,想不想试试呢。我们现在TextBox中写一个TextBox,然后在后台代码中添加一个绑定就可以了。这个和前面的比较简单,这里只是用来引出后面的东东哦

 <TextBox Name="textBox" Width="200" Height="100" IsReadOnly="True"
                 FontSize="32" Text="Text" Foreground="{Binding ForeBrush}"/>
textBox.Foreground = new SolidColorBrush(Colors.BlueViolet);

更改通知

1.Silder绑定到TextBlock,不使用更改通知

首先定义一个简单的类BindingSlider,同时在XAML中作如下定义。

    public class BindingSlider
    {
        private int sliderValue;
        public int SliderValue
        {
            get
            {
                return sliderValue;
            }
            set
            {
                sliderValue = value;
            }
        }
    }
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
    <Slider Name="slider1" Minimum="0" Maximum="100" Width="200" Value="{Binding SliderValue,Mode=TwoWay}"/>
    <Button x:Name="button" Content="Button" Width="200" Click="button_Click"/>
    <TextBlock Name="textBlock" FontSize="30"/>
</StackPanel>

虽然这里只是用到了OneWay传递,但还是需要使用TwoWay。因为在这里OneWay是指从BindingSlider类的SliderValue属性单向传递到Slider控件的Value属性。但我们需要的则是Slider控件的Value属性单向传递到BindingSlider类的SliderValue属性,所以才得使用TwoWay方式。

        BindingSlider bindingSlider = new BindingSlider();
        public MainPage()
        {
            this.InitializeComponent();
            slider1.DataContext = bindingSlider;
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            textBlock.Text = bindingSlider.SliderValue.ToString();
        }

首先实例化BindingSlider类,再在后台代码中奖bindingSlider对象绑定到slider1的数据上下文。最后通过Click事件来将bindingSlider对象的SliderValue属性传递给textBlock控件的Text属性。

这里的效果就是,拖动Slider但是TextBlock不会有变化,而需要Button来不断的更改TextBlock的Text。如果想要TextBlock的Text能够根据Slider实时的更改,这就需要”更改通知“了。

2.Silder绑定到TextBlock,使用更改通知

既然要使用通知更改的技术,那就可以在XAML代码中将Button控件删除掉了,包括后台代码中的Click事件。

紧接着来修改BindingSlider类,首先得使用INotifyPropertyChanged接口。这个接口有PropertyChanged事件,而这个事件则会告知绑定目标绑定源已经发生修改,这样绑定目标也会实时的进行更改。在新的set中,我们将SliderValue值传递到NotifyPropertyChanged中。

    public class BindingSlider :INotifyPropertyChanged
    {
        private int sliderValue;
        public int SliderValue
        {
            get
            {
                return sliderValue;
            }
            set
            {
                sliderValue = value;
                NotifyPropertyChanged("SliderValue");     
            }
        }                                                                           
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }
    }

最后我们还需要将bindingSlider对象绑定到textBlock的数据上下文。

        BindingSlider bindingSlider = new BindingSlider();
        public MainPage()
        {
            this.InitializeComponent();
            slider1.DataContext = bindingSlider;
            textBlock.DataContext = bindingSlider;           
        }

这样一来就全部更改完成了,试试就会发现TextBlock的Text会根据Slider的拖动而实时修改了。

数据转换

有时候默认的输出方式不能满足我们的需要,比如前面的OneWay示例,可能我们需要的是在TextBox中显示“开始加载“、”加载一半了“、”很快就加载完了“以及”已经加载好“等,甚至还可以让其能够转换成英文哦。

那么首先新建一个类SliderValueConverter.cs,然后实现IValueConverter接口。然后按自己的需要写它的Converter方法即可。

 public class SliderNotifyAndConverter : IValueConverter      
 {
       public object Convert(object value, Type targetType, object parameter, string language)
        {
            string valueTextBlock;
            string parameterValue = parameter.ToString();
            double valueSlider = (double)value;
            if (valueSlider > 0&&valueSlider<=5)
            {
                if (parameterValue == "zh-cn")
                    valueTextBlock = "开始加载";
                else
                    valueTextBlock = "Starts to load";
            }
            else if (valueSlider >= 45 && valueSlider <= 55)
            {
                if (parameterValue == "zh-cn")
                    valueTextBlock = "加载一半了";
                else
                    valueTextBlock = "loaded half";
            }
            else if (valueSlider >= 90&&valueSlider<100)
            {
                if (parameterValue == "zh-cn")
                    valueTextBlock = " 很快就加载完了";
                else
                    valueTextBlock = "finished loading very quickly";
            }
            else if (valueSlider == 100)
            {
                if (parameterValue == "zh-cn")
                    valueTextBlock = " 已经加载好";
                else
                    valueTextBlock = "loaded";
            }
            else
            {
                if (parameterValue == "zh-cn")
                    valueTextBlock = "加载中";
                else
                    valueTextBlock = "Loading";
            }
            return valueTextBlock;
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
 }           

最后还需要在XAML中添加如下代码哦,值转换器Converter所使用的静态资源已经在

    <Page.Resources>
        <local:SliderNotifyAndConverter x:Key="SliderNotifyAndConverterResources"/>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Name="stackPanel" Width="450" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <Slider Name="slider1" Minimum="0" Maximum="100" Value="95"/>
            <TextBlock FontSize="30" Text="{Binding ElementName=slider1, Path=Value, Converter={StaticResource SliderNotifyAndConverterResources}, ConverterParameter='zh-cn'}"/>
            <TextBlock FontSize="30" Text="{Binding ElementName=slider1, Path=Value, Converter={StaticResource SliderNotifyAndConverterResources}, ConverterParameter='en-us'}"/>
        </StackPanel>
    </Grid>

以下是Slider的Value取不同值时TextBlock的不同显示。

这里写图片描述

这里写图片描述

终于一口气把自我感觉最难的数据绑定部分给写完了,但愿写的还算清晰,欢迎指正。


欢迎大家点击左上角的“关注”或右上角的“收藏”方便以后阅读。


为使本文得到斧正和提问,转载请注明出处:
http://blog.csdn.net/nomasp

版权声明:本文为 NoMasp柯于旺 原创文章,未经许可严禁转载!欢迎访问我的博客:http://blog.csdn.net/nomasp

本文转载自:http://blog.csdn.net/nomasp/article/details/44984579

NoMasp
粉丝 7
博文 334
码字总数 0
作品 0
镇江
程序员
私信 提问
nomasp 博客导读:Lisp/Emacs、Algorithm、Android

版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/44966625 Profile Introduction to Blog 您能看到这篇博客导读...

nomasp
2015/09/17
0
0
用WijmoJS玩转您的Web应用 —— Ionic

前言: 在本文中,我们将着重介绍如何将WijmoJS与Ionic一起使用,来创建一款移动端支持优先、快捷高效的应用程序。在之前的文章中,我们已经介绍了使用WijmoJS与Angular、React、Vue三大框架...

葡萄城控件技术团队
2018/06/26
28
0
微信小程序之菜鸟选手入门教学(一)

1. 小程序的优缺点 优点:不需下载、卸载 使用方便 缺点:嵌套在微信中,属于微信的子应用 2. 使用的技术 实际上是Hybrid技术的应用 Hybrid App (混合模式移动应用),使用网页语言和程序语言...

爱吃鱼的考拉
2019/04/21
0
0
Node.js开发入门—UDP编程

Node.js也提供了UDP编程的能力,相关类库在“dgram”模块里。 与TCP不同,UDP是无连接的,不保障数据的可靠性,不过它的编程更为简单,有时候我们也需要它。比如做APP的统计或者日志或者流媒...

foruok
2015/09/19
0
0
Node.js开发入门—Angular简单示例

在“使用AngularJS”中,我们提到了如何在Node.js项目中引入AngularJS,这次提供一个非常简单的示例,演示AngularJS里的指令、数据绑定、服务等内容。 我准备做Web后台管理系统,不同的管理员...

foruok
2015/08/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

MBTI助你成功,让你更了解你自己

MBTI助你成功,让你更了解你自己 生活总是一个七日接着又一个七日,相信看过第七日的小伙伴,很熟悉这段开场白,人生是一个测试接着又一个测试,上学的时候测试,是为了证明你的智力,可谓从...

蛤蟆丸子
今天
55
0
Android实现App版本自动更新

现在很多的App中都会有一个检查版本的功能。例如斗鱼TV App的设置界面下: 当我们点击检查更新的时候,就会向服务器发起版本检测的请求。一般的处理方式是:服务器返回的App版本与当前手机安...

shzwork
昨天
63
0
npm 发布webpack插件 webpack-html-cdn-plugin

初始化一个项目 npm init 切换到npm源 淘宝 npm config set registry https://registry.npm.taobao.org npm npm config set registry http://registry.npmjs.org 登录 npm login 登录状态......

阿豪boy
昨天
87
0
java基础(16)递归

一.说明 递归:方法内调用自己 public static void run1(){ //递归 run1(); } 二.入门: 三.执行流程: 四.无限循环:经常用 无限递归不要轻易使用,无限递归的终点是:栈内存溢出错误 五.递...

煌sir
昨天
63
0
REST接口设计规范总结

URI格式规范 URI中尽量使用连字符”-“代替下划线”_”的使用 URI中统一使用小写字母 URI中不要包含文件(脚本)的扩展名 URI命名规范 文档(Document)类型的资源用名词(短语)单数命名 集合(Co...

Treize
昨天
69
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部