WPF学习笔记

版本

winform 和 WPF 框架的程序版本有:

.NET Framework 版本 早于 .NET Core,随后.NET 5.0版本包含它们,2022 后.NET 6.0长期支持,

.NET 6.0生成文件不再有config文件,

UWP 程序

exe不易安装,暂时放弃

程序创建

当您在创建 WPF 应用程序时,可以选择两种不同的桌面应用程序模板:带有 .NET Framework 和不带 .NET Framework。

带有 .NET Framework 的桌面应用程序模板是传统的 WPF 桌面应用程序模板,它依赖于 .NET Framework 运行时。使用这个模板创建的应用程序可以在支持 .NET Framework 的操作系统上运行,比如 Windows 7、Windows 8、Windows 10 等。这种应用程序可以使用全面的 .NET Framework 类库和功能,并可以使用 WPF 的全部功能,包括复杂的界面和视觉效果。这种模板是传统的 WPF 应用程序开发方式。

不带 .NET Framework 的桌面应用程序模板是一个新的模板,在使用 .NET Core 运行时的情况下创建 WPF 应用程序。使用这个模板创建的应用程序可以在支持 .NET Core 运行时的操作系统上运行,比如 Windows 7 SP1 及更高版本。与传统的 .NET Framework 应用程序相比,这种模板具有更小的应用程序大小,更快的启动速度和更好的性能。不过,由于 .NET Core 运行时的限制,一些特定的 .NET Framework 功能和类库可能在这种应用程序中不可用。这种模板是为了支持跨平台开发和轻量级部署而设计的,可以在 Windows、macOS 和 Linux 上运行。

选择使用哪个桌面应用程序模板取决于您的需求和目标。如果您的应用程序需要使用全面的 .NET Framework 功能,或者需要在不支持 .NET Core 的操作系统上运行,那么您可以选择带有 .NET Framework 的模板。如果您希望应用程序具有较小的大小、更快的启动速度和更好的跨平台支持,那么您可以选择不带 .NET Framework 的模板。

 

控件类

.NET为WPF准备了两个线程(WPF应用启动时),分别用于呈现界面(后台线程)和管理界面(UI线程)。

DispatcherObject类

UI线程上给你们提供一个中间商Dispatcher(调度员),将Dispatcher放到一个抽象类DispatcherObject,然后我向你保证,我所有的控件都从这个DispatcherObject类继承,这样当你们在后台线程中要访问控件时,就可以从控件中找到那位中间商Dispatcher,由中间商来完成你要对控件的操作访问。

            Task.Factory.StartNew(() =>
            {
                Task.Delay(3000).Wait();
 
                button.Dispatcher.Invoke(() =>
                {
                    button.Content = "www.wpfsoft.com";
                });
            }); 

F5运行调试,我们会看到3秒后,button控件的Content属性被我们改成了 "www.wpfsoft.com"。

 DependencyObject类 

public class DependencyObject : DispatcherObject

依赖对象类,控件的属性可以不再被直接赋值,而是绑定了另一个”变量“,当这个”变量“发生改变时,控件的属性也会跟着改变,这样的属性也被称为依赖属性。

Visual类

是WPF框架中第三个父类,命名空间:System.Windows.Media。

public abstract class Visual : DependencyObject, IResource

Visual类继承了DependencyObject类。另外Visual类是一个抽象类,不可以被实例。

Visual类所做的事情只为控件呈现相关,但还不是去呈现控件,只是提供呈现的基础。那么,谁又去继承了Visual类,成为继Visual类之后又一个控件的基类呢?答案是UIElement类。

UIElement类

public class UIElement : Visual, IAnimatable, IInputElement

第四个基类,

UIElement基类定义了大量的路由事件

UIElement基类还定义了大量的依赖属性。

UIElement基类为我们提供了一系列的鼠标、键盘和触摸事件,并提供了一些常用的依赖属性。它可以呈现继承它的所有控件,为控件布局时调整位置和大小,响应用户的输入,引发一系列的路由事件,并继承了IAnimatable动画接口,用于支持动画的某些方面。

FrameworkElement类

布局控件

除了Border控件,其它常见的控件都继承于Panel基类

Panel类

是一个抽象类,不可以实例化

public abstract class Panel : FrameworkElement, IAddChild

它继承于FrameworkElement基类和IAddChild接口。

它有一个Background属性,意味着所有的布局控件都可以设置背景颜色。另外,它还有一个Children属性,这是一个集合属性,也就是说,所有的布局控件都可以添加多个子元素。这一点从它继承的IAddChild接口也能得到印证。

Panel提供了GetZIndex和SetZIndex方法成员

Border控件

严格来说,Border并不是一个布局控件,因为它并不是Panel的子类,而是Decorator装饰器的子类,而Decorator继承于FrameworkElement。要了解Border的用法,我们要先看看它的父类Decorator。public class Decorator : FrameworkElement, IAddChild

Decorator 装饰器只有一个Child 属性,说明Decorator只能容纳一个子元素(UIElement),也就是Border只能容纳一个子元素

public class Border : Decorator

内容控件 
Control类

public class Control : FrameworkElement

Control是许多控件的基类。比如最常见的按钮(Button)、单选(RadioButton)、复选(CheckBox)、文本框(TextBox)、ListBox、DataGrid、日期控件等等。

图像控件

数据绑定

在xaml 和 C#代码里直接改变 属性值之外,还有第三种方法改变,

 Binding(绑定)

Binding类

Binding类架起了 控件 和 ViewModel 之间的桥梁,指示了哪个控件的哪个属性与哪个ViewModel类的哪个属性建立绑定关系

public class Binding : BindingBase

public Binding();
public Binding(string path);

Binding的数据源

第一种

Text="{Binding Person.Name}"   

例化了一个Binding对象,后面紧跟的Person.Name表示一个Path路径

第二种;指明某个具体的数据源对象及对象的属性。这种绑定方式要用了Binding类的Source属性和Path属性

Text="{Binding Source={StaticResource RedBrush},Path=Color}"

RedBrush是提前定义好的资源对象,Path=Color 是Path参数为资源对象的Color属性

第三种数据源

利用ElementName属性指明另一个控件作为数据源,这里仍然要用到Path来指定另一个控件的某个属性路径。

<StackPanel x:Name="panel" VerticalAlignment="Center" Margin="100,0">
    <TextBlock Margin="5">
        <Run Text="Source示例:"/>
        <Run Text="{Binding Source={StaticResource RedBrush},Path=Color}"/>
    </TextBlock>
    <TextBlock Margin="5">
        <Run Text="ElementName示例:"/>
        <Run Text="{Binding ElementName=panel,Path=Margin}"/>
    </TextBlock>
</StackPanel>

ElementName=panel 通过名称指定控件对象,并绑定Margin属性

第四种数据源

<TextBlock Margin="5">


        <Run Text="RelativeSource示例:"/>
<Run Text="{Binding RelativeSource={RelativeSource Mode=Self},Path=Foreground}"/>

表示将自己的前景色显示到Text属性上。


        <Run Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=StackPanel},Path=Margin}"/>

表示从当前控件开始找上级,一个类型为StackPanel的控件,并把这个StackPanel控件的Margin显示到当前控件的Text属性上。


    </TextBlock> 

RelativeSource的Mode属性:

Binding的绑定模式

Binding类的Mode属性

改变时机,它由Binding类的UpdateSourceTrigger属性的值决定 。

INotifyPropertyChanged接口

public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler PropertyChanged;
}

实现接口和使用

public class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

ViewModel和一个Model,让它们都继承这个属性通知基类

Person类(Model)、MainViewModel类(ViewModel),

两个类的get set 的 set中加继承自父类的RaisePropertyChanged(); 当属性修改时就执行这个函数,如果前端UI 有绑定改变的属性,将会立即更新内容

ObservableCollection<T>泛型集合

IValueConverter转换器

Binding类还有一个Converter属性,其实,它是一个IValueConverter接口。

它的主要作用是:前后端建立绑定时,定义一套自定义逻辑,让前端显示的数据与后端获取的数据建立一定的对应关系。

public interface IValueConverter
{
    object Convert(object value, Type targetType, object parameter, CultureInfo culture);
    object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
 
}

IValueConverter接口有两个方法成员,分别是Convert和ConvertBack。

Convert方法成员:输入的value及parameter参数,根据自定义逻辑判断,返回一个object对象给前端XAML使用。

ConvertBack方法成员:与Convert相反,将前端输入的数据转换成另一个对象返回给后端的数据源。

使用:

1.可以自定义继承自IValueConverter的AgeToColorConverter类,按年龄返回不同的背景色

2.然后再xaml 导入

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

3.UI控件中使用

Background="{Binding Person.Age,Converter={StaticResource AgeToColorConverter}}"

