Я создал пользовательский элемент управления WPF с 2 ListBox
es (с именами «Источник» и «Значение») и кнопкой «Добавить», которые позволяют пользователю «копировать» элементы, связанные с источником * 1002. * к значению ListBox
. Вот код:
public partial class MultiListSelect : UserControl
{
public static readonly DependencyProperty ListSourceProperty;
public static readonly DependencyProperty ListValueProperty;
public IEnumerable ListSource
{
get { return (ObservableCollection<object>)GetValue(MultiListSelect.ListSourceProperty); }
set { SetValue(MultiListSelect.ListSourceProperty, value); }
}
public IEnumerable ListValue
{
get { return (ObservableCollection<object>)GetValue(MultiListSelect.ListValueProperty); }
set { SetValue(MultiListSelect.ListValueProperty, value); }
}
public MultiListSelect()
{
InitializeComponent();
}
static MultiListSelect()
{
MultiListSelect.ListSourceProperty =
DependencyProperty.Register("ListSource",
typeof(IEnumerable), typeof(MultiListSelect));
MultiListSelect.ListValueProperty =
DependencyProperty.Register("ListValue",
typeof(IEnumerable), typeof(MultiListSelect));
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
foreach (object o in listBoxSource.SelectedItems)
{
listBoxValue.Items.Add(o);
}
}
}
Мое представление, которое использует этот пользовательский элемент управления, использует MVVM Light. Вот мой код ViewModel, который предоставляет данные, привязанные к каждому ListBox:
public class ProfileViewModel : ViewModelBase
{
public User User { get; set; }
// This gets bound to the Source listbox
private ObservableCollection<OperatingArea> _operatingAreas;
public ObservableCollection<OperatingArea> OperatingAreas
{
get { return _operatingAreas; }
private set
{
_operatingAreas = value;
}
}
public ProfileViewModel()
{
OperatingAreas = _rpDomain.LoadOperatingAreas();
WindowsIdentity identity = WindowsIdentity.GetCurrent();
if (User == null)
{
User = _rpDomain.CreateUser();
User.Preferences = _rpDomain.CreateUserPreferences();
User.UserId = identity.Name;
_rpDomain.Add(User);
}
if (User.Preferences == null)
{
User.Preferences = _rpDomain.CreateUserPreferences();
}
if (User.Preferences.ViewableOperatingAreas == null)
{
// This gets bound to my 'Value' ListBox
User.Preferences.ViewableOperatingAreas = new ObservableCollection<OperatingArea>();
}
}
}
Переплет работает нормально. Проблема заключается в том, что когда пользователь выбирает элементы в поле со списком «Источник» и нажимает кнопку «Добавить», я получаю сообщение об ошибке, когда эта строка выполняется в коде позади пользовательского элемента управления в обработчике события btnAdd_Click:
listBoxValue.Items.Add(o);
Я получаю сообщение об ошибке: «Операция недействительна, когда используется ItemSource. Получайте доступ и изменяйте элементы с помощью ItemsControl.ItemsSource вместо '. Итак, я понимаю проблему, но я не знаю лучший способ решить эту проблему. DependencyProperties имеют тип IEnumerable, поэтому я не могу добавить их непосредственно в код за пользовательским элементом управления. Также я не могу получить доступ к ViewModel и напрямую обновить свойства, потому что библиотека пользовательского элемента управления не может ссылаться на ViewModel для Views. Каков наилучший способ скопировать элементы из поля со списком «Источник» в поле со списком «Значение» (предпочтительнее иметь код, содержащийся в пользовательском элементе управления, а не требовать, чтобы View / ViewModel добавлял элементы). Кроме того, это пользовательский элемент управления, поэтому тип, который привязывается к каждому списку, зависит от использования элемента управления.
РЕДАКТИРОВАТЬ - 10-4-2011
Итак, реальный вопрос в том, как мне обновить (добавить) список «Значение» из моего пользовательского элемента управления? Свойство зависимости имеет тип IEnumerable, поэтому его нельзя обновить напрямую, и, как показано выше с ошибкой, я не могу просто вызвать listbox.items.Add (). Я использую MVVM, поэтому обязательным является привязка данных к свойствам, предоставляемым во ViewModel. Также пользовательский элемент управления не знает (и не должен знать) тип, связанный с ним во время выполнения. Я понимаю, что мой пользовательский элемент управления может вызвать событие «Добавлено», которое может быть обработано с помощью функции EventToCommand в MVVM Light, но это кажется ненужным - лично я не буду удовлетворен, если куплю элемент управления у стороннего поставщика элементов управления, который потребовал от меня обновить коллекцию сам. Я знаю, что это возможно, потому что это, по сути, способ, которым работает комбинированный список - этот комбинированный список имеет как свойство ItemsSource, так и свойство SelectedValue, которое можно связать со свойствами ViewModel.
<ComboBox
ItemsSource="{Binding Path=Categories}"
DisplayMemberPath="Name"
SelectedValue="{Binding Path=SelectedCategory, Mode=TwoWay}"/>