WPF数据绑定

这篇具有很好参考价值的文章主要介绍了WPF数据绑定。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据绑定是一种历经时间考验的传统方式,做法是从对象中提取信息,并在应用程序的用户界面中显示提取的信息,不用编写枯燥的代码就可以完成所有工作。富客户端通常使用双向绑定,这种数据绑定提供了从用户界面向一些对象推出信息的能力——同样,不需要或者几乎不需要编写代码。

构建数据对象

数据对象时准备在用户界面中显示的信息包,只要由公有属性组成(不支持字段和私有属性),任何类都可供使用。此外,如果希望使用这个对象进行修改(通过双向绑定),那么属性不能是只读的。

public class NormalClass
{
    public string BindData { get; set; } = "BindData";
}

绑定到对象

需要创建一个数据对象的实例,设置好DataContext,然后根据路径绑定到对应的属性

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        parentContainer.DataContext = this;
    }

    public NormalClass normalClass { get; set; } = new NormalClass();
}
<TextBox DataContext="{Binding Path=normalClass}" Text="{Binding Path=BindData, Mode=TwoWay}"/>

更改通知

普通的属性在发生变化时,并不会通知绑定到它的目标,可使用如下三种方法解决这个问题:

1、将属性改为依赖项属性。尽管这种方法可使WPF自动执行相应的工作,单最合理的做法是将其用于控件元素——在窗口中具有可视化外观的类。对于数据类,这并非最自然的方法。

2、可为每个属性引发事件。对于这种情况,事件必须由 PropertyNameChanged 的形式进行命名(如BindDataChanged)。当属性变化时,由您负责引发事件。

3、可实现 INotifyPropertyChanged 接口,该接口需要名为 PropertyChanged 的事件。无论何时属性发生变化,都必须引发PropertyChanged 事件,并通过将属性名称作为字符串提供来指示那个属性发生了变化。当属性发生变化时,仍由您负责引发事件,但不必为每个属性定义单独的事件。

第一张方法依赖于WPF的依赖属性基础架构,而第二种和第三种依赖于事件。通常,当创建数据对象时,会使用第三种方法。对于非元素类而言,这是最简单的选择。

我这里创建了一个继承自 INotifyPropertyChanged 的基类ViewModelBase,其他的数据模型继承自ViewModelBase,在set属性设置器里面调用SetProperty 即可。

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(member, value))
        {
            return false;
        }
        member = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}
public class NotifyClass : ViewModelBase
{
    private string bindData = "BindData";
    public string BindData { get => bindData; set => SetProperty(ref bindData, value); }
}

绑定到集合

绑定到单个对象是非常直观的。但当需要绑定到对象的某些集合时,问题会变得更有趣。依赖项属性基本都支持单值绑定,但集合绑定需要智能程度更高的元素。在WPF中,所有派生自ItemsControl 的类都能显示条目的完整列表。能支持集合数据绑定的元素包括 ListBox、ComboBox、ListView 和 DataGrid。

为支持集合,ItemsControl 类定义了三个重要属性:

ItemsSource:指向的集合包含在列表中显示所有对象

DisplayMemberPath:确定用于为每个项创建显示文本的属性

ItemTemplate:接受的数据模板用于为每个子项创建可视化外观。这个属性比DisplayMemberPath属性的功能强大的多

绑定到集合的数据类型可以是任意类型的集合,唯一的要求是支持IEnumerable 接口,数组、各种类型的集合以及许多特殊的封装了数据项组的对象都支持该接口。然而,基本的IEnumerate 接口仅支持只读绑定,如果希望编辑集合(如插入和删除元素),就需要更复杂的基础结构。

<ListBox ItemsSource="{Binding Path=Exchanges}" />

上面的代码会成功应用ExchangeViewModel对象填充列表,因为列表不知道如何显示产品对象,所以只是调用ToString() 方法。因为ExchangeViewModel没有重写该方法,所以只为每个项显示完全限定的类名。可以通过三种方法来解决这个问题:

设置列表的 DisplayMemberPath 属性 例如将该属性设置为 ExchangeID,以显示ExchangeID 属性的值

