You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 09:55:19