IMultiValueConverter多值转换器

public interface IMultiValueConverter
{
    object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
    object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture);
 
}

使用:

1.和单个值差不多,自定义类 继承IMultiValueConverter接口类,实现接口

2. xaml:

    <TextBlock Margin="5" >
    <Run Text="称号:"/>
    <Run>
        <Run.Text>
            <MultiBinding Converter="{StaticResource MultiColorConverter}">
                <Binding Path="Person.Age" />
                <Binding Path="Person.Money"/>
            </MultiBinding>
        </Run.Text>
    </Run>                    
</TextBlock>

ValidationRule验证规则

自定义类 继承IMultiValueConverter接口类,实现接口, 不符合规则就不通过

 样式

样式的类型叫Style,它继承于DispatcherObject

public class Style : DispatcherObject, INameScope, IAddChild, ISealable, IHaveResources, IQueryAmbient

Resource资源

在App.xaml文件中,通常默认的StartupUri属性值为MainWindow.xaml,这个MainWindow.xaml文件实际上就是一个资源文件,它包含了主窗体界面的所有XAML代码。

WPF的资源的形式是多样化的,它可以是一个主窗体,也可以是一个画笔,或一个样式,或一个别的对象。关键的一点是,它是一个对象。

Application类、FrameworkElement基类和FrameworkContentElement基类。有Resources属性

比如全局的 在App.xaml文件中

<Application x:Class="HelloWorld.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:HelloWorld"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        
    </Application.Resources>
</Application>

都是写在 这些类.Resources标签内

 ResourceDictionary 资源字典

Resources的属性,其类型就是ResourceDictionary

public class ResourceDictionary : IDictionary, ICollection, IEnumerable, ISupportInitialize, IUriContext, INameScope

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="BlueButtonStyle" TargetType="Button">
        <Setter Property="Width" Value="100"/>
        <Setter Property="Height" Value="30"/>
        <Setter Property="Background" Value="Blue"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="Margin" Value="3"/>
    </Style>
</ResourceDictionary>

在ResourceDictionary 内定义一些样式;

ResourceDictionary在<XXX.Resources>内。

在右键-添加-资源字典文件时,创建的就是xaml文件,文件最外层ResourceDictionary 标签,内部自定义样式内容。

 MergedDictionaries 属性 合并其它资源字典文件

<ResourceDictionary.MergedDictionaries>
          <ResourceDictionary Source="Style/Button.xaml"/>
</ResourceDictionary.MergedDictionaries>

总结:Resources属性的值只能是一个ResourceDictionary对象,

一个ResourceDictionary对象中可以定义多个资源。

如果有多个ResourceDictionary对象,则必须使用MergedDictionaries属性。

任意类型都可以在Resources中被实例化,但是必须在实例化时指明一个key,因为在xaml中要引入定义好的资源,都是以key进行查找的。

触发器

Trigger触发器

<Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Foreground" Value="Red"/>
                <Setter Property="Width" Value="150"/>
                <Setter Property="Height" Value="50"/>
                <Setter Property="Content" Value="鼠标移入"/>
        </Trigger>                
 </Style.Triggers>

需要注意的是:Trigger触发器的条件应该是当前控件拥有的属性名称。比如Button按钮没有IsChecked属性,就不可设置与CheckBox相同的触发器。

 MultiTrigger多条件触发器

<Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver" Value="True"/>
                        <Condition Property="IsChecked" Value="True"/>
                    </MultiTrigger.Conditions>
                    <MultiTrigger.Setters>
                        <Setter Property="Foreground" Value="Red"/>
                        <Setter Property="Content" Value="多条件触发器"/>
                    </MultiTrigger.Setters>
                </MultiTrigger>                        
            </Style.Triggers>

多条件触发器,Condition 是一个集合,满足多条件后触发;setter属性也是一个集合

DataTrigger数据触发器

<Style TargetType="DataGridRow">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Age}" Value="19">
                    <Setter Property="Background" Value="LightBlue"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Age}" Value="20">
                    <Setter Property="Background" Value="LightGreen"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Age}" Value="21">
                    <Setter Property="Background" Value="LightCoral"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=_CheckBox,Path=IsChecked}" Value="True">
                    <Setter Property="Foreground" Value="Red"/>
                    <Setter Property="FontSize" Value="16"/>
                    <Setter Property="FontWeight" Value="Bold"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>

绑定控件属性,或控件的bool类型属性(IsChecked )为true时触发

