如何将包含多个列的项添加到WPF listview控件 [英] How to add one item containing multiple columns to a WPF listview control

查看:92
本文介绍了如何将包含多个列的项添加到WPF listview控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我第一次尝试使用WPF ListView控件。我已经广泛使用了WinForms ListView控件,但这并没有帮助我解决这个问题。当我向WPF ListView控件添加多列项时,我得到这两个错误。



使用Visual Studio Debugger Quick Watch,我可以看到 DirectCast((New System.Linq.SystemCore_EnumerableDebugView(lstCSchedule.Items).Items(0) )),System.Windows.Controls.ContentControl).Content 对象包含Name和RecordDateTime属性名称和值,但除了列标题和空行之外,ListView控件中不显示任何内容。这告诉我数据被放入ListView控件,但ListView控件不显示它。



我可以使用什么语法将行正确添加到ListView Items集合中,以便它显示在ListView控件中并且不会导致错误消息?



运行时错误消息

System.Windows.Data错误:40:BindingExpression路径错误:在对象''CRecordSchedule'(HashCode = 18437496)'上找不到名称属性。 BindingExpression:路径=名称; DataItem ='CRecordSchedule'(HashCode = 18437496); target元素是'TextBlock'(Name =''); target属性为'Text'(类型'String')



System.Windows.Data错误:40:BindingExpression路径错误:'对象'找不到'RecordDateTime'属性'''CRecordSchedule'(HashCode = 18437496)'。 BindingExpression:路径= RecordDateTime; DataItem ='CRecordSchedule'(HashCode = 18437496); target元素是'TextBlock'(Name =''); target属性是'Text'(类型'String')




XAML WPF ListView控件的定义

This is my first attempt at using a WPF ListView control. I have used the WinForms ListView control extensively but that hasn't helped me solve this issue. I get these two errors when I add a multi-column item to the WPF ListView control.

Using the Visual Studio Debugger Quick Watch, I can see that the DirectCast((New System.Linq.SystemCore_EnumerableDebugView(lstCSchedule.Items).Items(0)), System.Windows.Controls.ContentControl).Content object contains both the Name and the RecordDateTime property names and values but nothing is displayed in the ListView control other than the column headings and a blank row. That tells me that the data is getting put into the ListView control but it is not displayed by the ListView control.

What syntax can I use to properly add a row to the ListView Items collection so that it displays in the ListView control and doesn't cause an error message?

Run-Time Error Messages
System.Windows.Data Error: 40 : BindingExpression path error: 'Name' property not found on 'object' ''CRecordSchedule' (HashCode=18437496)'. BindingExpression:Path=Name; DataItem='CRecordSchedule' (HashCode=18437496); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

System.Windows.Data Error: 40 : BindingExpression path error: 'RecordDateTime' property not found on 'object' ''CRecordSchedule' (HashCode=18437496)'. BindingExpression:Path=RecordDateTime; DataItem='CRecordSchedule' (HashCode=18437496); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')


XAML WPF Definition of the ListView Control

<ListView x:Name="lstCSchedule" HorizontalAlignment="Left" Height="108" Margin="27,88,0,0" VerticalAlignment="Top">
    <ListView.View>
        <GridView>
            <GridViewColumn Width="120" Header="Name" DisplayMemberBinding="{Binding Name}"  />
            <GridViewColumn Width="90" Header="Record DateTime" DisplayMemberBinding="{Binding RecordDateTime}" />
        </GridView>
    </ListView.View>
</ListView>





CRecordSchedule类定义



The CRecordSchedule Class Definition

Public Class CRecordSchedule
    Public Name As String
    Public RecordDateTime As String
    Public Sub New(n As String, r As String)
        Name = n
        RecordDateTime = r
    End Sub
End Class





向ListView控件添加一个新项目的代码



The code that adds one new item to the ListView control

Dim Schedule As New CRecordSchedule("A Test Show Title", "06/14/18 7:00PM")
Dim lvItem As New ListViewItem
lvItem.Content = Schedule
lstCSchedule.Items.Add(lvItem)





我的尝试:



1. lstCSchedule.Items.Add(Schedule)

2.添加一个数组到ListView Items集合而不是类对象。

3. DisplayMemberBinding ={Binding Path = Name}和DisplayMemberBinding ={Binding Path = RecordDateTime}

4.基本相同的尝试解决方案的许多其他迭代。



What I have tried:

1. lstCSchedule.Items.Add(Schedule)
2. Add an array to the ListView Items collection instead of a class object.
3. DisplayMemberBinding="{Binding Path=Name}" and DisplayMemberBinding="{Binding Path=RecordDateTime}"
4. Numerous other iterations of basically the same attempted solutions.

推荐答案

错误消息是所需内容的最佳指标。



WPF数据绑定 [ ^ 对于WinForm数据绑定系统来说,它更加优越和简单。一旦你花时间学习数据绑定在WPF中的工作方式,你就不会回头了。



这里有一个例子,你可以快速告诉你如何数据绑定有效:



WPF绑定属性更新需要为每个属性触发 PropertyChanged 事件。对于集合,我们可以使用一个名为 ObservableCollection 的专门集合类,它会触发 CollectionChanged 事件。



对于属性,下面是一个基类,用于简化类中每个属性所需的重复代码:

The error message is the best indicator of what is required.

WPF Data Binding[^] is far superior and simpler to the WinForm Data Binding system. Once you invest the time to learn how data binding works in WPF, you won't want to look back.

Here is an example for you to show you quickly how Data Binding works:

WPF Binding for property updates requires a PropertyChanged event to be fired for each property. For collections, we can use a specialized collection class called ObservableCollection which fires a CollectionChanged event.

For the properties, below is a base class to simplify the repetitive code required for each property in a class:
Public MustInherit Class ObservableBase
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler _
        Implements INotifyPropertyChanged.PropertyChanged

    Public Sub [Set](Of TValue)(ByRef field As TValue, ByVal newValue As TValue,
                                <CallerMemberName> ByVal Optional propertyName As String = "")
        If EqualityComparer(Of TValue).[Default].Equals(field, Nothing) OrElse Not field.Equals(newValue) Then
            field = newValue
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))

        End If
    End Sub

End Class



对于这个例子,我将使用一个 PersonModel 类继承上面的 ObservableBase 基类:


For this example, I will use a PersonModel class which inherits the ObservableBase base class above:

Public Class PersonModel
    Inherits ObservableBase

    Private mName As String

    Public Property Name As String
        Get
            Return mName
        End Get
        Set(ByVal value As String)
            [Set](mName, value)
        End Set
    End Property

    Private mAge As Integer

    Public Property Age As Integer
        Get
            Return mAge
        End Get
        Set(ByVal value As Integer)
            [Set](mAge, value)
        End Set
    End Property
End Class



现在我们可以创建我们的集合并添加一个 事件来演示实际中的绑定系统:


Now we can create our collection and add a event to demonstrate the binding system in action:

Imports System.Collections.ObjectModel

Class MainWindow

    Public Sub New()
        InitializeComponent()
        DataContext = Me
        Mock()
    End Sub

    Public Property Persons As ObservableCollection(Of PersonModel) = New ObservableCollection(Of PersonModel)()
    Private rand As Random = New Random()

    Private Sub Mock()
        For i As Integer = 0 To 10 - 1
            Persons.Add(New PersonModel With {
                    .Name = String.Format("Person {0}", i),
                    .Age = rand.[Next](20, 50)
                })
        Next
    End Sub

    Private Sub Button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        For Each person In Persons
            person.Age = rand.[Next](20, 50)
        Next
    End Sub
End Class



在上面的代码隐藏页面中,我将表单的 DataContext 设置为代码隐藏页面本身。这将允许UI(XAML)查看绑定通知并更新显示。



最后,我们可以绑定 UI元素代码隐藏的属性


In the above code-behind page, I set the form's DataContext to the code-behind page itself. This will allow the UI (XAML) to see the binding notifications and update the display.

Lastly, we can bind the properties of the code-behind to the UI elements:

<Window x:Class="MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"



        mc:Ignorable="d"

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        xmlns:local="clr-namespace:WpfSimpleBindingVB"

        Title="MainWindow"

    WindowStartupLocation="CenterScreen" Height="400" Width="300">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Grid.Resources>
            <DataTemplate DataType="{x:Type local:PersonModel}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}" Margin="10 3"/>
                    <TextBlock Text="{Binding Age}" Margin="10 3"

                               Grid.Column="1"/>
                </Grid>
            </DataTemplate>
        </Grid.Resources>

        <ListBox ItemsSource="{Binding Persons}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
        <Button Content="Randomize" Padding="10 5" Margin="10"

                HorizontalAlignment="Center" VerticalAlignment="Center"

                Grid.Row="1" Click="Button_Click"/>
    </Grid>

</Window>



上面的示例使用ListBox控件。 ListView的原理完全相同。以下是使用ListView的UI:


The above example uses a ListBox control. The principles for the ListView is exactly the same. Here is the UI using the ListView instead:

<Window x:Class="MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"



        mc:Ignorable="d"

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        xmlns:local="clr-namespace:WpfSimpleBindingVB"

        Title="MainWindow"

    WindowStartupLocation="CenterScreen" Height="400" Width="300">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <ListView ItemsSource="{Binding Persons}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"  />
                    <GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}" />
                </GridView>
            </ListView.View>
        </ListView>

        <Button Content="Randomize" Padding="10 5" Margin="10"

                HorizontalAlignment="Center" VerticalAlignment="Center"

                Grid.Row="1" Click="Button_Click"/>
    </Grid>

</Window>



This example is using the same data binding technique used in the MVVM (Model View ViewModel) Design Pattern except the View Model is in the code behind. MVVM is a little bit more involved and is beyond the scope of this answer but worthwhile investing the time in learning.


This example is using the same data binding technique used in the MVVM (Model View ViewModel) Design Pattern except the ViewModel is in the code behind. MVVM is a little bit more involved and is beyond the scope of this answer but worthwhile investing the time in learning.


I have solved my problem.



I changed the class definition by adding a Public Property for Name and RecordDateTime.

I have solved my problem.

I changed the class definition by adding a Public Property for Name and RecordDateTime.
Public Class CSchedule
    Private mName As String
    Private mRecordDateTime As String
    Public Sub New(ByVal Name As String, ByVal RecordDateTime As String)
        mName = Name
        mRecordDateTime = RecordDateTime
    End Sub
    Public Property Name As String
        Get
            Name = mName
        End Get
        Set(value As String)
            mName = value
        End Set
    End Property
    Public Property RecordDateTime As String
        Get
            RecordDateTime = mRecordDateTime
        End Get
        Set(value As String)
            mRecordDateTime = value
        End Set
    End Property
End Class







Dim Schedule As New CRecordSchedule(rs.GetString(rs.GetOrdinal("name")), Format(rs.GetDateTime(rs.GetOrdinal("rundatetime")), "MM/dd/yy hh:mm:sstt"))
lstCSchedule.Items.Add(Schedule)





To diagnose this issue, I added trace declarations to the XAML for the ListView columns. One of the trace messages said that the Accessor was null. I guessed that meant that I had to explicitly declare a Public Property for Name and RecordDateTime. The conclusion is that the binding mechanism can’t find the values in a class object unless the properties are explicitly declared.



To diagnose this issue, I added trace declarations to the XAML for the ListView columns. One of the trace messages said that the Accessor was null. I guessed that meant that I had to explicitly declare a Public Property for Name and RecordDateTime. The conclusion is that the binding mechanism can't find the values in a class object unless the properties are explicitly declared.

<ListView x:Name="lstCSchedule" HorizontalAlignment="Left" Height="108" Margin="1,88,0,0" VerticalAlignment="Top" Grid.Column="1" >
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="90" Header="Name" PresentationTraceSources.TraceLevel="High"  DisplayMemberBinding="{Binding Name,diag:PresentationTraceSources.TraceLevel=High}" />
                    <GridViewColumn Width="90" Header="Record DateTime" PresentationTraceSources.TraceLevel="High" DisplayMemberBinding="{Binding RecordDateTime,diag:PresentationTraceSources.TraceLevel=High}" />
                </GridView>
            </ListView.View>
</ListView>


这篇关于如何将包含多个列的项添加到WPF listview控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