首页 > 其他分享 >WPF绑定基础

WPF绑定基础

时间:2022-10-31 17:48:17浏览次数:44  
标签:绑定 基础 new WPF data public 属性

WPF类继承关系图

Object类:在.Net中所有类型的根类型

​ DispatcherObject类:WPF中的大多数对象是从 DispatcherObject 派生的,这提供了用于处理并发和线程的基本构造。WPF 基于调度程序实现的消息系统。

DependencyObject类:表示一个参与依赖项属性系统的对象。

​ Visual类:为 WPF 中的呈现提供支持,其中包括命中测试、坐标转换和边界框计算。

​ UIElement 类: WPF 核心级实现的基类,该类建立在 Windows Presentation Foundation (WPF) 元素和基本表示特征基础上。

​ FrameworkElement 类:为 Windows Presentation Foundation (WPF) 元素提供 WPF 框架级属性集、事件集和方法集。此类表示附带的 WPF 框架级实现,它是基于由UIElement定义的 WPF 核心级 API 构建的。

​ Control类:表示 用户界面 (UI) 元素的基类,这些元素使用 ControlTemplate 来定义其外观。

​ ContentControl类:表示包含单项内容的控件。

​ ItemsControl类:表示一个可用于呈现项的集合的控件。

​ Decorator类:提供在单个子元素(如 Border 或 Viewbox)上或周围应用效果的元素的基类。

​ Image类:表示显示图像的控件。

​ MediaElement类:表示包含音频和/或视频的控件。

​ Panel类:为所有 Panel 元素提供基类。使用 Panel 元素在 Windows Presentation Foundation (WPF) 应用程序中放置和排列子对象。

​ Sharp类:为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。

绑定的4个组成部分

1、bindingsource 绑定源,可以省略,省略后会至下而上的搜索DataContext, 它的值可以由Source或ElementName属性来指定,二者选其中之一

2、bindingsource.path 绑定源属性(源属性的路径) Path=bindingsource.path可以省略Path=, 除非顺序有变化的情况下需要添加Path=

3、bindingtarget 绑定目标

4、bindingtarget.property[denpendencyProperty] 绑定目标属性,必须是DenpendencyProperty

在xaml中查看可能不是那么直观

<TextBox Text="{Binding ElementName=textBox2, Path=Text, Mode=Default, UpdateSourceTrigger=PropertyChanged}">Text</Text>

在代码中进行绑定, 其中的binding目标是textBox,绑定的目标属性是TextProperty

textBox.SetBinding(TextBox.TextProperty, new Binding()
{
    //Source与ElementName都是指定绑定源
    //Path = new PropertyPath("TextStr"),
    //Source = data,
    Path = new PropertyPath("Text"),
    ElementName = "textBox2",
    Mode = BindingMode.TwoWay,
    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});

数据绑定方向,5个绑定模式

1、OneWay 绑定源属性的值可以传递到绑定目标属性的值

2、TwoWay 绑定源属性的值可以传递到绑定目标属性的值,同时绑定目标属性的值也可以传递到绑定源属性的值

3、OneWayToSource 绑定目标属性的值可以传递到绑元源属性的值

4、OneTime 绑定源的属性值在初始化的时候将值传递到目标属性的值上,后面不在传递

5、Default 如果bindingsource.path属性是private set的那么等同于OneWay, 如果为public set的那么等同于TwoWay

数据绑定的触发条件

涉及到的绑定模式:TwoWay、OneWayToSource、Default(当属性的set为public时)

由updatesourceTrigger来确定触发的条件

1、LostFocus 当控件失去焦点时传递值到源属性值

2、PropertyChanged 每当控件的属性值改变都立即传递值到源属性值

3、Explicit 用户仅通过调用UpdateSource方法来传递值到属性的值

4、Default 绑定目标属性的默认UpdateSourceTrigger值,多数依赖项属性的默认值为PropertyChanged,而Text属性的默认值为LostFocus;确定依赖项属性的默认UpdateSourceTrigger值的编程方法是使用GetMetadata来获取属性的属性元数据,然后检查DefaultUpdateSourceTrigger属性的值

绑定方向和触发条件的Default就是在依赖属性元数据中,我们可以通过获取依赖属性元数据来读取绑定方向和元数据

var meta = TextBox.TextProperty.GetMetadata(textBox) as FrameworkPropertyMetadata;
Console.WriteLine($"update source trigger is {meta.DefaultUpdateSourceTrigger}, binding two way is default {meta.BindsTwoWayByDefault}");

