WPF数据绑定对象Binding中的辅助属性

发布时间 2023-11-28 11:46:58作者: ZHIZRL

数据绑定方向——Model

namespace System.Windows.Data
{
    public enum BindingMode
    {
        TwoWay = 0,
        OneWay = 1,
        OneTime = 2,
        OneWayToSource = 3,
        Default = 4
    }
}

TwoWay = 0,

绑定的双方,值可以相互传递

OneWay = 1,

绑定后,数据从源到目标  Data.Value->TextBox.Text

OneTime = 2,

绑定的双向,在初始化的时候,数据同步一次,Data.Value->TextBox.Text

OneWayToSource = 3,

绑定后,数据从目标到源,TextBox.Text->Data.Value

Default = 4

默认,没有写Model属性的时候是这个值,目标依赖属性的什么形式就是什么形式

    public class Data
    {
        public string Value { get; set; } = "123";
    }
        <TabItem Header="关于Mode属性">
            <StackPanel>
                <!--==================关于Mode属性=========================-->
                <!--TextBlock的Text属性默认是单向绑定,TextBox的Text属性默认是双向绑定-->
                <TextBlock Text="{Binding Value}"/>
                <!--原因是:上面对象是只是做显示,下面对象可以做编辑(页面输入时,信息会回写,写到Value属性里去)-->
                <!--这里问题与Value的属性类型无关-->
                <TextBox Text="{Binding Value,Mode=TwoWay}"/>
                <TextBox Text="{Binding Value,Mode=OneTime}"/>
                <TextBox Text="{Binding Value,Mode=OneWayToSource}"/>
                <TextBox/>
            </StackPanel>
        </TabItem>

更新数据源时机——UpdateSourceTrigger

namespace System.Windows.Data
{
    public enum UpdateSourceTrigger
    {
        Default = 0,
        PropertyChanged = 1,
        LostFocus = 2,
        Explicit = 3
    }
}

Default = 0,

不更新源

PropertyChanged = 1,

属性值发生变化的时候,更新源

LostFocus = 2,

失去焦点,更新源

Explicit = 3

明确的告诉进行更新

        <TabItem Header="关于UpdateSourceTrigger属性">
            <StackPanel>
                <!--==============关于UpdateSourceTrigger属性==============-->
                <!--TextBox的Text属性推送给源的时机是控件失去焦点-->
                <Button Content="Button" Click="Button_Click"/>
                <TextBox Text="{Binding Value,Mode=TwoWay}"/>
                <TextBox Text="{Binding Value,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                <TextBox Text="{Binding Value,Mode=TwoWay,UpdateSourceTrigger=Explicit}" Name="tb"/>
            </StackPanel>
        </TabItem>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // 获取对应控件属性的绑定表达式
            BindingExpression bindingExpression = tb.GetBindingExpression(TextBox.TextProperty);
            // 指定更新源
            bindingExpression.UpdateSource();
        }

延时更新数据源——Delay

        <TabItem Header="关于Delay属性">
            <!--
                // 变化的时候做什么逻辑
                // UpdateSrouce的时候,每次变化都会触发Set,如果Set里有逻辑   跟不上,
                // 希望每次输入都能直接触发更新,不需要失去焦点,不希望每个字符都更新
                // 可以让更新的时机稍等下  Delay
                // Text属性被连续更新后多少毫秒
            -->
            <StackPanel>
                <!--这里更新的时机是失去焦点,Delay无效,不会等待2000-->
                <TextBox Text="{Binding Value,Mode=TwoWay,Delay=2000}"/>
                <!--实时同步更新源的时候,可以等待-->
                <TextBox Text="{Binding Value,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Delay=2000}"/>
                <TextBox/>
            </StackPanel>
        </TabItem>

