在 Xamarin Forms 中,如何在点击 ViewCell 后刷新 TableView 数据?

15

我有以下代码,可以动态地为我的TableView创建ViewCells:

XAML:

<StackLayout>
   <TableView Intent="Settings">
      <TableView.Root>
         <TableSection x:Name="tableSection"></TableSection>
      </TableView.Root>
   </TableView>
</StackLayout>

C#:

categories = getCategories();
foreach (var category in categories)
{
   var viewCell = new ViewCell 
   { 
      View = new StackLayout()
      {
         Padding = new Thickness(20, 0, 20, 0),
         HorizontalOptions = LayoutOptions.FillAndExpand,
         Children = { 
            new StackLayout() {
               Orientation = StackOrientation.Horizontal,
               VerticalOptions = LayoutOptions.CenterAndExpand,
               Children = {
                  new StackLayout() {
                     HorizontalOptions = LayoutOptions.StartAndExpand,
                     Children = {
                        new Label { Text = category.Name}
                      },
                  new StackLayout() {
                     HorizontalOptions = LayoutOptions.EndAndExpand,
                     Orientation = StackOrientation.Horizontal,
                     Children = {
                        new Label { Text = category.Count},
                        new Image { Source = "right1.png",
                                    IsVisible = category.Selected }
                     }
                  }
               }
            }
         }
       }
    };
    viewCell.Tapped += (sender, e) =>
    {
       if (category.Selected == false)
       {
          App.DB.UpdateSelected(true);
       }
       else
       {
          App.DB.UpdateSelected(false);
       }
       categories = getCategories();
       totalPhraseCount = getTotalPhraseCount();
       Title = totalPhraseCount.ToString() + " phrases";
     };
    tableSection.Add(viewCell);
}

我想做的是,每当我点击一个视图单元格来更新选定属性时,表格视图中的数据也会被更新。在ListView中,我可以调用ItemSelected事件,并使用更新后的类别再次调用ItemSource。这在TableView中是否可行?


为什么要使用TableView?回答这个问题对于实现正确的解决方案非常重要。如果你可能会在TableView中有很多行,那么你将不得不对你的解决方案进行创意性的处理。我想提供帮助,但需要更多关于你在应用程序中尝试通过这个TableView实现什么目标的指导(比如它是一个设置页面,还是向用户显示动态行等)。 - BrewMate
3个回答

5

试试这个:

输入图像描述

XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App54"
             x:Class="App54.MainPage">

 <ContentPage.Content>
        <TableView Intent="Settings">
            <TableRoot>
                <TableSection x:Name="tableSection" Title="Section Title">
                </TableSection>
            </TableRoot>
        </TableView>
    </ContentPage.Content>
</ContentPage>

主页:

public partial class MainPage : ContentPage
{
    MyViewModel vm;

    public MainPage()
    {
        InitializeComponent();

        vm = new MyViewModel();

        foreach (MyDataModel dm in vm.Data)
        {
            Image img = new Image();
            img.SetBinding(Image.SourceProperty, "MyImage", BindingMode.TwoWay, null, null);
            img.BindingContext = dm;

            Label label1 = new Label();
            label1.SetBinding(Label.TextProperty, "MyLabel", BindingMode.TwoWay, null, null);
            label1.BindingContext = dm;

            Label label2 = new Label();
            label2.SetBinding(Label.TextProperty, "Selected", BindingMode.TwoWay, null, null);
            label2.BindingContext = dm;

            StackLayout sl = new StackLayout();
            sl.Orientation = StackOrientation.Horizontal;
            sl.Children.Add(img);
            sl.Children.Add(label1);
            sl.Children.Add(label2);

            ViewCell vc = new ViewCell();
            vc.BindingContext = dm;
            vc.View = sl;
            vc.Tapped += Vc_Tapped;

            tableSection.Add(vc);
        }
    }

    private void Vc_Tapped(object sender, EventArgs e)
    {
        ViewCell vc = (ViewCell)sender;
        MyDataModel dm = (MyDataModel)vc.BindingContext;

        MyDataModel currSel = vm.Data.FirstOrDefault(d => d.Selected == true);

        if (currSel != null)
            currSel.Selected = false;

        dm.Selected = true;
    }
}

ViewModel:

public class MyViewModel
{
    public ObservableCollection<MyDataModel> Data { get; set; }

    public MyViewModel()
    {
        Data = new ObservableCollection<MyDataModel>
        {
            new MyDataModel {MyLabel = "Label 1", MyImage = "image.png", Selected = false },
            new MyDataModel {MyLabel = "Label 2", MyImage = "image.png", Selected = false },
            new MyDataModel {MyLabel = "Label 3", MyImage = "image.png", Selected = false },
            new MyDataModel {MyLabel = "Label 4", MyImage = "image.png", Selected = false },
            new MyDataModel {MyLabel = "Label 5", MyImage = "image.png", Selected = false }
        };
    }
}

模型:

public class MyDataModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    private string myLabel;
    public string MyLabel
    {
        get { return myLabel; }
        set
        {
            if (value != myLabel)
            {
                myLabel = value;
                PropertyChanged(this, new PropertyChangedEventArgs("MyLabel"));
            }
        }
    }
    private string myImage;
    public string MyImage
    {
        get { return myImage; }
        set
        {
            if (value != myImage)
            {
                myImage = value;
                PropertyChanged(this, new PropertyChangedEventArgs("MyImage"));
            }
        }
    }
    private bool selected;
    public bool Selected
    {
        get { return selected; }
        set
        {
            if (value != selected)
            {
                selected = value;
                PropertyChanged(this, new PropertyChangedEventArgs("Selected"));
            }
        }
    }
}

