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

简介:

简单的数据绑定示例

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

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

目录
相关文章
|
2月前
|
人工智能 文字识别 小程序
旅游社用什么工具收报名 + 资料?不开发 App 也能自动收集信息
本文探讨了旅游行业中报名信息收集的常见痛点及解决方案,重点介绍了二维码表单工具在提升信息收集效率、简化操作流程方面的优势。通过对比多种工具,分析其适用场景与实际应用逻辑,为一线旅游从业者提供高效、低成本的执行参考。
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:外卖App
仓颉语言实战分享,教你如何用仓颉开发外卖App界面。内容包括页面布局、导航栏自定义、搜索框实现、列表模块构建等,附完整代码示例。轻松掌握Scroll、List等组件使用技巧,提升HarmonyOS应用开发能力。
|
3月前
《仿盒马》app开发技术分享-- 确认订单页(数据展示)(29)
上一节我们实现了地址的添加,那么有了地址之后我们接下来的重点就可以放到订单生成上了,我们在购物车页面,点击结算会跳转到一个 订单确认页面,在这个页面我们需要有地址选择、加购列表展示、价格计算、优惠计算、商品数量展示等信息。
95 3
|
3月前
|
IDE 开发工具 开发者
使用DevEcoStudio 开发、编译鸿蒙 NEXT_APP 以及使用中文插件
# 使用DevEcoStudio 开发、编译鸿蒙 NEXT_APP 以及使用中文插件 #鸿蒙开发工具 #DevEco Studio
268 1
|
3月前
|
人工智能 小程序 JavaScript
【一步步开发AI运动APP】十、微调优化内置运动分析器,灵活适配不同的应用场景
本文介绍了如何通过【一步步开发AI运动APP】系列博文,利用`ISportOptimizer`对内置运动分析器进行微调优化。相比小程序,APP框架(如uni-app)因技术差异,无法直接修改运动分析器参数,因此提供了统一的优化机制。开发者可通过`ISportOptimizer`获取和更新检测规则、动作样本等,灵活适应不同场景需求,如全民运动赛事的宽松模式或学生体测的严格模式。文中还提供了示例代码,展示如何对具体运动项目(如仰卧起坐)进行优化。需要注意的是,微调优化适用于标准动作的小范围调整,若动作变化过大(如花式跳绳),可期待后续自定义扩展功能。
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:健身App
本期分享一个健身App首页的布局实现,顶部采用Stack容器实现重叠背景与偏移效果,列表部分使用List结合Scroll实现可滚动内容。代码结构清晰,适合学习HarmonyOS布局技巧。
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:银行App
仓颉语言银行App项目分享,页面布局采用List容器,实现沉浸式体验与模块化设计。顶部资产模块结合Stack与Row布局,背景图与内容分离,代码清晰易懂;功能按钮部分通过负边距实现上移效果,圆角仅保留顶部;热门推荐使用header组件,结构更规范。整体代码风格与ArkTS相似,但细节更灵活,适合金融类应用开发。
|
5月前
|
人工智能 JSON 小程序
【一步步开发AI运动APP】七、自定义姿态动作识别检测——之规则配置检测
本文介绍了如何通过【一步步开发AI运动APP】系列博文,利用自定义姿态识别检测技术开发高性能的AI运动应用。核心内容包括:1) 自定义姿态识别检测,满足人像入镜、动作开始/停止等需求;2) Pose-Calc引擎详解,支持角度匹配、逻辑运算等多种人体分析规则;3) 姿态检测规则编写与执行方法;4) 完整示例展示左右手平举姿态检测。通过这些技术,开发者可轻松实现定制化运动分析功能。
|
4月前
|
存储 消息中间件 前端开发
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
校园圈子系统校园论坛小程序采用uni-app前端框架,支持多端运行,结合PHP后端(如ThinkPHP/Laravel),实现用户认证、社交关系管理、动态发布与实时聊天功能。前端通过组件化开发和uni.request与后端交互,后端提供RESTful API处理业务逻辑并存储数据于MySQL。同时引入Redis缓存热点数据,RabbitMQ处理异步任务,优化系统性能。核心功能包括JWT身份验证、好友系统、WebSocket实时聊天及活动管理,确保高效稳定的用户体验。
233 4
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
|
3月前
|
安全 新制造
不用开发APP,也能实现设备巡检自动提醒和记录归档
设备巡检新方案!告别纸质表格,用二维码轻松搞定自动提醒和记录归档。某工厂分享如何通过草料二维码实现一物一码、扫码填写巡检信息,自动生成数据归档,还能设置定期提醒,确保巡检任务按时完成。无需额外开发 App,低成本高效管理,为设备巡检提供简单又实用的解决方案。

热门文章

最新文章