我正在使用C#,WPF,.NET Standard,Visual Studio。所有最新版本或几乎最新版本。
这是我的数据上下文模型(在名为ProgrammingManagerAPI的独立库中创建(:
public class MainModel
{
public List<Project> Projects { get; set; }
...
}
其中定义了像这样定义的 Project 类型的对象列表(也在单独的库 ProgrammingManagerAPI 中,在目录模型中(,一些属性和一些方法:
public class Project
{
public int Id { get; set; }
...
public TimeSpan? TotalWorkedTime(bool subtasksIncluded = true)
{
if (Id < 0)
return null;
else
return new TimeSpan(...);
}
...
}
在主窗口中,我有一个列表视图,我想用它来列出项目及其属性。
我有很多属性和一些方法,它们根据布尔参数返回值。
我读到在这种情况下我应该使用 ObjectDataProvider,所以我尝试如下:
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:API.Models="clr-namespace:ProgrammingManagerAPI.Models;assembly=ProgrammingManagerAPI"
<Window.Resources>
<ObjectDataProvider x:Key="yourStaticData"
ObjectType="{x:Type API.Models:Project}"
MethodName="TotalWorkedTime" >
<ObjectDataProvider.MethodParameters>
<s:Boolean>false</s:Boolean>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid Grid.Row="1" Grid.Column="0" Margin="10">
<ListView Margin="10" ItemsSource="{Binding Projects}" HorizontalAlignment="Center" HorizontalContentAlignment="Center">
<ListView.View>
<GridView>
<GridViewColumn HeaderContainerStyle="{StaticResource ListViewStyle}" Header="Id" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn HeaderContainerStyle="{StaticResource ListViewStyle}" Header="TotalWorkedTime" DisplayMemberBinding="{Binding Path=., Source={StaticResource yourStaticData}}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
触发对函数 TotalWorkedTime 的调用,因为命中断点。但是被击中一次,而我已经创建了 4 个对象进行测试。 此外,它像静态函数一样被击中,而不是像其他属性那样针对对象的每个实例。在即时窗口中,我试图查看其他属性是什么,这些属性是空值。当命中 Id 列时,所有属性都可用于项目的每个实例。 此外,我观察到它在调用 Id 属性 getter 之前被击中。
我已经尝试了许多版本,例如没有路径,绑定和许多其他方式。
有人可以指出我的错误吗?
当您有一个要绑定的对象(或静态类(的单个实例,但您使用的是ItemsControl
(ListView
(时,ObjectDataProvider
很有用,这使事情变得更加复杂。
你需要的是一个IValueConverter
。它接受一个对象并通过调用函数并返回结果来"转换"它。老实说,我希望能够通过谷歌搜索找到一个,但我无法做到。我想我迟早会使用这样的东西,所以我继续构建一个。这支持具有任何函数名称的任何类型的对象,该对象采用任意数量的参数。
public class FunctionConverter : IValueConverter
{
public string FunctionName { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.GetType().GetMethod(FunctionName).Invoke(value, (object[])parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
下面是如何使用它的示例:
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
TestInstance = new Test();
InitializeComponent();
}
public Test TestInstance { get; set; }
}
public class Test
{
public string Foo(string bar)
{
return bar;
}
}
MainWindow.xaml:
<Grid>
<Grid.Resources>
<KHS:FunctionConverter x:Key="FuncCon" FunctionName="Foo"/>
</Grid.Resources>
<TextBlock>
<TextBlock.Text>
<Binding Path="TestInstance" Converter="{StaticResource FuncCon}">
<Binding.ConverterParameter>
<x:Array Type="sys:Object">
<sys:String>Hello World</sys:String>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBlock.Text>
</TextBlock>
</Grid>
您将转换器声明为资源,就像使用ObjectDataProvider
一样,并将FunctionName
设置为要调用的函数的名称。然后,转换器使用MethodInfo.Invoke(Object, Object[])
运行该函数并返回结果。
通过绑定的ConverterParameter
属性传递函数的参数,这可能会为列表中的不同项传递不同的值。在示例中,我将字符串"Hello World"
传递给函数Foo
,它只返回传递的内容。
最后几点说明:此转换器仅以一种方式工作。提供的转换器不会检查null
,并且在找不到FunctionName
时没有适当的处理。使用这样的绑定不允许像依赖项属性那样提供更新通知。