首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >WPF MVVM ListBox MultiSelect

WPF MVVM ListBox MultiSelect
EN

Stack Overflow用户
提问于 2022-11-02 09:37:22
回答 2查看 52关注 0票数 -1

我已经创建了一个包含项目列表的列表框,我需要在选择更改(选择和取消选择)时绑定它们。

ABCD.xalm

代码语言:javascript
运行
复制
<ListBox Grid.Column="2" Grid.ColumnSpan="9" Height="30" Margin="0 0 5 0" Foreground="{StaticResource AcresTheme}" SelectedItem="{Binding Path=UpdateSimulationItem,UpdateSourceTrigger=PropertyChanged}"                               
                            ItemsSource="{Binding SmulationTypes, NotifyOnSourceUpdated=True}" 
                            Background="{Binding }"
                            MinHeight="65" SelectionMode="Multiple">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <CheckBox  Foreground="{StaticResource AcresTheme}"
                                           Content="{Binding Item}" 
                                           IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"></CheckBox>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                        <ListBox.ItemContainerStyle>
                            <Style TargetType="{x:Type ListBoxItem}">
                                <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
                            </Style>
                        </ListBox.ItemContainerStyle>
                    </ListBox>

ABCD.cs (视图模型)

代码语言:javascript
运行
复制
public List<string> SimulationTypesList { get; set; } = new List<string>();
    private ObservableCollection<SimulationType> _simulationTypes = new ObservableCollection<SimulationType>();

    public ObservableCollection<Items> SimulationTypes
    {
        get
        {
            return _simulationTypes;
        }
        set
        {
            _simulationTypes = value;
            OnPropertyChanged("SimulationTypes");
        }
    }

    private Items _updateSimulationItem;
    public Items UpdateSimulationItem
    {
        get
        {
            return _updateSimulationItem;
        }
        set
        {
          //Logic for getting the selected item
            _updateSimulationItem = value;
            OnPropertyChanged("UpdateSimulationItem");
        }
    }
public ABCD()
{
        SimulationTypes.Add(new SimulationType() { Item = "Simulation 1", IsSelected = false });
        SimulationTypes.Add(new SimulationType() { Item = "Simulation 2", IsSelected = false });
        SimulationTypes.Add(new SimulationType() { Item = "Simulation 3", IsSelected = false });
}

Items.cs

代码语言:javascript
运行
复制
public class Items: ViewModelBase
{
    private string item;
    public string Item
    {
        get { return item; }
        set
        {
            item = value;
            this.OnPropertyChanged("Item");
        }
    } 
    
    private bool isSelected;
    public bool IsSelected
    {
        get { return isSelected; }
        set
        {
            isSelected = value;
            this.OnPropertyChanged("IsSelected");
        }
    }
}

我确实尝试了在https://stackoverflow.com/a/34632944/12020323中给出的解决方案,这对于删除单个项或选择单个项都很有效。

当我们选择第二项时,它不会触发属性更改。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-11-10 08:33:27

@EldHasp,谢谢你花时间回答我的问题。对所提供的解决方案非常满意。

目前,我非常热衷于在ViewModel中使用完整的代码,我发现了我在ViewModel中所犯的错误。

旧法典:

代码语言:javascript
运行
复制
private ObservableCollection<SimulationType> _simulationTypes = new ObservableCollection<SimulationType>();

public ObservableCollection<Items> SimulationTypes
{
    get
    {
        return _simulationTypes;
    }
    set
    {
        _simulationTypes = value;
        OnPropertyChanged("SimulationTypes");
    }
}

private Items _updateSimulationItem;
public Items UpdateSimulationItem
{
    get
    {
        return _updateSimulationItem;
    }
    set
    {
        //Logic for getting the selected item
        _updateSimulationItem = value;
        OnPropertyChanged("UpdateSimulationItem");
    }
}

_updateSimulationItem =;将第一个选定的项绑定到UpdateSimulationItem,属性更改仅在更改该perticular项时才会触发。

例如:

