WPF 基础学习

WPF 基础

一、布局容器

  1. Grid 特点:表格布局

使用代码:

   <Grid ShowGridLines="True">
        <Grid.ColumnDefinitions >
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
    </Grid>

使用* 用来表示比例关系。也可以使用固定值

image-20230323203649176

  1. StackPanel 特点:当设置为横向(或者纵向)时,如果界面位置不够用,将会有控件被遮挡

代码:当前行只能放置3个控件,第四个有半截被遮挡

    <StackPanel Orientation="Horizontal">
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
    </StackPanel>

image-20230323204044283

  1. WrapPanel 环绕面板 特点:如果当前行位置不够放下控件,会把剩下的控件放到下一行

    代码:当前行只能放下3个控件。另起一行

    <WrapPanel Orientation="Horizontal">
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
        <Button Width="200" Height="50" Content="button1"/>
    </WrapPanel>

image-20230323204549599

  1. DockPanel 特定:可以为放入的控件设置停靠方向(上下左右),同时可以设置最后一个控件是否填充整个控件。如果想设置容器所有内控件的停靠方向可以使用HorizontalAlignment属性

代码如下:

    <DockPanel LastChildFill="False" HorizontalAlignment="Left">
        <Button Content="top" DockPanel.Dock="Top"/>
        <Button Content="bottom" DockPanel.Dock="Bottom"/>
        <Button Content="left" DockPanel.Dock="Left"/>
        <Button Content="right" DockPanel.Dock="Right"/>
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
        <Button Content="111" />
    </DockPanel>

image-20230323205154873

  1. Canvas 画布控件 特定:可以精确设置控件的停靠位置

        <Canvas>
            <Button Width="200" Height="50" Content="111" Canvas.Left="100" Canvas.Top="100"/>
            <Button  Width="200" Height="50" Content="222"  Canvas.Left="150" Canvas.Top="150"/>
            <Button  Width="200" Height="50" Content="333"  Canvas.Left="200" Canvas.Top="280"/>
        </Canvas>
    

    image-20230323205436666

二、样式(Style)

简单说明:

  • Style样式:对于控件外观属性的封装。用于统一设置控件的外观的资源。

  • Windows.Resources:只是对于当前窗体有效

  • Application.Resources:对于项目中所有的窗体都能生效

  • Style 一般属于静态资源

想法:给MainWindow中的Button控件设置样式

  1. 第一个按钮使用窗体的全局Button样式
  2. 第二个按钮使用自定义的buttonStyle1样式
  3. 第三个按钮使用自定义的buttonStyle2样式(buttonStyle2是在buttonStyle1的基础上进行修改的)
  4. 第三个按钮使用自定义的buttonStyle2样式,但是进行了背景颜色的修改。
  5. 在当前控件中自定义样式。(略)
    <Window.Resources>
        <Style  TargetType="Button">
            <Setter Property="Foreground" Value="Red"/>
            <Setter Property="Background" Value="Yellow"/>
            <Setter Property="FontFamily" Value="仿宋"/>
            <Setter Property="FontSize" Value="30"/>
            <Setter Property="FontStyle" Value="Italic"/>
            <Setter Property="FontWeight" Value="UltraBold"/>
        </Style>
        
        <Style x:Key="buttonStyle1" TargetType="Button" >
            <Setter Property="FontSize" Value="35"/>
            <Setter Property="FontFamily" Value="黑体"/>

        </Style>

        <Style x:Key="buttonStyle2" TargetType="Button" BasedOn="{StaticResource buttonStyle1}">
            <Setter Property="FontSize" Value="35"/>
            <Setter Property="FontFamily" Value="隶书"/>
        </Style>

    </Window.Resources>
    <Grid>
        <StackPanel Orientation="Horizontal">
            <Button x:Name="button1" Content="按钮1" Width="100" Height="45"/>
            <Button x:Name="button2" Content="按钮2" Width="100" Height="45" Style="{StaticResource buttonStyle1}"/>
            <Button x:Name="button3" Content="按钮3" Width="100" Height="45" Style="{StaticResource buttonStyle2}"/>
            <Button x:Name="button4" Content="按钮4" Width="100" Height="45" Style="{StaticResource buttonStyle2}" Background="Pink"/>
        </StackPanel>
    </Grid>