打印的结果update source trigger is LostFocus, binding two way is default True

绑定源的4种方式

1、Source

2、ElementName

3、RelativeSource

RelativeSourceMode枚举值的解释

名称 说明
Self 引用对其设置绑定的元素,并允许将该元素的一个属性绑定到同一元素的其他属性
FindAncestor 引用数据绑定元素父级,可以使用它绑定到特定类型或其子类的上级,如果要指定AncestorType和AncestorLevel可以使用此模式
PreviousData 允许绑定所显示数据项列表中以前的数据项
TemplatedParent 引用应用了模板的元素,这类似于设置TemplateBindingExtension,并仅当Binding在模板中时适用

4、DataContext

绑定数据更改通知

Actually you are encountering a another hidden aspect of WPF, that's it WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.

实际上,你遇到了WPF的另一个隐藏方面,那就是WPF的数据绑定引擎将数据绑定到PropertyDescriptor实例,如果源对象是一个普通的CLR对象,并且没有实现INotifyPropertyChanged接口,则PropertyDescriptor实例将包装源属性。数据绑定引擎将尝试通过PropertyDescriptor.AddValueChanged()方法订阅属性更改事件。当目标数据绑定元素更改属性值时,数据绑定引擎将调用PropertyDescriptor.SetValue()方法将更改的值传输回源属性,并且它将同时引发ValueChanged事件以通知其他订阅者。

在说明INotifyPropertyChanged之前看看上面的内容,普通的CLR类型的属性即使没有实现INotifyPropertyChanged也能完成数据的双向绑定,这是由WPF的绑定引擎来隐式的完成了ValueChanged和SetValue的属性回调方法。

public class Data4Binding
{
    private string _textStr = "Hello Binding";

    public string TextStr
    {
        get { return _textStr; }
        set { _textStr = value; }
    }
}

...
    
Data4Binding data = new Data4Binding();
textBox.SetBinding(TextBox.TextProperty, new Binding()
{
    Path = new PropertyPath("TextStr"),
    Source = data,
    Mode = BindingMode.TwoWay,
    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});

textBox2.SetBinding(TextBox.TextProperty, new Binding()
{
    Path = new PropertyPath("TextStr"),
    Source = data,
    Mode = BindingMode.OneWay,
    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});

其中textBox是双向绑定,textBox2是OneWay的单项绑定,也就是绑定源属性的更新跟根默认或设定的触发条件去更新textBox2的Text属性的值,那么很容看出来,Data4Binding并没有实现INotifyPropertyChanged,也能进行双向绑定。

下面为Data4Binding实现以下INotifyPropertyChannged接口

public class Data4Binding: INotifyPropertyChanged
{
    private string _textStr = "Hello Binding";

