it编程 > App开发 > Windows Phone

详解WPF中MVVM架构中的多种数据绑定方式

15人参与 2025-09-26 Windows Phone

wpf基础框架

通过继承接口inotifypropertychangedicommand 来实现。

数据绑定部分

inotifypropertychanged原代码如下,wpf框架会自动注册datacontext对象中的propertychanged事件(若事件存在)。然后根据该事件修改前端属性。

namespace system.componentmodel
{
    // notifies clients that a property value has changed.
    public interface inotifypropertychanged
    {
        // occurs when a property value changes.
        event propertychangedeventhandler? propertychanged;
    }
}

注意:inotifypropertychanged接口并非是强制要求的,当不需要通过设置属性自动修改页面数据时,也就是不需要执行set方法时,不需要继承该接口。若使用了通知集合observablecollection,也是不需要专门通过事件通知页面的。

public class nativeviewmodel : inotifypropertychanged
{
    private string _name;

    public string name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                _name = value;
                onpropertychanged();
            }
        }
    }
    protected virtual void onpropertychanged([callermembername] string? propertyname = null)
    {
        propertychanged?.invoke(this, new propertychangedeventargs(propertyname));
    }
}
<stackpanel>
    <textbox text="{binding name}" />
</stackpanel>

数据绑定中的命令绑定

icommand 原代码如下,可以看到其中有按钮操作常用的几种属性:是否可用、可用性变化、触发事件。与前端通过click属性指定事件相比:一个是前端指定要执行的逻辑,一个是由vm来确定最终的执行逻辑,相当于是反转了控制方。可以根据需要使用、并非强制要求。

#nullable enable

using system.componentmodel;
using system.windows.markup;

namespace system.windows.input
{
    //
    // 摘要:
    //     defines a command.
    [typeconverter("system.windows.input.commandconverter, presentationframework, version=4.0.0.0, culture=neutral, publickeytoken=31bf3856ad364e35, custom=null")]
    [valueserializer("system.windows.input.commandvalueserializer, presentationframework, version=4.0.0.0, culture=neutral, publickeytoken=31bf3856ad364e35, custom=null")]
    public interface icommand
    {
        //
        // 摘要:
        //     occurs when changes occur that affect whether or not the command should execute.
        event eventhandler? canexecutechanged;

        //
        // 摘要:
        //     defines the method that determines whether the command can execute in its current
        //     state.
        //
        // 参数:
        //   parameter:
        //     data used by the command. if the command does not require data to be passed,
        //     this object can be set to null.
        //
        // 返回结果:
        //     true if this command can be executed; otherwise, false.
        bool canexecute(object? parameter);
        //
        // 摘要:
        //     defines the method to be called when the command is invoked.
        //
        // 参数:
        //   parameter:
        //     data used by the command. if the command does not require data to be passed,
        //     this object can be set to null.
        void execute(object? parameter);
    }
}

对于icommand属性,推荐使用方式是不要让其触发propertychanged,不应该被动态设置,而应该初始定好。不过如果使用场景确实需要,应该也是能生效的。

public class nativeviewmodel
{
    public icommand greetcommand { get; }

    public nativeviewmodel()
    {
        // 使用自定义的 relaycommand 需要自己实现
        greetcommand = new relaycommand();
    }
}

// 需要实现的简单命令类
public class relaycommand : icommand
{
    // 略
}
<stackpanel>
    <button content="say hello" command="{binding greetcommand}" />
</stackpanel>

communitytoolkit.mvvm 方式

communitytoolkit.mvvm 利用 c# 的源码生成器,在编译时自动生成inotifypropertychangedicommand的样板代码。需要nuget包communitytoolkit.mvvm

其仅在vm上有区别,在实际绑定方式上没有区别。

using communitytoolkit.mvvm.componentmodel;
using communitytoolkit.mvvm.input;

// 3. 使用 [observableobject] 特性或继承 observableobject
[observableobject]
public partial class toolkitviewmodel
{
    // 4. 使用 [observableproperty] 标记字段,自动生成名为 "name" 的属性
    [observableproperty]
    [notifycanexecutechangedfor(nameof(greetcommand))] // 当 name 改变时,通知 greetcommand 重新验证
    private string _name;

    [observableproperty]
    private string _greeting;

    // 5. 使用 [relaycommand] 自动生成名为 greetcommand 的 icommand 属性
    [relaycommand(canexecute = nameof(cangreet))]
    private void greet()
    {
        greeting = $"hello from toolkit, {name}!";
    }

    private bool cangreet() => !string.isnullorwhitespace(name);
}

reactiveui

reactiveui 将响应式编程理念引入 mvvm,核心是使用reactiveobjectwhenanyvalue等来声明数据流和反应关系。需要nuget包reactiveui.wpf

其仅在vm上有区别,在实际绑定方式上没有区别。

using reactiveui;
using system.reactive.linq;

// 6. 继承 reactiveobject
public class reactiveviewmodel : reactiveobject
{
    // 7. 使用 [reactive] 特性或 whenanyvalue
    private string _name;
    public string name
    {
        get => _name;
        set => this.raiseandsetifchanged(ref _name, value);
    }

    private readonly observableaspropertyhelper<string> _greeting;
    public string greeting => _greeting.value;

    // 8. 使用 reactivecommand 创建命令
    public reactivecommand<unit, unit> greetcommand { get; }

    public reactiveviewmodel()
    {
        // 判断命令何时可执行:当 name 不为空时
        var cangreet = this.whenanyvalue(x => x.name, name => !string.isnullorwhitespace(name));

        // 创建命令
        greetcommand = reactivecommand.createfromtask(
            execute: async () => { /* 可以执行异步操作 */ return $"hello from reactiveui, {name}!"; },
            canexecute: cangreet // 绑定可执行条件
        );

        // 9. 将命令的执行结果(一个iobservable<string>)订阅到 greeting 属性
        _greeting = greetcommand.toproperty(this, x => x.greeting, initialvalue: "waiting...");

        // 另一种更直接的写法(不通过命令结果):
        // greetcommand = reactivecommand.create(() => { greeting = $"hello from reactiveui, {name}!"; }, cangreet);
        // 但上面那种方式展示了将 iobservable 流转换为属性的强大能力。
    }
}

到此这篇关于详解wpf中mvvm架构中的多种数据绑定方式的文章就介绍到这了,更多相关wpf mvvm架构的数据绑定内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

您想发表意见!!点此发布评论

推荐阅读

WPF数据新增与更新的完整指南

08-14

电脑提示wpcap.dll丢失怎么办? wpcap.dll缺失的修复技巧

08-01

基于WPF实现自定义弹窗输入功能

07-02

WPF封装实现懒加载下拉列表控件(支持搜索)

04-30

Audition焦点指示器怎么调节亮度? Au更改焦点指示器亮度的技巧

04-27

使用WPF实现一个虚拟键盘的代码示例

04-27

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论