binding

RaisePropertyChanged

属性改变事件

https://blog.csdn.net/cselmu9/article/details/8275255

 

 

INotification 通知

https://www.cnblogs.com/lonelyxmas/p/10669406.html

 

binding

<TextBox  Name="textBox1"  Text="{Binding Path=Value,ElementName=slider1}"/>
<Slider HorizontalAlignment="Left" Margin="84,106,0,0" Name="slider1" VerticalAlignment="Top" />

有的时候我们会在代码中我们看大Path是一个“.”或者干脆没有Path的Binding,着实让人摸不着头脑。原来这是一种比较特殊的情况---Binding源本身就是一种数据且不需要Path来指明。典型的string,int等基本类型都是这样,他们是实例本身就是数据,我们无法指定通过那个属性来访问这个数据,这是我们只需要将这个数据设置为.就可以了。在XAML中这个.可以忽略不写,但是在C#中编程必须要带上。

在使用集合类型的数据作为列表控件的ItemSource时一般会考虑使用ObservableCollection<T>替换List<T>,因为ObservableCollection<T>类实现了INotifyChange和INotifyPropertyChanged接口,能把集合的变化立刻通知显示到它的列表控件上,改变会立刻显示出来。

 

 

单值转换实例,IValueConverter

 

1.当值从绑定源传播给绑定目标时,调用方法Convert

2.当值从绑定目标传播给绑定源时,调用此方法ConvertBack,方法ConvertBack的实现必须是方法Convert的反向实现。

    /// <summary>
    /// 自定义事件转换
    /// </summary>
    public class TimeConver : IValueConverter
    {
        //当值从绑定源传播给绑定目标时,调用方法Convert
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return DependencyProperty.UnsetValue;
            DateTime date = (DateTime)value;
            return date.ToString("yyyy-MM-dd");
        }
        //当值从绑定目标传播给绑定源时,调用此方法ConvertBack
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string str = value as string;
            DateTime txtDate;
            if (DateTime.TryParse(str, out txtDate))
            {
                return txtDate;
            }
            return DependencyProperty.UnsetValue;
        }
    }

注:返回值DependencyProperty.UnsetValue表示转换器没有生成任何值。

在xaml中引用TimeConver的命名空间

xmlns:local="clr-namespace:AudioDemo.View"

在xaml中定义Resources

<Window.Resources>
    <local:TimeConver x:Key="cvtDate"/>
</Window.Resources>

在xaml重指定Binding值使用自定义Converter转换

 
<Grid>
    <DatePicker x:Name="dateOne"  
                HorizontalAlignment="Left" Margin="85,50,0,0" VerticalAlignment="Top" Width="183"
                SelectedDateFormat="Long"/>
    <TextBox x:Name="textBox" Text="{Binding ElementName=dateOne,Path=SelectedDate,Converter={StaticResource cvtDate}}" 
                HorizontalAlignment="Left" 
                Height="23" Margin="85,105,0,0" 
                TextWrapping="Wrap" VerticalAlignment="Top" Width="183"/>
    <Label x:Name="label" Content="选择结果:" HorizontalAlignment="Left" Margin="19,105,0,0" VerticalAlignment="Top"/>

    <Label x:Name="label1" Content="{Binding ElementName=dateOne,Path=Text}" 
            HorizontalAlignment="Left" Margin="85,145,0,0" VerticalAlignment="Top"/>
</Grid>
<TextBox x:Name="textBox" Text="{Binding ElementName=dateOne,Path=SelectedDate,Converter={StaticResource cvtDate}}" 
                 HorizontalAlignment="Left" 
                 Height="23" Margin="85,105,0,0" 
                 TextWrapping="Wrap" VerticalAlignment="Top" Width="183"/>
