WPF跨页面同名属性绑定报错:双向绑定无法作用于只读属性
问题场景
我有两个WPF页面TunerPage.xaml和FormCameraSetting.xaml,FormCameraSetting通过Frame嵌入到TunerPage中,核心代码如下:
TunerPage.xaml
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="300"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <UniformGrid Grid.Column="0" Rows="1" Columns="2"> <Label Content="Shutter" FontFamily="Arial" Foreground="White" VerticalContentAlignment="Center"/> <TextBox Style="{StaticResource TextBoxStyle1}" Text="{Binding Shutter, Mode=OneWay}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsEnabled ="False"/> </UniformGrid> <Frame Grid.Column="1" Content="{Binding PageDisplay}" NavigationUIVisibility="Hidden" Loaded="Frame_Loaded" Unloaded="Frame_Unloaded"/> </Grid>
FormCameraSetting.xaml
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="300"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label Content="Shutter" FontFamily="Arial" Foreground="White" VerticalContentAlignment="Center"/> <TextBox HorizontalContentAlignment="Right" Style="{StaticResource TextBoxStyleForm2}" Text="{Binding Shutter, UpdateSourceTrigger=Explicit}"/> </Grid>
两个页面的TextBox绑定同名Shutter属性,但对应不同ViewModel:
TunerPage的TextBox绑定TunerAction.cs的Shutter,模式为OneWay,该属性只读(仅包含getter):
public double Shutter { get { return 2000; } }
FormCameraSetting的TextBox绑定CameraSettingViewModel.cs的Shutter,默认TwoWay模式,该属性读写(包含getter和setter):
private double shutter; public double Shutter { get { return shutter; } set { shutter = value; } }
我在页面的Loaded事件中为各自设置对应的ViewModel作为DataContext:
- TunerPage.xaml.cs:
public TunerAction tunerAction = new TunerAction(); public TunerPage() { InitializeComponent(); } private void SettingPageBase_Loaded(object sender, RoutedEventArgs e) { this.DataContext = tunerAction; }
- FormCameraSetting.xaml.cs:
public CameraSettingViewModel viewModel = new CameraSettingViewModel(); public FormCameraSetting() { InitializeComponent(); } private void CameraSettingTemplate_Loaded(object sender, RoutedEventArgs e) { this.DataContext = viewModel; }
运行时报错:A TwoWay or OneWayToSource binding cannot work on the read-only property,但修改TunerAction.cs中Shutter的名称,或者在InitializeComponent()之后直接设置DataContext而非在Loaded事件中设置,就不会触发报错。
报错原因
1. DataContext继承与绑定解析时机冲突
WPF在执行InitializeComponent()时会解析XAML中的绑定表达式,但此时FormCameraSetting的DataContext尚未通过Loaded事件设置,它会向上继承父容器(TunerPage)的DataContext。而TunerPage的DataContext也是在自身Loaded事件中才赋值,所以在FormCameraSetting解析绑定的阶段,它临时继承了TunerPage后续会设置的TunerAction实例作为DataContext。
2. 默认绑定模式与只读属性不兼容
FormCameraSetting的TextBox绑定Shutter时未显式指定Mode,WPF对TextBox.Text的绑定默认是TwoWay模式。此时绑定指向的是TunerAction的只读Shutter属性(仅包含getter,无setter),双向绑定需要属性支持写入,因此触发“双向绑定无法作用于只读属性”的错误。
3. 规避方式的原理
- 修改
TunerAction中Shutter的名称:此时FormCameraSetting的绑定无法在继承的父级DataContext中找到同名属性,不会触发错误,等自身DataContext设置完成后,才会绑定到CameraSettingViewModel的读写Shutter属性。 - 在
InitializeComponent()后直接设置DataContext:FormCameraSetting在解析绑定表达式前就已经拥有了自己的DataContext,不会再继承父级的DataContext,自然不会绑定到只读属性上,避免报错。
内容的提问来源于stack exchange,提问作者Ricky Chen

