上一次我们学习了Blazor组件相关的知识(Asp.net Core Blazor Webassembly - 组件)。这次继续学习Blazor的数据绑定相关的知识。当代前端框架都离不开数据绑定技术。数据绑定技术以数据为主导来驱动UI界面,用户对数据的修改会实时提现在UI上,极大的提高了开发效率,让开发者从繁琐的dom操作中解脱出来。对于数据绑定.NET开发者并不会陌生,WPF里大量应用数据绑定技术,有过WPF开发经验的同学其实很容易理解前端的数据绑定。总之数据绑定技术及其概念、思维极其重要。下面让我们看看Blazor的数据绑定技术。

单向绑定

Blazor的数据绑定官方文档是直接从双向绑定开始的,但我觉得有必要说一下单向绑定。因为其他框架一般都会区分单向、双向,比如vue的v-bind单向,v-model就是双向。我们这里分开讲也有利于跟其他框架进行对比。下面我们实现一个计数器组件来演示下单向数据绑定。

使用@进行绑定

@page "/counter"

Current count: @currentCountp>

<button class="btn btn-primary" @onclick="IncrementCount">Click mebutton>

@code {

private int currentCount = 0;

private void IncrementCount()

{

currentCount++;

}

}

这个Counter组件默认的项目就自带。跟我们使用服务端Razor一样,使用@符号在需要替换值的地方插入对应的变量。这个值就会被渲染在相应的地方。当我们在前端修改变量的时候,对应的ui界面会同步进行修改。

使用@bind-{attribute}进行绑定

除了直接使用@进行绑定,我们还可以使用@bind-{attribute}来实现对html元素属性的绑定,比如对style,class内容进行绑定。下面演示下对class进行绑定。我们把p元素的class绑定到“currentClass”字段。

@page "/counter"

Counterh1>

<p @bind-class="currentClass" @bind-class:event="onchange">

current count: @currentCount

p>

<button class="btn btn-primary" @onclick="IncrementCount">Click mebutton>

@code {

private string currentClass = "text-danger";

private int currentCount = 0;

private void IncrementCount()

{

currentCount++;

}

}

使用@bind-{attribute}进行绑定有个比较奇怪的问题,当你使用@bind-{attribute}进行绑定的时候必须同时指定@bind-{attribute}:event。@bind-{attribute}:event是用来指定双向绑定的时候控件在发生某个事件的时候回写值到绑定的字段上。可是p,div这种元素根本不可能会激发onchange,oninput这种事件,也不可能去修改绑定的字段的值,这个用法感觉有点多此一举。Blazor的单向数据绑定的用法跟ASP.NET Core MVC的Razor基本相似,不同点就是Blazor不需要Http回发到服务器就可以实时渲染新的界面出来。

双向绑定

双向绑定主要使用在一些输入控件上,比如input,select等。当我们对这些控件上的值进行修改后会回写绑定的字段。这种特性在表单场景中非常有用。我们定义一个用户信息编辑的组件来演示下:

@page "/infoedit"

userName: @userName

p>

sex: @sex

p>

userName: <input @bind="userName" />

p>

sex:

<select @bind="sex">

<option value="m">男option>

<option value="f">女option>

select>

p>

@code {

private string userName="abc";

private string sex="f";

}

当我们运行这个组件,在文本框进行修改后,鼠标点击其他地方让文本框失去焦点值就会回写到绑定的字段上,上面的单向绑定信息会自动同步。但是如果你用过VUE或者Angularjs的双向绑定就会觉得失去焦点再回写字段数据太慢了,一点也不酷。要知道VUE的双向绑定可是实时同步的,那么Blazor如何做到在输入的同时就更新值呢,答案是使用@bind:event来指定回写的激发事件,我们改成“oninput”事件就可以实现:

userName: @bind="userName" @bind:event="oninput"/>

双向绑定的多种写法

看到这里也许你也明白了,@bind真正的本质是由对value的绑定和对某个事件的绑定协同完成的。这点跟VUE非常相似。@bind其实是@bind-value的缩写,我们可以用@bind-value来实现双向绑定:

userName: @bind-value="userName" @bind-value:event="oninput"/>

以上写法的效果跟@bind一模一样。再进一步,@bind-value也只是对@的包装,我们可以使用@来实现双向绑定:

@page "/infoedit"

userName: @userName

p>

sex: @sex

