gpt4 book ai didi

c# - 数据绑定(bind)引擎如何在后台运行?

转载 作者:IT王子 更新时间:2023-10-29 04:34:36 26 4
gpt4 key购买 nike

从技术上讲,数据绑定(bind)引擎如何在后台运行?尤其是,数据绑定(bind)中“同步器”的机制是如何工作的?

在.NET,Java,Flex等许多框架中,它们提供了数据绑定(bind)引擎。我一直在使用API​​调用,所以对我来说,每件事都很容易,因为我要做的就是对API进行调用。

现在,我有兴趣尝试为我正在开发的游戏编写一个相对简单的数据绑定(bind)引擎。尽管我使用的是C#,但仍有一些原因无法使用内置的WinForms和数据绑定(bind)引擎(请参阅下面的背景信息以了解原因)。由于我无法使用C#中的现有数据绑定(bind)引擎,因此我认为可能必须自己编写一个。因此,我需要了解数据绑定(bind)通常在幕后如何工作的精妙细节。这样,我并不是说如何在C#中使用数据绑定(bind)。我的意思是,数据绑定(bind)如何在内部和体系结构上工作。

我试图在网络上搜索有关数据绑定(bind)的教程和文章,但大多数结果是如何在C#中使用现有数据绑定(bind),这并不是我想要的。

因此,在开始计划编写自己的数据绑定(bind)器之前,我想我需要知道数据绑定(bind)引擎如何在后台运行?甚至更重要的是,数据绑定(bind)引擎中“同步器”的机制外观和工作方式如何,即,在单向或双向绑定(bind)中,数据如何始终保持同步?

为什么我问这个问题的一些背景信息:

不久前,我就如何在不使用标准WinForms的UI中在C#中使用数据绑定(bind)做了question。我得到的答案是C#中的数据绑定(bind)引擎与WPF/Windows Forms UI紧密结合。因此,我想我不能使用C#中的现有数据绑定(bind)引擎,而必须自己创建一个。这样做的目的是为了游戏,而我正在努力。游戏通常具有自己的自定义UI(非WinForm)。我的意图是为游戏中的UI和游戏对象设置类似于MVVM的设计。

最佳答案

您的问题是一个非常有趣的问题,但范围实际上很大。

在这种情况下,一个非常有用的工具是ILSpy,它使您可以查看框架的实现。

我要处理的一件事是以下语句:

The answer I got was that the data binding engine in C# is tightly coupled with the WPF/Windows Forms UI



我不同意;数据绑定(bind)引擎与.Net事件实现紧密耦合,但是Target和Source可以是任何东西-大多数示例将是Windows Forms,WPF或ASP.Net,因为它们是.Net语言最常见的前端,但是也可以在没有UI的其他情况下使用多重绑定(bind)。

