c# 学习笔记 PropertyChangedEventHandler、 =>、DependencyObject、DataContext 原创

在C#中,PropertyChangedEventHandlerPropertyChanged 常常与 INotifyPropertyChanged 接口一起使用,这是实现数据绑定和通知机制的关键部分,尤其在WPF (Windows Presentation Foundation) 或其他支持数据绑定的UI框架中。

PropertyChangedEventHandler

PropertyChangedEventHandler 是一个委托,它定义了当属性发生变化时应该调用的方法的签名。这个委托接受两个参数:发送更改通知的对象(通常是实现 INotifyPropertyChanged 接口的对象)和一个 PropertyChangedEventArgs 对象,后者包含关于更改的具体信息(即哪个属性发生了变化)。

PropertyChanged 事件

PropertyChanged 是一个事件,通常在一个实现了 INotifyPropertyChanged 接口的类中声明。当类的某个属性值发生变化时,类的实现者会触发这个事件,以通知任何订阅了这个事件的监听器。

示例

下面是一个简单的例子,展示了如何在C#中实现 INotifyPropertyChanged 接口,并使用 PropertyChangedEventHandlerPropertyChanged 事件:

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Person : INotifyPropertyChanged
{
    private string _name;
    private int _age;

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value) return;
            _name = value;
            OnPropertyChanged();
        }
    }

    public int Age
    {
        get { return _age; }
        set
        {
            if (_age == value) return;
            _age = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // 这个方法用于触发 PropertyChanged 事件
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

在这个例子中,Person 类实现了 INotifyPropertyChanged 接口,并声明了 PropertyChanged 事件。NameAge 属性在它们的setter中包含了调用 OnPropertyChanged 方法的逻辑。当这些属性的值发生变化时,OnPropertyChanged 方法会被调用,并触发 PropertyChanged 事件。

[CallerMemberName] 是一个特性,它允许你在没有显式传递属性名的情况下获取当前成员的名称。这使得代码更简洁,减少了错误的可能性。

任何订阅了 Person 对象的 PropertyChanged 事件的监听器现在都会收到通知,包括哪个属性发生了变化。这通常用于UI更新,或者任何需要响应数据变化的其他逻辑。

=>

在C#中,=> 是一个特殊的符号,它主要在两种上下文中有特殊的意义:

Lambda 表达式:
Lambda 表达式是一种简洁的编写匿名函数的方法。=> 符号用于分隔输入参数和表达式体。以下是一个Lambda表达式的例子:

csharp
Func<int, int, int> add = (x, y) => x + y;
在这个例子中,(x, y) 是输入参数,而 x + y 是表达式体。

如果Lambda表达式只有一个参数,你可以省略括号:

csharp
Func<int, bool> isPositive = x => x > 0;
表达式体成员(Expression-bodied members):
从C# 6.0开始,你可以使用 => 来定义一个只有表达式体的成员(如方法、属性、索引器、构造函数和析构函数)。这可以使得代码更加简洁。

例如,一个只有表达式体的方法:

csharp
public int Square(int x) => x * x;
一个只有表达式体的只读属性:

csharp
public int Length => someList.Count;
一个只有表达式体的索引器:

csharp
public int this[int index] => data[index];
在这两种情况下,=> 都起到了分隔左侧的定义(如参数列表、属性名或索引器签名)和右侧的表达式体的作用。

WPF DependencyObject

在WPF(Windows Presentation Foundation)中,DependencyObject 是一个基础类,用于实现依赖属性(Dependency Properties)系统。依赖属性是WPF中用于数据绑定、样式、动画和默认值的强大系统。

DependencyObject 类提供了存储和检索依赖属性值的机制。与传统的.NET属性不同,依赖属性可以参与WPF的属性系统,该系统提供了许多高级功能,如数据绑定、样式、动画、值继承、默认值等。

以下是关于 DependencyObject 的一些关键点:

基础类:所有具有依赖属性的WPF类(如 Control、FrameworkElement、Shape 等)都继承自 DependencyObject。
依赖属性存储:依赖属性的值不直接存储在类的字段中,而是存储在 DependencyObject 的内部字典中。这使得WPF能够在运行时高效地查询和修改这些值。
元数据:每个依赖属性都与一个 PropertyMetadata 对象相关联,该对象包含有关该属性的元数据,如默认值、属性更改回调等。
属性更改通知:当依赖属性的值更改时,WPF可以触发各种事件和回调,如 PropertyChangedCallback。
数据绑定:依赖属性是WPF数据绑定系统的核心。通过依赖属性,你可以将UI元素与数据源进行双向绑定。
动画和渐变:WPF的动画系统使用依赖属性来驱动动画和渐变效果。
样式和模板:依赖属性允许WPF样式和模板系统通过统一的方式修改多个UI元素的外观和行为。
要使用依赖属性,你需要定义一个 DependencyProperty 静态字段,并使用 DependencyProperty.Register 方法或 DependencyProperty.RegisterAttached 方法(对于附加属性)进行注册。然后,你可以使用 GetValue 和 SetValue 方法来读取和设置依赖属性的值。

下面是一个简单的示例,展示了如何定义一个依赖属性:

csharp
public class MyControl : Control
{
// 依赖属性注册
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(“MyProperty”, typeof(string), typeof(MyControl), new PropertyMetadata(string.Empty));

// .NET包装属性  
public string MyProperty  
{  
    get { return (string)GetValue(MyPropertyProperty); }  
    set { SetValue(MyPropertyProperty, value); }  
}  

}
在这个示例中,MyControl 类定义了一个名为 MyProperty 的依赖属性,并提供了一个相应的.NET包装属性以便于访问。

WPF DataContext

在C#的WPF(Windows Presentation Foundation)框架中,DataContext 是一个非常重要的概念,特别是在数据绑定(Data Binding)方面。DataContext 是一个对象,它包含了要绑定到UI元素(如TextBlock、ListBox、ComboBox等)的数据。这些数据可以是简单的数据类型(如string、int等),也可以是复杂的对象或集合。

DataContext 可以被设置为任何类型的对象,并且通常通过以下几种方式之一与UI元素相关联:

直接设置:你可以直接为某个UI元素(如UserControl、Window、Grid等)设置DataContext属性。这样,该元素及其所有子元素都可以访问这个DataContext中的数据。
csharp
myControl.DataContext = new MyViewModel();
继承:如果一个UI元素没有显式设置DataContext,它将从其父元素继承DataContext。这允许你在根元素(如Window或UserControl)上设置DataContext,并使其自动应用于所有子元素。
数据模板:在数据控件(如ListView、DataGrid等)中,你可以使用数据模板(Data Templates)来定义如何显示数据项。在这些模板中,你可以使用数据绑定表达式(如{Binding PropertyName})来引用当前数据项(即当前项的DataContext)的属性。
在MVVM(Model-View-ViewModel)架构中,DataContext 通常是一个ViewModel对象。ViewModel是一个充当数据中介的类,它将Model中的数据转换为适合在View中显示的形式,并处理来自View的用户输入。通过将数据绑定到ViewModel的属性,你可以轻松地将UI与数据隔离开来,从而实现更清晰的代码结构和更好的可维护性。

以下是一个简单的示例,展示了如何在WPF中设置和使用DataContext:

xml






csharp
// C# 代码
public class MyViewModel
{
public string MyProperty { get; set; } = “Hello, WPF!”;
}

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
}
在这个示例中,我们创建了一个名为MyViewModel的类,它有一个名为MyProperty的属性。然后,在MainWindow的构造函数中,我们将MyViewModel对象设置为窗口的DataContext。最后,在XAML文件中,我们使用数据绑定表达式{Binding MyProperty}将TextBlock的Text属性绑定到MyProperty属性上。当MyProperty属性的值改变时,TextBlock的文本将自动更新以反映新的值。

c# 给出4个C1ComboBox 其中其他值也跟着变化

在C#中,如果四个C1ComboBox(第三方库如C1 Studio for .NET中的控件)中的值能够相互关联,即改变一个C1ComboBox的值会导致其他C1ComboBox的值也相应地改变,你需要实现一种事件监听机制。

以下是一个基本的步骤说明和示例代码,展示如何实现这种功能:

定义事件处理函数:为每个C1ComboBox的SelectedIndexChanged或ValueChanged事件定义一个处理函数。
更新其他控件:在事件处理函数中,根据当前C1ComboBox的选中值来更新其他C1ComboBox的值或可选项。
避免无限循环:确保在更新其他C1ComboBox时不会触发它们自己的事件,从而导致无限循环。你可以通过设置某个标志或使用BeginInvoke来在UI线程上异步更新控件来避免这个问题。
下面是一个简化的示例代码:

csharp

public partial class MyForm : Form  
{  
    private bool isUpdating = false; // 用于防止无限循环的标志  
  
    public MyForm()  
    {  
        InitializeComponent();  
  
        // 假设你已经有四个C1ComboBox:comboBox1, comboBox2, comboBox3, comboBox4  
        comboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;  
        // 为其他三个也添加类似的事件监听  
    }  
  
    private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e)  
    {  
        if (isUpdating) return; // 如果正在更新,则直接返回  
  
        isUpdating = true; // 设置标志,表示开始更新  
  
        try  
        {  
            // 根据comboBox1的选中值来更新其他三个ComboBox  
            // 例如,如果comboBox1选中了某个值,你可能想要从某个数据源中获取与该值相关的其他选项  
            // 并设置到comboBox2, comboBox3, comboBox4中  
  
            // 伪代码示例:  
            // List<string> relatedOptions = GetDataRelatedTo(comboBox1.SelectedItem);  
            // comboBox2.DataSource = relatedOptions;  
            // comboBox3.DataSource = relatedOptions; // 根据实际情况可能需要不同的数据源  
            // comboBox4.DataSource = relatedOptions;  
  
            // ... 实际设置代码 ...  
        }  
        finally  
        {  
            isUpdating = false; // 更新完成,重置标志  
        }  
    }  
  
    // ... 为其他三个ComboBox也添加类似的事件处理函数 ...  
}
posted @ 2024-06-19 11:17  一点灯  阅读(0)  评论(0编辑  收藏  举报  来源