2010-06-24 18 views
5

Sto tentando di impostare un MenuItem che avrà un sottomenu di numeri di pagina che possono essere selezionati. Voglio associare lo ItemsSource a un elenco di numeri di pagina (in realtà al PageCount con un convertitore che crea l'elenco) e quindi associare la proprietà IsChecked di ogni MenuItem nel sottomenu a PageIndex. Il mio problema è con il secondo binding poiché richiede anche un convertitore, ma quel convertitore deve conoscere il numero di pagina che rappresenta il MenuItem, ma non riesco a capire come passare tali informazioni al convertitore. Ecco cosa ho provato finora.Binding ItemsSource e IsChecked di un MenuItem per ottenere un elenco di elementi controllabili in WPF

<MenuItem Header="_Goto Page" 
      ItemsSource="{Binding 
         Path=CurrentImage.PageCount, 
         Converter={StaticResource countToList}}"> 
    <MenuItem.ItemContainerStyle> 
     <Style TargetType="MenuItem"> 
      <Setter Property="IsCheckable" 
        Value="True"/> 
      <Setter Property="IsChecked" 
        Value="{Binding 
          ElementName=MainWindow, 
          Path=CurrentImage.PageIndex, 
          Mode=TwoWay, 
          Converter={StaticResource pageNumChecked}, 
          ConverterParameter={Binding 
               RelativeSource={RelativeSource Self}, 
               Path=Content}}"/> 
     </Style> 
    </MenuItem.ItemContainerStyle> 
</MenuItem> 

Naturalmente il problema è che il ConverterParameter non può essere vincolato in quanto non è un DependencyProperty. Quindi la mia domanda è come posso passare le informazioni di cui ho bisogno o c'è un altro modo per farlo.

Nota: Ho già provato a mettere le MenuItem s all'interno di un ListBox che ha funzionato molto bene per quanto riguarda le associazioni sono interessati, ma ha causato il sottomenu per comportarsi in modo non standard. Questo è quando hai aperto il sottomenu l'intero ListBox è stato trattato come uno MenuItem.

Modifica

Così qui è quello che ho avuto modo di lavorare fino ad ora. Ho provato il binding a un ListBox nascosto, ma quando ho associato il MenuItem.ItemsSource a "ListBox.Items" ho ottenuto l'elenco di int s invece di un elenco di ListBoxItem s che avevo bisogno di ottenere la proprietà IsSelected. Così ho finito per usare il suggerimento di Quartermeister di usare MultiBinding per ottenere la proprietà IsChecked da associare allo PageIndex nella modalità OneWay. Per gestire l'altra direzione, ho utilizzato un gestore di eventi sull'evento Click. Vale la pena notare che inizialmente avevo IsCheckable impostato su true e che funzionava con l'evento Checked, ma ne derivavano alcuni comportamenti strani.

<MenuItem x:Name="GotoPageMenuItem" Header="_Goto Page" 
      ItemsSource="{Binding Path=CurrentImage.PageCount, 
           Converter={StaticResource countToList}}"> 
    <MenuItem.ItemContainerStyle> 
     <Style TargetType="MenuItem"> 
      <Setter Property="IsCheckable" 
        Value="False"/> 
      <EventSetter Event="Click" 
         Handler="GotoPageMenuItem_Click"/> 
      <Setter Property="IsChecked"> 
       <Setter.Value> 
        <MultiBinding Converter="{StaticResource pageNumChecked}" 
               Mode="OneWay"> 
         <Binding RelativeSource="{RelativeSource FindAncestor, 
                AncestorType={x:Type Window}}" 
                Path="CurrentImage.PageIndex" 
                Mode="OneWay"/> 
         <Binding RelativeSource="{RelativeSource Self}" 
                Path="Header" 
                Mode="OneWay"/> 
        </MultiBinding> 
       </Setter.Value> 
      </Setter> 
     </Style> 
    </MenuItem.ItemContainerStyle> 
</MenuItem> 

Ed ecco il codice di GotoPageMenuItem_Click

private void GotoPageMenuItem_Click(object sender, RoutedEventArgs e) 
{ 
    var item = sender as MenuItem; 
    if (item != null) 
    { 
     //If the item is already checked then we don't need to do anything 
     if (!item.IsChecked) 
     { 
      var pageNum = (int)item.Header; 
      CurrentImage.PageIndex = (pageNum - 1); 
     } 
    } 
} 

risposta

3

Puoi fare quello che vuoi con un MultiBinding?

