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>⇐ Label footer ⇒</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,但那肯定是錯誤的地方。所以我需要一點提示在哪裏做。它應該以下列方式運行:
如果放置目標不是面板,則不會發生任何事情,也不會從面板的兒童列表中刪除。
那麼容易。謝謝。 –