首先:样式必须指定TargetType

  1. 在使用的时候需要
  2. 样式可以被继承,BasedOn
  3. 样式,如果没有指定key,那么是对于所有的对应类型的控件都生效。
  4. 控件内的标签声明优先级>指定Key的声明>不指定key的声明

补充:隐式写法的另外一种写法,和直接写Button效果是一致的。

<Style  TargetType="{x:Type Button}"></Style>

三、模板

数据模板与控件模板

第一部分:控件模板:对于控件样式属性的封装

ControlTemplate

案例:

  • 使用Style 嵌套ControlTemplate

  • 直接使用ControlTemplate

  • 在自定义的控件里面使用模板

    代码如下:

    首先是:使用Style 嵌套ControlTemplate。控件调用style就行了。

        <Style TargetType="Button" x:Key="buttonStyle1">
                <Setter Property="FontSize" Value="55"/>
                <Setter Property="Template" >
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Grid Width="200">
                                <Border BorderBrush="Black" BorderThickness="5"/>
                                <Ellipse  Fill="Yellow" StrokeThickness="5" Stroke="red" Name="ellipse1"/>
                                <TextBlock Text="112233" FontSize="30" HorizontalAlignment="Center"/>
                            </Grid>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="true">
                                    <Setter TargetName="ellipse1" Property="Stroke" Value="black"/>
                                </Trigger>
    
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    

    第二种 直接使用ControlTemplate。这种需要声明控件需要使用哪种Template。调用方式和使用style基本相同

           <ControlTemplate x:Key="buttonTemplate1">
                <WrapPanel  Orientation="Horizontal" HorizontalAlignment="Center" Width="200">
                    <TextBlock Text="左边" FontSize="30" HorizontalAlignment="Center"  Background="Aqua"/>
                    <TextBlock Text="中间" FontSize="30" HorizontalAlignment="Center"  Background="Red" />
                    <TextBlock Text="右边" FontSize="30" HorizontalAlignment="Center"  Background="Green"/>
                </WrapPanel>
            </ControlTemplate>
    

    第三种 在使用的控件中自定义模板(只会作用于当前的控件),见调用代码

    调用代码:

        <Grid>
            <StackPanel>
                <Button x:Name="Button1"  Margin="5" Style="{StaticResource buttonStyle1}"/>
                <Button  x:Name="Button2" Margin="5" Template="{StaticResource buttonTemplate1}"/>
                <Button  x:Name="Button3" Margin="5">
                    <Button.Template>
                        <ControlTemplate>
                            <Grid Width="200" >
                                <Border BorderBrush="Black" BorderThickness="5"/>
                                <Ellipse Fill="Orange" StrokeThickness="5" Stroke="red" Name="ellipse1"/>
                                <TextBlock Text="112233" FontSize="30" HorizontalAlignment="Center"/>
                            </Grid>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
    
            </StackPanel>
        </Grid><Button  x:Name="Button3" Margin="5">
                    <Button.Template>
                        <ControlTemplate>
                            <Grid Width="200" >
                                <Border BorderBrush="Black" BorderThickness="5"/>
                                <Ellipse Fill="Orange" StrokeThickness="5" Stroke="red" Name="ellipse1"/>
                                <TextBlock Text="112233" FontSize="30" HorizontalAlignment="Center"/>
                            </Grid>
                        </ControlTemplate>
                    </Button.Template>
     </Button>
    

    效果:

    image-20230324001508376

第二部分:数据模板

说明:数据模板实质上也是以内容模板为基础,在数据模板中,是对内容模板重复的显示,以达到迭代显示数据的作用,所以在数据模板中,可以添加呈现数据的任意元素。

最重要一点:继承自ItemsControl类的控件才能使用数据模板。例如listbox和ComboBox控件

思路:先考虑数据模板需要长啥样,然后在数据模板中绑定数据

数据模板代码:使用listbox,其中使用3个textblock控件

        <ListBox x:Name="listbox1" Width="300" Height="300" HorizontalContentAlignment="Stretch" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border Margin="5" BorderThickness="10" BorderBrush="DarkRed" CornerRadius="5">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <TextBlock Grid.Row="0" HorizontalAlignment="Center" Text="{Binding Path=Name}"/>
                            <TextBlock Grid.Row="1" HorizontalAlignment="Center" Text="{Binding Path=Age}"/>
                            <TextBlock Grid.Row="2" HorizontalAlignment="Center" Text="{Binding Path=Height}"/>
                            <TextBlock Grid.Row="3" HorizontalAlignment="Center" Text="666"/>
                        </Grid>
                    </Border>

                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

