AvaloniaUI中UserControl无法绑定对应ViewModel的问题咨询
问题描述
我正在使用AvaloniaUI和C# .NET开发应用,主窗口MainWindow绑定了MainWindowViewModel,并通过TabControl集成了两个UserControl。希望为每个UserControl分配独立的ViewModel,目前已为SettingsView创建了SettingsViewModel,并在其axaml文件中设置了命名空间与DataContext,但ViewModel始终未加载。
相关源码如下:
MainWindow.axaml
<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:MyApp.ViewModels" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:views ="clr-namespace:MyApp.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="MyApp.Views.MainWindow" WindowStartupLocation="CenterScreen" Icon="/Assets/Programmicon.png" Title="{Binding WindowTitle}" CanResize="False" > <Design.DataContext> <vm:MainWindowViewModel/> </Design.DataContext> <Window.Styles> <Style Selector="TabItem"> <Setter Property="FontSize" Value="16"/> <Setter Property="FontWeight" Value="Bold"/> <Setter Property="Height" Value="34"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Background" Value="#148198"/> <Setter Property="Foreground" Value="White"/> <Setter Property="Margin" Value="0 0 0 0"/> <Setter Property="Padding" Value="10 0"/> </Style> <Style Selector="TabControl WrapPanel"> <Setter Property="Background" Value="#148198"/> </Style> <Style Selector="TabItem:selected"> <Setter Property="Background" Value="White"/> <Setter Property="Foreground" Value="Black"/> <Setter Property="Margin" Value="0 0 0 0"/> <Setter Property="Padding" Value="10 0"/> </Style> </Window.Styles> <Grid> <TabControl Name="tabMenu" Background="White"> <TabItem Header="Import" VerticalContentAlignment="Center" > <views:ImportView/> </TabItem> <TabItem Header="Einstellungen" VerticalContentAlignment="Center"> <views:SettingsView/> </TabItem> </TabControl> <Label Name="lblErrorInfo" Content="" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" ZIndex="10" Background="Red" Foreground="White" FontSize="34" FontWeight="Bold" IsVisible="false"></Label> </Grid> </Window>
SettingsView.axaml
<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="using:MyApp.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="MyApp.Views.SettingsView"> <this.DataContext> <vm:SettingsViewModel/> </this.DataContext> <DataGrid Name="gEventlog" Items="{Binding EventlogData}" AutoGenerateColumns="False" CanUserResizeColumns="False" CanUserReorderColumns="False" Background="LightGray" CanUserSortColumns="True" Canvas.Top="20" Width="1024" Height="626" GridLinesVisibility="All" IsReadOnly="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" AlternatingRowBackground="Azure"> <DataGrid.Columns> <DataGridTextColumn Header="ID"/> <DataGridTextColumn Header="Zeitstempel"/> <DataGridTextColumn Header="Event-Typ"/> <DataGridTextColumn Header="Benutzer"/> <DataGridTextColumn Width="563" Header="Fehlermeldung"/> <DataGridTextColumn Header="Funktion"/> </DataGrid.Columns> </DataGrid> </UserControl>
SettingsViewModel.cs
using MyApp.Classes; using Microsoft.Data.Sqlite; using System; using System.Data; namespace MyApp.ViewModels { public class SettingsViewModel : ViewModelBase { // The appconfig class private readonly AppConfiguration _appConfig; // The utils class private readonly Utils _utils; // The eventlog class private readonly Eventlog _event; private string _importFilesPath; // The data of the eventlog-grid private DataView _eventlogData; public DataView EventlogData { get { return _eventlogData; } set { if (_eventlogData == value) { return; } _eventlogData = value; } } public string ImportFilesPath { get { return _importFilesPath; } set { if (_importFilesPath == value) { return; } _importFilesPath = value; } } public SettingsViewModel() { // Initialize the members _appConfig = new AppConfiguration(); _utils = new Utils(); _event = new Eventlog(); _eventlogData = new DataView(); _importFilesPath = ""; this.InitializeGUI(); } private void InitializeGUI() { //Fill the eventlog grid LoadEventlog(); _importFilesPath = _appConfig.ImportPath; } } }
解决方案
1. 修正XAML中DataContext的设置语法
Avalonia XAML中不能使用<this.DataContext>来设置当前控件的DataContext,正确写法是使用<UserControl.DataContext>:
修改SettingsView.axaml中的DataContext设置部分:
<UserControl.DataContext> <vm:SettingsViewModel/> </UserControl.DataContext>
2. 确保ViewModel属性触发PropertyChanged通知
你的SettingsViewModel继承了ViewModelBase(假设该基类已实现INotifyPropertyChanged接口),但属性的setter中没有触发属性变更通知,导致UI无法感知数据变化。需要在属性赋值后调用通知方法:
修改SettingsViewModel.cs中的属性:
public DataView EventlogData { get { return _eventlogData; } set { if (_eventlogData == value) { return; } _eventlogData = value; OnPropertyChanged(); // 或 OnPropertyChanged(nameof(EventlogData)) } } public string ImportFilesPath { get { return _importFilesPath; } set { if (_importFilesPath == value) { return; } _importFilesPath = value; OnPropertyChanged(); } }
同时,InitializeGUI方法中要通过属性而非字段赋值,确保触发通知:
private void InitializeGUI() { //Fill the eventlog grid LoadEventlog(); ImportFilesPath = _appConfig.ImportPath; // 改为属性赋值 }
3. (推荐)通过父ViewModel托管子ViewModel
这是更符合MVVM架构的做法,便于父子ViewModel间的通信,步骤如下:
步骤1:在MainWindowViewModel中添加子ViewModel属性
public class MainWindowViewModel : ViewModelBase { // 托管子ViewModel public SettingsViewModel SettingsVm { get; } = new SettingsViewModel(); public ImportViewModel ImportVm { get; } = new ImportViewModel(); // 其他原有属性... public string WindowTitle { get; set; } = "你的应用标题"; }
步骤2:在MainWindow.axaml中绑定UserControl的DataContext
<TabItem Header="Import" VerticalContentAlignment="Center" > <views:ImportView DataContext="{Binding ImportVm}"/> </TabItem> <TabItem Header="Einstellungen" VerticalContentAlignment="Center"> <views:SettingsView DataContext="{Binding SettingsVm}"/> </TabItem>
这种方式下,SettingsView.axaml中可以移除直接设置DataContext的代码,由父ViewModel统一管理子ViewModel的生命周期。
内容的提问来源于stack exchange,提问作者GeoCoder
相关产品推荐
相关产品推荐