重写ToString() 方法,返回更有用的信息 可为每个项返回包含ExchangeID,ExchangeName 拼接的信息,可以通过这种方法显示比列表中一个属性更多的信息,然而,仍不能对数据的显示进行更多控制

提供数据模板 可使用这种方法显示属性值的任何排列,在数据模板里面会展示这种方式

我这里分别用重写ToString()方法与设置DisplayMemberPath属性来解决。

<ListBox x:Name="exchangeListListBox1" ItemsSource="{Binding Path=ExchangeList}" />
<ListBox x:Name="exchangeListListBox2" ItemsSource="{Binding Path=ExchangeList}" DisplayMemberPath="ExchangeID"/>
public class ExchangeViewModel : ViewModelBase
{
    private string exchangeID = string.Empty;
    public string ExchangeID { get => exchangeID; set => SetProperty(ref exchangeID, value); }
    private string exchangeName = string.Empty;
    public string ExchangeName { get => exchangeName; set => SetProperty(ref exchangeName, value); }
    public ObservableCollection<InstrumentViewModel> Instruments { get; set; } = new ObservableCollection<InstrumentViewModel>();
    public override string ToString() => exchangeID + " - " + exchangeName;
}

这里的ExchangeList是List<T> 集合的对象:

public List<ExchangeViewModel> ExchangeList { get; set; } = new List<ExchangeViewModel>();

在我们添加或删除ExchangeList 集合的内容时,列表控件无法获取该修改。为启用集合更改的跟踪,需要使用实现了 INotifyCollectionChanged 接口的集合。大多数通用集合没有实现该接口,包括List集合。实际上,WPF提供了一个使用 INotifyCollectionChanged 接口的集合:ObserableCollection 类。

public ObservableCollection<ExchangeViewModel> ExchangeObservableCollection { get; set; } = new ObservableCollection<ExchangeViewModel>();
<ListBox x:Name="exchangeListListBox3" ItemsSource="{Binding Path=ExchangeObservableCollection}" />
<ListBox x:Name="exchangeListListBox4" ItemsSource="{Binding Path=ExchangeObservableCollection}" DisplayMemberPath="ExchangeID"/>

将集合改为 ObservableCollection 类型后,再删除集合中的元素,就能够实时反映到绑定至该集合的控件上面了。

将元素绑定与数据绑定结合起来

在填写表单时候,经常会遇到根据前面选定的项,自动调整后面选项的内容的情况。我们可以构建类似这样的数据模型:

public class InstrumentViewModel : ViewModelBase
{
    public InstrumentViewModel(string exchangeID, string instrumentID, string instrumentName)
    {
        ExchangeID = exchangeID;
        InstrumentID = instrumentID;
        InstrumentName = instrumentName;
    }

    private string _exchangeID = string.Empty;
    private string _instrumentID = string.Empty;
    private string _instrumentName = string.Empty;
    public string ExchangeID { get => _exchangeID; set => SetProperty(ref _exchangeID, value); }
    public string InstrumentID { get => _instrumentID; set => SetProperty(ref _instrumentID, value); }
    public string InstrumentName { get => _instrumentName; set => SetProperty(ref _instrumentName, value); }
}
public class ExchangeViewModel : ViewModelBase
{
    private string exchangeID = string.Empty;
    public string ExchangeID { get => exchangeID; set => SetProperty(ref exchangeID, value); }
    private string exchangeName = string.Empty;
    public string ExchangeName { get => exchangeName; set => SetProperty(ref exchangeName, value); }
    public ObservableCollection<InstrumentViewModel> Instruments { get; set; } = new ObservableCollection<InstrumentViewModel>();
    public override string ToString() => exchangeID + " - " + exchangeName;
}
public ObservableCollection<ExchangeViewModel> ExchangeObservableCollection { get; set; } = new ObservableCollection<ExchangeViewModel>();

然后在构建这样的控件元素:

