【发布时间】:2017-03-27 01:22:48
【问题描述】:
如果我使用类型转换器将 RadioButton 绑定到视图模型属性,则每次创建视图时,都会调用前一个 ViewModel 上的设置器,即使该视图已卸载且不应再存在。这是重现该问题的最少代码:
1) 定义一个枚举类型:
enum EnumType {
Value1,
Value2,
}
2) 定义一个转换器:
public class EnumTypeToBooleanConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, string language) {
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, string language) {
return EnumType.Value1;
}
}
3) 定义视图模型:
class ViewModel : INotifyPropertyChanged {
private EnumType value;
public ViewModel() {
Debug.WriteLine(string.Format("ViewModel ({0})::ctor", this.GetHashCode()));
}
public EnumType Value {
get {
Debug.WriteLine(string.Format("ViewModel ({0})::Value::get", this.GetHashCode()));
return this.value;
}
set {
Debug.WriteLine(string.Format("ViewModel ({0})::Value::set", this.GetHashCode()));
if (this.value != value) {
this.value = value;
this.OnPropertyChanged();
}
}
}
private void OnPropertyChanged([CallerMemberName] string name = null) {
if (this.PropertyChanged != null) {
var ea = new PropertyChangedEventArgs(name);
this.PropertyChanged(this, ea);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
4) 定义一个用户控件 (View.xaml)
<UserControl
x:Class="BindingIssue.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BindingIssue"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
x:Name="root">
<UserControl.DataContext>
<local:ViewModel x:Name="ViewModel"/>
</UserControl.DataContext>
<Grid>
<ScrollViewer>
<StackPanel>
<RadioButton x:Name="rdo1"
Content="Value1"
IsChecked="{Binding Path=Value, Converter={StaticResource EnumTypeToBooleanConverter}, ConverterParameter=Value1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button x:Name="btnClose"
Click="btnClose_Click"
Content="Close"/>
</StackPanel>
</ScrollViewer>
</Grid>
5) 在 View 后面添加代码:
public View() {
Debug.WriteLine(string.Format("View ({0})::ctor", this.GetHashCode()));
this.InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void btnClose_Click(object sender, RoutedEventArgs e) {
if (this.Parent is Popup) {
Debug.WriteLine("Closing the popup...");
((Popup)this.Parent).IsOpen = false;
}
}
private void OnLoaded(object sender, RoutedEventArgs e) {
Debug.WriteLine(string.Format("View ({0})::Loaded", this.GetHashCode()));
}
private void OnUnloaded(object sender, RoutedEventArgs e) {
Debug.WriteLine(string.Format("View ({0})::Unloaded", this.GetHashCode()));
}
6) 主页 (XAML)
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
x:Name="Grid">
<Button x:Name="btnNewView"
Click="btnNewView_Click"
Content="New View"
Margin="4"/>
</Grid>
7) 将事件处理程序添加到 MainPage
private void btnNewView_Click(object sender, RoutedEventArgs e) {
Debug.WriteLine("Opening a new popup...");
View view = new View();
view.HorizontalAlignment = HorizontalAlignment.Center;
view.VerticalAlignment = VerticalAlignment.Center;
Popup popup = new Popup();
popup.Child = view;
popup.HorizontalOffset = 300;
popup.VerticalOffset = 300;
popup.IsOpen = true;
}
多次打开和关闭弹窗会产生以下输出(请注意哈希码):
正在打开一个新的弹出窗口...
查看 (46418718)::ctor
ViewModel (59312528)::ctor
ViewModel (59312528)::Value::get
查看 (46418718)::加载
关闭弹出窗口...
查看 (46418718)::Unloaded
正在打开一个新的弹出窗口...
查看 (58892413)::ctor
ViewModel (61646925)::ctor
ViewModel (61646925)::Value::get
ViewModel (59312528)::Value::set
查看 (58892413)::加载
关闭弹出窗口...
查看 (58892413)::卸载
这意味着在 Unloaded 视图模型中创建的 ViewModel 的 setter 被调用,这有点奇怪。 x:Bind 和 Binding 的这种行为是相同的。
我想知道是否有对此行为的解释。
澄清更多: 每次都会创建一对全新的视图/视图模型实例,但是当加载新视图时,会调用前一个视图模型实例的设置器。视图的前一个实例已卸载,此时甚至不应该存在。 (想想每次都关闭的弹出窗口,并且没有事件引用旧视图/视图模型。)
【问题讨论】:
标签: xaml uwp win-universal-app uwp-xaml