【问题标题】:UWP ListView without DataBinding没有数据绑定的 UWP ListView
【发布时间】:2016-12-01 11:51:35
【问题描述】:

我刚刚开始使用 UWP,目前正在尝试创建一个包含项目的 ListView。 我看过一些教程,但所有教程都使用 DataBinding。

有没有办法在没有 DataBinding 的情况下创建 ListView? DataBinding 非常适合简单的模型,但是当它变得复杂时,我更喜欢自己填充视图。就像从 url 加载图像,日期到字符串等等。

这可能吗? (如果我完全看错了,也请纠正我)

编辑

这是我在 Android 上填写列表的方式:

  • 我有一个项目列表(例如,有名字和姓氏的人)。
  • 我有单个列表项的布局(例如只是一个 textBlock)
  • 我有一个 ViewHolder,它在单个列表项(例如 textBlock)中保存对视图的引用。这个 ViewHolder 会被重复使用。
  • 我有一个适配器,它向 ListView 提供信息(例如项目数和创建 ViewHolder 并将数据绑定到 ViewHolder)
  • 在将数据绑定到 ViewHolder 时,我可以完全灵活地进行操作。我引用了列表项中的每个视图。因此,例如,我可以用 firstName 和 lastName 组合填充 textBlock(这一切都在代码中完成,而不是在布局文件中完成)

