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>

原文地址:http://www.cnblogs.com/linxmouse/p/16845139.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性