2017-03-02 104 views
1

我认为我在UWP应用程序中遇到了线程问题。 我想做一个非常简单的事情:UWP:在设置另一个属性后设置属性时的异常

  • 一个带2个数字字段的UI;
  • 如果在field1中键入数字值,我希望field2设置为field1(例如:field2 = ratio * field1)的比率。

我正在使用x:BindTextChanging事件。由于不明原因,我无法在XAML中“调用”TextChanging事件,而无需在启动时发生异常。因此,我正在使用Loaded事件。

这里是我的模型类,简称MyModel

public class MyModel : INotifyPropertyChanged 
{ 
    private readonly uint r1 = 3; 

    private uint _field1; 
    public uint Field1 
    { 
     get { return this._field1; } 
     set 
     { 
      this.Set(ref this._field1, value); 

      if (value == 0) 
      { 
       Field2 = 0; 
      } 
      else 
      { 
       Field2 = value * r1; 
      } 
     } 
    } 

    private uint _field2; 
    public uint Field2 
    { 
     get { return this._field2; } 
     set 
     { 
      this.Set(ref this._field2, value); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void RaisedPropertyChanged([CallerMemberName]string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    protected bool Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null) 
    { 
     if (Equals(storage, value)) 
     { 
      return false; 
     } 
     else 
     { 
      storage = value; 
      this.RaisedPropertyChanged(propertyName); 
      return true; 
     } 
    } 
} 

我的视图模型:

public class MyModelViewModel : INotifyPropertyChanged 
{ 
    public MyModel MyModel { get; set; } 

    public MyModelViewModel() 
    { 
     // Initialisation de notre page 
     this.MyModel = new MyModel() 
     { 
      Field1 = 0 
     }; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

我后面的代码(我过滤输入,以避免铸造除外):

public sealed partial class MainPage : Page 
{ 
    public MyModelViewModel ViewModel { get; set; } = new MyModelViewModel(); 

    public MainPage() 
    { 
     this.InitializeComponent(); 
    } 

    private void InitField1(object sender, Windows.UI.Xaml.RoutedEventArgs e) 
    { 
     field1.TextChanging += field1_TextChanging; 
    } 

    private void InitField2(object sender, Windows.UI.Xaml.RoutedEventArgs e) 
    { 
     field2.TextChanging += field2_TextChanging; 
    } 

    private void field1_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args) 
    { 
     var error = errorTextBlock; 
     error.Visibility = Windows.UI.Xaml.Visibility.Collapsed; 

     Regex regex = new Regex("[^0-9]+"); // All but numeric 
     if (regex.IsMatch(sender.Text)) 
     { 
      error.Text = "Non numeric char"; 
      error.Visibility = Windows.UI.Xaml.Visibility.Visible; 
      sender.Text = this.ViewModel.MyModel.Field1.ToString(); 
     } 
     else 
     { 
      this.ViewModel.MyModel.Field1 = Convert.ToUInt32(sender.Text); 
     } 
    } 

    private void field2_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args) 
    { 
     var error = errorTextBlock; 
     error.Visibility = Windows.UI.Xaml.Visibility.Collapsed; 

     Regex regex = new Regex("[^0-9]+"); 
     if (regex.IsMatch(sender.Text)) 
     { 
      error.Text = "Non numeric char"; 
      error.Visibility = Windows.UI.Xaml.Visibility.Visible; 
      sender.Text = this.ViewModel.MyModel.Field2.ToString(); 
     } 
     else 
     { 
      this.ViewModel.MyModel.Field2 = Convert.ToUInt32(sender.Text); 
     } 
    } 
} 

最后,我的XAML:

<TextBlock Grid.Row="0" Grid.Column="0" x:Name="errorTextBlock" Text="" Visibility="Collapsed" /> 

<TextBlock Grid.Row="1" Grid.Column="0" Text="Field 1" /> 
<TextBox Grid.Row="1" Grid.Column="1" x:Name="field1" Text="{x:Bind ViewModel.MyModel.Field1, Mode=OneWay}" Loaded="InitField1" /> 
<TextBlock Grid.Row="2" Grid.Column="0" Text="Field 2" /> 
<TextBox Grid.Row="2" Grid.Column="1" x:Name="field2" Text="{x:Bind ViewModel.MyModel.Field2, Mode=OneWay}" Loaded="InitField2" /> 

在运行时,如果我输入field1非数字字符,输入过滤,field1返回先前没有屏幕“闪烁”的值(这就是为什么我使用TextChanging事件,而不是TextChanged)。完善!但是,如果我输入数字字符,field1正确更新(我可以看到,断点),但是当field2设置,我当RaisedPropertyChanged被称为本地异常: exception

我怀疑某种线程错误,但我对这种开发很新颖。任何想法?谢谢!

回答

1

更新使用一个单独的“模型”类

下面是当进入到一个数字(整数),另一个文本框显示所输入的数字乘以另一个号码如何创建一个文本框。

以下是用户界面。请注意用于每个绑定的Mode,第二个文本框是只读的,因为这仅用于显示。

在网页上我宣布我的模型

public MyViewModel ViewModel { get; set; } = new MyViewModel(); 

我的视图模型很简单

public class MyViewModel 
{ 
    public MyModel MyModel { get; set; } = new MyModel(); 
} 

模型类包含的逻辑

public class MyModel : INotifyPropertyChanged 
{ 
    private string _value1; 

    public string Value1 
    { 
     get { return _value1; } 
     set 
     { 
      if (_value1 != value) 
      { 
       _value1 = value; 

       // Cause the updated value to be displayed on the UI 
       OnPropertyChanged(nameof(Value1)); 

       // Is the entered value a number (int)? 
       int numericValue; 
       if (int.TryParse(value, out numericValue)) 
       { 
        // It's a number so set the other value 
        // multiplied by the ratio 
        Value2 = (numericValue * 3).ToString(); 
       } 
       else 
       { 
        // A number wasn't entered so indicate this 
        Value2 = "NaN"; 
       } 

       // Cause the updated value2 to be displayed 
       OnPropertyChanged(nameof(Value2)); 
      } 
     } 
    } 

    // We can use the automatic property here as don't need any logic 
    // relating the getting or setting this property 
    public string Value2 { get; set; } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

通过以上,当输入一个数字给Value1 t时如果Value2将显示数量的三倍(因为我将比率设置为3)。

您可能会注意到,如果您尝试以上更改不会立即发生,并且Value2仅在焦点离开Value1文本框时才会更新。这是因为,默认情况下,双向绑定仅在焦点丢失时才会更新。这可以很容易地改变。

如果不是使用新的x:Bind绑定方法,而是使用传统的Binding方法,我们可以强制绑定在我们想要的时候更新。说,当文字被改变时。

修改文本框的声明是这样的:

 <TextBox Text="{Binding ViewModel.Value1, Mode=TwoWay}" 
       TextChanged="TextBox_OnTextChanged" /> 

注意,绑定语法是不同的,我们增加了一个事件。
事件的处理程序是

private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e) 
{ 
    var be = (sender as TextBox).GetBindingExpression(TextBox.TextProperty); 
    be.UpdateSource(); 
} 

这迫使绑定更新,但有一点大家必须和其他的变化。
使用x:Bind语法尝试绑定到页面。使用旧的Binding语法,它将绑定到页面的DataContext。为了使这些相同的,更新的页面构造这样

public MainPage() 
{ 
    this.InitializeComponent(); 
    this.DataContext = this; 
} 

现在,应用程序将重新工作和值2将在值1文本框中每次按键后更新。

+0

感谢您的详细回复;如果它不打扰你,我会一步一步来:我尝试了“经典”的“绑定”,它工作得很好。对于'x:Bind',我明白你在ViewModel中做了什么,但这意味着我的模型“已经消失”了?是否可以将'Value1'和'Value2'放入模型中并使其以同样的方式工作?对我来说,这是我的模型,它说:“嗨,Value2必须始终是Value1 * 3”,所以看到ViewModel中的逻辑有点混乱。我清楚了吗? – benichka

+0

@benichka根据需要更新以分离出Model类。 –

+0

感谢您提供更多详情,我会尽力而为。不过,我仍然有点困惑,在我的模型中,我的字段是“字符串”而不是“int”。我可能还没有很好地理解MVVM,但我认为模型必须独立于视图之外?在这种情况下,情况并非如此(我们正在处理'string'而不是'int'来防止'argumentException')。我问了另一个问题来解决这个问题。显然唯一的选择是使用转换器。 – benichka