【问题讨论】:

    标签: listview data-binding uwp windows-10-universal uwp-xaml


    【解决方案1】:

    所以请允许我在开头说一下,你对数据绑定的看法是 100% 错误的,哈哈……事情越复杂,DataBinding 就越有用。事实上,事实恰恰相反。对于简单的示例,人们倾向于发现 MVVM 进程和数据绑定有点健壮。

    也就是说……

    这里是如何做你所要求的。

    要将项目添加到列表视图中,您只需致电Items.Add

    在我的 Xaml 中,我有一个名为 TestListview 的 ListView

    <Grid>
        <ListView x:Name="TestListview"/>
    </Grid>
    

    在我的背后代码(Xaml.cs)中,我的构造函数中有以下内容。

    TestListview.Items.Add("Item One");
    TestListview.Items.Add("Item Two");
    TestListview.Items.Add("Item Three"); 
    

    就是这样。现在项目将在我的列表视图中。再一次强调,这是最糟糕的方法,不可维护,是一种糟糕的学习习惯,而且会让你成为专业人士受到惩罚、去皮重和羽化。

    祝你好运!

    更新

    根据 OP 的要求,这里有一个例子说明为什么没有数据绑定的生活会很糟糕

    我们将探讨两个示例。在我们的第一个示例中,我们将研究如何使用具有一些属性的对象填充列表视图。然后我们将在单击该对象时启用该对象的详细视图。

    然后在我们的第二个示例中,我们将看到如何仅使用数据绑定来完成相同的事情。

    对于这两个示例,我将使用这个虚拟数据生成器和模型

       public static IEnumerable<PersonModel> GenerateFakePeople()
       {
                List<PersonModel> people = new List<PersonModel>();
                people.Add(new PersonModel() { FirstName = "Aaron", LastName = "Doe", Age = 11, Bio = "This is a short bio bio bio blah blah blah.", ImageURL= "http://media.boingboing.net/wp-content/uploads/2012/02/breadingcats.jpg" });
                people.Add(new PersonModel() { FirstName = "John", LastName = "Doe", Age = 21, Bio = "This is a short bio bio bio blah blah blah.", ImageURL = "http://media.boingboing.net/wp-content/uploads/2012/02/breadingcats.jpg" });
                people.Add(new PersonModel() { FirstName = "David", LastName = "Doe", Age = 31, Bio = "This is a short bio bio bio blah blah blah.", ImageURL = "http://media.boingboing.net/wp-content/uploads/2012/02/breadingcats.jpg" });
                return people;
        }
    
        public class PersonModel
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Age { get; set; }
            public string ImageURL { get; set; }
            public string Bio { get; set; }
        }
    

    正如您在 xaml 中看到的,我已将其扩展为包含一个显示已点击用户的位置。

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width=".3*"/>
            <ColumnDefinition Width=".7*"/>
        </Grid.ColumnDefinitions>
        <ListView x:Name="TestListview" Grid.Column="0" SelectionChanged="TestListview_SelectionChanged"/>
        <Grid Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width=".3*"/>
                <ColumnDefinition Width=".7*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="200"/>
                <RowDefinition Height="300"/>
            </Grid.RowDefinitions>
            <Image x:Name="PersonsImage" Grid.Column="0" Grid.Row="0"/>
            <StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="0">
                <TextBlock x:Name="PersonsName" FontSize="25"/>
                <TextBlock x:Name="PersonsAge" FontSize="25"/>
            </StackPanel>
            <TextBlock x:Name="PersonsBio" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" FontSize="30" TextWrapping="Wrap"/>
        </Grid>
    </Grid>
    

    在我们的后台代码中,您不仅可以看到信息是如何填充在屏幕上的,而且还可以看到创建每个单独的 UI 项所发生的高度耦合的工作。

    当我们像这样将 UI 元素紧密耦合在一起时,很难在不破坏事物的情况下更改 UI。不仅如此,模型也很难改变。

    例如,如果我想更改我的 PersonModel 对象以支持全名而不是名字和姓氏,我将不得不检查并更改引用了名字属性的每一行代码。

    更糟糕的是,如果我添加了全名属性,但决定保留名字和姓氏属性,但又想将它们切换出去,我将不得不手动检查并找到所有引用旧属性。

    哦,我如何访问简历?我们正在从 ListView 捕获选择更改事件,但我们只能从 UI 元素访问数据,因为我们从未真正将 PersonModel 绑定到 ListViewItem。

    最后,更新画面,这大概是数据绑定最大的卖点吧。如果出于某种原因我在代码中修改了某人的姓名,我更改了他们的年龄、他们的简历或其他一些附加在他们身上的财产,会发生什么?我必须通过 UI 并手动更新每个引用该属性的位置。这意味着我必须跟踪它引用的每个地方。这是高度耦合的,而且设计很差。

     public partial class MainWindow : Window
        {
            
        public MainWindow()
        {            
            InitializeComponent();
            
            foreach(PersonModel person in GenerateFakePeople())
            {
                TestListview.Items.Add(GenerateItem(person));
            }
        }
    
      
        public static StackPanel GenerateItem(PersonModel person)
        {
            StackPanel outerStackPanel = new StackPanel() { Orientation = Orientation.Horizontal };
            StackPanel innerStackPanel = new StackPanel() { Orientation = Orientation.Vertical };
            Image image = new Image() { Source = new BitmapImage(new Uri(person.ImageURL)) };
            TextBlock firstName = new TextBlock() { Text = person.FirstName };
            TextBlock lastName = new TextBlock() { Text = person.LastName };
            TextBlock age = new TextBlock() { Text = person.Age.ToString() };
            TextBlock bio = new TextBlock() { Text = person.Bio };
    
            outerStackPanel.Children.Add(image);
            outerStackPanel.Children.Add(innerStackPanel);
            innerStackPanel.Children.Add(firstName);
            innerStackPanel.Children.Add(lastName);
            innerStackPanel.Children.Add(age);
    
            return outerStackPanel;
        }
    
        private void TestListview_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            StackPanel outerStackPanel = (sender as ListView).SelectedItem as StackPanel;
    
            if (outerStackPanel != null)
            {
                Image image = outerStackPanel.Children[0] as Image;
                StackPanel innerStackPanel = outerStackPanel.Children[1] as StackPanel;
    
                if (innerStackPanel != null)
                {
                    TextBlock firstName = innerStackPanel.Children[0] as TextBlock;
                    TextBlock lastName = innerStackPanel.Children[1] as TextBlock;
                    TextBlock age = innerStackPanel.Children[2] as TextBlock;
                   // TextBlock bio = innerStackPanel.Children[3] as TextBlock;
    
                    PersonsImage.Source = image.Source;
                    PersonsName.Text = firstName.Text + " " + lastName.Text;
                    PersonsAge.Text = age.Text;
                 //   PersonsBio.Text = bio.Text; //Cant Even access the bio! It doesn't exist!
                }
            }
        }
    }  
    

    现在让我们看一下数据绑定示例。

    使用 DataBinding,我们有能力简化我们的后台代码。它允许我们使用诸如 MVVM 之类的模式。在大规模工作时非常强大。

    这是我们的新 Xaml 文件

    <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width=".3*"/>
                <ColumnDefinition Width=".7*"/>
            </Grid.ColumnDefinitions>
            <ListView x:Name="PeopleListView" Grid.Column="0" ItemsSource="{Binding People}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <Image Source="{Binding ImageURL}" Height="100" Width="100"/>
                            <StackPanel Orientation="Vertical">
                                <TextBlock Text="{Binding FirstName}" />
                                <TextBlock Text="{Binding LastName}" />
                                <TextBlock Text="{Binding Age}" />
                            </StackPanel>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Grid Grid.Column="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width=".3*"/>
                    <ColumnDefinition Width=".7*"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="200"/>
                    <RowDefinition Height="300"/>
                </Grid.RowDefinitions>
                <Image Source="{Binding ElementName=PeopleListView, Path=SelectedItem.ImageURL}"  Grid.Column="0" Grid.Row="0"/>
                <StackPanel Orientation="Vertical" Grid.Column="1" Grid.Row="0">
                    <TextBlock Text="{Binding ElementName=PeopleListView, Path=SelectedItem.FirstName}"  FontSize="25"/>
                    <TextBlock Text="{Binding ElementName=PeopleListView, Path=SelectedItem.Age}" FontSize="25"/>
                </StackPanel>
                <TextBlock Text="{Binding ElementName=PeopleListView, Path=SelectedItem.Bio}" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" FontSize="30" TextWrapping="Wrap"/>
            </Grid>
        </Grid>
    

    如您所见,发生了一些变化。您应该注意到的第一件事是 ListView 现在有一个 ItemTemplate。这表示对于添加到 ListView 的每个项目,这就是您希望它在控件中显示的方式。这允许我们将所有 UI 设计内容保留在它所属的 xaml 中。

    您应该注意的第二件事是,我现在将我们的人员显示区域绑定到 ListView 的 SelectedItem 属性。这使我们可以直接访问实际的数据对象,而不必通过控件进行挖掘。这是处理数据的一种优越得多的方式。

    最后让我们看看我们现在的CS

        private ObservableCollection<PersonModel> _people;
    
        public ObservableCollection<PersonModel> People
        {
            get { return _people; }
            set { _people = value; }
        }
    
    
        public MainWindow()
        {            
            InitializeComponent();
            People = new ObservableCollection<PersonModel>(GenerateFakePeople());
            this.DataContext = this;
        }
    

    如您所见,我们的 CS 大大简化了。不仅如此,我们还可以对对象进行操作,并且可以通过触发 PropertyChangedEvent 来保持 UI 同步。这将更新绑定到属性的所有内容。我们还可以轻松地将更多 PersonModel 添加到我们的 People 集合中,它也会反映在屏幕上。

    我理解您对 DataBinding 和 MVVM 架构的担忧。我向你保证,整个社区都没有错。我们正在从几十年的不良做法中吸取教训。 MVVM 为企业提供了一种非常模块化且具有成本效益的软件构建方式。它使您可以轻松地重用代码,最重要的是,如果出现问题,很容易找到,因为您无需在代码中搜索对属性的一百万个引用。您只需修复一次。

    我强烈建议您对 MVVM、数据绑定和面向对象编程进行一些研究。走出去,实际构建一些您需要维护的手机应用程序或软件。然后,当你因为更改了某些内容而不得不花一个小时更新代码时,你就会明白了。

    这里有一些我会研究的资源。如果他们说 WPF 而不是 UWP,请不要害怕。对于这些主题,模式几乎相同。

    What is MVVM?

    What is DataBinding?

    Creating User Controls

    Creating Lookless Controls

    【讨论】:

    • 为什么它变得更有用了?您发布了一个非常简单的示例,但我不明白为什么当事情变得更复杂时它会变得更有用。您能否展示一个更复杂的 DataBinding 示例?
    • 是的,让我进办公室,我给你做一份。我向你保证会的。
    • 感谢您的示例!我绝对不想在不重用这些视图的情况下添加所有视图。尽管我现在更好地理解了 DataBinding 和 MVVM 并且理解了它为什么非常有用,但我仍然认为我可能会遇到问题。我最近制作了一个可以扩展列表项的 Android 应用程序。这也可以使用 DataBinding 解决吗?我还根据我在 Android 上的习惯更新了我的问题,以便您更好地理解我的意思。
    • @KevinvanMierlo 我讨厌成为一个混蛋,但 Android、Java 和 XML 不是不是 UWP、WPF、C# 或 XAML。它们看起来一样,但它们不一样。如果您打算探索 C# 和 XAML,那么我建议您将 Android 体验放在一边。否则你的杯子已经满了,你无法学习编写具有专业外观的代码所需的知识。
    • 这不是我要说的,我知道它们非常不同,这是两个不同的平台。我将使用 DataBinding,因为这是要走的路,而且我试图了解在 UWP 上处理事情的最佳方式。但我要了解的是如何处理复杂的情况。就像列表项的扩展一样。如果所有这些事情都可以通过 DataBinding 完成,那就太好了!如果不是,我想知道如何处理它。
    【解决方案2】:

    在我看来,你不想使用数据绑定的情况并不多。性能损失没有引入 x:Bind 之前那么大。除此之外,您还可以使用转换器执行您提到的日期格式等操作。

    如果我有一个可以以多种方式呈现的非常复杂的项目,我会使用 UserControl。我只是将数据对象绑定到 UserControl 上的属性并处理此 UserControl 的代码隐藏中的所有内容。

    【讨论】:

    • 你能给我看一个转换器和用户控件的例子吗?因为这些事情在我的项目中经常发生,我不知道应该如何处理。
    • 转换器(WPF,但原理是一样的):wpftutorial.net/ValueConverters.html 而一个 UserConrol 只是一个 UserControl
    • 您也有一个用于 UserControl 的吗?我仍然不明白为什么 DataBinding 会比在包含这些复杂项目的代码中添加它更容易。
    【解决方案3】:

    在我看来,你完全错了! :-)

    创建一个 List 实例,其中 MyItem 是您要手动添加到 ListView 的同一个类(与您添加到 ListView 的 Items 属性相同)。最后你有一个包含很多对象的列表。

    那么你可以简单地:

    this.listView1.ItemsSource = your_instance_of_list
    

    完成!之后,您需要设置一个 ItemTemplate。

    【讨论】:

    • 这不能回答我的问题。我正在寻找将复杂事物添加到我的视图中的方法。另一个例子:我得到一个 base64 编码的字符串,需要解码成图像。您不能真正为此使用数据绑定
    • 请阅读数据绑定和转换器,您会发现很多有趣的东西。
    • 我刚刚做了一些关于它的阅读,这可以用于我的示例。但是还有更多我对 DataBinding 有疑问的例子。例如,从 url 加载图像,如果加载失败,则显示占位符和错误视图。你知道如何解决这个问题吗?
    • 创建一个自定义 UserControl 用于显示图像并将法师 url 绑定到它
    • 嗯,这可以解决我的大部分问题。您是否曾经在代码中进行过绑定,或者您不应该这样做?
    猜你喜欢
    • 2019-04-13
    • 2016-11-18
    • 1970-01-01
    • 2016-11-26
    • 2018-02-06
    • 1970-01-01
    • 2017-10-30
    • 2019-09-28
    • 2017-05-16
    相关资源
    最近更新 更多