<ListBox x:Name="exchangeListListBox4" ItemsSource="{Binding Path=ExchangeObservableCollection}" DisplayMemberPath="ExchangeID"/>
<ComboBox Name="instrumentCombbox" DataContext="{Binding ElementName=exchangeListListBox4, Path=SelectedItem}" ItemsSource="{Binding Path=Instruments}" DisplayMemberPath="InstrumentID"/>
<StackPanel Orientation="Vertical" DataContext="{Binding ElementName=instrumentCombbox, Path=SelectedItem}">
    <TextBlock Text="{Binding Path=ExchangeID, StringFormat=ExchangeID:{0}}"></TextBlock>
    <TextBlock Text="{Binding Path=InstrumentID, StringFormat=InstrumentID:{0}}"></TextBlock>
    <TextBlock Text="{Binding Path=InstrumentName, StringFormat=InstrumentName:{0}}"></TextBlock>
</StackPanel>

其中,ComboBox绑定到ListBox所选择的项,根据ListBox选择的项的Instruments属性来填充其子项,而下面的StackPanel 的 DataContext 又绑定到ComboBox选择的项,三个TextBlock控件展示ComboBox所选择项的各项属性。

完整代码如下:

MainWindow.xaml

<Window x:Class="TestBinding.MainWindow"
        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"
        xmlns:local="clr-namespace:TestBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:ExchangeViewModel x:Key="resourceExchange" ExchangeID="Test"/>
    </Window.Resources>
    <StackPanel Name="parentContainer">
        <StackPanel Orientation="Horizontal">
            <TextBlock>BindData:</TextBlock>
            <TextBox Text="{Binding Path=BindData}"></TextBox>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock>BindDatas:</TextBlock>
            <ListBox Name="exchangeListBox" ItemsSource="{Binding Path=Exchanges}" DisplayMemberPath="ExchangeID"></ListBox>
        </StackPanel>
        <StackPanel>
            <ComboBox Name="instrumentCombbox" DataContext="{Binding ElementName=exchangeListBox, Path=SelectedItem}" ItemsSource="{Binding Path=Instruments}" DisplayMemberPath="InstrumentID"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=instrumentCombbox, Path=SelectedItem}">
            <TextBlock>ExchangeID:</TextBlock>
            <TextBox Text="{Binding Path=ExchangeID}"></TextBox>
        </StackPanel>
        <StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=instrumentCombbox, Path=SelectedItem}">
            <TextBlock>InstrumentID:</TextBlock>
            <TextBox Text="{Binding Path=InstrumentID}"></TextBox>
        </StackPanel>
        <StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=instrumentCombbox, Path=SelectedItem}">
            <TextBlock>InstrumentName:</TextBlock>
            <TextBox Text="{Binding Path=InstrumentName}"></TextBox>
        </StackPanel>
        <StackPanel>
            <TextBlock Text="{Binding Source={StaticResource resourceExchange}, Path=ExchangeID}"/>
        </StackPanel>
        <StackPanel x:Name="BindingTextBlockStackPanel">
            <TextBlock x:Name="BindingSelfName" Text="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Name}"></TextBlock>
            <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Foreground, StringFormat={}{0}}"></TextBlock>
            <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=Name}"></TextBlock>
        </StackPanel>
        <StackPanel>
            <ItemsControl DataContext="{Binding Path=Exchanges}" ItemsSource="{Binding Path=Instruments}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" Margin="2">
                            <TextBlock Text="{Binding ExchangeID}" />
                            <TextBlock Text="{Binding Path=InstrumentID, RelativeSource={RelativeSource Mode=PreviousData}}" Foreground="Red" Margin="5,0,0,0"/>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
        <StackPanel >
            <Button Width="64" Height="64" Content="Ellipse" Background="OrangeRed">
                <Button.Template>
                    <ControlTemplate TargetType="Button">
                        <Grid>
                            <Ellipse Fill="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Background}"/>
                            <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        </Grid>
                    </ControlTemplate>
                </Button.Template>
            </Button>
        </StackPanel>
    </StackPanel>
</Window>

MainWindow.xaml.cs文章来源地址https://www.toymoban.com/news/detail-679535.html

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media.TextFormatting;

namespace TestBinding;

public class NormalClass
{
    public string BindData { get; set; } = "BindData";
}
public class NotifyClass : ViewModelBase
{
    private string bindData = "BindData";
    public string BindData { get => bindData; set => SetProperty(ref bindData, value); }
}
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(member, value))
        {
            return false;
        }
        member = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