数据显示格式——StringFormat

        <TabItem Header="StringFormat">
            <StackPanel>
                <TextBlock Text="{Binding IntValue,StringFormat={}{0:000000}}"/>
                <TextBlock Text="{Binding IntValue,StringFormat={}{0:P2}}"/>
                <TextBlock Text="{Binding FloatValue,StringFormat={}{0:0.000}}"/>
                <TextBlock Text="{Binding FloatValue,StringFormat={}{0:N3}}"/>
                <TextBlock Text="{Binding FloatValue,StringFormat={}{0:C3},ConverterCulture=zh-cn}"/>
                <TextBlock Text="{Binding DateTimeValue,StringFormat={}{0:yyyy-MM-dd}}"/>
                <Border Height="1" Background="Red"/>
                <TextBlock Text="{Binding IntValue,StringFormat=\{0:000000\}}"/>
                <TextBlock Text="{Binding IntValue,StringFormat=\{0:P2\}}"/>
            </StackPanel>
        </TabItem>

触发器——Converter

使用自定义触发器转义字符

    public class Data
    {
        public string Value { get; set; } = "123";

        public int IntValue { get; set; } = 100;

        public float FloatValue { get; set; } = 0.1f;

        public DateTime DateTimeValue { get; set; } = DateTime.Now;

        public int CodeValue { get; set; } = 64;  // '@'

        public Data()
        {
            //IntValue.ToString("000000.00");
            //DateTimeValue.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
    // MarupExtension这个基类不是必须继承,主要目标是希望CharConverter可以以{}的形式在XAML中实例化
    // 比如:<TextBlock Text="{Binding CodeValue,Converter={local:CharConverter}}"/>
    // 没有继承MarkupExtensionn的情况下,XAML中以资源或标签对象的方式进行引用
    // 比如:<TextBlock Text="{Binding CodeValue,Converter={StaticResource cc}}"/>
    public class CharConverter :MarkupExtension, IValueConverter
    {
        // 数据从源到目标的时候,执行这个方法,将这个方法的结果显示在目标
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //int.Parse(value.ToString()) * 0.1;
            // &#@e618  
            // (char)e618   ' '
            // 源的数据    64   int     
            return (char)int.Parse(value.ToString());
        }

        // 数据从目标到源的时候。执行这个方法,将这个方法的结果提交给源
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // 目标的数据   a    97 
            //value
            return (int)(value.ToString()[0]);
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
    <Window.Resources>
        <local:CharConverter x:Key="cc"/>
    </Window.Resources>
        <TabItem Header="Converter">
            <StackPanel>
                <!--这种方式需要CharConverter实现MarkupExtension基类-->
                <TextBlock Text="{Binding CodeValue,Converter={local:CharConverter}}"/>
                <!--这种方式需要提前定义资源-->
                <TextBlock Text="{Binding CodeValue,Converter={StaticResource cc}}"/>
                <!--这种方式 可以不用定义资源-->
                <TextBlock>
                    <TextBlock.Text>
                        <Binding Path="CodeValue">
                            <Binding.Converter>
                                <local:CharConverter/>
                            </Binding.Converter>
                        </Binding>
                    </TextBlock.Text>
                </TextBlock>

                <TextBox Text="{Binding CodeValue,Converter={StaticResource cc},UpdateSourceTrigger=PropertyChanged}"
                         TextChanged="TextBox_TextChanged">
                </TextBox>
            </StackPanel>
        </TabItem>

使用系统默认触发器转换控件属性

    <Window.Resources>
        <!--自定义转换器-->
        <local:CharConverter x:Key="cc"/>

        <!--系统默认的转换器,非常常用-->
        <BooleanToVisibilityConverter x:Key="btv"/>

        <!--主要针对GroupBox的边框处理,不常用-->
        <BorderGapMaskConverter x:Key="bgmc"/>
    </Window.Resources>
                <CheckBox Content="展开" VerticalContentAlignment="Center" IsChecked="{x:Null}"
                          Name="cb"/>
                <Border Height="30" Background="Orange" 
                        Visibility="{Binding ElementName=cb,Path=IsChecked,Converter={StaticResource btv}}"/>

<Window.Resources>
        <AlternationConverter x:Key="ac">
            <SolidColorBrush>red</SolidColorBrush>
            <SolidColorBrush>Green</SolidColorBrush>
            <SolidColorBrush>orange</SolidColorBrush>
        </AlternationConverter>

        <x:Array Type="sys:Int32" x:Key="adatas">
            <sys:Int32>1</sys:Int32>
            <sys:Int32>2</sys:Int32>
            <sys:Int32>3</sys:Int32>
            <sys:Int32>4</sys:Int32>
            <sys:Int32>5</sys:Int32>
        </x:Array>
        <Style TargetType="ListBoxItem">
            <Setter Property="Background" 
                    Value="{Binding RelativeSource={RelativeSource Self},Path=(ItemsControl.AlternationIndex),Converter={StaticResource ac}}"/>
        </Style>
    </Window.Resources>
                <ListBox ItemsSource="{StaticResource adatas}" AlternationCount="3">

触发器参数传递——ConverterParameter

        // 1:男   2:女
        public int Gender { get; set; } = 2;
// MarupExtension这个基类不是必须继承,主要目标是希望CharConverter可以以{}的形式在XAML中实例化
    // 比如:<TextBlock Text="{Binding CodeValue,Converter={local:CharConverter}}"/>
    // 没有继承MarkupExtensionn的情况下,XAML中以资源或标签对象的方式进行引用
    // 比如:<TextBlock Text="{Binding CodeValue,Converter={StaticResource cc}}"/>
    public class CharConverter : MarkupExtension, IValueConverter
    {
        // 数据从源到目标的时候,执行这个方法,将这个方法的结果显示在目标
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //int.Parse(value.ToString()) * 0.1;
            // &#@e618  
            // (char)e618   ' '
            // 源的数据    64   int     
            int p = parameter == null ? 0 : int.Parse(parameter.ToString());
            return (char)(int.Parse(value.ToString()) + p);
        }

        // 数据从目标到源的时候。执行这个方法,将这个方法的结果提交给源
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // 目标的数据   a    97 
            //value
            return (int)(value.ToString()[0]);
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
    public class GenderConverter : IValueConverter
    {
        // 源 到 目标(IsChecked   bool)
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //value是源的属性值 
            var v = int.Parse(value.ToString());
            var p = int.Parse(parameter.ToString());
            if (v == p) return true;

            return false;
        }

        // 目标到 源
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //value是目标的属性值
            if (Boolean.Parse(value.ToString()))
                return parameter;

            return null;
        }
    }
    <Window.Resources>
        <!--自定义转换器-->
        <local:CharConverter x:Key="cc"/>
        <local:GenderConverter x:Key="gc"/>
    </Window.Resources>
                <!--ConverterParameter可以参定参数参与到转换器逻辑中,但是不允许绑定动态值-->
                <TextBlock Text="{Binding CodeValue,Converter={StaticResource cc},ConverterParameter=0}"/>
                <TextBlock Text="{Binding CodeValue,Converter={StaticResource cc},ConverterParameter=2}"/>
                <TextBlock Text="{Binding CodeValue,Converter={StaticResource cc},ConverterParameter=4}"/>


                <!--小案例:用户编辑窗口,里面有个属性叫性别(可以下拉,RadioButton单选,选项是4个以内用单选)-->
                <!--如何区分这两个信息-》数据中的一个属性中-->
                <RadioButton Content="男" VerticalContentAlignment="Center" 
                             IsChecked="{Binding Gender,Converter={StaticResource gc},ConverterParameter=1}"/>
                <RadioButton Content="女" VerticalContentAlignment="Center" 
                             IsChecked="{Binding Gender,Converter={StaticResource gc},ConverterParameter=2}"/>