SimulationTypes.Add(新项目=“模拟1",IsSelected =假};SimulationTypes.Add(新SimulationType() { SimulationType() =”模拟2",IsSelected =假});SimulationTypes.Add(新SimulationType() {项目=“模拟3",IsSelected = false });

在这三个项目中,如果我选择模拟1,那么UpdateSimulationItem将绑定到模拟1,属性的变化将缩小到一个项目,即模拟1。现在如果我们点击仿真2,那么peopertychange将不会触发,因为UpdateSimulationItem仅绑定到模拟1项更改。

我所做的改变。

更新代码:

代码语言:javascript
运行
复制
private Items _updateSimulationItem;
public Items UpdateSimulationItem
{
    get
    {
        return _updateSimulationItem;
    }
    set
    {
      //Removed unnecessary code and the assignment of value to _updateSimulationItem
      OnPropertyChanged("UpdateSimulationItem");
    }
}

我们已经将SimulationTypes绑定到ABC.XAML中的ItemSource中,如下所示

代码语言:javascript
运行
复制
<ListBox Foreground="{StaticResource AcresTheme}" 
         SelectedItem="{Binding Path=UpdateSimulationItem,UpdateSourceTrigger=PropertyChanged}" 
         ItemsSource="{Binding SimulationTypes, NotifyOnSourceUpdated=True}" 
         MinHeight="65" SelectionMode="Multiple">

当我单击视图中的复选框时,它将自动将SimulationTypes绑定到IsSelected。

代码语言:javascript
运行
复制
<CheckBox  Foreground="{StaticResource AcresTheme}"
           Content="{Binding Item}"
           IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">

@EldHasp我们在代码中必须做的代码更改是将赋值移除给setter属性中的OnPropertychange(nameOf(SelectedItem)).,只保留

公共物品?SelectedItem { get => _selectedItem;set => Set(ref _selectedItem,value); }粗体文本使=>绑定到一个项,这将在选择其他项时限制触发器。

再次感谢 @EldHasp在这方面花费了你的时间。

票数 0
EN

Stack Overflow用户

发布于 2022-11-02 17:39:53

错误不在此代码中。您可能对VM实例感到困惑。这通常是初学者的情况。SimulationType的实现可能有问题。你没表现出来。

下面是一个完整的代码示例,演示多选择绑定正确工作。

代码语言:javascript
运行
复制
using Simplified;
using System;
using System.Collections.ObjectModel;

namespace Core2022.SO.ChaithanyaS
{
    public class Item : ViewModelBase
    {
        private string _title = string.Empty;
        public string Title
        {
            get => _title;
            set => Set(ref _title, value ?? string.Empty);
        }

        private bool _isSelected;
        public bool IsSelected
        {
            get => _isSelected;
            set => Set(ref _isSelected, value);
        }
    }

    public class SimulationType : Item
    {
        private int _count;
        public int Count { get => _count; set => Set(ref _count, value); }
    }

    public class ItemsViewModel : ViewModelBase
    {
        private static readonly Random random = new Random();
        private Item? _selectedItem;

        public ObservableCollection<Item> Items { get; } =
                    new ObservableCollection<Item>()
                    {
                new Item() {Title = "First" },
                new SimulationType() { Title = "Yield Simulation", Count = random.Next(5, 15) },
                new Item() {Title = "Second" },
                new SimulationType() { Title = "HLR Simulation", Count = random.Next(5, 15) },
                new SimulationType() { Title = "UnCorr HLR Simulation", Count = random.Next(5, 15)},
                new Item() {Title = "Third" }
                    };

        public Item? SelectedItem { get => _selectedItem; set => Set(ref _selectedItem, value); }
    }
}
代码语言:javascript
运行
复制
<Window x:Class="Core2022.SO.ChaithanyaS.ItemsWindow"
        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:Core2022.SO.ChaithanyaS"
        mc:Ignorable="d"
        Title="ItemsWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:ItemsViewModel/>
    </Window.DataContext>
    <UniformGrid Columns="2">
        <ListBox Margin="10"
                 SelectedItem="{Binding SelectedItem}"
                 ItemsSource="{Binding Items}"
                 SelectionMode="Multiple">
            <FrameworkElement.Resources>
                <DataTemplate DataType="{x:Type local:Item}">
                    <CheckBox  Foreground="Red"
                               Content="{Binding Title}"
                               IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"/>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:SimulationType}">
                    <CheckBox Foreground="Green"
                               IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
                        <TextBlock>
                            <TextBlock.Text>
                                <MultiBinding StringFormat="{}{0} ({1})">
                                    <Binding Path="Title"/>
                                    <Binding Path="Count"/>
                                </MultiBinding>
                            </TextBlock.Text>
                        </TextBlock>
                    </CheckBox>
                </DataTemplate>
            </FrameworkElement.Resources>
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>

        <ItemsControl ItemsSource="{Binding Items}" Margin="10"
                      BorderBrush="Green" BorderThickness="1"
                      Padding="10">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type local:Item}">
                    <DataTemplate.Resources>
                        <DataTemplate DataType="{x:Type local:Item}">
                            <TextBlock  Foreground="Red"
                               Text="{Binding Title}"/>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type local:SimulationType}">
                            <TextBlock Foreground="Green">
                                <TextBlock.Text>
                                    <MultiBinding StringFormat="{}{0} ({1})">
                                        <Binding Path="Title"/>
                                        <Binding Path="Count"/>
                                    </MultiBinding>
                                </TextBlock.Text>
                            </TextBlock>
                        </DataTemplate>
                    </DataTemplate.Resources>
                    <Label x:Name="cc" Content="{Binding}" Margin="1" BorderBrush="Gray" BorderThickness="1"/>
                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding IsSelected}" Value="True">
                            <Setter TargetName="cc" Property="Background" Value="LightPink"/>
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </UniformGrid>
</Window>

也许您的意思是,在多选择模式下,SelectedItem属性始终只有首先选定的项,直到取消选中为止?

不幸的是,这样一项任务的执行并不容易。最好是创建一个自定义AP属性,然后在XAML中使用它。