MultiDataTrigger 多数据触发器

多数据触发器,和多条件的差不多,Condition 属性是一个集合,要满足多个数据

EventTrigger事件触发器

<Window.Triggers>
        <EventTrigger RoutedEvent="ToggleButton.Checked" SourceName="_CheckBox">
            <BeginStoryboard Storyboard="{StaticResource OnChecked}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="ToggleButton.Unchecked" SourceName="_CheckBox">
            <BeginStoryboard Storyboard="{StaticResource OnUnchecked}"/>
        </EventTrigger>
    </Window.Triggers>

模板

WPF的模板基类叫FrameworkTemplate,它是一个抽象类,它有三个子类,分别是ControlTemplate(控件模板)、ItemsPanelTemplate(元素面板模板)和DataTemplate(数据模板)。

FrameworkTemplate基类有一个VisualTree属性

WPF拥有两棵树,即逻辑树(LogicalTree)和视觉树(VisualTree)

要介绍逻辑树和可视化树,我们要先了解两个基类,它们分别是FrameworkElement类和Visual类

逻辑树

LogicalTreeHelper 类就是专门来遍历查找WPF的逻辑树的。LogicalTreeHelper 类提供用于逻辑树遍历的 GetChildren、GetParent 和 FindLogicalNode 方法。

可视化树

遍历可视化树需要用到WPF提供的VisualTreeHelper 类,而VisualTreeHelper 类提供 GetChild、GetParent和 GetChildrenCount方法。

有同名的不同函数,可视化树获取的比逻辑树更丰富一些:

而绿色框中的内容是哪来的?其实,这些内容是控件的模板代码——即控件的内部组成。

ControlTemplate控件模板

以WPF中的Button为例:

可以设置它的Width和Height,改变它的尺寸,

但是,它始终是一个矩形的按钮。假如我们希望得到一个圆形的按钮、或者带图标的按钮,这个时候就需要去改变按钮的内部结构外观——ControlTemplate控件模板。

基类FrameworkElement  有一个Template属性就是指控件的ControlTemplate模板

<ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                            <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>

</ControlTemplate>

在ContentTemplate中的ContentPresenter可视为等于Content属性。

控件模板的几种设置方式:

 将ControlTemplate定义在在控件中

将ControlTemplate定义在资源中

将ControlTemplate定义在Style样式中

ControlTemplate的触发器

一、在控件中的ControlTemplate的触发器

二、在Resources定义的ControlTemplate的触发器

三、Style样式中的ConControlTemplate的触发器

ControlTemplate中使用触发器,还有一个好处是:可以指定设置某个可视化树中的控件对象。比如上面这段代码,当鼠标移上去的时候,我们修改了border对象的背景色,这个背景色其实并不是Button本身的背景颜色,而是Button内部可视化树中的Border的背景颜色。只需要利用Setter的TargetName属性来指定就行了。

而鼠标移上去的时候,我们还修改了Foreground前景颜色,这个Foreground才是Button本身的属性。说明什么问题?说明ControlTemplate中的触发器不但可以修改控件的属性,还可以修改控件模板中的可视化树的元素的属性,它真是太好用了。

TemplateBinding模板绑定

TemplateBinding和Binding在使用上类似,但是从它的定义上看,它的Property属性是要求传入一个DependencyProperty依赖属性。

DataTemplate数据模板

...........................

依赖属性

自定义

普通属性

private int length = 0;
public int Length
{
    get { return length; }
    set { length = value; }
}

依赖属性