绑定异常处理——FallbackValue-TargetNullValue

FallbackValue:无法绑定的时候,显示一个默认值

TargetNullValue:数据源属性的值为Null的时候目标属性中需要显示的信息

    public class MyBinding : Binding
    {
        public MyBinding()
        {
            this.TargetNullValue = "AAAA";
        }
    }
        <TabItem Header="FallbackValue-TargetNullValue">
            <StackPanel>
                <!--绑定失败的表示意思是:1、打不到数据源  2、Path路径无效    以上情况FallbackValue的指定值可以呈现-->
                <TextBlock Text="{Binding ElementName=cb,Path=aaa,FallbackValue=绑定失败}"/>
                <TextBlock Text="{Binding ElementName=cb,Path=IsChecked,FallbackValue=绑定失败,TargetNullValue=空值}"/>
                <TextBlock Text="{local:MyBinding ElementName=cb,Path=IsChecked}"/>
            </StackPanel>
        </TabItem>

数据验证——ValidationRules

系统验证-ExceptionValidationRule

    public class Data : INotifyPropertyChanged
    {
        private int _value;

        public event PropertyChangedEventHandler? PropertyChanged;

        public int Value
        {
            get { return _value; }
            set
            {
                if (value == 123)
                    throw new Exception("比如不能输入123");

                _value = value;
            }
        }
    }
    <Window.DataContext>
        <local:Data/>
    </Window.DataContext>
    <StackPanel Margin="20">
        <TextBox Text="{Binding Value,UpdateSourceTrigger=PropertyChanged}" Name="tb1"/>
        <TextBox TabIndex="0" Focusable="True" Name="tb2">
            <TextBox.Text>
                <Binding Path="Value" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        <Button Content="Get exception" Click="Button_Click"/>
        <TextBlock Text="{Binding Path=(Validation.Errors)[0].ErrorContent,ElementName=tb2}" Foreground="Red"/>
    </StackPanel>

