对 XAML ListView 控件中的 class 属性求和

Summing class attributes in XAML ListView control

我是 C# 的新手,所以我已经为此苦苦挣扎了很长时间,我不知道我应该做什么...

我想对我的 class (sum + interestRate_1) 中的 2 个(或更多)值求和,并在我各自的 ListView 中显示结果我在 ObservableCollections 列表中创建的每个 class 实例的列。 ListView 本身受 ItemSource 约束并包含所有已创建的 Cust -我试图对其执行计算的对象。

我是 XAML 的新手,所以我不知道它是如何工作的。人们告诉我使用转换器,但我不确定它意味着什么...

这是我试过的:

XAML声明:

<Window x:Class="MyProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    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:local="clr-namespace:MyProject"
    mc:Ignorable="d"
    Title="MyProject (beta)" Height="450" Width="1000" MinWidth="1000" MinHeight="450" ResizeMode="CanResize">

XAML ListView:

<ListView x:Name="ListTest" Margin="10,150,10,66" Background="#FF0077B6" Foreground="White" MouseDoubleClick="ListTest_MouseDoubleClick" SelectionChanged="ListTest_SelectionChanged" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding CustList}" Grid.RowSpan="2">
    <ListView.Effect>
        <DropShadowEffect/>
    </ListView.Effect>
    <ListView.View>
        <GridView>

            <GridViewColumn Header="Name" Width="136" DisplayMemberBinding="{Binding Name}"/>
            <GridViewColumn Header="Amount (€)" Width="80" DisplayMemberBinding="{Binding Sum}"/>
            <GridViewColumn Header="Amount With Interest (€)"  Width="80">
                <MultiBinding Converter="{StaticResource AddConverter}">
                    <Binding Path="Sum"/>
                    <Binding Path="interestRate_1"/>
                </MultiBinding>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

C# 转换器 + Cust 对象列表:

namespace MyProject
{
    public class AddConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type tagetType, object parameter, CultureInfo culture)
        {

            var decimalValues = values.Cast<decimal>().ToArray();
            var resultSum = decimalValues.Sum().ToString();
            return resultSum;

        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public partial class MainWindow : Window
    {

        public ObservableCollection<Cust> CustList = new ObservableCollection<Cust>();

        public MainWindow()
        {
            InitializeComponent();

            ListTest.ItemsSource = CustList;
            
        }

//...

相关 Class 属性/属性:

public class Cust
{
    public Name {get; set;}
    private decimal sum;
    public decimal Sum
    {

        get { return sum; }
        set { sum = value; }

    }

    public decimal interestRate_1 { get; set; } = 3.0M;
    public decimal interestRate_2 { get; set; } = 5.0M;

// ...

我知道 XAML 无法从 .cs 文件中找到转换器,并且我在我尝试转换的列中使用了两次 Header(我不明白)。我完全迷失在这里了吗?

欢迎来到 xaml 和 c# 的土地:)

WPF 或 XAML 和 C# 的关键概念是 MVVM,它代表模型视图视图模型 在您的示例中,Cust class 将是模型 XAML 后面的代码就是视图。 缺少的是 ViewModel。这是您的代码和视图之间的连接。

这是我印象深刻的观点

  1. 你应该看看 MVVM 和绑定的概念:
  • 顾客就是模特
  1. 转换器通常用于绑定一个值,例如
  • if(值 == null) return Visibility.Collapsed 所以我认为它不是这里工作的正确工具。
  1. 您可以考虑在 listView 中使用 DataGrid 控件而不是 GridView
  2. 如果您想使用 ListView,则必须搜索模板(尤其是 ItemTemplates)
  3. 如果您想更改您的值并更新您的视图,您必须查找 INotifyPropertyChanged 或找到一个基本实现来设置您的属性。

但无论如何这里有一个解决你的问题的方法:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        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:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>
        <DataGrid ItemsSource="{Binding Custs}"/>
        <Label Content="Result"/>
        <Label Content="{Binding ResultSum}"/>
    </StackPanel>
</Window>

下面是一个视图模型的基本示例

public class MainWindowViewModel
    {
        public ObservableCollection<Cust> Custs { get; set; }

        public decimal ResultSum { get; set; }

        public MainWindowViewModel()
        {
            Custs = new ObservableCollection<Cust>
            {
                new Cust
                {
                    Name = "Joe",
                    Sum = 1.2M,
                },
                new Cust
                {
                    Name = "Jane",
                    Sum = 3.2M,
                },
            };

            ResultSum = Custs.Sum(cust => cust.Sum);
        }
    }

以及如何使用它

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new MainWindowViewModel();
        }
    }

还有你的客户class

public class Cust
    {
        public string Name
        {
            get;
            set;
        }
        private decimal sum;

        public decimal Sum
        {

            get { return sum; }
            set { sum = value; }

        }

        public decimal interestRate_1 { get; set; } = 3.0M;
        public decimal interestRate_2 { get; set; } = 5.0M;
    }

The result looks like this

找不到转换器,因为没有它的实例。在 XAML 中的任何资源字典中创建一个实例,例如应用程序资源或本地 ListView.Resources.

<ListView.Resources>
   <local:AddConverter x:Key="AddConverter"/>
</ListView.Resources>

ItemsSource 的绑定是多余的,因为您已经在代码中分配它,使用或其他。

ItemsSource="{Binding CustList}"
ListTest.ItemsSource = CustList;

但是,ItemSource 绑定只有在正确设置数据上下文的情况下才会起作用,例如:

public MainWindow()
{
    InitializeComponent();
    DataContext = this;
}

或者像这样在 XAML 标记中。

<Window x:Class="MyProject.MainWindow"
    ...
    DataContext="{Binding RelativeSource={RelativeSource Self}}">

最后,它说你分配了两次header,因为GridViewColumn的直接内容也分配了Header 属性。明确地使用 DisplayMemberBinding 属性。

<GridViewColumn Header="Amount With Interest (€)"  Width="80">
   <GridViewColumn.DisplayMemberBinding>
      <MultiBinding Converter="{StaticResource AddConverter}">
         <Binding Path="Sum"/>
         <Binding Path="interestRate_1"/>
      </MultiBinding>
   </GridViewColumn.DisplayMemberBinding>
</GridViewColumn>

这是您 ListView 的完整代码。

<ListView x:Name="ListTest" Margin="10,150,10,66" Background="#FF0077B6" Foreground="White" MouseDoubleClick="ListTest_MouseDoubleClick" SelectionChanged="ListTest_SelectionChanged" IsSynchronizedWithCurrentItem="True" Grid.RowSpan="2">
   <ListView.Resources>
      <local:AddConverter x:Key="AddConverter"/>
   </ListView.Resources>
   <ListView.Effect>
      <DropShadowEffect/>
   </ListView.Effect>
   <ListView.View>
      <GridView>
         <GridViewColumn Header="Name" Width="136" DisplayMemberBinding="{Binding Name}"/>
         <GridViewColumn Header="Amount (€)" Width="80" DisplayMemberBinding="{Binding Sum}"/>
         <GridViewColumn Header="Amount With Interest (€)"  Width="80">
            <GridViewColumn.DisplayMemberBinding>
               <MultiBinding Converter="{StaticResource AddConverter}">
                  <Binding Path="Sum"/>
                  <Binding Path="interestRate_1"/>
               </MultiBinding>
            </GridViewColumn.DisplayMemberBinding>
         </GridViewColumn>
      </GridView>
   </ListView.View>
</ListView>

您不一定需要值转换器。您还可以在 Cust 中公开计算的 属性 并改为绑定它。这取决于您的要求。

public class Cust
{
   // ...your other code.

   public decimal AmountWithInterest => sum + interestRate_1;
}
<GridViewColumn Header="Amount With Interest (€)"  Width="80" DisplayMemberBinding="{Binding AmountWithInterest}"/>

另请注意,您没有 implement INotifyPropertyChanged,这意味着对您的属性所做的更改和计算的 属性 不会反映在用户界面中。


除了你得到的错误,你应该熟悉 MVVM pattern