public class ColorConverter : IMultiValueConverter
{
    //正向修改
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values == null || values.Length < 2)
            return DependencyProperty.UnsetValue;
        double verValue = (double)values[0];
        double horValue = (double)values[1];
        if (verValue > horValue)
            return new SolidColorBrush(Colors.Green);
        else if (verValue < horValue)
            return new SolidColorBrush(Colors.Red);
        return new SolidColorBrush(Colors.Yellow);
    }
    //反向修改
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        //返回空,标记不可双向转换
        return null;
    }
}

 

xmlns:local="clr-namespace:AudioDemo.View"
<Window.Resources>
    <local:ColorConverter x:Key="cvtColor"/>
</Window.Resources>
<Grid>
    <Label x:Name="label" Content="纵向值:" HorizontalAlignment="Left" Margin="10,40,0,0" VerticalAlignment="Top"/>
    <Label x:Name="label1" Content="横向值:" HorizontalAlignment="Left" Margin="10,80,0,0" VerticalAlignment="Top"/>
    <Slider x:Name="sliderVer" HorizontalAlignment="Left" Margin="75,43,0,0" VerticalAlignment="Top" Width="192"/>
    <Slider x:Name="sliderHor" HorizontalAlignment="Left" Margin="75,81,0,0" VerticalAlignment="Top" Width="192"/>
    <Ellipse   HorizontalAlignment="Left" Height="100" Margin="75,120,0,0" Stroke="Black" VerticalAlignment="Top" Width="100">
        <Ellipse.Fill>
            <MultiBinding Converter="{StaticResource cvtColor}">
                <Binding Path="Value" ElementName="sliderVer"/>
                <Binding Path="Value" ElementName="sliderHor"/>
            </MultiBinding>
        </Ellipse.Fill>
    </Ellipse>
</Grid>
public class RGBConverter : IMultiValueConverter
{
    //正向修改,整合颜色值
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values == null || values.Length < 3)
            return null;
        byte r = System.Convert.ToByte(values[0]);
        byte g = System.Convert.ToByte(values[1]);
        byte b = System.Convert.ToByte(values[2]);
        Color col = Color.FromRgb(r, g, b);
        SolidColorBrush brush = new SolidColorBrush(col);
        return brush;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return null;
    }
}
xmlns:local="clr-namespace:AudioDemo.View"
<Window.Resources>
    <local:RGBConverter  x:Key="rgbCvt"/>
</Window.Resources>
<Grid>
    <Label x:Name="label" Content="Red:" HorizontalAlignment="Left" Margin="10,48,0,0" VerticalAlignment="Top"/>
    <Label x:Name="label_Copy" Content="Green:" HorizontalAlignment="Left" Margin="7,85,0,0" VerticalAlignment="Top"/>
    <Label x:Name="label_Copy1" Content="Blue:" HorizontalAlignment="Left" Margin="7,123,0,0" VerticalAlignment="Top"/>
    <Slider x:Name="slider_r" Minimum="0" Maximum="255" Ticks="1"   HorizontalAlignment="Left" Margin="68,53,0,0" VerticalAlignment="Top" Width="207"/>
    <Slider x:Name="slider_g" Minimum="0" Maximum="255"  Ticks="1" HorizontalAlignment="Left" Margin="68,91,0,0" VerticalAlignment="Top" Width="207"/>
    <Slider x:Name="slider_b" Minimum="0" Maximum="255"  Ticks="1" HorizontalAlignment="Left" Margin="68,124,0,0" VerticalAlignment="Top" Width="207"/>
    <Rectangle  HorizontalAlignment="Left" Height="90" Margin="68,160,0,0" Stroke="Black" VerticalAlignment="Top" Width="142">
        <Rectangle.Fill>
            <MultiBinding Converter="{StaticResource rgbCvt}">
                <Binding ElementName="slider_r" Path="Value"></Binding>
                <Binding ElementName="slider_g" Path="Value"></Binding>
                <Binding ElementName="slider_b" Path="Value"></Binding>
            </MultiBinding>
        </Rectangle.Fill>
    </Rectangle>

