2010-08-06 7 views
0

Je travaille sur le fait de faire glisser des objets autour d'un canevas, qui sont encapsulés dans ListBoxItems - l'effet étant de créer un pseudo bureau simple.Déplacer ListBoxItems autour d'un canevas?

J'ai un ListBox avec une toile comme ItemsPanelTempalte, de sorte que les ListBoxItems peuvent apparaître n'importe où sur l'écran:

<ListBox ItemsSource="{Binding Windows}"> 
    <ListBox.ItemsPanel> 
     <ItemsPanelTemplate> 
      <Canvas /> 
     </ItemsPanelTemplate> 
    </ListBox.ItemsPanel> 
</ListBox> 

J'ai un style de définir la façon dont les ListBoxItems doivent apparaître:

<Style TargetType="{x:Type ListBoxItem}"> 
    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> 
    <Setter Property="Canvas.Left" Value="{Binding Left, Mode=TwoWay}" /> 
    <Setter Property="Canvas.Top" Value="{Binding Top, Mode=TwoWay}" /> 
    <Setter Property="OverridesDefaultStyle" Value="True" /> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ListBoxItem}"> 
       <local:PseudoWindowContainer Content="{TemplateBinding Content}" /> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Le "PseudoWindowContainer" s'étend à partir de ContentControl et a son propre style appliqué pour le faire ressembler à une boîte de dialogue (barre de titre, bouton de fermeture, etc ...). Voici un morceau de celui-ci:

<Style TargetType="{x:Type local:PseudoWindowContainer}"> 
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> 
<Setter Property="Width" Value="{Binding Width, Mode=TwoWay}" /> 
<Setter Property="Height" Value="{Binding Height, Mode=TwoWay}" /> 
<Setter Property="OverridesDefaultStyle" Value="True" /> 
<Setter Property="Template"> 
    <Setter.Value> 
    <ControlTemplate TargetType="{x:Type local:PseudoWindowContainer}"> 
     <Grid Name="LayoutRoot" Background="White"> 
     <!-- ... snip ... --> 
      <Border Name="PART_TitleBar" Grid.Row="0" Background="LightGray" CornerRadius="2,2,0,0" VerticalAlignment="Stretch" Cursor="Hand" /> 
      <TextBlock Name="TitleBar_Caption" Text="{Binding DisplayName}" Grid.Row="0" Background="Transparent" Padding="5,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" /> 
      <Button Name="TitleBar_CloseButton" Command="{Binding CloseCommand}" Grid.Row="0" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,5,5,0" Width="20" Height="20" Cursor="Hand" Background="#FFFF0000" Foreground="#FF212121" /> 
      <!-- ContentPresenter --> 
      <ContentPresenter Grid.Row="1" /> 
     <!-- ... snip ... --> 
     </Grid> 
     <ControlTemplate.Triggers> 
     <Trigger Property="IsSelected" Value="True"> 
      <Setter TargetName="WindowBorder" Property="Background" Value="Blue" /> 
     </Trigger> 
     <Trigger Property="IsSelected" Value="False"> 
      <Setter TargetName="WindowBorder" Property="Background" Value="#22000000" /> 
     </Trigger> 
     </ControlTemplate.Triggers> 
    </ControlTemplate> 
    </Setter.Value> 
</Setter> 

intérieur de la classe PseudoWindowContainer.cs Je crée des gestionnaires d'événements pour écouter les MouseDown/MouseUp/événements MoveMove:

public override void OnApplyTemplate() 
{ 
    _titleBar = (Border)Template.FindName("PART_TitleBar", this); 
    if (_titleBar != null) 
    { 
    _titleBar.MouseDown += TitleBar_MouseDown; 
    _titleBar.MouseUp += TitleBar_MouseUp; 
    } 

    _grip = (ResizeGrip)Template.FindName("PART_ResizeGrip", this); 
    if (_grip != null) 
    { 
    _grip.MouseLeftButtonDown += ResizeGrip_MouseLeftButtonDown; 
    _grip.MouseLeftButtonUp += ResizeGrip_MouseLeftButtonUp; 
    } 

    base.OnApplyTemplate(); 
} 

private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e) 
{ 
    _titleBar.MouseMove += TitleBar_MouseMove; 
    ((Border)sender).CaptureMouse(); 

    _windowLocation.X = Left; 
    _windowLocation.Y = Top; 

    _clickLocation = this.PointToScreen(Mouse.GetPosition(this)); 
} 

private void TitleBar_MouseUp(object sender, MouseButtonEventArgs e) 
{ 
    _titleBar.MouseMove -= TitleBar_MouseMove; 
    ((Border)sender).ReleaseMouseCapture(); 
} 

private void TitleBar_MouseMove(object sender, MouseEventArgs e) 
{ 
    Point currentLocation = this.PointToScreen(Mouse.GetPosition(this)); 

    Left = _windowLocation.X + currentLocation.X - _clickLocation.X; 
    Top = _windowLocation.Y + currentLocation.Y - _clickLocation.Y; 
} 

Le problème que je lance into est le "Left" et "Top" ne sont pas des propriétés définies, et les mettre à jour à Canvas.SetLeft/SetTop (ou GetLeft/GetTop, en conséquence) ne met pas à jour la position sur le canevas.

je « Gauche » et « Top » définis dans le ViewModel des contrôles je place dans les ListBoxItems, et sont donc enveloppés par la suite avec un PseudoWindowContainer à cause du modèle. Ces valeurs sont respectées et les objets apparaissent à l'emplacement correct lorsque l'application vient à l'origine.

Je crois que j'ai besoin de définir en quelque sorte "Left" et "Top" dans mon PseudoWindowContainer (alias: ContentControl) et de les faire propager jusqu'à mon ViewModel. Est-ce possible?

Merci encore pour toute aide!

Répondre

0

Avez-vous lu l'article de Bea Stollnitz: The power of Styles and Templates in WPF?
Voici un exemple de Listbox dans lequel les éléments sont dessinés à des coordonnées spécifiques calculées dans Convertisseur.

+0

Je n'avais pas encore trouvé cet article, je vais le lire de plus près quand je m'assois avec mon code suivant . Cependant, mon problème n'est pas de placer les objets initialement - il les déplace une fois qu'ils sont placés. Le deuxième bloc de code a les mappages Canvas.Left/Top, qui fonctionnent pour le placement initial. C'est lorsque j'essaie de mettre à jour ces valeurs dans le bloc de code affiché que j'ai des problèmes - les valeurs ne remontent pas à ListBoxItem. –

Questions connexes