MAUI.NET 更新失焦点时的绑定。

huangapple go评论93阅读模式
英文:

MAUI.NET update binding on lost focus alike

问题

以下是翻译好的部分:

introduction:

使用 MAUI.NET 与 MVVM 架构,但 MVVM 可以被牺牲(这对应用程序非常重要)。标准方法 - 我有一个 ViewModel,其中包含一个具有 get 和 set 实现 INotifyPropertyChanged 的属性,并将其绑定到具有所述 ViewModel 的 BindingContext 的视图中的 Entry 的 Text 属性。

need:

需要在用户退出 Entry(单击控件外部的某处或在键盘上按 TAB 键)后执行绑定(设置属性)。默认绑定情况是,在用户每次按键时执行绑定。

example:

如果用户进入 Entry 并输入 1234,则 VM 中绑定的属性的 set 函数会在每次按键后触发 4 次。我希望在用户完成输入后仅触发一次。

tries:

似乎很基础... 我已经尝试过:

  • 查找 UpdateSourceTrigger="LostFocus" 的等效项(就像我们在 WPF 中做的那样)
  • 尝试使用 UpdateSourceEventName 与 CompletedUnfocused
  • 尝试手动更新此绑定 - 我已经捕捉到用户离开 Entry 的时刻,通过检查 Unfocused 事件的第二个参数 private void Entry_Unfocused(object sender, FocusEventArgs e) { if(!e.IsFocused) { ... 但无法找到 BindingOperations.GetBindingExpression 的等效项(就像我们在 WPF 中做的那样),而且在 sender as Entry 上只有 SetBinding 而没有可以获取它的内容...
  • 搜索了将近 2 小时...

EDIT: my workaround:

我已经成功实现了一个类似以下的疯狂解决方法:

  • 在我的 ViewModel 中,我创建了一个额外的属性,称为 ValueTemp,它绑定到 Entry 的 Text 属性。视图绑定到此属性。
  • ViewModel 中的旧 Value 属性绑定到 Placeholder(必须绑定到某些内容以触发 getter,也许我会使用转换器或某些不可见的标签进行多重绑定),在其 getter 中,我正在将值设置为 ValueTemp
  • 在 Entry_Unfocused 事件期间,我正在获取 ValueTemp 并将其设置为旧的 Value 属性。在我的情况下,Completed 似乎不会触发。
  • 有一些用于正确的 PropertyChanged 和双向同步的技巧。
  • 实际上,我的代码要复杂得多,这就是为什么很难在这里放置一些代码。但总的来说,我在 VM 中使用了一个额外的属性,并将其绑定到视图以供用户完成编辑后将其值设置为我的适当属性,以便稍后进行处理。
英文:

introduction:

Using MAUI.NET with MVVM architecture, but MVVM can be sacrifice (this need is so important for the application). Standard way - I have ViewModel with a property with get and set implementing INotifyPropertyChanged and binded this one to the Text property of the Entry in the Views that have BindingContext of the mentioned ViewModel.

need:

Need binding to be executed (set property called) after the user go out of the Entry (click somewhere outside of the control or push TAB on keyboard). Default binding situation is that the binding is executed every keystroke the user do.

example:

If the user enters Entry and make input 1234 the set funciton on binded property in VM is fired 4 times, after every key pushed. I want it to fire just once after user ends with this Entry control.

tries:

Seems to be so basic... I already tried:

  • find equivalent for UpdateSourceTrigger="LostFocus" (like we did in WPF)
  • trying to use UpdateSourceEventName with Completed or Unfocused
  • trying to manually update this binding - I have managed to catch the moment of the user leaving the Entry by checking second parameter of Unfocused event private void Entry_Unfocused(object sender, FocusEventArgs e) { if(!e.IsFocused) { ... but can't find equivalent to BindingOperations.GetBindingExpression (like we did in WPF), moreover on sender as Entry there is only SetBinding while nothing to get it...
  • googled for almost 2 hours...

EDIT: my workaround:

I have managed to do crazy workaround like that:

  • In my ViewModel I have done an additional property like ValueTemp which is binded to Text property of the Entry. The View is binded to this property.
  • The old Value property from ViewModel is binded to Placeholder (must be binded to something to trigger getter, maybe I would do multibinding with converter or some non visible label) and during its getter I am setting the value to ValueTemp
  • During Entry_Unfocused event I am acquiring ValueTemp and setting it to the old Value property. Somehow Completed does not trigger in my case.
  • There are some tricks for proper PropertyChanged and two way synchronization.
  • In reality my code is much more complex, that is why it is hard to put some code here. But in general I use an additional property in VM and bind it for Views purposes, when user finishes editing it I set its value to my proper property so it can be processed later on.

答案1

得分: 2

以下是您提供的代码的中文翻译:

你可以按照MAUI文档中描述的方法编写行为。

在这个行为中,你声明了一个附加属性,用于与ViewModel绑定,并订阅输入框的Unfocused事件以更新附加属性。

public class EntryLostFocusBehavior : Behavior<Entry>
{
    public static readonly BindableProperty TextProperty = BindableProperty.CreateAttached(
        "Text",
        typeof( string ),
        typeof( EntryLostFocusBehavior ),
        null,
        BindingMode.TwoWay,
        propertyChanged: TextPropertyChanged );

    public static string GetText( Entry entry ) => (string)entry.GetValue( TextProperty );
    public static void SetText( Entry entry, string value ) => entry.SetValue( TextProperty, value );

    private static void TextPropertyChanged( BindableObject bindable, object oldValue, object newValue )
    {
        var entry = (Entry)bindable;
        entry.Placeholder = newValue as string;
    }

    protected override void OnAttachedTo( Entry bindable )
    {
        base.OnAttachedTo( bindable );
        bindable.Unfocused += Entry_Unfocused;
    }

    protected override void OnDetachingFrom( Entry bindable )
    {
        base.OnDetachingFrom( bindable );
        bindable.Unfocused -= Entry_Unfocused;
    }

    private void Entry_Unfocused( object sender, FocusEventArgs e )
    {
        var entry = (Entry)sender;
        SetText( entry, entry.Text );
    }
}

并在你的视图中使用它:

<Page ...
      xmlns:b="clr-namespace:MyMauiApp.Behaviors"
      ...>

  <Entry b:EntryLostFocusBehavior.Text="{Binding Value1}">
      <Entry.Behaviors>
          <b:EntryLostFocusBehavior />
      <Entry.Behaviors>
  </Entry>

  <Entry b:EntryLostFocusBehavior.Text="{Binding Value2}">
      <Entry.Behaviors>
          <b:EntryLostFocusBehavior />
      <Entry.Behaviors>
  </Entry>

</Page>

这就是视图/视图模型的全部内容。没有代码后台,也没有额外的属性。

英文:

You can write a behavior for this as described in the MAUI documentation.

In this behavior you declare an attached property for the binding with your ViewModel and subscribe to the Unfocused event of the entry to update the attached property.

public class EntryLostFocusBehavior : Behavior<Entry>
{
    public static readonly BindableProperty TextProperty = BindableProperty.CreateAttached(
        "Text",
        typeof( string ),
        typeof( EntryLostFocusBehavior ),
        null,
        BindingMode.TwoWay,
        propertyChanged: TextPropertyChanged );

    public static string GetText( Entry entry ) => (string)entry.GetValue( TextProperty );
    public static void SetText( Entry entry, string value ) => entry.SetValue( TextProperty, value );

    private static void TextPropertyChanged( BindableObject bindable, object oldValue, object newValue )
    {
        var entry = (Entry)bindable;
        entry.Placeholder = newValue as string;
    }

    protected override void OnAttachedTo( Entry bindable )
    {
        base.OnAttachedTo( bindable );
        bindable.Unfocused += Entry_Unfocused;
    }

    protected override void OnDetachingFrom( Entry bindable )
    {
        base.OnDetachingFrom( bindable );
        bindable.Unfocused -= Entry_Unfocused;
    }

    private void Entry_Unfocused( object sender, FocusEventArgs e )
    {
        var entry = (Entry)sender;
        SetText( entry, entry.Text );
    }
}

and use this in your view

<Page ...
      xmlns:b="clr-namespace:MyMauiApp.Behaviors"
      ...>

  <Entry b:EntryLostFocusBehavior.Text="{Binding Value1}">
      <Entry.Behaviors>
          <b:EntryLostFocusBehavior />
      <Entry.Behaviors>
  </Entry>

  <Entry b:EntryLostFocusBehavior.Text="{Binding Value2}">
      <Entry.Behaviors>
          <b:EntryLostFocusBehavior />
      <Entry.Behaviors>
  </Entry>

</Page>

And thats it for the view/viewmodel. No code-behind, no additional properties.

huangapple
  • 本文由 发表于 2023年5月21日 17:10:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76299115.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定