</Grid>
<Button Content="Test">
        <Button.Background>
            <MultiBinding Converter="{StaticResource mutiBoolConverter}">
                 <Binding Path="IsEnableCredentialsAdded"/>
                 <Binding Path="IsOld"/>
             </MultiBinding>
        </Button.Background> 
</Button>

 

 <TextBox Height="20" Margin="0,0,10,87" Text="{Binding ElementName=slider1,Path=Value}" BorderThickness="1"/>
 <Slider Maximum="100" Margin="0,50,0,35" Name="slider1"/>

 

改善优化WPF Binding

下面的内容在修正错误的同时,还将对原有的Binding做一些小小的改进。

 

情况如下:在将数据库中的DataTable对象的DefaultView通过XAML语言Binding到listView1对象的ItemsSource上时总是失败。

 

答案那就是使用DataContext,这里先来梳理一下Binding的一些基本概念及使用方法。

 

 

l 典型的Binding具有四个重要组成部分:Binding目标对象(binding target object)、目标对象属性(target property)、Binding数据源(binding source)、Path(用于指明要从数据源中取得的值,就是我们通常写的属性名称)。

 

 

例如:想要把一个员工(Employee)的姓名(EmpName)Binding显示到一个TextBox的Text属性上。

 

分析一下其中的4个组成部分:Binding目标对象是TextBox(因为它要最终使用这些数据);目标对象属性是TextProperty(这里要注意,不是Text属性,而是与Text属性相对应的Dependency Property,通常命名是属性名+Property);

 

数据源是员工(Employee);Path是EmpName。我的经验是:刚开始使用Binding的时候由于还不熟悉,建议在做之前先在心理把这4个部分对号入座一下,等日后就可以熟悉成自然了。

 

目标对象属性一定要是Dependency Property。由于只有DependencyObject类及其子类能够定义Dependency Property,因此,Binding目标对象就必须是DependencyObject类及其子类了。另一方面反过来说,UIElement(DependencyObject的子类)的大多数属性都有与其对应的Dependency Property,因此,大多数的UIElement属性我们都可以利用其Dependency Property作为目标对象属性从而与数据源建立Binding。

 

l 再有一点,建立Binding的数据源不一定要是一个CLR对象,也可以是XML数据、ADO.NET对象及DependencyObject。

 

关于为什么binding的对象一定要是DependencyObject,为什么binding对象的属性一定要是dependency property,这个我就不知道了。

在我第一次看到在XAML中设置Binding的语法时,简直是满头雾水。这东西到底怎么回事,简直就没有章法可循,一会儿ElementName,一会儿Source,还有时什么都没有,就一个{Binding },再有就是Mode,UpdateSourceTrigger等,简直没有头绪。

 

不过只要打开MSDN,看一下System.Windows.Data.Binding对象,似乎开始有点感觉了,查看Binding对象的属性栏(Properties),你会发现,原来我们在XAML中设置Binding时使用的东西和Binding对象的属性好像都是一一对应的。

 

确实如此,在XAML中设置Binding就是在这只System.Windows.Data.Binding对象,这里先提醒一下大家。

 

WPF可不是就这么一个Binding噢,实时上,从BindingBase继承过来的有三个兄弟,Binding只是最常用而已。

找到了binding的老家,相信大家对{Binding ElementName=ElemName, Path=PropertyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, …}这样的语法应该清楚了吧。

那么{Binding } 及{Binding XXXX}这样的语法呢?其实可以就是等于new Binding()及new Binding(XXXX),其实可以考虑成是分别使用了Binding的两种构造方法,而XXXX的作用自然也就清晰了。

其实和{Binding Path=XXXX}是等价的,都是设置Path属性的。至于这里的Path属性在我们平常使用时其实是用来设置属性名的地方,为什么不直接叫做是PropertyName之类的名字呢?这个又要留着以后思考了。

当一个数据源与一个UIElement形成Binding后,数据源的改变,如果传递给UIElement的指定属性?

同样,当UIElement中指定属性值改变了,如何才能够通知到数据源?要讨论这个问题,需要按数据源来分类进行讨论。这个分类并不是互斥的之前存在一些交集。