p>

userName: <input value="@userName" @oninput="oninput"/>

p>

sex:

<select @bind="sex">

<option value="m">男option>

<option value="f">女option>

select>

p>

@code {

private string userName="abc";

private string sex="f";

private void oninput(ChangeEventArgs e)

{

userName = e.Value.ToString();

}

}

以上代码的效果跟@bind一模一样。通过使用@对value直接进行绑定以及绑定一个oninput事件进行值的回写,同样实现了双向绑定。

格式化时间字符串

使用@bind:format 可以对绑定时间类型字段的时候进行格式化:

出生日期:<input @bind="birthDay" @bind:format="yyyy-MM-dd" />

这个功能有点类似Angularjs的filter功能,但是目前只能对时间进行格式化,功能很弱。

父组件绑定数据到子组件

组件之间往往都是嵌套的,很多子组件都依赖父组件的数据来决定如何呈现,这种场景非常常见。我们还是继续修改上面的编辑组件,用户信息不在自己初始化,而是从父组件传递过来:
子组件:

====================child==================

userName: <input @bind="UserInfo.UserName" />

p>

sex:

<select @bind="UserInfo.Sex">

<option value="m">男option>

<option value="f">女option>

select>

p>

BrithDay:<input @bind="UserInfo.BrithDay" />

p>

@code {

[Parameter]

public UserInfo UserInfo { get; set; }

[Parameter]

public EventCallback<UserInfo> UserInfoChanged { get; set; }

}

子组件定义一个UserInfo对象并且使用[Parameter]进行标记,同时如果父组件使用@bind-UserInfo来绑定的话,还必须实现一个UserInfoChanged事件。
父组件:

@page "/"

====================parent==================

userName: @userInfo.UserName

p>

sex: @userInfo.Sex

p>

brithday: @userInfo.BrithDay

p>

<InfoEdit @bind-UserInfo="userInfo">InfoEdit>

@code {

private UserInfo userInfo;

protected override void OnInitialized()

{

userInfo = new UserInfo

{

UserName = "abc",

Sex = "f",

BrithDay = DateTime.Now

};

base.OnInitialized();

}

}

父组件初始化一个UserInfo对象后通过@bind-UserInfo绑定给子组件。

注意这里我们修改子组件的值并不会同步给父组件,所以可以看到@bind-UserInfo的传值还是单向的。

子组件传值给父组件 ??

原来我以为父组件使用@bind-UserInfo并且子组件实现了对应的changed方法就可以实现子组件跟父组件的自动传值,就跟input的双向绑定一样。但是不管我怎么试都没有卵用。如果只是单向的那为什么要这么大费周章?我直接使用属性赋值不就可以了么?像下面这样:

UserInfo="userInfo" >

直接通过组件的属性直接把父组件的数据传递到子组件,效果跟上面是一样的,而且这样子组件我还能少写一个changed事件。我原本以为使用基本类型,比如string可以自动双向绑定,然后并没有什么卵用。没有办法我继续尝试父组件监听UserInfoChanged事件来接受子组件的数据,然后VS提示我同一个事件不能绑定两次。

我已经无语了,难道要我再定义一个事件吗?于是我放弃了@bind-来实现子组件给父组件传值,我直接使用属性赋值难道不比这个简单吗?子组件修改数据的时候不断对外抛事件:

====================child==================

userName: <input @bind="UserInfo.UserName" @oninput="InvokeChanged"/>

p>

sex:

<select @bind="UserInfo.Sex">

<option value="m">男option>

<option value="f">女option>

select>

p>

BrithDay:<input @bind="UserInfo.BrithDay" />

p>

@code {

[Parameter]

public UserInfo UserInfo { get; set; }

[Parameter]

public EventCallback<UserInfo> UserInfoChanged { get; set; }

private void InvokeChanged()

{

UserInfoChanged.InvokeAsync(this.UserInfo);

Console.WriteLine("InvokeChanged");

}

}

父组件监听事件后更新数据:

@page "/"

