Ich glaube, ich habe ein Threading-Problem in meiner UWP-App. Ich möchte eine sehr einfache Sache tun:UWP: Ausnahme beim Einstellen einer Eigenschaft direkt nach dem Einstellen einer anderen
- eine Benutzeroberfläche mit 2 numerischen Feldern;
- Wenn ein numerischer Wert in
field1
eingegeben wird, möchte ichfield2
mit einem Verhältnis vonfield1
(Beispiel:field2 = ratio * field1
) festgelegt werden.
Ich verwende x:Bind
und Ereignisse. Aus unbekannten Gründen konnte ich im XAML das Ereignis TextChanging
nicht aufrufen, ohne beim Start eine Ausnahme zu haben. Daher verwende ich das Ereignis Loaded
.
Hier ist meine Modellklasse, einfach MyModel
genannt:
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;
}
}
}
Mein Viewmodel:
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));
}
}
mein Code hinter (Ich bin Filtern des Eingangs eine gegossene Ausnahme zu vermeiden):
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);
}
}
}
Schließlich meine 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" />
Zur Laufzeit, wenn ich ein nicht numerischen Zeichen in field1
eingeben, wird die Eingabe, gefiltert field1
kehrt zu seinem vorherigen Wert, ohne den Bildschirm „Blinken“ (das ist, warum ich das TextChanging
Ereignis verwenden und nicht die TextChanged
). Perfekt! Aber wenn ich ein numerisches Zeichen eingeben, wird field1
korrekt aktualisiert (ich, dass mit Breakpoint sehen), aber wenn field2
gesetzt ist, bekam ich eine native Ausnahme, wenn RaisedPropertyChanged
genannt wird:
ich zu ahnen, eine Art von Threading-Fehler, aber ich bin ziemlich neu in dieser Art von Entwicklung. Irgendeine Idee? Vielen Dank!
Danke für die ausführliche Antwort; Ich werde Schritt für Schritt gehen, wenn es dich nicht stört: Ich habe das "klassische" 'Binding' ausprobiert und es funktioniert ganz gut. Für das 'x: Bind 'verstehe ich, was Sie im ViewModel gemacht haben, aber das bedeutet, dass mein Modell" weg "ist? Ist es möglich, "Value1" und "Value2" in ein Modell zu setzen und es so zu machen? Für mich ist es mein Modell, das sagt "hey, Value2 muss immer Value1 * 3 sein", also ist es ein wenig verwirrend, diese Logik im ViewModel zu sehen. Ist das klar? – benichka
@benichka aktualisiert, um die Model-Klasse wie gewünscht zu trennen. –
Vielen Dank für die zusätzlichen Details, ich werde es versuchen. Dennoch bin ich immer noch ein wenig verwirrt, dass in meinem Modell meine Felder "string" und nicht "int" sind. Ich verstehe die MVVM wahrscheinlich noch nicht sehr gut, aber ich dachte, dass Modelle von der Sichtweise unabhängig sein müssen. Und in diesem Szenario ist dies nicht der Fall (wir haben es mit 'string' anstatt mit' int' zu tun, um eine 'argumentException' zu verhindern). Ich habe eine andere Frage gestellt, um dieses Problem zu umgehen. anscheinend ist eine der wenigen Möglichkeiten, einen Konverter zu verwenden. – benichka