Имеется список всех контрагентов Contractors
, имеется список партнеров Partners
, второй список есть подмножество первого, оба свойства доступны в VM
.
Во View
есть ItemsControl
с CheckBox'ами
в котором содержится список контрагентов:
<ItemsControl ItemsSource="{Binding Contractors}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}">
...
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
мне нужно теперь расставить флажки в чекбоксах для всех контрагентов, которые являются партнерами:
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource CheckPartnersConverter}">
<Binding Mode="OneWay"/>
<Binding Path="DataContext.Partners"
RelativeSource="{RelativeSource FindAncestor, AncestorType=ItemsControl}"/>
</MultiBinding>
</CheckBox.IsChecked>
В конвертере реализовал метод Convert
:
class CheckPartnersConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[1] is List<Contractor> partners && values[0] is Contractor c)
return partners.Contains(c);
return false;
}
хорошо, это, вроде, работает.
Как теперь реализовать обратное конвертирование и вообще возможно ли его реализовать?
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
var res = new object[]
{
Binding.DoNothing,
Binding.DoNothing
};
return res;
}
}
Если это невозможно, готов отказаться от представления чекбоксами и заменить его, например на ListBox
с SelectionMode="Multiple"
и партнеров просто выделять, но там тоже проблемы с SelectedItems
победить не удается
Мне кажется, вы усложняете себе жизнь.
Смотрите, ваша бизнес-логика хочет отобрать из контрагентов партнёров. Это существенный кусок бизнес-логики, а значит, имеет смысл не пожалеть завести для него отдельный VM-класс:
class MaybePartner : NotifyPropertyChangedImpl
{
public Contractor Contractor { get; }
bool isPartner;
public bool IsPartner
{
get => isPartner;
set
{
if (Set(ref isPartner, value))
{
// тут можно включить контрагента в список или исключить его
}
}
}
public MaybePartner(Contractor c, bool isInitiallyPartner)
{
Contractor = c;
isPartner = isInitiallyPartner;
}
}
Теперь ваша задача тривиальна: вы при входе в эту часть бизнес логики создаёте список MaybePartner
'ов, и привязываетесь просто как
<ItemsControl ItemsSource="{Binding MaybePartners}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Contractor.Name}" IsChecked="{Binding IsPartner}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Вроде бы всё.
Ну тут на самом деле задачу можно решить несколькими способами, один из них это использовать EventTrigger
, есть еще вариант с (attached property
и behavior
), но на самом деле суть этих решений примерно одна.
XAML
код:
<ListBox Name="MyListBox"
Grid.Row="0"
DisplayMemberPath="Name"
ItemsSource="{Binding Users}"
SelectionMode="Multiple">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"
CommandParameter="{Binding ElementName=MyListBox, Path=SelectedItems}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
Далее во ViewModel
создаем команду:
public Command<IList> SelectedItemChangedCommand { get; set; }
В качестве параметров Action'
a данной команды как-раз таки и будут выделенные записи
SelectedItemChangedCommand = new Command<IList>(items =>
{
foreach (User item in items)
{
}
});
P.S. Для работы с EventTrigger
и InvokeCommandAction
у вас должна быть подключена библиотека System.Windows.Interactivity
.
В XAML
подключение данного пространства имен выглядит следующим образом:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Для того, что бы привязка к свойству SelectedItems
работала в двух направлениях, т.е. что бы была возможность устанавливать данное свойство из VM
, можно воспользоваться механизмом attached property
. При его использовании EventTrigger
уже не нужен.
public class SelectedItemsAttachedProperty
{
public static IList GetSelectedItems(ListBox obj)
{
return (IList)obj.GetValue(SelectedItemsProperty);
}
public static void SetSelectedItems(ListBox obj, IList value)
{
obj.SetValue(SelectedItemsProperty, value);
}
public static readonly DependencyProperty
SelectedItemsProperty =
DependencyProperty.RegisterAttached(
"SelectedItems",
typeof(IList),
typeof(SelectedItemsAttachedProperty),
new PropertyMetadata(null,
SelectedItemsChanged));
private static void SelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var lb = d as ListBox;
if(lb == null)
return;
IList coll = e.NewValue as IList;
if (coll is INotifyCollectionChanged)
{
(coll as INotifyCollectionChanged)
.CollectionChanged += (s, collArgs) =>
{
if (collArgs.OldItems != null)
foreach (var item in collArgs.OldItems)
lb.SelectedItems.Remove(item);
if (null != collArgs.NewItems)
foreach (var item in collArgs.NewItems)
lb.SelectedItems.Add(item);
};
}
if (coll != null)
{
if (coll.Count > 0)
{
lb.SelectedItems.Clear();
foreach (var item in coll)
lb.SelectedItems.Add(item);
}
lb.SelectionChanged += (s, lbArgs) =>
{
if (null != lbArgs.RemovedItems)
foreach (var item in lbArgs.RemovedItems)
coll.Remove(item);
if (null != lbArgs.AddedItems)
foreach (var item in lbArgs.AddedItems)
coll.Add(item);
};
}
}
}
XAML
<ListBox Grid.Row="0"
wpfApp:SelectedItemsAttachedProperty.SelectedItems="{Binding SelectedItems}"
DisplayMemberPath="Name"
ItemsSource="{Binding Data}"
SelectionMode="Multiple" />
Следовательно затем во VM
вы можете добавлять элементы в SelectedItems
, которое привязано к нашему AttachedProperty
.
Переделал пока вот так:
<ListBox ItemsSource="{Binding Contractors}"
DisplayMemberPath="Name" SelectionMode="Multiple"
DataContextChanged="OnDataContextChanged"/>
Код обработчика события:
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var listBox = sender as ListBox;
if (e.OldValue is Provider oldProv)
oldProv.Partners = listBox.SelectedItems.Cast<Contractor>().ToList();
if (e.NewValue is Provider newProv)
{
listBox.SelectedItems.Clear();
foreach (var c in newProv.Partners)
listBox.SelectedItems.Add(c);
}
}
В принципе всё работает и решение такое меня устраивает. Подсказал бы кто как его адаптировать к варианту представления с чекбоксами.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Как узнать какие порты открыты на моем девайсе, программным методом C#
Пишу академическое десктоп приложение с базой данныхИз базы контент вывожу посредством DataGrid, которое находится в интерфейсе десктоп - клиенте
Не могу придти к общему пониманию механизмов подписи xml-документовБуду благодарен за помощь разобраться)