因此,同一种Binding场景可能适用于多种情况。

下面的内容是来自MSDN中《Binding Sources Overview》中的内容,由于怕翻译的不好而误导大家,所以分类的名称还是使用英文吧。

Using a CLR Class as the Binding Source Object

在典型的Binding情景中,一个CLR 对象作为数据源,将其中一个属性值Binding到某个UIElement的属性上(以上面显示员工姓名的情景为例。

 

用XAML实现<TextBox Text=”{Binding Source=Employee, Path=EmpName}” />)。当数据源属性值变化了,相应让UIElement中对应属性变化。

 

需要实现一个中通知机制,最简单的方法是通过实现INotifyPropertyChanged接口完成此操作。具体实现,可以参考MSDN上的文章《How to: Implement Property Change Notification》。

 

其实就是实现了”public event PropertyChangedEventHandler PropertyChanged;”事件,通常的做法是在上面加封装一个”protected void OnPropertyChanged(string name)”函数。在属性的set方法结尾处,调用此函数实现通知的作用。

 

如果不实行此接口,则必须自己实现一套通知系统。为你的类中的每一个想要实现通知机制的属性创建一个PropertyChanged模板。

 

即为你的每一个属性创建一个名为”PropertyNameChanged”事件,其中PropertyName是与这个事件对应的属性名称。然后在属性发生变化是触发这个事件。(听起来就麻烦,我也懒得去实践验证了)

 

如果以上两种方法都无法实现的话,那么就只好在你认为需要的时候显示的调用UpdateTarget方法来手动更新了。

 

(这种方法没用过,感觉已经失去了Binding的意义了)

Entire Objects Used as a Binding Source

可以理解为将整个对象作为数据源,与上一个题目的区别很小。

 

基本上可以认为是原来Binding实现的是数据源属性与UIElement属性之间的Binding,而这次是直接将一个Object对象与一个UIElement的属性进行Binding了。

 

这种情况可以使用Binding的Source属性指明数据源(假设Employee对象与一个ListBox实现banding,用XAML实现<ListBox ItemsSource=”{Binding Employee}” />),或者还可以将DataContext属性设置为Employee(DataContext属性只有UIElement的一个子类System.Windows.FrameworkElement及其子类才会有)。

 

这样,在设置ItemsSource时就可以直接使用”{Binding }”这样的语法了。当然,DataContext属性设置成Employee还是需要使用DataContext=”{Binding Employee}”语句的。

 

有人在这里会嘲笑这一举动实在是啰嗦,事实上,DataContext属性是非常有用的。之所以Binding对象失败而最终不得不使用C#语句进行Binding就是因为没有设置DataContext。

 

因为,当我们使用{Binding Source= Employee, Path=”EmpName”}设置属性后,WPF查找Source对象是根据Element Tree逐级向上自己的Parent Element查找的,查找每一Element上的DataContext所指定的内容是否包含这一Source内容,并以第一个匹配到的结果作为最终对象。

 

也就是说,DataContext是按照Element Tree向下继承的,并且决定这WPF在运行时能否找到我们所制定的Source对象。即使我们使用{Binding Path=”EmpName”}的语句也是如此。

 

比较常见的做法是,当我们有大量的Element需要与一个数据源中的众多属性实现Binding时,我们可以直接在一个公共Parent Element的DataContext上设置这一对象(甚至可以是整个window或page上),这将会极大的加少我们重复的代码量,同时也会是程序更加可读。

当我们需要使用的数据源是一个集合时,如果想要实现改变通知的机制。

 

同样需要实现一个接口INotifyCollectionChanged用来在集合发生改变时发起,用法单个对象是实现INotifyPropertyChanged接口类似。

 

首先是将上以前做的不是很好的那一段代码改成XAML语言实现。

 

将TextBox标签中的Text属性与上面listView1中被选定的记录(listView1.SelectedItem)中对应属性(例如:ContactID)做Binding。

 

实现后如图所示:

