Я создал список, содержащий список элементов, и мне нужно связать их при выборе изменено (выбрать и отменить выбор).
ABCD.xalm
<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 (Просмотр модели)
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 });
}
Предметы.cs
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 Это сработало нормально для удаления одного элемента или выбора одного элемента.
Когда мы выбираем второй элемент, он не вызывает изменение свойства.





Ошибка где-то не в этом коде. Вы можете быть сбиты с толку экземплярами ВМ. Это часто бывает у начинающих. Возможно, что-то не так с реализацией SimulationType. Вы этого не показали.
Вот полный пример вашего кода, демонстрирующий, что привязка множественного выбора работает правильно.
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); }
}
}
<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.
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;
}
}
}
<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>
Спасибо @EldHasp за время, потраченное на ответ на мой вопрос. Очень благодарен за предоставленное решение.
В настоящее время я заинтересован в том, чтобы иметь полный код в ViewModel, я нашел ошибку, которую я сделал в ViewModel.
Старый код:
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, и изменение свойства срабатывает только при изменении этого конкретного элемента.
Например:
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});
В этих трех элементах, если я выберу Simulation 1, то UpdateSimulationItem будет привязан к Simulation 1, а изменение свойств сузится до одного элемента, т. е. Simulation 1. Теперь, если мы нажмем Simulation 2, peopertychange не сработает, поскольку UpdateSimulationItem привязан только к Изменение элемента Simulation 1.
Изменение, которое я сделал.
Обновленный код:
private Items _updateSimulationItem;
public Items UpdateSimulationItem
{
get
{
return _updateSimulationItem;
}
set
{
//Removed unnecessary code and the assignment of value to _updateSimulationItem
OnPropertyChanged("UpdateSimulationItem");
}
}
Поскольку мы привязали SimulationTypes к ItemSource в ABC.XAML, как показано ниже.
<ListBox Foreground = "{StaticResource AcresTheme}"
SelectedItem = "{Binding Path=UpdateSimulationItem,UpdateSourceTrigger=PropertyChanged}"
ItemsSource = "{Binding SimulationTypes, NotifyOnSourceUpdated=True}"
MinHeight = "65" SelectionMode = "Multiple">
когда я нажимаю на флажок, который присутствует в представлении, он автоматически обновляет SimulationTypes, поскольку я привязал флажок к IsSelected.
<CheckBox Foreground = "{StaticResource AcresTheme}"
Content = "{Binding Item}"
IsChecked = "{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
@EldHasp изменения кода, которые мы должны внести в ваш код, заключаются в том, чтобы удалить назначение _selectedItem в свойстве установки и просто сохранить OnPropertychange(nameOf(SelectedItem)).
публичный предмет? SelectedItem { получить => _selectedItem; установить => установить (ссылка _selectedItem, значение); } Жирный текст заставлял SelectedItem привязываться к одному элементу, что ограничивало триггер при выборе другого элемента.
Еще раз спасибо @EldHasp за то, что уделили этому время.