public class InstrumentViewModel : ViewModelBase
{
    public InstrumentViewModel(string exchangeID, string instrumentID, string instrumentName)
    {
        ExchangeID = exchangeID;
        InstrumentID = instrumentID;
        InstrumentName = instrumentName;
    }

    private string _exchangeID = string.Empty;
    private string _instrumentID = string.Empty;
    private string _instrumentName = string.Empty;
    public string ExchangeID { get => _exchangeID; set => SetProperty(ref _exchangeID, value); }
    public string InstrumentID { get => _instrumentID; set => SetProperty(ref _instrumentID, value); }
    public string InstrumentName { get => _instrumentName; set => SetProperty(ref _instrumentName, value); }
}
public class ExchangeViewModel : ViewModelBase
{
    private string exchangeID = string.Empty;
    public string ExchangeID { get => exchangeID; set => SetProperty(ref exchangeID, value); }
    private string exchangeName = string.Empty;
    public string ExchangeName { get => exchangeName; set => SetProperty(ref exchangeName, value); }
    public ObservableCollection<InstrumentViewModel> Instruments { get; set; } = new ObservableCollection<InstrumentViewModel>();
    public override string ToString() => exchangeID + " - " + exchangeName;
}
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        InitExchanges();
        parentContainer.DataContext = this;
    }
    public void InitExchanges()
    {
        ExchangeViewModel exchange1 = new ExchangeViewModel();
        exchange1.ExchangeID = "SHSE";
        exchange1.ExchangeName = "上海证券交易所";
        exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "601155", "新城控股"));
        exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "600036", "招商银行"));
        exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "600266", "城建发展"));
        exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "600837", "海通证券"));
        exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "601668", "中国建筑"));
        ExchangeViewModel exchange2 = new ExchangeViewModel();
        exchange2.ExchangeID = "SZSE";
        exchange2.ExchangeName = "深圳证券交易所";
        exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "000002", "万科A"));
        exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "000001", "平安银行"));
        exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "000623", "吉林敖东"));
        exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "002739", "万达电影"));
        exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "300642", "透景生命"));


        ExchangeList.Add(exchange1);
        ExchangeList.Add(exchange2);

        ExchangeObservableCollection.Add(exchange1);
        ExchangeObservableCollection.Add(exchange2);
    }

    public NormalClass normalClass { get; set; } = new NormalClass();
    public NotifyClass notifyClass { get; set; } = new NotifyClass();
    public List<ExchangeViewModel> ExchangeList { get; set; } = new List<ExchangeViewModel>();
    public ObservableCollection<ExchangeViewModel> ExchangeObservableCollection { get; set; } = new ObservableCollection<ExchangeViewModel>();

    private void ChangeDataButton_Click(object sender, RoutedEventArgs e)
    {
        normalClass.BindData = "BindDataChange";
        notifyClass.BindData = "BindDataChange";
    }
    private void RemoveSelectedList1Button_Click(object sender, RoutedEventArgs e)
    {
        if(exchangeListListBox1.SelectedItem != null)
        {
            ExchangeList.Remove((ExchangeViewModel)exchangeListListBox1.SelectedItem);
        }
    }
    private void RemoveSelectedList2Button_Click(object sender, RoutedEventArgs e)
    {
        if (exchangeListListBox2.SelectedItem != null)
        {
            ExchangeList.Remove((ExchangeViewModel)exchangeListListBox1.SelectedItem);
        }
    }

    private void RemoveSelectedList3Button_Click(object sender, RoutedEventArgs e)
    {
        if (exchangeListListBox3.SelectedItem != null)
        {
            ExchangeObservableCollection.Remove((ExchangeViewModel)exchangeListListBox3.SelectedItem);
        }
    }
    private void RemoveSelectedList4Button_Click(object sender, RoutedEventArgs e)
    {
        if (exchangeListListBox4.SelectedItem != null)
        {
            ExchangeObservableCollection.Remove((ExchangeViewModel)exchangeListListBox4.SelectedItem);
        }
    }
}

