WPF MVVM中同一窗口不同用户控件绑定公共模型UI不显示数据问题
核心问题原因
- 多个用户控件各自在内部初始化了ViewModel实例,和主窗口绑定的共用ViewModel不是同一个对象,主窗口更新的属性无法同步到用户控件
- 选中项变更时重新实例化Model对象,而非更新现有实例属性,且绑定的集合未实现变更通知,UI无法感知数据更新
修复方案
1. 统一ViewModel实例
删除RuleSuggestionMetaData、RuleSuggestionAvalonEditor、RuleTreeView三个用户控件内部所有对DataContext的赋值代码,包括XAML中的DataContext配置、后台构造函数中的DataContext = new xxxViewModel()逻辑,让用户控件自动继承主窗口的DataContext,保证所有控件共用同一个ViewModel实例。
2. 完善属性变更通知
- 所有用于绑定的属性都实现
INotifyPropertyChanged接口,属性值修改时触发PropertyChanged事件通知UI更新 - 所有列表绑定的集合类型改用
ObservableCollection<T>,不要使用List<T>,后者不会主动通知UI集合内容变更 - 选中项变更时不要重新实例化
RuleMetadata或绑定的Model对象,直接修改现有实例的属性值即可
3. 优化选中变更命令逻辑
你当前使用的ListBox为单选模式,无需额外传递SelectedItems参数,直接读取已双向绑定的RuleMetadata.SelectedRule属性即可,命令执行逻辑参考:
// 命令执行方法 private void ExecuteRuleSuggestionSelection() { if(RuleMetadata.SelectedRule == null) return; // 直接更新现有属性,不要new新的Model实例 CurrentRuleMetaData = RuleMetadata.SelectedRule.MetaData; CurrentRuleScript = RuleMetadata.SelectedRule.ScriptContent; CurrentRuleTreeNodes = new ObservableCollection<TreeNode>(RuleMetadata.SelectedRule.TreeNodeList); }
对应的XAML中可以删掉命令的Parameter配置:
<interactivity:InvokeCommandAction Command="{Binding RuleSuggestionSelectionCommand}" />
4. 修正绑定路径和布局冲突
- 所有用户控件内部的绑定路径要和主ViewModel的公开属性保持一致,比如MetaData用户控件内的输入框绑定要写
{Binding CurrentRuleMetaData.RuleName},不要使用用户控件内部独立ViewModel的路径 - 删除
RuleSuggestionAvalonEditor的固定Width="537"配置,该配置和HorizontalAlignment="Stretch"冲突,会导致布局异常
修正后的页面XAML代码
<Grid > <Grid.ColumnDefinitions> <ColumnDefinition Width="4*"/> <ColumnDefinition Width="17*"/> </Grid.ColumnDefinitions> <Border BorderThickness="1" BorderBrush="Black" Margin="5,5,5.4,5.2" Grid.ColumnSpan="2"> <Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" Margin="0 3 0 3" BorderThickness="1" BorderBrush="Gray"> <Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="300"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="310"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="2*"/> </Grid.ColumnDefinitions> <!--ROW 2--> <GroupBox Grid.Row="1" Grid.Column="0" Grid.RowSpan="3" Header="Rule Name" > <ListBox ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Visible" BorderBrush="Gray" BorderThickness="1" Style="{x:Null}" Name="lstSuggestedRule" SelectionMode="Single" ItemsSource="{Binding RuleMetadata.SuggestedRuleList, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectedItem="{Binding RuleMetadata.SelectedRule,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="0 3 0 3" > <ListBox.ItemTemplate> <DataTemplate> <Grid> <TextBlock Width="350" HorizontalAlignment="Stretch" Text="{Binding Path=ExpressionName}"> </TextBlock> </Grid> </DataTemplate> </ListBox.ItemTemplate> <interactivity:Interaction.Triggers> <interactivity:EventTrigger EventName="SelectionChanged"> <interactivity:InvokeCommandAction Command="{Binding RuleSuggestionSelectionCommand}" /> </interactivity:EventTrigger> </interactivity:Interaction.Triggers> </ListBox> </GroupBox> <GroupBox Grid.Row="1" Grid.Column="1" Header="Rule MetaData" Grid.ColumnSpan="2" > <DockPanel> <local:RuleSuggestionMetaData /> </DockPanel> </GroupBox> <GroupBox Grid.Row="2" Grid.Column="1" Header="Rule Script" Grid.ColumnSpan="2"> <DockPanel> <local:RuleSuggestionAvalonEditor HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Visibility="{Binding ElementName=lstSuggestedRule,Path=SelectedItem,Converter={StaticResource SuggestionVisibilityConverter},ConverterParameter= VB}" Margin="0,10,0,10.2" /> <local:RuleTreeView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Visibility="{Binding ElementName=lstSuggestedRule,Path=SelectedItem,Converter={StaticResource SuggestionVisibilityConverter} ,ConverterParameter= XML}" /> </DockPanel> </GroupBox> </Grid> </Border> <Grid Grid.Row="1" Grid.Column="1" Margin="0 0 10 3" HorizontalAlignment="Right"> <Grid.RowDefinitions> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Button Grid.Row="0" Grid.Column="3" Width="100" Height="25" Margin="0,3,0,3" ToolTip="Click to Cancel Operation" Content="Close" Command="{Binding CancelRuleSuggestion}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" > </Button> </Grid> </Grid> </Border> </Grid>
内容的提问来源于stack exchange,提问作者Ankita Gupta
相关产品推荐
相关产品推荐