调用代码:

    <Button Content="按钮" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="30,220,0,0" Width="150" Click="Button_Click"/>
    <TextBox x:Name="textbox1" HorizontalAlignment="Left"  VerticalAlignment="Top"  Margin="30,60,0,0"  Width="150" Text="textbox" TextWrapping="Wrap" Background="Yellow" Foreground="Red" />
    <TextBox x:Name="textbox2" HorizontalAlignment="Left"  VerticalAlignment="Top"  Margin="30,90,0,0"  Width="150" Text="textbox" TextWrapping="Wrap" Background="Yellow" Foreground="Red"/>
    <TextBox x:Name="textbox3" HorizontalAlignment="Left"  VerticalAlignment="Top"  Margin="30,120,0,0"  Width="150" Text="textbox" TextWrapping="Wrap" Background="Yellow" Foreground="Red"/>

单击事件:

 private void Button_Click(object sender, RoutedEventArgs e)
        {
            People p = new People();
            p.Name = textbox1.Text;
            p.Age = int.Parse(textbox2.Text);
            p.Height = int.Parse(textbox3.Text);
            listbox1.Items.Add(p);
        }

People类:

    class People
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public int Height { get; set; }
    }

结果如下:当点击buttton后,向界面显示数据

image-20230324014314280

四、数据绑定

方式如下:

  1. 绑定相关联控件的属性
  2. 绑定静态资源
  3. (相对)绑定 RelativeSource
  4. listbox 绑定ItemSource,textblock实现联动
  5. 控件的DataContext绑定实例化的对象

案例1:TextBox的Text绑定了Slider的数值

                <Slider
                    x:Name="slider1"
                    Width="200"
                    Height="50"
                    Background="Yellow" />
                <TextBox
                    x:Name="tb1"
                    Width="200"
                    Height="50"
                    HorizontalContentAlignment="Center"
                    Background="red"
                    FontSize="45"
                    Text="{Binding ElementName=slider1, Path=Value, UpdateSourceTrigger=PropertyChanged, FallbackValue='NoFound', Mode=OneWayToSource}" />

案例2:绑定静态资源中的string字符串

需要先引入(此处使用了string类型,所以需要引入)

        xmlns:sys="clr-namespace:System;assembly=mscorlib"

资源代码如下:

    <Window.Resources>
        <FontFamily x:Key="ff1">宋体字</FontFamily>
        <sys:String x:Key="string111">我真帅</sys:String>
    </Window.Resources>

调用代码如下

<TextBlock x:Name="textblock1"  Width="200" Height="50"  Background="Blue" Text="{Binding Source={StaticResource string111}}" FontSize="45" Foreground="OrangeRed"/>

案例3:相对资源绑定

向上绑定对应对应控件的属性

    <Grid x:Name="gr1">
        <Grid x:Name="gr2">
            <StackPanel>
                <TextBlock x:Name="textblock1"  Width="200" Height="50"  Background="Blue" Text="{Binding Source={StaticResource ff1}}" FontSize="45" Foreground="OrangeRed"/>
                <TextBlock x:Name="textblock2"  Width="200" Height="50"  Background="Green" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Grid},AncestorLevel=2,Mode=FindAncestor},Path=Name}" FontSize="45" Foreground="OrangeRed"/>
            </StackPanel>
        </Grid>

AncestorType :绑定资源来源的控件类型

Mode:寻找方式。FindAncestor找父类,就是向着外层去找。同理也能设置向着内层去找

AncestorLevel:找几层。当前是找2层,就是找到gr1;如果是1,就是gr2。

Path:寻找上层属性。

案例4:绑定到对象集合

(从ItemsControl派生的类都能数据绑定 ListBox,ComboBox,ListView,DataGrid都能使用)

listbox 中绑定一个含有Student类的list,然后当单击listbox 中的某项,Textblock会显示对应项实例的某个属值(Age)