<TextBox Text="{Binding ElementName=listView1, Path=SelectedItem.ContactID}" MinWidth="100" />

 

看以来代码有点长,而且,下面的几个TextBox也都存在大量的重复内容,这里就可以考虑使用上面所说的DataContext了。

listView1中被选定的记录(listView1.SelectedItem)作为DataContext放在公共Parent Element上,做法如下:

 

<WrapPanel Grid.Row="1" Orientation="Horizontal" DataContext="{Binding ElementName=listView1, Path=SelectedItem}">

然后剩下的代码就可以简化很多了:

<StackPanel Orientation="Horizontal" Margin="5,2,5,2">

<TextBlock Text="ContactID:" TextAlignment="Center" />

<TextBox Text="{Binding ContactID}" MinWidth="100" />

</StackPanel>

<StackPanel Orientation="Horizontal" Margin="5,2,5,2">

<TextBlock Text="FirstName:" TextAlignment="Center" />

<TextBox Text="{Binding FirstName}" MinWidth="100" />

</StackPanel>

<StackPanel Orientation="Horizontal" Margin="5,2,5,2">

<TextBlock Text="LastName:" TextAlignment="Center" />

<TextBox Text="{Binding EmailAddress}" MinWidth="100" />

</StackPanel>

<StackPanel Orientation="Horizontal" Margin="5,2,5,2">

<TextBlock Text="EmailAddress:" TextAlignment="Center" />

<TextBox Text="{Binding LastName}" MinWidth="100" />

</StackPanel>

 

原来的SelectionChanged事件处理方法现在已经没有用了,将多余的代码去掉:

 

<ListView Name="listView1" SelectionChanged="listView1_SelectionChanged" MinWidth="280" >

 

后台代码中的对应方法也可以删掉了private void listView1_SelectionChanged(object sender, SelectionChangedEventArgs e)

 

F5 Debug运行一下,结果和原来是完全一样的。

在上一文中无得以选择使用C#实现Binding的部分代码,现在使用XAML语言实现,有几种方案可以选择,通过如下几步实现:

1) 将原先建立给listView1建立Binding那一大串代码删掉,替换成如下代码:

listView1.DataContext = dt.DefaultView;

执行结果如下图所示:

 

 

2) 将其与listView1的ItemsSource属性建立Binding,设置每一列的header及DisplayMemberBinding值。

这里由于DataContext的原因,ItemsSource可以写成"{Binding }"的形式而其Children Element也都可以简写成"{Binding ContactID}"这样的形式。

<ListView Name="listView1" MinWidth="280" ItemsSource="{Binding }">

<ListView.View>

<GridView x:Name="gridView1">

<GridView.Columns>

<GridViewColumn DisplayMemberBinding="{Binding ContactID}" Header="ContactID"></GridViewColumn>

<GridViewColumn DisplayMemberBinding="{Binding FirstName}" Header="FirstName"></GridViewColumn>

<GridViewColumn DisplayMemberBinding="{Binding LastName}" Header="LastName"></GridViewColumn>

</GridView.Columns>

</GridView>

</ListView.View>

</ListView>

改造完后,运行结果会发现,效果和原来是一样。效率提高了,代码更加简化。

UpdateSourceTrigger  绑定的UpdateSourceTrigger属性用来控制改变的值何时传递给源。

PropertyChanged  一旦绑定的属性值改变,源会立即更新

LostFocus 一旦目标控件失去焦点,源就会被更新

Explicit 源不会更新除非你手动来操作

要使用事件绑定需要借助System.Windows. interactivity,如果安装了Blend,里面就包含了这个dll。需要在Interaction.Triggers里面添加一个或多个EventTrigger并指定关注的的事件名称,在EventTrigger中通过InvokeCommandAction来绑定事件对应的命令。图中所示绑定了主窗口的Loaded事件,在事件触发后会调用绑定的命令对象LoadedCommand的Execute方法执行命令,当命令绑定需要参数时可以通过绑定CommandParameter实现。需要指出的是之前在实现MyCommand的Execute方法时我们加入了CanExecute的判断,因此事件触发后是否能够真正执行绑定的命令也受到绑定的LoadedCommand的CanExecute方法的影响。

 

