Note that there are some explanatory texts on larger screens.

plurals
  1. POMoving ListBoxItems around a Canvas?
    text
    copied!<p>I'm working on dragging objects around a Canvas, which are encapsulated in ListBoxItems -- the effect being to create a simple pseudo desktop.</p> <p>I have a ListBox with a Canvas as the ItemsPanelTempalte, so that the ListBoxItems can appear anywhere on screen:</p> <pre><code>&lt;ListBox ItemsSource="{Binding Windows}"&gt; &lt;ListBox.ItemsPanel&gt; &lt;ItemsPanelTemplate&gt; &lt;Canvas /&gt; &lt;/ItemsPanelTemplate&gt; &lt;/ListBox.ItemsPanel&gt; &lt;/ListBox&gt; </code></pre> <p>I have a Style to define how the ListBoxItems should appear:</p> <pre><code>&lt;Style TargetType="{x:Type ListBoxItem}"&gt; &lt;Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /&gt; &lt;Setter Property="Canvas.Left" Value="{Binding Left, Mode=TwoWay}" /&gt; &lt;Setter Property="Canvas.Top" Value="{Binding Top, Mode=TwoWay}" /&gt; &lt;Setter Property="OverridesDefaultStyle" Value="True" /&gt; &lt;Setter Property="Template"&gt; &lt;Setter.Value&gt; &lt;ControlTemplate TargetType="{x:Type ListBoxItem}"&gt; &lt;local:PseudoWindowContainer Content="{TemplateBinding Content}" /&gt; &lt;/ControlTemplate&gt; &lt;/Setter.Value&gt; &lt;/Setter&gt; &lt;/Style&gt; </code></pre> <p>The "PseudoWindowContainer" extends from the ContentControl and has its own Style applied to make it look like a dialog box (title bar, close button, etc...). Here is a chunk of it:</p> <pre><code> &lt;Style TargetType="{x:Type local:PseudoWindowContainer}"&gt; &lt;Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /&gt; &lt;Setter Property="Width" Value="{Binding Width, Mode=TwoWay}" /&gt; &lt;Setter Property="Height" Value="{Binding Height, Mode=TwoWay}" /&gt; &lt;Setter Property="OverridesDefaultStyle" Value="True" /&gt; &lt;Setter Property="Template"&gt; &lt;Setter.Value&gt; &lt;ControlTemplate TargetType="{x:Type local:PseudoWindowContainer}"&gt; &lt;Grid Name="LayoutRoot" Background="White"&gt; &lt;!-- ... snip ... --&gt; &lt;Border Name="PART_TitleBar" Grid.Row="0" Background="LightGray" CornerRadius="2,2,0,0" VerticalAlignment="Stretch" Cursor="Hand" /&gt; &lt;TextBlock Name="TitleBar_Caption" Text="{Binding DisplayName}" Grid.Row="0" Background="Transparent" Padding="5,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" /&gt; &lt;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" /&gt; &lt;!-- ContentPresenter --&gt; &lt;ContentPresenter Grid.Row="1" /&gt; &lt;!-- ... snip ... --&gt; &lt;/Grid&gt; &lt;ControlTemplate.Triggers&gt; &lt;Trigger Property="IsSelected" Value="True"&gt; &lt;Setter TargetName="WindowBorder" Property="Background" Value="Blue" /&gt; &lt;/Trigger&gt; &lt;Trigger Property="IsSelected" Value="False"&gt; &lt;Setter TargetName="WindowBorder" Property="Background" Value="#22000000" /&gt; &lt;/Trigger&gt; &lt;/ControlTemplate.Triggers&gt; &lt;/ControlTemplate&gt; &lt;/Setter.Value&gt; &lt;/Setter&gt; </code></pre> <p></p> <p>Inside the PseudoWindowContainer.cs class I create some event handlers to listen for MouseDown/MouseUp/MoveMove events:</p> <pre><code>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; } </code></pre> <p>The trouble I run into is the "Left" and "Top" are not defined properties, and updating them to Canvas.SetLeft/SetTop (or GetLeft/GetTop, accordingly) does not update the position on the Canvas.</p> <p>I have "Left" and "Top" defined in the ViewModel of the controls I place <em>into</em> the ListBoxItems, and are thus subsequently wrapped with a PseudoWindowContainer because of the Template. These values are being honored and the objects do appear in the correct location when the application comes originally.</p> <p>I believe I need to somehow define "Left" and "Top" in my PseudoWindowContainer (aka: ContentControl) and have them propagate back up to my ViewModel. Is this possible?</p> <p>Thanks again for any help!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload