Avalonia代码后置中DataContext为空,如何更新DataGrid?
Avalonia中通过DataContext动态配置DataGrid的问题
应用结构
视图
- MainWindow
- MainView
- View1
- View2
视图模型
- MainViewModel
相关代码
MainWindow.xaml
<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:Frontend.ViewModels" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:views="clr-namespace:Frontend.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Frontend.Views.MainWindow" Icon="/Assets/avalonia-logo.ico" Title="Frontend"> <views:MainView/> </Window>
MainView.xaml
<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:Frontend.ViewModels" xmlns:views="clr-namespace:Frontend.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Frontend.Views.MainView" x:DataType="vm:MainViewModel"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.3*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="0.4*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <views:View1 Grid.Row="0" Grid.Column="0"/> <views:View2 Grid.Row="1" Grid.Column="1"/> </Grid> </UserControl>
View1.xaml
<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:Frontend.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Frontend.Views.View1" x:DataType="vm:MainViewModel"> <DataGrid Name="TableDataGrid" AutoGenerateColumns="False" IsReadOnly="True" GridLinesVisibility="All" CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserSortColumns="True" BorderThickness="1" BorderBrush="Gray" VerticalAlignment="Center" HorizontalAlignment="Center"/> </UserControl>
问题代码(View1.axaml.cs)
using Avalonia.Controls; using Avalonia.Data; using Frontend.ViewModels; using System.Data; namespace Frontend.Views; public partial class View1 : UserControl { public View1() { InitializeComponent(); var viewModel = DataContext as MainViewModel; TableDataGrid.ItemsSource = viewModel.TableData.DefaultView; foreach (DataColumn x in viewModel.TableData.Columns) { if (x.DataType == typeof(bool)) { TableDataGrid.Columns.Add(new DataGridCheckBoxColumn { Header = x.ColumnName, Binding = new Binding($"Row.ItemArray[{x.Ordinal}]") }); } else { TableDataGrid.Columns.Add(new DataGridTextColumn { Header = x.ColumnName, Binding = new Binding($"Row.ItemArray[{x.Ordinal}]") }); } } } }
问题描述
在View1的构造函数中尝试通过DataContext获取MainViewModel时,DataContext为空。虽然View2能正常绑定MainViewModel的属性,说明父级DataContext是有效的,但View1初始化时上下文尚未完成赋值,导致无法配置DataGrid。
解决方案
方案1:监听DataContextChanged事件
控件构造时DataContext还未从父级继承,因此需要订阅DataContextChanged事件,在上下文赋值完成后再执行DataGrid的配置逻辑:
using Avalonia.Controls; using Avalonia.Data; using Frontend.ViewModels; using System.Data; namespace Frontend.Views; public partial class View1 : UserControl { public View1() { InitializeComponent(); // 订阅DataContext变化事件 DataContextChanged += OnDataContextChanged; } private void OnDataContextChanged(object? sender, EventArgs e) { var viewModel = DataContext as MainViewModel; // 空值校验,避免异常 if (viewModel == null || viewModel.TableData == null) return; // 清除已有列,防止重复添加 if (TableDataGrid.Columns.Count > 0) TableDataGrid.Columns.Clear(); TableDataGrid.ItemsSource = viewModel.TableData.DefaultView; foreach (DataColumn x in viewModel.TableData.Columns) { if (x.DataType == typeof(bool)) { TableDataGrid.Columns.Add(new DataGridCheckBoxColumn { Header = x.ColumnName, Binding = new Binding($"Row.ItemArray[{x.Ordinal}]") }); } else { TableDataGrid.Columns.Add(new DataGridTextColumn { Header = x.ColumnName, Binding = new Binding($"Row.ItemArray[{x.Ordinal}]") }); } } } }
方案2:适配异步加载的场景
如果MainViewModel中的TableData是异步加载的,还需要监听ViewModel的属性变化,确保数据加载完成后更新DataGrid:
首先让MainViewModel实现INotifyPropertyChanged:
using System.ComponentModel; using System.Data; namespace Frontend.ViewModels; public class MainViewModel : INotifyPropertyChanged { private DataTable _tableData; public DataTable TableData { get => _tableData; set { _tableData = value; OnPropertyChanged(nameof(TableData)); } } public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } // 示例数据加载方法 public void LoadData() { // 模拟异步加载(实际场景可能用Task) TableData = new DataTable(); // 添加列和数据逻辑... } }
然后在View1中同时监听DataContext变化和ViewModel的属性变化:
using Avalonia.Controls; using Avalonia.Data; using Frontend.ViewModels; using System.ComponentModel; using System.Data; namespace Frontend.Views; public partial class View1 : UserControl { private MainViewModel? _currentViewModel; public View1() { InitializeComponent(); DataContextChanged += OnDataContextChanged; } private void OnDataContextChanged(object? sender, EventArgs e) { // 取消订阅旧ViewModel的事件 if (_currentViewModel != null) _currentViewModel.PropertyChanged -= ViewModel_PropertyChanged; _currentViewModel = DataContext as MainViewModel; if (_currentViewModel == null) return; // 订阅新ViewModel的属性变化事件 _currentViewModel.PropertyChanged += ViewModel_PropertyChanged; // 初始配置DataGrid ConfigureDataGrid(_currentViewModel); } private void ViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e) { // 当TableData属性变化时重新配置 if (e.PropertyName == nameof(MainViewModel.TableData)) { var viewModel = sender as MainViewModel; ConfigureDataGrid(viewModel); } } private void ConfigureDataGrid(MainViewModel? viewModel) { if (viewModel == null || viewModel.TableData == null) return; TableDataGrid.Columns.Clear(); TableDataGrid.ItemsSource = viewModel.TableData.DefaultView; foreach (DataColumn x in viewModel.TableData.Columns) { if (x.DataType == typeof(bool)) { TableDataGrid.Columns.Add(new DataGridCheckBoxColumn { Header = x.ColumnName, Binding = new Binding($"Row.ItemArray[{x.Ordinal}]") }); } else { TableDataGrid.Columns.Add(new DataGridTextColumn { Header = x.ColumnName, Binding = new Binding($"Row.ItemArray[{x.Ordinal}]") }); } } } }
内容的提问来源于stack exchange,提问作者user107586
相关产品推荐
相关产品推荐