目标到源自定义验证-ValidationRule

    public class ValueValidationRule : ValidationRule
    {
        // 实例的调用时机:是在界面上控件被绑定的属性发生变化的时候
        // 从目标到源过程,做这个处理
        // 关于从源中赋值更新的异常提示:IDataErrorInfo
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            // 做值的校验规则

            //value参数指的是目标值 

            if (int.TryParse(value.ToString(), out int v))
            {
                if (v == 123)
                    return new ValidationResult(false, "比如不能输入123");
            }
            else
                return new ValidationResult(false, "输入信息不对");

            return new ValidationResult(true, string.Empty);
        }
    }
    <StackPanel Margin="20">
        <TextBox Text="{Binding Value,UpdateSourceTrigger=PropertyChanged}" Name="tb1"/>
        <TextBox TabIndex="0" Focusable="True" Name="tb2">
            <TextBox.Text>
                <Binding Path="Value" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        <Button Content="Get exception" Click="Button_Click"/>
        <TextBlock Text="{Binding Path=(Validation.Errors)[0].ErrorContent,ElementName=tb3}" Foreground="Red"/>

        <TextBox TabIndex="0" Focusable="True" Name="tb3">
            <TextBox.Text>
                <Binding Path="Value" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <local:ValueValidationRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
    </StackPanel>

源到目标自定义验证-IDataErrorInfo

    public class Data :  INotifyPropertyChanged, IDataErrorInfo
    {
        public event PropertyChangedEventHandler? PropertyChanged;

        private int _value;
        public int Value
        {
            get { return _value; }
            set { _value = value;}
        }

        // 索引
        public string this[string propName]
        {
            get
            {
                if (propName == "Value")
                {
                    if (this.Value > 1000)
                        return "大于1000![IDataErrorInfo]";
                }
                return "";
            }
        }
    }
<TextBox Text="{Binding Value,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}" Name="tb4"/>

 ValidatesOnDataErrors验证开关,不设置此属性为True则不进行校验。