2016-01-27 14 views
0

我嘗試用下面的示範代碼以拖放的UIElement,在這種情況下一個按鈕,從一個面板到另一個:如果將UIElement從面板a拖放到面板b失敗,該如何退後?

[MainWindow.xaml]

<Window x:Class="WpfDemo.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="WPF Demonstration" 
    MinHeight="480" 
    MinWidth="640"> 
<Grid x:Name="mainGrid" Margin="3,3,3,3" ShowGridLines="False" UseLayoutRounding="True"> 

    <!-- Row definition for 3 rows --> 
    <Grid.RowDefinitions> 
     <!-- Row0 header --> 
     <RowDefinition Height="Auto"></RowDefinition> 
     <!-- Row1 content --> 
     <RowDefinition Height="*"></RowDefinition> 
     <!-- Row2 footer --> 
     <RowDefinition Height="Auto"></RowDefinition> 
    </Grid.RowDefinitions> 

    <!-- Column definition for 5 colums --> 
    <Grid.ColumnDefinitions> 
     <!-- Col0 left panel --> 
     <ColumnDefinition Width="*" MinWidth="100" MaxWidth="300"></ColumnDefinition> 
     <!-- Col1 for GridSplitter left --> 
     <ColumnDefinition Width="Auto"></ColumnDefinition> 
     <!-- Col2 center panel --> 
     <ColumnDefinition Width="*" MinWidth="100"></ColumnDefinition> 
     <!-- Col3 for GridSplitter right --> 
     <ColumnDefinition Width="Auto"></ColumnDefinition> 
     <!-- Col4 right panel --> 
     <ColumnDefinition Width="*" MinWidth="100" MaxWidth="300"></ColumnDefinition> 
    </Grid.ColumnDefinitions> 

    <!-- Grid content Row0 header --> 
    <!-- TODO: Move Button Margin into Style --> 
    <Button Grid.Column="0" Grid.Row="0" Margin="0,0,0,3" HorizontalAlignment="Left">Button 1</Button> 
    <Button Grid.Column="2" Grid.Row="0" Margin="0,0,0,3" HorizontalAlignment="Center">Button 2</Button> 
    <Button Grid.Column="4" Grid.Row="0" Margin="0,0,0,3" HorizontalAlignment="Right">Button 3</Button> 

    <!-- Grid content Row2 content --> 
    <!-- Grid content Col0 --> 
    <StackPanel Grid.Row="1" Grid.Column="0" AllowDrop="True" DragEnter="Panel_DragEnter" Drop="Panel_Drop"> 
     <StackPanel.Background> 
      <SolidColorBrush Color="LightGreen"/> 
     </StackPanel.Background> 
     <Label HorizontalAlignment="Left">Stack Panel</Label> 
     <Button x:Name="btnDragDrop" PreviewMouseDown="btnDragDrop_PreviewMouseDown">Drag me to an other container</Button> 
    </StackPanel> 
    <DockPanel Grid.Row="1" Grid.Column="2" AllowDrop="True" DragEnter="Panel_DragEnter" Drop="Panel_Drop"> 
     <DockPanel.Background> 
      <SolidColorBrush Color="LightBlue"/> 
     </DockPanel.Background> 
     <Label>Dock Panel</Label> 
    </DockPanel> 
    <WrapPanel Grid.Row="1" Grid.Column="4" AllowDrop="True" DragEnter="Panel_DragEnter" Drop="Panel_Drop"> 
     <WrapPanel.Background> 
      <SolidColorBrush Color="LightPink"/> 
     </WrapPanel.Background> 
     <Label>Wrap Panel</Label> 
    </WrapPanel> 
    <!-- Grid content Row4 footer --> 
     <StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="5"> 
      <Label x:Name="lbl" HorizontalContentAlignment="Center" Margin="0,3,0,0"> 
       <Label.Background> 
        <SolidColorBrush Color="LightGray"/> 
       </Label.Background> 
       <Label.Content>&#8656; Label footer &#8658;</Label.Content> 
      </Label> 
     </StackPanel> 
    <!-- GridSplitter for Col1 left --> 
    <GridSplitter x:Name="myGsLeft" Grid.Column="1" Grid.RowSpan="2" VerticalAlignment="Stretch" HorizontalAlignment="Center" Width="3" DragDelta="GridSplitter_DragDelta" DragCompleted="GridSplitter_DragCompleted"/> 


    <!-- GridSplitter for Col3 right --> 
    <GridSplitter x:Name="myGsRight" Grid.Column="3" Grid.RowSpan="2" VerticalAlignment="Stretch" HorizontalAlignment="Center" Width="3" DragDelta="GridSplitter_DragDelta" DragCompleted="GridSplitter_DragCompleted"/> 
</Grid> 

[主窗口.xaml.cs]

