UWP银行账户列表拖拽:账户间转账落地逻辑实现求助
Hey there! Let’s walk through exactly how to implement that drag-and-drop transfer logic for your UWP bank app. I’ll break it down into actionable steps with code examples you can reuse.
Step 1: Define Your Account Data Model
First, you’ll need a robust account model that supports UI updates. Implement INotifyPropertyChanged so changes to the balance reflect immediately in your views:
using System.ComponentModel; using System.Runtime.CompilerServices; public class Account : INotifyPropertyChanged { private decimal _balance; public string Id { get; set; } public string AccountName { get; set; } public decimal Balance { get => _balance; set { if (_balance != value) { _balance = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
In your ViewModel, use ObservableCollection for your account lists to enable automatic UI updates when items change:
using System.Collections.ObjectModel; public class TransferViewModel : INotifyPropertyChanged { public ObservableCollection<Account> OutgoingAccounts { get; set; } = new ObservableCollection<Account>(); public ObservableCollection<Account> IncomingAccounts { get; set; } = new ObservableCollection<Account>(); // Don't forget to implement INotifyPropertyChanged here too if needed public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
Step 2: Set Up the Outgoing Accounts List as a Drag Source
Configure your outgoing accounts ListView to allow dragging, and handle the DragItemsStarting event to pass the selected account data:
XAML:
<ListView x:Name="OutgoingAccountsListView" ItemsSource="{Binding OutgoingAccounts}" AllowDrop="True" DragItemsStarting="OutgoingAccountsListView_DragItemsStarting"> <!-- Your item template (e.g., display account name and balance) --> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding AccountName}" Margin="0 0 10 0"/> <TextBlock Text="{Binding Balance, StringFormat=C}"/> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>
Code-Behind:
private void OutgoingAccountsListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) { // Only allow dragging a single account if (e.Items.Count == 1 && e.Items[0] is Account sourceAccount) { // Use a custom data format to avoid conflicts with other drag operations e.Data.SetData("BankTransferAccount", sourceAccount); // Add a friendly caption to the drag UI e.DragUIOverride.Caption = $"Transfer from {sourceAccount.AccountName}"; e.DragUIOverride.IsCaptionVisible = true; } else { // Cancel if multiple items or invalid data e.Cancel = true; } }
Step 3: Make Each Incoming Account Item a Drop Target
You need to enable drop functionality on every individual incoming account list item. Use an ItemContainerStyle to attach the necessary events:
XAML:
<ListView x:Name="IncomingAccountsListView" ItemsSource="{Binding IncomingAccounts}"> <ListView.ItemContainerStyle> <Style TargetType="ListViewItem"> <Setter Property="AllowDrop" Value="True"/> <EventSetter Event="DragOver" Handler="IncomingAccountItem_DragOver"/> <EventSetter Event="Drop" Handler="IncomingAccountItem_Drop"/> </Style> </ListView.ItemContainerStyle> <!-- Your incoming account item template --> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding AccountName}" Margin="0 0 10 0"/> <TextBlock Text="{Binding Balance, StringFormat=C}"/> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>
Handle Drag Over (Validate Drop):
private void IncomingAccountItem_DragOver(object sender, DragEventArgs e) { // Check if the dragged data is our custom account format if (e.Data.Contains("BankTransferAccount")) { // Set accepted operation to Copy (since we're duplicating value, not moving the account) e.AcceptedOperation = DataPackageOperation.Copy; // Show visual feedback that drop is allowed e.DragUIOverride.IsGlyphVisible = true; } else { e.AcceptedOperation = DataPackageOperation.None; } }
Step 4: Implement the Transfer Logic on Drop
This is the core part—when the user drops the outgoing account onto an incoming one, handle amount input, balance validation, and update the accounts:
private async void IncomingAccountItem_Drop(object sender, DragEventArgs e) { // Get the target incoming account from the dropped item's DataContext if (sender is ListViewItem targetItem && targetItem.DataContext is Account targetAccount) { // Retrieve the source outgoing account from the drag data if (e.Data.GetData("BankTransferAccount") is Account sourceAccount) { // Prevent transferring to the same account if (sourceAccount.Id == targetAccount.Id) { await new ContentDialog { Title = "Invalid Transfer", Content = "You can't transfer to the same account.", CloseButtonText = "OK" }.ShowAsync(); return; } // Prompt user for transfer amount var amountInput = new TextBox { PlaceholderText = "Enter amount", Keyboard = Windows.UI.Xaml.Input.Keyboard.Numeric, Margin = new Windows.UI.Xaml.Thickness(0, 10, 0, 0) }; var amountDialog = new ContentDialog { Title = "Enter Transfer Amount", Content = amountInput, PrimaryButtonText = "Transfer", CloseButtonText = "Cancel" }; var result = await amountDialog.ShowAsync(); if (result == ContentDialogResult.Primary) { if (decimal.TryParse(amountInput.Text, out decimal transferAmount) && transferAmount > 0) { // Check if source account has enough balance if (sourceAccount.Balance >= transferAmount) { // Update balances (INotifyPropertyChanged will refresh the UI) sourceAccount.Balance -= transferAmount; targetAccount.Balance += transferAmount; // Show success message await new ContentDialog { Title = "Transfer Successful", Content = $"${transferAmount:N2} transferred from {sourceAccount.AccountName} to {targetAccount.AccountName}", CloseButtonText = "OK" }.ShowAsync(); } else { await new ContentDialog { Title = "Insufficient Funds", Content = $"Source account has only ${sourceAccount.Balance:N2}, which is less than ${transferAmount:N2}", CloseButtonText = "OK" }.ShowAsync(); } } else { await new ContentDialog { Title = "Invalid Amount", Content = "Please enter a valid positive number.", CloseButtonText = "OK" }.ShowAsync(); } } } } }
Bonus: MVVM-Friendly Approach (Optional)
If you’re following MVVM, you can decouple the drag-and-drop logic from the code-behind using the Microsoft.Xaml.Behaviors.Uwp.Managed NuGet package. This lets you bind drag/drop events to commands in your ViewModel:
- Install the NuGet package
- Add behaviors to your ListView items:
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:core="using:Microsoft.Xaml.Interactions.Core" xmlns:dragDrop="using:Microsoft.Xaml.Interactions.DragDrop" <!-- For the outgoing list --> <ListView ItemsSource="{Binding OutgoingAccounts}"> <interactivity:Interaction.Behaviors> <dragDrop:DragStartingBehavior DragStartingCommand="{Binding DragAccountCommand}"/> </interactivity:Interaction.Behaviors> </ListView> <!-- For incoming list items --> <ListView ItemsSource="{Binding IncomingAccounts}"> <ListView.ItemContainerStyle> <Style TargetType="ListViewItem"> <Setter Property="AllowDrop" Value="True"/> <Setter Property="interactivity:Interaction.Behaviors"> <Setter.Value> <interactivity:BehaviorCollection> <dragDrop:DropBehavior DroppedCommand="{Binding DropAccountCommand}"/> </interactivity:BehaviorCollection> </Setter.Value> </Setter> </Style> </ListView.ItemContainerStyle> </ListView>
Then implement the commands in your ViewModel to handle the drag/drop logic without code-behind.
内容的提问来源于stack exchange,提问作者Mohammad Alqurm