其中MainWindow.xaml.cs

    public partial class MainWindow : Window
    {
        List<Student> list = new List<Student>()
        {
            new Student(){ Name="Tim",Age=22},
            new Student(){ Name="Tom",Age=25},
            new Student(){ Name="Tony",Age=28},
            new Student(){ Name="Kate",Age=30}
        };
        public MainWindow()
        {
            InitializeComponent();
            listbox1.ItemsSource = list;
            listbox1.DisplayMemberPath = "Name";
        }
    }

    class Student 
    {
        private string name;

        public String Name
        {
            get { return name; }
            set { name = value;}
        }

        private int age;

      
        public int Age
        {
            get { return age; }
            set { age = value;  }
        }
    }
<ListBox x:Name="listbox1" Width="200" Height="150" Background="Yellow"/>
<TextBlock x:Name="textblock3"  Width="200" Height="50"  Background="Green" Text="{Binding ElementName=listbox1,Path=SelectedItem.Age}" FontSize="45" Foreground="OrangeRed"/>

案例5 :使用控件的上下文绑定实例化对象

后台:把控件的上下文绑定实例化的对象。前台直接绑定属性

前台:

<TextBlock x:Name="textblock4"  Width="200" Height="50"  Background="Green" Text="{Binding Age}" FontSize="45" Foreground="OrangeRed"/>

后台:

Student st = new Student() { Name = "张三", Age = 88 };
textblock4.DataContext = st;

五、依赖属性

DependencyProperties

propdp 两下tab

定义:

普通 .NET 属性的值是直接从类中的私有成员读取的,而 DependencyProperty 的值是在调用从 DependencyObject 继承的 GetValue() 方法时动态解析

优点:

  1. 减少内存占用:如果多个对象依赖属性的值没有变动,那么在内存中这个依赖属性在堆内存中仅仅存放了一次。而不是多次。
  2. 值继承
  3. 更改通知:依赖属性具有内置的更改通知机制。通过在属性元数据中注册回调,您会在属性值更改时收到通知。

使用依赖属性的情景:

  1. 双向绑定。有了这个,依赖项属性不用写额的代码,也不用实现什么接口,它本身就俱备双向绑定的特性,比如,我把员工对象的姓名绑定到摇文本框,一旦绑定,只要文本框中的值发生改变,依赖项属性员工姓名也会跟着变化,反之亦然;

  2. 触发器。这个东西在WPF中很重要,比如,一个按钮背景是红色,我想让它在鼠标停留在它上面是背景变成绿色,而鼠标一旦移开,按钮恢复红色。
    如果在传统的Windows编程中,你一定会想办法弄一些事件,或者委托来处理,还要写一堆代码。告诉你,有了依赖项属性,你将一行代码都不用写,所有的处理均由WPF属性系统自动处理。而触发器只是临时改变属性的值,当触完成时,属性值自动被“还原”。

  3. 附加属性。附加属性也是依赖项属性,它可以把A类型的的某些属性推迟到运行时根据B类型的具体情况来进行设置,而且可以同时被多个类型对象同时维护同一个属性值,但每个实例的属性值是独立的。

  4. A属性改变时,也同时改变其它属性的值,如TogleButton按下的同时,弹出下拉框。

public class Student : DependencyObject
{
         //声明一个静态只读的DependencyProperty字段
        public static readonly DependencyProperty NameProperty;    
        static Student() 
        { 
            //注册我们定义的依赖属性Name 
            NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student), 
                new PropertyMetadata("名称", OnValueChanged)); 
        }    
        private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
        {
             //当值改变时,我们可以在此做一些逻辑处理
         }    
        //属性包装器,通过它来读取和设置我们刚才注册的依赖属性 
        public string Name 
        { 
            get { return (string)GetValue(NameProperty); } 
            set { SetValue(NameProperty, value); } 
        } 
}

第一步: 让自己的类继承自 DependencyObject基类。

第二步:依赖属性的定义必须使用 public static 声明一个 DependencyProperty的变量,并且有一个Property作为后缀,该变量才是真正的依赖属性 。

第三步:在静态构造函数中向属性系统注册依赖属性,并获取对象引用。依赖属性是通过调用DependencyProperty.Register静态方法创建,该方法需要传递一个属性名称,这个名称非常重要。

在定义控件Style和Template的时候,Setter的Property属性填入的值就是注册依赖属性时使用的名称。propertyType指明了依赖属性实际的类型,ownerType指明了是哪个类注册了此依赖属性,最后typeMetadata存放了一些依赖属 性的元信息,包括依赖属性使用的默认值,还有属性值发生变更时的通知函数。