    public string TextStr
    {
        get => _textStr;
        set {
            if (value == _textStr) return;
            _textStr = value;
            OnPropertyChanged(nameof(TextStr));
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler? PropertyChanged;
}

绑定数据转换器

用于不同数据类型间值的转换

public class ExerciseConverter : IValueConverter
{
    // 绑定源属性->绑定目标属性转换
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    // 绑定目标属性->绑定源属性转换
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

绑定数据验证

接受用户输入的大多数应用程序都需要具有验证逻辑,确保用户输入了符合要求的输入;验证检查可以基于类型、范围、格式或其他特定的要求。

验证规则

ValidationRule对象可检查属性值是否有效,WPF具有两种内置的ValidationRule对象:ExceptionValidationRule、DataErrorValidationRule

ExceptionValidationRule检查在更新绑定源属性时引发的异常,用于显示设置ExceptionValidationRule是将绑定对象上的ValidatesOnExceptions设置为true

DataErrorValidationRule检查由实现IDataErrorInfo接口对象引发的错误,用于显示设置DataErrorValidationRule是将绑定对象上的ValidatesOnDataErrors设置为true

自定义验证规则

做一个只能输入数字的自定义验证

public class ExerciseValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        if (string.IsNullOrWhiteSpace(value.ToString())) return new ValidationResult(false, "不能为空");
        if (Regex.IsMatch(value.ToString(), @"^(\-)?\d+(\.\d+)?$"))
        {
            return ValidationResult.ValidResult;
        }
        else
        {
            return new ValidationResult(false, "只能输入数字");
        }
    }
}
验证方式1

在绑定数据源中添加用于绑定使用的ErrorMessage属性

private string _errorMessage;
public string ErrorMessage
{
    get { return _errorMessage; }
    set
    {
        if (value == _errorMessage) return;
        _errorMessage = value;
        OnPropertyChanged(nameof(ErrorMessage));
    }
}

在Xaml中添加一个使用了ValidationRules的TextBox和一个用于绑定错误显示的TextBlock

<Window.Resources>
    <local:Data4Binding x:Key="data4Binding"/>
</Window.Resources>
<Window.DataContext>
    <StaticResource ResourceKey="data4Binding"/>
</Window.DataContext>

<DockPanel>
    <TextBlock DockPanel.Dock="Right" Text="{Binding ErrorMessage}" Foreground="Red" VerticalAlignment="Center"/>
    <TextBox x:Name="textBoxWithValidation"  MinWidth="120" Margin="5" Validation.Error="textBox_Error" VerticalAlignment="Center">
        <TextBox.Text>
            <Binding Path="TextStr" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
                <Binding.ValidationRules>
                    <local:ExerciseValidationRule ValidatesOnTargetUpdated="True"/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
</DockPanel>

其中textBox_Error的回调函数

private void textBox_Error(object sender, ValidationErrorEventArgs e)
{
    if (Validation.GetErrors(textBoxWithValidation).Count > 0)
    {
        var data = DataContext as Data4Binding;
        data.ErrorMessage = Validation.GetErrors(textBoxWithValidation)[0].ErrorContent.ToString();
    }
    else
    {
        var data = DataContext as Data4Binding;
        data.ErrorMessage = "";
    }
}

当然也可以在当前xaml的后台代码中直接添加依赖属性,这个情况根据实际开发中遇到的情况,怎么方便怎么来

public string ErrorMessage
        {
            get { return (string)GetValue(ErrorMessageProperty); }
            set { SetValue(ErrorMessageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ErrorMessage.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ErrorMessageProperty =
            DependencyProperty.Register("ErrorMessage", typeof(string), typeof(MainWindow), new PropertyMetadata(""));
验证方式2

推荐的方式

为TextBox的Validation.ErrorTemplate指定ControlTemplate

这部分的内容需要对Xaml有需要深入一些的理解,不做细致的说明, App.xaml中对于资源的引入

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/ExerciseBinding;component/ValidationContent.xaml" />
        </ResourceDictionary.MergedDictionaries>
        <ControlTemplate x:Key="ErrorTemplate">
            <AdornedElementPlaceholder>
                <local:ValidationContent/>
            </AdornedElementPlaceholder>
        </ControlTemplate>
    </ResourceDictionary>
</Application.Resources>

新建一个独立的文件,ValidationContent.xaml

<ResourceDictionary  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ExerciseBinding">
    <ControlTemplate x:Key="ValidationToolTipTemplate">
        <Border x:Name="Root"
                Margin="5,0,0,0"
                Opacity="0"
                Padding="0,0,20,20"
                RenderTransformOrigin="0,0">
            <Border.RenderTransform>
                <TranslateTransform x:Name="xform" X="-25" />
            </Border.RenderTransform>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="OpenStates">
                    <VisualStateGroup.Transitions>
                        <VisualTransition GeneratedDuration="0" />
                        <VisualTransition GeneratedDuration="0:0:0.2" To="Open">
                            <Storyboard>
                                <DoubleAnimation Duration="0:0:0.2"
                                                 To="0"
                                                 Storyboard.TargetProperty="X"
                                                 Storyboard.TargetName="xform">
                                    <DoubleAnimation.EasingFunction>
                                        <BackEase Amplitude=".3" EasingMode="EaseOut" />
                                    </DoubleAnimation.EasingFunction>
                                </DoubleAnimation>
                                <DoubleAnimation Duration="0:0:0.2"
                                                 To="1"
                                                 Storyboard.TargetProperty="Opacity"
                                                 Storyboard.TargetName="Root" />
                            </Storyboard>
                        </VisualTransition>
                    </VisualStateGroup.Transitions>
                    <VisualState x:Name="Closed">
                        <Storyboard>
                            <DoubleAnimation Duration="0"
                                             To="0"
                                             Storyboard.TargetProperty="Opacity"
                                             Storyboard.TargetName="Root" />
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="Open">
                        <Storyboard>
                            <DoubleAnimation Duration="0"
                                             To="0"
                                             Storyboard.TargetProperty="X"
                                             Storyboard.TargetName="xform" />
                            <DoubleAnimation Duration="0"
                                             To="1"
                                             Storyboard.TargetProperty="Opacity"
                                             Storyboard.TargetName="Root" />
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <FrameworkElement.Effect>
                <DropShadowEffect  BlurRadius="11"
                                   ShadowDepth="6"
                                   Opacity="0.4" />
            </FrameworkElement.Effect>
            <Border Background="#FFDC000C"
                    BorderThickness="1"
                    BorderBrush="#FFBC000C">
                <TextBlock Foreground="White"
                           MaxWidth="250"
                           Margin="8,4,8,4"
                           TextWrapping="Wrap"
                           Text="{Binding [0].ErrorContent}"
                           UseLayoutRounding="false" />
            </Border>
        </Border>
    </ControlTemplate>

    <Style TargetType="local:ValidationContent">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Border  BorderBrush="#FFDB000C"
                             BorderThickness="1"
                             x:Name="root">
                        <ToolTipService.ToolTip>
                            <ToolTip x:Name="validationTooltip"
                                     Placement="Left"  
                                     PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                                     Template="{StaticResource ValidationToolTipTemplate}" />
                        </ToolTipService.ToolTip>
                        <Grid Background="Transparent"
                              HorizontalAlignment="Right"
                              Height="12"
                              Width="12"
                              Margin="1,-4,-4,0"
                              VerticalAlignment="Top">
                            <Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z"
                                  Fill="#FFDC000C"
                                  Margin="1,3,0,0" />
                            <Path Data="M 0,0 L2,0 L 8,6 L8,8"
                                  Fill="#ffffff"
                                  Margin="1,3,0,0" />
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type AdornedElementPlaceholder}}, Path= AdornedElement.IsKeyboardFocusWithin, Mode=OneWay}"
                                           Value="True" />
                                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type AdornedElementPlaceholder}}, Path= AdornedElement.(Validation.HasError), Mode=OneWay}"
                                           Value="True" />
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="validationTooltip"
                                    Property="IsOpen"
                                    Value="True" />
                        </MultiDataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