<Setter Property="IsChecked"> 
    <Setter.Value> 
     <MultiBinding Converter="{StaticResource pageNumChecked}"> 
      <Binding ElementName="MainWindow" Path="CurrentImage.PageIndex" Mode="TwoWay"/> 
      <Binding RelativeSource="{RelativeSource Self}" Path="Content"/> 
     </MultiBinding> 
    </Setter.Value> 
</Setter> 

avere il vostro convertitore pageNumChecked implementare IMultiValueConverter invece di IValueConverter e convertire otterrà un array con il risultato di ogni bambino vincolante. In questo caso, sarebbe un array a due elementi in cui il primo elemento è l'input corrente, PageIndex e il secondo indice è il tuo ConverterParameter corrente, Contenuto. Se si desidera il binding a due vie, ConvertBack dovrà restituire un array a due elementi e restituirà Binding.DoNothing per il secondo parametro in modo che non tenti di aggiornare il Contenuto.

+0

Questo non funziona per due -way binding perché ho bisogno di conoscere il valore 'Content' per entrambe le direzioni e viene passato solo a' Convert' e non a 'ConvertBack'. – juharr

+0

Anche se questa idea non ha funzionato in entrambe le direzioni, mi ha portato a metà strada così, hai vinto. – juharr

3

Sembra che si stia tentando di creare menu dinamici che controllano lo stato controllato di ciascuna voce di menu.
Ho esteso il codice che ho scritto per creare menu dinamici in WPF con lo schema MVVM e aggiunto la logica controllata.

Ecco il codice XAML:

<Menu DockPanel.Dock="Top"> 
    <MenuItem ItemsSource="{Binding Commands}" 
       Header="_Item Container Style"> 
     <MenuItem.ItemContainerStyle> 
      <Style TargetType="{x:Type MenuItem}"> 
       <Setter Property="IsCheckable" Value="True"/> 
       <Setter Property="IsChecked" Value="{Binding Path=Checked}"/> 
       <Setter Property="Header" Value="{Binding Path=Text}" /> 
       <Setter Property="Command" Value="{Binding Path=Command}" /> 
       <Setter Property="CommandParameter" Value="{Binding Path=Parameter}" /> 
      </Style> 
     </MenuItem.ItemContainerStyle> 
    </MenuItem> 
</Menu> 

Ecco la vista Modello:

public class MainViewModel : ViewModelBase 
{ 
    public MainViewModel() 
    { 
    GoCommand = new DelegateCommand<object>(OnGoCommand, CanGoCommand); 
    LoadCommands(); 
    } 

    private List<MyCommand> _commands = new List<MyCommand>(); 
    public List<MyCommand> Commands 
    { 
    get { return _commands; } 
    } 

    private void LoadCommands() 
    { 
    MyCommand c1 = new MyCommand { Command = GoCommand, Parameter = "1", Text = "Menu1", Checked = true}; 
    MyCommand c2 = new MyCommand { Command = GoCommand, Parameter = "2", Text = "Menu2", Checked = true }; 
    MyCommand c3 = new MyCommand { Command = GoCommand, Parameter = "3", Text = "Menu3", Checked = false }; 
    MyCommand c4 = new MyCommand { Command = GoCommand, Parameter = "4", Text = "Menu4", Checked = true }; 
    MyCommand c5 = new MyCommand { Command = GoCommand, Parameter = "5", Text = "Menu5", Checked = false }; 
    _commands.Add(c1); 
    _commands.Add(c2); 
    _commands.Add(c3); 
    _commands.Add(c4); 
    _commands.Add(c5); 
    } 

    public ICommand GoCommand { get; private set; } 
    private void OnGoCommand(object obj) 
    { 
    } 

    private bool CanGoCommand(object obj) 
    { 
    return true; 
    } 
} 

Qui è la classe che contiene i comandi:

public class MyCommand 
    { 
    public ICommand Command { get; set; } 
    public string Text { get; set; } 
    public string Parameter { get; set; } 
    public Boolean Checked { get; set; } 
    } 
+0

Il problema con questo esempio è che il numero di voci di menu è statico. Nella mia applicazione il 'CurrentImage' può cambiare e quindi avrei bisogno di aggiornare il numero di voci di menu corrispondenti. Anche questo sembra tanto codice quanto la creazione di voci di menu nel codice con la gestione degli eventi e quindi la gestione di ogni evento di menu 'Checked' ed ero fiducioso per una soluzione XAML. – juharr

+0

È possibile estendere la mia soluzione per ricaricare la lista di comandi quando la CurrentImage cambia, ma sono d'accordo che è un sacco di codice (semplice). Se pubblichi il codice per CurrentImage che potrebbe aiutare me e altri a capire meglio la tua domanda. – Zamboni

Problemi correlati