第四步:在前面的三步中,我们完成了一个依赖属性的注册,那么我们怎样才能对这个依赖属性进行读写呢?答案就是提供一个依赖属性的实例化包装属性,通过这个属性来实现具体的读写操作。和CLR属性不同,依赖属性不是直接对私有变量的操纵,而是通过GetValue()和SetValue()方法来操作属性值的,可以使用标准的.NET属性定义语法进行封装,使依赖属性可以像标准属性那样来使用。

使用默认值的代码案例:

DependencyProperty.Register("Name", typeof(string), typeof(Student), new PropertyMetadata("小学生"));

PropertyMetadata:默认值

调用代码:

    public partial class MainWindow : Window
    {
        Student student;
        public MainWindow()
        {
            InitializeComponent();
            student = new Student() { Name = "Hello" };
            tb1.DataContext = student;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            student.Name = "李四";
        }
    }

六、触发器

  1. Trigger 简单触发器(监测依赖项属性进行触发)

  2. MultiTrigger 多条件触发器(当多个条件后触发)

  3. DataTrigger 数据触发器(监测所绑定的数据)

  4. EventTrigger 当一个时间发生时,应用一个动画

(1)简单触发器

在控件样式里面设置触发器:

当满足条件以后,TextBox的字体颜色变为黄色,控件背景变为红色

        <Style TargetType="TextBox">
            <Style.Triggers>
                <Trigger Property="Text" Value="5">
                    <Setter Property="Background" Value="red"/>
                    <Setter Property="Foreground" Value="Yellow"/>
                </Trigger>
            </Style.Triggers>
        </Style>

(2)数据触发器

需要设置绑定的数据来源

    <Window.Resources>
        <Style TargetType="TextBox">
            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self},Path=Text}" Value="51">
                <Setter Property="Background" Value="red"/>
                <Setter Property="Foreground" Value="Yellow"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

(3)多条件触发器

需要设置执行执行条件与执行结果:

        <Style TargetType="CheckBox">
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsVisible" Value="true"/>
                        <Condition Property="IsChecked" Value="true"/>
                    </MultiTrigger.Conditions>
                    
                    <MultiTrigger.Setters>
                        <Setter Property="BorderBrush" Value="Yellow"/>
                        <Setter Property="BorderThickness" Value="5"/>
                    </MultiTrigger.Setters>
                </MultiTrigger>
            </Style.Triggers>
        </Style>

(4)事件触发器

七、转换器(值转换器)

负责在目前中显示数据之前转换源数据,并且在数据应用回源之前转换新的目标值。

有以下几个应用场景:

  1. 将数据格式化为字符串表示形式。
  2. 创建特定类型的wpf对象。
  3. 根据绑定数据有条件地改变元素中的属性

案例1:使用值转换器设置字符串的格式

效果:左边TextBox输入数字,右边TextBlock显示奇数还是偶数

第一步 引入命名空间:

​ xmlns:local="clr-namespace:ConverterLesson"

第二步 编写转换器方法:

