【问题标题】:Xamarin Forms Switch Toggled event doesn't bind with viewmodelXamarin Forms Switch Toggled 事件未与视图模型绑定
【发布时间】: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 框架

【问题讨论】:

    标签: xamarin mvvm xamarin.forms


    【解决方案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}"
                            />
      

      只需执行两个步骤。

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

      在您的 ContentPage 类中

      void OnToggled(object sender, ToggledEventArgs e){
      
      }
      
      1. 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.....);
        

        请特别注意粗体部分。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-06-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-08
          • 1970-01-01
          相关资源
          最近更新 更多