添加双向绑定(bind)时会发生什么?好吧,如果我们查看 MultiBinding的源代码,则会注意到一些有趣的事情:
  • 它公开了描述绑定(bind)方案的BindingMode属性-通常为OneWayTwoWay
  • 它公开了两个有趣的事件:NotifyOnSourceUpdatedNotifyOnTargetUpdated

  • 其中具有基本形式:
    // System.Windows.Data.MultiBinding
    /// <summary>Gets or sets a value that indicates whether to raise the <see cref="E:System.Windows.FrameworkElement.SourceUpdated" /> event when a value is transferred from the binding target to the binding source.</summary>
    /// <returns>true if the <see cref="E:System.Windows.FrameworkElement.SourceUpdated" /> event will be raised when the binding source value is updated; otherwise, false. The default value is false.</returns>
    [DefaultValue(false)]
    public bool NotifyOnSourceUpdated
    {
    get
    {
    return base.TestFlag(BindingBase.BindingFlags.NotifyOnSourceUpdated);
    }
    set
    {
    bool flag = base.TestFlag(BindingBase.BindingFlags.NotifyOnSourceUpdated);
    if (flag != value)
    {
    base.CheckSealed();
    base.ChangeFlag(BindingBase.BindingFlags.NotifyOnSourceUpdated, value);
    }
    }
    }

    即我们使用事件来告诉我们何时更新源( OneWay)和何时更新目标(用于 TwoWay绑定(bind))

    请注意,还有一个 PriorityBinding类,该类的运行方式类似,不同之处在于您可以预订多个数据源,并且它将优先处理最早返回数据的数据源。

    因此,它的工作方式很清楚-创建绑定(bind)时,我们订阅的是一侧(用于只读更新)或两侧(例如,可以在GUI中更改数据并发送回)的更改到数据源),并通过事件管理所有通知。

    下一个问题是,实际上,谁来管理事件?简单的答案是目标和源都可以。这就是为什么实现 INotifyPropertyChanged如此重要的原因-例如,所有Bindings真正要做的就是为双方应如何订阅彼此的更改创建契约(Contract)-实际上,这是Target和Source紧密结合的契约(Contract)。

    ObservableCollection是一个有趣的测试案例,因为它已广泛用于GUI应用程序中,用于将数据源中的更新升级到UI,并将UI中的数据更改发送回基础数据源。

    注意(通过查看代码),用于传达事物已发生更改的实际事件真的非常简单,但是用于管理“添加”,“删除”,“更新”的代码实际上非常依赖于SimpleMonitor属性( BlockReentrancyCheckReentrancy)的一致性-这是有效的保证操作是原子的,并向订户通知其发生顺序的变化,并且基础集合与更新的集合保持一致。

    这确实是整个操作的棘手部分。

    简而言之,.Net中的DataBinding实现并没有与GUI技术紧密耦合。只是大多数示例都会在Windows Forms,WPF或ASP.Net应用程序的上下文中呈现DataBinding。实际的数据绑定(bind)是事件驱动的,对于您来说,利用它,同步和管理对数据的更改更为重要-DataBinding框架仅允许您通过契约(Contract)将目标和源耦合在一起以共享数据进行更新(接口(interface))。

    玩得开心 ;-)

    编辑:

    我坐下并创建了两个类 MyCharacterMyCharacterAttribute,其明确目的是在 HealthHealthValue属性之间设置TwoWay数据绑定(bind):
    public class MyCharacter : DependencyObject
    {
    public static DependencyProperty HealthDependency =
    DependencyProperty.Register("Health",
    typeof(Double),
    typeof(MyCharacter),
    new PropertyMetadata(100.0, HealthDependencyChanged));

    private static void HealthDependencyChanged(DependencyObject source,
    DependencyPropertyChangedEventArgs e)
    {
    }

    public double Health
    {
    get
    {
    return (double)GetValue(HealthDependency);
    }
    set
    {
    SetValue(HealthDependency, value);
    }
    }

    public void DrinkHealthPotion(double healthRestored)
    {
    Health += healthRestored;
    }
    }

    public class MyCharacterAttributes : DependencyObject
    {
    public static DependencyProperty HealthDependency =
    DependencyProperty.Register("HealthValue",
    typeof(Double),
    typeof(MyCharacterAttributes),
    new PropertyMetadata(100.0, HealthAttributeDependencyChanged));

    public double HealthValue
    {
    get
    {
    return (Double)GetValue(HealthDependency);
    }
    set
    {
    SetValue(HealthDependency, value);
    }
    }

    public List<BindingExpressionBase> Bindings { get; set; }

    public MyCharacterAttributes()
    {
    Bindings = new List<BindingExpressionBase>();
    }

    private static void HealthAttributeDependencyChanged(DependencyObject source,
    DependencyPropertyChangedEventArgs e)
    {
    }
    }

    这里要注意的最重要的事情是从 DependencyObject的继承和 DependencyProperty的实现。

    在实践中,将发生以下情况。我创建了一个简单的WPF表单,并设置了以下代码:
    MyCharacter Character { get; set; }

    MyCharacterAttributes CharacterAttributes = new MyCharacterAttributes();

    public MainWindow()
    {
    InitializeComponent();

    Character = new MyCharacter();
    CharacterAttributes = new MyCharacterAttributes();

    // Set up the data binding to point at Character (Source) and
    // Property Health (via the constructor argument for Binding)
    var characterHealthBinding = new Binding("Health");

    characterHealthBinding.Source = Character;
    characterHealthBinding.NotifyOnSourceUpdated = true;
    characterHealthBinding.NotifyOnTargetUpdated = true;
    characterHealthBinding.Mode = BindingMode.TwoWay;
    characterHealthBinding.IsAsync = true;

    // Now we bind any changes to CharacterAttributes, HealthDependency
    // to Character.Health via the characterHealthBinding Binding
    var bindingExpression =
    BindingOperations.SetBinding(CharacterAttributes,
    MyCharacterAttributes.HealthDependency,
    characterHealthBinding);

    // Store the binding so we can look it up if necessary in a
    // List<BindingExpressionBase> in our CharacterAttributes class,
    // and so it "lives" as long as CharacterAttributes does, too
    CharacterAttributes.Bindings.Add(bindingExpression);
    }

    private void HitChracter_Button(object sender, RoutedEventArgs e)
    {
    CharacterAttributes.HealthValue -= 10.0;
    }

    private void DrinkHealth_Button(object sender, RoutedEventArgs e)
    {
    Character.DrinkHealthPotion(20.0);
    }

    单击HitCharacter按钮会使 CharacterAttributes.HealthValue属性减少10。这将触发一个事件,该事件通过我们之前设置的Binding也从 Character.Health值中减去10.0。按下DrinkHealth按钮会将 Character.Health恢复20.0,还将 CharacterAttributes.HealthValue增加20.0。

    还要注意,这些东西确实已经烘焙到UI框架中- FrameworkElement(继承自 UIElement)已实现 SetBindingGetBinding。这很有道理-DataBinding GUI元素对于用户界面而言是完美的方案!但是,如果您看得更深一些,例如 SetValue只是在内部接口(interface)上调用 BindingOperations.SetBinding,因此我们可以实现它而不必实际使用 UIElement(按照上面的示例)。但是,我们必须保留的依赖项是 DependencyObjectDependencyProperty-这些是数据绑定(bind)正常工作所必需的,但是,只要您的对象是从 DependencyObject继承的,就不需要在文本框附近的任何地方:- )

    但是,不利之处在于某些Binding东西是通过 internal方法实现的,因此您可能会遇到以下情况:要实现的绑定(bind)操作可能需要您编写其他代码,因为您根本无法访问框架实现,例如 native 类可以。但是,如上所述,TwoWay数据绑定(bind)是完全可能的。

    关于c# - 数据绑定(bind)引擎如何在后台运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12950084/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com