到了这里,关于WPF数据绑定的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 哈工大CSAPP程序人生大作业

    正在上传…重新上传取消 计算机系统 大作业 题     目   程序人生 -Hello’s P2P  专       业    计算机科学与技术        学    号   2021110991             班    级      2103101             学       生         安心           指 导 教 师    

    2023年04月24日
    浏览(52)
  • 【程序人生】上海城市开发者社区小聚有感

    📫作者简介: 小明java问道之路 , 2022年度博客之星全国TOP3 ,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公司后端高级工程师。          📫 热衷分享,喜欢原

    2024年02月06日
    浏览(52)
  • 程序人生 | 编程的上帝视角应该怎么去找

      前言 📫 作者简介 :小明java问道之路,专注于Linux内核/汇编/HotSpot/C++/Java/源码/架构/算法 就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计📫  🏆 CSDN专家博主/Java优质创作者/CSDN内容合伙人 、InfoQ签约作者 、阿里云专家/签约博主、

    2023年04月24日
    浏览(71)
  • C罗老矣,我的程序人生还有多远

    ☆ 随着12月11号摩洛哥1-0葡萄牙比赛的结束,不仅说明葡萄牙对要结束本届卡塔尔世界杯了,就连C罗此生的世界杯之旅也将画上句号了。 ☆ 37岁的球星本该是人生最璀璨的阶段,但在足球生涯中,这已经是大龄了。不禁让我想到,身为开发的我,也大概类似吧。   目录  1、

    2024年01月16日
    浏览(33)
  • 【程序人生】如何在工作中保持稳定的情绪?

    在工作中保持稳定的情绪是现代生活中一个备受关注的话题。随着职场压力和工作挑战的增加,我们常常发现自己情绪波动不定,甚至受到负面情绪的困扰。然而,保持稳定的情绪对于我们的工作效率、人际关系和整体幸福感都至关重要。 无论你是一位职场新人还是一位资深

    2024年02月15日
    浏览(31)
  • 程序人生——Java数组和集合使用建议(2)

    程序人生——Java数组和集合使用建议(2) 需求:要删除一个ArrayList中的20-30范围内的元素;将原列表转换为一个可变列表,然后使用subList获取到原列表20到30范围内的一个视图(View),然后清空该视图内的元素,即可在原列表中删除20到30范围内的元素 建议72:生成子列表后

    2024年03月19日
    浏览(30)
  • wpf数据绑定之元素、资源、后台绑定

            wpf前端的数据绑定主要分为元素、资源以及后台数据三种,元素可以简单的理解为前端的空间数据绑定,资源是在resource里找数据,而后台就是跟cs文件之间的数据互相传递。           先说下元素吧,也就是控件元素,因为代码比较简单,就不上效果了,自己可以

    2024年02月04日
    浏览(49)
  • C# WPF 数据绑定

    后台变量发生改变,前端对应的相关属性值也发生改变 接口 INotifyPropertyChanged 用于通知客户端(通常绑定客户端)属性值已更改。 官方示例代码如下 示例演示 before after 本示例提供了多种绑定方式,使用接口进行绑定,不使用接口进行绑定 1.在MainWindow中进行属性更改 2.在

    2024年02月02日
    浏览(32)
  • WPF数据绑定

    数据绑定是一种历经时间考验的传统方式,做法是从对象中提取信息,并在应用程序的用户界面中显示提取的信息,不用编写枯燥的代码就可以完成所有工作。富客户端通常使用双向绑定,这种数据绑定提供了从用户界面向一些对象推出信息的能力——同样,不需要或者几乎

    2024年02月11日
    浏览(32)
  • WPF 入门笔记 - 04 - 数据绑定

    慢慢来,谁还没有一个努力的过程。 --网易云音乐 数据绑定概述 (WPF .NET) 什么是数据绑定? 数据绑定(Data Binding)是 WPF 一种强大的机制,用于在应用程序的各个部分之间建立数据的双向关联。它允许你将数据从一个源(例如对象、集合、数据库等)绑定到目标控件的属性,

    2024年02月09日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包