2009-07-08 8 views
2

Je travaille sur un contrôle de panneau personnalisé et l'une des choses que j'essaie de faire est d'échanger son contenu lors de l'exécution. Il y a deux états à ce contrôle: maximisé et normal. Lorsque l'utilisateur clique sur un bouton du contrôle, l'état bascule. Il y a deux propriétés sur ce contrôle: MaximizedContent et MinimizedContent. Lorsque vous cliquez sur le bouton pour échanger les états, la propriété Content du contrôle doit permuter entre MaximizedContent et MinimizedContent. Le problème vient quand il y a des liaisons à l'intérieur de MaximizedContent ou MinimizedContent. Le ne semble pas faire partie de l '"Arbre" et donc la liaison ne fonctionne pas ... du moins c'est ma théorie. Donc, ma question est comment puis-je les faire partie de l'arbre?WPF - Echange ContentControl (éléments de reliure ???)

Voici un exemple simplifié:

MainWindow.xaml

<Window x:Class="SwappingContentTest.MainWindow" 
     Loaded="Window_Loaded" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:local="clr-namespace:SwappingContentTest" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 

    <StackPanel HorizontalAlignment="Left"> 
     <Button x:Name="swapContentButton" 
       Click="swapContentButton_Click" 
       Content="Swap Content" /> 

     <local:SwappableContentControl x:Name="swappableControl"> 
      <local:SwappableContentControl.MaximizedContent> 
       <StackPanel> 
        <CheckBox x:Name="maximizedCheckBox" 
           Content="Maximized CheckBox" /> 
        <Button x:Name="maximizedButton" 
          Content="Maximized Button" 
          IsEnabled="{Binding ElementName=maximizedCheckBox, Path=IsChecked}" /> 
       </StackPanel> 
      </local:SwappableContentControl.MaximizedContent> 

      <local:SwappableContentControl.MinimizedContent> 
       <StackPanel> 
        <CheckBox x:Name="minimizedCheckBox" 
           Content="Minimized CheckBox" /> 
        <Button x:Name="minimizedButton" 
          Content="Minimized Button" 
          IsEnabled="{Binding ElementName=minimizedCheckBox, Path=IsChecked}" /> 
       </StackPanel> 
      </local:SwappableContentControl.MinimizedContent> 
     </local:SwappableContentControl> 

     <CheckBox x:Name="standardCheckBox" 
        Content="Standard CheckBox" 
        Margin="0,20,0,0"/> 
     <Button x:Name="standardButton" 
       Content="StandardButton" 
       IsEnabled="{Binding ElementName=standardCheckBox, Path=IsChecked}" /> 
    </StackPanel> 
</Window> 

MainWindow.cs

namespace SwappingContentTest 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void Window_Loaded(object sender, RoutedEventArgs e) 
     { 
      swappableControl.SwapContent(); 
     } 

     private void swapContentButton_Click(object sender, RoutedEventArgs e) 
     { 
      swappableControl.SwapContent(); 
     } 
    } 
} 

SwappableContentControl.cs

namespace SwappingContentTest 
{ 
    public class SwappableContentControl : ContentControl 
    { 
     public static readonly DependencyProperty MaximizedContentProperty = DependencyProperty.Register("MaximizedContent", typeof(object), typeof(SwappableContentControl)); 

     public static readonly DependencyProperty MinimizedContentProperty = DependencyProperty.Register("MinimizedContent", typeof(object), typeof(SwappableContentControl)); 

     public static readonly DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(SwappableContentControlState), typeof(SwappableContentControl), 
      new PropertyMetadata(new PropertyChangedCallback(StatePropertyCallback))); 

     public static void StatePropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      SwappableContentControl control = (SwappableContentControl)d; 
      if ((SwappableContentControlState)e.NewValue == SwappableContentControlState.Maximized) 
      { 
       control.Content = control.MaximizedContent; 
      } 
      else 
      { 
       control.Content = control.MinimizedContent; 
      } 
     } 

     public object MaximizedContent 
     { 
      get { return GetValue(MaximizedContentProperty); } 
      set { SetValue(MaximizedContentProperty, value); } 
     } 


     public object MinimizedContent 
     { 
      get { return GetValue(MinimizedContentProperty); } 
      set { SetValue(MinimizedContentProperty, value); } 
     } 


     public SwappableContentControlState State 
     { 
      get { return (SwappableContentControlState)GetValue(StateProperty); } 
      set { SetValue(StateProperty, value); } 
     } 

     public void SwapContent() 
     { 
      if (State == SwappableContentControlState.Maximized) 
      { 
       State = SwappableContentControlState.Normal; 
      } 
      else 
      { 
       State = SwappableContentControlState.Maximized; 
      } 
     } 
    } 
} 

