Jon*_*eet 29 c# data-binding maui
这个问题是关于两个 MAUI 控件 (Switch和ListView) - 我在同一个问题中询问它们,因为我期望两个控件的问题根本原因是相同的。它们完全有可能是不同的问题,只是有一些共同的症状。(CollectionView有类似的问题,但其他混杂因素使得演示变得更加困难。)
我在 MAUI 应用程序中使用 2 路数据绑定:对数据的更改可以直接来自用户,也可以来自检查规范数据是否已在其他地方更改的后台轮询任务。我面临的问题是,对视图模型的更改不会在视觉上传播到Switch.IsToggled和ListView.SelectedItem属性,即使控件确实引发了事件,表明它们已经“注意到”属性更改。其他控件(例如Label和Checkbox)在视觉上进行了更新,表明视图模型通知工作正常并且 UI 本身总体上是健康的。
构建环境:Visual Studio 2022 17.2.0 预览版 2.1
应用程序环境:Android,模拟器“Pixel 5 - API 30”或真实 Pixel 6
示例代码全部如下,但根本问题是这是否是我的代码中的某个错误(我是否需要“告诉”控件因某种原因更新自身?)或者可能是 MAUI 中的错误(在这种情况下我应该大概报告一下)?
下面的示例代码可以直接添加到“文件新项目”MAUI 应用程序(名称为“MauiPlayground”以使用相同的命名空间),或者可以从我的演示代码存储库中获取。每个示例都是独立的 - 您可以只尝试一个。(然后更新App.cs以设置MainPage正确的示例。)
这两个示例都有一个非常简单的情况:一个双向绑定到视图模型的控件,以及一个更新视图模型属性的按钮(以模拟真实应用程序中的“数据已在其他地方修改”)。在这两种情况下,控件在视觉上都保持不变。
请注意,我{Binding ..., Mode=TwoWay}在这两种情况下都指定了,即使这是这些属性的默认值,只是为了非常清楚地表明这不是问题。
该ViewModelBase代码由两个示例共享,并且只是一种便捷的引发方式,INotifyPropertyChanged.PropertyChanged无需任何额外的依赖项:
ViewModelBase.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MauiPlayground;
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool SetProperty<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
SwitchDemo.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiPlayground.SwitchDemo">
<StackLayout>
<Label Text="Switch binding demo" />
<HorizontalStackLayout>
<Switch x:Name="switchControl"
IsToggled="{Binding Toggled, Mode=TwoWay}"
Toggled="Toggled" />
<CheckBox IsChecked="{Binding Toggled, Mode=TwoWay}" />
<Label Text="{Binding Toggled}" />
</HorizontalStackLayout>
<Button Text="Toggle" Clicked="Toggle" />
<Label x:Name="manualLabel1" Text="Value set in button click handler" />
<Label x:Name="manualLabel2" Text="Value set in toggled handler" />
</StackLayout>
</ContentPage>
Run Code Online (Sandbox Code Playgroud)
SwitchDemo.cs
namespace MauiPlayground;
public partial class SwitchDemo : ContentPage
{
public SwitchDemo()
{
InitializeComponent();
BindingContext = new ViewModel();
}
private void Toggle(object sender, EventArgs e)
{
var vm = (ViewModel)BindingContext;
vm.Toggled = !vm.Toggled;
manualLabel1.Text = $"Set in click handler: {switchControl.IsToggled}";
}
private void Toggled(object sender, ToggledEventArgs e) =>
manualLabel2.Text = $"Set in toggled handler: {switchControl.IsToggled}";
private class ViewModel : ViewModelBase
{
private bool toggled;
public bool Toggled
{
get => toggled;
set => SetProperty(ref toggled, value);
}
}
}
Run Code Online (Sandbox Code Playgroud)
单击“切换”按钮后更新视图模型的模拟器屏幕截图:
笔记:
switch.IsToggled是正确的Switch.Toggled事件已引发Switch本身并没有改变可见状态Switch直接单击控件可以直观地切换它。
ListViewDemo.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiPlayground.ListViewDemo">
<StackLayout>
<Label Text="ListView binding demo" />
<ListView x:Name="listView" ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
VerticalOptions="Start"
ItemSelected="ItemSelected"/>
<Label Text="{Binding SelectedItem}" />
<Button Text="Toggle" Clicked="Toggle" />
<Label x:Name="manualLabel1" Text="Text set in button click handler" />
<Label x:Name="manualLabel2" Text="Text set in item selected handler" />
</StackLayout>
</ContentPage>
Run Code Online (Sandbox Code Playgroud)
ListViewDemo.cs
namespace MauiPlayground;
public partial class ListViewDemo : ContentPage
{
public ListViewDemo()
{
InitializeComponent();
BindingContext = new ViewModel();
}
private void Toggle(object sender, EventArgs e)
{
var vm = (ViewModel)BindingContext;
vm.SelectedItem = vm.SelectedItem == "First" ? "Second" : "First";
manualLabel1.Text = $"Set in click handler: {listView.SelectedItem}";
}
private void ItemSelected(object sender, EventArgs e) =>
manualLabel2.Text = $"Set in item selected handler: {listView.SelectedItem}";
private class ViewModel : ViewModelBase
{
public List<string> Items { get; } = new List<string> { "First", "Second" };
private string selectedItem = "First";
public string SelectedItem
{
get => selectedItem;
set => SetProperty(ref selectedItem, value);
}
}
}
Run Code Online (Sandbox Code Playgroud)
单击“切换”按钮后更新视图模型的模拟器屏幕截图:
笔记:
listView.SelectedItem具有新值ListView.ItemSelected事件已引发ListView本身似乎没有选定的项目有趣的是,列表视图实际上确实改变了外观:在单击按钮之前,第一个项目在视觉上被选择(橙色)。从列表中选择一个项目会手动更新所有属性,但我们看不到所选项目呈橙色。
r2d*_*igo 17
该问题Switch.IsToggled已知并已修复,但要等到下一个 RC 发布 (6.0.300-rc.1) 后才可用。
我还没有发现任何有关该ListView问题的报告问题,但我能够重现它。这似乎是由ViewCell为每个项目创建的默认值引起的。它可以通过指定一个自定义来修复,ListView.ItemTemplate如下所示:
<ListView x:Name="listView" ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
VerticalOptions="Start"
ItemSelected="ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7702 次 |
| 最近记录: |