MainWindow.xaml.cs

    class IntToStringConverter : IValueConverter
    {
        public bool IsNumber(string str)
        {
            bool isMatch = Regex.IsMatch(str, @"^-?\d+$");
            return isMatch;
        }
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {

            if (IsNumber(value.ToString())==true&& value.ToString() != "" && int.Parse(value.ToString()) % 2 == 0)
                return "Even";
            else if (IsNumber(value.ToString()) == true && value.ToString() != "" && int.Parse(value.ToString()) % 2 != 0)
                return "Odd";
            else
                return Binding.DoNothing;

        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

数据流说明:

image-20230324181824480

说明:根据要求:修改Convert()方法就可以做到,修改显示信息的功能。

MainWindow.xaml

    
    <Window.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="FontSize" Value="50"/>
            <Setter Property="Margin" Value="5"/>
        </Style>
        <!--引入转换器函数-->
        <local:IntToStringConverter x:Key="inttostring"/>
    </Window.Resources>
    
    <!--在控件上绑定转换器函数-->
    <TextBox x:Name="tb1" FontSize="30" BorderThickness="5" Margin="5"/>
    <TextBlock Grid.Column="1" Text="{Binding ElementName=tb1,Path=Text,Converter={StaticResource inttostring}}"/>

重点:

  1. 除了要写绑定函数以外,其他内容和数据绑定一样
  2. 需要引入转换器函数
  3. 需要引入数据转换的命名空间

案例2:使用值转换器创建对象

文本框中输入男/女。右侧图像框中显示对应图像

MainWindow.xaml

<Window.Resources>
<local:StringToImageConverter x:Key="stringtoimage"/>
</Window.Resources>

<TextBox x:Name="tb3" FontSize="30" BorderThickness="5" Margin="5" Grid.Row="0" />
<Image Grid.Column="1" Grid.Row="0" Source="{Binding ElementName=tb3,Path=Text,Converter={StaticResource stringtoimage}}"/>

MainWindow.xaml.cs

    class StringToImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value.ToString() == "m")
                return new BitmapImage(new Uri("/image/男人.png", UriKind.Relative));
            else if (value.ToString() == "f")
                return new BitmapImage(new Uri("/image/女人.png", UriKind.Relative));
            else
                return Binding.DoNothing;

        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

七、命令

简单用法:

MainWindow.xaml

    public partial class MainWindow : Window
    {
        public MyCommand CommandExecute { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            CommandExecute = new MyCommand(Message);
        }
        private void Message()
        {
            MessageBox.Show("Hello World");
        }


    }

前台代码:

<Button x:Name="tb3" FontSize="30" BorderThickness="5" Margin="5" Grid.Row="0" Command="{Binding CommandExecute}" />

MyCommand.cs

    public class MyCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;
        private Action _action;

        public MyCommand (Action action)
        {
            _action = action;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            _action();
        }

八、通知

通知:需要实现INotifyPropertyChanged接口

一个语法糖的妙用。需要进行属性变更通知的时候,直接调用OnPropertyChanged()。

    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged([CallerMemberName] string propertyName="")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

九、mvvmlight

(1) 初次认识 mvvmlight

1、View负责前端展示,与ViewModel进行数据和命令的交互。

2、ViewModel,负责前端视图业务级别的逻辑结构组织,并将其反馈给前端。

3、Model,主要负责数据实体的结构处理,与ViewModel进行交互。

image-20230325002220123

第一步:先编写Model

StudentModel.cs

代码如下。注意该类需要继承ObservableObject类(该类是对于INotifyPropertyChanged的封装)

需要使用双向绑定的属性,需要写RaisePropertyChanged

    public class StudentModel : ObservableObject
    {
        private int _age;

        public int Age
        {
            get { return _age; }
            set
            {
                _age = value;
                RaisePropertyChanged(() => Age);
            }
        }
        private string _name;

        public string Name
        {
            get { return _name; }
            set { _name = value;
                RaisePropertyChanged(() => Name);
            }
        }

    }

第二步:编写ViewModel

StudentViewModel.cs

首先在构造函数里面实例化了一个Model对象。

然后设置Model的属性字段

代码如下:

    public class StudentViewModel : ViewModelBase
    {

        public StudentViewModel()
        {
            Student = new StudentModel() { Name = "Tom", Age = 17 };
        }




        private StudentModel _student;

        public StudentModel Student
        {
            get { return _student; }
            set
            { _student = value; RaisePropertyChanged(() => Student); }
        }
    }

第三步:注册ViewModel

ViewModelLocator.cs

  1. 在ViewModelLocator的构造函数中,在Ioc容器中注册StudentViewModel

  2. 然后在Main函数中,实例化这个StudentViewModel

  public class ViewModelLocator
    {
	 public ViewModelLocator(){
    	 ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
         SimpleIoc.Default.Register<StudentViewModel>();}

        #region 实例化
        public StudentViewModel Student
        {get{return ServiceLocator.Current.GetInstance<StudentViewModel>();}}
        #endregion

        public static void Cleanup(){}
     }

第四步:修改前端样式代码,显示内容

StudentView.xaml

页面样式代码:

        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock FontSize="30" Text="{Binding Student.Name}" />
            <TextBlock FontSize="30" Text="{Binding Student.Age}" />
        </StackPanel>

StudentView.xaml.cs 中添加上下文引用:

        public StudentView()
        {
            InitializeComponent();
            this.DataContext = new StudentViewModel();
        }

第五步(可以不需要):在App.xml中修改StartupUri

StartupUri="View/StudentView.xaml" 
posted @ 2023-03-25 00:52  聆听微风  阅读(249)  评论(0编辑  收藏  举报