# C# WPF 多摄像头条码触发拍照系统 下面是一个完整的WPF应用程序实现,它可以在输入条码后按回车键触发多个摄像头同时拍照,并保存照片,同时提供预览功能。 ## 主窗口XAML (MainWindow.xaml) ```xml <Window x:Class="MultiCameraBarcodeApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="多摄像头条码拍照系统" Height="600" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- 条码输入区域 --> <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10"> <TextBlock Text="输入条码:" VerticalAlignment="Center" Margin="0,0,10,0"/> <TextBox x:Name="BarcodeTextBox" Width="200" KeyDown="BarcodeTextBox_KeyDown"/> <Button Content="拍照" Margin="10,0,0,0" Click="CaptureButton_Click"/> </StackPanel> <!-- 摄像头预览区域 --> <ItemsControl x:Name="CameraPreviews" Grid.Row="1" Margin="10"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Columns="2" Rows="2"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Border BorderBrush="Gray" BorderThickness="1" Margin="5"> <Grid> <Image Source="{Binding PreviewImage}" Stretch="Uniform"/> <TextBlock Text="{Binding CameraName}" HorizontalAlignment="Center" VerticalAlignment="Top" Background="#80000000" Foreground="White" Padding="5"/> <TextBlock Text="{Binding Status}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Background="#80000000" Foreground="White" Padding="5"/> </Grid> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <!-- 状态栏 --> <StatusBar Grid.Row="2"> <StatusBarItem> <TextBlock x:Name="StatusTextBlock" Text="准备就绪"/> </StatusBarItem> </StatusBar> </Grid> </Window> ``` ## 主窗口代码 (MainWindow.xaml.cs) ```csharp using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Windows; using System.Windows.Input; using System.Windows.Media.Imaging; using System.Drawing; using System.Drawing.Imaging; using AForge.Video; using AForge.Video.DirectShow; namespace MultiCameraBarcodeApp { public class CameraInfo { public string CameraName { get; set; } public BitmapImage PreviewImage { get; set; } public string Status { get; set; } public VideoCaptureDevice VideoSource { get; set; } } public partial class MainWindow : Window { private List<CameraInfo> _cameras = new List<CameraInfo>(); private FilterInfoCollection _videoDevices; private string _savePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "CameraCaptures"); public MainWindow() { InitializeComponent(); InitializeCameras(); Directory.CreateDirectory(_savePath); } private void InitializeCameras() { _videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); if (_videoDevices.Count == 0) { StatusTextBlock.Text = "未检测到摄像头设备"; return; } for (int i = 0; i < Math.Min(_videoDevices.Count, 4); i++) { var camera = new CameraInfo { CameraName = _videoDevices[i].Name, Status = "正在初始化...", VideoSource = new VideoCaptureDevice(_videoDevices[i].MonikerString) }; camera.VideoSource.NewFrame += (sender, eventArgs) => { Application.Current.Dispatcher.Invoke(() => { camera.PreviewImage = ConvertBitmapToBitmapImage((Bitmap)eventArgs.Frame.Clone()); camera.Status = "运行中"; }); }; camera.VideoSource.Start(); _cameras.Add(camera); } CameraPreviews.ItemsSource = _cameras; } private BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap) { using (var memory = new MemoryStream()) { bitmap.Save(memory, ImageFormat.Bmp); memory.Position = 0; var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); bitmapImage.Freeze(); return bitmapImage; } } private void CapturePhotos(string barcode) { if (string.IsNullOrWhiteSpace(barcode)) { StatusTextBlock.Text = "请输入有效的条码"; return; } if (_cameras.Count == 0) { StatusTextBlock.Text = "没有可用的摄像头"; return; } string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss"); string folderPath = Path.Combine(_savePath, $"{barcode}_{timestamp}"); Directory.CreateDirectory(folderPath); foreach (var camera in _cameras) { try { if (camera.VideoSource != null && camera.VideoSource.IsRunning) { var frame = camera.VideoSource.GetCurrentVideoFrame(); if (frame != null) { string fileName = Path.Combine(folderPath, $"{camera.CameraName}.jpg"); frame.Save(fileName, ImageFormat.Jpeg); camera.Status = $"已保存: {fileName}"; } } } catch (Exception ex) { camera.Status = $"错误: {ex.Message}"; } } StatusTextBlock.Text = $"照片已保存到: {folderPath}"; } private void BarcodeTextBox_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { CapturePhotos(BarcodeTextBox.Text.Trim()); } } private void CaptureButton_Click(object sender, RoutedEventArgs e) { CapturePhotos(BarcodeTextBox.Text.Trim()); } protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { foreach (var camera in _cameras) { if (camera.VideoSource != null && camera.VideoSource.IsRunning) { camera.VideoSource.SignalToStop(); camera.VideoSource.WaitForStop(); } } base.OnClosing(e); } } } ``` ## 注意事项 1. 需要安装AForge.NET库来处理摄像头捕获,可以通过NuGet安装: ``` Install-Package AForge.Video Install-Package AForge.Video.DirectShow ``` 2. 系统最多支持4个摄像头同时工作,预览界面采用2x2网格布局,如需更多摄像头需要调整布局和代码。 3. 照片保存路径为用户的"图片"文件夹下的"CameraCaptures"子文件夹,每次拍照会创建一个以条码和时间戳命名的子文件夹。 ## 额外逻辑点 1. 摄像头初始化失败处理:代码中检测了摄像头数量,但没有处理初始化失败的情况,实际应用中应添加重试机制。 2. 资源释放:虽然关闭窗口时停止了摄像头,但更完善的实现应该实现IDisposable接口确保资源释放。 3. 性能优化:频繁的Bitmap转换可能影响性能,可以考虑使用WriteableBitmap或其他更高效的方式处理视频帧。 [2025-04-10 17:10:31 | AI写代码神器 | 1749点数解答]