2
您应该使用绑定到您的视图模型,而不是手动设置值。以下文章应该对您有所帮助:https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/。为了更方便地进行示例编写,我使用了一个ListView,因为您实际上正在创建一个列表。
XAML:
<StackLayout>

        <ListView x:Name="CategoriesList" ItemsSource="{Binding Categories}">

            <ListView.ItemTemplate>

                <DataTemplate>

                    <ViewCell>

                        <StackLayout HorizontalOptions="FillAndExpand"
                                     Padding="20,0,20,0"
                                     VerticalOptions="CenterAndExpand">

                            <StackLayout HorizontalOptions="StartAndExpand">

                                <Label Text="{Binding Name}" />

                            </StackLayout>

                            <StackLayout HorizontalOptions="StartAndExpand">

                                <Label Text="{Binding Count" />

                                <Image IsVisible="{Binding Selected}" Source="right1.png" />

                            </StackLayout>


                        </StackLayout>

                    </ViewCell>

                </DataTemplate>

            </ListView.ItemTemplate>

        </ListView>

    </StackLayout>

C#: 在您的页面构造函数中添加对此方法的调用: 还要确保将绑定上下文设置为您的视图模型!

public PageConstructor(PageViewModel viewModel)
        {
            this.BindingContext = viewModel;
            Listeners();
        }

private void Listeners()
        {
            CategoriesList.ItemTapped += (sender, e) =>
            {
                if (category.Selected == false)
                {
                    App.DB.UpdateSelected(true);
                }
                else
                {
                    App.DB.UpdateSelected(false);
                }
                categories = getCategories();
                totalPhraseCount = getTotalPhraseCount();
                Title = totalPhraseCount.ToString() + " phrases";
            };
        }

您的ViewModel应该具有一个Categories属性,并实现INotifyPropertyChanged接口:
ViewModel C#:
class PageViewModel : INotifyPropertyChanged
    {
        List<Category> Categories;
    public event PropertyChangedEventHandler PropertyChanged;

    public PageViewModel()
    {
        this.Categories = //API call to retrieve the categories
    }

    public List<Category> Categories
    {
        set
        {
            if (Categories != value)
            {
                Categories = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this,
                        new PropertyChangedEventArgs("Categories"));
                }
            }
        }
        get
        {
            return Categories;
        }
    }
}

如果您想实现更好的MVVM模型,可以尝试以下解决方案:


感谢您的建议,@Lucas。我知道如何在ListView中实现这一点,但我被特别告知要使用TableView,因此提出了我的问题。 - user6742877
我不想让你感到烦恼,但我不确定 TableView 是否适合你。如果你查看文档,它指出 TableView 应该用于静态数据,这也是为什么它没有 ItemTemplate 属性的原因。此外,它设计用于呈现每行数据不同的数据,这也不符合你的情况。文档可以在 https://developer.xamarin.com/guides/xamarin-forms/user-interface/tableview/#Use_Cases 找到。 - Lucas Moura Veloso

-1

请尝试以下操作

categories = getCategories();
foreach (var category in categories)
{
   var viewCell = new ViewCell 
   { 
      View = new StackLayout()
      {
         Padding = new Thickness(20, 0, 20, 0),
         HorizontalOptions = LayoutOptions.FillAndExpand,
         Children = { 
            new StackLayout() {
               Orientation = StackOrientation.Horizontal,
               VerticalOptions = LayoutOptions.CenterAndExpand,
               Children = {
                  new StackLayout() {
                     HorizontalOptions = LayoutOptions.StartAndExpand,
                     Children = {
                        new Label { Text = category.Name}
                      },
                  new StackLayout() {
                     HorizontalOptions = LayoutOptions.EndAndExpand,
                     Orientation = StackOrientation.Horizontal,
                     Children = {
                        new Label { Text = category.Count},
                        new Image { Source = "right1.png",
                                    IsVisible = category.Selected }
                     }
                  }
               }
            }
         }
       }
    };
    viewCell.Tapped += (sender, e) =>
    {
       if (category.Selected == false)
       {
          App.DB.UpdateSelected(true);
       }
       else
       {
          App.DB.UpdateSelected(false);
       }
       categories = getCategories();
       totalPhraseCount = getTotalPhraseCount();
       Title = totalPhraseCount.ToString() + " phrases";
     };

}
tableSection.Add(viewCell);
tableSection.ForceLayout();
// Or try tableview.Forcelayout();
// Or try parent_stacklayout.ForceLayout();

谢谢@Atul。但是这仍然不起作用。我的TableView仍然没有更新。 - user6742877
你能否给我发送一个小的样例项目,这样我就可以为您完成它? - Atul
Is it possible for you? - Atul
1
也尝试将我的最后三个语句放在循环之外。 - Atul
这个解决方案在安卓手机上的性能会很差。应该避免调用 ForceLayout(),因为它会使布局无效,并导致所有布局计算再次运行。你还在循环中调用了它,这在 Xamarin.Forms 中是不应该做的。 - BrewMate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接