Xamarin Forms Switch Toggled 事件未与视图模型绑定

Posted

技术标签:

【中文标题】Xamarin Forms Switch Toggled 事件未与视图模型绑定【英文标题】:Xamarin Forms Switch Toggled event doesn't bind with viewmodel 【发布时间】:2017-04-25 12:48:54 【问题描述】:

我有一个表单 XAML 页面,其中有一个列表视图,每个元素都有一个 Switch(xamarin 默认)。我可以将项目中的数据绑定到列表视图,但我不能订阅 Switch 事件“Toggled”,因为它会导致项目不显示。我也尝试使用 ICommand 和 Command,因为它被指示使用按钮,但结果是一样的,没有显示。如何处理来自我的视图模型的开关切换?

查看

    <?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="TouristicWallet.Views.WalletManagementPage"
             xmlns:vm="clr-namespace:TouristicWallet.ViewModels"
             xmlns:converters="clr-namespace:TouristicWallet.Converters"
             >

  <ContentPage.BindingContext>
    <vm:WalletManagementViewModel x:Name="ViewModel"/>
  </ContentPage.BindingContext>

  <ContentPage.Resources>
    <ResourceDictionary>
      <converters:CurrencyIdToCodeConverter x:Key="idToCodeConverter"/>
    </ResourceDictionary>
  </ContentPage.Resources>

  <StackLayout>
    <ListView x:Name="MyCurrencies" ItemsSource="Binding Currencies, Mode=OneWay">
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <StackLayout Orientation="Horizontal">
              <Label Text="Binding Currency.Initials, Mode=OneWay" />
              <Switch IsToggled="Binding IsOwned, Mode=TwoWay"
                      Toggled="Binding Toggled"
                      />
            </StackLayout>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>

  </StackLayout>
</ContentPage>

视图模型

public class WalletManagementViewModel : ViewModelBase


    private readonly List<OwnedCurrencyWrapper> _currencies = new List<OwnedCurrencyWrapper>();
    public List<OwnedCurrencyWrapper> Currencies  get  return _currencies;  

    public WalletManagementViewModel()
    
        CurrencyDataAccess cda = new CurrencyDataAccess();
        foreach (var item in cda.GetCurrencies())
        
            Currencies.Add(new OwnedCurrencyWrapper(item));
        

        OnPropertyChanged(nameof(Currencies));
    

    public class OwnedCurrencyWrapper
    
        public Currency Currency  get; private set; 
        public Boolean IsOwned  get; set; 
        public ICommand Toggled  get; set; 


        public OwnedCurrencyWrapper(Currency currency)
        
            Currency = currency;
            WalletDataAccess wda = WalletDataAccess.Instance;
            IsOwned = wda.IsOwned(Currency.Id);

            Toggled = new Command(() => Update());
        

        public void Update()
        
            WalletDataAccess wda = WalletDataAccess.Instance;
            if (IsOwned) wda.RemoveOwnedCurrency(Currency.Id);
            else wda.OwnCurrency(Currency.Id);

        

        public void Toggled_handler(object sender, ToggledEventArgs e)
        
            Update();
        
    

我没有使用任何 mvvm 框架

【问题讨论】:

【参考方案1】:

首先,Switch 无法绑定到Command。看: https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/#Commanding_with_ViewModels

从上面可以看出,可以绑定到ICommand 的 Forms 控件有:

按钮 菜单项 工具栏项 搜索栏 TextCell(因此也 图像单元) 列表视图 TapGestureRecognizer

您只需执行以下操作即可在 View 的代码隐藏文件中运行代码,请在 XAML 中执行此操作:

<Switch IsToggled="Binding IsOwned, Mode=TwoWay"
        Toggled="Handle_Toggled" />

然后在代码隐藏文件中:

void Handle_Toggled(object sender, Xamarin.Forms.ToggledEventArgs e)

    // Do stuff

另外,由于您正在绑定,您可以在实际的 OwnedCurrencyWrapper 类中运行代码(这似乎是您想要的),只需将代码添加到 IsOwned 的设置器即可。在这种情况下,不要为你的开关的 Toggled 属性分配任何东西::

<Switch IsToggled="Binding IsOwned, Mode=TwoWay" />

然后在你的OwnedCurrencyWrapper 类中:

bool _isOwned;
public bool IsOwned  
    get 
    
        return _isOwned;
     
    set
    
        _isOwned = value;
        // Do any other stuff you want here
    

也就是说,您的绑定不完整,因为您的视图模型没有实现INotifyPropertyChanged,因此直接对视图模型所做的更改不会反映在 UI 中。有关与 Forms MVVM 绑定的更多信息,请参阅: https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/

更新:我不知道 Xamarin 表单中的行为。看: https://github.com/xamarin/xamarin-forms-samples/tree/master/Behaviors/EventToCommandBehavior

在命令的上下文中,行为是将控件连接到命令的有用方法。此外,它们还可用于将命令与未设计用于与命令交互的控件相关联。此示例演示了在事件触发时使用行为来调用命令。

所以这应该允许您将 Toggled 事件绑定到命令。

【讨论】:

我正在实现 INotifyPropertyChanged,它在我的自定义 ViewModelBase 类中,我的所有 ViewModel 都在扩展。你的答案(两者,我现在在我的代码中保留第二种方式)实际上是我解决问题的方式,但这不是很像“MVVM”。 这是另一个选项,行为:github.com/xamarin/xamarin-forms-samples/tree/master/Behaviors/…“行为是将控件连接到命令的有用方法。此外,它们还可用于将命令与并非旨在与命令交互。此示例演示了在事件触发时使用行为来调用命令。抱歉,我之前不知道行为。 只需将 IsToggled 绑定到模型中的属性并在 Getter/Setter 中实现您的逻辑。【参考方案2】:

如果您遵循 Prism 框架,您可以轻松地将事件连接到命令。您的 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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
                 xmlns:b="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
                 x:Class="TouristicWallet.Views.WalletManagementPage">
    <ContentPage.Content>
        <StackLayout VerticalOptions="CenterAndExpand" Padding="20">
            <Switch IsToggled="Binding IsOwned"  x:Name="IsOwnedSwitch">
                <Switch.Behaviors>
                    <b:EventToCommandBehavior EventName="Toggled"  Command="Binding ToggleIsOwnedCommand"/>
                </Switch.Behaviors>
            </Switch>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

【讨论】:

【参考方案3】:

正如其他人所提到的,您应该将 Toggled 事件绑定到将转发命令的 eventHandler 行为。可以使用下面的代码。

<Switch IsToggled="Binding SwitchEnabled"  x:Name="MySwitch">
    <Switch.Behaviors>
    <!-- behaviors namespace comes from "Xamarin.Forms Behaviors" nuget  -->
        <behaviors:EventHandlerBehavior EventName="Toggled">
            <behaviors:InvokeCommandAction Command="Binding ToggleSwitchCommand" />
        </behaviors:EventHandlerBehavior>
    </Switch.Behaviors>
</Switch>

【讨论】:

这对你有用吗?因为我已经设法让事件命令行为适用于其他控件,但无法让它与此一起使用 是的,它对我有用。它可能是您缺少的一些小东西。如果你愿意,我可以给你另一双眼睛。我在 Xamarin 论坛上使用相同的用户名,名字和姓氏之间没有空格。给我留言,如果你愿意,我会看看。 非常感谢,明天我会回到办公室 我无法添加行为我收到此错误:错误:“行为”未声明 @lyndonhughey 谢谢我已经下载了github.com/nuitsjp/Xamarin.Forms.BehaviorsPack,它运行良好。【参考方案4】:

解决方案:在做了一些研发后,我找到了这个问题的根本原因,

第一篇文章中的错误代码:

<Switch IsToggled="Binding IsOwned, Mode=TwoWay"
                      Toggled="Binding Toggled"
                      />

只需执行两个步骤。

    ContentPage 类中声明事件侦听器函数 OnToggled 并且不在您需要绑定的 ViewModel 类中

在您的 ContentPage 类中

void OnToggled(object sender, ToggledEventArgs e)


    Toggled="Binding Toggled" == 更改为 ==> Toggled="OnToggled"

它将解决问题,不知道为什么它不适用于在 ViweModel 类中声明的事件侦听器函数

--我希望它会起作用。

【讨论】:

【参考方案5】:

我遇到了同样的问题,并以非常简单的方式解决了它。

=> 目标:在列表视图中获取带有开关控件的项目以响应命令。

<?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="TouristicWallet.Views.WalletManagementPage"
             xmlns:vm="clr-namespace:TouristicWallet.ViewModels"
             x:Name="pageName"
             xmlns:converters="clr-namespace:TouristicWallet.Converters"
             >

  <ContentPage.BindingContext>
    <vm:WalletManagementViewModel x:Name="ViewModel"/>
  </ContentPage.BindingContext>

  <ContentPage.Resources>
    <ResourceDictionary>
      <converters:CurrencyIdToCodeConverter x:Key="idToCodeConverter"/>
    </ResourceDictionary>
  </ContentPage.Resources>

  <StackLayout>
    <ListView x:Name="MyCurrencies" ItemsSource="Binding Currencies, Mode=OneWay">
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <StackLayout Orientation="Horizontal">
              <Label Text="Binding Currency.Initials, Mode=OneWay" />
  

 <Switch IsToggled="Binding Selected"   HorizontalOptions="Start">
                                  <Switch.Behaviors>
                                            <b:EventToCommandBehavior 
                                              EventName="Toggled"  Command=" 
                                                    Binding                                                 
                                                      Path=BindingContext.SendCommand, 
                                                    Source=x:Reference 
                                                   Name=pageName" />
                                            </Switch.Behaviors>
                                        </Switch>
            </StackLayout>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>

  </StackLayout>
</ContentPage>

在视图模型中 定义你的命令 /ICommand

public ICommand SendCommand  get; set; 

SendCommand = new Command(() => //do something.....);

请特别注意粗体部分。

【讨论】:

以上是关于Xamarin Forms Switch Toggled 事件未与视图模型绑定的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin.Forms:Forms.Context 已过时

Xamarin.Forms 手势密码实现

Xamarin.Forms 和 Xamarin Native 有啥区别? [关闭]

如何使用 Xamarin.Forms.Maps(无 Xamarin.Forms.GoogleMaps)在地图中应用样式或更改颜色

Xamarin Forms Prism:prism ResourceDictionary 中已存在具有键“Xamarin.Forms.NavigationPage”的资源

Xamarin.Forms.Forms.Init(e) Onlaunched 中的 FileNotFoundExeception