2015-09-25 3 views
1

Я пытаюсь реализовать меню выбора тем в приложении WPF/MVVM. У меня есть сам выбор, но я не могу понять, как установить IsChecked на соответствующий MenuItem с чистым привязкой к данным (иначе, не нарушая шаблон MVVM).WPF/MVVM - проверка элемента меню на основе строкового соответствия

XAML:

<MenuItem Header="_Theme"> 
    <MenuItem Header="Classic" Command="{Binding ChangeThemeCommand}" CommandParameter="Classic" /> 
    <MenuItem Header="Metro White" Command="{Binding ChangeThemeCommand}" CommandParameter="MetroWhite" /> 
</MenuItem> 

ViewModel:

RelayCommand _changeThemeCommand; 
public ICommand ChangeThemeCommand 
{ 
    get 
    { 
     return _changeThemeCommand ?? (_changeThemeCommand = new RelayCommand(param => 
     { 
      ThemeManager.CurrentTheme = param.ToString(); 
     })); 
    } 
} 

тематизации обрабатывается с помощью пакета управления WPF Actipro в (http://www.actiprosoftware.com); как вы можете видеть, текущая тема представлена ​​только как строка.

Моя проблема заключается в выяснении того, как привязать IsChecked таким образом, который будет отмечать MenuItem для активной темы. Способ структурирования XAML, что означало бы совмещение текущего имени темы с CommandParameter MenuItem.

Любые советы/указатели будут очень признательны.

ответ

3

Ваша первая проблема заключается в том, что вы жестко кодируете все свои темы. Лучше бы создать класс под названием Тема:

public class Theme : INotifyPropertyChanged 
{ 
    public string Name { get; set; } // Implement PropertyChanged event on this. 
    public bool Checked { get; set; } // Implement PropertyChanged event on this. 
} 

В вашей основной модели представления, есть наблюдаемая коллекция из них, а затем заполнить его с тем, а именно:

ObservableCollection<Theme> Themes { get; private set; } 

В конструкторе что-то вроде:

Themes.Add(new Theme() { Name = "Classic" }); 
Themes.Add(new Theme() { Name = "MetroWhite" }); 

Теперь ваше контекстное меню должно выглядеть примерно так:

<MenuItem Header="_Theme" ItemsSource="{Binding Themes}"> 
    <MenuItem.ItemTemplate> 
     <DataTemplate> 
      <MenuItem Header="{Binding Name}" IsChecked="{Binding Checked}" IsCheckable="True"/> 
     </DataTemplate> 
    </MenuItem.ItemTemplate> 
</MenuItem> 

Теперь это дает вам набор тем, и когда вы нажимаете на него, устанавливается свойство Checked. Теперь вы можете назначить свою команду в MenuItems, желательно как часть класса Theme (т. Е. Theme.Set() кажется мне разумным дизайном OO). Все должно быть довольно прямо здесь.

Update

Как обеспечить соблюдение, что только одна тема выбрана сразу?

Предполагая, что у вас есть MainViewModel, разверните конструктор темы, чтобы вернуть ссылку на MainViewModel. Затем в вашей команде SetTheme() повторите все остальные темы, убедившись, что они не проверены.

void SetTheme() 
{ 
    foreach (Theme theme in MainViewModel.Themes) 
    { 
     if (theme != this) 
     { 
      theme.Checked = false; 
     } 
    } 

    // Do actual theme setting . 
} 

Почему я должен реализовать INotifyPropertyChanged?

Потому что вышеуказанное не работает, если вы этого не сделали. Конечно, вы можете просто реализовать его для Checked, но в качестве хорошей практики я рекомендую реализовать его для всех общедоступных доступных свойств, которые являются частью интерфейса. Таким образом, если вы используете этот ViewModel с некоторым другим представлением позже, который хочет отредактировать эти свойства, все будет работать.

+0

Это, безусловно, более чистый подход, спасибо :) Но я думаю, что есть еще часть головоломки, которую я не получаю.С этим дизайном я могу установить тему в Setter Theme.Checked (даже не нужна команда), но затем я вернусь туда, где я начал: функциональность работает, но свойства IsChecked в MenuItem не являются взаимоисключающими; мы в основном просто добавили набор объектов Тема, отражающих Проверенное состояние MenuItems, но по-прежнему ничего не сравнится с фактической активной темой (+ убедитесь, что только один MenuItem остается отмеченным за раз). Имеют смысл ...? – Metal450

+0

Я также не понимаю, почему Name & Checked необходимо реализовать PropertyChanged, как в приведенном выше примере, ни один из них не обновляется ничем, кроме самих MenuItems? Разумеется, нам, по крайней мере, не нужно было бы реализовывать его на Имени? – Metal450

+0

Другими словами, представьте, что вы выбираете одну тему, затем другую. Теперь будут проверены оба параметра MenuItems (и оба объекта темы «Проверено свойства будут истинными») ... но, очевидно, текущая тема является только последней, которую вы выбрали. Ничто в приведенной выше реализации фактически не связывает MenuItems с самой текущей темой - поэтому, пока активна одна тема, все еще можно проверить несколько объектов MenuItems (+ Тема). – Metal450

Смежные вопросы