【问题标题】:Tracking if a form is dirty in UWP跟踪 UWP 中的表单是否脏
【发布时间】:2018-04-23 20:56:33
【问题描述】:

我正在尝试跟踪 UWP 中表单的脏(修改)状态,该表单在视图和模型之间使用两种方式绑定。

我当前跟踪相应更改事件的方法(文本框的 TextChange、组合框的 SelectionChanged 等)在页面加载数据时有效。

似乎正在发生的是,在页面完全加载后仍在评估绑定,从而使表单认为它已被修改。从单步执行我的代码开始,我在加载页面时构建了以下典型时间线:

  • 模型已初始化(dirty = false)
  • 数据被检索并放置在模型中
  • 绑定触发器,填写表单(dirty = true)
  • 页面的Loaded事件被命中,此时我设置dirty = false
  • 绑定继续设置,因此将脏设置为真
  • (我需要在这里设置一些东西来将dirty设置为false)

是否有在所有绑定第一次评估后触发的事件,或以某种方式跟踪绑定是否已全部评估?

编辑:一些粗略的示例代码可以更好地了解我是如何设置的

型号:

public class OrderModel : INotifyPropertyChanged
{ 
     private OrderCase order;
     public OrderCase Order
     {
        get
        {
            return order;
        }
        set
        {
            order = value;

            this.RaisePropertyChanged("Order");
        }
     }
    private bool dirty;
    public bool Dirty
    {
        get
        {
            return dirty;
        }
        set
        {
            dirty = value;
        }
    }
}

查看 - 代码隐藏

    OrderModel Model;
    int OrderId;
    protected async override void OnNavigatedTo(NavigationEventArgs e)
    {
       //Call a method in the View Model which calls a WCF service
       //to get the OrderCase object out of our database and into the
       //Order object in the model
       Model = new OrderModel();
       Model.Order = await WcfService.GetOrder(OrderId);
       //Once this returns all the bindings start evaluating
    }
    private void Page_Loaded(object sender, RoutedEventArgs e)
    {
       SetUndirty();
    }
    public override void TextBoxSetDirty(object sender, TextChangedEventArgs e)
    {
        //one of these exists for each type of control I use
        //this gets triggered when the text changes due to the model changing
        SetDirty();
    }
    public void SetDirty()
    {
        Model.Dirty = true;
    }

查看 - XAML

<TextBox 
 Text = "{x:Bind Path=Order.CustomerName, mode=TwoWay}"
 TextChanged="TextBoxSetDirty"/>

视图大体相同,包括 ComboBoxes、RadioButtons、ToggleSwitches 和 SyncFusion Datagrid

编辑 2: 我目前有一个解决方法,另一个名为“UserHasInteracted”的属性最初为 False,但在页面的 KeyDown 或 Tapped 事件触发时,它被设置为 true,并且 SetDirty 方法被修改为:

    public void SetDirty()
    {
        if (UserHasInteracted)
            Model.Dirty = true;
    }

最终结果是,在用户对其进行某种交互之前,该表单无法设置为“脏”。

这大约有 75% 的时间有效,并且不会产生误报(说表单不是脏的时候),但确实会产生一些误报,因为当 RadioButton 时,Tapped 事件似乎不会触发、 CheckBox 或 ToggleSwitch 被单击。如果用户执行的唯一交互是文本框中的单个按键,它也会失败,因为 TextChanged 事件在 KeyDown 之前触发,但是我们的用户只会在文本框中输入单个字符的可能性很小,因为他们的修改很小没有,所以这不是一个主要问题。

【问题讨论】:

  • 你不能在你的其他属性设置器中设置一个 IsDirty-Propertie 吗?只有当你的数据发生变化时,setter 才会被调用
  • 问题在于绑定到表单并存储在模型中的实际数据属于来自 WCF 数据协定的类型,因此对象中属性的设置器来自 WCF 无法访问模型的 IsDirty 字段。
  • 你怎么知道绑定的设置(最后一个要点)已经完成?这个问题的答案基本上取决于绑定设置后触发的任何事件。如果没有事件,这将是一场猜谜游戏。
  • 您可以添加一个方法来设置私有属性而不是公共属性,因此不会触发 NotifyPropertyChangedEvent
  • @Flater 我的理解是 Loaded 事件应该是在用户拥有控制权之前触发的最后一个事件,但是在 Loaded 事件之后仍然触发控件 on_change 事件,几乎就像一个竞争条件.

标签: c# uwp


【解决方案1】:

你的 ViewModel 是如何实例化的?

为你的班级:

public class OrderModel : INotifyPropertyChanged
{ 
     private OrderCase order;
     public OrderCase Order
     {
        get
        {
            return order;
        }
        set
        {
            if (value != order) { IsDirty = true; }
            order = value;

            this.RaisePropertyChanged("Order");
        }
     }
    private bool dirty;
    public bool Dirty
    {
        get
        {
            return dirty;
        }
        set
        {
            dirty = value;
        }
    }
}

这应该有效还是无效?

OrderModel Model;
int OrderId;
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
   //Call a method in the View Model which calls a WCF service
   //to get the OrderCase object out of our database and into the
   //Order object in the model
   var tempModel = new OrderModel();
   tempModel = await WcfService.GetOrder(OrderId);
   Model.Order = tempModel;
   //Setting the ViewModel's property only when getting order is completed
   //Once this returns all the bindings start evaluating
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
   SetUndirty();
}
public override void TextBoxSetDirty(object sender, TextChangedEventArgs e)
{
    //one of these exists for each type of control I use
    //this gets triggered when the text changes due to the model changing
    SetDirty();
}
public void SetDirty()
{
    Model.Dirty = true;
}

否则,您可以尝试在 ViewModel 中添加每个属性,而不是仅添加 OrderCase 的一个副本...只是一个想法

【讨论】:

  • 如果设置了整个 Order 对象,这将起作用,但是 OrderCase 类型中有一堆对象和属性,它们是绑定到视图的。订单中的各个对象,如字符串 CustomerName,在更改时需要将脏属性设置为 true。
  • 如何设置 Dirty 属性?你能给我们看一个更完整的代码吗?谢谢
  • 这不起作用有两个原因: 1. 尽管对 OnNavigatedTo 方法进行了更改,但仍会出现竞争条件。在 Page_Loaded 方法完成后,仍在评估绑定。 2. Order setter 中设置 Dirty 不起作用,因为 Order 对象只是在一开始就被设置,当 OrderCase 中的属性被设置时,setter 不会触发。
  • 如果您无法控制 OrderModel,您可能会修改您的 ViewModel 并公开 OrderModel 的每个属性,这样您就可以访问每个属性的设置器...不是最好的解决方案,我知道
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
  • 2015-04-26
相关资源
最近更新 更多