https://www.cnblogs.com/durow/p/4860836.html

注意;

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"就是导入Blend的dll,然后在控件内部用<i:Interaction.Triggers/>即可,其它应该一看就知道,我通过事件触发器,来引发ViewModel中两个Command,第二个Command的参数是Button对象,通过ElementName=btn来指定。

<Button Name="btn" Content="Button" Height="33"  VerticalAlignment="Top" Width="109">
        <i:Interaction.Triggers>
             <i:EventTrigger EventName="Click">
                  <i:InvokeCommandAction Command="{Binding Command1}" CommandParameter="10" />
             </i:EventTrigger>
            <i:EventTrigger EventName="MouseMove">
              <i:InvokeCommandAction Command="{Binding Command2}" CommandParameter="{Binding ElementName=btn}" />
            </i:EventTrigger>
       </i:Interaction.Triggers>
 </Button>

--------------------- 

进行Bingding时,如果明确知道数据源的Name,就能用Source或者ElementName进行绑定,但是有时候我们需要绑定的数据源可能没有明确的Name,此时我们就需要利用Bingding的RelativeSource进行绑定。

一、控件关联自身的属性

 <TextBox Name="textbox" FontSize="24" Margin="10"
                             Text="{Binding RelativeSource={RelativeSource Mode=Self},Path=Name}"/>
二、控件关联其父级容器的属性

<TextBox Name="textbox" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Grid},AncestorLevel=1},Path=Name}"/>

RelativeSource属性的数据类型为RelativeSource类,AncestorLevel指的是以Bingding目标控件为起点的层级偏移量,d2的偏移量是1,g2的偏移量是2,AncestorType指的是要找的目标对象的类型。值得注意的是AncestorLevel需要参考AncestorType使用,如上面设置了AncestorType={x:Type Grid},则Bingding在寻找时会忽略非Grid的控件,此时g2的偏移量是1,g1的偏移量是2,DockPanel被忽略。


将一个Behavior拖到一个元素上,就能使这个元素具有某种行为,而实现这种行为的程序集和你的项目是独立的,你创建的Behavior也可以被其他人使用。

<!-- 绑定自定义行为 -->
<i:Interaction.Behaviors>
  <hei:MouseDoubleClickCopyTextBehavior CopyText="{Binding ElementName=ContentPresenter,Path=Content}"/>
</i:Interaction.Behaviors>

public class MouseDoubleClickCopyTextBehavior : Behavior<FrameworkElement>
    {
        /// <summary>
        /// 需要复制的内容
        /// </summary>
        public string CopyText
        {
            get { return (string)GetValue(CopyTextProperty); }
            set { SetValue(CopyTextProperty, value); }
        }


        public static readonly DependencyProperty CopyTextProperty = DependencyProperty.Register("CopyText", typeof(string), typeof(MouseDoubleClickCopyTextBehavior), new PropertyMetadata(null));


        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObject_PreviewMouseLeftButtonDown;
        }

        void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount >= 2)
                Clipboard.SetDataObject(CopyText);
        }

获取构成在合并字典的各种资源字典 ResourceDictionary 字典的集合  合并多个外部资源字典成为本地字典

合并多个外部资源字典成为本地字典。当需要合并更多字典的时候只要在ResourceDictionary.MergedDictionaries节中顺序增加引用。

合并字典(MergedDictionaries 集合中的字典)中对同一对象的同一子元素进行定义的时候,会产生覆盖效果:在这种情况下,所返回的资源将来自在 MergedDictionaries 集合中最后一个找到的字典。(在这里会显示myresourcedictionary2.xaml中的定义)。

合并字典(MergedDictionaries 集合中的字典)中对同一对象的不同子元素定义的时候会产生叠加效果。

 <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="xxx.xaml"></ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值