标签:绑定,基础,new,WPF,data,public,属性
From: https://www.cnblogs.com/linxmouse/p/16845139.html

相关文章

  • WPF DataGrid 按 Enter 移动到下一个单元格 开启编辑
    MoveFocustoNextCellonEnterKeyPressinWPFDataGrid?BegineditmodeonkeypresswithDataGridTemplateColumnWPF中DataGrid按Enter移动到下一个单元格开启......
  • python三大基础函数
    在Python中有三个基础函数,分别是filter()、map()和reduce(),他们分别为我们提供过滤、映射和聚合的功能。1. filter()函数在数据筛选和过滤的时候我们通常会采用filter()......
  • js基础知识
    JS有三种方式行内式<ajavascrip="JS代码"></a><divonclick="fn()"></div>内嵌式=><script>JS代码</script>外链式=>外部引入的方式JS的数据类型......
  • 【WPF 数据验证机制】三、INotifyDataErrorInfo接口+DataAnnotation数据特性实现model
    环境vs2022+.net6.0+wpf+MVVM+EFcore6.0MVVM验证示意图INotifyDataErrorInfo接口功能publicinterfaceINotifyDataErrorInfo{boolHasErrors{get;}//提供......
  • 大一学生《Web编程基础》期末网页制作 HTML+CSS+JavaScript 网页设计实例 企业网站制
    HTML实例网页代码,本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置,有div的样式格局,这个实例比较全面,有助于同学的学习,本文将介绍如何通过从头开始设计个人......
  • linux基础命令4
    用户和组群账户管理用户的角色是通过UID(用户ID号)来标识的,每个用户的UID都是不同的。在Linux系统中有三大类用户,分别是root用户、系统用户和普通用户。root用户UID为0......
  • 绑定本地的Session
    绑定本地的Session图示解析: 代码的结构:代码:SaveServlet.java1packagecom.itheima.servlet;23importjava.io.IOException;4importjavax.servlet.ServletExce......
  • vue基础题
    Vue2和Vue3的双向数据绑定的原理Vue2的核心就是通过Object.defineProperty()方法设置set和get函数来实现数据的劫持Vue3的核心是使用newProxy(对象,{get(),set()})方法来......
  • 前端基础7天快速入门——狂神说css笔记
    1、css的3种导入方式优先级:就近原则<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><!--内部样式-->......
  • 前端基础7天快速入门——狂神说html笔记
    网页基本信息<!--DOCTYPE——规范--><!DOCTYPEhtml><htmllang="en"><!--head标签代表网页头部--><head><!--meta描述性标签,它用来描述我们网站的一些信息......