自己写一个ObjectDataProvider类
ObjectDataProvider.cs代码
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Xml.Linq;
using System.Xml.XPath;
using Avalonia.Platform;
using System.Diagnostics;
using System.Linq;
namespace Shares.Avalonia
{
public partial class ObjectDataProvider : ObservableObject
{
[ObservableProperty]
private Type? objectType;
[ObservableProperty]
private string? methodName;
[ObservableProperty]
private string? source;
[ObservableProperty]
private string? xPath;
[ObservableProperty]
private object? data;
private bool isRefreshing;
public ObjectDataProvider()
{
PropertyChanged += (_, e) =>
{
if (e.PropertyName is nameof(ObjectType)
or nameof(MethodName)
or nameof(Source)
or nameof(XPath))
{
Refresh();
}
};
}
public void Refresh()
{
if (isRefreshing)
return;
isRefreshing = true;
try
{
if (ObjectType != null && !string.IsNullOrWhiteSpace(MethodName))
{
Data = CallObjectMethod();
return;
}
if (!string.IsNullOrWhiteSpace(Source) && !string.IsNullOrWhiteSpace(XPath))
{
Data = LoadFromXml();
return;
}
Data = null;
}
finally
{
isRefreshing = false;
}
}
private object? CallObjectMethod()
{
try
{
var instance = CreateInstanceEvenWithoutDefaultCtor(ObjectType!);
var method = ObjectType!.GetMethod(MethodName!);
return method?.Invoke(instance, null);
}
catch (Exception ex)
{
Debug.WriteLine($"[ObjectDataProvider] Method invoke error: {ex}");
return null;
}
}
private object? LoadFromXml()
{
try
{
var uri = new Uri(Source!, UriKind.RelativeOrAbsolute);
using var stream = AssetLoader.Open(uri);
var doc = XDocument.Load(stream);
var nodes = doc.XPathSelectElements(XPath!);
// 只在 ObjectType 为空时推断,且避免循环触发
if (ObjectType == null)
{
var nodeName = GetLastNodeNameFromXPath(XPath!);
var inferred = FindTypeByName(nodeName);
if (inferred != null && inferred != ObjectType)
ObjectType = inferred;
// 若仍然找不到类型 → 返回 XElement 列表
if (ObjectType == null)
return new List<XElement>(nodes);
}
var listType = typeof(List<>).MakeGenericType(ObjectType!);
IList list = (IList)Activator.CreateInstance(listType)!;
foreach (var node in nodes)
{
var obj = CreateObjectFromXml(node);
if (obj != null)
list.Add(obj);
}
return list;
}
catch (Exception ex)
{
Debug.WriteLine($"[ObjectDataProvider] XML load error: {ex}");
return null;
}
}
private object? CreateObjectFromXml(XElement node)
{
try
{
var instance = CreateInstanceEvenWithoutDefaultCtor(ObjectType!);
if (instance == null)
return null;
foreach (var prop in ObjectType!.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
// 先查找 Element
var el = node.Element(prop.Name);
string? xmlValue = el?.Value;
// 查找 Attribute
if (xmlValue == null)
{
var attr = node.Attribute(prop.Name);
xmlValue = attr?.Value;
}
if (xmlValue == null)
continue;
try
{
object? converted = ConvertXmlValue(xmlValue, prop.PropertyType);
if (converted != null || prop.PropertyType.IsClass)
prop.SetValue(instance, converted);
}
catch (Exception ex)
{
Debug.WriteLine($"[ObjectDataProvider] Convert error on {prop.Name}: {ex}");
}
}
return instance;
}
catch (Exception ex)
{
Debug.WriteLine($"[ObjectDataProvider] Object create error: {ex}");
return null;
}
}
// 支持 Enum、Nullable<T>、基本类型
private static object? ConvertXmlValue(string value, Type targetType)
{
try
{
// Nullable<T>
var underlying = Nullable.GetUnderlyingType(targetType);
if (underlying != null)
return Convert.ChangeType(value, underlying, CultureInfo.InvariantCulture);
// Enum
if (targetType.IsEnum)
return Enum.Parse(targetType, value, ignoreCase: true);
// 默认行为
return Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture);
}
catch
{
return null;
}
}
private static object? CreateInstanceEvenWithoutDefaultCtor(Type type)
{
try
{
var defaultCtor = type.GetConstructor(Type.EmptyTypes);
if (defaultCtor != null)
return defaultCtor.Invoke(null);
// 使用“参数最少构造器”
var ctor = type.GetConstructors()
.OrderBy(c => c.GetParameters().Length)
.First();
var parameters = ctor.GetParameters();
var args = new object?[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
args[i] = GetDefaultValue(parameters[i].ParameterType);
return ctor.Invoke(args);
}
catch (Exception ex)
{
Debug.WriteLine($"[ObjectDataProvider] Create instance error: {ex}");
return null;
}
}
private static object? GetDefaultValue(Type t)
{
if (t.IsValueType)
return Activator.CreateInstance(t);
return null;
}
private static string GetLastNodeNameFromXPath(string xpath)
{
var p = xpath.Trim().Split('/', StringSplitOptions.RemoveEmptyEntries);
return p.Length > 0 ? p[^1] : "";
}
private static Type? FindTypeByName(string name)
{
try
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var t in asm.GetTypes())
{
if (t.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
return t;
}
}
return null;
}
catch (Exception ex)
{
Debug.WriteLine($"[ObjectDataProvider] Find type error: {ex}");
return null;
}
}
}
}
BindToObjectDataProvider.axaml代码
<Window 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" Height="550" Width="400" x:Class="AvaloniaUI.BindToObjectDataProvider" xmlns:data="using:AvaloniaUI.Demos.Book._19.StoreDatabase" Title="BindToObjectDataProvider"> <Window.Resources> <!-- <ObjectDataProvider x:Key="products" ObjectType="data:StoreDb1" MethodName="GetProducts"/> --> <ObjectDataProvider x:Key="products" Source="avares://AvaloniaUI/Resources/Datas/store.xml" XPath="/NewDataSet/Products" ObjectType="data:Product"/> </Window.Resources> <Grid RowDefinitions="*,auto,*"> <Grid RowDefinitions="auto,*"> <TextBlock Margin="5">ObjectDataProvider Example...</TextBlock> <ListBox Grid.Row="1" x:Name="lstProducts" Margin="5" ItemsSource="{Binding Source={StaticResource products}, Path=Data}"> <ListBox.ItemTemplate> <DataTemplate x:DataType="data:Product"> <TextBlock Text="{Binding ModelName}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> <GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" /> <Border Grid.Row="2" Padding="7" Margin="7" Background="LightSteelBlue"> <Grid DataContext="{Binding #lstProducts.SelectedItem}" x:DataType="data:Product" RowDefinitions="auto,auto,auto,auto,*" ColumnDefinitions="auto,*"> <TextBlock Margin="7">Model Number:</TextBlock> <TextBox Margin="5" Grid.Column="1" Text="{Binding ModelNumber}" /> <TextBlock Margin="7" Grid.Row="1">Model Name:</TextBlock> <TextBox Margin="5" Grid.Row="1" Grid.Column="1" Text="{Binding ModelName}" /> <TextBlock Margin="7" Grid.Row="2">Unit Cost:</TextBlock> <TextBox Margin="5" Grid.Row="2" Grid.Column="1" Text="{Binding UnitCost}" /> <TextBlock Margin="7,7,7,0" Grid.Row="3">Description:</TextBlock> <ScrollViewer Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2"> <TextBox Margin="7" AcceptsReturn="True" TextWrapping="Wrap" Text="{Binding Description}" /> </ScrollViewer> </Grid> </Border> </Grid> </Window>
BindToObjectDataProvider.axaml.cs代码
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace AvaloniaUI;
public partial class BindToObjectDataProvider : Window
{
public BindToObjectDataProvider()
{
InitializeComponent();
}
}
运行效果

浙公网安备 33010602011771号