Xamarin Grouped ListView - 除非我滚动,否则编辑项目不会更新 UI
Xamarin Grouped ListView - Editing Item doesn't update UI unless I scroll
所以我正在尝试在 Xamarin 中构建自定义分组多选,但我遇到了 UI 未正确更新的问题。
这是我的视图代码:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="testApp.Views.TestPage"
xmlns:viewmodels="clr-namespace:testApp.ViewModels">
<ContentPage.BindingContext>
<viewmodels:TestViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<ListView ItemsSource="{Binding ItemsGrouped}"
CachingStrategy="RecycleElement"
GroupDisplayBinding="{Binding GroupTitle}"
IsGroupingEnabled="true"
GroupShortNameBinding="{Binding GroupTitle}"
Margin="20"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="25">
<Label Text="{Binding GroupTitle}" TextColor="Black" VerticalOptions="Center"/>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Margin="20,0,0,0">
<!--<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:TestViewModel}}, Path=OnItemTappedCommand}" CommandParameter="{Binding .}" />
</Grid.GestureRecognizers>-->
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding Heading}" FontAttributes="Bold" />
<Label Grid.Row="1" Grid.Column="0" Text="{Binding SubText}" />
<Image Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Source="Checkmark" HorizontalOptions="End" IsVisible="{Binding Selected}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
这是我的视图模型的代码:
using testApp.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Xamarin.Forms;
namespace testApp.ViewModels
{
public class TestViewModel : BaseViewModel
{
private ObservableCollection<MultiSelectItem> items;
public ObservableCollection<MultiSelectItem> Items
{
get { return items; }
set { items = value; OnPropertyChanged(nameof(Items)); }
}
private ObservableCollection<MultiSelectItemGrouping<string, MultiSelectItem>> itemsGrouped;
public ObservableCollection<MultiSelectItemGrouping<string, MultiSelectItem>> ItemsGrouped
{
get { return itemsGrouped; }
set { itemsGrouped = value; OnPropertyChanged(nameof(ItemsGrouped)); }
}
private MultiSelectItem selectedItem;
public MultiSelectItem SelectedItem
{
get { return selectedItem; }
set
{
MultiSelectItem item = value;
if (item != null)
{
bool found = false;
foreach (var result in Items)
{
if (result.Equals(item))
{
found = true;
item.Selected = !item.Selected;
item.SubText = "I was selected";
break;
}
}
if (found)
{
GroupData();
}
value = null;
}
selectedItem = value;
OnPropertyChanged(nameof(SelectedItem));
}
}
public Command OnItemTappedCommand { get; }
public TestViewModel()
{
Items = GetData();
GroupData();
OnItemTappedCommand = new Command(OnItemTapped);
}
public void GroupData()
{
IEnumerable<MultiSelectItemGrouping<string, MultiSelectItem>> groupedItems = Items.GroupBy(x => x.GroupName).Select(group => new MultiSelectItemGrouping<string, MultiSelectItem>(group.Key, group));
ItemsGrouped = new ObservableCollection<MultiSelectItemGrouping<string, MultiSelectItem>>(groupedItems);
}
private void OnItemTapped(object parameter)
{
MultiSelectItem item = (MultiSelectItem)parameter;
item.Selected = !item.Selected;
}
private ObservableCollection<MultiSelectItem> GetData()
{
return new ObservableCollection<MultiSelectItem>()
{
new MultiSelectItem()
{
GroupName = "TestCompany",
Heading = "TestCompany1",
SubText = "TestCompanySub1"
},
new MultiSelectItem()
{
GroupName = "Bruh",
Heading = "Bruh2",
SubText = "BruhSub2",
Selected = true
},
new MultiSelectItem()
{
GroupName = "TestCompany",
Heading = "TestCompany2",
SubText = "TestCompanySub2"
},
new MultiSelectItem()
{
GroupName = "Blergh",
Heading = "Blergh2",
SubText = "BlerghSub2"
},
new MultiSelectItem()
{
GroupName = "TestCompany",
Heading = "TestCompany3",
SubText = "TestCompanySub3",
Selected = true
},
new MultiSelectItem()
{
GroupName = "Blergh",
Heading = "Blergh1",
SubText = "BlerghSub1"
},
new MultiSelectItem()
{
GroupName = "Cat",
Heading = "I Like Cats",
SubText = "Yup",
Selected = true
},
new MultiSelectItem()
{
GroupName = "Bruh",
Heading = "Bruh1",
SubText = "BruhSub1"
},
new MultiSelectItem()
{
GroupName = "Bruh",
Heading = "Bruh3",
SubText = "BruhSub3"
},
new MultiSelectItem()
{
GroupName = "Bruh",
Heading = "Bruh4",
SubText = "BruhSub4"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test1",
SubText = "TestSub1",
Selected = true
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test2",
SubText = "TestSub2"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test3",
SubText = "TestSub3"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test4",
SubText = "TestSub4",
Selected = true
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test5",
SubText = "TestSub5"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test6",
SubText = "TestSub6"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test7",
SubText = "TestSub7"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test8",
SubText = "TestSub8"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test9",
SubText = "TestSub9"
},
};
}
}
}
TestViewModel 继承自的我的 BaseViewModel 实现了 INotifyPropertyChanged,它在所有其他屏幕上都能正常工作。当我单步执行代码时,我可以看到 SubText 和 Selected 属性发生变化,但 UI 永远不会更新。但是,如果我滚动出视图,然后返回视图,它会更新。
因为我使用的是 MVVM,所以我不能完全刷新 ListView,但这看起来很奇怪。
我们将不胜感激任何帮助。
编辑:这里要求的是 MultiSelectItem 的代码 class
using System;
using System.Collections.Generic;
using System.Text;
namespace testApp.Models
{
public class MultiSelectItem
{
public string GroupName { get; set; }
public string Heading { get; set; }
public string SubText { get; set; }
public int ID { get; set; }
public bool Selected { get; set; }
}
}
正如 Jason 所说,问题出在 MultiSelectItem class 上。我没有意识到它也需要实现 INotifyPropertyChanged,但它确实需要。将其添加到修复的所有内容中。
所以我正在尝试在 Xamarin 中构建自定义分组多选,但我遇到了 UI 未正确更新的问题。
这是我的视图代码:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="testApp.Views.TestPage"
xmlns:viewmodels="clr-namespace:testApp.ViewModels">
<ContentPage.BindingContext>
<viewmodels:TestViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<ListView ItemsSource="{Binding ItemsGrouped}"
CachingStrategy="RecycleElement"
GroupDisplayBinding="{Binding GroupTitle}"
IsGroupingEnabled="true"
GroupShortNameBinding="{Binding GroupTitle}"
Margin="20"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="25">
<Label Text="{Binding GroupTitle}" TextColor="Black" VerticalOptions="Center"/>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Margin="20,0,0,0">
<!--<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:TestViewModel}}, Path=OnItemTappedCommand}" CommandParameter="{Binding .}" />
</Grid.GestureRecognizers>-->
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding Heading}" FontAttributes="Bold" />
<Label Grid.Row="1" Grid.Column="0" Text="{Binding SubText}" />
<Image Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Source="Checkmark" HorizontalOptions="End" IsVisible="{Binding Selected}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
这是我的视图模型的代码:
using testApp.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Xamarin.Forms;
namespace testApp.ViewModels
{
public class TestViewModel : BaseViewModel
{
private ObservableCollection<MultiSelectItem> items;
public ObservableCollection<MultiSelectItem> Items
{
get { return items; }
set { items = value; OnPropertyChanged(nameof(Items)); }
}
private ObservableCollection<MultiSelectItemGrouping<string, MultiSelectItem>> itemsGrouped;
public ObservableCollection<MultiSelectItemGrouping<string, MultiSelectItem>> ItemsGrouped
{
get { return itemsGrouped; }
set { itemsGrouped = value; OnPropertyChanged(nameof(ItemsGrouped)); }
}
private MultiSelectItem selectedItem;
public MultiSelectItem SelectedItem
{
get { return selectedItem; }
set
{
MultiSelectItem item = value;
if (item != null)
{
bool found = false;
foreach (var result in Items)
{
if (result.Equals(item))
{
found = true;
item.Selected = !item.Selected;
item.SubText = "I was selected";
break;
}
}
if (found)
{
GroupData();
}
value = null;
}
selectedItem = value;
OnPropertyChanged(nameof(SelectedItem));
}
}
public Command OnItemTappedCommand { get; }
public TestViewModel()
{
Items = GetData();
GroupData();
OnItemTappedCommand = new Command(OnItemTapped);
}
public void GroupData()
{
IEnumerable<MultiSelectItemGrouping<string, MultiSelectItem>> groupedItems = Items.GroupBy(x => x.GroupName).Select(group => new MultiSelectItemGrouping<string, MultiSelectItem>(group.Key, group));
ItemsGrouped = new ObservableCollection<MultiSelectItemGrouping<string, MultiSelectItem>>(groupedItems);
}
private void OnItemTapped(object parameter)
{
MultiSelectItem item = (MultiSelectItem)parameter;
item.Selected = !item.Selected;
}
private ObservableCollection<MultiSelectItem> GetData()
{
return new ObservableCollection<MultiSelectItem>()
{
new MultiSelectItem()
{
GroupName = "TestCompany",
Heading = "TestCompany1",
SubText = "TestCompanySub1"
},
new MultiSelectItem()
{
GroupName = "Bruh",
Heading = "Bruh2",
SubText = "BruhSub2",
Selected = true
},
new MultiSelectItem()
{
GroupName = "TestCompany",
Heading = "TestCompany2",
SubText = "TestCompanySub2"
},
new MultiSelectItem()
{
GroupName = "Blergh",
Heading = "Blergh2",
SubText = "BlerghSub2"
},
new MultiSelectItem()
{
GroupName = "TestCompany",
Heading = "TestCompany3",
SubText = "TestCompanySub3",
Selected = true
},
new MultiSelectItem()
{
GroupName = "Blergh",
Heading = "Blergh1",
SubText = "BlerghSub1"
},
new MultiSelectItem()
{
GroupName = "Cat",
Heading = "I Like Cats",
SubText = "Yup",
Selected = true
},
new MultiSelectItem()
{
GroupName = "Bruh",
Heading = "Bruh1",
SubText = "BruhSub1"
},
new MultiSelectItem()
{
GroupName = "Bruh",
Heading = "Bruh3",
SubText = "BruhSub3"
},
new MultiSelectItem()
{
GroupName = "Bruh",
Heading = "Bruh4",
SubText = "BruhSub4"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test1",
SubText = "TestSub1",
Selected = true
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test2",
SubText = "TestSub2"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test3",
SubText = "TestSub3"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test4",
SubText = "TestSub4",
Selected = true
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test5",
SubText = "TestSub5"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test6",
SubText = "TestSub6"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test7",
SubText = "TestSub7"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test8",
SubText = "TestSub8"
},
new MultiSelectItem()
{
GroupName = "Test",
Heading = "Test9",
SubText = "TestSub9"
},
};
}
}
}
TestViewModel 继承自的我的 BaseViewModel 实现了 INotifyPropertyChanged,它在所有其他屏幕上都能正常工作。当我单步执行代码时,我可以看到 SubText 和 Selected 属性发生变化,但 UI 永远不会更新。但是,如果我滚动出视图,然后返回视图,它会更新。
因为我使用的是 MVVM,所以我不能完全刷新 ListView,但这看起来很奇怪。
我们将不胜感激任何帮助。
编辑:这里要求的是 MultiSelectItem 的代码 class
using System;
using System.Collections.Generic;
using System.Text;
namespace testApp.Models
{
public class MultiSelectItem
{
public string GroupName { get; set; }
public string Heading { get; set; }
public string SubText { get; set; }
public int ID { get; set; }
public bool Selected { get; set; }
}
}
正如 Jason 所说,问题出在 MultiSelectItem class 上。我没有意识到它也需要实现 INotifyPropertyChanged,但它确实需要。将其添加到修复的所有内容中。