解决.NET Framework4.8 WPF中IsAsync绑定与MahApps全选冲突
问题:WPF DataGrid异步绑定与MahApps CellEditAssist全选功能冲突
在基于.NET Framework 4.8的WPF应用中,为优化DataGrid性能给列绑定设置IsAsync=true,但该设置与MahApps的mah:DataGridHelper.EnableCellEditAssist="True"功能冲突:编辑单元格时,先显示选中的回退值,异步加载绑定值后光标跳至文本开头,无法保持正常全选状态;仅当IsAsync=false时全选功能正常。
临时解决方案
找到一种可行的临时解决办法:借助Binding的NotifyOnTargetUpdated属性(需设为true),通过自定义附加属性监听TargetUpdated事件,在绑定值更新后重新聚焦并全选文本。
原冲突场景代码
XAML布局
<UserControl ... xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" ... > ... <DataGrid CanUserDeleteRows="False" Panel.ZIndex="0" ScrollViewer.IsDeferredScrollingEnabled="False" Visibility="{Binding ShowSalesLines, Converter={StaticResource BooleanToVisibilityConverter}}" Grid.Column="0" ItemsSource="{Binding CurrentSalesLines, IsAsync=True, UpdateSourceTrigger=PropertyChanged}" x:Name="SalesLinesDatagrid" SelectionMode="Extended" PreviewKeyDown="SalesLinesDatagrid_PreviewKeyDown" VirtualizingPanel.IsVirtualizingWhenGrouping="True" EnableRowVirtualization="True" VirtualizingPanel.IsVirtualizing="True" EnableColumnVirtualization="True" CanUserAddRows="False" AutoGenerateColumns="False" SelectedItem="{Binding CurrentSaleLine, Mode=TwoWay}" Style="{StaticResource MahApps.Styles.DataGrid.Azure}" OverridesDefaultStyle="False" Margin="10" IsReadOnly="false" CanUserReorderColumns="False" CanUserResizeRows="False" CanUserSortColumns="False" VirtualizingPanel.IsContainerVirtualizable="True" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.ScrollUnit="Pixel" SnapsToDevicePixels="True" VirtualizingPanel.CacheLengthUnit="Pixel" ScrollViewer.PanningDeceleration="5" > <DataGrid.Columns> <DataGridTextColumn VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.IsContainerVirtualizable="True" VirtualizingPanel.IsVirtualizing="True" Header="Prec. Unit." Width="auto" VirtualizingPanel.ScrollUnit="Pixel" MinWidth="75" mah:DataGridHelper.EnableCellEditAssist="True" EditingElementStyle="{StaticResource DataGridTextEditingStyle}" ElementStyle="{StaticResource DataGridTextColumnHorizontalRight}" Binding="{Binding SALESPRICE, StringFormat='0.000', ConverterCulture=es-ES, UpdateSourceTrigger=LostFocus, IsAsync=True, Mode=TwoWay, ValidatesOnDataErrors=True, FallbackValue='0'}" /> </DataGrid.Columns> </DataGrid> ... </UserControl>
样式定义
<Style x:Key="DataGridTextColumnHorizontalRight" TargetType="TextBlock" > <Setter Property="HorizontalAlignment" Value="Right" /> </Style> <Style x:Key="DataGridTextEditingStyle" TargetType="TextBox" BasedOn="{StaticResource MahApps.Styles.TextBox.DataGrid.Editing}" > <Setter Property="mah:DataGridHelper.EnableCellEditAssist" Value="True" /> <Setter Property="mah:TextBoxHelper.ClearTextButton" Value="False" /> </Style>
ViewModel属性
private decimal _salesPrice; [Column(TypeName = "numeric")] public decimal SALESPRICE { get { return _salesPrice; } set { SetProperty(ref _salesPrice, Math.Round(value, 3, MidpointRounding.AwayFromZero)); OnPropertyChanged(new PropertyChangedEventArgs(nameof(LINEAMOUNT))); } }
修复方案代码
自定义附加属性辅助类
public static class TextBoxBehavior { public static readonly DependencyProperty SelectAllOnGotFocusProperty = DependencyProperty.RegisterAttached( "SelectAllOnGotFocus", typeof(bool), typeof(TextBoxBehavior), new UIPropertyMetadata(false, OnSelectAllOnGotFocusChanged)); public static bool GetSelectAllOnGotFocus(DependencyObject obj) { return (bool)obj.GetValue(SelectAllOnGotFocusProperty); } public static void SetSelectAllOnGotFocus(DependencyObject obj, bool value) { obj.SetValue(SelectAllOnGotFocusProperty, value); } private static void OnSelectAllOnGotFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is TextBox textBox) { if ((bool)e.NewValue) { textBox.GotFocus += TextBox_GotFocus; textBox.TargetUpdated += TextBox_TargetUpdated; textBox.LostFocus += TextBox_LostFocus; } else { textBox.GotFocus -= TextBox_GotFocus; textBox.TargetUpdated -= TextBox_TargetUpdated; textBox.LostFocus -= TextBox_LostFocus; } } } private static void TextBox_GotFocus(object sender, RoutedEventArgs e) { if (sender is TextBox textBox) { textBox.SelectAll(); } } private static void TextBox_TargetUpdated(object sender, DataTransferEventArgs e) { if (sender is TextBox textBox) { textBox.Dispatcher.Invoke(() => { textBox.Focus(); textBox.SelectAll(); }); } } private static void TextBox_LostFocus(object sender, RoutedEventArgs e) { if (sender is TextBox textBox) { textBox.GotFocus -= TextBox_GotFocus; textBox.TargetUpdated -= TextBox_TargetUpdated; textBox.LostFocus -= TextBox_LostFocus; } } }
修改后的样式与绑定
新编辑样式
<Style x:Key="SelectAllTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource MahApps.Styles.TextBox.DataGrid.Editing}" > <Setter Property="control:TextBoxBehavior.SelectAllOnGotFocus" Value="True" /> <Setter Property="mah:TextBoxHelper.ClearTextButton" Value="False" /> </Style>
更新后的DataGrid列绑定
<DataGridTextColumn VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.IsContainerVirtualizable="True" VirtualizingPanel.IsVirtualizing="True" Header="Precio Unitario" VirtualizingPanel.ScrollUnit="Pixel" EditingElementStyle="{StaticResource SelectAllTextBoxStyle}" Binding="{Binding SALESPRICE, StringFormat='0.000', ConverterCulture=es-ES, IsAsync=True, UpdateSourceTrigger=LostFocus, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnTargetUpdated=True}" ElementStyle="{StaticResource DataGridTextColumnHorizontalRight}" />
最终选择
上述临时方案虽能解决问题,但并非推荐做法,最终将绑定设置回IsAsync=false。
内容的提问来源于stack exchange,提问作者Yago
相关产品推荐
相关产品推荐

