【问题标题】:EventTrigger in WPFWPF 中的事件触发器
【发布时间】:2019-08-23 15:40:03
【问题描述】:

我想从通过 InvokeCommandAction 选择 Checkbox 的 Listbox 中获取 SelectedItems 并将它们存储在 obsevableCollection SelectedItems 中,但我没有让 SelectedItemChangedCommand 正常工作(断点未命中)并且不确定如何填充SelectedItems 集合中的项目。我尝试了以下操作,希望一旦选中或取消选中复选框,就会调用 SelectedItemChangedCommand 并且我可以调用一个方法来填充 SelectedItems

请注意,我正在寻找一种无需任何代码即可实现此目的的方法。

 <ListBox Margin="45,7,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2"
         ItemsSource="{Binding ListItems}"
         SelectionMode="Multiple" Height="146">
    <ListBox.Resources>
        <Style TargetType="ListBoxItem">
            <Setter Property="OverridesDefaultStyle" Value="true" />
            <Setter Property="SnapsToDevicePixels" Value="true" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <CheckBox Margin="5,2"
                                  IsChecked="{TemplateBinding IsSelected}">
                            <ContentPresenter />
                        </CheckBox>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.Resources>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"  CommandParameter="{Binding ElementName=myListBox, Path=SelectedItem}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

更新的 Xaml 文件

<Window x:Class="stack.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:stack"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <ListBox x:Name="myListBox" Margin="45,7,0,0" VerticalAlignment="Top" 

                 ItemsSource="{Binding ListItems}"
                 SelectionMode="Multiple" Height="146">
            <ListBox.Resources>
            <Style TargetType="ListBoxItem">
                <Setter Property="OverridesDefaultStyle" Value="true" />
                <Setter Property="SnapsToDevicePixels" Value="true" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListBoxItem">
                            <CheckBox 
                                Margin="5,2" 
                                IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}}" 
                            >
                                <ContentPresenter />
                            </CheckBox>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.Resources>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"  
                                       CommandParameter="{Binding ElementName=myListBox, Path=SelectedItem}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
        </ListBox>
    </Grid>
</Window>

我将列表框绑定到在视图模型中定义的 observableCollection ListItems

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace stack
{


    public class MainViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<string> ListItems { get; set; }
        public ObservableCollection<string> SelectedListItems { get; set; }

        public RelayCommand SelectedItemChangedCommand { get; set; }

        public string _selectedItem;
        public string SelectedItem
        {
            get => _selectedItem;
            set
            {
                _selectedItem = value;
                OnPropertyChanged("SelectedItem");
            }
        }

        public bool _isSelected;
        public bool IsSelected
        {
            get => _isSelected;
            set
            {
                _isSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }

        public MainViewModel()
        {
            ListItems = new ObservableCollection<string>();
            ListItems.Add("One");
            ListItems.Add("Two");
            ListItems.Add("three");
            ListItems.Add("Four");
            ListItems.Add("Five");
            SelectedItemChangedCommand = new RelayCommand(this.ExecuteItemChanged);
        }

        public void ExecuteItemChanged(object parameter)
        {
            if (IsSelected)
            {
                SelectedListItems.Add(SelectedItem);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler _propertyChangedEventHandler = PropertyChanged;
            _propertyChangedEventHandler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

【问题讨论】:

    标签: wpf data-binding


    【解决方案1】:

    TemplateBinding 很便宜,但它不做双向绑定。因此,项目没有被选中。您需要使用 RelativeSourceTemplatedParent 的常规绑定:

    <CheckBox 
        Margin="5,2" 
        IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}}" 
        >
    

    请注意,您将CheckBox.IsChecked 绑定到ListBoxItem.IsSelected。模板化的父级是 ListBoxItem,而不是您的主视图模型。

    其次,您想将 SelectedItems 复数属性传递给您的命令。 SelectedItem单数。这只是一项。 It'll be the topmost selected item when many are selected.您的列表框上有 SelectionMode="Multiple",所以我假设您想要完整的选择。

    <i:InvokeCommandAction 
        Command="{Binding SelectedItemChangedCommand}" 
        CommandParameter="{Binding ElementName=myListBox, Path=SelectedItems}"
        />
    

    并确保 ListBox 上有 x:Name="myListBox":CommandParameter 绑定需要它来查找 SelectedItems。

    最后:ExecuteItemChanged() 非常糟糕。您的主要视图模型属性 IsSelectedSelectedItem 没有绑定到任何东西。它们总是假的和空的。每次选择更改时,执行命令并将第一个选定项作为parameter传递,然后忽略它并查看false是否仍然是false,即这是。如果不是,您的 viewmodel 的 SelectedItem 属性仍将为空,因为您也从未更新过它。

    Here's what you want to do: When the selection changes, pass the entire collection of currently selected items into your command.用控件的当前状态替换视图模型的当前选定项目的整个集合。您必须,必须,必须将 SelectedItems 绑定为上面的 CommandParameter。

    去掉视图模型上的SelectedItemIsSelected,它们没有任何作用。

    如果可能的话,永远不要从事维护两个列表并试图使它们保持同步的业务。它总是一团糟。在这种情况下,您不需要这样做。

    public void ExecuteItemChanged(object parameter)
    {
        //  ListBox.SelectedItems is System.Windows.Controls.SelectedItemCollection,
        //  a precambrian monster that's declared internal in PresentationFramework.dll. 
        //  However, it does implement non-generic IList, so cast it to that. 
        if (parameter is System.Collections.IList selectedItems)
        {
            if (SelectedListItems == null)
            {
                SelectedListItems = new ObservableCollection<String>();
            }
    
            SelectedListItems.Clear();
    
            foreach (string item in selectedItems)
            {
                SelectedListItems.Add(item);
            }
        }
    }
    

    【讨论】:

    • 谢谢,我根据您的建议更新了 Xaml 文件,但仍然无法填充所选项目 :-(,我现在已将完成的 ViewModel 类粘贴到此处。我想我还缺少其他内容
    • ExecuteItemChanged(object parameter) 坏了。视图模型的SelectedItem 属性是什么?无效的。它没有绑定。您费尽心思将列表框中的第一个选定项目作为parameter 传递给命令,然后忽略它。视图模型上的IsSelected 是什么?这是假的。你从不绑定它,你从不设置它。为什么它会神奇地与 UI 中某处关闭的整个复选框集合中的某个随机属性具有相同的值?
    • 是的,我想从列表框中进行多项选择,如您所见,列表框中有复选框,我看过一些帖子,他们在后面的代码中使用事件处理程序,但我想在没有的情况下实现这一点使用后面的任何代码。我是 MVVM 的新手,所以第一次尝试一下
    • @RajeevVerma 在需要时使用代码隐藏。 MVVM 并不意味着“没有代码隐藏”;它的意思是“对属于视图的逻辑使用代码隐藏,对属于视图模型的逻辑使用视图模型”。通常没有太多属于视图的逻辑,但通常有一些,并且代码隐藏可以满足其预期目的。
    • 了解,我正在尝试多种方法,并在此处分块粘贴代码,结果导致粘贴此版本的 ExecuteItemChanged() 以及那些未使用的属性 感谢您的回复和对代码的解释支持 +1 以供您对此发表评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多