public int MyProperty
{
    get { return (int)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

使用 DependencyProperty.Register函数注册

在C#中,依赖属性(Dependency Property)和普通属性(Regular Property)有一些区别,特别是在绑定方面。

  1. 属性更改通知: 依赖属性自带属性更改通知机制,当属性的值发生变化时,会自动触发通知,使绑定的目标能够感知和响应属性的变化。而普通属性需要手动实现属性更改通知,通常通过INotifyPropertyChanged接口来实现。

  2. 绑定支持: 依赖属性天生支持绑定功能,可以将其与其他属性、表达式或数据源进行绑定,实现值的自动更新和同步。普通属性在没有额外实现的情况下,无法直接进行绑定。

  3. 样式和模板的应用: 依赖属性可以在控件的样式和模板中使用,可以通过样式来改变属性的外观和行为。普通属性通常无法直接在样式和模板中使用。

  4. 元数据支持: 依赖属性可以附加元数据,用于指定属性的默认值、验证规则、属性更改回调等。元数据提供了对属性行为的进一步控制。普通属性通常没有内置的元数据支持。

附加属性

附加与某个条件下的属性,

比如 读书时 有附加 某某学校的或某某班的 ,毕业或升年级后 原来的就没有了。

比如 酒店提供住宿,提供了房间,人成了顾客就有权 对房间有入住权 

常用的Canvas.Left  ,在Canvas 子集里就可以附加,不在就没有。

命令

事件驱动模式有一个不太好的地方是,它的前端XAML代码和后端的C#代码建立了一种强关联的耦合关系,无法体现WPF的MVVM开发模式的优势, 于是,WPF提供了一个ICommand接口

路由事件

路由事件的处理模型常用的有种:

  • 冒泡事件:由子控件位次向父容器传递,大部分的路由事件都是冒泡事件
  • 隧道事件:由父容器位次向其子容器、控件传递,一般PreXXX事件属性隧道事件
  • 直接 只有事件源才能响应触发的事件

常见的

冒泡事件:MouseUp

  1. ButtonClick:当按钮被单击时发生。
  2. MouseUp:当鼠标按钮释放时发生。
  3. KeyUp:当释放键盘键时发生。
  4. LostKeyboardFocus:当元素失去键盘焦点时发生。
  5. TextChanged:当文本框中的文本更改时发生。

隧道事件: PreviewMouseUp

  1. PreviewKeyDown:在按下键盘键之前发生。
  2. PreviewMouseDown:在鼠标按下之前发生。
  3. PreviewTextInput:在输入文本之前发生。
  4. PreviewMouseMove:在鼠标移动之前发生。
  5. PreviewGotKeyboardFocus:在元素获取键盘焦点之前发生。

直接:

  1. GotFocus:当元素获取焦点时发生。
  2. LostFocus:当元素失去焦点时发生。
  3. MouseEnter:当鼠标指针进入元素时发生。
  4. MouseLeave:当鼠标指针离开元素时发生。
  5. PreviewMouseLeftButtonDown:当鼠标左键按下时发生,

Canvas、Border的背景颜色必须赋值(哪怕是透明色)才能响应鼠标的单击事件。

冒泡和隧道事件都订阅了(侦听)谁先谁后?先隧道,后冒泡

冒泡事件适合处理具体元素上的事件,而隧道事件适合在事件传递到具体元素之前进行预处理或过滤操作。

附加事件

非控件的类型中定义路由事件

。。。。。。。。。

Transform转换

2D转换子类

RotateTransform        按指定的 Angle 旋转元素。

ScaleTransform        按指定的 ScaleX 和 ScaleY 量缩放元素。

SkewTransform        按指定的 AngleX 和 AngleY 量倾斜元素。

TranslateTransform        按指定的 X 和 Y 量移动(转换)元素。

TransformGroup与图片查看器

复合转换就是将旋转、平移、缩放、倾斜合并起来形成一个集团转换。由于Children是一个集合,所以可以叠加多个不同的转换。 后面的动画也是,有单个动画的,多个要使用有集合的,

画刷(Brush)

public abstract class Brush : Animatable, IFormattable, IResource

SolidColorBrush纯色画刷 

<Window.Resources>
    <SolidColorBrush x:Key="BackgroundBrush" Color="#123456"/>
</Window.Resources>
<Grid x:Name="grid" Background="{StaticResource BackgroundBrush}"/>

LinearGradientBrush 渐变画刷

动画

动画本质上是一系列快速播放的图像。

DoubleAnimation动画

public class DoubleAnimation : DoubleAnimationBase

 <Storyboard x:Key="WidthStoryboard" TargetProperty="Width">
        <DoubleAnimation 
                     From="200" 
                     To="300" 
                     Duration="0:0:1.5"
                     AutoReverse="True" 
                     RepeatBehavior="Forever">
        </DoubleAnimation>
    </Storyboard>

故事板 目标属性=Width ,200到300  0小时0分1.5秒总时长  自动倒带(自动重播) 行为=永久

xaml 加个触发:

<控件.Triggers>
                <EventTrigger RoutedEvent="Loaded" >
                    <EventTrigger.Actions>
                        <BeginStoryboard Storyboard="{StaticResource WidthStoryboard }"/>
                    </EventTrigger.Actions>
                </EventTrigger>
            </控件.Triggers>

Behavior类(行为)

 要使用行为,我们需要引入一个.dll动态库,或者您可以在nuget上找到它。请安装Microsoft.Xaml.Behaviors.Wpf这个组件包。

..................

 WPF中文网 - 从小白到大佬 以上类到动画,参考 重庆教主WPF中文网

双引号

@+"复制的地址中的斜杠可以直接用"

$+"可以加{变量}"

地址

dll 源码中地址   " /程序集名称;component" 是一种指定资源文件路径的特殊语法,用于引用程序集中的资源文件。

在WPF中,可以使用以下几种方式来表示程序目录:

  1. .:表示当前目录。例如,./Resource.xaml表示引用与当前XAML文件在同一目录下的Resource.xaml文件。

  2. ..:表示上级目录。例如,../Images/Image.png表示引用上级目录中的Images文件夹中的Image.png文件。

  3. ~:表示根目录。例如,~/Resource.xaml表示引用应用程序的根目录中的Resource.xaml文件。

     4. pack://application:,,,/    当前应用程序的根目录

程序集 

WPF 程序集有 

winform程序集 

显示

hidden 隐藏并占位 还可能挡住点击

Collapsed 隐藏不占位,不能被点击  和 挡住点击

快捷

快捷键

排版: 选中后 按Ctrl+K 接着F

启动: F5 ,停止Shift+F5

快捷打字:

输出Console.WriteLine():打字cw 接Tab两下

XAML

三个顶级元素 Window Application Page

依赖属性

依赖属性

是在DependencyObject类上定义的公共静态字段。它们通常用于控件和自定义类,以提供可扩展的属性功能。  绑定则绑定到依赖属性上

依赖属性(Dependency Property)是在 WPF(Windows Presentation Foundation)和 UWP(Universal Windows Platform)中用于实现属性系统的一种机制。依赖属性具有以下特点和功能:

  1. 值的存储和获取:依赖属性可以存储和获取属性的值,与普通属性类似。不同之处在于,依赖属性的值可以是从多个来源获取,例如本地值、绑定值、样式值、默认值等。

  2. 值的变更通知:依赖属性支持属性值的变更通知机制。当属性的值发生变化时,系统会自动触发相应的事件(例如PropertyChanged事件),从而可以在界面上更新相关内容。

  3. 值的继承和覆盖:依赖属性支持值的继承和覆盖。可以在控件的父子层次结构中定义依赖属性,并通过继承关系自动传递和覆盖属性的值。

  4. 值的动画和转换:依赖属性支持动画和转换。可以使用动画来改变依赖属性的值,并通过转换器将属性的值从一种类型转换为另一种类型。

  5. 值的绑定:依赖属性支持数据绑定,即将属性与数据源进行绑定,实现属性值的自动同步和更新。

依赖属性的特点和功能使其在 WPF 和 UWP 中扮演了重要的角色,它们可以用于控件的外观和行为的定义、布局的管理、数据的绑定和动态更新等方面。通过使用依赖属性,开发者可以更加灵活和高效地处理属性的值和状态,提高应用程序的可维护性和可扩展性。

属性的标记扩展,绑定表达式:

  1. 属性绑定表达式({Binding}):用于将属性绑定到数据源上。

    • 例如:Text="{Binding Name}" 将文本框的内容绑定到数据源中的 Name 属性。
  2. 静态资源引用表达式({StaticResource}):用于引用在 XAML 中定义的静态资源。

    • 例如:<TextBlock Foreground="{StaticResource MyBrush}" /> 引用名为 MyBrush 的静态资源。
  3. 动态资源引用表达式({DynamicResource}):用于引用在 XAML 中定义的动态资源。

    • 例如:<TextBlock Foreground="{DynamicResource MyBrush}" /> 引用名为 MyBrush 的动态资源。
  4. 静态成员引用表达式({x:Static}):用于引用静态类或静态成员。

    • 例如:<TextBlock Text="{x:Static local:Constants.ApplicationName}" /> 引用名为 Constants 的静态类中的 ApplicationName 属性。
  5. 事件绑定表达式({Event}):用于将事件绑定到处理程序方法。

    • 例如:<Button Click="{Event HandlerMethod}" /> 将按钮的点击事件绑定到名为 HandlerMethod 的处理程序方法。
  6. 转换器表达式({Binding ..., Converter=}):用于在绑定过程中应用值转换器。

    • 例如:<TextBlock Text="{Binding Age, Converter={StaticResource AgeConverter}}" /> 将 Age 属性的值通过名为 AgeConverter 的转换器进行转换。
  7. 多绑定表达式({MultiBinding}):用于将多个数据源绑定到一个属性。

    • 例如:<TextBlock> <TextBlock.Text> <MultiBinding Converter="{StaticResource FullNameConverter}"> <Binding Path="FirstName" /> <Binding Path="LastName" /> </MultiBinding> </TextBlock.Text> </TextBlock> 将 FirstName 和 LastName 属性的值通过 FullNameConverter 转换器进行组合。
知识点1:

{Binding Path=Name} 和 {Binding Name} 的区别在于使用属性 Path 进行了显式的属性路径指定。

知识点2:

绑定表达式不仅可以放到依赖属性中,还可以用于其他可以进行数据绑定的属性,例如控件的内容、样式、命令等。

在 WPF 或 UWP 中,绑定表达式通常用于将数据源与目标属性进行绑定,以实现数据的自动同步和更新。这些目标属性可以是依赖属性,也可以是其他支持绑定的属性。

以下是一些常见的使用绑定表达式的属性:

1. 依赖属性:这是最常见的使用绑定表达式的属性类型。依赖属性是在 WPF 和 UWP 中用于实现属性系统的一种机制。可以使用绑定表达式将数据源绑定到依赖属性上,实现属性值的动态更新。

2. 内容属性:某些控件(如 Label、TextBlock 等)具有内容属性,可以使用绑定表达式将数据源绑定到该属性上,实现内容的动态更新。

```xaml
<Label Content="{Binding MyText}" />
```

3. 样式属性:样式是一种用于定义控件外观和行为的机制。可以使用绑定表达式将样式应用于控件上,实现样式的动态更新。

```xaml
<Label Style="{Binding MyStyle}" />
```

4. 命令属性:某些控件(如 Button、MenuItem 等)具有命令属性,可以使用绑定表达式将命令与控件进行绑定,实现命令的动态更新。

```xaml
<Button Command="{Binding MyCommand}" />
```

总结来说,绑定表达式不仅可以放到依赖属性中,还可以用于其他支持绑定的属性,以实现数据的自动同步和更新。

2022全新录制 WPF数据绑定详细教程 零基础学习入门到精通一步到位 (C#/零基础/WPF/数据绑定)B0976_哔哩哔哩_bilibili

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

WPF使用控件模板,资源字典,矢量图形_wpf background 使用矢量图_凶猛的瘦子的博客-CSDN博客 1

语法:

委托 :

相当于一个委托对象,帮忙做某个事(帮忙运行某个函数),委托是一个特殊的类

使用:

//先声明,定义一个叫xxx的委托类型

  public等访问修饰符 delegate 返回类型 委托类型名称(参数类型 参数)

//实例化 相应的委托类

委托类型名称 实例变量=委托类型名称(目标函数名称)

 //使用

var 变量=委托类型名称(参数)

使用场景举例:

A 委托 中介B 找租金1500的房子

public  delegate ZuFang类型  ZhongJie(float numberRMB);

ZhongJie zhongJieB=new ZhongJie(ZhaoFang);

// zhongJieB+=new ZhongJie(ZhaoFangByNet);

// zhongJieB+=new ZhongJie(ZhaoFangByFangdong);

// zhongJieB+=new ZhongJie(ZhaoFangByGuanggao);

ZuFang类型 fangZi =zhongJieB(1500);

这里

A  就是 运行使用代码  ZuFang类型 fangZi =zhongJieB(1500);  的类

中介B 就是 ZhongJie委托类型 的实例zhongJieB

找租金1500的房子  就是 函数ZhaoFang

如果已有zhongJieB就不用再定义,没有就定义zhongJieB函数,返回类型为ZuFang类型

当然,如果A 能执行  函数ZhaoFang(有不通过中介B,自己找房子的能力), 也可以不使用委托

Action 

返回类型为void 无参数输入的一种委托, 可以像上面委托一样使用,也可以简写

Action action = () => 
{
    Console.WriteLine("Method2");
}
action.Invoke() // 委托() 和 委托.Invoke() 都一样的,光标放上有提示
Action<T> 

返回类型为void 泛型参数输入的一种委托

Action<string> action = new Action<string>(cw)

function void cw(string message)
{
    Console.WriteLine($"{message}");
}
action.Invoke("shu ru");



Action<string,int> action2 = new Action<string,int>(cw2)

function void cw2(string message,int index)
{
    Console.WriteLine($"{message}{int}");
}
action2.Invoke("shu ru",1);//最大16个参数
Func 

有返回类型 的委托 

function int getNum()
{
    return 1;
}

Func<int> func1=new Func<int>(getNum);
var result=func1(1);


function int addNum2(int p_inputNumber1,int p_inputNumber2)
{
    return p_inputNumber+int p_inputNumber2;
}

Func<int,int,int> func2=new Func<int,int,int>(addNum2);  //最后一个是返回的
var result2=func2(1,2);

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

事件:

不是委托,而是利用委托。

触发什么事件, 表示事件所携带的信息(事件的发起者,事件的数据)

//事件封装的类写上
namespace xxx
{
    public class Catcher
    {
        public event Action OnEat;//事件的定义
        public event Action<object,EventArgs> OnEat2;//一般会带上参数
        public Catcher()
        {
            Task. Factory.StartNew(0 =>
            {
                Thread.Sleep(3000) ;
                OnEat?.Invoke();//事件的触发
            })
        }

        public void  asd()
        {
            /*对应上面的泛型,可以使用自定义的继承EventArgs的类,
            **继承之后可以加属性,EventArgs是没有多余的属性存信息的*/
            var arg=new EventArgs(); 
            //如果可以,设置arg
             OnEat2?.Invoke(this,arg);
        }
    }

}

//侦听触发的地方写上
//实例化
Chtcher catcher =new Chtcher(); //实例化加触发OnEar
//侦听。 没有侦听时OnEar 和OnEar2 事件是null,所有看到上面OnEat2?先判断是否为null
catcher.OnEar += OnHandler;
catcher.OnEar2 += OnHandler2;  
carcher.asd(); //触发OnEar2

function void OnHandler()
{
    consolo.WriteLine("OnEar事件触发")
}
function void OnHandler(object sender,EventArgs e)
{
    consolo.WriteLine($"OnEar2事件触发,e>{e}")
}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

线程 :

006什么是子线程?子线程的定义、执行、线程信号取消,线程池的概念Task任务的使用_哔哩哔哩_bilibili

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Adorner是WPF中一种特殊的视觉元素,可以用于在其他元素上添加装饰或者附加功能。Adorner通常用于实现在用户界面上的一些特殊效果,比如鼠标悬停提示、错误提示、操作提示等。 Adorner需要继承自Adorner类,并在构造函数中调用基类的构造函数,并将需要装饰的元素作为参数传递进去。Adorner类中有一个AdornedElement属性,用于获取被装饰的元素。 Adorner类中有一个OnRender方法,用于绘制Adorner的外观。在OnRender方法中可以使用DrawingContext来绘制Adorner的外观,也可以使用VisualBrush来填充Adorner的内容。 Adorner的使用方法: 1. 创建一个Adorner类,继承自Adorner类,并在构造函数中调用基类的构造函数,并将需要装饰的元素作为参数传递进去。 2. 重写Adorner类的OnRender方法,用于绘制Adorner的外观。 3. 在需要装饰的元素上调用AdornerLayer.GetAdornerLayer方法获取AdornerLayer对象。 4. 调用AdornerLayer的Add方法,将Adorner添加到AdornerLayer中。 下面是一个简单的例子: ```csharp public class MyAdorner : Adorner { public MyAdorner(UIElement adornedElement) : base(adornedElement) { } protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); // 绘制Adorner的外观 drawingContext.DrawEllipse(Brushes.Red, null, new Point(AdornedElement.RenderSize.Width / 2, AdornedElement.RenderSize.Height / 2), AdornedElement.RenderSize.Width / 2, AdornedElement.RenderSize.Height / 2); } } // 在需要装饰的元素上添加Adorner var adornedElement = new Button { Content = "Click me!" }; var adornerLayer = AdornerLayer.GetAdornerLayer(adornedElement); var myAdorner = new MyAdorner(adornedElement); adornerLayer.Add(myAdorner); ``` 这个例子中,我们创建了一个MyAdorner类,继承自Adorner类,并重写了OnRender方法,在OnRender方法中绘制了一个红色的圆形。然后我们创建了一个Button元素,并将其作为参数传递给MyAdorner的构造函数,创建了一个AdornerLayer对象,并调用Add方法将MyAdorner添加到AdornerLayer中,从而实现了在Button元素上添加了一个红色的圆形装饰器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值