public partial class MainWindow : Window 
{ 
    /// <summary> 
    /// Provides a reusable ToolTip object used by GridSplitter_DragDelta and GridSplitter_DragComplete 
    /// </summary> 
    private ToolTip flyingToolTip = new ToolTip(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    /// <summary> 
    /// Shows a tooltip with the actual width of the left grid column near the registered GridSplitter column while it is dragging 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    private void GridSplitter_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) 
    { 
     if (sender.GetType() != typeof(GridSplitter)) 
     { return; } 

     ShowActualWidthToolTip(gs: (sender as GridSplitter), tt: flyingToolTip); 
    } 

    private void GridSplitter_DragCompleted(object sender, DragCompletedEventArgs e) 
    { 
     flyingToolTip.IsOpen = false; 
    } 
    /// <summary> 
    /// ShowActualWidthToolTip shows actual width of the left and right column near the GridSplitter column, so one can split two columns precisely 
    /// </summary> 
    /// <param name="gs"></param> 
    /// <param name="tt"></param> 
    // TODO: MainWindow.ShowActualWidthToolTip seems to be to tricky for reusability, maybe one find a more scaleable solution 
    private void ShowActualWidthToolTip(GridSplitter gs, ToolTip tt) 
    { 
     // If the GridSplitter isn't positioned correctly in a seperate column between two other columns, drop functionality 
     Grid parentGrid = gs.Parent as Grid; 
     double? leftColumnActualWidth = null; 
     double? rightColumnActualWidth = null; 
     try 
     { 
      leftColumnActualWidth = parentGrid.ColumnDefinitions[(Grid.GetColumn(gs) - 1)].ActualWidth; 
      rightColumnActualWidth = parentGrid.ColumnDefinitions[Grid.GetColumn(gs) + 1].ActualWidth; 

     } 
     catch (ArgumentOutOfRangeException ex) 
     { 
      MessageBox.Show("Something went wrong in your GridSplitter layout. Splitter must been set in a column between the two columns who method tries to evaluate actual width. \n\n" + ex.Message, "Error", MessageBoxButton.OK); 
     } 

     tt.Content = String.Format("\u21E4 Width left {0} | {1} Width right \u21E5", leftColumnActualWidth, rightColumnActualWidth); 
     tt.PlacementTarget = this; 
     tt.Placement = PlacementMode.Relative; 
     tt.HorizontalOffset = (Mouse.GetPosition(this).X - (tt.ActualWidth/2)); 
     tt.VerticalOffset = (Mouse.GetPosition(this).Y + 10); 
     tt.IsOpen = true; 
     return; 
    } 
    /// <summary> 
    /// Prepares a button for moving from one panel to another panel 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    /// TODO: Implement fallback, if button wasn't drop down to another panel 
    private void btnDragDrop_PreviewMouseDown(object sender, MouseButtonEventArgs e) 
    { 
     Button btn = sender as Button; 

     // TODO: This should be done on another place, but where? 
     Panel parent = btn.Parent as Panel; 
     parent.Children.Remove(btn); 

     DragDrop.DoDragDrop(btn, btn, DragDropEffects.Move); 
    } 
    /// <summary> 
    /// Prepares a panel for moving in an element via drag and drop 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    private void Panel_DragEnter(object sender, DragEventArgs e) 
    { 
     e.Effects = DragDropEffects.Move; 
    } 
    /// <summary> 
    /// 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    private void Panel_Drop(object sender, DragEventArgs e) 
    { 
     Panel panel = sender as Panel; 
     panel.Children.Add(((UIElement)e.Data.GetData(typeof(Button)))); 
    } 
} 

面板之間的拖放按照預期的方式工作。但是,如果按鈕掉落在其他地方,按鈕可能會被銷燬。正如你所看到的,我刪除了btnDragDrop_PreviewMouseDown(object sender, MouseButtonEventArgs e)中的btn.parent,但那肯定是錯誤的地方。所以我需要一點提示在哪裏做。它應該以下列方式運行:

如果放置目標不是面板,則不會發生任何事情,也不會從面板的兒童列表中刪除。

來源:WpfDemo on github.com

回答

1

你可以做的是定義兩個私有字段,一個將舉行draged button的以外的其他將持有源panel

private Button tmpButton=null; 
private Panel sourcePanel=null; 

然後改變你的btnDragDrop_PreviewMouseDown處理程序舉行對製圖的button和源panel的引用而不是將其刪除:

private void btnDragDrop_PreviewMouseDown(object sender, MouseButtonEventArgs e) 
    {    
     var btn = sender as Button; 
     tmpButton = btn;    
     var parent = btn.Parent as Panel; 
     sourcePanel = parent;    
     DragDrop.DoDragDrop(btn, btn, DragDropEffects.Move); 
    } 

最後你Panel_Drop處理器將從源中刪除BTM並把它添加到目標panel

private void Panel_Drop(object sender, DragEventArgs e) 
    { 
     Panel panel = sender as Panel; 
     if (sourcePanel != null && tmpButton!=null) 
     { 
      sourcePanel.Children.Remove(tmpButton); 
      panel.Children.Add(((UIElement)e.Data.GetData(typeof(Button)))); 
      sourcePanel = null; 
      tmpButton = null; 
     }      
    } 
+0

那麼容易。謝謝。 –