Voici un lien vers le projet: http://www.freewebs.com/thrash505/SwappingContentTest.zip

+0

S'il vous plaît expliquer un peu plus quel est le problème. Et essayez de réduire tout ce code à quelque chose d'un peu plus gérable, il y a des fichiers comme App.cs ou tout le code DependecyProperty qui pourrait être enlevé et rendre votre problème plus facile à comprendre –

+0

J'ai réduit une partie du code, espérons que cela aide. Il est préférable de télécharger le projet et de voir par vous-même si. J'ai un ContentControl avec deux propriétés de contenu supplémentaires; un pour maximzied et un pour minimized. J'échange ces contenus dans la propriété Content réelle au moment de l'exécution.Mais, puisqu'il n'y a pas de partie de l'arborescence réelle lorsque le contrôle est chargé, la liaison ne fonctionne pas une fois que je les échange au moment de l'exécution. J'ai besoin de comprendre comment les faire partie de l'arbre quand je permute ... Je pense que c'est le problème, mais je peux me tromper. – Thrash505

+0

Des idées là-dessus? – Thrash505

Répondre

5

Je suggère de ne pas échanger le contenu lui-même, mais plutôt de placer deux instances de ContentControl dans votre contrôle et changer la visibilité. En plus d'être globalement plus propre, cela aura l'avantage de ne mettre à jour que la disposition de contrôle et de ne pas forcer la reconstruction des arbres. Cela signifie également que les deux ContentControls restent dans l'arborescence logique à tout moment, ce qui les rend beaucoup plus faciles à référencer dans votre implémentation de contrôle et à maintenir les liaisons correctement mises à jour. De plus, vous obtenez l'avantage de pouvoir les modéliser séparément, ouvrant la porte à de jolis changements d'état visuel.

+0

J'ai pensé à faire ça il y a un moment, mais pour une raison quelconque, je pensais que ce serait plus lent ... surtout à cause de tous les contrôles des deux contenus à la fois ... mais maintenant que j'y repense , le moteur de rendu, le moteur d'entrée, et ce genre de choses ne font probablement rien avec des contrôles non visibles ... Je suis d'accord avec votre suggestion et je vais essayer, merci. – Thrash505

+0

Tant que vous utilisez 'Réduit' pour masquer et non 'Caché', vos performances devraient être correctes car cela ne fera que mettre à jour la mise en page plutôt que de reconstruire des arbres visuels et logiques. – AndyM

0

Dans l'exemple initial, l'erreur de liaison suivante sera générée après la permutation du contenu:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=maximizedCheckBox'. BindingExpression:Path=IsChecked; DataItem=null; target element is 'Button' (Name='maximizedButton'); target property is 'IsEnabled' (type 'Boolean')

FrameworkElement propose deux méthodes: AddLogicalChild et RemoveLogicalChild

Toutes les propriétés de dépendance « de contenu » devrait fournir une valeur modifiée rappeler. Dans ce gestionnaire de rappel, appelez RemoveLogicalChild pour l'ancien contenu et appelez AddLogicalChild pour le nouveau contenu. Cela permet de synchroniser l'arborescence logique et de prendre en charge le modèle DataContext.

En regardant l'exemple initial de la nouvelle mise en œuvre de MinimizedContent ressemblerait à quelque chose comme ceci:

// MinimizedContent 
public static readonly DependencyProperty MinimizedContentProperty 
    = DependencyProperty.Register("MinimizedContent", typeof(object), typeof(SwappableContentControl), 
    new PropertyMetadata(new PropertyChangedCallback(OnMinimizedContentChanged))); 

public object MinimizedContent 
{ 
    get { return GetValue(MinimizedContentProperty); } 
    set { SetValue(MinimizedContentProperty, value); } 
} 

private static void OnMinimizedContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    SwappableContentControl swappableContentControl = d as SwappableContentControl; 
    if (swappableContentControl == null) 
     return; 

    swappableContentControl.RemoveLogicalChild(e.OldValue); 
    swappableContentControl.AddLogicalChild(e.NewValue); 
} 

Faire les appels à AddLogicalChild et RemoveLogicalChild résout le problème et les liaisons fonctionnent comme prévu.

Questions connexes