====================parent```==================

userName: @userInfo.UserName

sex: @userInfo.Sex

brithday: @userInfo.BrithDay

title: @title

@code {

private UserInfo userInfo;

private string title;

protected override void OnInitialized()

{

userInfo = new UserInfo

{

UserName = "abc",

Sex = "f",

BrithDay = DateTime.Now

};

base.OnInitialized();

}

private void HandleUserInfoChanged(UserInfo info)

{

this.userInfo.UserName = info.UserName;

Console.WriteLine("HandleUserInfoChanged");

}

}

我原以为这样就没什么问题了,可奇怪的是,父组件页面重新渲染需要在子组件第二次修改数据后呈现且呈现的是前一次的。

到这里我已经无语了,最后我只能在子组件直接添加一个按钮,修改完后点击保存来触发InvokeChanged事件,这样子是可以的:

====================child==================

userName: <input @bind="UserInfo.UserName" />

p>

sex:

<select @bind="UserInfo.Sex">

<option value="m">男option>

<option value="f">女option>

select>

p>

BrithDay:<input @bind="UserInfo.BrithDay" />

p>

<button class="btn btn-danger" @onclick="InvokeChanged">保存button>

@code {

[Parameter]

public UserInfo UserInfo { get; set; }

[Parameter]

public EventCallback<UserInfo> UserInfoChanged { get; set; }

private void InvokeChanged()

{

UserInfoChanged.InvokeAsync(this.UserInfo);

Console.WriteLine("InvokeChanged");

}

}

到此数据绑定也演示完了,可是关于子组件往父组件传值的事我实在没像明白,难道是我哪里错了?

终于搞定子组件往父组件传值

接上面,当子组件绑定父组件的一个字段,并且子组件修改它的时候父组件不能实时进行同步更新UI的问题,最近终于在Blazui作者的指导下搞定了。

UserInfo类要实现INotifyPropertyChanged接口

public class UserInfo: INotifyPropertyChanged

{

private string _userName;

public string UserName {

get

{

return _userName;

}

set

{

_userName = value;

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));

}

}

public string Sex { get; set; }

public DateTime BrithDay { get; set; }

public event PropertyChangedEventHandler PropertyChanged;

}

没想到微软blazor还是借用了WPF搞MVVM的模式,模型需要实现INotifyPropertyChanged类,在属性发生修改的时候可以发出通知。

父组件订阅PropertyChanged事件:

@page "/"

====================parent```==================

userName: @userInfo.UserName

sex: @userInfo.Sex

brithday: @userInfo.BrithDay

title: @title

@code {

private UserInfo userInfo;

private string title;

protected override void OnInitialized()

{

userInfo = new UserInfo

{

UserName = "abc",

Sex = "f",

BrithDay = DateTime.Now

};

this.userInfo.PropertyChanged += (o, e) => StateHasChanged();

base.OnInitialized();

}

private void HandleUserInfoChanged(UserInfo info)

{

this.userInfo = info;

Console.WriteLine("HandleUserInfoChanged");

}

}

父组件订阅子组件的PropertyChanged事件,当事件发生的时候调用组件的StateHasChanged方法。StateHasChanged方法会通知组件说状态发生变化了,也就是说组件会被重新渲染。这就是最关键的东西了。

子组件

====================child==================

userName: <input @bind="UserInfo.UserName" @bind:event="oninput" />

p>

sex:

<select @bind="UserInfo.Sex">

<option value="m">男option>

<option value="f">女option>

select>

p>

BrithDay:<input @bind="UserInfo.BrithDay" />

p>

<button class="btn btn-danger" @onclick="InvokeChanged">保存button>

@code {

[Parameter]

public UserInfo UserInfo { get; set; }

[Parameter]

public EventCallback<UserInfo> UserInfoChanged { get; set; }

private void InvokeChanged()

{

UserInfoChanged.InvokeAsync(this.UserInfo);

Console.WriteLine("InvokeChanged");

}

}

运行

一些吐槽

虽然搞定了子父组件同步的问题,但是我不能理解的是,为什么微软要搞的这么复杂。使用@bind-UserInfo会强制用户在子组件实现一个 EventCallbackUserInfoChanged 事件。那么既然@bind:event="oninput"可以实时回写字段的值,那么为什么不直接同时调用UserInfoChanged对外抛事件呢?而且在父组件同样可以在编译器直接植入对UserInfoChanged事件的监听同时刷新UI。可能是微软为了性能,想要用户手工控制父组件的渲染时机吧。

相关内容:
ASP.NET Core Blazor 初探之 Blazor Server

ASP.NET Core Blazor 初探之 Blazor WebAssembly

关注我的公众号一起玩转技术

