2017-03-02 99 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