代码语言:javascript
运行
复制
    public static class MuliSelectorHelper
    {
        /// <summary>Returns the value of the IsSelectedItemLast attached property for <paramref name="multiSelector"/>.</summary>
        /// <param name="multiSelector"><see cref="DependencyObject"/> whose property value will be returned.</param>
        /// <returns><see cref="bool"/> property value.</returns>
        public static bool GetIsSelectedItemLast(DependencyObject multiSelector)
        {
            return (bool)multiSelector.GetValue(IsSelectedItemLastProperty);
        }

        /// <summary>Sets the value of the IsSelectedItemLast attached property for <paramref name="multiSelector"/>.</summary>
        /// <param name="multiSelector"><see cref="MultiSelector"/> whose property value will be returned.</param>
        /// <param name="value"><see cref="bool"/> value for property.</param>
        public static void SetIsSelectedItemLast(DependencyObject multiSelector, bool value)
        {
            multiSelector.SetValue(IsSelectedItemLastProperty, value);
        }

        /// <summary><see cref="DependencyProperty"/> for methods <see cref="GetIsSelectedItemLast(MultiSelector)"/>
        /// and <see cref="SetIsSelectedItemLast(MultiSelector, bool)"/>.</summary>
        public static readonly DependencyProperty IsSelectedItemLastProperty =
            DependencyProperty.RegisterAttached(
                nameof(GetIsSelectedItemLast).Substring(3),
                typeof(bool),
                typeof(MuliSelectorHelper),
                new PropertyMetadata(false, OnIsSelectedItemLastChanged));

        private static void OnIsSelectedItemLastChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is not Selector selector || selector.GetValue(ListBox.SelectedItemsProperty) is not IList list)
            {
                throw new NotImplementedException("Implemented only types that derive from Selector and that use the ListBox.SelectedItems dependency property.");
            }

            if (Equals(e.NewValue, true))
            {
                selector.SelectionChanged += OnSelectionChanged;
            }
            else
            {
                selector.SelectionChanged -= OnSelectionChanged;
            }
        }

        private static async void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count == 0)
                return;

            Selector selector = (Selector)sender;
            IList selectedItems = (IList)selector.GetValue(ListBox.SelectedItemsProperty);

            if (selectedItems.Count != e.AddedItems.Count && !Equals(selectedItems[0], e.AddedItems[0]))
            {
                selector.SelectionChanged -= OnSelectionChanged;

                int beginIndex = selectedItems.Count - e.AddedItems.Count;
                var selectedItemsArray = new object[selectedItems.Count];
                selectedItems.CopyTo(selectedItemsArray, 0);
                selectedItems.Clear();
                await selector.Dispatcher.BeginInvoke(() =>
                {
                    for (int i = selectedItemsArray.Length-1; i >= beginIndex; i--)
                    {
                        selectedItems.Add(selectedItemsArray[i]);
                    }
                    for (int i = 0; i < beginIndex; i++)
                    {
                        selectedItems.Add(selectedItemsArray[i]);
                    }

                });

                selector.SelectionChanged += OnSelectionChanged;
            }

        }
    }
代码语言:javascript
运行
复制
    <UniformGrid Columns="2">
        <ListBox Margin="10"
                 SelectedItem="{Binding SelectedItem}"
                 ItemsSource="{Binding Items}"
                 SelectionMode="Multiple"
                 local:MuliSelectorHelper.IsSelectedItemLast="true">
            <FrameworkElement.Resources>
                <DataTemplate DataType="{x:Type local:Item}">
                    <CheckBox  Foreground="Red"
                               Content="{Binding Title}"
                               IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"/>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:SimulationType}">
                    <CheckBox Foreground="Green"
                               IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
                        <TextBlock>
                            <TextBlock.Text>
                                <MultiBinding StringFormat="{}{0} ({1})">
                                    <Binding Path="Title"/>
                                    <Binding Path="Count"/>
                                </MultiBinding>
                            </TextBlock.Text>
                        </TextBlock>
                    </CheckBox>
                </DataTemplate>
            </FrameworkElement.Resources>
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>

        <ContentControl Content="{Binding SelectedItem}">
            <ContentControl.ContentTemplate>
                <DataTemplate DataType="{x:Type local:Item}">
                    <DataTemplate.Resources>
                        <DataTemplate DataType="{x:Type local:Item}">
                            <TextBlock  Foreground="Red"
                               Text="{Binding Title}"/>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type local:SimulationType}">
                            <TextBlock Foreground="Green">
                                <TextBlock.Text>
                                    <MultiBinding StringFormat="{}{0} ({1})">
                                        <Binding Path="Title"/>
                                        <Binding Path="Count"/>
                                    </MultiBinding>
                                </TextBlock.Text>
                            </TextBlock>
                        </DataTemplate>
                    </DataTemplate.Resources>
                    <Label x:Name="cc" Content="{Binding}" Margin="1" BorderBrush="Gray" BorderThickness="1"/>
                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding IsSelected}" Value="True">
                            <Setter TargetName="cc" Property="Background" Value="LightPink"/>
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </ContentControl.ContentTemplate>
        </ContentControl>
    </UniformGrid>
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74287084

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档