vs mfc数据与控件绑定错了_ASP.NET Core Blazor Webassembly 之 数据绑定相关推荐

  1. vs mfc数据与控件绑定错了_如何进行数据趋势分析?VS扩展工具——C1迷你图控件了解一下...

    点击"了解更多"获取ComponentOne 2020 v1正式版下载 迷你图 -- Sparklines是迷你的轻量级图表,有助于快速可视化数据. 它们是由数据可视化传奇人物Ed ...

  2. 动态字段无法双向绑定_ASP.NET Core Blazor Webassembly 之 数据绑定

    上一次我们学习了Blazor组件相关的知识(Asp.net Core Blazor Webassembly - 组件).这次继续学习Blazor的数据绑定相关的知识.当代前端框架都离不开数据绑定技术. ...

  3. vs2008 MFC使用DataGrid控件绑定数据库

    1.首先在对话框上点击右键,选择插入ActiveX控件,向下拖拉,找到MicrosoftDataGrid Control 6.0(SP6)(OLEDB),点击确定,在界面上会新增一个控件. 2.在界面 ...

  4. Asp.net--DropDownList控件绑定数据库数据

    DropDownList控件绑定数据库数据:DB是公共类,reDt是公共类中的方法 放在Page_Load事件中 if (!IsPostBack){DB db = new DB();DataTable ...

  5. QT给控件绑定数据:setProperty

    setProperty().property()函数 bool setProperty(const char *name, const QVariant &value); QVariant p ...

  6. repeater 控件ajax绑定数据源,【ASP.NET】第九课——使用DataList控件和Repeater控件绑定数据...

    知识点:掌握 DataList 控件呈现数据的方法: 掌握 Repeater 控件呈现数据的方法:掌握处理 Repeater 控件中的按钮事件. 1. 使用 DataList 控件呈现数据 在用 AS ...

  7. Winform开发之ComboBox和ComboBoxEdit控件绑定key/value数据

    使用 ComboBox 控件绑定key/value值: 因为 ComboBox 是有 DataSource 属性的,所以它可以直接绑定数据源,如 DataTable.ListItem 等. 使用 Da ...

  8. MFC之ComboBox控件用法

    本文以实例形式较为详细的讲述了MFC中ComboBox控件的用法.分享给大家供大家参考之用.具体方法如下: 一.ComboBox简介: ComboBox控件是由一个文本输入控件和一个下拉菜单组成的.用 ...

  9. 基于MFC的ActiveX控件开发

    转自:http://www.cnblogs.com/lidabo/archive/2012/12/04/2800921.html ActiveX 控件是基于组件对象模型 (COM) 的可重用软件组件, ...

最新文章

  1. ASP连接Access2013
  2. Flex Javascript 交互实现代码
  3. java ee io_JAVAEE细细看 进阶 14 - IO总结-Go语言中文社区
  4. Ansible 详解2-Playbook使用
  5. hdu 2648 Shopping
  6. python beautifulsoup4 table tr_python BeautifulSoup解析表
  7. java中String对象作为参数传递问题
  8. 第九十五题(推断一字符串是不是对称的)
  9. MySQL的基本命令
  10. js动态创建Form表单并提交
  11. 【元胞自动机】基于元胞自动机模拟交通流仿真含Matlab源码
  12. 随心测试_Python Se_007下拉列表操作2
  13. winform 读取用户控件的变量_VS2005winform程序中获取用户控件的值
  14. redhat linux 系统修复,简述rhel7系统修复
  15. 推荐几款牛逼Chrome网页翻译插件,功能强大内容惊艳,务必低调使用
  16. NetBIOS 漏洞的入侵与防御
  17. Python学习记录-项目案例实现:爬虫篇 02
  18. Vue 使用jsPlumb 实现连线绘图
  19. Apache Camel 了解一下?
  20. HTML5的内容模型及页面结构图

热门文章

  1. centos php mcrypt_Centos 建议使用epel源
  2. vscode 连接docker_在VS Code中使用带Docker容器的Java开发环境 – Bruno Borge
  3. 【Beta阶段】第四次Scrum Meeting
  4. SQL:查询重复出现记录
  5. 《掌握需求过程》——阅读笔记05
  6. 怎么看待MYSQL的性能
  7. redhat7下对用户账户的管理
  8. 使用Sublime Text 3做Python开发
  9. CentOS 最小化安装后安装桌面
  10. PCRE正则表达式语法